@nuggetslife/vc 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Cargo.toml CHANGED
@@ -10,6 +10,7 @@ crate-type = ["cdylib"]
10
10
  # Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
11
11
  base64 = "0.22.1"
12
12
  bs58 = "0.5.1"
13
+ hex = "0.4.3"
13
14
  # Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
14
15
  napi = { version = "2.12.2", default-features = false, features = ["napi4", "tokio_rt"] }
15
16
  napi-derive = "2.12.2"
package/index.d.ts CHANGED
@@ -15,6 +15,14 @@ export interface JwkKeyPairOptions {
15
15
  privateKeyJwk?: JsonWebKey
16
16
  publicKeyJwk?: JsonWebKey
17
17
  }
18
+ export interface FingerPrintFromPublicKeyOptions {
19
+ publicKeyBase58: string
20
+ }
21
+ export interface KeyPairFromFingerPrintOptions {
22
+ id?: string
23
+ controller?: string
24
+ fingerprint: string
25
+ }
18
26
  export interface JsonWebKey {
19
27
  /**
20
28
  * Indicates the key type used
@@ -96,10 +104,16 @@ export class Bls12381G2KeyPair {
96
104
  static generate(options?: GenerateKeyPairOptions | undefined | null): Promise<Bls12381G2KeyPair>
97
105
  static from(options: KeyPairOptions): Promise<Bls12381G2KeyPair>
98
106
  static fromJwk(options: JwkKeyPairOptions): Promise<Bls12381G2KeyPair>
107
+ static fromFingerprint(options: KeyPairFromFingerPrintOptions): Promise<Bls12381G2KeyPair>
99
108
  get publicKey(): string | null
109
+ publicKeyJwk(): JsonWebKey
100
110
  get privateKey(): string | null
111
+ privateKeyJwk(): JsonWebKey
101
112
  signer(): KeyPairSigner
102
113
  verifier(): KeyPairVerifier
114
+ fingerprint(): string
115
+ static fingerprintFromPublicKey(options: FingerPrintFromPublicKeyOptions): string
116
+ verifyFingerprint(fingerprint: string): void
103
117
  }
104
118
  export class KeyPairSigner {
105
119
  sign(options: KeyPairSignerOptions): Promise<Uint8Array>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuggetslife/vc",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "main": "index.js",
5
5
  "types": "index.d.ts",
6
6
  "napi": {
@@ -38,6 +38,6 @@
38
38
  },
39
39
  "packageManager": "yarn@4.3.1",
40
40
  "optionalDependencies": {
41
- "@nuggetslife/vc-darwin-arm64": "0.0.7"
41
+ "@nuggetslife/vc-darwin-arm64": "0.0.8"
42
42
  }
43
43
  }
package/src/lib.rs CHANGED
@@ -3,12 +3,19 @@
3
3
  #[macro_use]
4
4
  extern crate napi_derive;
5
5
 
6
- use base64::{engine::general_purpose::URL_SAFE, Engine as _};
6
+ use base64::{
7
+ engine::general_purpose::URL_SAFE, engine::general_purpose::URL_SAFE_NO_PAD, Engine as _,
8
+ };
7
9
  use napi::bindgen_prelude::*;
8
- use types::{Bls12381G2KeyPair, GenerateKeyPairOptions, JwkKeyPairOptions, KeyPairOptions};
10
+ use types::{
11
+ Bls12381G2KeyPair, BlsCurveName, FingerPrintFromPublicKeyOptions, GenerateKeyPairOptions,
12
+ JsonWebKey, JwkKeyPairOptions, JwkKty, KeyPairFromFingerPrintOptions, KeyPairOptions,
13
+ };
9
14
  use validators::{assert_bls_12381_g2_private_jwk, assert_bls_12381_g2_public_jwk};
10
15
  use vc::bbs_signatures::bls12381::{
11
16
  bls_generate_g2_key, bls_sign, bls_verify, BlsBbsSignRequest, BlsBbsVerifyRequest, BlsKeyPair,
17
+ BLS12381G2_MULTICODEC_IDENTIFIER, DEFAULT_BLS12381_G2_PUBLIC_KEY_LENGTH,
18
+ MULTIBASE_ENCODED_BASE58_IDENTIFIER, VARIABLE_INTEGER_TRAILING_BYTE,
12
19
  };
13
20
 
14
21
  pub mod types;
@@ -151,6 +158,84 @@ impl Bls12381G2KeyPair {
151
158
  }
152
159
  }
153
160
 
161
+ #[napi(factory)]
162
+ pub async fn from_fingerprint(
163
+ options: KeyPairFromFingerPrintOptions,
164
+ ) -> Result<Bls12381G2KeyPair> {
165
+ let KeyPairFromFingerPrintOptions {
166
+ id,
167
+ controller,
168
+ fingerprint,
169
+ } = options;
170
+ let mut chars = fingerprint.chars();
171
+ let head = chars
172
+ .next()
173
+ .ok_or(napi::Error::from_reason("fingerprint string is empty"))?
174
+ .to_string();
175
+
176
+ if head != MULTIBASE_ENCODED_BASE58_IDENTIFIER {
177
+ return Err(napi::Error::from_reason(
178
+ format!("Unsupported fingerprint type: expected first character to be `z` indicating base58 encoding, received {head}")
179
+ ));
180
+ };
181
+
182
+ let rest = chars.collect::<String>();
183
+ let buffer = bs58::decode(rest).into_vec().map_err(|err| {
184
+ napi::Error::from_reason(format!(
185
+ "failed to decode bs58 fingerprint to bytes. error: {err}"
186
+ ))
187
+ })?;
188
+
189
+ if buffer.len() != BLS12381G2_MULTICODEC_IDENTIFIER as usize {
190
+ return Err(napi::Error::from_reason(
191
+ format!("Unsupported public key length: expected `${DEFAULT_BLS12381_G2_PUBLIC_KEY_LENGTH}` received {}", buffer.len())
192
+ ));
193
+ }
194
+
195
+ if buffer[0] != DEFAULT_BLS12381_G2_PUBLIC_KEY_LENGTH {
196
+ return Err(napi::Error::from_reason(
197
+ format!("Unsupported public key identifier: expected second character to be {BLS12381G2_MULTICODEC_IDENTIFIER} indicating BLS12381G2 key pair, received {}", buffer[0])
198
+ ));
199
+ }
200
+
201
+ if buffer[1] != VARIABLE_INTEGER_TRAILING_BYTE {
202
+ return Err(napi::Error::from_reason(
203
+ format!("Missing variable integer trailing byte: expected third character to be {BLS12381G2_MULTICODEC_IDENTIFIER} indicating BLS12381G2 key pair, received {}", buffer[1])
204
+ ));
205
+ }
206
+
207
+ let pubkey_base58 = bs58::encode(buffer).into_string();
208
+ let opts = FingerPrintFromPublicKeyOptions {
209
+ public_key_base58: pubkey_base58.clone(),
210
+ };
211
+ let fingerprint = Bls12381G2KeyPair::fingerprint_from_public_key(opts).map_err(|err| {
212
+ napi::Error::from_reason(format!("fingerprint_from_public_key failed. error: {err}"))
213
+ })?;
214
+ let mapped_controller = match controller {
215
+ Some(v) => v,
216
+ None => {
217
+ format!("did:key:{fingerprint}",)
218
+ }
219
+ };
220
+
221
+ let mapped_id = match id {
222
+ Some(v) => v,
223
+ None => {
224
+ format!("#{fingerprint}",)
225
+ }
226
+ };
227
+
228
+ let kp = Bls12381G2KeyPair::new(Some(KeyPairOptions {
229
+ id: Some(mapped_id),
230
+ controller: Some(mapped_controller),
231
+ public_key_base58: Some(pubkey_base58),
232
+ private_key_base58: None,
233
+ }))
234
+ .await;
235
+
236
+ Ok(kp)
237
+ }
238
+
154
239
  #[napi(getter)]
155
240
  pub fn public_key(&self) -> Option<String> {
156
241
  self
@@ -159,6 +244,26 @@ impl Bls12381G2KeyPair {
159
244
  .map(|b| bs58::encode(b).into_string())
160
245
  }
161
246
 
247
+ #[napi]
248
+ pub fn public_key_jwk(&self) -> Result<JsonWebKey> {
249
+ let Some(ref public_key_buffer) = self.public_key_buffer else {
250
+ return Err(napi::Error::from_reason("no public_key_buffer"));
251
+ };
252
+
253
+ Ok(JsonWebKey {
254
+ kid: self.id.to_owned(),
255
+ kty: JwkKty::EC.into(),
256
+ crv: BlsCurveName::G2.into(),
257
+ x: URL_SAFE_NO_PAD.encode(public_key_buffer),
258
+ use_: None,
259
+ key_ops: None,
260
+ alg: None,
261
+ d: None,
262
+ y: None,
263
+ ext: None,
264
+ })
265
+ }
266
+
162
267
  #[napi(getter)]
163
268
  pub fn private_key(&self) -> Option<String> {
164
269
  self
@@ -167,14 +272,115 @@ impl Bls12381G2KeyPair {
167
272
  .map(|b| bs58::encode(b).into_string())
168
273
  }
169
274
 
275
+ #[napi]
276
+ pub fn private_key_jwk(&self) -> Result<JsonWebKey> {
277
+ let Some(ref public_key_buffer) = self.public_key_buffer else {
278
+ return Err(napi::Error::from_reason("no public_key_buffer"));
279
+ };
280
+
281
+ let Some(ref private_key_buffer) = self.private_key_buffer else {
282
+ return Err(napi::Error::from_reason("no private_key_buffer"));
283
+ };
284
+
285
+ Ok(JsonWebKey {
286
+ kid: self.id.to_owned(),
287
+ kty: JwkKty::EC.into(),
288
+ crv: BlsCurveName::G2.into(),
289
+ x: URL_SAFE_NO_PAD.encode(public_key_buffer),
290
+ d: Some(URL_SAFE_NO_PAD.encode(private_key_buffer)),
291
+ use_: None,
292
+ key_ops: None,
293
+ alg: None,
294
+ y: None,
295
+ ext: None,
296
+ })
297
+ }
298
+
170
299
  #[napi]
171
300
  pub fn signer(&self) -> KeyPairSigner {
172
301
  KeyPairSigner { key: self.clone() }
173
302
  }
303
+
174
304
  #[napi]
175
305
  pub fn verifier(&self) -> KeyPairVerifier {
176
306
  KeyPairVerifier { key: self.clone() }
177
307
  }
308
+
309
+ #[napi]
310
+ pub fn fingerprint(&self) -> Result<String> {
311
+ let Some(public_key_base58) = self.public_key() else {
312
+ return Err(napi::Error::from_reason("no public key"));
313
+ };
314
+ Bls12381G2KeyPair::fingerprint_from_public_key(FingerPrintFromPublicKeyOptions {
315
+ public_key_base58,
316
+ })
317
+ }
318
+
319
+ #[napi]
320
+ pub fn fingerprint_from_public_key(options: FingerPrintFromPublicKeyOptions) -> Result<String> {
321
+ let FingerPrintFromPublicKeyOptions { public_key_base58 } = options;
322
+ let mut key_bytes = bs58::decode(public_key_base58).into_vec().map_err(|err| {
323
+ napi::Error::from_reason(format!(
324
+ "failed to decode public_key_base58 value. error: {err}"
325
+ ))
326
+ })?;
327
+
328
+ let mut buffer = vec![
329
+ BLS12381G2_MULTICODEC_IDENTIFIER,
330
+ VARIABLE_INTEGER_TRAILING_BYTE,
331
+ ];
332
+ buffer.append(&mut key_bytes);
333
+
334
+ Ok(format!(
335
+ "{}{}",
336
+ MULTIBASE_ENCODED_BASE58_IDENTIFIER,
337
+ bs58::encode(buffer).into_string()
338
+ ))
339
+ }
340
+
341
+ #[napi]
342
+ pub fn verify_fingerprint(&self, fingerprint: String) -> Result<()> {
343
+ // fingerprint should have `z` prefix indicating
344
+ // that it's multi-base encoded
345
+ let mut chars = fingerprint.chars();
346
+ let head = chars
347
+ .next()
348
+ .ok_or(napi::Error::from_reason("fingerprint string is empty"))?
349
+ .to_string();
350
+
351
+ let rest = chars.collect::<String>();
352
+
353
+ if head != MULTIBASE_ENCODED_BASE58_IDENTIFIER {
354
+ return Err(napi::Error::from_reason(
355
+ "`fingerprint` must be a multibase encoded string.",
356
+ ));
357
+ };
358
+
359
+ let fingerprint_buffer = bs58::decode(rest).into_vec().map_err(|err| {
360
+ napi::Error::from_reason(format!("failed to decode bs58 value: error: {err}"))
361
+ })?;
362
+
363
+ let public_key_buffer = self
364
+ .public_key_buffer
365
+ .clone()
366
+ .ok_or(napi::Error::from_reason("public key buffer is missing"))?;
367
+
368
+ let leader = hex::encode(&fingerprint_buffer[..2]);
369
+ let leader_match = if leader == "eb01" { true } else { false };
370
+ let bytes_match = if &public_key_buffer == &fingerprint_buffer[2..] {
371
+ true
372
+ } else {
373
+ false
374
+ };
375
+
376
+ if leader_match && bytes_match {
377
+ Ok(())
378
+ } else {
379
+ Err(napi::Error::from_reason(
380
+ "The fingerprint does not match the public key",
381
+ ))
382
+ }
383
+ }
178
384
  }
179
385
 
180
386
  pub fn convert_base64_url_to_base58(value: String) -> Result<String> {
package/src/types.rs CHANGED
@@ -27,6 +27,18 @@ pub struct JwkKeyPairOptions {
27
27
  pub public_key_jwk: Option<JsonWebKey>,
28
28
  }
29
29
 
30
+ #[napi(object)]
31
+ pub struct FingerPrintFromPublicKeyOptions {
32
+ pub public_key_base58: String,
33
+ }
34
+
35
+ #[napi(object)]
36
+ pub struct KeyPairFromFingerPrintOptions {
37
+ pub id: Option<String>,
38
+ pub controller: Option<String>,
39
+ pub fingerprint: String,
40
+ }
41
+
30
42
  #[napi(object)]
31
43
  pub struct JsonWebKey {
32
44
  /////
@@ -130,6 +142,17 @@ impl TryFrom<&str> for BlsCurveName {
130
142
  }
131
143
  }
132
144
 
145
+ impl Into<String> for BlsCurveName {
146
+ fn into(self) -> String {
147
+ match self {
148
+ BlsCurveName::DeprecatedG1 => String::from("BLS12381_G1"),
149
+ BlsCurveName::DeprecatedG2 => String::from("BLS12381_G2"),
150
+ BlsCurveName::G1 => String::from("Bls12381G1"),
151
+ BlsCurveName::G2 => String::from("Bls12381G2"),
152
+ }
153
+ }
154
+ }
155
+
133
156
  #[derive(PartialEq)]
134
157
  pub enum JwkKty {
135
158
  OctetKeyPair,
@@ -152,3 +175,13 @@ impl TryFrom<&str> for JwkKty {
152
175
  }
153
176
  }
154
177
  }
178
+
179
+ impl Into<String> for JwkKty {
180
+ fn into(self) -> String {
181
+ match self {
182
+ JwkKty::OctetKeyPair => String::from("OKP"),
183
+ JwkKty::EC => String::from("EC"),
184
+ JwkKty::RSA => String::from("RSA"),
185
+ }
186
+ }
187
+ }