bitwarden_fido/
crypto.rs

1use coset::{iana, CoseKey};
2use p256::{
3    pkcs8::{DecodePrivateKey, EncodePrivateKey},
4    SecretKey,
5};
6use passkey::authenticator::{private_key_from_cose_key, CoseKeyPair};
7use thiserror::Error;
8
9#[derive(Debug, Error)]
10pub enum CoseKeyToPkcs8Error {
11    #[error("Failed to extract private key from cose_key")]
12    FailedToExtractPrivateKeyFromCoseKey,
13    #[error("Failed to convert P256 private key to PKC8")]
14    FailedToConvertP256PrivateKeyToPkcs8,
15}
16
17pub(crate) fn cose_key_to_pkcs8(cose_key: &CoseKey) -> Result<Vec<u8>, CoseKeyToPkcs8Error> {
18    // cose_key.
19    let secret_key = private_key_from_cose_key(cose_key).map_err(|error| {
20        log::error!("Failed to extract private key from cose_key: {:?}", error);
21        CoseKeyToPkcs8Error::FailedToExtractPrivateKeyFromCoseKey
22    })?;
23
24    let vec = secret_key
25        .to_pkcs8_der()
26        .map_err(|error| {
27            log::error!("Failed to convert P256 private key to PKC8: {:?}", error);
28            CoseKeyToPkcs8Error::FailedToConvertP256PrivateKeyToPkcs8
29        })?
30        .as_bytes()
31        .to_vec();
32
33    Ok(vec)
34}
35
36#[derive(Debug, Error)]
37#[error("Failed to extract private key from secret_key")]
38pub struct PrivateKeyFromSecretKeyError;
39
40pub fn pkcs8_to_cose_key(secret_key: &[u8]) -> Result<CoseKey, PrivateKeyFromSecretKeyError> {
41    let secret_key = SecretKey::from_pkcs8_der(secret_key).map_err(|error| {
42        log::error!("Failed to extract private key from secret_key: {:?}", error);
43        PrivateKeyFromSecretKeyError
44    })?;
45
46    let cose_key_pair = CoseKeyPair::from_secret_key(&secret_key, iana::Algorithm::ES256);
47    Ok(cose_key_pair.private)
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    fn private_key_for_testing() -> CoseKey {
55        // Hardcoded CoseKey for testing purposes
56        let bytes = vec![
57            166, 1, 2, 3, 38, 32, 1, 33, 88, 32, 200, 30, 161, 146, 196, 121, 165, 149, 92, 232,
58            49, 48, 245, 253, 73, 234, 204, 3, 209, 153, 166, 77, 59, 232, 70, 16, 206, 77, 84,
59            156, 28, 77, 34, 88, 32, 82, 141, 165, 28, 241, 82, 31, 33, 183, 206, 29, 91, 93, 111,
60            216, 216, 26, 62, 211, 49, 191, 86, 238, 118, 241, 124, 131, 106, 214, 95, 170, 160,
61            35, 88, 32, 147, 171, 4, 49, 68, 170, 47, 51, 74, 211, 94, 40, 212, 244, 95, 55, 154,
62            92, 171, 241, 0, 55, 84, 151, 79, 244, 151, 198, 135, 45, 97, 238,
63        ];
64
65        <CoseKey as coset::CborSerializable>::from_slice(bytes.as_slice()).unwrap()
66    }
67
68    #[test]
69    fn test_cose_key_to_pkcs8_and_back() {
70        let cose_key = private_key_for_testing();
71
72        let pkcs8 = cose_key_to_pkcs8(&cose_key).expect("CoseKey to PKCS8 failed");
73        let cose_key2 = pkcs8_to_cose_key(&pkcs8).expect("PKCS8 to CoseKey failed");
74
75        assert_eq!(cose_key, cose_key2);
76    }
77
78    fn pkcs8_key_for_testing() -> Vec<u8> {
79        vec![
80            0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce,
81            0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x04,
82            0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20, 0x06, 0x76, 0x5e, 0x85, 0xe0, 0x7f,
83            0xef, 0x43, 0xaa, 0x17, 0xe0, 0x7a, 0xd7, 0x85, 0x63, 0x01, 0x80, 0x70, 0x8c, 0x6c,
84            0x61, 0x43, 0x7d, 0xc3, 0xb1, 0xe6, 0xf9, 0x09, 0x24, 0xeb, 0x1f, 0xf5, 0xa1, 0x44,
85            0x03, 0x42, 0x00, 0x04, 0x35, 0x9a, 0x52, 0xf3, 0x82, 0x44, 0x66, 0x5f, 0x3f, 0xe2,
86            0xc4, 0x0b, 0x1c, 0x16, 0x34, 0xc5, 0x60, 0x07, 0x3a, 0x25, 0xfe, 0x7e, 0x7f, 0x7f,
87            0xda, 0xd4, 0x1c, 0x36, 0x90, 0x00, 0xee, 0xb1, 0x8e, 0x92, 0xb3, 0xac, 0x91, 0x7f,
88            0xb1, 0x8c, 0xa4, 0x85, 0xe7, 0x03, 0x07, 0xd1, 0xf5, 0x5b, 0xd3, 0x7b, 0xc3, 0x56,
89            0x11, 0xdf, 0xbc, 0x7a, 0x97, 0x70, 0x32, 0x4b, 0x3c, 0x84, 0x05, 0x71,
90        ]
91    }
92
93    #[test]
94    fn test_pkcs8_to_cose_key_and_back() {
95        let pkcs8 = pkcs8_key_for_testing();
96
97        let cose_key = pkcs8_to_cose_key(&pkcs8).expect("PKCS8 to CoseKey failed");
98        let pkcs8_2 = cose_key_to_pkcs8(&cose_key).expect("CoseKey to PKCS8 failed");
99
100        assert_eq!(pkcs8, pkcs8_2);
101    }
102}