Skip to content

Commit

Permalink
Almost done, just need to fix sign_response verification
Browse files Browse the repository at this point in the history
  • Loading branch information
Flavio Oliveira committed Sep 11, 2017
1 parent 10ff7cb commit 2da55ab
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 58 deletions.
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
untrusted = "0.5.1"
9 changes: 5 additions & 4 deletions example/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ <h1>Rust U2F App test</h1>
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");
Expand All @@ -61,15 +61,16 @@ <h1>Rust U2F App test</h1>

/* 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");
}
}

Expand Down
53 changes: 32 additions & 21 deletions src/authorization.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -17,32 +17,43 @@ pub struct Authorization {
pub user_presence: bool,
}

pub fn parse_sign_response(app_id: String, client_data: Vec<u8>, pub_key: Vec<u8>, sign_data: Vec<u8>) -> Result<Authorization> {

pub fn parse_sign_response(app_id: String, client_data: Vec<u8>, attestation_certificate: Vec<u8>, sign_data: Vec<u8>) -> Result<Authorization> {
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),
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ extern crate chrono;
extern crate base64;
extern crate webpki;
extern crate untrusted;
extern crate crypto_hash;

mod util;

Expand Down
17 changes: 8 additions & 9 deletions src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -120,19 +120,18 @@ impl U2f {
return Err(U2fError::ChallengeExpired);
}

if sign_resp.key_handle != encode(&reg.key_handle[..]) {
if sign_resp.key_handle != get_encoded(&reg.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<u8> = decode_config(&sign_resp.client_data[..], config).unwrap();
let sign_data: Vec<u8> = 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<u8> = decode_config(&sign_resp.client_data[..], config).map_err(|_e| U2fError::InvalidClientData)?;
let sign_data: Vec<u8> = 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)
}
}
39 changes: 20 additions & 19 deletions src/register.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -48,29 +46,32 @@ pub fn parse_registration(app_id: String, client_data: Vec<u8>, 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(),
Expand All @@ -85,6 +86,6 @@ pub fn get_registered_key(app_id: String, key_handle: Vec<u8>) -> 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()))
}
}
12 changes: 10 additions & 2 deletions src/u2ferror.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ pub enum U2fError {
WrongKeyHandler,
InvalidClientData,
InvalidSignatureData,
InvalidUserPresenceByte
InvalidUserPresenceByte,
BadCertificate,
NotTrustedAnchor
}

impl fmt::Display for U2fError {
Expand All @@ -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"),
}
}
}
Expand All @@ -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",
}
}

Expand All @@ -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,
}
}
}
9 changes: 9 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -57,4 +58,12 @@ pub fn asn_length(mem: Bytes) -> Result<usize> {
}

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()
}

0 comments on commit 2da55ab

Please sign in to comment.