@nuggetslife/vc 0.0.5 → 0.0.7

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/index.d.ts CHANGED
@@ -9,11 +9,83 @@ 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 JsonWebKey {
19
+ /**
20
+ * Indicates the key type used
21
+ * For BLS12381_G1 and BLS12381_G2 the string "EC" MUST be used
22
+ * @see https://tools.ietf.org/html/rfc7517#section-4.1
23
+ *
24
+ */
25
+ kty: string
26
+ /**
27
+ * Indicates the curve this key is associated with
28
+ * In the case of BLS12-381, the curve will also indicate if it's a G1 or G2 point
29
+ * For a G1 point, use the string "BLS12381_G1"
30
+ * For a G2 point, use the string "BLS12381_G2"
31
+ */
32
+ crv: string
33
+ /**
34
+ * This is a compression of the public key point
35
+ * For a G1 public key, X is a 384bit base64url encoding of the octet string representation of the coordinate
36
+ * For a G2 public key, X is a 768bit made up of the concatenation of two 384 bit x coordinates known as
37
+ * 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
38
+ *
39
+ */
40
+ x: string
41
+ /** @see https://tools.ietf.org/html/rfc7517#section-4.2 */
42
+ use?: string
43
+ /**
44
+ * @see https://tools.ietf.org/html/rfc7517#section-4.3
45
+ *
46
+ */
47
+ keyOps?: Array<string>
48
+ /**
49
+ * @see https://tools.ietf.org/html/rfc7517#section-4.4
50
+ *
51
+ */
52
+ alg?: string
53
+ /**
54
+ * @see https://tools.ietf.org/html/rfc7517#section-4.5
55
+ * TODO: Add note about referencing did-jose-extensions when ready
56
+ *
57
+ */
58
+ kid?: string
59
+ /**
60
+ * IMPORTANT NOTE: d represents the private key value and should not be shared
61
+ * IT IS HIGHLY SENSITIVE DATA AND IF NOT SECURED PROPERLY CONSIDER THE KEY COMPROMISED
62
+ * @see https://tools.ietf.org/html/rfc7517#section-9.2
63
+ *
64
+ */
65
+ d?: string
66
+ /**
67
+ * This coordinate is not used for BLS Keys, but is kept here to make the interface more standard
68
+ *
69
+ */
70
+ y?: string
71
+ /**
72
+ * @see https://www.w3.org/TR/WebCryptoAPI/#cryptokey-interface-members
73
+ *
74
+ */
75
+ ext?: boolean
76
+ }
12
77
  export interface GenerateKeyPairOptions {
13
78
  id?: string
14
79
  controller?: string
15
80
  seed?: Buffer
16
81
  }
82
+ export interface KeyPairSignerOptions {
83
+ data: Array<Uint8Array>
84
+ }
85
+ export interface KeyPairVerifierOptions {
86
+ data: Array<Uint8Array>
87
+ signature: Uint8Array
88
+ }
17
89
  export class Bls12381G2KeyPair {
18
90
  id?: string
19
91
  controller?: string
@@ -23,6 +95,15 @@ export class Bls12381G2KeyPair {
23
95
  constructor(options?: KeyPairOptions | undefined | null)
24
96
  static generate(options?: GenerateKeyPairOptions | undefined | null): Promise<Bls12381G2KeyPair>
25
97
  static from(options: KeyPairOptions): Promise<Bls12381G2KeyPair>
98
+ static fromJwk(options: JwkKeyPairOptions): Promise<Bls12381G2KeyPair>
26
99
  get publicKey(): string | null
27
100
  get privateKey(): string | null
101
+ signer(): KeyPairSigner
102
+ verifier(): KeyPairVerifier
103
+ }
104
+ export class KeyPairSigner {
105
+ sign(options: KeyPairSignerOptions): Promise<Uint8Array>
106
+ }
107
+ export class KeyPairVerifier {
108
+ verify(options: KeyPairVerifierOptions): Promise<boolean>
28
109
  }
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.5",
3
+ "version": "0.0.7",
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.5"
41
+ "@nuggetslife/vc-darwin-arm64": "0.0.7"
42
42
  }
