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