diff --git a/Cargo.toml b/Cargo.toml
index ac31d53..5ec93c5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,5 +28,4 @@ serde_derive = "^1.0"
byteorder = "1.1.0"
ring = "0.11.0"
webpki = "0.16.0"
-untrusted = "0.5.1"
-crypto-hash = "0.3.0"
\ No newline at end of file
+untrusted = "0.5.1"
\ No newline at end of file
diff --git a/example/static/index.html b/example/static/index.html
index 59e2f2c..87805bb 100644
--- a/example/static/index.html
+++ b/example/static/index.html
@@ -52,7 +52,7 @@
Rust U2F App test
function authenticate() {
$.getJSON('/api/sign_request')
.done(function(req) {
- console.log(req);
+ showNotification("Authentication");
u2f.sign(req.appId, req.challenge, req.registeredKeys, postSignedResponse, 30);
}).fail(function(response) {
sweetAlert("Authentication failed", response.responseText, "error");
@@ -61,15 +61,16 @@ Rust U2F App test
/* Verify the results on the server */
function postSignedResponse(response) {
- if (response.errorCode === u2f.ErrorCodes['OK']) {
- $.post('/api/sign_response', JSON.stringify(response))
+ if (typeof response.errorCode === 'undefined' || response.errorCode === u2f.ErrorCodes['OK']) {
+ console.log(response);
+ postJSON('/api/sign_response', response)
.done(function() {
swal("Authenticated!");
}).fail(function(response) {
sweetAlert("Authentication failed", response.responseText, "error");
});
} else {
- alert(parseError(response.errorCode));
+ sweetAlert("Authentication failed", (parseError(response.errorCode)), "error");
}
}
diff --git a/src/authorization.rs b/src/authorization.rs
index f873dd1..74f7792 100644
--- a/src/authorization.rs
+++ b/src/authorization.rs
@@ -1,8 +1,8 @@
use bytes::{Bytes, Buf, BufMut, BigEndian};
use std::io::Cursor;
-use ring::{error, signature};
+use ring::{digest};
use untrusted::Input;
-use crypto_hash::{Algorithm, hex_digest};
+use webpki::{SignatureAlgorithm, EndEntityCert, ECDSA_P256_SHA256};
use util::*;
use u2ferror::U2fError;
@@ -17,32 +17,43 @@ pub struct Authorization {
pub user_presence: bool,
}
-pub fn parse_sign_response(app_id: String, client_data: Vec, pub_key: Vec, sign_data: Vec) -> Result {
-
+pub fn parse_sign_response(app_id: String, client_data: Vec, attestation_certificate: Vec, sign_data: Vec) -> Result {
if get_user_presence(&sign_data[..]) != 1 {
return Err(U2fError::InvalidUserPresenceByte);
}
//Start parsing ...
let mut mem = Bytes::from(sign_data);
- let _ = mem.split_to(1); // advance the user presence byte.
+ let user_presence_flag = mem.split_to(1);
let counter = mem.split_to(4);
- let signature_len = asn_length(mem.clone()).unwrap();
- let signature = mem.split_to(signature_len as usize);
-
- let app_id_hash = hex_digest(Algorithm::SHA256, &app_id.into_bytes());
- let client_data_hash = hex_digest(Algorithm::SHA256, &client_data);
-
- let mut sign_base = vec![];
- sign_base.put(app_id_hash);
- sign_base.put(client_data_hash);
- sign_base.put(counter.clone());
-
- let input_pub_key = Input::from(&pub_key[..]);
- let input_sign = Input::from(&signature[..]);
- let input_sign_base = Input::from(&sign_base[..]);
- signature::verify(&signature::ECDSA_P256_SHA256_FIXED, input_pub_key, input_sign_base, input_sign)
- .map_err(|error::Unspecified| U2fError::BadSignature)?;
+
+ let raw_data = mem.clone();
+
+ let sig_len = asn_length(mem.clone()).unwrap();
+ let signature = mem.split_to(sig_len);
+
+ // Let's build the msg to verify the signature
+ let app_id_hash = digest::digest(&digest::SHA256, &app_id.into_bytes());
+ let client_data_hash = digest::digest(&digest::SHA256, &client_data[..]);
+
+ let mut msg = vec![];
+ msg.put(app_id_hash.as_ref());
+ msg.put(user_presence_flag.clone());
+ msg.put(raw_data.clone());
+ msg.put(client_data_hash.as_ref());
+
+ let msg_hash = digest::digest(&digest::SHA256, &msg);
+
+ let input_sig = Input::from(&signature[..]);
+ let input_msg = Input::from(msg_hash.as_ref());
+
+ // The signature is to be verified by the relying party using the public key certified
+ // in the attestation certificate.
+ let cert = EndEntityCert::from(Input::from(&attestation_certificate[..]))
+ .map_err(|_e| U2fError::BadCertificate)?;
+
+ let algo : &[&SignatureAlgorithm] = &[&ECDSA_P256_SHA256];
+ cert.verify_signature(algo[0], input_msg, input_sig).map_err(|_e| U2fError::BadSignature)?;
let authorization = Authorization {
counter: get_counter(counter),
diff --git a/src/lib.rs b/src/lib.rs
index ed2a234..b8c2ed2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,7 +11,6 @@ extern crate chrono;
extern crate base64;
extern crate webpki;
extern crate untrusted;
-extern crate crypto_hash;
mod util;
diff --git a/src/protocol.rs b/src/protocol.rs
index eb085c5..3e99f46 100644
--- a/src/protocol.rs
+++ b/src/protocol.rs
@@ -3,7 +3,7 @@ use messages::*;
use register::*;
use authorization::*;
-use base64::{encode, decode, decode_config, Config, CharacterSet, LineWrap};
+use base64::{encode, decode_config, Config, CharacterSet, LineWrap};
use chrono::prelude::*;
use time::Duration;
use u2ferror::U2fError;
@@ -120,19 +120,18 @@ impl U2f {
return Err(U2fError::ChallengeExpired);
}
- if sign_resp.key_handle != encode(®.key_handle[..]) {
+ if sign_resp.key_handle != get_encoded(®.key_handle[..]) {
return Err(U2fError::WrongKeyHandler);
}
- decode(&sign_resp.client_data).map_err(|_e| U2fError::InvalidClientData)?;
- decode(&sign_resp.signature_data).map_err(|_e| U2fError::InvalidSignatureData)?;
-
let config = Config::new(CharacterSet::UrlSafe, true, false, LineWrap::NoWrap);
- let client_data: Vec = decode_config(&sign_resp.client_data[..], config).unwrap();
- let sign_data: Vec = decode_config(&sign_resp.signature_data[..], config).unwrap();
-
- let auth = parse_sign_response(self.app_id.clone(), client_data.clone(), reg.pub_key, sign_data.clone());
+ let client_data: Vec = decode_config(&sign_resp.client_data[..], config).map_err(|_e| U2fError::InvalidClientData)?;
+ let sign_data: Vec = decode_config(&sign_resp.signature_data[..], config).map_err(|_e| U2fError::InvalidSignatureData)?;
+ let cert = reg.attestation_cert.unwrap();
+
+ let auth = parse_sign_response(self.app_id.clone(), client_data.clone(), cert, sign_data.clone());
+
Ok(auth.unwrap().counter)
}
}
\ No newline at end of file
diff --git a/src/register.rs b/src/register.rs
index 7ca0835..f146d44 100644
--- a/src/register.rs
+++ b/src/register.rs
@@ -1,10 +1,8 @@
use bytes::{Bytes, BufMut};
-use base64::{encode};
-use ring::{error, signature};
+use ring::{digest};
use untrusted::Input;
use byteorder::{ByteOrder, BigEndian};
-use crypto_hash::{Algorithm, hex_digest};
-use webpki::trust_anchor_util;
+use webpki::{SignatureAlgorithm, trust_anchor_util, EndEntityCert, ECDSA_P256_SHA256};
use util::*;
use messages::RegisteredKey;
@@ -48,29 +46,32 @@ pub fn parse_registration(app_id: String, client_data: Vec, registration_dat
let attestation_certificate = mem.split_to(cert_len);
// Check certificate as trust anchor
- let anchor = trust_anchor_util::cert_der_as_trust_anchor(
- Input::from(&attestation_certificate[..])).unwrap();
-
+ trust_anchor_util::cert_der_as_trust_anchor(Input::from(&attestation_certificate[..]))
+ .map_err(|_e| U2fError::NotTrustedAnchor)?;
+
// Remaining data corresponds to the signature
let signature = mem;
- let app_id_hash = hex_digest(Algorithm::SHA256, &app_id.into_bytes());
- let client_data_hash = hex_digest(Algorithm::SHA256, &client_data[..]);
+ // Let's build the msg to verify the signature
+ let app_id_hash = digest::digest(&digest::SHA256, &app_id.into_bytes());
+ let client_data_hash = digest::digest(&digest::SHA256, &client_data[..]);
- let mut sig_base = vec![0x00]; // A byte reserved for future use [1 byte] with the value 0x00
- sig_base.put(app_id_hash);
- sig_base.put(client_data_hash);
- sig_base.put(key_handle.clone());
- sig_base.put(public_key.clone());
+ let mut msg = vec![0x00]; // A byte reserved for future use [1 byte] with the value 0x00
+ msg.put(app_id_hash.as_ref());
+ msg.put(client_data_hash.as_ref());
+ msg.put(key_handle.clone());
+ msg.put(public_key.clone());
- let input_pub_key = Input::from(&anchor.spki[..]);
let input_sig = Input::from(&signature[..]);
- let input_sig_base = Input::from(&sig_base[..]);
+ let input_msg = Input::from(&msg[..]);
// The signature is to be verified by the relying party using the public key certified
// in the attestation certificate.
- signature::verify(&signature::ECDSA_P256_SHA256_ASN1, input_pub_key, input_sig_base, input_sig)
- .map_err(|error::Unspecified| U2fError::BadSignature)?;
+ let cert = EndEntityCert::from(Input::from(&attestation_certificate[..]))
+ .map_err(|_e| U2fError::BadCertificate)?;
+
+ let algo : &[&SignatureAlgorithm] = &[&ECDSA_P256_SHA256];
+ cert.verify_signature(algo[0], input_msg, input_sig).map_err(|_e| U2fError::BadSignature)?;
let registration = Registration {
key_handle: key_handle[..].to_vec(),
@@ -85,6 +86,6 @@ pub fn get_registered_key(app_id: String, key_handle: Vec) -> RegisteredKey
RegisteredKey {
app_id: app_id,
version: U2F_V2.into(),
- key_handle: Some(encode(&key_handle[..]))
+ key_handle: Some(get_encoded(key_handle.as_slice()))
}
}
\ No newline at end of file
diff --git a/src/u2ferror.rs b/src/u2ferror.rs
index 42bbbce..14ac19d 100644
--- a/src/u2ferror.rs
+++ b/src/u2ferror.rs
@@ -11,7 +11,9 @@ pub enum U2fError {
WrongKeyHandler,
InvalidClientData,
InvalidSignatureData,
- InvalidUserPresenceByte
+ InvalidUserPresenceByte,
+ BadCertificate,
+ NotTrustedAnchor
}
impl fmt::Display for U2fError {
@@ -26,6 +28,8 @@ impl fmt::Display for U2fError {
U2fError::InvalidClientData => write!(f, "Invalid Client Data"),
U2fError::InvalidSignatureData => write!(f, "Invalid Signature Data"),
U2fError::InvalidUserPresenceByte => write!(f, "Invalid User Presence Byte"),
+ U2fError::BadCertificate => write!(f, "Failed to parse certificate"),
+ U2fError::NotTrustedAnchor => write!(f, "Not Trusted Anchor"),
}
}
}
@@ -42,6 +46,8 @@ impl error::Error for U2fError {
U2fError::InvalidClientData => "Invalid Client Data",
U2fError::InvalidSignatureData => "Invalid Signature Data",
U2fError::InvalidUserPresenceByte => "Invalid User Presence Byte",
+ U2fError::BadCertificate => "Failed to parse certificate",
+ U2fError::NotTrustedAnchor => "Not Trusted Anchor",
}
}
@@ -55,7 +61,9 @@ impl error::Error for U2fError {
U2fError::WrongKeyHandler => None,
U2fError::InvalidClientData => None,
U2fError::InvalidSignatureData => None,
- U2fError::InvalidUserPresenceByte => None,
+ U2fError::InvalidUserPresenceByte => None,
+ U2fError::BadCertificate => None,
+ U2fError::NotTrustedAnchor => None,
}
}
}
\ No newline at end of file
diff --git a/src/util.rs b/src/util.rs
index 3b7deeb..76ee689 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -2,6 +2,7 @@ use chrono::prelude::*;
use time::Duration;
use ring::rand::{SecureRandom, SystemRandom};
use bytes::{Bytes};
+use base64::{encode_config, Config, CharacterSet, LineWrap};
use u2ferror::U2fError;
/// The `Result` type used in this crate.
@@ -57,4 +58,12 @@ pub fn asn_length(mem: Bytes) -> Result {
}
Ok(length + 2) // Add the 2 initial bytes: type and length.
+}
+
+pub fn get_encoded(data: &[u8]) -> String {
+ let config = Config::new(CharacterSet::UrlSafe, true, false, LineWrap::NoWrap);
+
+ let encoded: String = encode_config(data, config);
+
+ encoded.trim_right_matches('=').to_string()
}
\ No newline at end of file