@nuggetslife/vc 0.0.6 → 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
@@ -9,11 +9,91 @@ export interface KeyPairOptions {
9
9
  privateKeyBase58?: string
10
10
  publicKeyBase58?: string
11
11
  }
12
+ export interface JwkKeyPairOptions {
13
+ id?: string
14
+ controller?: string
15
+ privateKeyJwk?: JsonWebKey
16
+ publicKeyJwk?: JsonWebKey
17
+ }
18
+ export interface FingerPrintFromPublicKeyOptions {
19
+ publicKeyBase58: string
20
+ }
21
+ export interface KeyPairFromFingerPrintOptions {
22
+ id?: string
23
+ controller?: string
24
+ fingerprint: string
25
+ }
26
+ export interface JsonWebKey {
27
+ /**
28
+ * Indicates the key type used
29
+ * For BLS12381_G1 and BLS12381_G2 the string "EC" MUST be used
30
+ * @see https://tools.ietf.org/html/rfc7517#section-4.1
31
+ *
32
+ */
33
+ kty: string
34
+ /**
35
+ * Indicates the curve this key is associated with
36
+ * In the case of BLS12-381, the curve will also indicate if it's a G1 or G2 point
37
+ * For a G1 point, use the string "BLS12381_G1"
38
+ * For a G2 point, use the string "BLS12381_G2"
39
+ */
40
+ crv: string
41
+ /**
42
+ * This is a compression of the public key point
43
+ * For a G1 public key, X is a 384bit base64url encoding of the octet string representation of the coordinate
44
+ * For a G2 public key, X is a 768bit made up of the concatenation of two 384 bit x coordinates known as
45
+ * x_a and x_b in the following form (x_a || x_b) as a base64url encoding of the octet string representation of the two coordinates
46
+ *
47
+ */
48
+ x: string
49
+ /** @see https://tools.ietf.org/html/rfc7517#section-4.2 */
50
+ use?: string
51
+ /**
52
+ * @see https://tools.ietf.org/html/rfc7517#section-4.3
53
+ *
54
+ */
55
+ keyOps?: Array<string>
56
+ /**
57
+ * @see https://tools.ietf.org/html/rfc7517#section-4.4
58
+ *
59
+ */
60
+ alg?: string
61
+ /**
62
+ * @see https://tools.ietf.org/html/rfc7517#section-4.5
63
+ * TODO: Add note about referencing did-jose-extensions when ready
64
+ *
65
+ */
66
+ kid?: string
67
+ /**
68
+ * IMPORTANT NOTE: d represents the private key value and should not be shared
69
+ * IT IS HIGHLY SENSITIVE DATA AND IF NOT SECURED PROPERLY CONSIDER THE KEY COMPROMISED
70
+ * @see https://tools.ietf.org/html/rfc7517#section-9.2
71
+ *
72
+ */
73
+ d?: string
74
+ /**
75
+ * This coordinate is not used for BLS Keys, but is kept here to make the interface more standard
76
+ *
77
+ */
78
+ y?: string
79
+ /**
80
+ * @see https://www.w3.org/TR/WebCryptoAPI/#cryptokey-interface-members
81
+ *
82
+ */
83
+ ext?: boolean
84
+ }
12
85
  export interface GenerateKeyPairOptions {
13
86
  id?: string
14
87
  controller?: string
15
88
  seed?: Buffer
16
89
  }
