From 9c2cc1d3f39f509123ff963036c4488b06d6be23 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sat, 3 Jul 2021 06:28:38 +0800 Subject: [PATCH] feat: CTAP/FIDO cert --- src/fido.rs | 23 ++++++++++++++ src/register.rs | 80 ++++++++++++++++++++++++------------------------- 2 files changed, 62 insertions(+), 41 deletions(-) diff --git a/src/fido.rs b/src/fido.rs index 1b97e52..67aca19 100644 --- a/src/fido.rs +++ b/src/fido.rs @@ -59,4 +59,27 @@ pub fn start_status_updater() -> Sender { } }); status_tx +} + +pub fn to_pem(bs: &[u8], sub: &str, w: usize) -> String { + let mut s = String::with_capacity(bs.len() * 2); + s.push_str(&format!("-----BEGIN {}-----", sub)); + let b64 = base64::encode(bs).chars().collect::>(); + let mut b64 = b64.as_slice(); + while b64.len() > 0 { + s.push('\n'); + if b64.len() >= w { + for i in 0..w { + s.push(b64[i]); + } + b64 = &b64[w..]; + } else { + for c in b64 { + s.push(*c); + } + b64 = &[]; + } + } + s.push_str(&format!("\n-----END {}-----", sub)); + s } \ No newline at end of file diff --git a/src/register.rs b/src/register.rs index 374de7b..a61c0b4 100644 --- a/src/register.rs +++ b/src/register.rs @@ -4,7 +4,6 @@ use authenticator::authenticatorservice::AuthenticatorService; use authenticator::statecallback::StateCallback; use authenticator::RegisterFlags; use std::sync::mpsc::channel; -use rust_util::XResult; use crate::fido; use crate::digest; use crate::fido::U2fV2Challenge; @@ -28,7 +27,8 @@ impl Command for CommandImpl { }; let u2fv2_challenge = U2fV2Challenge::new_random(app_id); - let chall_bytes = digest::sha256(&u2fv2_challenge.to_json()); + let u2fv2_challenge_str = u2fv2_challenge.to_json(); + let chall_bytes = digest::sha256(&u2fv2_challenge_str); let app_bytes = digest::sha256(app_id); @@ -61,47 +61,45 @@ impl Command for CommandImpl { success!("Device info: {}", &device_info); success!("Register result: {}", base64::encode(®ister_data)); - let credential = u2f_get_key_handle_from_register_response(®ister_data).unwrap(); - success!("Key handle: {}", base64::encode(&credential)); - success!("Key handle: {}", hex::encode(&credential)); + let client_data = &u2fv2_challenge_str; + let rr = match u2f::register::parse_registration(app_id.to_string(), client_data.as_bytes().to_vec(), register_data) { + Ok(rr) => rr, + Err(e) => return simple_error!("Parse registration data failed: {}", e), + }; + if let Some(cert) = rr.attestation_cert { + success!("Certificate: {}", fido::to_pem(&cert, "CERTIFICATE", 64)); + } + if let Some(device_name) = rr.device_name { + success!("Device name: {}", device_name); + } + success!("Public key: {}", hex::encode(rr.pub_key)); + success!("Key handle: {}", hex::encode(rr.key_handle)); Ok(()) } } - -// U2F raw message format specification (version 20170411) section 4.3 -// In case of success we need to send back the following reply -// (excluding ISO7816 success code) -// +------+--------------------+---------------------+------------+------------+------+ -// + 0x05 | User pub key (65B) | key handle len (1B) | key handle | X.509 Cert | Sign | -// +------+--------------------+---------------------+------------+------------+------+ // -// Where Sign is an ECDSA signature over the following structure: -// +------+-------------------+-----------------+------------+--------------------+ -// + 0x00 | application (32B) | challenge (32B) | key handle | User pub key (65B) | -// +------+-------------------+-----------------+------------+--------------------+ -// see https://github.com/google/OpenSK/blob/stable/src/ctap/ctap1.rs -fn u2f_get_key_handle_from_register_response(register_response: &[u8]) -> XResult> { - if register_response[0] != 0x05 { - return simple_error!("Reserved byte not set correctly"); - } - - let key_handle_len = register_response[66] as usize; - let mut public_key = register_response.to_owned(); - let mut key_handle = public_key.split_off(67); - let _attestation = key_handle.split_off(key_handle_len); - - Ok(key_handle) -} - -#[test] -fn test() { - let app_id = "https://webencrypt.org"; - let client_data = base64::decode("eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZmluaXNoRW5yb2xsbWVudCIsImNoYWxsZW5nZSI6ImFHVnNiRzlmZDI5eWJHUSIsIm9yaWdpbiI6Imh0dHBzOi8vd2ViZW5jcnlwdC5vcmciLCJjcm9zc09yaWdpbiI6ZmFsc2V9").unwrap(); - let register_data = base64::decode("BQSpAR+aliPCpz2u8c84Mv13bAYUcJnT9OgiXkCX9CTR/xhWqbJwoL9l6WRqvqwtG77NMvkDexcTKf9Mtf5+V1NCQKYfXWCP+IL2Lfbyng7mX0GV/etsHqlIiiaoEQo5g0Zetn+JimnLx5f259OZlEsvzB7Qs6swN5WRy57FRqREOPcwggE0MIHboAMCAQICCiBzHdQQUIQZ+ZgwCgYIKoZIzj0EAwIwFTETMBEGA1UEAxMKVTJGIElzc3VlcjAaFwswMDAxMDEwMDAwWhcLMDAwMTAxMDAwMFowFTETMBEGA1UEAxMKVTJGIERldmljZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCvum43LiwAMJacjIFh7SCIrl1e1ltYXeK50v2HWDRZ9pw38HSPmx2SjT+2hTY1YMVagbgXimYrb8+WWlZ6j9bqjFzAVMBMGCysGAQQBguUcAgEBBAQDAgUgMAoGCCqGSM49BAMCA0gAMEUCIQDBo6aOLxanIUYnBX9iu3KMngPnobpi0EZSTkVtLC8/cwIgC1945RGqGBKfbyNtkhMifZK05n7fU+gW37Bdnci5D94wRQIhAJHR1EsN/+Fb+74FyxSjBMaoD6p2edlqGPYEb4SiXwccAiAM48XFf/sWsgzS2YU4EeObztVLErUb1JaA2qHJtTUoig==").unwrap(); - let r = u2f::register::parse_registration(app_id.to_string(), client_data, register_data); - let rr = r.unwrap(); - println!("{}", hex::encode(rr.pub_key)); - println!("{}", hex::encode(rr.attestation_cert.unwrap())); - println!("{}", hex::encode(rr.key_handle)); -} \ No newline at end of file +// // U2F raw message format specification (version 20170411) section 4.3 +// // In case of success we need to send back the following reply +// // (excluding ISO7816 success code) +// // +------+--------------------+---------------------+------------+------------+------+ +// // + 0x05 | User pub key (65B) | key handle len (1B) | key handle | X.509 Cert | Sign | +// // +------+--------------------+---------------------+------------+------------+------+ +// // +// // Where Sign is an ECDSA signature over the following structure: +// // +------+-------------------+-----------------+------------+--------------------+ +// // + 0x00 | application (32B) | challenge (32B) | key handle | User pub key (65B) | +// // +------+-------------------+-----------------+------------+--------------------+ +// // see https://github.com/google/OpenSK/blob/stable/src/ctap/ctap1.rs +// fn u2f_get_key_handle_from_register_response(register_response: &[u8]) -> XResult> { +// if register_response[0] != 0x05 { +// return simple_error!("Reserved byte not set correctly"); +// } +// +// let key_handle_len = register_response[66] as usize; +// let mut public_key = register_response.to_owned(); +// let mut key_handle = public_key.split_off(67); +// let _attestation = key_handle.split_off(key_handle_len); +// +// Ok(key_handle) +// }