43
43
  }
package/src/lib.rs CHANGED
@@ -3,34 +3,16 @@
3
3
  #[macro_use]
4
4
  extern crate napi_derive;
5
5
 
6
- // use base64::{engine::general_purpose::STANDARD, Engine as _};
6
+ use base64::{engine::general_purpose::URL_SAFE, Engine as _};
7
7
  use napi::bindgen_prelude::*;
8
- use vc::bbs_signatures::bls12381::bls_generate_g2_key;
8
+ use types::{Bls12381G2KeyPair, GenerateKeyPairOptions, JwkKeyPairOptions, KeyPairOptions};
9
+ use validators::{assert_bls_12381_g2_private_jwk, assert_bls_12381_g2_public_jwk};
10
+ use vc::bbs_signatures::bls12381::{
11
+ bls_generate_g2_key, bls_sign, bls_verify, BlsBbsSignRequest, BlsBbsVerifyRequest, BlsKeyPair,
12
+ };
9
13
 
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
- }
14
+ pub mod types;
15
+ pub mod validators;
34
16
 
35
17
  #[napi]
36
18
  impl Bls12381G2KeyPair {
@@ -126,6 +108,49 @@ impl Bls12381G2KeyPair {
126
108
  Bls12381G2KeyPair::new(Some(options)).await
127
109
  }
128
110
 
111
+ #[napi(factory)]
112
+ pub async fn from_jwk(options: JwkKeyPairOptions) -> Result<Bls12381G2KeyPair> {
113
+ let JwkKeyPairOptions {
114
+ id,
115
+ controller,
116
+ private_key_jwk,
117
+ public_key_jwk,
118
+ } = options;
119
+
120
+ if let Some(jwk) = private_key_jwk {
121
+ assert_bls_12381_g2_private_jwk(&jwk)?;
122
+ let public_key_base58 = Some(convert_base64_url_to_base58(jwk.x)?);
123
+ let d = jwk
124
+ .d
125
+ .ok_or(napi::Error::from_reason("jwk.d value is none"))?;
126
+ let private_key_base58 = Some(convert_base64_url_to_base58(d)?);
127
+
128
+ let kp = Bls12381G2KeyPair::new(Some(KeyPairOptions {
129
+ id,
130
+ controller,
131
+ public_key_base58,
132
+ private_key_base58,
133
+ }))
134
+ .await;
135
+
136
+ Ok(kp)
137
+ } else if let Some(jwk) = public_key_jwk {
138
+ assert_bls_12381_g2_public_jwk(&jwk)?;
139
+ let public_key_base58 = Some(convert_base64_url_to_base58(jwk.x)?);
140
+ let kp = Bls12381G2KeyPair::new(Some(KeyPairOptions {
141
+ id,
142
+ controller,
143
+ public_key_base58,
144
+ private_key_base58: None,
145
+ }))
146
+ .await;
147
+
148
+ Ok(kp)
149
+ } else {
150
+ Err(napi::Error::from_reason("The JWK provided is not a valid"))
151
+ }
152
+ }
153
+
129
154
  #[napi(getter)]
130
155
  pub fn public_key(&self) -> Option<String> {
131
156
  self
@@ -141,6 +166,87 @@ impl Bls12381G2KeyPair {
141
166
  .clone()
142
167
  .map(|b| bs58::encode(b).into_string())
143
168
  }
169
+
170
+ #[napi]
171
+ pub fn signer(&self) -> KeyPairSigner {
172
+ KeyPairSigner { key: self.clone() }
173
+ }
174
+ #[napi]
175
+ pub fn verifier(&self) -> KeyPairVerifier {
176
+ KeyPairVerifier { key: self.clone() }
177
+ }
178
+ }
179
+
180
+ pub fn convert_base64_url_to_base58(value: String) -> Result<String> {
181
+ let decoded = URL_SAFE.decode(value).map_err(|err| {
182
+ napi::Error::from_reason(format!("failed to decode base64 url_safe. error {err}"))
183
+ })?;
184
+ Ok(bs58::encode(decoded).into_string())
185
+ }
186
+
187
+ #[napi]
188
+ pub struct KeyPairSigner {
189
+ key: Bls12381G2KeyPair,
144
190
  }
145
191
 
146
- pub fn test() {}
192
+ #[napi(object)]
193
+ pub struct KeyPairSignerOptions {
194
+ pub data: Vec<Uint8Array>,
195
+ }
196
+
197
+ #[napi]
198
+ impl KeyPairSigner {
199
+ #[napi]
200
+ pub async fn sign(&self, options: KeyPairSignerOptions) -> Result<Uint8Array> {
201
+ if self.key.private_key_buffer.is_none() {
202
+ return Err(napi::Error::from_reason("No private key to sign with."));
203
+ }
204
+
205
+ let messages: Vec<Vec<u8>> = options.data.into_iter().map(|x| x.to_vec()).collect();
206
+ let key_pair = BlsKeyPair {
207
+ public_key: self.key.public_key_buffer.clone(),
208
+ secret_key: self.key.private_key_buffer.clone(),
209
+ };
210
+ let sig = bls_sign(BlsBbsSignRequest { messages, key_pair })
211
+ .await
212
+ .map_err(|err| napi::Error::from_reason(format!("bls_signed failed: {err}")))?;
213
+
214
+ Ok(Uint8Array::from(sig))
215
+ }
216
+ }
217
+
218
+ #[napi]
219
+ pub struct KeyPairVerifier {
220
+ key: Bls12381G2KeyPair,
221
+ }
222
+
223
+ #[napi(object)]
224
+ pub struct KeyPairVerifierOptions {
225
+ pub data: Vec<Uint8Array>,
226
+ pub signature: Uint8Array,
227
+ }
228
+
229
+ #[napi]
230
+ impl KeyPairVerifier {
231
+ #[napi]
232
+ pub async fn verify(&self, options: KeyPairVerifierOptions) -> Result<bool> {
233
+ let KeyPairVerifierOptions { data, signature } = options;
234
+ let Some(public_key) = self.key.public_key_buffer.clone() else {
235
+ return Err(napi::Error::from_reason(
236
+ "key.public_key is required for verify",
237
+ ));
238
+ };
239
+ let signature = signature.to_vec();
240
+ let messages = data.into_iter().map(|a| a.to_vec()).collect::<Vec<_>>();
241
+
242
+ let verified = bls_verify(BlsBbsVerifyRequest {
243
+ public_key,
244
+ signature,
245
+ messages,
246
+ })
247
+ .await
248
+ .map_err(|err| napi::Error::from_reason(format!("bls_verify failed: {err}")))?;
249
+
250
+ Ok(verified)
251
+ }
252
+ }
package/src/types.rs ADDED
@@ -0,0 +1,154 @@
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 JsonWebKey {
32
+ /////
33
+ /// Indicates the key type used
34
+ /// For BLS12381_G1 and BLS12381_G2 the string "EC" MUST be used
35
+ //
36
+ /// @see https://tools.ietf.org/html/rfc7517#section-4.1
37
+ ///
38
+ pub kty: String,
39
+
40
+ /////
41
+ /// Indicates the curve this key is associated with
42
+ /// In the case of BLS12-381, the curve will also indicate if it's a G1 or G2 point
43
+ //
44
+ /// For a G1 point, use the string "BLS12381_G1"
45
+ /// For a G2 point, use the string "BLS12381_G2"
46
+ //
47
+ pub crv: String,
48
+
49
+ /////
50
+ /// This is a compression of the public key point
51
+ //
52
+ /// For a G1 public key, X is a 384bit base64url encoding of the octet string representation of the coordinate
53
+ /// For a G2 public key, X is a 768bit made up of the concatenation of two 384 bit x coordinates known as
54
+ /// 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
55
+ ///
56
+ pub x: String,
57
+
58
+ /////
59
+ /// @see https://tools.ietf.org/html/rfc7517#section-4.2
60
+ //
61
+ #[napi(js_name = "use")]
62
+ pub use_: Option<String>,
63
+
64
+ /////
65
+ /// @see https://tools.ietf.org/html/rfc7517#section-4.3
66
+ ///
67
+ pub key_ops: Option<Vec<String>>,
68
+
69
+ /////
70
+ /// @see https://tools.ietf.org/html/rfc7517#section-4.4
71
+ ///
72
+ pub alg: Option<String>,
73
+
74
+ /////
75
+ /// @see https://tools.ietf.org/html/rfc7517#section-4.5
76
+ //
77
+ /// TODO: Add note about referencing did-jose-extensions when ready
78
+ ///
79
+ pub kid: Option<String>,
80
+
81
+ /////
82
+ //
83
+ /// IMPORTANT NOTE: d represents the private key value and should not be shared
84
+ /// IT IS HIGHLY SENSITIVE DATA AND IF NOT SECURED PROPERLY CONSIDER THE KEY COMPROMISED
85
+ //
86
+ /// @see https://tools.ietf.org/html/rfc7517#section-9.2
87
+ ///
88
+ pub d: Option<String>,
89
+
90
+ /////
91
+ /// This coordinate is not used for BLS Keys, but is kept here to make the interface more standard
92
+ ///
93
+ pub y: Option<String>,
94
+
95
+ /////
96
+ /// @see https://www.w3.org/TR/WebCryptoAPI/#cryptokey-interface-members
97
+ ///
98
+ pub ext: Option<bool>,
99
+ }
100
+
101
+ #[napi(object)]
102
+ pub struct GenerateKeyPairOptions {
103
+ pub id: Option<String>,
104
+ pub controller: Option<String>,
105
+ pub seed: Option<Buffer>,
106
+ }
107
+
108
+ #[derive(PartialEq)]
109
+ pub enum BlsCurveName {
110
+ DeprecatedG1,
111
+ DeprecatedG2,
112
+ G1,
113
+ G2,
114
+ }
115
+
116
+ impl TryFrom<&str> for BlsCurveName {
117
+ type Error = napi::Error;
118
+
119
+ fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
120
+ match value {
121
+ "BLS12381_G1" => Ok(BlsCurveName::DeprecatedG1),
122
+ "BLS12381_G2" => Ok(BlsCurveName::DeprecatedG2),
123
+ "Bls12381G1" => Ok(BlsCurveName::G1),
124
+ "Bls12381G2" => Ok(BlsCurveName::G2),
125
+ _ => Err(napi::Error::from_reason(format!(
126
+ "no matching BlsNamedCurve for value: {}",
127
+ value
128
+ ))),
129
+ }
130
+ }
131
+ }
132
+
133
+ #[derive(PartialEq)]
134
+ pub enum JwkKty {
135
+ OctetKeyPair,
136
+ EC,
137
+ RSA,
138
+ }
139
+
140
+ impl TryFrom<&str> for JwkKty {
141
+ type Error = napi::Error;
142
+
143
+ fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
144
+ match value {
145
+ "OKP" => Ok(JwkKty::OctetKeyPair),
146
+ "EC" => Ok(JwkKty::EC),
147
+ "RSA" => Ok(JwkKty::RSA),
148
+ _ => Err(napi::Error::from_reason(format!(
149
+ "no matching JwkKty for value: {}",
150
+ value
151
+ ))),
152
+ }
153
+ }
154
+ }
@@ -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
+ })