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