@pezkuwi/wasm-crypto 7.5.13 → 7.5.15

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.
Files changed (122) hide show
  1. package/Cargo.toml +50 -0
  2. package/Xargo.toml +2 -0
  3. package/build/README.md +29 -0
  4. package/build/bundle-pezkuwi-wasm-crypto.js +2092 -0
  5. package/build/bundle.d.ts +40 -0
  6. package/build/bundle.js +505 -0
  7. package/build/cjs/bundle.d.ts +40 -0
  8. package/build/cjs/bundle.js +544 -0
  9. package/{index.d.ts → build/cjs/index.d.ts} +0 -1
  10. package/build/cjs/index.js +4 -0
  11. package/build/cjs/init.js +21 -0
  12. package/build/cjs/initNone.js +20 -0
  13. package/build/cjs/initOnlyAsm.js +20 -0
  14. package/build/cjs/initOnlyWasm.js +20 -0
  15. package/build/cjs/initWasmAsm.js +20 -0
  16. package/build/cjs/packageInfo.js +4 -0
  17. package/build/index.d.ts +1 -0
  18. package/build/index.js +1 -0
  19. package/build/init.d.ts +16 -0
  20. package/build/init.js +17 -0
  21. package/build/initNone.d.ts +10 -0
  22. package/build/initNone.js +17 -0
  23. package/build/initOnlyAsm.d.ts +10 -0
  24. package/build/initOnlyAsm.js +17 -0
  25. package/build/initOnlyWasm.d.ts +10 -0
  26. package/build/initOnlyWasm.js +17 -0
  27. package/build/initWasmAsm.d.ts +10 -0
  28. package/build/initWasmAsm.js +17 -0
  29. package/build/package.json +208 -0
  30. package/build/packageDetect.d.ts +1 -0
  31. package/build/packageDetect.js +8 -0
  32. package/build/packageInfo.d.ts +6 -0
  33. package/build/packageInfo.js +1 -0
  34. package/build-deno/README.md +25 -0
  35. package/build-deno/bundle.ts +597 -0
  36. package/build-deno/index.ts +2 -0
  37. package/build-deno/init.ts +23 -0
  38. package/build-deno/initNone.ts +21 -0
  39. package/build-deno/initOnlyAsm.ts +21 -0
  40. package/build-deno/initOnlyWasm.ts +21 -0
  41. package/build-deno/initWasmAsm.ts +21 -0
  42. package/build-deno/mod.ts +2 -0
  43. package/build-deno/packageDetect.ts +12 -0
  44. package/build-deno/packageInfo.ts +3 -0
  45. package/build-deno/rs/.editorconfig +10 -0
  46. package/build-tsc/bundle.d.ts +40 -0
  47. package/{cjs → build-tsc}/index.d.ts +0 -1
  48. package/build-tsc-cjs/bundle.js +544 -0
  49. package/{cjs → build-tsc-cjs}/index.js +0 -1
  50. package/build-tsc-cjs/packageDetect.js +10 -0
  51. package/{cjs → build-tsc-cjs}/packageInfo.js +1 -1
  52. package/build-tsc-esm/bundle.js +505 -0
  53. package/{index.js → build-tsc-esm/index.js} +0 -1
  54. package/{packageInfo.js → build-tsc-esm/packageInfo.js} +1 -1
  55. package/package.json +89 -87
  56. package/src/bundle.ts +613 -0
  57. package/src/index.ts +5 -0
  58. package/src/init.ts +25 -0
  59. package/src/initNone.ts +23 -0
  60. package/src/initOnlyAsm.ts +23 -0
  61. package/src/initOnlyWasm.ts +23 -0
  62. package/src/initWasmAsm.ts +23 -0
  63. package/src/lib.rs +24 -0
  64. package/src/mod.ts +4 -0
  65. package/src/packageDetect.ts +16 -0
  66. package/src/packageInfo.ts +6 -0
  67. package/src/rs/.editorconfig +10 -0
  68. package/src/rs/bip39.rs +139 -0
  69. package/src/rs/ed25519.rs +142 -0
  70. package/src/rs/hashing.rs +322 -0
  71. package/src/rs/secp256k1.rs +150 -0
  72. package/src/rs/sr25519.rs +331 -0
  73. package/src/rs/vrf.rs +144 -0
  74. package/test/all/bip39.js +86 -0
  75. package/test/all/ed25519.js +84 -0
  76. package/test/all/hashing.js +138 -0
  77. package/test/all/index.js +126 -0
  78. package/test/all/secp256k1.js +105 -0
  79. package/test/all/sr25519.js +211 -0
  80. package/test/all/vrf.js +74 -0
  81. package/test/asm.js +10 -0
  82. package/test/deno.ts +38 -0
  83. package/test/jest.spec.ts +25 -0
  84. package/test/loader-build.js +39 -0
  85. package/test/wasm.js +8 -0
  86. package/tsconfig.build.json +19 -0
  87. package/tsconfig.build.tsbuildinfo +1 -0
  88. package/tsconfig.spec.json +16 -0
  89. package/tsconfig.spec.tsbuildinfo +1 -0
  90. package/bundle-pezkuwi-wasm-crypto.js +0 -825
  91. package/bundle.d.ts +0 -37
  92. package/bundle.js +0 -165
  93. package/cjs/bundle.d.ts +0 -37
  94. package/cjs/bundle.js +0 -171
  95. /package/{LICENSE → build/LICENSE} +0 -0
  96. /package/{init.d.ts → build/cjs/init.d.ts} +0 -0
  97. /package/{initNone.d.ts → build/cjs/initNone.d.ts} +0 -0
  98. /package/{initOnlyAsm.d.ts → build/cjs/initOnlyAsm.d.ts} +0 -0
  99. /package/{initOnlyWasm.d.ts → build/cjs/initOnlyWasm.d.ts} +0 -0
  100. /package/{initWasmAsm.d.ts → build/cjs/initWasmAsm.d.ts} +0 -0
  101. /package/{cjs → build/cjs}/package.json +0 -0
  102. /package/{packageDetect.d.ts → build/cjs/packageDetect.d.ts} +0 -0
  103. /package/{cjs → build/cjs}/packageDetect.js +0 -0
  104. /package/{packageInfo.d.ts → build/cjs/packageInfo.d.ts} +0 -0
  105. /package/{cjs → build-tsc}/init.d.ts +0 -0
  106. /package/{cjs → build-tsc}/initNone.d.ts +0 -0
  107. /package/{cjs → build-tsc}/initOnlyAsm.d.ts +0 -0
  108. /package/{cjs → build-tsc}/initOnlyWasm.d.ts +0 -0
  109. /package/{cjs → build-tsc}/initWasmAsm.d.ts +0 -0
  110. /package/{cjs → build-tsc}/packageDetect.d.ts +0 -0
  111. /package/{cjs → build-tsc}/packageInfo.d.ts +0 -0
  112. /package/{cjs → build-tsc-cjs}/init.js +0 -0
  113. /package/{cjs → build-tsc-cjs}/initNone.js +0 -0
  114. /package/{cjs → build-tsc-cjs}/initOnlyAsm.js +0 -0
  115. /package/{cjs → build-tsc-cjs}/initOnlyWasm.js +0 -0
  116. /package/{cjs → build-tsc-cjs}/initWasmAsm.js +0 -0
  117. /package/{init.js → build-tsc-esm/init.js} +0 -0
  118. /package/{initNone.js → build-tsc-esm/initNone.js} +0 -0
  119. /package/{initOnlyAsm.js → build-tsc-esm/initOnlyAsm.js} +0 -0
  120. /package/{initOnlyWasm.js → build-tsc-esm/initOnlyWasm.js} +0 -0
  121. /package/{initWasmAsm.js → build-tsc-esm/initWasmAsm.js} +0 -0
  122. /package/{packageDetect.js → build-tsc-esm/packageDetect.js} +0 -0
