@bsv/sdk 1.0.13 → 1.0.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/README.md +1 -1
- package/dist/cjs/package.json +2 -2
- package/dist/cjs/src/compat/BSM.js.map +1 -1
- package/dist/cjs/src/compat/ECIES.js +105 -76
- package/dist/cjs/src/compat/ECIES.js.map +1 -1
- package/dist/cjs/src/compat/HD.js +65 -65
- package/dist/cjs/src/compat/HD.js.map +1 -1
- package/dist/cjs/src/compat/Mnemonic.js +79 -79
- package/dist/cjs/src/compat/Mnemonic.js.map +1 -1
- package/dist/cjs/src/compat/bip-39-wordlist-en.js +2 -2
- package/dist/cjs/src/compat/bip-39-wordlist-en.js.map +1 -1
- package/dist/cjs/src/primitives/AESGCM.js.map +1 -1
- package/dist/cjs/src/primitives/BigNumber.js.map +1 -1
- package/dist/cjs/src/primitives/DRBG.js.map +1 -1
- package/dist/cjs/src/primitives/ECDSA.js.map +1 -1
- package/dist/cjs/src/primitives/Hash.js +26 -13
- package/dist/cjs/src/primitives/Hash.js.map +1 -1
- package/dist/cjs/src/primitives/PrivateKey.js +3 -2
- package/dist/cjs/src/primitives/PrivateKey.js.map +1 -1
- package/dist/cjs/src/primitives/PublicKey.js +1 -2
- package/dist/cjs/src/primitives/PublicKey.js.map +1 -1
- package/dist/cjs/src/primitives/Random.js +2 -2
- package/dist/cjs/src/primitives/Random.js.map +1 -1
- package/dist/cjs/src/primitives/Signature.js +141 -4
- package/dist/cjs/src/primitives/Signature.js.map +1 -1
- package/dist/cjs/src/primitives/SymmetricKey.js.map +1 -1
- package/dist/cjs/src/primitives/TransactionSignature.js.map +1 -1
- package/dist/cjs/src/primitives/utils.js +14 -9
- package/dist/cjs/src/primitives/utils.js.map +1 -1
- package/dist/cjs/src/script/Spend.js.map +1 -1
- package/dist/cjs/src/script/templates/P2PKH.js +1 -1
- package/dist/cjs/src/script/templates/P2PKH.js.map +1 -1
- package/dist/cjs/src/transaction/MerklePath.js +3 -3
- package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
- package/dist/cjs/src/transaction/Transaction.js +2 -2
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/compat/BSM.js.map +1 -1
- package/dist/esm/src/compat/ECIES.js +105 -76
- package/dist/esm/src/compat/ECIES.js.map +1 -1
- package/dist/esm/src/compat/HD.js +65 -65
- package/dist/esm/src/compat/HD.js.map +1 -1
- package/dist/esm/src/compat/Mnemonic.js +79 -79
- package/dist/esm/src/compat/Mnemonic.js.map +1 -1
- package/dist/esm/src/compat/bip-39-wordlist-en.js +2 -2
- package/dist/esm/src/compat/bip-39-wordlist-en.js.map +1 -1
- package/dist/esm/src/primitives/AESGCM.js.map +1 -1
- package/dist/esm/src/primitives/BigNumber.js.map +1 -1
- package/dist/esm/src/primitives/DRBG.js.map +1 -1
- package/dist/esm/src/primitives/ECDSA.js.map +1 -1
- package/dist/esm/src/primitives/Hash.js +26 -13
- package/dist/esm/src/primitives/Hash.js.map +1 -1
- package/dist/esm/src/primitives/PrivateKey.js +3 -2
- package/dist/esm/src/primitives/PrivateKey.js.map +1 -1
- package/dist/esm/src/primitives/PublicKey.js +1 -2
- package/dist/esm/src/primitives/PublicKey.js.map +1 -1
- package/dist/esm/src/primitives/Random.js +2 -2
- package/dist/esm/src/primitives/Random.js.map +1 -1
- package/dist/esm/src/primitives/Signature.js +141 -4
- package/dist/esm/src/primitives/Signature.js.map +1 -1
- package/dist/esm/src/primitives/SymmetricKey.js.map +1 -1
- package/dist/esm/src/primitives/TransactionSignature.js.map +1 -1
- package/dist/esm/src/primitives/utils.js +14 -9
- package/dist/esm/src/primitives/utils.js.map +1 -1
- package/dist/esm/src/script/Spend.js.map +1 -1
- package/dist/esm/src/script/templates/P2PKH.js +1 -1
- package/dist/esm/src/script/templates/P2PKH.js.map +1 -1
- package/dist/esm/src/transaction/MerklePath.js +3 -3
- package/dist/esm/src/transaction/MerklePath.js.map +1 -1
- package/dist/esm/src/transaction/Transaction.js +2 -2
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/compat/ECIES.d.ts +36 -36
- package/dist/types/src/compat/ECIES.d.ts.map +1 -1
- package/dist/types/src/compat/HD.d.ts +65 -65
- package/dist/types/src/compat/HD.d.ts.map +1 -1
- package/dist/types/src/compat/Mnemonic.d.ts +79 -79
- package/dist/types/src/compat/Mnemonic.d.ts.map +1 -1
- package/dist/types/src/primitives/AESGCM.d.ts.map +1 -1
- package/dist/types/src/primitives/BigNumber.d.ts.map +1 -1
- package/dist/types/src/primitives/Hash.d.ts.map +1 -1
- package/dist/types/src/primitives/PrivateKey.d.ts.map +1 -1
- package/dist/types/src/primitives/PublicKey.d.ts.map +1 -1
- package/dist/types/src/primitives/Signature.d.ts +62 -0
- package/dist/types/src/primitives/Signature.d.ts.map +1 -1
- package/dist/types/src/primitives/SymmetricKey.d.ts.map +1 -1
- package/dist/types/src/primitives/TransactionSignature.d.ts.map +1 -1
- package/dist/types/src/primitives/utils.d.ts.map +1 -1
- package/dist/types/src/script/Spend.d.ts.map +1 -1
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/docs/compat.md +4 -4
- package/docs/primitives.md +288 -79
- package/mod.ts +8 -0
- package/package.json +5 -3
- package/src/compat/BSM.ts +12 -12
- package/src/compat/ECIES.ts +417 -418
- package/src/compat/HD.ts +228 -228
- package/src/compat/Mnemonic.ts +173 -173
- package/src/compat/__tests/BSM.test.ts +13 -2
- package/src/compat/bip-39-wordlist-en.ts +2052 -2052
- package/src/primitives/AESGCM.ts +30 -30
- package/src/primitives/BigNumber.ts +0 -1
- package/src/primitives/DRBG.ts +5 -5
- package/src/primitives/ECDSA.ts +1 -1
- package/src/primitives/Hash.ts +278 -293
- package/src/primitives/PrivateKey.ts +18 -19
- package/src/primitives/PublicKey.ts +9 -10
- package/src/primitives/Random.ts +4 -4
- package/src/primitives/Signature.ts +158 -14
- package/src/primitives/SymmetricKey.ts +3 -3
- package/src/primitives/TransactionSignature.ts +9 -9
- package/src/primitives/index.ts +1 -1
- package/src/primitives/utils.ts +60 -64
- package/src/script/Spend.ts +12 -12
- package/src/script/index.ts +1 -1
- package/src/script/templates/P2PKH.ts +1 -1
- package/src/transaction/MerklePath.ts +3 -3
- package/src/transaction/Transaction.ts +23 -23
package/src/compat/ECIES.ts
CHANGED
|
@@ -6,70 +6,70 @@ import Point from '../primitives/Point.js'
|
|
|
6
6
|
import * as Hash from '../primitives/Hash.js'
|
|
7
7
|
import { toArray, toHex, encode } from '../primitives/utils.js'
|
|
8
8
|
|
|
9
|
-
function AES(key) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
function AES (key) {
|
|
10
|
+
if (!this._tables[0][0][0]) this._precompute()
|
|
11
|
+
|
|
12
|
+
let tmp, encKey, decKey
|
|
13
|
+
const sbox = this._tables[0][4]
|
|
14
|
+
const decTable = this._tables[1]
|
|
15
|
+
const keyLen = key.length
|
|
16
|
+
let rcon = 1
|
|
17
|
+
|
|
18
|
+
if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
|
|
19
|
+
throw new Error('invalid aes key size')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
this._key = [encKey = key.slice(0), decKey = []]
|
|
23
|
+
|
|
24
|
+
// schedule encryption keys
|
|
25
|
+
for (var i = keyLen; i < 4 * keyLen + 28; i++) {
|
|
26
|
+
tmp = encKey[i - 1]
|
|
27
|
+
|
|
28
|
+
// apply sbox
|
|
29
|
+
if (i % keyLen === 0 || (keyLen === 8 && i % keyLen === 4)) {
|
|
30
|
+
tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255]
|
|
31
|
+
|
|
32
|
+
// shift rows and add rcon
|
|
33
|
+
if (i % keyLen === 0) {
|
|
34
|
+
tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24
|
|
35
|
+
rcon = rcon << 1 ^ (rcon >> 7) * 283
|
|
36
|
+
}
|
|
20
37
|
}
|
|
21
38
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// schedule encryption keys
|
|
25
|
-
for (var i = keyLen; i < 4 * keyLen + 28; i++) {
|
|
26
|
-
tmp = encKey[i - 1];
|
|
27
|
-
|
|
28
|
-
// apply sbox
|
|
29
|
-
if (i % keyLen === 0 || (keyLen === 8 && i % keyLen === 4)) {
|
|
30
|
-
tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255];
|
|
31
|
-
|
|
32
|
-
// shift rows and add rcon
|
|
33
|
-
if (i % keyLen === 0) {
|
|
34
|
-
tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24;
|
|
35
|
-
rcon = rcon << 1 ^ (rcon >> 7) * 283;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
encKey[i] = encKey[i - keyLen] ^ tmp;
|
|
40
|
-
}
|
|
39
|
+
encKey[i] = encKey[i - keyLen] ^ tmp
|
|
40
|
+
}
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
// schedule decryption keys
|
|
43
|
+
for (let j = 0; i; j++, i--) {
|
|
44
|
+
tmp = encKey[j & 3 ? i : i - 4]
|
|
45
|
+
if (i <= 4 || j < 4) {
|
|
46
|
+
decKey[j] = tmp
|
|
47
|
+
} else {
|
|
48
|
+
decKey[j] = decTable[0][sbox[tmp >>> 24]] ^
|
|
49
49
|
decTable[1][sbox[tmp >> 16 & 255]] ^
|
|
50
50
|
decTable[2][sbox[tmp >> 8 & 255]] ^
|
|
51
|
-
decTable[3][sbox[tmp & 255]]
|
|
52
|
-
}
|
|
51
|
+
decTable[3][sbox[tmp & 255]]
|
|
53
52
|
}
|
|
53
|
+
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
AES.prototype = {
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
/**
|
|
59
59
|
* Encrypt an array of 4 big-endian words.
|
|
60
60
|
* @param {Array} data The plaintext.
|
|
61
61
|
* @return {Array} The ciphertext.
|
|
62
62
|
*/
|
|
63
|
-
|
|
63
|
+
encrypt: function (data) { return this._crypt(data, 0) },
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
/**
|
|
66
66
|
* Decrypt an array of 4 big-endian words.
|
|
67
67
|
* @param {Array} data The ciphertext.
|
|
68
68
|
* @return {Array} The plaintext.
|
|
69
69
|
*/
|
|
70
|
-
|
|
70
|
+
decrypt: function (data) { return this._crypt(data, 1) },
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
/**
|
|
73
73
|
* The expanded S-box and inverse S-box tables. These will be computed
|
|
74
74
|
* on the client so that we don't have to send them down the wire.
|
|
75
75
|
*
|
|
@@ -81,388 +81,387 @@ AES.prototype = {
|
|
|
81
81
|
*
|
|
82
82
|
* @private
|
|
83
83
|
*/
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
84
|
+
_tables: [
|
|
85
|
+
[new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)],
|
|
86
|
+
[new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)]
|
|
87
|
+
],
|
|
88
|
+
|
|
89
|
+
// Expand the S-box tables.
|
|
90
|
+
_precompute: function () {
|
|
91
|
+
const encTable = this._tables[0]; const decTable = this._tables[1]
|
|
92
|
+
const sbox = encTable[4]; const sboxInv = decTable[4]
|
|
93
|
+
let i; let x; let xInv; const d = new Uint8Array(256); const th = new Uint8Array(256); let x2; let x4; let x8; let s; let tEnc; let tDec
|
|
94
|
+
|
|
95
|
+
// Compute double and third tables
|
|
96
|
+
for (i = 0; i < 256; i++) {
|
|
97
|
+
th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
|
|
101
|
+
// Compute sbox
|
|
102
|
+
s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4
|
|
103
|
+
s = s >> 8 ^ s & 255 ^ 99
|
|
104
|
+
sbox[x] = s
|
|
105
|
+
sboxInv[s] = x
|
|
106
|
+
|
|
107
|
+
// Compute MixColumns
|
|
108
|
+
x8 = d[x4 = d[x2 = d[x]]]
|
|
109
|
+
tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100
|
|
110
|
+
tEnc = d[s] * 0x101 ^ s * 0x1010100
|
|
111
|
+
|
|
112
|
+
for (i = 0; i < 4; i++) {
|
|
113
|
+
encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8
|
|
114
|
+
decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
120
|
* Encryption and decryption core.
|
|
121
121
|
* @param {Array} input Four words to be encrypted or decrypted.
|
|
122
122
|
* @param dir The direction, 0 for encrypt and 1 for decrypt.
|
|
123
123
|
* @return {Array} The four encrypted or decrypted words.
|
|
124
124
|
* @private
|
|
125
125
|
*/
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
126
|
+
_crypt: function (input, dir) {
|
|
127
|
+
if (input.length !== 4) {
|
|
128
|
+
throw new Error('invalid aes block size')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const key = this._key[dir]
|
|
132
|
+
// state variables a,b,c,d are loaded with pre-whitened data
|
|
133
|
+
let a = input[0] ^ key[0]
|
|
134
|
+
let b = input[dir ? 3 : 1] ^ key[1]
|
|
135
|
+
let c = input[2] ^ key[2]
|
|
136
|
+
let d = input[dir ? 1 : 3] ^ key[3]
|
|
137
|
+
let a2; let b2; let c2
|
|
138
|
+
|
|
139
|
+
const nInnerRounds = key.length / 4 - 2
|
|
140
|
+
let i
|
|
141
|
+
let kIndex = 4
|
|
142
|
+
const out = new Uint32Array(4); const // <--- this is slower in Node.js, about the same in Chrome */
|
|
143
|
+
table = this._tables[dir]
|
|
144
|
+
|
|
145
|
+
// load up the tables
|
|
146
|
+
const t0 = table[0]
|
|
147
|
+
const t1 = table[1]
|
|
148
|
+
const t2 = table[2]
|
|
149
|
+
const t3 = table[3]
|
|
150
|
+
const sbox = table[4]
|
|
151
|
+
|
|
152
|
+
// Inner rounds. Cribbed from OpenSSL.
|
|
153
|
+
for (i = 0; i < nInnerRounds; i++) {
|
|
154
|
+
a2 = t0[a >>> 24] ^ t1[b >> 16 & 255] ^ t2[c >> 8 & 255] ^ t3[d & 255] ^ key[kIndex]
|
|
155
|
+
b2 = t0[b >>> 24] ^ t1[c >> 16 & 255] ^ t2[d >> 8 & 255] ^ t3[a & 255] ^ key[kIndex + 1]
|
|
156
|
+
c2 = t0[c >>> 24] ^ t1[d >> 16 & 255] ^ t2[a >> 8 & 255] ^ t3[b & 255] ^ key[kIndex + 2]
|
|
157
|
+
d = t0[d >>> 24] ^ t1[a >> 16 & 255] ^ t2[b >> 8 & 255] ^ t3[c & 255] ^ key[kIndex + 3]
|
|
158
|
+
kIndex += 4
|
|
159
|
+
a = a2; b = b2; c = c2
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Last round.
|
|
163
|
+
for (i = 0; i < 4; i++) {
|
|
164
|
+
out[dir ? 3 & -i : i] =
|
|
165
165
|
sbox[a >>> 24] << 24 ^
|
|
166
166
|
sbox[b >> 16 & 255] << 16 ^
|
|
167
167
|
sbox[c >> 8 & 255] << 8 ^
|
|
168
168
|
sbox[d & 255] ^
|
|
169
|
-
key[kIndex++]
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return out;
|
|
169
|
+
key[kIndex++]
|
|
170
|
+
a2 = a; a = b; b = c; c = d; d = a2
|
|
174
171
|
}
|
|
172
|
+
|
|
173
|
+
return out
|
|
174
|
+
}
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
class AESWrapper {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
178
|
+
public static encrypt (messageBuf: number[], keyBuf: number[]): number[] {
|
|
179
|
+
const key = AESWrapper.buf2Words((keyBuf))
|
|
180
|
+
const message = AESWrapper.buf2Words((messageBuf))
|
|
181
|
+
const a = new AES(key)
|
|
182
|
+
const enc = a.encrypt(message)
|
|
183
|
+
const encBuf = AESWrapper.words2Buf(enc)
|
|
184
|
+
return encBuf
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
public static decrypt (encBuf: number[], keyBuf: number[]): number[] {
|
|
188
|
+
const enc = AESWrapper.buf2Words((encBuf))
|
|
189
|
+
const key = AESWrapper.buf2Words((keyBuf))
|
|
190
|
+
const a = new AES(key)
|
|
191
|
+
const message = a.decrypt(enc)
|
|
192
|
+
const messageBuf = AESWrapper.words2Buf(message)
|
|
193
|
+
return messageBuf
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
public static buf2Words (buf: number[]): number[] {
|
|
197
|
+
if (buf.length % 4) {
|
|
198
|
+
throw new Error('buf length must be a multiple of 4')
|
|
185
199
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const key = AESWrapper.buf2Words((keyBuf))
|
|
190
|
-
const a = new AES(key)
|
|
191
|
-
const message = a.decrypt(enc)
|
|
192
|
-
const messageBuf = AESWrapper.words2Buf(message)
|
|
193
|
-
return messageBuf
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
public static buf2Words(buf: number[]): number[] {
|
|
197
|
-
if (buf.length % 4) {
|
|
198
|
-
throw new Error('buf length must be a multiple of 4')
|
|
199
|
-
}
|
|
200
|
-
const words = []
|
|
201
|
-
for (let i = 0; i < buf.length / 4; i++) {
|
|
202
|
-
const val =
|
|
200
|
+
const words = []
|
|
201
|
+
for (let i = 0; i < buf.length / 4; i++) {
|
|
202
|
+
const val =
|
|
203
203
|
(buf[i * 4] * 0x1000000) + // Shift the first byte by 24 bits
|
|
204
204
|
((buf[i * 4 + 1] << 16) | // Shift the second byte by 16 bits
|
|
205
205
|
(buf[i * 4 + 2] << 8) | // Shift the third byte by 8 bits
|
|
206
206
|
buf[i * 4 + 3]) // The fourth byte
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
return words
|
|
207
|
+
words.push(val)
|
|
210
208
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return buf
|
|
209
|
+
return words
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
public static words2Buf (words: number[]): number[] {
|
|
213
|
+
const buf = new Array(words.length * 4)
|
|
214
|
+
|
|
215
|
+
for (let i = 0; i < words.length; i++) {
|
|
216
|
+
const word = words[i]
|
|
217
|
+
buf[i * 4] = (word >>> 24) & 0xFF
|
|
218
|
+
buf[i * 4 + 1] = (word >>> 16) & 0xFF
|
|
219
|
+
buf[i * 4 + 2] = (word >>> 8) & 0xFF
|
|
220
|
+
buf[i * 4 + 3] = word & 0xFF
|
|
224
221
|
}
|
|
222
|
+
|
|
223
|
+
return buf
|
|
224
|
+
}
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
class CBC {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
for (let i = 0; i <= buf.length / bytesize; i++) {
|
|
233
|
-
let blockBuf = buf.slice(i * bytesize, i * bytesize + bytesize)
|
|
234
|
-
|
|
235
|
-
if (blockBuf.length < blockSize) {
|
|
236
|
-
blockBuf = CBC.pkcs7Pad(blockBuf, blockSize)
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
blockBufs.push(blockBuf)
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return blockBufs
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
public static blockBufs2Buf(blockBufs: number[][]): number[] {
|
|
246
|
-
let last = blockBufs[blockBufs.length - 1]
|
|
247
|
-
last = CBC.pkcs7Unpad(last)
|
|
248
|
-
blockBufs[blockBufs.length - 1] = last
|
|
228
|
+
public static buf2BlocksBuf (buf: number[], blockSize: number): number[][] {
|
|
229
|
+
const bytesize = blockSize / 8
|
|
230
|
+
const blockBufs = []
|
|
249
231
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
return buf
|
|
253
|
-
}
|
|
232
|
+
for (let i = 0; i <= buf.length / bytesize; i++) {
|
|
233
|
+
let blockBuf = buf.slice(i * bytesize, i * bytesize + bytesize)
|
|
254
234
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
blockCipher: any /* TODO: type */,
|
|
259
|
-
cipherKeyBuf: number[]
|
|
260
|
-
): number[] {
|
|
261
|
-
const blockSize = ivBuf.length * 8
|
|
262
|
-
const blockBufs = CBC.buf2BlocksBuf(messageBuf, blockSize)
|
|
263
|
-
const encBufs = CBC.encryptBlocks(blockBufs, ivBuf, blockCipher, cipherKeyBuf)
|
|
264
|
-
const encBuf = encBufs.flat()
|
|
265
|
-
return encBuf
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
public static decrypt(
|
|
269
|
-
encBuf: number[],
|
|
270
|
-
ivBuf: number[],
|
|
271
|
-
blockCipher: any /* TODO: type */,
|
|
272
|
-
cipherKeyBuf: number[]
|
|
273
|
-
): number[] {
|
|
274
|
-
const bytesize = ivBuf.length
|
|
275
|
-
const encBufs = []
|
|
276
|
-
for (let i = 0; i < encBuf.length / bytesize; i++) {
|
|
277
|
-
encBufs.push(encBuf.slice(i * bytesize, i * bytesize + bytesize))
|
|
278
|
-
}
|
|
279
|
-
const blockBufs = CBC.decryptBlocks(encBufs, ivBuf, blockCipher, cipherKeyBuf)
|
|
280
|
-
const buf = CBC.blockBufs2Buf(blockBufs)
|
|
281
|
-
return buf
|
|
282
|
-
}
|
|
235
|
+
if (blockBuf.length < blockSize) {
|
|
236
|
+
blockBuf = CBC.pkcs7Pad(blockBuf, blockSize)
|
|
237
|
+
}
|
|
283
238
|
|
|
284
|
-
|
|
285
|
-
blockBuf: number[],
|
|
286
|
-
ivBuf: number[],
|
|
287
|
-
blockCipher: any /* TODO: type */,
|
|
288
|
-
cipherKeyBuf: number[]
|
|
289
|
-
): number[] {
|
|
290
|
-
const xorbuf = CBC.xorBufs(blockBuf, ivBuf)
|
|
291
|
-
const encBuf = blockCipher.encrypt(xorbuf, cipherKeyBuf)
|
|
292
|
-
return encBuf
|
|
239
|
+
blockBufs.push(blockBuf)
|
|
293
240
|
}
|
|
294
241
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
242
|
+
return blockBufs
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
public static blockBufs2Buf (blockBufs: number[][]): number[] {
|
|
246
|
+
let last = blockBufs[blockBufs.length - 1]
|
|
247
|
+
last = CBC.pkcs7Unpad(last)
|
|
248
|
+
blockBufs[blockBufs.length - 1] = last
|
|
249
|
+
|
|
250
|
+
const buf = blockBufs.flat()
|
|
251
|
+
|
|
252
|
+
return buf
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
public static encrypt (
|
|
256
|
+
messageBuf: number[],
|
|
257
|
+
ivBuf: number[],
|
|
258
|
+
blockCipher: any /* TODO: type */,
|
|
259
|
+
cipherKeyBuf: number[]
|
|
260
|
+
): number[] {
|
|
261
|
+
const blockSize = ivBuf.length * 8
|
|
262
|
+
const blockBufs = CBC.buf2BlocksBuf(messageBuf, blockSize)
|
|
263
|
+
const encBufs = CBC.encryptBlocks(blockBufs, ivBuf, blockCipher, cipherKeyBuf)
|
|
264
|
+
const encBuf = encBufs.flat()
|
|
265
|
+
return encBuf
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
public static decrypt (
|
|
269
|
+
encBuf: number[],
|
|
270
|
+
ivBuf: number[],
|
|
271
|
+
blockCipher: any /* TODO: type */,
|
|
272
|
+
cipherKeyBuf: number[]
|
|
273
|
+
): number[] {
|
|
274
|
+
const bytesize = ivBuf.length
|
|
275
|
+
const encBufs = []
|
|
276
|
+
for (let i = 0; i < encBuf.length / bytesize; i++) {
|
|
277
|
+
encBufs.push(encBuf.slice(i * bytesize, i * bytesize + bytesize))
|
|
304
278
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
279
|
+
const blockBufs = CBC.decryptBlocks(encBufs, ivBuf, blockCipher, cipherKeyBuf)
|
|
280
|
+
const buf = CBC.blockBufs2Buf(blockBufs)
|
|
281
|
+
return buf
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
public static encryptBlock (
|
|
285
|
+
blockBuf: number[],
|
|
286
|
+
ivBuf: number[],
|
|
287
|
+
blockCipher: any /* TODO: type */,
|
|
288
|
+
cipherKeyBuf: number[]
|
|
289
|
+
): number[] {
|
|
290
|
+
const xorbuf = CBC.xorBufs(blockBuf, ivBuf)
|
|
291
|
+
const encBuf = blockCipher.encrypt(xorbuf, cipherKeyBuf)
|
|
292
|
+
return encBuf
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
public static decryptBlock (
|
|
296
|
+
encBuf: number[],
|
|
297
|
+
ivBuf: number[],
|
|
298
|
+
blockCipher: any /* TODO: type */,
|
|
299
|
+
cipherKeyBuf: number[]
|
|
300
|
+
): number[] {
|
|
301
|
+
const xorbuf = blockCipher.decrypt(encBuf, cipherKeyBuf)
|
|
302
|
+
const blockBuf = CBC.xorBufs(xorbuf, ivBuf)
|
|
303
|
+
return blockBuf
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
public static encryptBlocks (
|
|
307
|
+
blockBufs: number[][],
|
|
308
|
+
ivBuf: number[],
|
|
309
|
+
blockCipher: any /* TODO: type */,
|
|
310
|
+
cipherKeyBuf: number[]
|
|
311
|
+
): number[][] {
|
|
312
|
+
const encBufs = []
|
|
313
|
+
|
|
314
|
+
for (let i = 0; i < blockBufs.length; i++) {
|
|
315
|
+
const blockBuf = blockBufs[i]
|
|
316
|
+
const encBuf = CBC.encryptBlock(blockBuf, ivBuf, blockCipher, cipherKeyBuf)
|
|
317
|
+
|
|
318
|
+
encBufs.push(encBuf)
|
|
319
|
+
|
|
320
|
+
ivBuf = encBuf
|
|
324
321
|
}
|
|
325
322
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
ivBuf: number[],
|
|
329
|
-
blockCipher: any /* TODO: type */,
|
|
330
|
-
cipherKeyBuf: number[]
|
|
331
|
-
): number[][] {
|
|
332
|
-
const blockBufs = []
|
|
323
|
+
return encBufs
|
|
324
|
+
}
|
|
333
325
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
326
|
+
public static decryptBlocks (
|
|
327
|
+
encBufs: number[][],
|
|
328
|
+
ivBuf: number[],
|
|
329
|
+
blockCipher: any /* TODO: type */,
|
|
330
|
+
cipherKeyBuf: number[]
|
|
331
|
+
): number[][] {
|
|
332
|
+
const blockBufs = []
|
|
337
333
|
|
|
338
|
-
|
|
334
|
+
for (let i = 0; i < encBufs.length; i++) {
|
|
335
|
+
const encBuf = encBufs[i]
|
|
336
|
+
const blockBuf = CBC.decryptBlock(encBuf, ivBuf, blockCipher, cipherKeyBuf)
|
|
339
337
|
|
|
340
|
-
|
|
341
|
-
}
|
|
338
|
+
blockBufs.push(blockBuf)
|
|
342
339
|
|
|
343
|
-
|
|
340
|
+
ivBuf = encBuf
|
|
344
341
|
}
|
|
345
342
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
343
|
+
return blockBufs
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
public static pkcs7Pad (buf: number[], blockSize: number): number[] {
|
|
347
|
+
const bytesize = blockSize / 8
|
|
348
|
+
const padbytesize = bytesize - buf.length
|
|
349
|
+
const pad = new Array(padbytesize)
|
|
350
|
+
pad.fill(padbytesize)
|
|
351
|
+
const paddedbuf = [...buf, ...pad]
|
|
352
|
+
return paddedbuf
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
public static pkcs7Unpad (paddedbuf: number[]): number[] {
|
|
356
|
+
const padlength = paddedbuf[paddedbuf.length - 1]
|
|
357
|
+
const padbuf = paddedbuf.slice(paddedbuf.length - padlength, paddedbuf.length)
|
|
358
|
+
const padbuf2 = new Array(padlength)
|
|
359
|
+
padbuf2.fill(padlength)
|
|
360
|
+
if (toHex(padbuf) !== toHex(padbuf2)) {
|
|
361
|
+
throw new Error('invalid padding')
|
|
353
362
|
}
|
|
363
|
+
return paddedbuf.slice(0, paddedbuf.length - padlength)
|
|
364
|
+
}
|
|
354
365
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
const padbuf2 = new Array(padlength)
|
|
359
|
-
padbuf2.fill(padlength)
|
|
360
|
-
if (toHex(padbuf) !== toHex(padbuf2)) {
|
|
361
|
-
throw new Error('invalid padding')
|
|
362
|
-
}
|
|
363
|
-
return paddedbuf.slice(0, paddedbuf.length - padlength)
|
|
366
|
+
public static xorBufs (buf1: number[], buf2: number[]): number[] {
|
|
367
|
+
if (buf1.length !== buf2.length) {
|
|
368
|
+
throw new Error('bufs must have the same length')
|
|
364
369
|
}
|
|
365
370
|
|
|
366
|
-
|
|
367
|
-
if (buf1.length !== buf2.length) {
|
|
368
|
-
throw new Error('bufs must have the same length')
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
const buf = new Array(buf1.length)
|
|
372
|
-
|
|
373
|
-
for (let i = 0; i < buf1.length; i++) {
|
|
374
|
-
buf[i] = buf1[i] ^ buf2[i]
|
|
375
|
-
}
|
|
371
|
+
const buf = new Array(buf1.length)
|
|
376
372
|
|
|
377
|
-
|
|
373
|
+
for (let i = 0; i < buf1.length; i++) {
|
|
374
|
+
buf[i] = buf1[i] ^ buf2[i]
|
|
378
375
|
}
|
|
376
|
+
|
|
377
|
+
return buf
|
|
378
|
+
}
|
|
379
379
|
}
|
|
380
380
|
|
|
381
381
|
class AESCBC {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
382
|
+
public static encrypt (messageBuf: number[], cipherKeyBuf: number[], ivBuf: number[], concatIvBuf = true): number[] {
|
|
383
|
+
ivBuf = ivBuf || new Array(128 / 8).fill(0) || Random(128 / 8)
|
|
384
|
+
const ctBuf = CBC.encrypt(messageBuf, ivBuf, AESWrapper, cipherKeyBuf)
|
|
385
|
+
if (concatIvBuf) {
|
|
386
|
+
return [...ivBuf, ...ctBuf]
|
|
387
|
+
} else {
|
|
388
|
+
return [...ctBuf]
|
|
390
389
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
public static decrypt (encBuf: number[], cipherKeyBuf: number[], ivBuf?: number[]): number[] {
|
|
393
|
+
if (!ivBuf) {
|
|
394
|
+
ivBuf = encBuf.slice(0, 128 / 8)
|
|
395
|
+
const ctBuf = encBuf.slice(128 / 8)
|
|
396
|
+
return CBC.decrypt(ctBuf, ivBuf, AESWrapper, cipherKeyBuf)
|
|
397
|
+
} else {
|
|
398
|
+
const ctBuf = encBuf
|
|
399
|
+
return CBC.decrypt(ctBuf, ivBuf, AESWrapper, cipherKeyBuf)
|
|
401
400
|
}
|
|
401
|
+
}
|
|
402
402
|
}
|
|
403
403
|
|
|
404
404
|
/**
|
|
405
405
|
* @class ECIES
|
|
406
406
|
* Implements the Electrum ECIES protocol for encrypted communication.
|
|
407
|
-
*
|
|
407
|
+
*
|
|
408
408
|
* @prprecated This class is deprecated in favor of the BRC-78 standard for portable encrypted messages,
|
|
409
409
|
* which provides a more comprehensive and secure solution by integrating with BRC-42 and BRC-43 standards.
|
|
410
410
|
*/
|
|
411
411
|
export default class ECIES {
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
* Generates the initialization vector (iv), encryption key (kE), and MAC key (kM)
|
|
412
|
+
/**
|
|
413
|
+
* Generates the initialization vector (iv), encryption key (kE), and MAC key (kM)
|
|
415
414
|
* using the sender's private key and receiver's public key.
|
|
416
415
|
*
|
|
417
416
|
* @param {PrivateKey} privKey - The sender's private key.
|
|
418
417
|
* @param {PublicKey} pubKey - The receiver's public key.
|
|
419
418
|
* @returns {Object} An object containing the iv, kE, and kM as number arrays.
|
|
420
419
|
*/
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
}
|
|
420
|
+
public static ivkEkM (privKey: PrivateKey, pubKey: PublicKey): { iv: number[], kE: number[], kM: number[] } {
|
|
421
|
+
const r = privKey
|
|
422
|
+
const KB = pubKey
|
|
423
|
+
const P = KB.mul(r)
|
|
424
|
+
const S = new PublicKey(P.x, P.y)
|
|
425
|
+
const Sbuf = S.encode(true) as number[]
|
|
426
|
+
const hash = Hash.sha512(Sbuf)
|
|
427
|
+
return {
|
|
428
|
+
iv: hash.slice(0, 16),
|
|
429
|
+
kE: hash.slice(16, 32),
|
|
430
|
+
kM: hash.slice(32, 64)
|
|
433
431
|
}
|
|
432
|
+
}
|
|
434
433
|
|
|
435
|
-
|
|
434
|
+
/**
|
|
436
435
|
* Encrypts a given message using the Electrum ECIES method.
|
|
437
|
-
*
|
|
436
|
+
*
|
|
438
437
|
* @param {number[]} messageBuf - The message to be encrypted, in number array format.
|
|
439
438
|
* @param {PublicKey} toPublicKey - The public key of the recipient.
|
|
440
439
|
* @param {PrivateKey} [fromPrivateKey] - The private key of the sender. If not provided, a random private key is used.
|
|
441
440
|
* @param {boolean} [noKey=false] - If true, does not include the sender's public key in the encrypted message.
|
|
442
441
|
* @returns {number[]} The encrypted message as a number array.
|
|
443
442
|
*/
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
}
|
|
449
|
-
if (!noKey) {
|
|
450
|
-
Rbuf = fromPrivateKey.toPublicKey().encode(true)
|
|
451
|
-
}
|
|
452
|
-
const { iv, kE, kM } = ECIES.ivkEkM(fromPrivateKey, toPublicKey)
|
|
453
|
-
const ciphertext = AESCBC.encrypt(messageBuf, kE, iv, false)
|
|
454
|
-
const BIE1 = toArray('BIE1', 'utf8')
|
|
455
|
-
let encBuf: number[]
|
|
456
|
-
if (Rbuf) {
|
|
457
|
-
encBuf = [...BIE1, ...Rbuf, ...ciphertext]
|
|
458
|
-
} else {
|
|
459
|
-
encBuf = [...BIE1, ...ciphertext]
|
|
460
|
-
}
|
|
461
|
-
const hmac = Hash.sha256hmac(kM, encBuf) as number[]
|
|
462
|
-
return [...encBuf, ...hmac]
|
|
443
|
+
public static electrumEncrypt (messageBuf: number[], toPublicKey: PublicKey, fromPrivateKey?: PrivateKey, noKey = false): number[] {
|
|
444
|
+
let Rbuf
|
|
445
|
+
if (fromPrivateKey === null) {
|
|
446
|
+
fromPrivateKey = PrivateKey.fromRandom()
|
|
463
447
|
}
|
|
448
|
+
if (!noKey) {
|
|
449
|
+
Rbuf = fromPrivateKey.toPublicKey().encode(true)
|
|
450
|
+
}
|
|
451
|
+
const { iv, kE, kM } = ECIES.ivkEkM(fromPrivateKey, toPublicKey)
|
|
452
|
+
const ciphertext = AESCBC.encrypt(messageBuf, kE, iv, false)
|
|
453
|
+
const BIE1 = toArray('BIE1', 'utf8')
|
|
454
|
+
let encBuf: number[]
|
|
455
|
+
if (Rbuf) {
|
|
456
|
+
encBuf = [...BIE1, ...Rbuf, ...ciphertext]
|
|
457
|
+
} else {
|
|
458
|
+
encBuf = [...BIE1, ...ciphertext]
|
|
459
|
+
}
|
|
460
|
+
const hmac = Hash.sha256hmac(kM, encBuf)
|
|
461
|
+
return [...encBuf, ...hmac]
|
|
462
|
+
}
|
|
464
463
|
|
|
465
|
-
|
|
464
|
+
/**
|
|
466
465
|
* Decrypts a message encrypted using the Electrum ECIES method.
|
|
467
466
|
*
|
|
468
467
|
* @param {number[]} encBuf - The encrypted message buffer.
|
|
@@ -470,88 +469,88 @@ export default class ECIES {
|
|
|
470
469
|
* @param {PublicKey} [fromPublicKey=null] - The public key of the sender. If not provided, it is extracted from the message.
|
|
471
470
|
* @returns {number[]} The decrypted message as a number array.
|
|
472
471
|
*/
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
}
|
|
480
|
-
let offset = 4
|
|
481
|
-
if (fromPublicKey === null) {
|
|
482
|
-
// BIE1 use compressed public key, length is always 33.
|
|
483
|
-
const pub = encBuf.slice(4, 37)
|
|
484
|
-
fromPublicKey = PublicKey.fromString(toHex(pub))
|
|
485
|
-
offset = 37
|
|
486
|
-
}
|
|
487
|
-
const { iv, kE, kM } = ECIES.ivkEkM(toPrivateKey, fromPublicKey)
|
|
488
|
-
const ciphertext = encBuf.slice(offset, encBuf.length - tagLength)
|
|
489
|
-
const hmac = encBuf.slice(encBuf.length - tagLength, encBuf.length)
|
|
490
|
-
|
|
491
|
-
const hmac2 = Hash.sha256hmac(kM, encBuf.slice(0, encBuf.length - tagLength)) as number[]
|
|
492
|
-
|
|
493
|
-
if (toHex(hmac) !== toHex(hmac2)) {
|
|
494
|
-
throw new Error('Invalid checksum')
|
|
495
|
-
}
|
|
496
|
-
return AESCBC.decrypt(ciphertext, kE, iv)
|
|
472
|
+
public static electrumDecrypt (encBuf: number[], toPrivateKey: PrivateKey, fromPublicKey: PublicKey = null): number[] {
|
|
473
|
+
const tagLength = 32
|
|
474
|
+
|
|
475
|
+
const magic = encBuf.slice(0, 4)
|
|
476
|
+
if (encode(magic, 'utf8') !== 'BIE1') {
|
|
477
|
+
throw new Error('Invalid Magic')
|
|
497
478
|
}
|
|
479
|
+
let offset = 4
|
|
480
|
+
if (fromPublicKey === null) {
|
|
481
|
+
// BIE1 use compressed public key, length is always 33.
|
|
482
|
+
const pub = encBuf.slice(4, 37)
|
|
483
|
+
fromPublicKey = PublicKey.fromString(toHex(pub))
|
|
484
|
+
offset = 37
|
|
485
|
+
}
|
|
486
|
+
const { iv, kE, kM } = ECIES.ivkEkM(toPrivateKey, fromPublicKey)
|
|
487
|
+
const ciphertext = encBuf.slice(offset, encBuf.length - tagLength)
|
|
488
|
+
const hmac = encBuf.slice(encBuf.length - tagLength, encBuf.length)
|
|
489
|
+
|
|
490
|
+
const hmac2 = Hash.sha256hmac(kM, encBuf.slice(0, encBuf.length - tagLength))
|
|
491
|
+
|
|
492
|
+
if (toHex(hmac) !== toHex(hmac2)) {
|
|
493
|
+
throw new Error('Invalid checksum')
|
|
494
|
+
}
|
|
495
|
+
return AESCBC.decrypt(ciphertext, kE, iv)
|
|
496
|
+
}
|
|
498
497
|
|
|
499
|
-
|
|
498
|
+
/**
|
|
500
499
|
* Encrypts a given message using the Bitcore variant of ECIES.
|
|
501
|
-
*
|
|
500
|
+
*
|
|
502
501
|
* @param {number[]} messageBuf - The message to be encrypted, in number array format.
|
|
503
502
|
* @param {PublicKey} toPublicKey - The public key of the recipient.
|
|
504
503
|
* @param {PrivateKey} [fromPrivateKey] - The private key of the sender. If not provided, a random private key is used.
|
|
505
504
|
* @param {number[]} [ivBuf] - The initialization vector for encryption. If not provided, a random IV is used.
|
|
506
505
|
* @returns {number[]} The encrypted message as a number array.
|
|
507
506
|
*/
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
}
|
|
512
|
-
const r = fromPrivateKey
|
|
513
|
-
const RPublicKey = fromPrivateKey.toPublicKey()
|
|
514
|
-
const RBuf = RPublicKey.encode(true) as number[]
|
|
515
|
-
const KB = toPublicKey
|
|
516
|
-
const P = KB.mul(r)
|
|
517
|
-
const S = P.getX()
|
|
518
|
-
const Sbuf = S.toArray('be', 32)
|
|
519
|
-
const kEkM = Hash.sha512(Sbuf) as number[]
|
|
520
|
-
const kE = kEkM.slice(0, 32)
|
|
521
|
-
const kM = kEkM.slice(32, 64)
|
|
522
|
-
const c = AESCBC.encrypt(messageBuf, kE, ivBuf)
|
|
523
|
-
const d = Hash.sha256hmac(kM, [...c]) as number[]
|
|
524
|
-
const encBuf = [...RBuf, ...c, ...d]
|
|
525
|
-
return encBuf
|
|
507
|
+
public static bitcoreEncrypt (messageBuf: number[], toPublicKey: PublicKey, fromPrivateKey?: PrivateKey, ivBuf?: number[]): number[] {
|
|
508
|
+
if (!fromPrivateKey) {
|
|
509
|
+
fromPrivateKey = PrivateKey.fromRandom()
|
|
526
510
|
}
|
|
527
|
-
|
|
528
|
-
|
|
511
|
+
const r = fromPrivateKey
|
|
512
|
+
const RPublicKey = fromPrivateKey.toPublicKey()
|
|
513
|
+
const RBuf = RPublicKey.encode(true) as number[]
|
|
514
|
+
const KB = toPublicKey
|
|
515
|
+
const P = KB.mul(r)
|
|
516
|
+
const S = P.getX()
|
|
517
|
+
const Sbuf = S.toArray('be', 32)
|
|
518
|
+
const kEkM = Hash.sha512(Sbuf)
|
|
519
|
+
const kE = kEkM.slice(0, 32)
|
|
520
|
+
const kM = kEkM.slice(32, 64)
|
|
521
|
+
const c = AESCBC.encrypt(messageBuf, kE, ivBuf)
|
|
522
|
+
const d = Hash.sha256hmac(kM, [...c])
|
|
523
|
+
const encBuf = [...RBuf, ...c, ...d]
|
|
524
|
+
return encBuf
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
529
528
|
* Decrypts a message encrypted using the Bitcore variant of ECIES.
|
|
530
529
|
*
|
|
531
530
|
* @param {number[]} encBuf - The encrypted message buffer.
|
|
532
531
|
* @param {PrivateKey} toPrivateKey - The private key of the recipient.
|
|
533
532
|
* @returns {number[]} The decrypted message as a number array.
|
|
534
533
|
*/
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
}
|
|
554
|
-
const messageBuf = AESCBC.decrypt(c, kE)
|
|
555
|
-
return [...messageBuf]
|
|
534
|
+
public static bitcoreDecrypt (encBuf: number[], toPrivateKey: PrivateKey): number[] {
|
|
535
|
+
const kB = toPrivateKey
|
|
536
|
+
const fromPublicKey = PublicKey.fromString(toHex(encBuf.slice(0, 33)))
|
|
537
|
+
const R = fromPublicKey
|
|
538
|
+
const P = R.mul(kB)
|
|
539
|
+
if (P.eq(new Point(0, 0))) {
|
|
540
|
+
throw new Error('P equals 0')
|
|
541
|
+
}
|
|
542
|
+
const S = P.getX()
|
|
543
|
+
const Sbuf = S.toArray('be', 32)
|
|
544
|
+
const kEkM = Hash.sha512(Sbuf)
|
|
545
|
+
const kE = kEkM.slice(0, 32)
|
|
546
|
+
const kM = kEkM.slice(32, 64)
|
|
547
|
+
const c = encBuf.slice(33, encBuf.length - 32)
|
|
548
|
+
const d = encBuf.slice(encBuf.length - 32, encBuf.length)
|
|
549
|
+
const d2 = Hash.sha256hmac(kM, c)
|
|
550
|
+
if (toHex(d) !== toHex(d2)) {
|
|
551
|
+
throw new Error('Invalid checksum')
|
|
556
552
|
}
|
|
553
|
+
const messageBuf = AESCBC.decrypt(c, kE)
|
|
554
|
+
return [...messageBuf]
|
|
555
|
+
}
|
|
557
556
|
}
|