From 2da55ab38931b229f1d5c0f59079e277a2ea19b2 Mon Sep 17 00:00:00 2001 From: Flavio Oliveira Date: Mon, 11 Sep 2017 22:22:05 +0200 Subject: [PATCH] Almost done, just need to fix sign_response verification --- Cargo.toml | 3 +-- example/static/index.html | 9 ++++--- src/authorization.rs | 53 +++++++++++++++++++++++---------------- src/lib.rs | 1 - src/protocol.rs | 17 ++++++------- src/register.rs | 39 ++++++++++++++-------------- src/u2ferror.rs | 12 +++++++-- src/util.rs | 9 +++++++ 8 files changed, 85 insertions(+), 58 deletions(-) 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