@@ -0,0 +1,331 @@
1
+ // Copyright 2019-2025 @pezkuwi/wasm-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // Based on original work by Paritytech (https://github.com/pezkuwichain/schnorrkel-js/)
5
+ // Adapted for PezkuwiChain by Kurdistan Tech Institute
6
+ // Original fork history: pezkuwichain/schnorrkel-js → pezkuwi-js/schnorrkel-js → pezkuwi/wasm
7
+
8
+ use curve25519_dalek::scalar::Scalar;
9
+ use schnorrkel::{
10
+ ExpansionMode, Keypair, MiniSecretKey, PublicKey, SecretKey, Signature,
11
+ derive::{Derivation, ChainCode, CHAIN_CODE_LENGTH},
12
+ };
13
+ use wasm_bindgen::prelude::*;
14
+
15
+ // We must make sure that this is the same as declared in the bizinikiwi source code.
16
+ // PezkuwiChain uses "bizinikiwi" as the signing context instead of "bizinikiwi".
17
+ const CTX: &'static [u8] = b"bizinikiwi";
18
+
19
+ /// ChainCode construction helper
20
+ fn new_cc(data: &[u8]) -> ChainCode {
21
+ let mut cc = [0u8; CHAIN_CODE_LENGTH];
22
+
23
+ cc.copy_from_slice(&data);
24
+
25
+ ChainCode(cc)
26
+ }
27
+
28
+ /// Perform a derivation on a secret
29
+ ///
30
+ /// * secret: UIntArray with 64 bytes
31
+ /// * cc: UIntArray with 32 bytes
32
+ ///
33
+ /// returned vector the derived keypair as a array of 96 bytes
34
+ #[wasm_bindgen]
35
+ pub fn ext_sr_derive_keypair_hard(pair: &[u8], cc: &[u8]) -> Vec<u8> {
36
+ match Keypair::from_half_ed25519_bytes(pair) {
37
+ Ok(p) => p.secret
38
+ .hard_derive_mini_secret_key(Some(new_cc(cc)), &[]).0
39
+ .expand_to_keypair(ExpansionMode::Ed25519)
40
+ .to_half_ed25519_bytes()
41
+ .to_vec(),
42
+ _ => panic!("Invalid pair provided.")
43
+ }
44
+ }
45
+
46
+ /// Perform a derivation on a secret
47
+ ///
48
+ /// * secret: UIntArray with 64 bytes
49
+ /// * cc: UIntArray with 32 bytes
50
+ ///
51
+ /// returned vector the derived keypair as a array of 96 bytes
52
+ #[wasm_bindgen]
53
+ pub fn ext_sr_derive_keypair_soft(pair: &[u8], cc: &[u8]) -> Vec<u8> {
54
+ match Keypair::from_half_ed25519_bytes(pair) {
55
+ Ok(p) => p
56
+ .derived_key_simple(new_cc(cc), &[]).0
57
+ .to_half_ed25519_bytes()
58
+ .to_vec(),
59
+ _ => panic!("Invalid pair provided.")
60
+ }
61
+ }
62
+
63
+ /// Perform a derivation on a publicKey
64
+ ///
65
+ /// * pubkey: UIntArray with 32 bytes
66
+ /// * cc: UIntArray with 32 bytes
67
+ ///
68
+ /// returned vector is the derived publicKey as a array of 32 bytes
69
+ #[wasm_bindgen]
70
+ pub fn ext_sr_derive_public_soft(pubkey: &[u8], cc: &[u8]) -> Vec<u8> {
71
+ match PublicKey::from_bytes(pubkey) {
72
+ Ok(k) => k
73
+ .derived_key_simple(new_cc(cc), &[]).0
74
+ .to_bytes()
75
+ .to_vec(),
76
+ _ => panic!("Invalid pubkey provided.")
77
+ }
78
+ }
79
+
80
+ /// Generate a key pair.
81
+ ///
82
+ /// * seed: UIntArray with 32 element
83
+ ///
84
+ /// returned vector is the concatenation of first the private key (64 bytes)
85
+ /// followed by the public key (32) bytes.
86
+ #[wasm_bindgen]
87
+ pub fn ext_sr_from_seed(seed: &[u8]) -> Vec<u8> {
88
+ match MiniSecretKey::from_bytes(seed) {
89
+ Ok(s) => s
90
+ .expand_to_keypair(ExpansionMode::Ed25519)
91
+ .to_half_ed25519_bytes()
92
+ .to_vec(),
93
+ _ => panic!("Invalid seed provided.")
94
+ }
95
+ }
96
+
97
+ /// Generate a key pair from a known pair. (This is not exposed via WASM)
98
+ ///
99
+ /// * seed: UIntArray with 96 element
100
+ ///
101
+ /// returned vector is the concatenation of first the private key (64 bytes)
102
+ /// followed by the public key (32) bytes.
103
+ pub fn ext_sr_from_pair(pair: &[u8]) -> Vec<u8> {
104
+ match Keypair::from_half_ed25519_bytes(pair) {
105
+ Ok(p) => p
106
+ .to_half_ed25519_bytes()
107
+ .to_vec(),
108
+ _ => panic!("Invalid pair provided.")
109
+ }
110
+ }
111
+
112
+ /// Sign a message
113
+ ///
114
+ /// The combination of both public and private key must be provided.
115
+ /// This is effectively equivalent to a keypair.
116
+ ///
117
+ /// * pubkey: UIntArray with 32 element
118
+ /// * private: UIntArray with 64 element
119
+ /// * message: Arbitrary length UIntArray
120
+ ///
121
+ /// * returned vector is the signature consisting of 64 bytes.
122
+ #[wasm_bindgen]
123
+ pub fn ext_sr_sign(pubkey: &[u8], secret: &[u8], message: &[u8]) -> Vec<u8> {
124
+ match (SecretKey::from_ed25519_bytes(secret), PublicKey::from_bytes(pubkey)) {
125
+ (Ok(s), Ok(k)) => s
126
+ .sign_simple(CTX, message, &k)
127
+ .to_bytes()
128
+ .to_vec(),
129
+ _ => panic!("Invalid secret or pubkey provided.")
130
+ }
131
+ }
132
+
133
+ /// Verify a message and its corresponding against a public key;
134
+ ///
135
+ /// * signature: UIntArray with 64 element
136
+ /// * message: Arbitrary length UIntArray
137
+ /// * pubkey: UIntArray with 32 element
138
+ #[wasm_bindgen]
139
+ pub fn ext_sr_verify(signature: &[u8], message: &[u8], pubkey: &[u8]) -> bool {
140
+ match (Signature::from_bytes(signature), PublicKey::from_bytes(pubkey)) {
141
+ (Ok(s), Ok(k)) => k
142
+ .verify_simple(CTX, message, &s)
143
+ .is_ok(),
144
+ _ => false
145
+ }
146
+ }
147
+
148
+ /// Key agreement between other's public key and self secret key.
149
+ ///
150
+ /// * pubkey: UIntArray with 32 element
151
+ /// * secret: UIntArray with 64 element
152
+ ///
153
+ /// * returned vector is the generated secret of 32 bytes.
154
+ #[wasm_bindgen]
155
+ pub fn ext_sr_agree(pubkey: &[u8], secret: &[u8]) -> Vec<u8> {
156
+ match SecretKey::from_ed25519_bytes(secret) {
157
+ Ok(s) => {
158
+ // The first 32 bytes holds the canonical private key
159
+ let mut key = [0u8; 32];
160
+
161
+ key.copy_from_slice(&s.to_bytes()[0..32]);
162
+
163
+ match (Scalar::from_canonical_bytes(key), PublicKey::from_bytes(pubkey)) {
164
+ (Some(n), Ok(k)) => (&n * k.as_point())
165
+ .compress().0
166
+ .to_vec(),
167
+ _ => panic!("Invalid scalar or pubkey provided.")
168
+ }
169
+ },
170
+ _ => panic!("Invalid secret provided.")
171
+ }
172
+ }
173
+
174
+ #[cfg(test)]
175
+ pub mod tests {
176
+ extern crate rand;
177
+ extern crate schnorrkel;
178
+
179
+ use super::*;
180
+ use hex_literal::hex;
181
+ use schnorrkel::{SIGNATURE_LENGTH, KEYPAIR_LENGTH, SECRET_KEY_LENGTH};
182
+
183
+ fn generate_random_seed() -> Vec<u8> {
184
+ (0..32).map(|_| rand::random::<u8>()).collect()
185
+ }
186
+
187
+ #[test]
188
+ fn can_new_keypair() {
189
+ let seed = generate_random_seed();
190
+ let keypair = ext_sr_from_seed(seed.as_slice());
191
+
192
+ assert!(keypair.len() == KEYPAIR_LENGTH);
193
+ }
194
+
195
+ #[test]
196
+ fn creates_pair_from_known_seed() {
197
+ let seed = hex!("fac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e");
198
+ let expected = hex!("46ebddef8cd9bb167dc30878d7113b7e168e6f0646beffd77d69d39bad76b47a");
199
+ let keypair = ext_sr_from_seed(&seed);
200
+ let public = &keypair[SECRET_KEY_LENGTH..KEYPAIR_LENGTH];
201
+
202
+ assert_eq!(public, expected);
203
+ }
204
+
205
+ #[test]
206
+ fn new_pair_from_known_pair() {
207
+ let input = hex!("28b0ae221c6bb06856b287f60d7ea0d98552ea5a16db16956849aa371db3eb51fd190cce74df356432b410bd64682309d6dedb27c76845daf388557cbac3ca3446ebddef8cd9bb167dc30878d7113b7e168e6f0646beffd77d69d39bad76b47a");
208
+ let keypair = ext_sr_from_pair(&input);
209
+ let expected = hex!("46ebddef8cd9bb167dc30878d7113b7e168e6f0646beffd77d69d39bad76b47a");
210
+ let public = &keypair[SECRET_KEY_LENGTH..KEYPAIR_LENGTH];
211
+
212
+ assert_eq!(public, expected);
213
+ }
214
+
215
+ #[test]
216
+ fn can_sign_message() {
217
+ let seed = generate_random_seed();
218
+ let keypair = ext_sr_from_seed(seed.as_slice());
219
+ let private = &keypair[0..SECRET_KEY_LENGTH];
220
+ let public = &keypair[SECRET_KEY_LENGTH..KEYPAIR_LENGTH];
221
+ let message = b"this is a message";
222
+ let signature = ext_sr_sign(public, private, message);
223
+
224
+ assert!(signature.len() == SIGNATURE_LENGTH);
225
+ }
226
+
227
+ #[test]
228
+ fn can_verify_message() {
229
+ let seed = generate_random_seed();
230
+ let keypair = ext_sr_from_seed(seed.as_slice());
231
+ let private = &keypair[0..SECRET_KEY_LENGTH];
232
+ let public = &keypair[SECRET_KEY_LENGTH..KEYPAIR_LENGTH];
233
+ let message = b"this is a message";
234
+ let signature = ext_sr_sign(public, private, message);
235
+ let is_valid = ext_sr_verify(&signature[..], message, public);
236
+
237
+ assert!(is_valid);
238
+ }
239
+
240
+ #[test]
241
+ fn can_verify_known_message() {
242
+ let message = b"I hereby verify that I control 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY";
243
+ let public = hex!("d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d");
244
+ let signature = hex!("1037eb7e51613d0dcf5930ae518819c87d655056605764840d9280984e1b7063c4566b55bf292fcab07b369d01095879b50517beca4d26e6a65866e25fec0d83");
245
+ let is_valid = ext_sr_verify(&signature, message, &public);
246
+
247
+ assert!(is_valid);
248
+ }
249
+
250
+ #[test]
251
+ fn can_verify_known_wrapped_message() {
252
+ let message = b"<Bytes>message to sign</Bytes>";
253
+ let public = hex!("f84d048da2ddae2d9d8fd6763f469566e8817a26114f39408de15547f6d47805");
254
+ let signature = hex!("48ce2c90e08651adfc8ecef84e916f6d1bb51ebebd16150ee12df247841a5437951ea0f9d632ca165e6ab391532e75e701be6a1caa88c8a6bcca3511f55b4183");
255
+ let is_valid = ext_sr_verify(&signature, message, &public);
256
+
257
+ assert!(is_valid);
258
+ }
259
+
260
+ #[test]
261
+ fn can_verify_known_wrapped_message_fail() {
262
+ let message = b"message to sign";
263
+ let public = hex!("f84d048da2ddae2d9d8fd6763f469566e8817a26114f39408de15547f6d47805");
264
+ let signature = hex!("48ce2c90e08651adfc8ecef84e916f6d1bb51ebebd16150ee12df247841a5437951ea0f9d632ca165e6ab391532e75e701be6a1caa88c8a6bcca3511f55b4183");
265
+ let is_valid = ext_sr_verify(&signature, message, &public);
266
+
267
+ assert!(!is_valid);
268
+ }
269
+
270
+ #[test]
271
+ fn soft_derives_pair() {
272
+ let cc = hex!("0c666f6f00000000000000000000000000000000000000000000000000000000"); // foo
273
+ let seed = hex!("fac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e");
274
+ let expected = hex!("40b9675df90efa6069ff623b0fdfcf706cd47ca7452a5056c7ad58194d23440a");
275
+ let keypair = ext_sr_from_seed(&seed);
276
+ let derived = ext_sr_derive_keypair_soft(&keypair, &cc);
277
+ let public = &derived[SECRET_KEY_LENGTH..KEYPAIR_LENGTH];
278
+
279
+ assert_eq!(public, expected);
280
+ }
281
+
282
+ #[test]
283
+ fn soft_derives_public() {
284
+ let cc = hex!("0c666f6f00000000000000000000000000000000000000000000000000000000"); // foo
285
+ let public = hex!("46ebddef8cd9bb167dc30878d7113b7e168e6f0646beffd77d69d39bad76b47a");
286
+ let expected = hex!("40b9675df90efa6069ff623b0fdfcf706cd47ca7452a5056c7ad58194d23440a");
287
+ let derived = ext_sr_derive_public_soft(&public, &cc);
288
+
289
+ assert_eq!(derived, expected);
290
+ }
291
+
292
+ #[test]
293
+ fn hard_derives_pair() {
294
+ let cc = hex!("14416c6963650000000000000000000000000000000000000000000000000000"); // Alice
295
+ let seed = hex!("fac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e");
296
+ let expected = hex!("d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d");
297
+ let keypair = ext_sr_from_seed(&seed);
298
+ let derived = ext_sr_derive_keypair_hard(&keypair, &cc);
299
+ let public = &derived[SECRET_KEY_LENGTH..KEYPAIR_LENGTH];
300
+
301
+ assert_eq!(public, expected);
302
+ }
303
+
304
+ #[test]
305
+ fn key_agreement() {
306
+ let self_seed = hex!("98b3d305d5a5eace562387e47e59badd4d77e3f72cabfb10a60f8a197059f0a8");
307
+ let other_seed = hex!("9732eea001851ff862d949a1699c9971f3a26edbede2ad7922cbbe9a0701f366");
308
+ let expected = hex!("b03a0b198c34c16f35cae933d88b16341b4cef3e84e851f20e664c6a30527f4e");
309
+ let self_pair = ext_sr_from_seed(&self_seed);
310
+ let self_sk = &self_pair[0..SECRET_KEY_LENGTH];
311
+ let self_pk = &self_pair[SECRET_KEY_LENGTH..KEYPAIR_LENGTH];
312
+ let other_pair = ext_sr_from_seed(&other_seed);
313
+ let other_sk = &other_pair[0..SECRET_KEY_LENGTH];
314
+ let other_pk = &other_pair[SECRET_KEY_LENGTH..KEYPAIR_LENGTH];
315
+
316
+ assert_eq!(ext_sr_agree(self_pk, other_sk), expected);
317
+ assert_eq!(ext_sr_agree(other_pk, self_sk), expected);
318
+
319
+ let seed = generate_random_seed();
320
+ let self_pair = ext_sr_from_seed(seed.as_slice());
321
+ let self_sk = &self_pair[0..SECRET_KEY_LENGTH];
322
+ let self_pk = &self_pair[SECRET_KEY_LENGTH..KEYPAIR_LENGTH];
323
+
324
+ let seed = generate_random_seed();
325
+ let other_pair = ext_sr_from_seed(seed.as_slice());
326
+ let other_sk = &other_pair[0..SECRET_KEY_LENGTH];
327
+ let other_pk = &other_pair[SECRET_KEY_LENGTH..KEYPAIR_LENGTH];
328
+
329
+ assert_eq!(ext_sr_agree(self_pk, other_sk), ext_sr_agree(other_pk, self_sk));
330
+ }
331
+ }
package/src/rs/vrf.rs ADDED
@@ -0,0 +1,144 @@
1
+ // Copyright 2019-2025 @pezkuwi/wasm-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // Copyright 2019 Paritytech via https://github.com/pezkuwichain/schnorrkel-js/
5
+ //
6
+ // Originally developed (as a fork) in https://github.com/pezkuwi-js/schnorrkel-js/
7
+ // which was adpated from the initial https://github.com/pezkuwichain/schnorrkel-js/
8
+ // forked at commit eff430ddc3090f56317c80654208b8298ef7ab3f
9
+
10
+ use merlin::Transcript;
11
+ use schnorrkel::{signing_context, vrf::{VRFOutput, VRFProof}, PublicKey, SecretKey};
12
+ use wasm_bindgen::prelude::*;
13
+
14
+ /// Size of VRF output, bytes
15
+ pub const OUTPUT_SIZE: usize = 32;
16
+
17
+ /// Size of VRF proof, bytes
18
+ pub const PROOF_SIZE: usize = 64;
19
+
20
+ /// Size of VRF raw output, bytes
21
+ pub const RAW_OUTPUT_SIZE: usize = 16;
22
+
23
+ /// Size of VRF limit, bytes
24
+ pub const THRESHOLD_SIZE: usize = 16;
25
+
26
+ /// Size of the final full output
27
+ pub const RESULT_SIZE: usize = OUTPUT_SIZE + PROOF_SIZE;
28
+
29
+ pub fn new_transcript(extra: &[u8]) -> Transcript {
30
+ let mut transcript = Transcript::new(b"VRF");
31
+
32
+ // if is important that we don't append when empty, since the
33
+ // append in merging encodes length information
34
+ if extra.len() > 0 {
35
+ transcript.append_message(b"", extra);
36
+ }
37
+
38
+ transcript
39
+ }
40
+
41
+ // We don't use the non-extra sign and verify internally since in sr25519 they only wrap -
42
+ //
43
+ // https://github.com/w3f/schnorrkel/blob/8fa2ad3e9fbf0b652c724df6a87a4b3c5500f759/src/vrf.rs#L660
44
+
45
+ /// Run a Random Verifiable Function (VRF) on one single input
46
+ /// (message) transcript, and an extra message transcript,
47
+ /// producing the output signature and corresponding short proof.
48
+ ///
49
+ /// * secret: UIntArray with 64 element
50
+ /// * context: Arbitrary length UIntArray
51
+ /// * message: Arbitrary length UIntArray
52
+ /// * extra: Arbitrary length UIntArray
53
+ ///
54
+ /// * returned vector is the 32-byte output (signature) and 64-byte proof.
55
+ #[wasm_bindgen]
56
+ pub fn ext_vrf_sign(secret: &[u8], ctx: &[u8], msg: &[u8], extra: &[u8]) -> Vec<u8> {
57
+ match SecretKey::from_ed25519_bytes(secret) {
58
+ Ok(s) => {
59
+ let mut res: [u8; RESULT_SIZE] = [0u8; RESULT_SIZE];
60
+ let (io, proof, _) = s
61
+ .to_keypair()
62
+ .vrf_sign_extra(signing_context(ctx).bytes(msg), new_transcript(extra));
63
+
64
+ res[..OUTPUT_SIZE].copy_from_slice(io.as_output_bytes());
65
+ res[OUTPUT_SIZE..].copy_from_slice(&proof.to_bytes());
66
+
67
+ res.to_vec()
68
+ },
69
+ _ => panic!("Invalid secret provided.")
70
+ }
71
+ }
72
+
73
+ /// Verify VRF proof for one single input transcript, and an extra message transcript,
74
+ /// and corresponding output.
75
+ ///
76
+ /// * pubkey: UIntArray with 32 element
77
+ /// * context: Arbitrary length UIntArray
78
+ /// * message: Arbitrary length UIntArray
79
+ /// * extra: Arbitrary length UIntArray
80
+ /// * out_and_proof: 96-byte output & proof array from the ext_sign function.
81
+ #[wasm_bindgen]
82
+ pub fn ext_vrf_verify(pubkey: &[u8], ctx: &[u8], msg: &[u8], extra: &[u8], out: &[u8]) -> bool {
83
+ match (PublicKey::from_bytes(pubkey), VRFOutput::from_bytes(&out[..OUTPUT_SIZE]), VRFProof::from_bytes(&out[OUTPUT_SIZE..RESULT_SIZE])) {
84
+ (Ok(k), Ok(o), Ok(p)) => k
85
+ .vrf_verify_extra(signing_context(ctx).bytes(msg), &o, &p, new_transcript(extra))
86
+ .is_ok(),
87
+ _ => false,
88
+ }
89
+ }
90
+
91
+ #[cfg(test)]
92
+ pub mod tests {
93
+ extern crate rand;
94
+ extern crate schnorrkel;
95
+
96
+ use super::*;
97
+ use crate::sr25519::ext_sr_from_seed;
98
+ use schnorrkel::{KEYPAIR_LENGTH, SECRET_KEY_LENGTH};
99
+
100
+ fn generate_random_seed() -> Vec<u8> {
101
+ (0..32).map(|_| rand::random::<u8>()).collect()
102
+ }
103
+
104
+ #[test]
105
+ fn sign_extra_and_verify() {
106
+ let seed = generate_random_seed();
107
+ let keypair = ext_sr_from_seed(seed.as_slice());
108
+ let private = &keypair[0..SECRET_KEY_LENGTH];
109
+ let public = &keypair[SECRET_KEY_LENGTH..KEYPAIR_LENGTH];
110
+ let context = b"my VRF context";
111
+ let message = b"this is a message";
112
+ let extra = b"this is an extra";
113
+
114
+ // Perform multiple sign_extra calls w/ same context, message, extra args
115
+ let out1 = ext_vrf_sign(private, context, message, extra);
116
+ let out2 = ext_vrf_sign(private, context, message, extra);
117
+
118
+ // Basic size checks
119
+ assert!(out1.len() == RESULT_SIZE);
120
+ assert!(out2.len() == RESULT_SIZE);
121
+
122
+ // Given the same context, message & extra, output should be deterministic
123
+ assert_eq!(&out1[..OUTPUT_SIZE], &out2[..OUTPUT_SIZE]);
124
+
125
+ // But proof is non-deterministic
126
+ assert_ne!(&out1[OUTPUT_SIZE..], &out2[OUTPUT_SIZE..]);
127
+
128
+ // VRF outputs can verified w/ original context, message & extra args
129
+ assert!(ext_vrf_verify(
130
+ public,
131
+ context,
132
+ message,
133
+ extra,
134
+ &out1
135
+ ));
136
+ assert!(ext_vrf_verify(
137
+ public,
138
+ context,
139
+ message,
140
+ extra,
141
+ &out2
142
+ ));
143
+ }
144
+ }
@@ -0,0 +1,86 @@
1
+ // Copyright 2019-2026 @pezkuwi/wasm-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /* global it */
5
+
6
+ import { assert, u8aToHex } from '@pezkuwi/util';
7
+
8
+ /**
9
+ * @param {*} wasm
10
+ */
11
+ export function bip39Generate (wasm) {
12
+ it('generates a bip39 phrase', () => {
13
+ const res = wasm.bip39Generate(21);
14
+
15
+ // console.log('\tPHR', res);
16
+
17
+ assert(res.split(' ').length === 21, 'ERROR: Invalid bip39 Phase length');
18
+ });
19
+ }
20
+
21
+ /**
22
+ * @param {*} wasm
23
+ */
24
+ export function bip39GenerateSubsequent (wasm) {
25
+ it('generates different subsequent mnemonics', () => {
26
+ const val1 = wasm.bip39Generate(24);
27
+ const val2 = wasm.bip39Generate(24);
28
+
29
+ // console.log('\tVL1', val1);
30
+ // console.log('\tVL2', val2);
31
+
32
+ assert(val1 !== val2, 'ERROR: Subsequent mnemonics are the same');
33
+ });
34
+ }
35
+
36
+ /**
37
+ * @param {*} wasm
38
+ */
39
+ export function bip39Validate (wasm) {
40
+ it('validates a created mnemonic', () => {
41
+ const res = wasm.bip39Validate(wasm.bip39Generate(12));
42
+
43
+ // console.log('\tVAL', res);
44
+
45
+ assert(res, 'ERROR: Invalid bip39 validation');
46
+ });
47
+ }
48
+
49
+ /**
50
+ * @param {*} wasm
51
+ */
52
+ export function bip39ToEntropy (wasm) {
53
+ it('creates correct entropy for a known mnemonic', () => {
54
+ const res = u8aToHex(wasm.bip39ToEntropy('legal winner thank year wave sausage worth useful legal winner thank yellow'));
55
+
56
+ // console.log('\tENT', res);
57
+
58
+ assert(res === '0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f', 'ERROR: Invalid bip39 entropy');
59
+ });
60
+ }
61
+
62
+ /**
63
+ * @param {*} wasm
64
+ */
65
+ export function bip39ToMiniSecret (wasm) {
66
+ it('creates the correct minisecret from a mnemonic', () => {
67
+ const res = u8aToHex(wasm.bip39ToMiniSecret('legal winner thank year wave sausage worth useful legal winner thank yellow', 'Bizinikiwi'));
68
+
69
+ // console.log('\tMIN', res);
70
+
71
+ assert(res === '0x4313249608fe8ac10fd5886c92c4579007272cb77c21551ee5b8d60b78041685', 'ERROR: Invalid bip39 mini secret');
72
+ });
73
+ }
74
+
75
+ /**
76
+ * @param {*} wasm
77
+ */
78
+ export function bip39ToSeed (wasm) {
79
+ it('creates the correct seed for a mnemonic', () => {
80
+ const res = u8aToHex(wasm.bip39ToSeed('seed sock milk update focus rotate barely fade car face mechanic mercy', ''));
81
+
82
+ // console.log('\tSEE', res);
83
+
84
+ assert(res === '0x3c121e20de068083b49c2315697fb59a2d9e8643c24e5ea7628132c58969a027', 'ERROR: Invalid bip39 mini secret');
85
+ });
86
+ }
@@ -0,0 +1,84 @@
1
+ // Copyright 2019-2026 @pezkuwi/wasm-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /* global it */
5
+
6
+ import crypto from 'crypto';
7
+
8
+ import { assert, hexToU8a, stringToU8a, u8aToHex } from '@pezkuwi/util';
9
+
10
+ /**
11
+ * @internal
12
+ * @param {*} pair
13
+ */
14
+ function extractKeys (pair) {
15
+ return [pair, pair.slice(32), pair.slice(0, 32)];
16
+ }
17
+
18
+ /**
19
+ * @internal
20
+ * @param {*} wasm
21
+ */
22
+ function randomPair (wasm) {
23
+ return extractKeys(wasm.ed25519KeypairFromSeed(crypto.randomBytes(32)));
24
+ }
25
+
26
+ /**
27
+ * @param {*} wasm
28
+ */
29
+ export function ed25519PairFromSeed (wasm) {
30
+ it('creates a known pair from a known seed', () => {
31
+ const [pair, pk, sk] = extractKeys(wasm.ed25519KeypairFromSeed(stringToU8a('12345678901234567890123456789012')));
32
+
33
+ // console.log('\tSEC', u8aToHex(sk));
34
+ // console.log('\tPUB', u8aToHex(pk));
35
+
36
+ assert(u8aToHex(sk) === '0x3132333435363738393031323334353637383930313233343536373839303132', 'Secretkey does not match');
37
+ assert(u8aToHex(pk) === '0x2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee', 'Public key does not match');
38
+ assert(u8aToHex(pair) === '0x31323334353637383930313233343536373839303132333435363738393031322f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee', 'ERROR: pairFromSeed() does not match');
39
+ });
40
+ }
41
+
42
+ /**
43
+ * @param {*} wasm
44
+ */
45
+ export function ed25519SignAndVerify (wasm) {
46
+ it('signs and verifies', () => {
47
+ const [, pk, sk] = randomPair(wasm);
48
+ const signature = wasm.ed25519Sign(pk, sk, stringToU8a('this is a message'));
49
+ const isValid = wasm.ed25519Verify(signature, stringToU8a('this is a message'), pk);
50
+
51
+ // console.log('\tSIG', u8aToHex(signature));
52
+ // console.log('\tRES', isValid);
53
+
54
+ assert(isValid, 'ERROR: Unable to verify signature');
55
+ });
56
+ }
57
+
58
+ /**
59
+ * @param {*} wasm
60
+ */
61
+ export function ed25519VerifyExisting (wasm) {
62
+ it('verifies a known signature', () => {
63
+ const isValid = wasm.ed25519Verify(hexToU8a('0x90588f3f512496f2dd40571d162e8182860081c74e2085316e7c4396918f07da412ee029978e4dd714057fe973bd9e7d645148bf7b66680d67c93227cde95202'), stringToU8a('this is a message'), hexToU8a('0x2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee'));
64
+
65
+ // console.log('\tRES', isValid);
66
+
67
+ assert(isValid, 'ERROR: Unable to verify signature');
68
+ });
69
+ }
70
+
71
+ /**
72
+ * @param {*} wasm
73
+ */
74
+ export function ed25519Benchmark (wasm) {
75
+ it('runs a verification benchamark', () => {
76
+ const MESSAGE = stringToU8a('this is a message');
77
+
78
+ for (let i = 0; i < 256; i++) {
79
+ const [, pk, sk] = randomPair(wasm);
80
+
81
+ assert(wasm.ed25519Verify(wasm.ed25519Sign(pk, sk, MESSAGE), MESSAGE, pk), 'ERROR: Unable to verify signature');
82
+ }
83
+ });
84
+ }