90
+ export interface KeyPairSignerOptions {
91
+ data: Array<Uint8Array>
92
+ }
93
+ export interface KeyPairVerifierOptions {
94
+ data: Array<Uint8Array>
95
+ signature: Uint8Array
96
+ }
17
97
  export class Bls12381G2KeyPair {
18
98
  id?: string
19
99
  controller?: string
@@ -23,6 +103,21 @@ export class Bls12381G2KeyPair {
23
103
  constructor(options?: KeyPairOptions | undefined | null)
24
104
  static generate(options?: GenerateKeyPairOptions | undefined | null): Promise<Bls12381G2KeyPair>
25
105
  static from(options: KeyPairOptions): Promise<Bls12381G2KeyPair>
106
+ static fromJwk(options: JwkKeyPairOptions): Promise<Bls12381G2KeyPair>
107
+ static fromFingerprint(options: KeyPairFromFingerPrintOptions): Promise<Bls12381G2KeyPair>
26
108
  get publicKey(): string | null
109
+ publicKeyJwk(): JsonWebKey
27
110
  get privateKey(): string | null
111
+ privateKeyJwk(): JsonWebKey
112
+ signer(): KeyPairSigner
113
+ verifier(): KeyPairVerifier
114
+ fingerprint(): string
115
+ static fingerprintFromPublicKey(options: FingerPrintFromPublicKeyOptions): string
116
+ verifyFingerprint(fingerprint: string): void
117
+ }
118
+ export class KeyPairSigner {
119
+ sign(options: KeyPairSignerOptions): Promise<Uint8Array>
120
+ }
121
+ export class KeyPairVerifier {
122
+ verify(options: KeyPairVerifierOptions): Promise<boolean>
28
123
  }
package/index.js CHANGED
@@ -310,6 +310,8 @@ if (!nativeBinding) {
310
310
  throw new Error(`Failed to load native binding`)
311
311
  }
312
312
 
313
- const { Bls12381G2KeyPair } = nativeBinding
313
+ const { Bls12381G2KeyPair, KeyPairSigner, KeyPairVerifier } = nativeBinding
314
314
 
315
315
  module.exports.Bls12381G2KeyPair = Bls12381G2KeyPair
316
+ module.exports.KeyPairSigner = KeyPairSigner
317
+ module.exports.KeyPairVerifier = KeyPairVerifier
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuggetslife/vc",
3
- "version": "0.0.6",
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.6"
41
+ "@nuggetslife/vc-darwin-arm64": "0.0.8"
42
42
  }
43
43
  }
package/src/lib.rs CHANGED
@@ -3,34 +3,23 @@
3
3
  #[macro_use]
4
4
  extern crate napi_derive;
5
5
 
6
- // use base64::{engine::general_purpose::STANDARD, 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 vc::bbs_signatures::bls12381::bls_generate_g2_key;
10
+ use types::{
11
+ Bls12381G2KeyPair, BlsCurveName, FingerPrintFromPublicKeyOptions, GenerateKeyPairOptions,
12
+ JsonWebKey, JwkKeyPairOptions, JwkKty, KeyPairFromFingerPrintOptions, KeyPairOptions,
13
+ };
14
+ use validators::{assert_bls_12381_g2_private_jwk, assert_bls_12381_g2_public_jwk};
15
+ use vc::bbs_signatures::bls12381::{
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,
19
+ };
9
20
 
10
- #[napi]
11
- pub struct Bls12381G2KeyPair {
12
- pub id: Option<String>,
13
- pub controller: Option<String>,
14
- pub private_key_buffer: Option<Vec<u8>>,
15
- pub public_key_buffer: Option<Vec<u8>>,
16
- #[napi(js_name = "type")]
17
- pub type_: String,
18
- }
19
-
20
- #[napi(object)]
21
- pub struct KeyPairOptions {
22
- pub id: Option<String>,
23
- pub controller: Option<String>,
24
- pub private_key_base58: Option<String>,
25
- pub public_key_base58: Option<String>,
26
- }
27
-
28
- #[napi(object)]
29
- pub struct GenerateKeyPairOptions {
30
- pub id: Option<String>,
31
- pub controller: Option<String>,
32
- pub seed: Option<Buffer>,
33
- }
21
+ pub mod types;
22
+ pub mod validators;
34
23
 
35
24
  #[napi]
