Skip to main content

ctap_hid_fido2/
lib.rs

1/*!
2## Examples
3
4[-> Examples](https://github.com/gebogebogebo/ctap-hid-fido2/blob/master/README.md#examples)
5
6*/
7
8pub mod auth_data;
9mod crypto;
10mod ctapdef;
11mod ctaphid;
12mod encrypt {
13    pub mod cose;
14    pub mod enc_aes256_cbc;
15    pub mod enc_hmac_sha_256;
16    pub mod p256;
17    pub mod shared_secret;
18    pub mod shared_secret2;
19}
20mod hmac_ext;
21mod pintoken;
22pub mod public_key;
23pub mod public_key_credential_descriptor;
24pub mod public_key_credential_rp_entity;
25pub mod public_key_credential_user_entity;
26pub mod str_buf;
27pub mod util;
28pub mod util_ciborium;
29pub mod verifier;
30
31use anyhow::{anyhow, Result};
32
33pub mod fidokey;
34pub use fidokey::FidoKeyHid;
35
36mod hid;
37pub use hid::{HidInfo, HidParam};
38
39pub type Cfg = LibCfg;
40
41#[derive(Clone)]
42pub struct LibCfg {
43    pub enable_log: bool,
44    pub use_pre_bio_enrollment: bool,
45    pub use_pre_credential_management: bool,
46    pub enable_keep_alive_msg: bool,
47    pub keep_alive_msg: String,
48    pub keep_alive_msg_to_stderr: bool,
49}
50
51impl LibCfg {
52    pub fn init() -> Self {
53        LibCfg {
54            // Print CTAP/HID debug traces (hex dumps, etc.) to stdout when true.
55            enable_log: false,
56            // Use pre-FIDO 2.1 bio enrollment command variants when true.
57            use_pre_bio_enrollment: true,
58            // Use pre-FIDO 2.1 credential management command variants when true.
59            use_pre_credential_management: true,
60            // Print a user-facing message while waiting on CTAPHID_KEEPALIVE (user presence).
61            enable_keep_alive_msg: true,
62            // Text shown for keep-alive; empty string disables output even if enable_keep_alive_msg is true.
63            keep_alive_msg: "- Touch the sensor on the authenticator".to_string(),
64            // When true, keep_alive_msg goes to stderr; when false, to stdout.
65            keep_alive_msg_to_stderr: false,
66        }
67    }
68
69    pub fn with_enable_log(mut self, enable: bool) -> Self {
70        self.enable_log = enable;
71        self
72    }
73
74    pub fn with_keep_alive_msg_to_stderr(mut self, to_stderr: bool) -> Self {
75        self.keep_alive_msg_to_stderr = to_stderr;
76        self
77    }
78}
79
80/// Get HID devices
81pub fn get_hid_devices() -> Vec<HidInfo> {
82    hid::get_hid_devices(None)
83}
84
85/// Get HID FIDO devices
86pub fn get_fidokey_devices() -> Vec<HidInfo> {
87    hid::get_hid_devices(Some(0xf1d0))
88}
89
90/// Simple factory to create FidoKeyHid
91pub struct FidoKeyHidFactory {}
92
93impl FidoKeyHidFactory {
94    pub fn create(cfg: &LibCfg) -> Result<FidoKeyHid> {
95        let device = {
96            let mut devs = get_fidokey_devices();
97            if devs.is_empty() {
98                return Err(anyhow!("FIDO device not found."));
99            }
100            if devs.len() > 1 {
101                return Err(anyhow!("Multiple FIDO devices found."));
102            }
103
104            let device = devs.pop().unwrap().param;
105
106            let params = vec![device];
107            FidoKeyHid::new(&params, cfg)?
108        };
109        Ok(device)
110    }
111
112    pub fn create_by_params(params: &[HidParam], cfg: &LibCfg) -> Result<FidoKeyHid> {
113        FidoKeyHid::new(params, cfg)
114    }
115}
116
117//
118// test
119//
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use crate::crypto::digest;
124    use std::convert::TryFrom;
125    use str_buf::StrBuf;
126
127    #[test]
128    fn test_create_pin_auth() {
129        let out_bytes = hex::decode("1A81CD600A1F6CF4BE5260FE3257B241").unwrap();
130        let client_data_hash =
131            hex::decode("E61E2BD6C4612662960B159CD54CF8EFF1A998C89B3742519D11F85E0F5E7876")
132                .unwrap();
133        let check = "F0AC99D6AAD2E199AF9CF25F6568A6F5".to_string();
134        let sig = encrypt::enc_hmac_sha_256::authenticate(&out_bytes, &client_data_hash);
135        let pin_auth = sig[0..16].to_vec();
136        assert_eq!(check, hex::encode(pin_auth).to_uppercase());
137    }
138
139    #[test]
140    fn test_hmac() {
141        let key = b"this is key".to_vec();
142        let message = b"this is message".to_vec();
143
144        let sig = encrypt::enc_hmac_sha_256::authenticate(&key, &message);
145
146        let check = "1BCF27BDA4891AFA5F53CC027B8835564E35A8E3B631AA0F0563299296AD5909".to_string();
147        assert_eq!(check, hex::encode(sig).to_uppercase());
148    }
149
150    #[test]
151    fn test_enc_hmac_sha_256() {
152        let key_str = "this is key.";
153        let hasher = digest::digest(&digest::SHA256, key_str.as_bytes());
154        let key = <[u8; 32]>::try_from(hasher.as_ref()).unwrap();
155
156        let message = "this is message.";
157        let sig = encrypt::enc_hmac_sha_256::authenticate(&key, message.as_bytes());
158        print!("{}", StrBuf::bufh("- hmac signature", &sig));
159        assert_eq!(
160            sig,
161            util::to_str_hex("BF3D3FCFC4462CDCBEBBBC8AF82EA38B7B5ED4259B2061322C57B5CA696D6080")
162        );
163    }
164
165    #[test]
166    fn test_enc_aes256_cbc() {
167        let key_str = "this is key.";
168        let hasher = digest::digest(&digest::SHA256, key_str.as_bytes());
169        let key = <[u8; 32]>::try_from(hasher.as_ref()).unwrap();
170
171        let message = "this is message.";
172        let enc_data = encrypt::enc_aes256_cbc::encrypt_message_str(&key, message);
173        print!("{}", StrBuf::bufh("- enc_data", &enc_data));
174        assert_eq!(
175            enc_data,
176            util::to_str_hex("37455A8392187439EFAA249617AAB5C2")
177        );
178
179        let dec_data = encrypt::enc_aes256_cbc::decrypt_message_str(&key, &enc_data);
180        print!("- dec_data = {}", dec_data);
181        assert_eq!(dec_data, message);
182    }
183}