askar_crypto/alg/
bls.rs

1//! BLS12-381 key support
2
3use core::{
4    fmt::{self, Debug, Formatter},
5    ops::Add,
6};
7
8use aead::generic_array::GenericArray;
9use blake2::Digest;
10use bls12_381::{G1Affine, G1Projective, G2Affine, G2Projective, Scalar};
11use group::GroupEncoding;
12use sha2::Sha256;
13use subtle::ConstantTimeEq;
14use zeroize::{Zeroize, Zeroizing};
15
16use crate::generic_array::{
17    typenum::{self, Unsigned, U144, U32, U48, U96},
18    ArrayLength,
19};
20
21use super::{BlsCurves, HasKeyAlg, HasKeyBackend, KeyAlg};
22use crate::{
23    buffer::ArrayKey,
24    error::Error,
25    jwk::{FromJwk, JwkEncoder, JwkParts, ToJwk},
26    random::KeyMaterial,
27    repr::{KeyGen, KeyMeta, KeyPublicBytes, KeySecretBytes, KeypairMeta},
28};
29
30/// The 'kty' value of a BLS key JWK
31pub const JWK_KEY_TYPE: &str = "OKP";
32
33/// A BLS12-381 key pair
34#[derive(Clone, Zeroize)]
35pub struct BlsKeyPair<Pk: BlsPublicKeyType> {
36    secret: Option<BlsSecretKey>,
37    public: Pk::Buffer,
38}
39
40impl<Pk: BlsPublicKeyType> BlsKeyPair<Pk> {
41    /// Generate a new BLS key from a seed according to the KeyGen algorithm
42    pub fn from_seed(seed: &[u8]) -> Result<Self, Error> {
43        Ok(Self::from_secret_key(BlsSecretKey::generate(
44            BlsKeyGen::new(seed)?,
45        )?))
46    }
47
48    #[inline]
49    pub(crate) fn from_secret_key(sk: BlsSecretKey) -> Self {
50        let public = Pk::from_secret_scalar(&sk.0);
51        Self {
52            secret: Some(sk),
53            public,
54        }
55    }
56
57    pub(crate) fn check_public_bytes(&self, pk: &[u8]) -> Result<(), Error> {
58        if Pk::with_bytes(&self.public, None, |slf| slf.ct_eq(pk)).into() {
59            Ok(())
60        } else {
61            Err(err_msg!(InvalidKeyData, "invalid BLS keypair"))
62        }
63    }
64
65    /// Accessor for the associated public key
66    pub fn bls_public_key(&self) -> &Pk::Buffer {
67        &self.public
68    }
69
70    /// Accessor for the associated secret key value, if any
71    pub fn bls_secret_scalar(&self) -> Option<&Scalar> {
72        self.secret.as_ref().map(|s| &s.0)
73    }
74}
75
76impl<Pk: BlsPublicKeyType> Debug for BlsKeyPair<Pk> {
77    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
78        f.debug_struct("BlsKeyPair")
79            .field("crv", &Pk::JWK_CURVE)
80            .field("secret", &self.secret)
81            .field("public", &self.public)
82            .finish()
83    }
84}
85
86impl<Pk: BlsPublicKeyType> PartialEq for BlsKeyPair<Pk> {
87    fn eq(&self, other: &Self) -> bool {
88        other.secret == self.secret && other.public == self.public
89    }
90}
91
92impl<Pk: BlsPublicKeyType> Eq for BlsKeyPair<Pk> {}
93
94impl<Pk: BlsPublicKeyType> HasKeyBackend for BlsKeyPair<Pk> {}
95
96impl<Pk: BlsPublicKeyType> HasKeyAlg for BlsKeyPair<Pk> {
97    fn algorithm(&self) -> KeyAlg {
98        KeyAlg::Bls12_381(Pk::ALG_TYPE)
99    }
100}
101
102impl<Pk: BlsPublicKeyType> KeyMeta for BlsKeyPair<Pk> {
103    type KeySize = U32;
104}
105
106impl<Pk> KeypairMeta for BlsKeyPair<Pk>
107where
108    Pk: BlsPublicKeyType,
109    U32: Add<Pk::BufferSize>,
110    <U32 as Add<Pk::BufferSize>>::Output: ArrayLength<u8>,
111{
112    type PublicKeySize = Pk::BufferSize;
113    type KeypairSize = typenum::Sum<Self::KeySize, Pk::BufferSize>;
114}
115
116impl<Pk: BlsPublicKeyType> KeyGen for BlsKeyPair<Pk> {
117    fn generate(rng: impl KeyMaterial) -> Result<Self, Error> {
118        let secret = BlsSecretKey::generate(rng)?;
119        Ok(Self::from_secret_key(secret))
120    }
121}
122
123impl<Pk: BlsPublicKeyType> KeySecretBytes for BlsKeyPair<Pk> {
124    fn from_secret_bytes(key: &[u8]) -> Result<Self, Error>
125    where
126        Self: Sized,
127    {
128        let sk = BlsSecretKey::from_bytes(key)?;
129        Ok(Self::from_secret_key(sk))
130    }
131
132    fn with_secret_bytes<O>(&self, f: impl FnOnce(Option<&[u8]>) -> O) -> O {
133        if let Some(sk) = self.secret.as_ref() {
134            let mut skb = Zeroizing::new(sk.0.to_bytes());
135            skb.reverse(); // into big-endian
136            f(Some(&*skb))
137        } else {
138            f(None)
139        }
140    }
141}
142
143impl<Pk: BlsPublicKeyType> KeyPublicBytes for BlsKeyPair<Pk>
144where
145    Self: KeypairMeta,
146{
147    fn from_public_bytes(key: &[u8]) -> Result<Self, Error> {
148        Ok(Self {
149            secret: None,
150            public: Pk::from_public_bytes(key)?,
151        })
152    }
153
154    fn with_public_bytes<O>(&self, f: impl FnOnce(&[u8]) -> O) -> O {
155        Pk::with_bytes(&self.public, None, f)
156    }
157}
158
159impl<Pk: BlsPublicKeyType> ToJwk for BlsKeyPair<Pk> {
160    fn encode_jwk(&self, enc: &mut dyn JwkEncoder) -> Result<(), Error> {
161        enc.add_str("crv", Pk::get_jwk_curve(enc.alg()))?;
162        enc.add_str("kty", JWK_KEY_TYPE)?;
163        Pk::with_bytes(&self.public, enc.alg(), |buf| enc.add_as_base64("x", buf))?;
164        if enc.is_secret() {
165            self.with_secret_bytes(|buf| {
166                if let Some(sk) = buf {
167                    enc.add_as_base64("d", sk)
168                } else {
169                    Ok(())
170                }
171            })?;
172        }
173        Ok(())
174    }
175}
176
177impl<Pk: BlsPublicKeyType> FromJwk for BlsKeyPair<Pk> {
178    fn from_jwk_parts(jwk: JwkParts<'_>) -> Result<Self, Error> {
179        if jwk.kty != JWK_KEY_TYPE &&
180            /* compatibility with previous version */
181            jwk.kty != "EC"
182        {
183            return Err(err_msg!(InvalidKeyData, "Unsupported key type"));
184        }
185        if jwk.crv != Pk::JWK_CURVE {
186            return Err(err_msg!(InvalidKeyData, "Unsupported key algorithm"));
187        }
188        ArrayKey::<Pk::BufferSize>::temp(|pk_arr| {
189            if jwk.x.decode_base64(pk_arr)? != pk_arr.len() {
190                Err(err_msg!(InvalidKeyData))
191            } else if jwk.d.is_some() {
192                ArrayKey::<U32>::temp(|sk_arr| {
193                    if jwk.d.decode_base64(sk_arr)? != sk_arr.len() {
194                        Err(err_msg!(InvalidKeyData))
195                    } else {
196                        let result = BlsKeyPair::from_secret_key(BlsSecretKey::from_bytes(sk_arr)?);
197                        result.check_public_bytes(pk_arr)?;
198                        Ok(result)
199                    }
200                })
201            } else {
202                Ok(Self {
203                    secret: None,
204                    public: Pk::from_public_bytes(pk_arr)?,
205                })
206            }
207        })
208    }
209}
210
211#[derive(Clone, Debug, PartialEq, Eq, Zeroize)]
212#[repr(transparent)]
213pub(crate) struct BlsSecretKey(Scalar);
214
215impl BlsSecretKey {
216    fn generate(mut rng: impl KeyMaterial) -> Result<Self, Error> {
217        let mut secret = Zeroizing::new([0u8; 64]);
218        rng.read_okm(&mut secret[16..]);
219        secret.reverse(); // into little endian
220        Ok(Self(Scalar::from_bytes_wide(&secret)))
221    }
222
223    pub fn from_bytes(sk: &[u8]) -> Result<Self, Error> {
224        if sk.len() != 32 {
225            return Err(err_msg!(InvalidKeyData));
226        }
227        let mut skb = Zeroizing::new([0u8; 32]);
228        skb.copy_from_slice(sk);
229        skb.reverse(); // into little endian
230        let result: Option<Scalar> = Scalar::from_bytes(&skb).into();
231        Ok(Self(result.ok_or_else(|| err_msg!(InvalidKeyData))?))
232    }
233}
234
235impl Drop for BlsSecretKey {
236    fn drop(&mut self) {
237        self.zeroize();
238    }
239}
240
241/// A key material generator compatible with KeyGen from the
242/// bls-signatures RFC draft 4 (incompatible with earlier)
243#[derive(Debug, Clone)]
244pub struct BlsKeyGen<'g> {
245    salt: Option<GenericArray<u8, U32>>,
246    ikm: &'g [u8],
247}
248
249impl<'g> BlsKeyGen<'g> {
250    /// Construct a new `BlsKeyGen` from a seed value
251    pub fn new(ikm: &'g [u8]) -> Result<Self, Error> {
252        if ikm.len() < 32 {
253            return Err(err_msg!(Usage, "Insufficient length for seed"));
254        }
255        Ok(Self { salt: None, ikm })
256    }
257}
258
259impl KeyMaterial for BlsKeyGen<'_> {
260    fn read_okm(&mut self, buf: &mut [u8]) {
261        const SALT: &[u8] = b"BLS-SIG-KEYGEN-SALT-";
262
263        self.salt.replace(match self.salt {
264            None => Sha256::digest(SALT),
265            Some(salt) => Sha256::digest(salt),
266        });
267        let mut extract = hkdf::HkdfExtract::<Sha256>::new(Some(self.salt.as_ref().unwrap()));
268        extract.input_ikm(self.ikm);
269        extract.input_ikm(&[0u8]);
270        let (_, hkdf) = extract.finalize();
271        hkdf.expand(&(buf.len() as u16).to_be_bytes(), buf)
272            .expect("HDKF extract failure");
273    }
274}
275
276/// Trait implemented by supported BLS public key types
277pub trait BlsPublicKeyType: 'static {
278    /// The concrete key representation
279    type Buffer: Clone + Debug + PartialEq + Sized + Zeroize;
280
281    /// The size of the serialized public key
282    type BufferSize: ArrayLength<u8>;
283
284    /// The associated algorithm type
285    const ALG_TYPE: BlsCurves;
286    /// The associated JWK curve name
287    const JWK_CURVE: &'static str;
288
289    /// Get the JWK curve for a specific key algorithm
290    fn get_jwk_curve(_alg: Option<KeyAlg>) -> &'static str {
291        Self::JWK_CURVE
292    }
293
294    /// Initialize from the secret scalar
295    fn from_secret_scalar(secret: &Scalar) -> Self::Buffer;
296
297    /// Initialize from the compressed bytes
298    fn from_public_bytes(key: &[u8]) -> Result<Self::Buffer, Error>;
299
300    /// Access the bytes of the public key
301    fn with_bytes<O>(buf: &Self::Buffer, alg: Option<KeyAlg>, f: impl FnOnce(&[u8]) -> O) -> O;
302}
303
304/// G1 curve
305#[derive(Debug)]
306pub struct G1;
307
308impl BlsPublicKeyType for G1 {
309    type Buffer = G1Affine;
310    type BufferSize = U48;
311
312    const ALG_TYPE: BlsCurves = BlsCurves::G1;
313    const JWK_CURVE: &'static str = "BLS12381_G1";
314
315    #[inline]
316    fn from_secret_scalar(secret: &Scalar) -> Self::Buffer {
317        G1Affine::from(G1Projective::generator() * secret)
318    }
319
320    fn from_public_bytes(key: &[u8]) -> Result<Self::Buffer, Error> {
321        let buf: Option<G1Affine> = G1Affine::from_compressed(
322            TryInto::<&[u8; 48]>::try_into(key).map_err(|_| err_msg!(InvalidKeyData))?,
323        )
324        .into();
325        buf.ok_or_else(|| err_msg!(InvalidKeyData))
326    }
327
328    fn with_bytes<O>(buf: &Self::Buffer, _alg: Option<KeyAlg>, f: impl FnOnce(&[u8]) -> O) -> O {
329        f(buf.to_bytes().as_ref())
330    }
331}
332
333/// G2 curve
334#[derive(Debug)]
335pub struct G2;
336
337impl BlsPublicKeyType for G2 {
338    type Buffer = G2Affine;
339    type BufferSize = U96;
340
341    const ALG_TYPE: BlsCurves = BlsCurves::G2;
342    const JWK_CURVE: &'static str = "BLS12381_G2";
343
344    #[inline]
345    fn from_secret_scalar(secret: &Scalar) -> Self::Buffer {
346        G2Affine::from(G2Projective::generator() * secret)
347    }
348
349    fn from_public_bytes(key: &[u8]) -> Result<Self::Buffer, Error> {
350        let buf: Option<G2Affine> = G2Affine::from_compressed(
351            TryInto::<&[u8; 96]>::try_into(key).map_err(|_| err_msg!(InvalidKeyData))?,
352        )
353        .into();
354        buf.ok_or_else(|| err_msg!(InvalidKeyData))
355    }
356
357    fn with_bytes<O>(buf: &Self::Buffer, _alg: Option<KeyAlg>, f: impl FnOnce(&[u8]) -> O) -> O {
358        f(buf.to_bytes().as_ref())
359    }
360}
361
362/// G1 + G2 curves
363#[derive(Debug)]
364pub struct G1G2;
365
366impl BlsPublicKeyType for G1G2 {
367    type Buffer = G1G2Pair;
368    type BufferSize = U144;
369
370    const ALG_TYPE: BlsCurves = BlsCurves::G1G2;
371    const JWK_CURVE: &'static str = "BLS12381_G1G2";
372
373    fn get_jwk_curve(alg: Option<KeyAlg>) -> &'static str {
374        if alg == Some(KeyAlg::Bls12_381(BlsCurves::G1)) {
375            G1::JWK_CURVE
376        } else if alg == Some(KeyAlg::Bls12_381(BlsCurves::G2)) {
377            G2::JWK_CURVE
378        } else {
379            Self::JWK_CURVE
380        }
381    }
382
383    #[inline]
384    fn from_secret_scalar(secret: &Scalar) -> Self::Buffer {
385        G1G2Pair(
386            G1Affine::from(G1Projective::generator() * secret),
387            G2Affine::from(G2Projective::generator() * secret),
388        )
389    }
390
391    fn from_public_bytes(key: &[u8]) -> Result<Self::Buffer, Error> {
392        if key.len() != Self::BufferSize::USIZE {
393            return Err(err_msg!(InvalidKeyData));
394        }
395        let g1: Option<G1Affine> =
396            G1Affine::from_compressed(TryInto::<&[u8; 48]>::try_into(&key[..48]).unwrap()).into();
397        let g2: Option<G2Affine> =
398            G2Affine::from_compressed(TryInto::<&[u8; 96]>::try_into(&key[48..]).unwrap()).into();
399        if let (Some(g1), Some(g2)) = (g1, g2) {
400            Ok(G1G2Pair(g1, g2))
401        } else {
402            Err(err_msg!(InvalidKeyData))
403        }
404    }
405
406    fn with_bytes<O>(buf: &Self::Buffer, alg: Option<KeyAlg>, f: impl FnOnce(&[u8]) -> O) -> O {
407        if alg == Some(KeyAlg::Bls12_381(BlsCurves::G1)) {
408            ArrayKey::<U48>::temp(|arr| {
409                arr.copy_from_slice(buf.0.to_bytes().as_ref());
410                f(&arr[..])
411            })
412        } else if alg == Some(KeyAlg::Bls12_381(BlsCurves::G2)) {
413            ArrayKey::<U96>::temp(|arr| {
414                arr.copy_from_slice(buf.1.to_bytes().as_ref());
415                f(&arr[..])
416            })
417        } else {
418            ArrayKey::<U144>::temp(|arr| {
419                arr[0..48].copy_from_slice(buf.0.to_bytes().as_ref());
420                arr[48..].copy_from_slice(buf.1.to_bytes().as_ref());
421                f(&arr[..])
422            })
423        }
424    }
425}
426
427impl From<&BlsKeyPair<G1G2>> for BlsKeyPair<G1> {
428    fn from(kp: &BlsKeyPair<G1G2>) -> Self {
429        BlsKeyPair {
430            secret: kp.secret.clone(),
431            public: kp.public.0,
432        }
433    }
434}
435
436impl From<&BlsKeyPair<G1G2>> for BlsKeyPair<G2> {
437    fn from(kp: &BlsKeyPair<G1G2>) -> Self {
438        BlsKeyPair {
439            secret: kp.secret.clone(),
440            public: kp.public.1,
441        }
442    }
443}
444
445#[derive(Clone, Debug, PartialEq, Eq, Zeroize)]
446/// A utility struct combining G1 and G2 public keys
447pub struct G1G2Pair(G1Affine, G2Affine);
448
449#[cfg(test)]
450mod tests {
451    use base64::Engine;
452    use std::string::ToString;
453
454    use super::*;
455    use crate::repr::{ToPublicBytes, ToSecretBytes};
456
457    // test against EIP-2333 (as updated for signatures draft 4)
458    #[test]
459    fn key_gen_expected() {
460        let seed = &hex!(
461            "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e5349553
462            1f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04"
463        );
464        let kp = BlsKeyPair::<G1>::from_seed(&seed[..]).unwrap();
465        let sk = kp.to_secret_bytes().unwrap();
466        assert_eq!(
467            sk.as_hex().to_string(),
468            "0d7359d57963ab8fbbde1852dcf553fedbc31f464d80ee7d40ae683122b45070"
469        );
470    }
471
472    #[test]
473    fn g1_key_expected() {
474        let sk = hex!("0d7359d57963ab8fbbde1852dcf553fedbc31f464d80ee7d40ae683122b45070");
475        let kp = BlsKeyPair::<G1>::from_secret_bytes(&sk[..]).unwrap();
476        let pk = kp.to_public_bytes().unwrap();
477        assert_eq!(
478            pk.as_hex().to_string(),
479            "a2c975348667926acf12f3eecb005044e08a7a9b7d95f30bd281b55445107367a2e5d0558be7943c8bd13f9a1a7036fb"
480        );
481        assert_eq!(
482            BlsKeyPair::<G1>::from_public_bytes(pk.as_ref())
483                .unwrap()
484                .to_public_bytes()
485                .unwrap(),
486            pk
487        );
488    }
489
490    #[test]
491    fn g2_key_expected() {
492        let sk = hex!("0d7359d57963ab8fbbde1852dcf553fedbc31f464d80ee7d40ae683122b45070");
493        let kp = BlsKeyPair::<G2>::from_secret_bytes(&sk[..]).unwrap();
494        let pk = kp.to_public_bytes().unwrap();
495        assert_eq!(
496            pk.as_hex().to_string(),
497            "a5e43d5ecb7b8c01ceb3b91f7413b628ef02c6859dc42a4354b21f9195531988a648655037faafd1bac2fd2d7d9466180baa3705a45a6c597853db51eaf431616057fd8049c6bee8764292f9a104200a45a63ceae9d3c368643ab9e5ff0f8810"
498        );
499        assert_eq!(
500            BlsKeyPair::<G2>::from_public_bytes(pk.as_ref())
501                .unwrap()
502                .to_public_bytes()
503                .unwrap(),
504            pk
505        );
506    }
507
508    #[test]
509    fn g1g2_key_expected() {
510        let sk = hex!("0d7359d57963ab8fbbde1852dcf553fedbc31f464d80ee7d40ae683122b45070");
511        let kp = BlsKeyPair::<G1G2>::from_secret_bytes(&sk[..]).unwrap();
512        let pk = kp.to_public_bytes().unwrap();
513        assert_eq!(
514            pk.as_hex().to_string(),
515            "a2c975348667926acf12f3eecb005044e08a7a9b7d95f30bd281b55445107367a2e5d0558be7943c8bd13f9a1a7036fb\
516            a5e43d5ecb7b8c01ceb3b91f7413b628ef02c6859dc42a4354b21f9195531988a648655037faafd1bac2fd2d7d9466180baa3705a45a6c597853db51eaf431616057fd8049c6bee8764292f9a104200a45a63ceae9d3c368643ab9e5ff0f8810"
517        );
518        assert_eq!(
519            BlsKeyPair::<G1G2>::from_public_bytes(pk.as_ref())
520                .unwrap()
521                .to_public_bytes()
522                .unwrap(),
523            pk
524        );
525    }
526
527    #[test]
528    fn g1_jwk_expected() {
529        let test_pvt = &hex!("0d7359d57963ab8fbbde1852dcf553fedbc31f464d80ee7d40ae683122b45070");
530        let test_pub_g1 = &hex!("a2c975348667926acf12f3eecb005044e08a7a9b7d95f30bd281b55445107367a2e5d0558be7943c8bd13f9a1a7036fb");
531        let kp = BlsKeyPair::<G1>::from_secret_bytes(&test_pvt[..]).expect("Error creating key");
532
533        let jwk = kp.to_jwk_public(None).expect("Error converting key to JWK");
534        let jwk = JwkParts::try_from_str(&jwk).expect("Error parsing JWK");
535        assert_eq!(jwk.kty, JWK_KEY_TYPE);
536        assert_eq!(jwk.crv, G1::JWK_CURVE);
537        assert_eq!(
538            jwk.x,
539            base64::engine::general_purpose::URL_SAFE_NO_PAD
540                .encode(test_pub_g1)
541                .as_str()
542        );
543        assert_eq!(jwk.d, None);
544        let pk_load = BlsKeyPair::<G1>::from_jwk_parts(jwk).unwrap();
545        assert_eq!(kp.to_public_bytes(), pk_load.to_public_bytes());
546
547        let jwk = kp.to_jwk_secret(None).expect("Error converting key to JWK");
548        let jwk = JwkParts::from_slice(&jwk).expect("Error parsing JWK");
549        assert_eq!(jwk.kty, JWK_KEY_TYPE);
550        assert_eq!(jwk.crv, G1::JWK_CURVE);
551        assert_eq!(
552            jwk.x,
553            base64::engine::general_purpose::URL_SAFE_NO_PAD
554                .encode(test_pub_g1)
555                .as_str()
556        );
557        assert_eq!(
558            jwk.d,
559            base64::engine::general_purpose::URL_SAFE_NO_PAD
560                .encode(test_pvt)
561                .as_str()
562        );
563        let _sk_load = BlsKeyPair::<G1>::from_jwk_parts(jwk).unwrap();
564        // assert_eq!(
565        //     kp.to_keypair_bytes().unwrap(),
566        //     sk_load.to_keypair_bytes().unwrap()
567        // );
568    }
569
570    #[cfg(feature = "any_key")]
571    #[test]
572    // test loading of a key with the EC key type
573    fn g1_jwk_any_compat() {
574        use crate::alg::{any::AnyKey, BlsCurves, KeyAlg};
575        use alloc::boxed::Box;
576
577        let test_jwk_compat = r#"
578            {
579                "crv": "BLS12381_G1",
580                "kty": "EC",
581                "x": "osl1NIZnkmrPEvPuywBQROCKept9lfML0oG1VEUQc2ei5dBVi-eUPIvRP5oacDb7"
582            }"#;
583        let key = Box::<AnyKey>::from_jwk(test_jwk_compat).expect("Error decoding BLS key JWK");
584        assert_eq!(key.algorithm(), KeyAlg::Bls12_381(BlsCurves::G1));
585        let as_bls = key
586            .downcast_ref::<BlsKeyPair<G1>>()
587            .expect("Error downcasting BLS key");
588        let _ = as_bls
589            .to_jwk_public(None)
590            .expect("Error converting key to JWK");
591    }
592}