identity_storage/key_storage/
bls.rs

1// Copyright 2020-2024 IOTA Stiftung, Fondazione Links
2// SPDX-License-Identifier: Apache-2.0
3
4use anyhow::Context;
5use identity_verification::jose::jwk::Jwk;
6use identity_verification::jose::jwu;
7use identity_verification::jwk::BlsCurve;
8use identity_verification::jwk::JwkParamsEc;
9use jsonprooftoken::jpa::algs::ProofAlgorithm;
10use zkryptium::bbsplus::ciphersuites::BbsCiphersuite;
11use zkryptium::bbsplus::ciphersuites::Bls12381Sha256;
12use zkryptium::bbsplus::ciphersuites::Bls12381Shake256;
13use zkryptium::bbsplus::keys::BBSplusPublicKey;
14use zkryptium::bbsplus::keys::BBSplusSecretKey;
15use zkryptium::keys::pair::KeyPair;
16use zkryptium::schemes::algorithms::BBSplus;
17use zkryptium::schemes::generics::Signature;
18
19use crate::key_storage::KeyStorageError;
20use crate::key_storage::KeyStorageErrorKind;
21use crate::key_storage::KeyStorageResult;
22use crate::ProofUpdateCtx;
23
24fn random_bbs_keypair<S>() -> Result<(BBSplusSecretKey, BBSplusPublicKey), zkryptium::errors::Error>
25where
26  S: BbsCiphersuite,
27{
28  KeyPair::<BBSplus<S>>::random().map(KeyPair::into_parts)
29}
30
31/// Generates a new BBS+ keypair using either `BLS12381-SHA256` or `BLS12381-SHAKE256`.
32pub fn generate_bbs_keypair(alg: ProofAlgorithm) -> KeyStorageResult<(BBSplusSecretKey, BBSplusPublicKey)> {
33  match alg {
34    ProofAlgorithm::BLS12381_SHA256 => random_bbs_keypair::<Bls12381Sha256>(),
35    ProofAlgorithm::BLS12381_SHAKE256 => random_bbs_keypair::<Bls12381Shake256>(),
36    _ => return Err(KeyStorageErrorKind::UnsupportedProofAlgorithm.into()),
37  }
38  .map_err(|err| KeyStorageError::new(KeyStorageErrorKind::Unspecified).with_source(err))
39}
40
41/// Encodes a private BBS+ key into JWK.
42pub fn encode_bls_jwk(
43  private_key: &BBSplusSecretKey,
44  public_key: &BBSplusPublicKey,
45  alg: ProofAlgorithm,
46) -> (Jwk, Jwk) {
47  let (x, y) = public_key.to_coordinates();
48  let x = jwu::encode_b64(x);
49  let y = jwu::encode_b64(y);
50
51  let d = jwu::encode_b64(private_key.to_bytes());
52  let params = JwkParamsEc {
53    x,
54    y,
55    d: Some(d),
56    crv: BlsCurve::BLS12381G2.name().to_owned(),
57  };
58
59  let mut jwk = Jwk::from_params(params);
60
61  jwk.set_alg(alg.to_string());
62  jwk.set_kid(jwk.thumbprint_sha256_b64());
63  let public_jwk = jwk.to_public().expect("kty != oct");
64
65  (jwk, public_jwk)
66}
67
68/// Attempts to decode JWK into a BBS+ keypair.
69pub fn expand_bls_jwk(jwk: &Jwk) -> KeyStorageResult<(Option<BBSplusSecretKey>, BBSplusPublicKey)> {
70  // Check the provided JWK represents a BLS12381G2 key.
71  let params = jwk
72    .try_ec_params()
73    .ok()
74    .filter(|params| {
75      params
76        .try_bls_curve()
77        .map(|curve| curve == BlsCurve::BLS12381G2)
78        .unwrap_or(false)
79    })
80    .context(format!("not a {} curve key", BlsCurve::BLS12381G2))
81    .map_err(|e| KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType).with_source(e))?;
82
83  let sk = params
84    .d
85    .as_deref()
86    .map(|d| {
87      jwu::decode_b64(d)
88        .context("`d` parameter is not base64 encoded")
89        .and_then(|bytes| BBSplusSecretKey::from_bytes(&bytes).context("invalid key size"))
90    })
91    .transpose()
92    .map_err(|e| KeyStorageError::new(KeyStorageErrorKind::Unspecified).with_source(e))?;
93
94  let x = jwu::decode_b64(&params.x)
95    .context("`x` parameter is not base64 encoded")
96    .and_then(|bytes| bytes.try_into().ok().context("invalid coordinate size"))
97    .map_err(|e| KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType).with_source(e))?;
98  let y = jwu::decode_b64(&params.y)
99    .context("`y` parameter is not base64 encoded")
100    .and_then(|bytes| bytes.try_into().ok().context("invalid coordinate size"))
101    .map_err(|e| KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType).with_source(e))?;
102
103  let pk = BBSplusPublicKey::from_coordinates(&x, &y).map_err(|e| {
104    KeyStorageError::new(KeyStorageErrorKind::Unspecified)
105      .with_source(e)
106      .with_custom_message("invalid BBS+ public key".to_owned())
107  })?;
108
109  Ok((sk, pk))
110}
111
112fn _sign_bbs<S>(
113  data: &[Vec<u8>],
114  sk: &BBSplusSecretKey,
115  pk: &BBSplusPublicKey,
116  header: &[u8],
117) -> Result<Vec<u8>, zkryptium::errors::Error>
118where
119  S: BbsCiphersuite,
120{
121  Signature::<BBSplus<S>>::sign(Some(data), sk, pk, Some(header)).map(|s| s.to_bytes().to_vec())
122}
123
124/// Signs data and header using the given keys.
125pub fn sign_bbs(
126  alg: ProofAlgorithm,
127  data: &[Vec<u8>],
128  sk: &BBSplusSecretKey,
129  pk: &BBSplusPublicKey,
130  header: &[u8],
131) -> KeyStorageResult<Vec<u8>> {
132  match alg {
133    ProofAlgorithm::BLS12381_SHA256 => _sign_bbs::<Bls12381Sha256>(data, sk, pk, header),
134    ProofAlgorithm::BLS12381_SHAKE256 => _sign_bbs::<Bls12381Shake256>(data, sk, pk, header),
135    _ => return Err(KeyStorageErrorKind::UnsupportedProofAlgorithm.into()),
136  }
137  .map_err(|e| {
138    KeyStorageError::new(KeyStorageErrorKind::Unspecified)
139      .with_source(e)
140      .with_custom_message("signature failed".to_owned())
141  })
142}
143
144fn _update_bbs_signature<S>(
145  sig: &[u8; 80],
146  sk: &BBSplusSecretKey,
147  update_ctx: &ProofUpdateCtx,
148) -> Result<[u8; 80], zkryptium::errors::Error>
149where
150  S: BbsCiphersuite,
151{
152  let sig = Signature::<BBSplus<S>>::from_bytes(sig)?;
153  let ProofUpdateCtx {
154    old_start_validity_timeframe,
155    new_start_validity_timeframe,
156    old_end_validity_timeframe,
157    new_end_validity_timeframe,
158    index_start_validity_timeframe,
159    index_end_validity_timeframe,
160    number_of_signed_messages,
161  } = update_ctx;
162  let half_updated = sig.update_signature(
163    sk,
164    old_start_validity_timeframe,
165    new_start_validity_timeframe,
166    *index_start_validity_timeframe,
167    *number_of_signed_messages,
168  )?;
169  half_updated
170    .update_signature(
171      sk,
172      old_end_validity_timeframe,
173      new_end_validity_timeframe,
174      *index_end_validity_timeframe,
175      *number_of_signed_messages,
176    )
177    .map(|sig| sig.to_bytes())
178}
179
180/// Updates BBS+ signature's timeframe data.
181pub fn update_bbs_signature(
182  alg: ProofAlgorithm,
183  sig: &[u8],
184  sk: &BBSplusSecretKey,
185  update_ctx: &ProofUpdateCtx,
186) -> KeyStorageResult<Vec<u8>> {
187  let exact_size_signature = sig.try_into().map_err(|_| {
188    KeyStorageError::new(KeyStorageErrorKind::Unspecified).with_custom_message("invalid signature size".to_owned())
189  })?;
190  match alg {
191    ProofAlgorithm::BLS12381_SHA256 => _update_bbs_signature::<Bls12381Sha256>(exact_size_signature, sk, update_ctx),
192    ProofAlgorithm::BLS12381_SHAKE256 => {
193      _update_bbs_signature::<Bls12381Shake256>(exact_size_signature, sk, update_ctx)
194    }
195    _ => return Err(KeyStorageErrorKind::UnsupportedProofAlgorithm.into()),
196  }
197  .map(Vec::from)
198  .map_err(|e| {
199    KeyStorageError::new(KeyStorageErrorKind::Unspecified)
200      .with_custom_message("signature failed")
201      .with_source(e)
202  })
203}