36
25
  impl Bls12381G2KeyPair {
@@ -126,6 +115,127 @@ impl Bls12381G2KeyPair {
126
115
  Bls12381G2KeyPair::new(Some(options)).await
127
116
  }
128
117
 
118
+ #[napi(factory)]
119
+ pub async fn from_jwk(options: JwkKeyPairOptions) -> Result<Bls12381G2KeyPair> {
120
+ let JwkKeyPairOptions {
121
+ id,
122
+ controller,
123
+ private_key_jwk,
124
+ public_key_jwk,
125
+ } = options;
126
+
127
+ if let Some(jwk) = private_key_jwk {
128
+ assert_bls_12381_g2_private_jwk(&jwk)?;
129
+ let public_key_base58 = Some(convert_base64_url_to_base58(jwk.x)?);
130
+ let d = jwk
131
+ .d
132
+ .ok_or(napi::Error::from_reason("jwk.d value is none"))?;
133
+ let private_key_base58 = Some(convert_base64_url_to_base58(d)?);
134
+
135
+ let kp = Bls12381G2KeyPair::new(Some(KeyPairOptions {
136
+ id,
137
+ controller,
138
+ public_key_base58,
139
+ private_key_base58,
140
+ }))
141
+ .await;
142
+
143
+ Ok(kp)
144
+ } else if let Some(jwk) = public_key_jwk {
145
+ assert_bls_12381_g2_public_jwk(&jwk)?;
146
+ let public_key_base58 = Some(convert_base64_url_to_base58(jwk.x)?);
147
+ let kp = Bls12381G2KeyPair::new(Some(KeyPairOptions {
148
+ id,
149
+ controller,
150
+ public_key_base58,
151
+ private_key_base58: None,
152
+ }))
153
+ .await;
154
+
155
+ Ok(kp)
156
+ } else {
157
+ Err(napi::Error::from_reason("The JWK provided is not a valid"))
158
+ }
159
+ }
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
+
129
239
  #[napi(getter)]
130
240
  pub fn public_key(&self) -> Option<String> {
131
241
  self
@@ -134,6 +244,26 @@ impl Bls12381G2KeyPair {
134
244
  .map(|b| bs58::encode(b).into_string())
135
245
  }
136
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
+
137
267
  #[napi(getter)]
138
268
  pub fn private_key(&self) -> Option<String> {
139
269
  self
@@ -141,6 +271,188 @@ impl Bls12381G2KeyPair {
141
271
  .clone()
142
272
  .map(|b| bs58::encode(b).into_string())
143
273
  }
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
+
299
+ #[napi]
300
+ pub fn signer(&self) -> KeyPairSigner {
301
+ KeyPairSigner { key: self.clone() }
302
+ }
303
+
304
+ #[napi]
305
+ pub fn verifier(&self) -> KeyPairVerifier {
306
+ KeyPairVerifier { key: self.clone() }
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
+ }
384
+ }
385
+
386
+ pub fn convert_base64_url_to_base58(value: String) -> Result<String> {
387
+ let decoded = URL_SAFE.decode(value).map_err(|err| {
388
+ napi::Error::from_reason(format!("failed to decode base64 url_safe. error {err}"))
389
+ })?;
390
+ Ok(bs58::encode(decoded).into_string())
391
+ }
392
+
393
+ #[napi]
394
+ pub struct KeyPairSigner {
395
+ key: Bls12381G2KeyPair,
396
+ }
397
+
398
+ #[napi(object)]
399
+ pub struct KeyPairSignerOptions {
400
+ pub data: Vec<Uint8Array>,
401
+ }
402
+
403
+ #[napi]
404
+ impl KeyPairSigner {
405
+ #[napi]
406
+ pub async fn sign(&self, options: KeyPairSignerOptions) -> Result<Uint8Array> {
407
+ if self.key.private_key_buffer.is_none() {
408
+ return Err(napi::Error::from_reason("No private key to sign with."));
409
+ }
410
+
411
+ let messages: Vec<Vec<u8>> = options.data.into_iter().map(|x| x.to_vec()).collect();
412
+ let key_pair = BlsKeyPair {
413
+ public_key: self.key.public_key_buffer.clone(),
414
+ secret_key: self.key.private_key_buffer.clone(),
415
+ };
416
+ let sig = bls_sign(BlsBbsSignRequest { messages, key_pair })
417
+ .await
418
+ .map_err(|err| napi::Error::from_reason(format!("bls_signed failed: {err}")))?;
419
+
420
+ Ok(Uint8Array::from(sig))
421
+ }
144
422
  }
145
423
 
146
- pub fn test() {}
424
+ #[napi]
425
+ pub struct KeyPairVerifier {
426
+ key: Bls12381G2KeyPair,
427
+ }
428
+
429
+ #[napi(object)]
430
+ pub struct KeyPairVerifierOptions {
431
+ pub data: Vec<Uint8Array>,
432
+ pub signature: Uint8Array,
433
+ }
434
+
435
+ #[napi]
436
+ impl KeyPairVerifier {
437
+ #[napi]
438
+ pub async fn verify(&self, options: KeyPairVerifierOptions) -> Result<bool> {
439
+ let KeyPairVerifierOptions { data, signature } = options;
440
+ let Some(public_key) = self.key.public_key_buffer.clone() else {
441
+ return Err(napi::Error::from_reason(
442
+ "key.public_key is required for verify",
443
+ ));
444
+ };
445
+ let signature = signature.to_vec();
446
+ let messages = data.into_iter().map(|a| a.to_vec()).collect::<Vec<_>>();
447
+
448
+ let verified = bls_verify(BlsBbsVerifyRequest {
449
+ public_key,
450
+ signature,
451
+ messages,
452
+ })
453
+ .await
454
+ .map_err(|err| napi::Error::from_reason(format!("bls_verify failed: {err}")))?;
455
+
456
+ Ok(verified)
457
+ }
458
+ }
package/src/types.rs ADDED
@@ -0,0 +1,187 @@
1
+ use napi::bindgen_prelude::*;
2
+
3
+ #[derive(Clone)]
4
+ #[napi]
5
+ pub struct Bls12381G2KeyPair {
6
+ pub id: Option<String>,
7
+ pub controller: Option<String>,
8
+ pub private_key_buffer: Option<Vec<u8>>,
9
+ pub public_key_buffer: Option<Vec<u8>>,
10
+ #[napi(js_name = "type")]
11
+ pub type_: String,
12
+ }
13
+
14
+ #[napi(object)]
15
+ pub struct KeyPairOptions {
16
+ pub id: Option<String>,
17
+ pub controller: Option<String>,
18
+ pub private_key_base58: Option<String>,
19
+ pub public_key_base58: Option<String>,
20
+ }
21
+
22
+ #[napi(object)]
23
+ pub struct JwkKeyPairOptions {
24
+ pub id: Option<String>,
25
+ pub controller: Option<String>,
26
+ pub private_key_jwk: Option<JsonWebKey>,
27
+ pub public_key_jwk: Option<JsonWebKey>,
28
+ }
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
+
42
+ #[napi(object)]
43
+ pub struct JsonWebKey {
44
+ /////
45
+ /// Indicates the key type used
46
+ /// For BLS12381_G1 and BLS12381_G2 the string "EC" MUST be used
47
+ //
48
+ /// @see https://tools.ietf.org/html/rfc7517#section-4.1
49
+ ///
50
+ pub kty: String,
51
+
52
+ /////
53
+ /// Indicates the curve this key is associated with
54
+ /// In the case of BLS12-381, the curve will also indicate if it's a G1 or G2 point
55
+ //
56
+ /// For a G1 point, use the string "BLS12381_G1"
57
+ /// For a G2 point, use the string "BLS12381_G2"
58
+ //
59
+ pub crv: String,
60
+
61
+ /////
62
+ /// This is a compression of the public key point
63
+ //
64
+ /// For a G1 public key, X is a 384bit base64url encoding of the octet string representation of the coordinate
65
+ /// For a G2 public key, X is a 768bit made up of the concatenation of two 384 bit x coordinates known as
66
+ /// x_a and x_b in the following form (x_a || x_b) as a base64url encoding of the octet string representation of the two coordinates
67
+ ///
68
+ pub x: String,
69
+
70
+ /////
71
+ /// @see https://tools.ietf.org/html/rfc7517#section-4.2
72
+ //
73
+ #[napi(js_name = "use")]
74
+ pub use_: Option<String>,
75
+
76
+ /////
77
+ /// @see https://tools.ietf.org/html/rfc7517#section-4.3
78
+ ///
79
+ pub key_ops: Option<Vec<String>>,
80
+
81
+ /////
82
+ /// @see https://tools.ietf.org/html/rfc7517#section-4.4
83
+ ///
84
+ pub alg: Option<String>,
85
+
86
+ /////
87
+ /// @see https://tools.ietf.org/html/rfc7517#section-4.5
88
+ //
89
+ /// TODO: Add note about referencing did-jose-extensions when ready
90
+ ///
91
+ pub kid: Option<String>,
92
+
93
+ /////
94
+ //
95
+ /// IMPORTANT NOTE: d represents the private key value and should not be shared
96
+ /// IT IS HIGHLY SENSITIVE DATA AND IF NOT SECURED PROPERLY CONSIDER THE KEY COMPROMISED
97
+ //
98
+ /// @see https://tools.ietf.org/html/rfc7517#section-9.2
99
+ ///
100
+ pub d: Option<String>,
101
+
102
+ /////
103
+ /// This coordinate is not used for BLS Keys, but is kept here to make the interface more standard
104
+ ///
105
+ pub y: Option<String>,
106
+
107
+ /////
108
+ /// @see https://www.w3.org/TR/WebCryptoAPI/#cryptokey-interface-members
109
+ ///
110
+ pub ext: Option<bool>,
111
+ }
112
+
113
+ #[napi(object)]
114
+ pub struct GenerateKeyPairOptions {
115
+ pub id: Option<String>,
116
+ pub controller: Option<String>,
117
+ pub seed: Option<Buffer>,
118
+ }
119
+
120
+ #[derive(PartialEq)]
121
+ pub enum BlsCurveName {
122
+ DeprecatedG1,
123
+ DeprecatedG2,
124
+ G1,
125
+ G2,
126
+ }
127
+
128
+ impl TryFrom<&str> for BlsCurveName {
129
+ type Error = napi::Error;
130
+
131
+ fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
132
+ match value {
133
+ "BLS12381_G1" => Ok(BlsCurveName::DeprecatedG1),
134
+ "BLS12381_G2" => Ok(BlsCurveName::DeprecatedG2),
135
+ "Bls12381G1" => Ok(BlsCurveName::G1),
136
+ "Bls12381G2" => Ok(BlsCurveName::G2),
137
+ _ => Err(napi::Error::from_reason(format!(
138
+ "no matching BlsNamedCurve for value: {}",
139
+ value
140
+ ))),
141
+ }
142
+ }
143
+ }
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
+
156
+ #[derive(PartialEq)]
157
+ pub enum JwkKty {
158
+ OctetKeyPair,
159
+ EC,
160
+ RSA,
161
+ }
162
+
163
+ impl TryFrom<&str> for JwkKty {
164
+ type Error = napi::Error;
165
+
166
+ fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
167
+ match value {
168
+ "OKP" => Ok(JwkKty::OctetKeyPair),
169
+ "EC" => Ok(JwkKty::EC),
170
+ "RSA" => Ok(JwkKty::RSA),
171
+ _ => Err(napi::Error::from_reason(format!(
172
+ "no matching JwkKty for value: {}",
173
+ value
174
+ ))),
175
+ }
176
+ }
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
+ }
@@ -0,0 +1,68 @@
1
+ use crate::types::{BlsCurveName, JsonWebKey, JwkKty};
2
+
3
+ pub fn check_common_bls_jwk_values(jwk: &JsonWebKey) -> napi::Result<()> {
4
+ BlsCurveName::try_from(jwk.crv.as_str())?;
5
+ let kty = JwkKty::try_from(jwk.kty.as_str())?;
6
+ if kty == JwkKty::EC || kty == JwkKty::OctetKeyPair {
7
+ Ok(())
8
+ } else {
9
+ Err(napi::Error::from_reason(format!(
10
+ "kty value: {} not allowed",
11
+ jwk.kty
12
+ )))
13
+ }
14
+ }
15
+
16
+ pub fn assert_public_bls_jwk(jwk: &JsonWebKey) -> napi::Result<()> {
17
+ check_common_bls_jwk_values(jwk)?;
18
+ if jwk.d.is_none() {
19
+ Ok(())
20
+ } else {
21
+ Err(napi::Error::from_reason(
22
+ "expected jwk.d to be none, found some value",
23
+ ))
24
+ }
25
+ }
26
+
27
+ pub fn assert_private_bls_jwk(jwk: &JsonWebKey) -> napi::Result<()> {
28
+ check_common_bls_jwk_values(jwk)?;
29
+ Ok(())
30
+ }
31
+
32
+ pub fn assert_bls_12381_g2_public_jwk(jwk: &JsonWebKey) -> napi::Result<()> {
33
+ let curve = BlsCurveName::try_from(jwk.crv.as_str())?;
34
+ if curve != BlsCurveName::DeprecatedG2 || curve != BlsCurveName::G2 {
35
+ return Err(napi::Error::from_reason(format!(
36
+ "curve value: {} not allowed",
37
+ jwk.crv
38
+ )));
39
+ }
40
+ assert_public_bls_jwk(jwk)?;
41
+ if jwk.x.len() != 128 {
42
+ Err(napi::Error::from_reason(format!(
43
+ "jwk.x expected length 128, got {}",
44
+ jwk.x.len()
45
+ )))
46
+ } else {
47
+ Ok(())
48
+ }
49
+ }
50
+
51
+ pub fn assert_bls_12381_g2_private_jwk(jwk: &JsonWebKey) -> napi::Result<()> {
52
+ let curve = BlsCurveName::try_from(jwk.crv.as_str())?;
53
+ if curve != BlsCurveName::DeprecatedG2 || curve != BlsCurveName::G2 {
54
+ return Err(napi::Error::from_reason(format!(
55
+ "curve value: {} not allowed",
56
+ jwk.crv
57
+ )));
58
+ }
59
+ assert_private_bls_jwk(jwk)?;
60
+ if jwk.x.len() != 128 {
61
+ Err(napi::Error::from_reason(format!(
62
+ "jwk.x expected length 128, got {}",
63
+ jwk.x.len()
64
+ )))
65
+ } else {
66
+ Ok(())
67
+ }
68
+ }
package/test.mjs ADDED
@@ -0,0 +1,78 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert';
3
+ import { Bls12381G2KeyPair } from '@mattrglobal/bls12381-key-pair';
4
+ import { Bls12381G2KeyPair as NewKeyPairClass } from './index.js'
5
+ import bs58 from 'bs58'
6
+ import bbs from '@nuggetslife/ffi-bbs-signatures'
7
+
8
+ const seed = Buffer.alloc(32, 0)
9
+ const address = '0x581510277Bc56802dE75BA021b66873437e0169f'
10
+ const addressBase58 = bs58.encode(Buffer.from(address.slice(2), 'hex'))
11
+
12
+ //Set of messages we wish to sign
13
+ const messages = [
14
+ Uint8Array.from(Buffer.from("message1", "utf8")),
15
+ Uint8Array.from(Buffer.from("message2", "utf8")),
16
+ ];
17
+
18
+ test('gen keypair for same seed', async () => {
19
+ const mattr = await Bls12381G2KeyPair.generate({ seed, });
20
+ const harry = await NewKeyPairClass.generate({ seed })
21
+ const ffi = await bbs.generateBls12381G2KeyPair(seed)
22
+ const ffi_sec_key = bs58.encode(Buffer.from(ffi.secretKey))
23
+ const ffi_pub_key = bs58.encode(Buffer.from(ffi.publicKey))
24
+
25
+ // private keys match
26
+ assert.equal(mattr.privateKey, harry.privateKey)
27
+ assert.equal(ffi_sec_key, harry.privateKey)
28
+
29
+ // public keys match
30
+ assert.equal(mattr.publicKey, harry.publicKey)
31
+ assert.equal(ffi_pub_key, harry.publicKey)
32
+ })
33
+
34
+
35
+ test('keypair from keypair', async () => {
36
+ const mattr = await Bls12381G2KeyPair.generate({ seed, });
37
+ const harry = await NewKeyPairClass.from({
38
+ id: mattr.id,
39
+ controller: mattr.controller,
40
+ publicKeyBase58: mattr.publicKey,
41
+ privateKeyBase58: mattr.privateKey
42
+ })
43
+
44
+ // private keys match
45
+ assert.equal(mattr.id, harry.id)
46
+ assert.equal(mattr.controller, harry.controller)
47
+ assert.equal(mattr.publicKey, harry.publicKey)
48
+ assert.equal(mattr.privateKey, harry.privateKey)
49
+ })
50
+
51
+
52
+ test('sign and verify', async () => {
53
+ const mattr = await Bls12381G2KeyPair.generate({ seed, });
54
+ const harry = await NewKeyPairClass.generate({ seed })
55
+
56
+ const ms = mattr.signer();
57
+ const hs = harry.signer();
58
+
59
+ const sign_opts = { data: messages }
60
+
61
+ let ms_sig = await ms.sign(sign_opts);
62
+ let hs_sig = await hs.sign(sign_opts);
63
+
64
+ assert.equal(ms_sig.length, hs_sig.length)
65
+
66
+ const hsv = harry.verifier();
67
+ const msv = mattr.verifier();
68
+
69
+ let hs_verify_hs_sig = await hsv.verify({ data: messages, signature: hs_sig })
70
+ let hs_verify_ms_sig = await hsv.verify({ data: messages, signature: ms_sig })
71
+ let ms_verify_ms_sig = await msv.verify({ data: messages, signature: ms_sig })
72
+ let ms_verify_hs_sig = await msv.verify({ data: messages, signature: hs_sig })
73
+
74
+ assert.equal(hs_verify_hs_sig, true)
75
+ assert.equal(hs_verify_ms_sig, true)
76
+ assert.equal(ms_verify_ms_sig, true)
77
+ assert.equal(ms_verify_hs_sig, true)
78
+ })