askar_crypto/alg/
p384.rs

1//! Elliptic curve ECDH and ECDSA support on curve secp384r1
2
3use core::convert::TryFrom;
4
5use p384::{
6    ecdsa::{
7        signature::{
8            hazmat::{PrehashSigner, PrehashVerifier},
9            Signer, Verifier,
10        },
11        Signature, SigningKey, VerifyingKey,
12    },
13    elliptic_curve::{
14        self,
15        ecdh::diffie_hellman,
16        sec1::{Coordinates, FromEncodedPoint, ToEncodedPoint},
17    },
18    EncodedPoint, PublicKey, SecretKey,
19};
20use subtle::ConstantTimeEq;
21
22use super::{ec_common, EcCurves, HasKeyAlg, HasKeyBackend, KeyAlg};
23use crate::{
24    buffer::{ArrayKey, WriteBuffer},
25    error::Error,
26    generic_array::typenum::{U48, U49, U97},
27    jwk::{FromJwk, JwkEncoder, JwkParts, ToJwk},
28    kdf::KeyExchange,
29    random::KeyMaterial,
30    repr::{KeyGen, KeyMeta, KeyPublicBytes, KeySecretBytes, KeypairBytes, KeypairMeta},
31    sign::{KeySigVerify, KeySign, SignatureType},
32};
33
34// SECURITY: PublicKey contains a p384::AffinePoint, which is always checked
35// to be on the curve when loaded.
36// The identity point is rejected when converting into a p384::PublicKey.
37// This satisfies 5.6.2.3.4 ECC Partial Public-Key Validation Routine from
38// NIST SP 800-56A: _Recommendation for Pair-Wise Key-Establishment Schemes
39// Using Discrete Logarithm Cryptography_.
40
41/// The length of an ES384 signature
42pub const ES384_SIGNATURE_LENGTH: usize = 96;
43
44/// The length of a compressed public key in bytes
45pub const PUBLIC_KEY_LENGTH: usize = 49;
46/// The length of a secret key
47pub const SECRET_KEY_LENGTH: usize = 48;
48/// The length of a keypair in bytes
49pub const KEYPAIR_LENGTH: usize = SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH;
50
51/// The 'kty' value of an elliptic curve key JWK
52pub const JWK_KEY_TYPE: &str = "EC";
53/// The 'crv' value of a P-384 key JWK
54pub const JWK_CURVE: &str = "P-384";
55
56type FieldSize = elliptic_curve::FieldBytesSize<p384::NistP384>;
57
58/// A P-384 (secp384r1) public key or keypair
59#[derive(Clone, Debug)]
60pub struct P384KeyPair {
61    // SECURITY: SecretKey zeroizes on drop
62    secret: Option<SecretKey>,
63    public: PublicKey,
64}
65
66impl P384KeyPair {
67    #[inline]
68    pub(crate) fn from_secret_key(sk: SecretKey) -> Self {
69        let pk = sk.public_key();
70        Self {
71            secret: Some(sk),
72            public: pk,
73        }
74    }
75
76    pub(crate) fn check_public_bytes(&self, pk: &[u8]) -> Result<(), Error> {
77        if self.with_public_bytes(|slf| slf.ct_eq(pk)).into() {
78            Ok(())
79        } else {
80            Err(err_msg!(InvalidKeyData, "invalid p384 keypair"))
81        }
82    }
83
84    pub(crate) fn to_signing_key(&self) -> Option<SigningKey> {
85        self.secret.clone().map(SigningKey::from)
86    }
87
88    /// Sign a message with the secret key
89    pub fn sign(&self, message: &[u8]) -> Option<[u8; ES384_SIGNATURE_LENGTH]> {
90        if let Some(skey) = self.to_signing_key() {
91            let sig: Signature = skey.sign(message);
92            let mut sigb = [0u8; 96];
93            sigb.copy_from_slice(&sig.to_bytes());
94            Some(sigb)
95        } else {
96            None
97        }
98    }
99
100    /// Sign a pre-hashed message with the secret key
101    pub fn sign_prehashed(&self, hashed_message: &[u8]) -> Option<[u8; ES384_SIGNATURE_LENGTH]> {
102        if let Some(skey) = self.to_signing_key() {
103            if let Ok(sig) = PrehashSigner::<Signature>::sign_prehash(&skey, hashed_message) {
104                let mut sigb = [0u8; 96];
105                sigb.copy_from_slice(&sig.to_bytes());
106                return Some(sigb);
107            }
108        }
109        None
110    }
111
112    /// Verify a signature against the public key
113    pub fn verify_signature(&self, message: &[u8], signature: &[u8]) -> bool {
114        if let Ok(sig) = Signature::try_from(signature) {
115            let vk = VerifyingKey::from(&self.public);
116            vk.verify(message, &sig).is_ok()
117        } else {
118            false
119        }
120    }
121
122    /// Verify a signature on a prehashed message against the public key
123    pub fn verify_signature_prehashed(&self, hashed_message: &[u8], signature: &[u8]) -> bool {
124        if let Ok(sig) = Signature::try_from(signature) {
125            let vk = VerifyingKey::from(&self.public);
126            vk.verify_prehash(hashed_message, &sig).is_ok()
127        } else {
128            false
129        }
130    }
131}
132
133impl HasKeyBackend for P384KeyPair {}
134
135impl HasKeyAlg for P384KeyPair {
136    fn algorithm(&self) -> KeyAlg {
137        KeyAlg::EcCurve(EcCurves::Secp384r1)
138    }
139}
140
141impl KeyMeta for P384KeyPair {
142    type KeySize = U48;
143}
144
145impl KeyGen for P384KeyPair {
146    fn generate(mut rng: impl KeyMaterial) -> Result<Self, Error> {
147        ArrayKey::<FieldSize>::temp(|buf| loop {
148            rng.read_okm(buf);
149            if let Ok(key) = SecretKey::from_bytes(buf) {
150                return Ok(Self::from_secret_key(key));
151            }
152        })
153    }
154}
155
156impl KeySecretBytes for P384KeyPair {
157    fn from_secret_bytes(key: &[u8]) -> Result<Self, Error> {
158        if key.len() == SECRET_KEY_LENGTH {
159            if let Ok(sk) = SecretKey::from_bytes(key.into()) {
160                return Ok(Self::from_secret_key(sk));
161            }
162        }
163        Err(err_msg!(InvalidKeyData))
164    }
165
166    fn with_secret_bytes<O>(&self, f: impl FnOnce(Option<&[u8]>) -> O) -> O {
167        if let Some(sk) = self.secret.as_ref() {
168            ArrayKey::<FieldSize>::temp(|arr| {
169                ec_common::write_sk(sk, &mut arr[..]);
170                f(Some(arr))
171            })
172        } else {
173            f(None)
174        }
175    }
176}
177
178impl KeypairMeta for P384KeyPair {
179    type PublicKeySize = U49;
180    type KeypairSize = U97;
181}
182
183impl KeypairBytes for P384KeyPair {
184    fn from_keypair_bytes(kp: &[u8]) -> Result<Self, Error> {
185        if kp.len() != KEYPAIR_LENGTH {
186            return Err(err_msg!(InvalidKeyData));
187        }
188        let result = P384KeyPair::from_secret_bytes(&kp[..SECRET_KEY_LENGTH])
189            .map_err(|_| err_msg!(InvalidKeyData))?;
190        result.check_public_bytes(&kp[SECRET_KEY_LENGTH..])?;
191        Ok(result)
192    }
193
194    fn with_keypair_bytes<O>(&self, f: impl FnOnce(Option<&[u8]>) -> O) -> O {
195        if let Some(sk) = self.secret.as_ref() {
196            ArrayKey::<<Self as KeypairMeta>::KeypairSize>::temp(|arr| {
197                ec_common::write_sk(sk, &mut arr[..SECRET_KEY_LENGTH]);
198                let pk_enc = self.public.to_encoded_point(true);
199                arr[SECRET_KEY_LENGTH..].copy_from_slice(pk_enc.as_bytes());
200                f(Some(&*arr))
201            })
202        } else {
203            f(None)
204        }
205    }
206}
207
208impl KeyPublicBytes for P384KeyPair {
209    fn from_public_bytes(key: &[u8]) -> Result<Self, Error> {
210        let pk = PublicKey::from_sec1_bytes(key).map_err(|_| err_msg!(InvalidKeyData))?;
211        Ok(Self {
212            secret: None,
213            public: pk,
214        })
215    }
216
217    fn with_public_bytes<O>(&self, f: impl FnOnce(&[u8]) -> O) -> O {
218        f(self.public.to_encoded_point(true).as_bytes())
219    }
220}
221
222impl KeySign for P384KeyPair {
223    fn write_signature(
224        &self,
225        message: &[u8],
226        sig_type: Option<SignatureType>,
227        out: &mut dyn WriteBuffer,
228    ) -> Result<(), Error> {
229        match sig_type {
230            None | Some(SignatureType::ES384) => {
231                if let Some(sig) = self.sign(message) {
232                    out.buffer_write(&sig[..])?;
233                    Ok(())
234                } else {
235                    Err(err_msg!(Unsupported, "Undefined secret key"))
236                }
237            }
238            Some(SignatureType::ES384ph) => {
239                if let Some(sig) = self.sign_prehashed(message) {
240                    out.buffer_write(&sig[..])?;
241                    Ok(())
242                } else {
243                    Err(err_msg!(Unsupported, "Signing operation not supported"))
244                }
245            }
246            #[allow(unreachable_patterns)]
247            _ => Err(err_msg!(Unsupported, "Unsupported signature type")),
248        }
249    }
250}
251
252impl KeySigVerify for P384KeyPair {
253    fn verify_signature(
254        &self,
255        message: &[u8],
256        signature: &[u8],
257        sig_type: Option<SignatureType>,
258    ) -> Result<bool, Error> {
259        match sig_type {
260            None | Some(SignatureType::ES384) => Ok(self.verify_signature(message, signature)),
261            Some(SignatureType::ES384ph) => Ok(self.verify_signature_prehashed(message, signature)),
262            #[allow(unreachable_patterns)]
263            _ => Err(err_msg!(Unsupported, "Unsupported signature type")),
264        }
265    }
266}
267
268impl ToJwk for P384KeyPair {
269    fn encode_jwk(&self, enc: &mut dyn JwkEncoder) -> Result<(), Error> {
270        let pk_enc = self.public.to_encoded_point(false);
271        let (x, y) = match pk_enc.coordinates() {
272            Coordinates::Identity => {
273                return Err(err_msg!(
274                    Unsupported,
275                    "Cannot convert identity point to JWK"
276                ))
277            }
278            Coordinates::Uncompressed { x, y } => (x, y),
279            Coordinates::Compressed { .. } | Coordinates::Compact { .. } => unreachable!(),
280        };
281
282        enc.add_str("crv", JWK_CURVE)?;
283        enc.add_str("kty", JWK_KEY_TYPE)?;
284        enc.add_as_base64("x", &x[..])?;
285        enc.add_as_base64("y", &y[..])?;
286        if enc.is_secret() {
287            self.with_secret_bytes(|buf| {
288                if let Some(sk) = buf {
289                    enc.add_as_base64("d", sk)
290                } else {
291                    Ok(())
292                }
293            })?;
294        }
295        Ok(())
296    }
297}
298
299impl FromJwk for P384KeyPair {
300    fn from_jwk_parts(jwk: JwkParts<'_>) -> Result<Self, Error> {
301        if jwk.kty != JWK_KEY_TYPE {
302            return Err(err_msg!(InvalidKeyData, "Unsupported key type"));
303        }
304        if jwk.crv != JWK_CURVE {
305            return Err(err_msg!(InvalidKeyData, "Unsupported key algorithm"));
306        }
307        let pk_x = ArrayKey::<FieldSize>::try_new_with(|arr| {
308            if jwk.x.decode_base64(arr)? != arr.len() {
309                Err(err_msg!(InvalidKeyData))
310            } else {
311                Ok(())
312            }
313        })?;
314        let pk_y = ArrayKey::<FieldSize>::try_new_with(|arr| {
315            if jwk.y.decode_base64(arr)? != arr.len() {
316                Err(err_msg!(InvalidKeyData))
317            } else {
318                Ok(())
319            }
320        })?;
321        let pk = Option::from(PublicKey::from_encoded_point(
322            &EncodedPoint::from_affine_coordinates(pk_x.as_ref(), pk_y.as_ref(), false),
323        ))
324        .ok_or_else(|| err_msg!(InvalidKeyData))?;
325        if jwk.d.is_some() {
326            ArrayKey::<FieldSize>::temp(|arr| {
327                if jwk.d.decode_base64(arr)? != arr.len() {
328                    Err(err_msg!(InvalidKeyData))
329                } else {
330                    let kp = P384KeyPair::from_secret_bytes(arr)?;
331                    if kp.public != pk {
332                        Err(err_msg!(InvalidKeyData))
333                    } else {
334                        Ok(kp)
335                    }
336                }
337            })
338        } else {
339            Ok(Self {
340                secret: None,
341                public: pk,
342            })
343        }
344    }
345}
346
347impl KeyExchange for P384KeyPair {
348    fn write_key_exchange(&self, other: &Self, out: &mut dyn WriteBuffer) -> Result<(), Error> {
349        match self.secret.as_ref() {
350            Some(sk) => {
351                let xk = diffie_hellman(sk.to_nonzero_scalar(), other.public.as_affine());
352                out.buffer_write(xk.raw_secret_bytes().as_ref())?;
353                Ok(())
354            }
355            None => Err(err_msg!(MissingSecretKey)),
356        }
357    }
358}
359
360#[cfg(test)]
361mod tests {
362    use base64::Engine;
363    use sha2::Digest;
364
365    use super::*;
366    use crate::repr::ToPublicBytes;
367
368    #[test]
369    fn jwk_expected() {
370        // {
371        //   "kty": "EC",
372        //   "x": "p3ZI8DAmxn8BJ3936Y5MHRLXTAg6SxCNhuH6JBEuieuicUY9wqZk8C63SZIj4htA",
373        //   "y": "eqSjvs1X7eI9V2o8sYUpsrj6WUKOymqFtkCxMwWQuDPtZKOHC3fSWkjQvf_73GH-",
374        //   "crv": "P-384",
375        //   "d": "rgFYq-b_toGb-wN3URCk_e-6Sj2PtUvoefF284q9oKnVCi7sglAmCZkOv-2nOAeE"
376        // }
377        let test_pvt_b64 = "rgFYq-b_toGb-wN3URCk_e-6Sj2PtUvoefF284q9oKnVCi7sglAmCZkOv-2nOAeE";
378        let test_pub_b64 = (
379            "p3ZI8DAmxn8BJ3936Y5MHRLXTAg6SxCNhuH6JBEuieuicUY9wqZk8C63SZIj4htA",
380            "eqSjvs1X7eI9V2o8sYUpsrj6WUKOymqFtkCxMwWQuDPtZKOHC3fSWkjQvf_73GH-",
381        );
382        let test_pvt = base64::engine::general_purpose::URL_SAFE_NO_PAD
383            .decode(test_pvt_b64)
384            .unwrap();
385        let sk = P384KeyPair::from_secret_bytes(&test_pvt).expect("Error creating signing key");
386
387        let jwk = sk.to_jwk_public(None).expect("Error converting key to JWK");
388        let jwk = JwkParts::try_from_str(&jwk).expect("Error parsing JWK");
389        assert_eq!(jwk.kty, JWK_KEY_TYPE);
390        assert_eq!(jwk.crv, JWK_CURVE);
391        assert_eq!(jwk.x, test_pub_b64.0);
392        assert_eq!(jwk.y, test_pub_b64.1);
393        assert_eq!(jwk.d, None);
394        let pk_load = P384KeyPair::from_jwk_parts(jwk).unwrap();
395        assert_eq!(sk.to_public_bytes(), pk_load.to_public_bytes());
396
397        let jwk = sk.to_jwk_secret(None).expect("Error converting key to JWK");
398        let jwk = JwkParts::from_slice(&jwk).expect("Error parsing JWK");
399        assert_eq!(jwk.kty, JWK_KEY_TYPE);
400        assert_eq!(jwk.crv, JWK_CURVE);
401        assert_eq!(jwk.x, test_pub_b64.0);
402        assert_eq!(jwk.y, test_pub_b64.1);
403        assert_eq!(jwk.d, test_pvt_b64);
404        let sk_load = P384KeyPair::from_jwk_parts(jwk).unwrap();
405        assert_eq!(
406            sk.to_keypair_bytes().unwrap(),
407            sk_load.to_keypair_bytes().unwrap()
408        );
409    }
410
411    #[test]
412    fn jwk_thumbprint() {
413        let pk = P384KeyPair::from_jwk(
414            r#"{
415                "kty": "EC",
416                "x": "p3ZI8DAmxn8BJ3936Y5MHRLXTAg6SxCNhuH6JBEuieuicUY9wqZk8C63SZIj4htA",
417                "y": "eqSjvs1X7eI9V2o8sYUpsrj6WUKOymqFtkCxMwWQuDPtZKOHC3fSWkjQvf_73GH-",
418                "crv": "P-384"
419            }"#,
420        )
421        .unwrap();
422        assert_eq!(
423            pk.to_jwk_thumbprint(None).unwrap(),
424            "4zlc15_l012-r5pFk7mnEFs6MghkhSAkdMeNeyL00u4"
425        );
426    }
427
428    #[test]
429    fn sign_verify_expected() {
430        let test_msg = b"This is a dummy message for use with tests";
431        let test_sig = &hex!(
432            "acf7e9f0975738d446b26aa1651ad699cac490a496d6f70221126c35d8e4fcc5a28f63f611557be9d4c321d8fa24dbf2
433             846e3bcbea2e45eff577974664b1e98fffdad8ddbe7bfa792c17a9981915aa63755cfd338fd28874de02c42d966ece67"
434        );
435        let test_pvt = base64::engine::general_purpose::URL_SAFE_NO_PAD
436            .decode("rgFYq-b_toGb-wN3URCk_e-6Sj2PtUvoefF284q9oKnVCi7sglAmCZkOv-2nOAeE")
437            .unwrap();
438        let kp = P384KeyPair::from_secret_bytes(&test_pvt).unwrap();
439        let sig = kp.sign(&test_msg[..]).unwrap();
440        assert_eq!(sig, &test_sig[..]);
441        assert!(kp.verify_signature(&test_msg[..], &sig[..]));
442        assert!(!kp.verify_signature(b"Not the message", &sig[..]));
443        assert!(!kp.verify_signature(&test_msg[..], &[0u8; 96]));
444    }
445
446    #[test]
447    fn sign_verify_expected_prehash() {
448        let test_msg = sha2::Sha384::digest(b"This is a dummy message for use with tests");
449        let test_sig = &hex!(
450            "acf7e9f0975738d446b26aa1651ad699cac490a496d6f70221126c35d8e4fcc5a28f63f611557be9d4c321d8fa24dbf2
451             846e3bcbea2e45eff577974664b1e98fffdad8ddbe7bfa792c17a9981915aa63755cfd338fd28874de02c42d966ece67"
452        );
453        let test_pvt = base64::engine::general_purpose::URL_SAFE_NO_PAD
454            .decode("rgFYq-b_toGb-wN3URCk_e-6Sj2PtUvoefF284q9oKnVCi7sglAmCZkOv-2nOAeE")
455            .unwrap();
456        let kp = P384KeyPair::from_secret_bytes(&test_pvt).unwrap();
457        let sig = kp.sign_prehashed(&test_msg[..]).unwrap();
458        assert_eq!(sig, &test_sig[..]);
459        assert!(kp.verify_signature_prehashed(&test_msg[..], &sig[..]));
460        assert!(!kp.verify_signature_prehashed(b"Not the message", &sig[..]));
461        assert!(!kp.verify_signature_prehashed(&test_msg[..], &[0u8; 96]));
462    }
463
464    #[test]
465    fn key_exchange_random() {
466        let kp1 = P384KeyPair::random().unwrap();
467        let kp2 = P384KeyPair::random().unwrap();
468        assert_ne!(
469            kp1.to_keypair_bytes().unwrap(),
470            kp2.to_keypair_bytes().unwrap()
471        );
472
473        let xch1 = kp1.key_exchange_bytes(&kp2).unwrap();
474        let xch2 = kp2.key_exchange_bytes(&kp1).unwrap();
475        assert_eq!(xch1.len(), 48);
476        assert_eq!(xch1, xch2);
477    }
478
479    #[test]
480    fn round_trip_bytes() {
481        let kp = P384KeyPair::random().unwrap();
482        let cmp = P384KeyPair::from_keypair_bytes(&kp.to_keypair_bytes().unwrap()).unwrap();
483        assert_eq!(
484            kp.to_keypair_bytes().unwrap(),
485            cmp.to_keypair_bytes().unwrap()
486        );
487    }
488}