feat: CTAP/FIDO cert
This commit is contained in:
23
src/fido.rs
23
src/fido.rs
@@ -59,4 +59,27 @@ pub fn start_status_updater() -> Sender<StatusUpdate> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
status_tx
|
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::<Vec<char>>();
|
||||||
|
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
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,6 @@ use authenticator::authenticatorservice::AuthenticatorService;
|
|||||||
use authenticator::statecallback::StateCallback;
|
use authenticator::statecallback::StateCallback;
|
||||||
use authenticator::RegisterFlags;
|
use authenticator::RegisterFlags;
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
use rust_util::XResult;
|
|
||||||
use crate::fido;
|
use crate::fido;
|
||||||
use crate::digest;
|
use crate::digest;
|
||||||
use crate::fido::U2fV2Challenge;
|
use crate::fido::U2fV2Challenge;
|
||||||
@@ -28,7 +27,8 @@ impl Command for CommandImpl {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let u2fv2_challenge = U2fV2Challenge::new_random(app_id);
|
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);
|
let app_bytes = digest::sha256(app_id);
|
||||||
|
|
||||||
@@ -61,47 +61,45 @@ impl Command for CommandImpl {
|
|||||||
|
|
||||||
success!("Device info: {}", &device_info);
|
success!("Device info: {}", &device_info);
|
||||||
success!("Register result: {}", base64::encode(®ister_data));
|
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(())
|
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:
|
// // U2F raw message format specification (version 20170411) section 4.3
|
||||||
// +------+-------------------+-----------------+------------+--------------------+
|
// // In case of success we need to send back the following reply
|
||||||
// + 0x00 | application (32B) | challenge (32B) | key handle | User pub key (65B) |
|
// // (excluding ISO7816 success code)
|
||||||
// +------+-------------------+-----------------+------------+--------------------+
|
// // +------+--------------------+---------------------+------------+------------+------+
|
||||||
// see https://github.com/google/OpenSK/blob/stable/src/ctap/ctap1.rs
|
// // + 0x05 | User pub key (65B) | key handle len (1B) | key handle | X.509 Cert | Sign |
|
||||||
fn u2f_get_key_handle_from_register_response(register_response: &[u8]) -> XResult<Vec<u8>> {
|
// // +------+--------------------+---------------------+------------+------------+------+
|
||||||
if register_response[0] != 0x05 {
|
// //
|
||||||
return simple_error!("Reserved byte not set correctly");
|
// // Where Sign is an ECDSA signature over the following structure:
|
||||||
}
|
// // +------+-------------------+-----------------+------------+--------------------+
|
||||||
|
// // + 0x00 | application (32B) | challenge (32B) | key handle | User pub key (65B) |
|
||||||
let key_handle_len = register_response[66] as usize;
|
// // +------+-------------------+-----------------+------------+--------------------+
|
||||||
let mut public_key = register_response.to_owned();
|
// // see https://github.com/google/OpenSK/blob/stable/src/ctap/ctap1.rs
|
||||||
let mut key_handle = public_key.split_off(67);
|
// fn u2f_get_key_handle_from_register_response(register_response: &[u8]) -> XResult<Vec<u8>> {
|
||||||
let _attestation = key_handle.split_off(key_handle_len);
|
// if register_response[0] != 0x05 {
|
||||||
|
// return simple_error!("Reserved byte not set correctly");
|
||||||
Ok(key_handle)
|
// }
|
||||||
}
|
//
|
||||||
|
// let key_handle_len = register_response[66] as usize;
|
||||||
#[test]
|
// let mut public_key = register_response.to_owned();
|
||||||
fn test() {
|
// let mut key_handle = public_key.split_off(67);
|
||||||
let app_id = "https://webencrypt.org";
|
// let _attestation = key_handle.split_off(key_handle_len);
|
||||||
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();
|
// Ok(key_handle)
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user