@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.
- package/Cargo.toml +50 -0
- package/Xargo.toml +2 -0
- package/build/README.md +29 -0
- package/build/bundle-pezkuwi-wasm-crypto.js +2092 -0
- package/build/bundle.d.ts +40 -0
- package/build/bundle.js +505 -0
- package/build/cjs/bundle.d.ts +40 -0
- package/build/cjs/bundle.js +544 -0
- package/{index.d.ts → build/cjs/index.d.ts} +0 -1
- package/build/cjs/index.js +4 -0
- package/build/cjs/init.js +21 -0
- package/build/cjs/initNone.js +20 -0
- package/build/cjs/initOnlyAsm.js +20 -0
- package/build/cjs/initOnlyWasm.js +20 -0
- package/build/cjs/initWasmAsm.js +20 -0
- package/build/cjs/packageInfo.js +4 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -0
- package/build/init.d.ts +16 -0
- package/build/init.js +17 -0
- package/build/initNone.d.ts +10 -0
- package/build/initNone.js +17 -0
- package/build/initOnlyAsm.d.ts +10 -0
- package/build/initOnlyAsm.js +17 -0
- package/build/initOnlyWasm.d.ts +10 -0
- package/build/initOnlyWasm.js +17 -0
- package/build/initWasmAsm.d.ts +10 -0
- package/build/initWasmAsm.js +17 -0
- package/build/package.json +208 -0
- package/build/packageDetect.d.ts +1 -0
- package/build/packageDetect.js +8 -0
- package/build/packageInfo.d.ts +6 -0
- package/build/packageInfo.js +1 -0
- package/build-deno/README.md +25 -0
- package/build-deno/bundle.ts +597 -0
- package/build-deno/index.ts +2 -0
- package/build-deno/init.ts +23 -0
- package/build-deno/initNone.ts +21 -0
- package/build-deno/initOnlyAsm.ts +21 -0
- package/build-deno/initOnlyWasm.ts +21 -0
- package/build-deno/initWasmAsm.ts +21 -0
- package/build-deno/mod.ts +2 -0
- package/build-deno/packageDetect.ts +12 -0
- package/build-deno/packageInfo.ts +3 -0
- package/build-deno/rs/.editorconfig +10 -0
- package/build-tsc/bundle.d.ts +40 -0
- package/{cjs → build-tsc}/index.d.ts +0 -1
- package/build-tsc-cjs/bundle.js +544 -0
- package/{cjs → build-tsc-cjs}/index.js +0 -1
- package/build-tsc-cjs/packageDetect.js +10 -0
- package/{cjs → build-tsc-cjs}/packageInfo.js +1 -1
- package/build-tsc-esm/bundle.js +505 -0
- package/{index.js → build-tsc-esm/index.js} +0 -1
- package/{packageInfo.js → build-tsc-esm/packageInfo.js} +1 -1
- package/package.json +89 -87
- package/src/bundle.ts +613 -0
- package/src/index.ts +5 -0
- package/src/init.ts +25 -0
- package/src/initNone.ts +23 -0
- package/src/initOnlyAsm.ts +23 -0
- package/src/initOnlyWasm.ts +23 -0
- package/src/initWasmAsm.ts +23 -0
- package/src/lib.rs +24 -0
- package/src/mod.ts +4 -0
- package/src/packageDetect.ts +16 -0
- package/src/packageInfo.ts +6 -0
- package/src/rs/.editorconfig +10 -0
- package/src/rs/bip39.rs +139 -0
- package/src/rs/ed25519.rs +142 -0
- package/src/rs/hashing.rs +322 -0
- package/src/rs/secp256k1.rs +150 -0
- package/src/rs/sr25519.rs +331 -0
- package/src/rs/vrf.rs +144 -0
- package/test/all/bip39.js +86 -0
- package/test/all/ed25519.js +84 -0
- package/test/all/hashing.js +138 -0
- package/test/all/index.js +126 -0
- package/test/all/secp256k1.js +105 -0
- package/test/all/sr25519.js +211 -0
- package/test/all/vrf.js +74 -0
- package/test/asm.js +10 -0
- package/test/deno.ts +38 -0
- package/test/jest.spec.ts +25 -0
- package/test/loader-build.js +39 -0
- package/test/wasm.js +8 -0
- package/tsconfig.build.json +19 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.spec.json +16 -0
- package/tsconfig.spec.tsbuildinfo +1 -0
- package/bundle-pezkuwi-wasm-crypto.js +0 -825
- package/bundle.d.ts +0 -37
- package/bundle.js +0 -165
- package/cjs/bundle.d.ts +0 -37
- package/cjs/bundle.js +0 -171
- /package/{LICENSE → build/LICENSE} +0 -0
- /package/{init.d.ts → build/cjs/init.d.ts} +0 -0
- /package/{initNone.d.ts → build/cjs/initNone.d.ts} +0 -0
- /package/{initOnlyAsm.d.ts → build/cjs/initOnlyAsm.d.ts} +0 -0
- /package/{initOnlyWasm.d.ts → build/cjs/initOnlyWasm.d.ts} +0 -0
- /package/{initWasmAsm.d.ts → build/cjs/initWasmAsm.d.ts} +0 -0
- /package/{cjs → build/cjs}/package.json +0 -0
- /package/{packageDetect.d.ts → build/cjs/packageDetect.d.ts} +0 -0
- /package/{cjs → build/cjs}/packageDetect.js +0 -0
- /package/{packageInfo.d.ts → build/cjs/packageInfo.d.ts} +0 -0
- /package/{cjs → build-tsc}/init.d.ts +0 -0
- /package/{cjs → build-tsc}/initNone.d.ts +0 -0
- /package/{cjs → build-tsc}/initOnlyAsm.d.ts +0 -0
- /package/{cjs → build-tsc}/initOnlyWasm.d.ts +0 -0
- /package/{cjs → build-tsc}/initWasmAsm.d.ts +0 -0
- /package/{cjs → build-tsc}/packageDetect.d.ts +0 -0
- /package/{cjs → build-tsc}/packageInfo.d.ts +0 -0
- /package/{cjs → build-tsc-cjs}/init.js +0 -0
- /package/{cjs → build-tsc-cjs}/initNone.js +0 -0
- /package/{cjs → build-tsc-cjs}/initOnlyAsm.js +0 -0
- /package/{cjs → build-tsc-cjs}/initOnlyWasm.js +0 -0
- /package/{cjs → build-tsc-cjs}/initWasmAsm.js +0 -0
- /package/{init.js → build-tsc-esm/init.js} +0 -0
- /package/{initNone.js → build-tsc-esm/initNone.js} +0 -0
- /package/{initOnlyAsm.js → build-tsc-esm/initOnlyAsm.js} +0 -0
- /package/{initOnlyWasm.js → build-tsc-esm/initOnlyWasm.js} +0 -0
- /package/{initWasmAsm.js → build-tsc-esm/initWasmAsm.js} +0 -0
- /package/{packageDetect.js → build-tsc-esm/packageDetect.js} +0 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
// Copyright 2019-2025 @pezkuwi/wasm-crypto authors & contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
use blake2_rfc::blake2b::blake2b;
|
|
5
|
+
use byteorder::{ByteOrder, LittleEndian};
|
|
6
|
+
use hmac::{Hmac, Mac};
|
|
7
|
+
use pbkdf2::pbkdf2;
|
|
8
|
+
use scrypt::{ScryptParams, scrypt};
|
|
9
|
+
use sha2::{Digest, Sha256, Sha512};
|
|
10
|
+
use tiny_keccak::{Hasher, Keccak};
|
|
11
|
+
use twox_hash::XxHash;
|
|
12
|
+
use wasm_bindgen::prelude::*;
|
|
13
|
+
|
|
14
|
+
/// blake2b hash for the specified input
|
|
15
|
+
///
|
|
16
|
+
/// * data: Arbitrary data to be hashed
|
|
17
|
+
/// * key: Key to add to the hashing (normally empty)
|
|
18
|
+
/// * size: Size in bytes of the resulting output
|
|
19
|
+
///
|
|
20
|
+
/// Returns a vector with the hash result
|
|
21
|
+
#[wasm_bindgen]
|
|
22
|
+
pub fn ext_blake2b(data: &[u8], key: &[u8], size: u32) -> Vec<u8> {
|
|
23
|
+
// we cast to usize here - due to the WASM, we'd rather have u32 inputs
|
|
24
|
+
blake2b(size as usize, key, data)
|
|
25
|
+
.as_bytes()
|
|
26
|
+
.to_vec()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// hmac with sha256
|
|
30
|
+
#[wasm_bindgen]
|
|
31
|
+
pub fn ext_hmac_sha256(key: &[u8], data: &[u8]) -> Vec<u8> {
|
|
32
|
+
match Hmac::<Sha256>::new_varkey(key) {
|
|
33
|
+
Ok(mut m) => {
|
|
34
|
+
m.input(data);
|
|
35
|
+
|
|
36
|
+
m
|
|
37
|
+
.result()
|
|
38
|
+
.code()
|
|
39
|
+
.to_vec()
|
|
40
|
+
},
|
|
41
|
+
_ => panic!("Invalid key provided.")
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// hmac with sha512
|
|
46
|
+
#[wasm_bindgen]
|
|
47
|
+
pub fn ext_hmac_sha512(key: &[u8], data: &[u8]) -> Vec<u8> {
|
|
48
|
+
match Hmac::<Sha512>::new_varkey(key) {
|
|
49
|
+
Ok(mut m) => {
|
|
50
|
+
m.input(data);
|
|
51
|
+
|
|
52
|
+
m
|
|
53
|
+
.result()
|
|
54
|
+
.code()
|
|
55
|
+
.to_vec()
|
|
56
|
+
},
|
|
57
|
+
_ => panic!("Invalid key provided.")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// Create a keccak256 hash for the specified input
|
|
63
|
+
///
|
|
64
|
+
// * data: Arbitrary data to be hashed
|
|
65
|
+
///
|
|
66
|
+
/// Returns the hash as a vector
|
|
67
|
+
#[wasm_bindgen]
|
|
68
|
+
pub fn ext_keccak256(data: &[u8]) -> Vec<u8> {
|
|
69
|
+
let mut keccak = Keccak::v256();
|
|
70
|
+
let mut res = [0u8; 32];
|
|
71
|
+
|
|
72
|
+
keccak.update(data);
|
|
73
|
+
keccak.finalize(&mut res);
|
|
74
|
+
|
|
75
|
+
res.to_vec()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// Create a keccak512 hash for the specified input
|
|
79
|
+
///
|
|
80
|
+
// * data: Arbitrary data to be hashed
|
|
81
|
+
///
|
|
82
|
+
/// Returns the hash as a vector
|
|
83
|
+
#[wasm_bindgen]
|
|
84
|
+
pub fn ext_keccak512(data: &[u8]) -> Vec<u8> {
|
|
85
|
+
let mut keccak = Keccak::v512();
|
|
86
|
+
let mut res = [0u8; 64];
|
|
87
|
+
|
|
88
|
+
keccak.update(data);
|
|
89
|
+
keccak.finalize(&mut res);
|
|
90
|
+
|
|
91
|
+
res.to_vec()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// pbkdf2 kdf from an input, salt for the number of specified rounds
|
|
95
|
+
///
|
|
96
|
+
/// * data: Arbitrary data to be hashed
|
|
97
|
+
/// * salt: Salt for this hash
|
|
98
|
+
/// * rounds: The number of rounds to perform
|
|
99
|
+
///
|
|
100
|
+
/// Returns a vector with the hashed result
|
|
101
|
+
#[wasm_bindgen]
|
|
102
|
+
pub fn ext_pbkdf2(data: &[u8], salt: &[u8], rounds: u32) -> Vec<u8> {
|
|
103
|
+
// As per RFC 2898 [https://datatracker.ietf.org/doc/html/rfc2898], the number of rounds must be greater than 0
|
|
104
|
+
if rounds == 0 {
|
|
105
|
+
panic!("PBKDF2 rounds must be greater than 0");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let mut res = [0u8; 64];
|
|
109
|
+
|
|
110
|
+
// we cast to usize here - due to the WASM, we'd rather have u32 inputs
|
|
111
|
+
pbkdf2::<Hmac::<Sha512>>(data, salt, rounds as usize, &mut res);
|
|
112
|
+
|
|
113
|
+
res.to_vec()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/// scrypt kdf from input, salt and config
|
|
117
|
+
///
|
|
118
|
+
/// * password: Password to hash
|
|
119
|
+
/// * salt: Salt for this hash
|
|
120
|
+
/// * log2_n: log2(n)
|
|
121
|
+
/// * r: r
|
|
122
|
+
/// * p: p
|
|
123
|
+
///
|
|
124
|
+
/// Returns vector with the hashed result
|
|
125
|
+
#[wasm_bindgen]
|
|
126
|
+
pub fn ext_scrypt(password: &[u8], salt: &[u8], log2_n: u8, r: u32, p: u32) -> Vec<u8> {
|
|
127
|
+
// As per RFC 7914 [https://datatracker.ietf.org/doc/html/rfc7914.html], the cost parameter N (2 ^ log2_n) must be larger than 1
|
|
128
|
+
if log2_n == 0 {
|
|
129
|
+
panic!("Scrypt cost parameter N must be larger than 1, log2_n must be at least 1");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
match ScryptParams::new(log2_n, r, p) {
|
|
133
|
+
Ok(p) => {
|
|
134
|
+
let mut res = [0u8; 64];
|
|
135
|
+
|
|
136
|
+
match scrypt(password, salt, &p, &mut res) {
|
|
137
|
+
Ok(_) => res.to_vec(),
|
|
138
|
+
_ => panic!("Invalid scrypt hash.")
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
_ => panic!("Invalid scrypt params.")
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/// sha256 hash for the specified input
|
|
147
|
+
///
|
|
148
|
+
/// * data: Arbitrary data to be hashed
|
|
149
|
+
///
|
|
150
|
+
/// Returns a vector with the hash result
|
|
151
|
+
#[wasm_bindgen]
|
|
152
|
+
pub fn ext_sha256(data: &[u8]) -> Vec<u8> {
|
|
153
|
+
let mut hasher = Sha256::new();
|
|
154
|
+
|
|
155
|
+
hasher.input(data);
|
|
156
|
+
|
|
157
|
+
hasher
|
|
158
|
+
.result()
|
|
159
|
+
.to_vec()
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/// sha512 hash for the specified input
|
|
163
|
+
///
|
|
164
|
+
/// * data: Arbitrary data to be hashed
|
|
165
|
+
///
|
|
166
|
+
/// Returns a vector with the hash result
|
|
167
|
+
#[wasm_bindgen]
|
|
168
|
+
pub fn ext_sha512(data: &[u8]) -> Vec<u8> {
|
|
169
|
+
let mut hasher = Sha512::new();
|
|
170
|
+
|
|
171
|
+
hasher.input(data);
|
|
172
|
+
|
|
173
|
+
hasher
|
|
174
|
+
.result()
|
|
175
|
+
.to_vec()
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/// twox hash for the specified input and rounds
|
|
179
|
+
///
|
|
180
|
+
/// * data: Arbitrary data to be hashed
|
|
181
|
+
/// * rounds: Number of 8-byte rounds to add to the output
|
|
182
|
+
///
|
|
183
|
+
/// Returns a vector with the hash result
|
|
184
|
+
#[wasm_bindgen]
|
|
185
|
+
pub fn ext_twox(data: &[u8], rounds: u32) -> Vec<u8> {
|
|
186
|
+
use ::std::hash::Hasher;
|
|
187
|
+
let mut res = vec![];
|
|
188
|
+
let mut buf = [0u8; 8];
|
|
189
|
+
|
|
190
|
+
for round in 0..rounds {
|
|
191
|
+
// we cast to u64 here - due to the WASM, we'd rather have u32 inputs
|
|
192
|
+
let mut hasher = XxHash::with_seed(round as u64);
|
|
193
|
+
|
|
194
|
+
hasher.write(data);
|
|
195
|
+
LittleEndian::write_u64(&mut buf, hasher.finish());
|
|
196
|
+
res.extend_from_slice(&buf);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
res
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
#[cfg(test)]
|
|
203
|
+
pub mod tests {
|
|
204
|
+
use hex_literal::hex;
|
|
205
|
+
use super::*;
|
|
206
|
+
|
|
207
|
+
#[test]
|
|
208
|
+
fn can_blake2b() {
|
|
209
|
+
let data = b"abc";
|
|
210
|
+
let expected_32 = hex!("bddd813c634239723171ef3fee98579b94964e3bb1cb3e427262c8c068d52319");
|
|
211
|
+
let expected_64 = hex!("ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923");
|
|
212
|
+
let hash_32 = ext_blake2b(data, &[], 32);
|
|
213
|
+
let hash_64 = ext_blake2b(data, &[], 64);
|
|
214
|
+
|
|
215
|
+
assert_eq!(hash_32[..], expected_32[..]);
|
|
216
|
+
assert_eq!(hash_64[..], expected_64[..]);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
#[test]
|
|
220
|
+
fn can_keccak256() {
|
|
221
|
+
let data = b"test value";
|
|
222
|
+
let expected = hex!("2d07364b5c231c56ce63d49430e085ea3033c750688ba532b24029124c26ca5e");
|
|
223
|
+
let hash = ext_keccak256(data);
|
|
224
|
+
|
|
225
|
+
assert_eq!(hash[..], expected[..]);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
#[test]
|
|
229
|
+
fn can_hmac_sha256() {
|
|
230
|
+
let key = b"secret";
|
|
231
|
+
let data = b"some message";
|
|
232
|
+
let expected = hex!("f28a70b41263840e5c059a0a733336e0957efba87902aa8cca11441d4b0c96d7");
|
|
233
|
+
let hash = ext_hmac_sha256(key, data);
|
|
234
|
+
|
|
235
|
+
assert_eq!(hash[..], expected[..]);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
#[test]
|
|
239
|
+
fn can_hmac_sha512() {
|
|
240
|
+
let key = b"secret";
|
|
241
|
+
let data = b"some message";
|
|
242
|
+
let expected = hex!("295832e97ed77be75a9fa98029497e4a722c4b9a2f21b39d34f1befa931a39ec520fd24711d6f5c03501384ea66b83066a01a82c57a0460f8cd1f471fcce5841");
|
|
243
|
+
let hash = ext_hmac_sha512(key, data);
|
|
244
|
+
|
|
245
|
+
assert_eq!(hash[..], expected[..]);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
#[test]
|
|
249
|
+
fn can_keccak512() {
|
|
250
|
+
let data = b"test";
|
|
251
|
+
let expected = hex!("1e2e9fc2002b002d75198b7503210c05a1baac4560916a3c6d93bcce3a50d7f00fd395bf1647b9abb8d1afcc9c76c289b0c9383ba386a956da4b38934417789e");
|
|
252
|
+
let hash = ext_keccak512(data);
|
|
253
|
+
|
|
254
|
+
assert_eq!(hash[..], expected[..]);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
#[test]
|
|
258
|
+
fn can_pbkdf2() {
|
|
259
|
+
let salt = b"this is a salt";
|
|
260
|
+
let data = b"hello world";
|
|
261
|
+
let expected = hex!("5fcbe04f05300a3ecc5c35d18ea0b78f3f6853d2ae5f3fca374f69a7d1f78b5def5c60dae1a568026c7492511e0c53521e8bb6e03a650e1263265fee92722270");
|
|
262
|
+
let hash = ext_pbkdf2(data, salt, 2048);
|
|
263
|
+
|
|
264
|
+
assert_eq!(hash[..], expected[..]);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
#[test]
|
|
268
|
+
#[should_panic(expected = "PBKDF2 rounds must be greater than 0")]
|
|
269
|
+
fn pbkdf2_zero_rounds_panics() {
|
|
270
|
+
let salt = b"this is a salt";
|
|
271
|
+
let data = b"hello world";
|
|
272
|
+
ext_pbkdf2(data, salt, 0);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
#[test]
|
|
276
|
+
fn can_scrypt() {
|
|
277
|
+
let password = b"password";
|
|
278
|
+
let salt = b"salt";
|
|
279
|
+
let expected = hex!("745731af4484f323968969eda289aeee005b5903ac561e64a5aca121797bf7734ef9fd58422e2e22183bcacba9ec87ba0c83b7a2e788f03ce0da06463433cda6");
|
|
280
|
+
let hash = ext_scrypt(password, salt, 14, 8, 1);
|
|
281
|
+
|
|
282
|
+
assert_eq!(hash[..], expected[..]);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
#[test]
|
|
286
|
+
#[should_panic(expected = "Scrypt cost parameter N must be larger than 1, log2_n must be at least 1")]
|
|
287
|
+
fn scrypt_zero_log2_n_panics() {
|
|
288
|
+
let password = b"password";
|
|
289
|
+
let salt = b"salt";
|
|
290
|
+
ext_scrypt(password, salt, 0, 8, 1);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
#[test]
|
|
294
|
+
fn can_sha256() {
|
|
295
|
+
let data = b"hello world";
|
|
296
|
+
let expected = hex!("b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9");
|
|
297
|
+
let hash = ext_sha256(data);
|
|
298
|
+
|
|
299
|
+
assert_eq!(hash[..], expected[..]);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
#[test]
|
|
303
|
+
fn can_sha512() {
|
|
304
|
+
let data = b"hello world";
|
|
305
|
+
let expected = hex!("309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f");
|
|
306
|
+
let hash = ext_sha512(data);
|
|
307
|
+
|
|
308
|
+
assert_eq!(hash[..], expected[..]);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
#[test]
|
|
312
|
+
fn can_twox() {
|
|
313
|
+
let data = b"abc";
|
|
314
|
+
let expected_64 = hex!("990977adf52cbc44");
|
|
315
|
+
let expected_256 = hex!("990977adf52cbc440889329981caa9bef7da5770b2b8a05303b75d95360dd62b");
|
|
316
|
+
let hash_64 = ext_twox(data, 1);
|
|
317
|
+
let hash_256 = ext_twox(data, 4);
|
|
318
|
+
|
|
319
|
+
assert_eq!(hash_64[..], expected_64[..]);
|
|
320
|
+
assert_eq!(hash_256[..], expected_256[..]);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// Copyright 2019-2025 @pezkuwi/wasm-crypto authors & contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
use secp256k1::{
|
|
5
|
+
ecdsa::{RecoverableSignature, RecoveryId},
|
|
6
|
+
Message, PublicKey, SecretKey, SECP256K1,
|
|
7
|
+
};
|
|
8
|
+
use wasm_bindgen::prelude::*;
|
|
9
|
+
|
|
10
|
+
#[wasm_bindgen]
|
|
11
|
+
pub fn ext_secp_pub_compress(pubkey: &[u8]) -> Vec<u8> {
|
|
12
|
+
match PublicKey::from_slice(&pubkey) {
|
|
13
|
+
Ok(p) => p.serialize().to_vec(),
|
|
14
|
+
_ => panic!("Invalid pubkey provided."),
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#[wasm_bindgen]
|
|
19
|
+
pub fn ext_secp_pub_expand(pubkey: &[u8]) -> Vec<u8> {
|
|
20
|
+
match PublicKey::from_slice(&pubkey) {
|
|
21
|
+
Ok(p) => p.serialize_uncompressed().to_vec(),
|
|
22
|
+
_ => panic!("Invalid pubkey provided."),
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#[wasm_bindgen]
|
|
27
|
+
pub fn ext_secp_from_seed(seed: &[u8]) -> Vec<u8> {
|
|
28
|
+
match SecretKey::from_slice(seed) {
|
|
29
|
+
Ok(s) => {
|
|
30
|
+
let mut res = vec![];
|
|
31
|
+
let pubkey = PublicKey::from_secret_key(SECP256K1, &s);
|
|
32
|
+
|
|
33
|
+
res.extend_from_slice(&s.serialize_secret());
|
|
34
|
+
res.extend_from_slice(&pubkey.serialize());
|
|
35
|
+
|
|
36
|
+
res
|
|
37
|
+
}
|
|
38
|
+
_ => panic!("Invalid seed provided."),
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#[wasm_bindgen]
|
|
43
|
+
pub fn ext_secp_recover(hash: &[u8], sig: &[u8], rec: i32) -> Vec<u8> {
|
|
44
|
+
match RecoveryId::from_i32(rec) {
|
|
45
|
+
Ok(r) => match (
|
|
46
|
+
Message::from_slice(hash),
|
|
47
|
+
RecoverableSignature::from_compact(&sig, r),
|
|
48
|
+
) {
|
|
49
|
+
(Ok(m), Ok(s)) => {
|
|
50
|
+
let standard = s.to_standard();
|
|
51
|
+
let mut normalized = standard;
|
|
52
|
+
normalized.normalize_s();
|
|
53
|
+
|
|
54
|
+
// check if the signature is normalized
|
|
55
|
+
if normalized.ne(&standard) {
|
|
56
|
+
panic!("Non-normalized signature provided.");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
match s.recover(&m) {
|
|
60
|
+
Ok(k) => k.serialize().to_vec(),
|
|
61
|
+
_ => panic!("Unable to recover."),
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
_ => panic!("Invalid signature provided."),
|
|
65
|
+
},
|
|
66
|
+
_ => panic!("Invalid recovery data provided."),
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#[wasm_bindgen]
|
|
71
|
+
pub fn ext_secp_sign(hash: &[u8], seckey: &[u8]) -> Vec<u8> {
|
|
72
|
+
match (Message::from_slice(hash), SecretKey::from_slice(seckey)) {
|
|
73
|
+
(Ok(m), Ok(s)) => {
|
|
74
|
+
let mut res = vec![];
|
|
75
|
+
let (rec, sig) = SECP256K1.sign_ecdsa_recoverable(&m, &s).serialize_compact();
|
|
76
|
+
|
|
77
|
+
res.extend_from_slice(&sig);
|
|
78
|
+
res.push(rec.to_i32() as u8);
|
|
79
|
+
|
|
80
|
+
res
|
|
81
|
+
}
|
|
82
|
+
_ => panic!("Invalid message or secret provided."),
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#[cfg(test)]
|
|
87
|
+
pub mod tests {
|
|
88
|
+
use super::*;
|
|
89
|
+
use hex_literal::hex;
|
|
90
|
+
|
|
91
|
+
#[test]
|
|
92
|
+
fn can_create_pair() {
|
|
93
|
+
let seckey = hex!("4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae");
|
|
94
|
+
let expected = hex!("4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae03fd8c74f795ced92064b86191cb2772b1e3a0947740aa0a5a6e379592471fd85b");
|
|
95
|
+
let res = ext_secp_from_seed(&seckey);
|
|
96
|
+
|
|
97
|
+
assert_eq!(res[..], expected[..]);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
#[test]
|
|
101
|
+
fn can_pub_compress_full() {
|
|
102
|
+
let pubkey = hex!("04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af");
|
|
103
|
+
let expected = hex!("03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077");
|
|
104
|
+
let res = ext_secp_pub_compress(&pubkey);
|
|
105
|
+
|
|
106
|
+
assert_eq!(res[..], expected[..]);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
#[test]
|
|
110
|
+
fn can_pub_expand_comp() {
|
|
111
|
+
let pubkey = hex!("03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077");
|
|
112
|
+
let expected = hex!("04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af");
|
|
113
|
+
let res = ext_secp_pub_expand(&pubkey);
|
|
114
|
+
|
|
115
|
+
assert_eq!(res[..], expected[..]);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
#[test]
|
|
119
|
+
fn can_recover() {
|
|
120
|
+
let expected = hex!("028d13da15a02f3a70677339d51b14177ee9b49657662b35e56a9d9dee17db1d30");
|
|
121
|
+
let sig = hex!("7505f2880114da51b3f5d535f8687953c0ab9af4ab81e592eaebebf53b728d2b6dfd9b5bcd70fee412b1f31360e7c2774009305cb84fc50c1d0ff8034dfa5fff");
|
|
122
|
+
let msg = hex!("a30b64ce1eedf409c8afb801d72c05234e64849ea538c15dd3c8cf4ffcf166c9");
|
|
123
|
+
let res = ext_secp_recover(&msg, &sig, 0);
|
|
124
|
+
|
|
125
|
+
assert_eq!(res[..], expected[..]);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
#[test]
|
|
129
|
+
fn can_sign() {
|
|
130
|
+
// JS expectation - doesn't match?
|
|
131
|
+
// let expected = hex!("df92f73d9f060cefacf187b5414491cb992998ace017fa48839b5cda3e264ba8c4efa521361678d9b8582744d77aa4b8d886d7380b7808a683174afad9c4700300");
|
|
132
|
+
let expected = hex!("df92f73d9f060cefacf187b5414491cb992998ace017fa48839b5cda3e264ba83b105adec9e9872647a7d8bb28855b45e22805aea3d097953cbb1391f671d13e01");
|
|
133
|
+
let seckey = hex!("4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae");
|
|
134
|
+
let msg = hex!("68c731589a583d08b70861683b59ce3dd56284cb2f0da5b6cd83e6641dac3aab");
|
|
135
|
+
let res = ext_secp_sign(&msg, &seckey);
|
|
136
|
+
|
|
137
|
+
assert_eq!(res[..], expected[..]);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
#[test]
|
|
141
|
+
#[should_panic(expected = "Non-normalized signature provided.")]
|
|
142
|
+
fn rejects_high_s_signature() {
|
|
143
|
+
// valid ECDSA signature with a high S value (non-normalized)
|
|
144
|
+
let sig = hex!(
|
|
145
|
+
"fa1b7a71500322d70bcd5b982c678c96c95f68893063e1621b8b85c867b80b1caae8c7c4f8b5c6d181e4c8898a3f4c329da0e2c21f6f80552a660e6c1d9fa201");
|
|
146
|
+
let msg = hex!("9a3c9be3a14a94df8cda1c474c0e9f8b1b35b1d85d2122cb87e6eb3c518e3be3");
|
|
147
|
+
|
|
148
|
+
let _ = ext_secp_recover(&msg, &sig, 0);
|
|
149
|
+
}
|
|
150
|
+
}
|