|
1 | 1 | use libwebauthn::proto::ctap2::Ctap2COSEAlgorithmIdentifier; |
2 | | -use ring::{ |
3 | | - rand::SystemRandom, |
4 | | - signature::{ |
5 | | - EcdsaKeyPair, Ed25519KeyPair, KeyPair, RsaKeyPair, ECDSA_P256_SHA256_ASN1_SIGNING, |
6 | | - }, |
7 | | -}; |
8 | 2 | use tracing::debug; |
9 | 3 |
|
10 | 4 | #[derive(Clone, Copy, Debug, PartialEq)] |
11 | 5 | #[repr(i64)] |
12 | 6 | pub(super) enum CoseKeyType { |
13 | | - ES256_P256, |
14 | | - EDDSA_ED25519, |
| 7 | + Es256P256, |
| 8 | + EddsaEd25519, |
15 | 9 | RS256, |
16 | 10 | } |
17 | 11 |
|
18 | | -impl CoseKeyType { |
19 | | - pub fn algorithm(&self) -> CoseKeyAlgorithmIdentifier { |
20 | | - let params: CoseKeyParameters = (*self).into(); |
21 | | - params.algorithm() |
22 | | - } |
23 | | -} |
24 | | - |
25 | | -impl CoseKeyType { |
26 | | - pub fn curve(&self) -> Option<CoseEllipticCurveIdentifier> { |
27 | | - let params: CoseKeyParameters = (*self).into(); |
28 | | - params.curve() |
29 | | - } |
30 | | -} |
31 | | - |
32 | | -pub(super) struct CoseKeyParameters { |
33 | | - alg: CoseKeyAlgorithmIdentifier, |
34 | | - crv: Option<CoseEllipticCurveIdentifier>, |
35 | | -} |
36 | | - |
37 | | -impl CoseKeyParameters { |
38 | | - pub fn algorithm(&self) -> CoseKeyAlgorithmIdentifier { |
39 | | - self.alg |
40 | | - } |
41 | | - |
42 | | - pub fn curve(&self) -> Option<CoseEllipticCurveIdentifier> { |
43 | | - self.crv |
44 | | - } |
45 | | -} |
46 | | - |
47 | | -impl From<CoseKeyType> for CoseKeyParameters { |
48 | | - fn from(value: CoseKeyType) -> Self { |
49 | | - match value { |
50 | | - CoseKeyType::ES256_P256 => CoseKeyParameters { |
51 | | - alg: CoseKeyAlgorithmIdentifier::ES256, |
52 | | - crv: Some(CoseEllipticCurveIdentifier::P256), |
53 | | - }, |
54 | | - CoseKeyType::EDDSA_ED25519 => CoseKeyParameters { |
55 | | - alg: CoseKeyAlgorithmIdentifier::EdDSA, |
56 | | - crv: Some(CoseEllipticCurveIdentifier::Ed25519), |
57 | | - }, |
58 | | - CoseKeyType::RS256 => CoseKeyParameters { |
59 | | - alg: CoseKeyAlgorithmIdentifier::RS256, |
60 | | - crv: None, |
61 | | - }, |
62 | | - } |
63 | | - } |
64 | | -} |
65 | | - |
66 | 12 | #[derive(Clone, Copy, Debug, PartialEq)] |
67 | 13 | pub enum CoseKeyAlgorithmIdentifier { |
68 | 14 | ES256, |
@@ -99,7 +45,7 @@ impl TryFrom<Ctap2COSEAlgorithmIdentifier> for CoseKeyAlgorithmIdentifier { |
99 | 45 | Ctap2COSEAlgorithmIdentifier::ES256 => Ok(CoseKeyAlgorithmIdentifier::ES256), |
100 | 46 | Ctap2COSEAlgorithmIdentifier::TOPT => { |
101 | 47 | debug!("Unknown public key algorithm type: {:?}", value); |
102 | | - return Err(Error::Unsupported); |
| 48 | + Err(Error::Unsupported) |
103 | 49 | } |
104 | 50 | } |
105 | 51 | } |
@@ -133,99 +79,3 @@ pub enum Error { |
133 | 79 | InvalidKey, |
134 | 80 | Unsupported, |
135 | 81 | } |
136 | | - |
137 | | -pub(super) fn encode_pkcs8_key(key_type: CoseKeyType, pkcs8_key: &[u8]) -> Result<Vec<u8>, Error> { |
138 | | - match key_type { |
139 | | - CoseKeyType::ES256_P256 => { |
140 | | - let key_pair = EcdsaKeyPair::from_pkcs8( |
141 | | - &ECDSA_P256_SHA256_ASN1_SIGNING, |
142 | | - pkcs8_key, |
143 | | - &SystemRandom::new(), |
144 | | - ) |
145 | | - .unwrap(); |
146 | | - let public_key = key_pair.public_key().as_ref(); |
147 | | - // ring outputs public keys with uncompressed 32-byte x and y coordinates |
148 | | - if public_key.len() != 65 || public_key[0] != 0x04 { |
149 | | - return Err(Error::InvalidKey); |
150 | | - } |
151 | | - let (x, y) = public_key[1..].split_at(32); |
152 | | - let mut cose_key: Vec<u8> = Vec::new(); |
153 | | - cose_key.push(0b101_00101); // map with 5 items |
154 | | - cose_key.extend([0b000_00001, 0b000_00010]); // kty (1): EC2 (2) |
155 | | - cose_key.extend([0b000_00011, 0b001_00110]); // alg (3): ECDSA-SHA256 (-7) |
156 | | - cose_key.extend([0b001_00000, 0b000_00001]); // crv (-1): P256 (1) |
157 | | - cose_key.extend([0b001_00001, 0b010_11000, 0b0010_0000]); // x (-2): <32-byte string> |
158 | | - cose_key.extend(x); |
159 | | - cose_key.extend([0b001_00010, 0b010_11000, 0b0010_0000]); // y (-3): <32-byte string> |
160 | | - cose_key.extend(y); |
161 | | - Ok(cose_key) |
162 | | - } |
163 | | - CoseKeyType::EDDSA_ED25519 => { |
164 | | - let key_pair = Ed25519KeyPair::from_pkcs8(pkcs8_key).map_err(|_| Error::InvalidKey)?; |
165 | | - let public_key = key_pair.public_key().as_ref(); |
166 | | - let mut cose_key: Vec<u8> = Vec::new(); |
167 | | - cose_key.push(0b101_00100); // map with 4 items |
168 | | - cose_key.extend([0b000_00001, 0b000_00001]); // kty (1): OKP (1) |
169 | | - cose_key.extend([0b000_00011, 0b001_00111]); // alg (3): EdDSA (-8) |
170 | | - cose_key.extend([0b001_00000, 0b000_00110]); // crv (-1): ED25519 (6) |
171 | | - cose_key.extend([0b001_00001, 0b010_11000, 0b0010_0000]); // x (-2): <32-byte string> |
172 | | - cose_key.extend(public_key); |
173 | | - Ok(cose_key) |
174 | | - } |
175 | | - CoseKeyType::RS256 => { |
176 | | - let key_pair = RsaKeyPair::from_pkcs8(pkcs8_key).map_err(|_| Error::InvalidKey)?; |
177 | | - let public_key = key_pair.public_key().as_ref(); |
178 | | - // TODO: This is ASN.1 with DER encoding. We could parse this to extract |
179 | | - // the modulus and exponent properly, but the key length will |
180 | | - // probably not change, so we're winging it |
181 | | - // https://stackoverflow.com/a/12750816/11931787 |
182 | | - let n = &public_key[9..(9 + 256)]; |
183 | | - let e = &public_key[public_key.len() - 3..]; |
184 | | - debug_assert_eq!(n.len(), key_pair.public().modulus_len()); |
185 | | - let mut cose_key: Vec<u8> = Vec::new(); |
186 | | - cose_key.push(0b101_00100); // map with 4 items |
187 | | - cose_key.extend([0b000_00001, 0b000_00010]); // kty (1): RSA (3) |
188 | | - cose_key.extend([0b000_00011, 0b001_00110]); // alg (3): RSASSA-PKCS1-v1_5 using SHA-256 (-257) |
189 | | - cose_key.extend([0b001_00000, 0b010_11001, 0b0000_0001, 0b0000_0000]); // n (-1): <256-byte string> |
190 | | - cose_key.extend(n); |
191 | | - cose_key.extend([0b001_00001, 0b010_00011]); // e (-2): <3-byte string> |
192 | | - cose_key.extend(e); |
193 | | - Ok(cose_key) |
194 | | - } |
195 | | - _ => todo!(), |
196 | | - } |
197 | | -} |
198 | | - |
199 | | -/// returns CTAP2-serialized public key and algorithm |
200 | | -pub(crate) fn encode_cose_key(public_key: &cosey::PublicKey) -> Result<Vec<u8>, Error> { |
201 | | - match public_key { |
202 | | - cosey::PublicKey::P256Key(p256_key) => { |
203 | | - let mut cose_key: Vec<u8> = Vec::new(); |
204 | | - cose_key.push(0b101_00101); // map with 5 items |
205 | | - cose_key.extend([0b000_00001, 0b000_00010]); // kty (1): EC2 (2) |
206 | | - cose_key.extend([0b000_00011, 0b001_00110]); // alg (3): ECDSA-SHA256 (-7) |
207 | | - cose_key.extend([0b001_00000, 0b000_00001]); // crv (-1): P256 (1) |
208 | | - cose_key.extend([0b001_00001, 0b010_11000, 0b0010_0000]); // x (-2): <32-byte string> |
209 | | - cose_key.extend(p256_key.x.clone()); |
210 | | - cose_key.extend([0b001_00010, 0b010_11000, 0b0010_0000]); // y (-3): <32-byte string> |
211 | | - cose_key.extend(p256_key.y.clone()); |
212 | | - Ok(cose_key) |
213 | | - } |
214 | | - cosey::PublicKey::Ed25519Key(ed25519_key) => { |
215 | | - // TODO: Check this |
216 | | - let mut cose_key: Vec<u8> = Vec::new(); |
217 | | - cose_key.push(0b101_00100); // map with 4 items |
218 | | - cose_key.extend([0b000_00001, 0b000_00001]); // kty (1): OKP (1) |
219 | | - cose_key.extend([0b000_00011, 0b001_00111]); // alg (3): EdDSA (-8) |
220 | | - cose_key.extend([0b001_00000, 0b000_00110]); // crv (-1): ED25519 (6) |
221 | | - cose_key.extend([0b001_00001, 0b010_11000, 0b0010_0000]); // x (-2): <32-byte string> |
222 | | - cose_key.extend(ed25519_key.x.clone()); |
223 | | - Ok(cose_key) |
224 | | - } |
225 | | - |
226 | | - _ => { |
227 | | - debug!("Cannot serialize unknown key type {:?}", public_key); |
228 | | - Err(Error::Unsupported) |
229 | | - } |
230 | | - } |
231 | | -} |
0 commit comments