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