xref: /aosp_15_r20/tools/security/remote_provisioning/hwtrust/src/cbor/publickey.rs (revision d9ecfb0f4d734c9ce41cde8ac4d585b094fd4222)
1 //! CBOR encoding and decoding of a [`PublicKey`].
2 
3 use crate::publickey::{EcKind, KeyAgreementPublicKey, PublicKey, SignatureKind};
4 use anyhow::{anyhow, bail, ensure, Context, Result};
5 use coset::cbor::value::Value;
6 use coset::iana::{self, EnumI64};
7 use coset::{Algorithm, CoseKey, CoseKeyBuilder, CoseSign1, KeyOperation, KeyType, Label};
8 use openssl::bn::{BigNum, BigNumContext};
9 use openssl::ec::{EcGroup, EcKey};
10 use openssl::ecdsa::EcdsaSig;
11 use openssl::nid::Nid;
12 use openssl::pkey::{Id, PKey, Public};
13 use std::collections::HashSet;
14 
15 impl PublicKey {
16     /// Create a public key from a [`CoseKey`].
from_cose_key(cose_key: &CoseKey) -> Result<Self>17     pub fn from_cose_key(cose_key: &CoseKey) -> Result<Self> {
18         if !cose_key.key_ops.is_empty() {
19             ensure!(cose_key.key_ops.contains(&KeyOperation::Assigned(iana::KeyOperation::Verify)));
20         }
21         let pkey = to_pkey(cose_key)?;
22         pkey.try_into().context("Making PublicKey from PKey")
23     }
24 
25     /// Verifies a COSE_Sign1 signature over its message. This function handles the conversion of
26     /// the signature format that is needed for some algorithms.
verify_cose_sign1(&self, sign1: &CoseSign1, aad: &[u8]) -> Result<()>27     pub fn verify_cose_sign1(&self, sign1: &CoseSign1, aad: &[u8]) -> Result<()> {
28         ensure!(sign1.protected.header.crit.is_empty(), "No critical headers allowed");
29         ensure!(
30             sign1.protected.header.alg == Some(Algorithm::Assigned(iana_algorithm(self.kind()))),
31             "Algorithm mistmatch in protected header. \
32              Signature - protected.header.alg: {:?}, Key - kind: {:?}",
33             sign1.protected.header.alg,
34             iana_algorithm(self.kind())
35         );
36         sign1.verify_signature(aad, |signature, message| match self.kind() {
37             SignatureKind::Ec(_) => {
38                 let der =
39                     ec_cose_signature_to_der(self.kind(), signature).context("Signature to DER")?;
40                 self.verify(&der, message)
41             }
42             _ => self.verify(signature, message),
43         })
44     }
45 
46     /// Convert the public key into a [`CoseKey`].
to_cose_key(&self) -> Result<CoseKey>47     pub fn to_cose_key(&self) -> Result<CoseKey> {
48         let builder = match self.kind() {
49             SignatureKind::Ed25519 => {
50                 let label_crv = iana::OkpKeyParameter::Crv.to_i64();
51                 let label_x = iana::OkpKeyParameter::X.to_i64();
52                 let x = self.pkey().raw_public_key().context("Get ed25519 raw public key")?;
53                 CoseKeyBuilder::new_okp_key()
54                     .param(label_crv, Value::from(iana::EllipticCurve::Ed25519.to_i64()))
55                     .param(label_x, Value::from(x))
56             }
57             SignatureKind::Ec(ec) => {
58                 let key = self.pkey().ec_key().unwrap();
59                 let group = key.group();
60                 let mut ctx = BigNumContext::new().context("Failed to create bignum context")?;
61                 let mut x = BigNum::new().context("Failed to create x coord")?;
62                 let mut y = BigNum::new().context("Failed to create y coord")?;
63                 key.public_key()
64                     .affine_coordinates_gfp(group, &mut x, &mut y, &mut ctx)
65                     .context("Get EC coordinates")?;
66                 let (crv, coord_len) = match ec {
67                     EcKind::P256 => (iana::EllipticCurve::P_256, 32),
68                     EcKind::P384 => (iana::EllipticCurve::P_384, 48),
69                 };
70 
71                 let x = adjust_coord(x.to_vec(), coord_len)?;
72                 let y = adjust_coord(y.to_vec(), coord_len)?;
73                 CoseKeyBuilder::new_ec2_pub_key(crv, x, y)
74             }
75         };
76         Ok(builder
77             .algorithm(iana_algorithm(self.kind()))
78             .add_key_op(iana::KeyOperation::Verify)
79             .build())
80     }
81 }
82 
83 impl KeyAgreementPublicKey {
from_cose_key(cose_key: &CoseKey) -> Result<Self>84     pub(super) fn from_cose_key(cose_key: &CoseKey) -> Result<Self> {
85         // RFC 9052 says the key_op for derive key is only for private keys. The public key
86         // has no key_ops (see Appendix B for an example).
87         ensure!(cose_key.key_ops.is_empty());
88         let pkey = to_pkey(cose_key)?;
89         pkey.try_into().context("Making KeyAgreementPublicKey from PKey")
90     }
91 }
92 
to_pkey(cose_key: &CoseKey) -> Result<PKey<Public>>93 fn to_pkey(cose_key: &CoseKey) -> Result<PKey<Public>> {
94     match cose_key.kty {
95         KeyType::Assigned(iana::KeyType::OKP) => Ok(pkey_from_okp_key(cose_key)?),
96         KeyType::Assigned(iana::KeyType::EC2) => Ok(pkey_from_ec2_key(cose_key)?),
97         _ => bail!("Unexpected KeyType value: {:?}", cose_key.kty),
98     }
99 }
100 
adjust_coord(mut coordinate: Vec<u8>, length: usize) -> Result<Vec<u8>>101 fn adjust_coord(mut coordinate: Vec<u8>, length: usize) -> Result<Vec<u8>> {
102     // Use loops "just in case". However we should never see a coordinate with more than one
103     // extra leading byte. The chances of more than one trailing byte is also quite small --
104     // roughly 1/65000.
105     while coordinate.len() > length {
106         ensure!(coordinate[0] == 0, "unexpected non-zero leading byte on public key");
107         coordinate.remove(0);
108     }
109 
110     while coordinate.len() < length {
111         coordinate.insert(0, 0);
112     }
113 
114     Ok(coordinate)
115 }
116 
pkey_from_okp_key(cose_key: &CoseKey) -> Result<PKey<Public>>117 fn pkey_from_okp_key(cose_key: &CoseKey) -> Result<PKey<Public>> {
118     ensure!(cose_key.kty == KeyType::Assigned(iana::KeyType::OKP));
119     ensure!(
120         cose_key.alg == Some(Algorithm::Assigned(iana::Algorithm::EdDSA))
121             || cose_key.alg == Some(Algorithm::Assigned(iana::Algorithm::ECDH_ES_HKDF_256))
122     );
123     ensure_no_disallowed_labels(cose_key)?;
124     let crv = get_label_value(cose_key, Label::Int(iana::OkpKeyParameter::Crv.to_i64()))?;
125     let x = get_label_value_as_bytes(cose_key, Label::Int(iana::OkpKeyParameter::X.to_i64()))?;
126     let curve_id = if crv == &Value::from(iana::EllipticCurve::Ed25519.to_i64()) {
127         Id::ED25519
128     } else if crv == &Value::from(iana::EllipticCurve::X25519.to_i64()) {
129         Id::X25519
130     } else {
131         bail!("Given crv is invalid: '{crv:?}'");
132     };
133     PKey::public_key_from_raw_bytes(x, curve_id).context("Failed to instantiate key")
134 }
135 
pkey_from_ec2_key(cose_key: &CoseKey) -> Result<PKey<Public>>136 fn pkey_from_ec2_key(cose_key: &CoseKey) -> Result<PKey<Public>> {
137     ensure!(cose_key.kty == KeyType::Assigned(iana::KeyType::EC2));
138     ensure_no_disallowed_labels(cose_key)?;
139     let crv = get_label_value(cose_key, Label::Int(iana::Ec2KeyParameter::Crv.to_i64()))?;
140     let x = get_label_value_as_bytes(cose_key, Label::Int(iana::Ec2KeyParameter::X.to_i64()))?;
141     let y = get_label_value_as_bytes(cose_key, Label::Int(iana::Ec2KeyParameter::Y.to_i64()))?;
142     match cose_key.alg {
143         Some(Algorithm::Assigned(iana::Algorithm::ES256))
144         | Some(Algorithm::Assigned(iana::Algorithm::ECDH_ES_HKDF_256)) => {
145             ensure!(crv == &Value::from(iana::EllipticCurve::P_256.to_i64()));
146             pkey_from_ec_coords(Nid::X9_62_PRIME256V1, x, y).context("Failed to instantiate key")
147         }
148         Some(Algorithm::Assigned(iana::Algorithm::ES384)) => {
149             ensure!(crv == &Value::from(iana::EllipticCurve::P_384.to_i64()));
150             pkey_from_ec_coords(Nid::SECP384R1, x, y).context("Failed to instantiate key")
151         }
152         _ => bail!("Need to specify ES256 or ES384 key algorithm. Got {:?}", cose_key.alg),
153     }
154 }
155 
pkey_from_ec_coords(nid: Nid, x: &[u8], y: &[u8]) -> Result<PKey<Public>>156 fn pkey_from_ec_coords(nid: Nid, x: &[u8], y: &[u8]) -> Result<PKey<Public>> {
157     let group = EcGroup::from_curve_name(nid).context("Failed to construct curve group")?;
158     let x = BigNum::from_slice(x).context("Failed to create x coord")?;
159     let y = BigNum::from_slice(y).context("Failed to create y coord")?;
160     let key = EcKey::from_public_key_affine_coordinates(&group, &x, &y)
161         .context("Failed to create EC public key")?;
162     PKey::from_ec_key(key).context("Failed to create PKey")
163 }
164 
ensure_no_disallowed_labels(cose_key: &CoseKey) -> Result<()>165 fn ensure_no_disallowed_labels(cose_key: &CoseKey) -> Result<()> {
166     let allow_list = match cose_key.kty {
167         KeyType::Assigned(iana::KeyType::EC2) => HashSet::from([
168             iana::Ec2KeyParameter::Crv.to_i64(),
169             iana::Ec2KeyParameter::X.to_i64(),
170             iana::Ec2KeyParameter::Y.to_i64(),
171         ]),
172         KeyType::Assigned(iana::KeyType::OKP) => {
173             HashSet::from([iana::OkpKeyParameter::Crv.to_i64(), iana::OkpKeyParameter::X.to_i64()])
174         }
175         _ => bail!("Invalid key type in COSE key"),
176     };
177 
178     let params = cose_key.params.clone();
179     let disallowed: Vec<(Label, String)> = params
180         .into_iter()
181         .filter(|(label, _)| match label {
182             Label::Int(int) => !allow_list.contains(int),
183             Label::Text(_) => true,
184         })
185         .map(|(label, value)| -> (Label, String) {
186             let string = match value.as_bytes() {
187                 Some(bytes) => hex::encode(bytes),
188                 None => String::from("Expected Bytes, got {value:?}"),
189             };
190             (label, string)
191         })
192         .collect();
193 
194     ensure!(disallowed.is_empty(), "disallowed labels should be empty: {:?}", disallowed);
195 
196     Ok(())
197 }
198 
199 /// Get the value corresponding to the provided label within the supplied CoseKey or error if it's
200 /// not present.
get_label_value(key: &CoseKey, label: Label) -> Result<&Value>201 fn get_label_value(key: &CoseKey, label: Label) -> Result<&Value> {
202     Ok(&key
203         .params
204         .iter()
205         .find(|(k, _)| k == &label)
206         .ok_or_else(|| anyhow!("Label {:?} not found", label))?
207         .1)
208 }
209 
210 /// Get the byte string for the corresponding label within the key if the label exists and the
211 /// value is actually a byte array.
get_label_value_as_bytes(key: &CoseKey, label: Label) -> Result<&[u8]>212 fn get_label_value_as_bytes(key: &CoseKey, label: Label) -> Result<&[u8]> {
213     get_label_value(key, label)?
214         .as_bytes()
215         .ok_or_else(|| anyhow!("Value not a bstr."))
216         .map(Vec::as_slice)
217 }
218 
ec_cose_signature_to_der(kind: SignatureKind, signature: &[u8]) -> Result<Vec<u8>>219 fn ec_cose_signature_to_der(kind: SignatureKind, signature: &[u8]) -> Result<Vec<u8>> {
220     let coord_len = ec_coord_len(kind);
221     ensure!(signature.len() == coord_len * 2, "Unexpected signature length");
222     let r = BigNum::from_slice(&signature[..coord_len]).context("Creating BigNum for r")?;
223     let s = BigNum::from_slice(&signature[coord_len..]).context("Creating BigNum for s")?;
224     let signature = EcdsaSig::from_private_components(r, s).context("Creating ECDSA signature")?;
225     signature.to_der().context("Failed to DER encode signature")
226 }
227 
ec_coord_len(kind: SignatureKind) -> usize228 fn ec_coord_len(kind: SignatureKind) -> usize {
229     match kind {
230         SignatureKind::Ec(kind) => match kind {
231             EcKind::P256 => 32,
232             EcKind::P384 => 48,
233         },
234         SignatureKind::Ed25519 => 32,
235     }
236 }
237 
iana_algorithm(kind: SignatureKind) -> iana::Algorithm238 fn iana_algorithm(kind: SignatureKind) -> iana::Algorithm {
239     match kind {
240         SignatureKind::Ed25519 => iana::Algorithm::EdDSA,
241         SignatureKind::Ec(EcKind::P256) => iana::Algorithm::ES256,
242         SignatureKind::Ec(EcKind::P384) => iana::Algorithm::ES384,
243     }
244 }
245 
246 #[cfg(test)]
247 mod tests {
248     use super::*;
249     use crate::publickey::testkeys::{
250         PrivateKey, EC2_KEY_WITH_HIGH_BITS_SET_PEM, EC2_KEY_WITH_LEADING_ZEROS_PEM,
251         ED25519_KEY_PEM, ED25519_KEY_WITH_LEADING_ZEROS_PEM, P256_KEY_PEM,
252     };
253     use coset::{CoseSign1Builder, HeaderBuilder};
254     use std::collections::HashSet;
255 
256     impl PrivateKey {
sign_cose_sign1(&self, payload: Vec<u8>) -> CoseSign1257         pub(in crate::cbor) fn sign_cose_sign1(&self, payload: Vec<u8>) -> CoseSign1 {
258             CoseSign1Builder::new()
259                 .protected(HeaderBuilder::new().algorithm(iana_algorithm(self.kind())).build())
260                 .payload(payload)
261                 .create_signature(b"", |m| {
262                     let signature = self.sign(m).unwrap();
263                     match self.kind() {
264                         SignatureKind::Ec(_) => ec_der_signature_to_cose(self.kind(), &signature),
265                         _ => signature,
266                     }
267                 })
268                 .build()
269         }
270     }
271 
ec_der_signature_to_cose(kind: SignatureKind, signature: &[u8]) -> Vec<u8>272     fn ec_der_signature_to_cose(kind: SignatureKind, signature: &[u8]) -> Vec<u8> {
273         let coord_len = ec_coord_len(kind);
274         let signature = EcdsaSig::from_der(signature).unwrap();
275         let mut r = signature.r().to_vec_padded(coord_len.try_into().unwrap()).unwrap();
276         let mut s = signature.s().to_vec_padded(coord_len.try_into().unwrap()).unwrap();
277         r.append(&mut s);
278         r
279     }
280 
sign_and_verify(pem: &str)281     fn sign_and_verify(pem: &str) {
282         let key = PrivateKey::from_pem(pem);
283         let sign1 = key.sign_cose_sign1(b"signed payload".to_vec());
284         key.public_key().verify_cose_sign1(&sign1, b"").unwrap();
285     }
286 
287     #[test]
sign_and_verify_okp()288     fn sign_and_verify_okp() {
289         sign_and_verify(ED25519_KEY_PEM[0])
290     }
291 
292     #[test]
sign_and_verify_ec2()293     fn sign_and_verify_ec2() {
294         sign_and_verify(P256_KEY_PEM[0])
295     }
296 
297     #[test]
verify_cose_sign1()298     fn verify_cose_sign1() {
299         let key = PrivateKey::from_pem(ED25519_KEY_PEM[0]);
300         let sign1 = CoseSign1Builder::new()
301             .protected(HeaderBuilder::new().algorithm(iana::Algorithm::EdDSA).build())
302             .payload(b"the message".to_vec())
303             .create_signature(b"the aad", |m| key.sign(m).unwrap())
304             .build();
305         key.public_key().verify_cose_sign1(&sign1, b"the aad").unwrap();
306     }
307 
308     #[test]
verify_cose_sign1_fails_with_wrong_aad()309     fn verify_cose_sign1_fails_with_wrong_aad() {
310         let key = PrivateKey::from_pem(ED25519_KEY_PEM[0]);
311         let sign1 = CoseSign1Builder::new()
312             .protected(HeaderBuilder::new().algorithm(iana::Algorithm::ES256).build())
313             .payload(b"the message".to_vec())
314             .create_signature(b"correct aad", |m| key.sign(m).unwrap())
315             .build();
316         key.public_key().verify_cose_sign1(&sign1, b"bad").unwrap_err();
317     }
318 
319     #[test]
verify_cose_sign1_fails_with_wrong_algorithm()320     fn verify_cose_sign1_fails_with_wrong_algorithm() {
321         let key = PrivateKey::from_pem(ED25519_KEY_PEM[0]);
322         let sign1 = CoseSign1Builder::new()
323             .protected(HeaderBuilder::new().algorithm(iana::Algorithm::ES256).build())
324             .payload(b"the message".to_vec())
325             .create_signature(b"", |m| key.sign(m).unwrap())
326             .build();
327         key.public_key().verify_cose_sign1(&sign1, b"").unwrap_err();
328     }
329 
330     #[test]
verify_cose_sign1_with_non_crit_header()331     fn verify_cose_sign1_with_non_crit_header() {
332         let key = PrivateKey::from_pem(ED25519_KEY_PEM[0]);
333         let sign1 = CoseSign1Builder::new()
334             .protected(
335                 HeaderBuilder::new()
336                     .algorithm(iana::Algorithm::EdDSA)
337                     .value(1000, Value::from(2000))
338                     .build(),
339             )
340             .payload(b"the message".to_vec())
341             .create_signature(b"", |m| key.sign(m).unwrap())
342             .build();
343         key.public_key().verify_cose_sign1(&sign1, b"").unwrap()
344     }
345 
346     #[test]
verify_cose_sign1_fails_with_crit_header()347     fn verify_cose_sign1_fails_with_crit_header() {
348         let key = PrivateKey::from_pem(ED25519_KEY_PEM[0]);
349         let sign1 = CoseSign1Builder::new()
350             .protected(
351                 HeaderBuilder::new()
352                     .algorithm(iana::Algorithm::EdDSA)
353                     .add_critical(iana::HeaderParameter::Alg)
354                     .build(),
355             )
356             .payload(b"the message".to_vec())
357             .create_signature(b"", |m| key.sign(m).unwrap())
358             .build();
359         key.public_key().verify_cose_sign1(&sign1, b"").unwrap_err();
360     }
361 
to_and_from_cose_key(pem: &str)362     fn to_and_from_cose_key(pem: &str) {
363         let key = PrivateKey::from_pem(pem).public_key();
364         let value = key.to_cose_key().unwrap();
365         let new_key = PublicKey::from_cose_key(&value).unwrap();
366         assert!(key.pkey().public_eq(new_key.pkey()));
367     }
368     #[test]
to_and_from_okp_cose_key()369     fn to_and_from_okp_cose_key() {
370         to_and_from_cose_key(ED25519_KEY_PEM[0]);
371     }
372 
373     #[test]
to_and_from_ec2_cose_key()374     fn to_and_from_ec2_cose_key() {
375         to_and_from_cose_key(P256_KEY_PEM[0]);
376     }
377 
378     #[test]
from_ed25519_pkey_with_leading_zeros()379     fn from_ed25519_pkey_with_leading_zeros() {
380         for pem in ED25519_KEY_WITH_LEADING_ZEROS_PEM {
381             let key = PrivateKey::from_pem(pem).public_key();
382             let cose_key = key.to_cose_key().unwrap();
383             let kind = key.kind();
384             assert_eq!(kind, SignatureKind::Ed25519);
385             let expected_size = ec_coord_len(kind);
386             let x =
387                 get_label_value_as_bytes(&cose_key, Label::Int(iana::OkpKeyParameter::X.to_i64()))
388                     .unwrap();
389             assert_eq!(x.len(), expected_size, "X coordinate is the wrong size\n{}", pem);
390             assert_eq!(x[0], 0);
391         }
392     }
393 
check_coordinate_lengths_and_first_byte( pems: &[&str], first_byte_check: fn(&[u8], &[u8]) -> bool, )394     fn check_coordinate_lengths_and_first_byte(
395         pems: &[&str],
396         first_byte_check: fn(&[u8], &[u8]) -> bool,
397     ) {
398         let mut curves = HashSet::new();
399         for pem in pems {
400             let key = PrivateKey::from_pem(pem).public_key();
401             let cose_key = key.to_cose_key().unwrap();
402             let kind = key.kind();
403             match kind {
404                 SignatureKind::Ec(inner) => {
405                     curves.insert(inner);
406                 }
407                 SignatureKind::Ed25519 => panic!("signature kind should not be ED25519"),
408             };
409             let expected_size = ec_coord_len(kind);
410             let x =
411                 get_label_value_as_bytes(&cose_key, Label::Int(iana::Ec2KeyParameter::X.to_i64()))
412                     .unwrap();
413             assert_eq!(x.len(), expected_size, "X coordinate is the wrong size\n{}", pem);
414 
415             let y =
416                 get_label_value_as_bytes(&cose_key, Label::Int(iana::Ec2KeyParameter::Y.to_i64()))
417                     .unwrap();
418             assert_eq!(y.len(), expected_size, "Y coordinate is the wrong size\n{}", pem);
419             assert!(first_byte_check(x, y));
420         }
421         assert!(curves.contains(&EcKind::P256));
422         assert!(curves.contains(&EcKind::P384));
423     }
424 
425     #[test]
from_ec2_pkey_with_leading_zeros()426     fn from_ec2_pkey_with_leading_zeros() {
427         fn check(x: &[u8], y: &[u8]) -> bool {
428             x[0] == 0 || y[0] == 0
429         }
430         check_coordinate_lengths_and_first_byte(EC2_KEY_WITH_LEADING_ZEROS_PEM, check)
431     }
432 
433     #[test]
from_ec2_pkey_with_high_bits_set()434     fn from_ec2_pkey_with_high_bits_set() {
435         fn check(x: &[u8], y: &[u8]) -> bool {
436             (x[0] & 0x80 == 0x80) && (y[0] & 0x80 == 0x80)
437         }
438         check_coordinate_lengths_and_first_byte(EC2_KEY_WITH_HIGH_BITS_SET_PEM, check)
439     }
440 }
441