smartledger-bsv 3.0.0
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/LICENSE +36 -0
- package/README.md +305 -0
- package/SECURITY.md +75 -0
- package/bsv-ecies.min.js +12 -0
- package/bsv-message.min.js +10 -0
- package/bsv-mnemonic.min.js +12 -0
- package/bsv.d.ts +440 -0
- package/bsv.min.js +37 -0
- package/ecies/index.js +1 -0
- package/index.js +101 -0
- package/lib/address.js +526 -0
- package/lib/block/block.js +277 -0
- package/lib/block/blockheader.js +294 -0
- package/lib/block/index.js +4 -0
- package/lib/block/merkleblock.js +316 -0
- package/lib/crypto/bn.js +278 -0
- package/lib/crypto/ecdsa.js +330 -0
- package/lib/crypto/elliptic-fixed.js +74 -0
- package/lib/crypto/hash.browser.js +171 -0
- package/lib/crypto/hash.js +2 -0
- package/lib/crypto/hash.node.js +171 -0
- package/lib/crypto/point.js +217 -0
- package/lib/crypto/random.js +37 -0
- package/lib/crypto/signature.js +410 -0
- package/lib/crypto/smartledger_verify.js +109 -0
- package/lib/ecies/bitcore-ecies.js +163 -0
- package/lib/ecies/electrum-ecies.js +175 -0
- package/lib/ecies/errors.js +16 -0
- package/lib/ecies/index.js +1 -0
- package/lib/encoding/base58.js +108 -0
- package/lib/encoding/base58check.js +112 -0
- package/lib/encoding/bufferreader.js +200 -0
- package/lib/encoding/bufferwriter.js +150 -0
- package/lib/encoding/varint.js +71 -0
- package/lib/errors/index.js +57 -0
- package/lib/errors/spec.js +184 -0
- package/lib/hdprivatekey.js +655 -0
- package/lib/hdpublickey.js +509 -0
- package/lib/message/index.js +4 -0
- package/lib/message/message.js +181 -0
- package/lib/mnemonic/errors.js +18 -0
- package/lib/mnemonic/index.js +4 -0
- package/lib/mnemonic/mnemonic.js +304 -0
- package/lib/mnemonic/pbkdf2.js +68 -0
- package/lib/mnemonic/words/chinese.js +5 -0
- package/lib/mnemonic/words/english.js +5 -0
- package/lib/mnemonic/words/french.js +5 -0
- package/lib/mnemonic/words/index.js +8 -0
- package/lib/mnemonic/words/italian.js +5 -0
- package/lib/mnemonic/words/japanese.js +5 -0
- package/lib/mnemonic/words/spanish.js +5 -0
- package/lib/networks.js +392 -0
- package/lib/opcode.js +248 -0
- package/lib/privatekey.js +373 -0
- package/lib/publickey.js +387 -0
- package/lib/script/index.js +3 -0
- package/lib/script/interpreter.js +1807 -0
- package/lib/script/script.js +1153 -0
- package/lib/transaction/index.js +7 -0
- package/lib/transaction/input/index.js +6 -0
- package/lib/transaction/input/input.js +202 -0
- package/lib/transaction/input/multisig.js +220 -0
- package/lib/transaction/input/multisigscripthash.js +189 -0
- package/lib/transaction/input/publickey.js +96 -0
- package/lib/transaction/input/publickeyhash.js +103 -0
- package/lib/transaction/output.js +192 -0
- package/lib/transaction/sighash.js +288 -0
- package/lib/transaction/signature.js +88 -0
- package/lib/transaction/transaction.js +1208 -0
- package/lib/transaction/unspentoutput.js +97 -0
- package/lib/util/_.js +44 -0
- package/lib/util/js.js +91 -0
- package/lib/util/preconditions.js +34 -0
- package/message/index.js +1 -0
- package/mnemonic/index.js +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var BN = require('./bn')
|
|
4
|
+
var _ = require('../util/_')
|
|
5
|
+
var $ = require('../util/preconditions')
|
|
6
|
+
var JSUtil = require('../util/js')
|
|
7
|
+
|
|
8
|
+
var Signature = function Signature (r, s) {
|
|
9
|
+
if (!(this instanceof Signature)) {
|
|
10
|
+
return new Signature(r, s)
|
|
11
|
+
}
|
|
12
|
+
if (r instanceof BN) {
|
|
13
|
+
this.set({
|
|
14
|
+
r: r,
|
|
15
|
+
s: s
|
|
16
|
+
})
|
|
17
|
+
} else if (r) {
|
|
18
|
+
var obj = r
|
|
19
|
+
this.set(obj)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
Signature.prototype.set = function (obj) {
|
|
24
|
+
this.r = obj.r || this.r || undefined
|
|
25
|
+
this.s = obj.s || this.s || undefined
|
|
26
|
+
|
|
27
|
+
this.i = typeof obj.i !== 'undefined' ? obj.i : this.i // public key recovery parameter in range [0, 3]
|
|
28
|
+
this.compressed = typeof obj.compressed !== 'undefined'
|
|
29
|
+
? obj.compressed : this.compressed // whether the recovered pubkey is compressed
|
|
30
|
+
this.nhashtype = obj.nhashtype || this.nhashtype || undefined
|
|
31
|
+
return this
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
Signature.fromCompact = function (buf) {
|
|
35
|
+
$.checkArgument(Buffer.isBuffer(buf), 'Argument is expected to be a Buffer')
|
|
36
|
+
|
|
37
|
+
var sig = new Signature()
|
|
38
|
+
|
|
39
|
+
var compressed = true
|
|
40
|
+
var i = buf.slice(0, 1)[0] - 27 - 4
|
|
41
|
+
if (i < 0) {
|
|
42
|
+
compressed = false
|
|
43
|
+
i = i + 4
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
var b2 = buf.slice(1, 33)
|
|
47
|
+
var b3 = buf.slice(33, 65)
|
|
48
|
+
|
|
49
|
+
$.checkArgument(i === 0 || i === 1 || i === 2 || i === 3, new Error('i must be 0, 1, 2, or 3'))
|
|
50
|
+
$.checkArgument(b2.length === 32, new Error('r must be 32 bytes'))
|
|
51
|
+
$.checkArgument(b3.length === 32, new Error('s must be 32 bytes'))
|
|
52
|
+
|
|
53
|
+
sig.compressed = compressed
|
|
54
|
+
sig.i = i
|
|
55
|
+
sig.r = BN.fromBuffer(b2)
|
|
56
|
+
sig.s = BN.fromBuffer(b3)
|
|
57
|
+
|
|
58
|
+
return sig
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
Signature.fromDER = Signature.fromBuffer = function (buf, strict) {
|
|
62
|
+
var obj = Signature.parseDER(buf, strict)
|
|
63
|
+
var sig = new Signature()
|
|
64
|
+
|
|
65
|
+
sig.r = obj.r
|
|
66
|
+
sig.s = obj.s
|
|
67
|
+
|
|
68
|
+
return sig
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// The format used in a tx
|
|
72
|
+
Signature.fromTxFormat = function (buf) {
|
|
73
|
+
var nhashtype = buf.readUInt8(buf.length - 1)
|
|
74
|
+
var derbuf = buf.slice(0, buf.length - 1)
|
|
75
|
+
var sig = Signature.fromDER(derbuf, false)
|
|
76
|
+
sig.nhashtype = nhashtype
|
|
77
|
+
return sig
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Signature.fromString = function (str) {
|
|
81
|
+
var buf = Buffer.from(str, 'hex')
|
|
82
|
+
return Signature.fromDER(buf)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* In order to mimic the non-strict DER encoding of OpenSSL, set strict = false.
|
|
87
|
+
*/
|
|
88
|
+
Signature.parseDER = function (buf, strict) {
|
|
89
|
+
$.checkArgument(Buffer.isBuffer(buf), new Error('DER formatted signature should be a buffer'))
|
|
90
|
+
if (_.isUndefined(strict)) {
|
|
91
|
+
strict = true
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
var header = buf[0]
|
|
95
|
+
$.checkArgument(header === 0x30, new Error('Header byte should be 0x30'))
|
|
96
|
+
|
|
97
|
+
var length = buf[1]
|
|
98
|
+
var buflength = buf.slice(2).length
|
|
99
|
+
$.checkArgument(!strict || length === buflength, new Error('Length byte should length of what follows'))
|
|
100
|
+
|
|
101
|
+
length = length < buflength ? length : buflength
|
|
102
|
+
|
|
103
|
+
var rheader = buf[2 + 0]
|
|
104
|
+
$.checkArgument(rheader === 0x02, new Error('Integer byte for r should be 0x02'))
|
|
105
|
+
|
|
106
|
+
var rlength = buf[2 + 1]
|
|
107
|
+
var rbuf = buf.slice(2 + 2, 2 + 2 + rlength)
|
|
108
|
+
var r = BN.fromBuffer(rbuf)
|
|
109
|
+
var rneg = buf[2 + 1 + 1] === 0x00
|
|
110
|
+
$.checkArgument(rlength === rbuf.length, new Error('Length of r incorrect'))
|
|
111
|
+
|
|
112
|
+
var sheader = buf[2 + 2 + rlength + 0]
|
|
113
|
+
$.checkArgument(sheader === 0x02, new Error('Integer byte for s should be 0x02'))
|
|
114
|
+
|
|
115
|
+
var slength = buf[2 + 2 + rlength + 1]
|
|
116
|
+
var sbuf = buf.slice(2 + 2 + rlength + 2, 2 + 2 + rlength + 2 + slength)
|
|
117
|
+
var s = BN.fromBuffer(sbuf)
|
|
118
|
+
var sneg = buf[2 + 2 + rlength + 2 + 2] === 0x00
|
|
119
|
+
$.checkArgument(slength === sbuf.length, new Error('Length of s incorrect'))
|
|
120
|
+
|
|
121
|
+
var sumlength = 2 + 2 + rlength + 2 + slength
|
|
122
|
+
$.checkArgument(length === sumlength - 2, new Error('Length of signature incorrect'))
|
|
123
|
+
|
|
124
|
+
var obj = {
|
|
125
|
+
header: header,
|
|
126
|
+
length: length,
|
|
127
|
+
rheader: rheader,
|
|
128
|
+
rlength: rlength,
|
|
129
|
+
rneg: rneg,
|
|
130
|
+
rbuf: rbuf,
|
|
131
|
+
r: r,
|
|
132
|
+
sheader: sheader,
|
|
133
|
+
slength: slength,
|
|
134
|
+
sneg: sneg,
|
|
135
|
+
sbuf: sbuf,
|
|
136
|
+
s: s
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return obj
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
Signature.prototype.toCompact = function (i, compressed) {
|
|
143
|
+
i = typeof i === 'number' ? i : this.i
|
|
144
|
+
compressed = typeof compressed === 'boolean' ? compressed : this.compressed
|
|
145
|
+
|
|
146
|
+
if (!(i === 0 || i === 1 || i === 2 || i === 3)) {
|
|
147
|
+
throw new Error('i must be equal to 0, 1, 2, or 3')
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
var val = i + 27 + 4
|
|
151
|
+
if (compressed === false) {
|
|
152
|
+
val = val - 4
|
|
153
|
+
}
|
|
154
|
+
var b1 = Buffer.from([val])
|
|
155
|
+
var b2 = this.r.toBuffer({
|
|
156
|
+
size: 32
|
|
157
|
+
})
|
|
158
|
+
var b3 = this.s.toBuffer({
|
|
159
|
+
size: 32
|
|
160
|
+
})
|
|
161
|
+
return Buffer.concat([b1, b2, b3])
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
Signature.prototype.toBuffer = Signature.prototype.toDER = function () {
|
|
165
|
+
var rnbuf = this.r.toBuffer()
|
|
166
|
+
var snbuf = this.s.toBuffer()
|
|
167
|
+
|
|
168
|
+
var rneg = !!(rnbuf[0] & 0x80)
|
|
169
|
+
var sneg = !!(snbuf[0] & 0x80)
|
|
170
|
+
|
|
171
|
+
var rbuf = rneg ? Buffer.concat([Buffer.from([0x00]), rnbuf]) : rnbuf
|
|
172
|
+
var sbuf = sneg ? Buffer.concat([Buffer.from([0x00]), snbuf]) : snbuf
|
|
173
|
+
|
|
174
|
+
var rlength = rbuf.length
|
|
175
|
+
var slength = sbuf.length
|
|
176
|
+
var length = 2 + rlength + 2 + slength
|
|
177
|
+
var rheader = 0x02
|
|
178
|
+
var sheader = 0x02
|
|
179
|
+
var header = 0x30
|
|
180
|
+
|
|
181
|
+
var der = Buffer.concat([Buffer.from([header, length, rheader, rlength]), rbuf, Buffer.from([sheader, slength]), sbuf])
|
|
182
|
+
return der
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
Signature.prototype.toString = function () {
|
|
186
|
+
var buf = this.toDER()
|
|
187
|
+
return buf.toString('hex')
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* This function is translated from bitcoind's IsDERSignature and is used in
|
|
192
|
+
* the script interpreter. This "DER" format actually includes an extra byte,
|
|
193
|
+
* the nhashtype, at the end. It is really the tx format, not DER format.
|
|
194
|
+
*
|
|
195
|
+
* A canonical signature exists of: [30] [total len] [02] [len R] [R] [02] [len S] [S] [hashtype]
|
|
196
|
+
* Where R and S are not negative (their first byte has its highest bit not set), and not
|
|
197
|
+
* excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
|
|
198
|
+
* in which case a single 0 byte is necessary and even required).
|
|
199
|
+
*
|
|
200
|
+
* See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
|
|
201
|
+
*/
|
|
202
|
+
Signature.isTxDER = function (buf) {
|
|
203
|
+
if (buf.length < 9) {
|
|
204
|
+
// Non-canonical signature: too short
|
|
205
|
+
return false
|
|
206
|
+
}
|
|
207
|
+
if (buf.length > 73) {
|
|
208
|
+
// Non-canonical signature: too long
|
|
209
|
+
return false
|
|
210
|
+
}
|
|
211
|
+
if (buf[0] !== 0x30) {
|
|
212
|
+
// Non-canonical signature: wrong type
|
|
213
|
+
return false
|
|
214
|
+
}
|
|
215
|
+
if (buf[1] !== buf.length - 3) {
|
|
216
|
+
// Non-canonical signature: wrong length marker
|
|
217
|
+
return false
|
|
218
|
+
}
|
|
219
|
+
var nLenR = buf[3]
|
|
220
|
+
if (5 + nLenR >= buf.length) {
|
|
221
|
+
// Non-canonical signature: S length misplaced
|
|
222
|
+
return false
|
|
223
|
+
}
|
|
224
|
+
var nLenS = buf[5 + nLenR]
|
|
225
|
+
if ((nLenR + nLenS + 7) !== buf.length) {
|
|
226
|
+
// Non-canonical signature: R+S length mismatch
|
|
227
|
+
return false
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
var R = buf.slice(4)
|
|
231
|
+
if (buf[4 - 2] !== 0x02) {
|
|
232
|
+
// Non-canonical signature: R value type mismatch
|
|
233
|
+
return false
|
|
234
|
+
}
|
|
235
|
+
if (nLenR === 0) {
|
|
236
|
+
// Non-canonical signature: R length is zero
|
|
237
|
+
return false
|
|
238
|
+
}
|
|
239
|
+
if (R[0] & 0x80) {
|
|
240
|
+
// Non-canonical signature: R value negative
|
|
241
|
+
return false
|
|
242
|
+
}
|
|
243
|
+
if (nLenR > 1 && (R[0] === 0x00) && !(R[1] & 0x80)) {
|
|
244
|
+
// Non-canonical signature: R value excessively padded
|
|
245
|
+
return false
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
var S = buf.slice(6 + nLenR)
|
|
249
|
+
if (buf[6 + nLenR - 2] !== 0x02) {
|
|
250
|
+
// Non-canonical signature: S value type mismatch
|
|
251
|
+
return false
|
|
252
|
+
}
|
|
253
|
+
if (nLenS === 0) {
|
|
254
|
+
// Non-canonical signature: S length is zero
|
|
255
|
+
return false
|
|
256
|
+
}
|
|
257
|
+
if (S[0] & 0x80) {
|
|
258
|
+
// Non-canonical signature: S value negative
|
|
259
|
+
return false
|
|
260
|
+
}
|
|
261
|
+
if (nLenS > 1 && (S[0] === 0x00) && !(S[1] & 0x80)) {
|
|
262
|
+
// Non-canonical signature: S value excessively padded
|
|
263
|
+
return false
|
|
264
|
+
}
|
|
265
|
+
return true
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Compares to bitcoind's IsLowDERSignature
|
|
270
|
+
* See also ECDSA signature algorithm which enforces this.
|
|
271
|
+
* See also BIP 62, "low S values in signatures"
|
|
272
|
+
*/
|
|
273
|
+
Signature.prototype.hasLowS = function () {
|
|
274
|
+
if (this.s.lt(new BN(1)) ||
|
|
275
|
+
this.s.gt(new BN('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0', 'hex'))) {
|
|
276
|
+
return false
|
|
277
|
+
}
|
|
278
|
+
return true
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* @returns true if the nhashtype is exactly equal to one of the standard options or combinations thereof.
|
|
283
|
+
* Translated from bitcoind's IsDefinedHashtypeSignature
|
|
284
|
+
*/
|
|
285
|
+
Signature.prototype.hasDefinedHashtype = function () {
|
|
286
|
+
if (!JSUtil.isNaturalNumber(this.nhashtype)) {
|
|
287
|
+
return false
|
|
288
|
+
}
|
|
289
|
+
// accept with or without Signature.SIGHASH_ANYONECANPAY by ignoring the bit
|
|
290
|
+
var temp = this.nhashtype & 0x1F
|
|
291
|
+
if (temp < Signature.SIGHASH_ALL || temp > Signature.SIGHASH_SINGLE) {
|
|
292
|
+
return false
|
|
293
|
+
}
|
|
294
|
+
return true
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
Signature.prototype.toTxFormat = function () {
|
|
298
|
+
var derbuf = this.toDER()
|
|
299
|
+
var buf = Buffer.alloc(1)
|
|
300
|
+
buf.writeUInt8(this.nhashtype, 0)
|
|
301
|
+
return Buffer.concat([derbuf, buf])
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
Signature.SIGHASH_ALL = 0x01
|
|
305
|
+
Signature.SIGHASH_NONE = 0x02
|
|
306
|
+
Signature.SIGHASH_SINGLE = 0x03
|
|
307
|
+
Signature.SIGHASH_FORKID = 0x40
|
|
308
|
+
Signature.SIGHASH_ANYONECANPAY = 0x80
|
|
309
|
+
|
|
310
|
+
// === SmartLedger Security Enhancement Methods ===
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Check if signature is canonical (s <= n/2) - SmartLedger security feature
|
|
314
|
+
*/
|
|
315
|
+
Signature.prototype.isCanonical = function () {
|
|
316
|
+
var Point = require('./point')
|
|
317
|
+
var nh = Point.getN().shrn(1) // n/2
|
|
318
|
+
return this.s.lte(nh)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Return canonicalized version of signature - SmartLedger security feature
|
|
323
|
+
*/
|
|
324
|
+
Signature.prototype.toCanonical = function () {
|
|
325
|
+
if (this.isCanonical()) {
|
|
326
|
+
return new Signature({ r: this.r, s: this.s, i: this.i, compressed: this.compressed, nhashtype: this.nhashtype })
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
var Point = require('./point')
|
|
330
|
+
var n = Point.getN()
|
|
331
|
+
var canonicalS = n.sub(this.s)
|
|
332
|
+
|
|
333
|
+
return new Signature({ r: this.r, s: canonicalS, i: this.i, compressed: this.compressed, nhashtype: this.nhashtype })
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Validate signature parameters - SmartLedger security feature
|
|
338
|
+
*/
|
|
339
|
+
Signature.prototype.validate = function () {
|
|
340
|
+
if (!this.r || !this.s) {
|
|
341
|
+
throw new Error('Signature missing r or s component')
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (this.r.isZero()) {
|
|
345
|
+
throw new Error('Signature r component is zero')
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (this.s.isZero()) {
|
|
349
|
+
throw new Error('Signature s component is zero')
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
var Point = require('./point')
|
|
353
|
+
var n = Point.getN()
|
|
354
|
+
|
|
355
|
+
if (this.r.gte(n)) {
|
|
356
|
+
throw new Error('Signature r component >= curve order')
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (this.s.gte(n)) {
|
|
360
|
+
throw new Error('Signature s component >= curve order')
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (this.r.isNeg()) {
|
|
364
|
+
throw new Error('Signature r component is negative')
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (this.s.isNeg()) {
|
|
368
|
+
throw new Error('Signature s component is negative')
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return true
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Boolean signature validation - SmartLedger security feature
|
|
376
|
+
*/
|
|
377
|
+
Signature.prototype.isValid = function () {
|
|
378
|
+
try {
|
|
379
|
+
this.validate()
|
|
380
|
+
return true
|
|
381
|
+
} catch (e) {
|
|
382
|
+
return false
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Apply SmartLedger security canonicalization - called during crypto operations
|
|
388
|
+
*/
|
|
389
|
+
Signature.prototype.applySecurityPatches = function () {
|
|
390
|
+
var Point = require('./point')
|
|
391
|
+
var n = Point.getN()
|
|
392
|
+
var nh = n.shrn(1) // n/2
|
|
393
|
+
|
|
394
|
+
// Security validation
|
|
395
|
+
if (this.r.isZero() || this.s.isZero()) {
|
|
396
|
+
throw new Error('Invalid signature: zero r or s')
|
|
397
|
+
}
|
|
398
|
+
if (this.r.gte(n) || this.s.gte(n)) {
|
|
399
|
+
throw new Error('Invalid signature: out of range')
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Canonicalize s value (anti-malleability)
|
|
403
|
+
if (this.s.gt(nh)) {
|
|
404
|
+
this.s = n.sub(this.s)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return this
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
module.exports = Signature
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SmartLedger Hardened Verification Module
|
|
5
|
+
* Provides secure ECDSA signature verification with malleability protection
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const BN = require('./bn')
|
|
9
|
+
const Point = require('./point')
|
|
10
|
+
const ECDSA = require('./ecdsa')
|
|
11
|
+
|
|
12
|
+
// Cache curve constants for performance
|
|
13
|
+
const n = Point.getN()
|
|
14
|
+
const nh = n.shrn(1) // n / 2
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Hardened signature verification with canonicalization
|
|
18
|
+
* @param {Buffer} msgHash - 32-byte message hash
|
|
19
|
+
* @param {Signature} sig - Signature object with r,s components
|
|
20
|
+
* @param {PublicKey} pubkey - Public key for verification
|
|
21
|
+
* @returns {boolean} - true if signature is valid and canonical
|
|
22
|
+
*/
|
|
23
|
+
function smartVerify (msgHash, sig, pubkey) {
|
|
24
|
+
// Strict input validation
|
|
25
|
+
if (!Buffer.isBuffer(msgHash) || msgHash.length !== 32) {
|
|
26
|
+
throw new Error('Invalid message hash: must be 32-byte buffer')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!sig || !sig.r || !sig.s) {
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Ensure r and s are BN instances
|
|
34
|
+
const r = BN.isBN(sig.r) ? sig.r : new BN(sig.r)
|
|
35
|
+
const s = BN.isBN(sig.s) ? sig.s : new BN(sig.s)
|
|
36
|
+
|
|
37
|
+
// Reject zero values
|
|
38
|
+
if (r.isZero() || s.isZero()) {
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Reject values >= n (curve order)
|
|
43
|
+
if (r.gte(n) || s.gte(n)) {
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Canonicalize s to lower half (anti-malleability)
|
|
48
|
+
let canonicalS = s
|
|
49
|
+
if (s.gt(nh)) {
|
|
50
|
+
canonicalS = n.sub(s)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Create canonicalized signature object
|
|
54
|
+
const canonicalSig = {
|
|
55
|
+
r: r,
|
|
56
|
+
s: canonicalS
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Use BSV's original ECDSA verify with canonical signature
|
|
60
|
+
return ECDSA.verify(msgHash, canonicalSig, pubkey)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check if signature is in canonical form (s <= n/2)
|
|
65
|
+
* @param {Object} sig - Signature with r,s components
|
|
66
|
+
* @returns {boolean} - true if signature is canonical
|
|
67
|
+
*/
|
|
68
|
+
function isCanonical (sig) {
|
|
69
|
+
if (!sig || !sig.s) {
|
|
70
|
+
return false
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const s = BN.isBN(sig.s) ? sig.s : new BN(sig.s)
|
|
74
|
+
return s.lte(nh)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Canonicalize signature to ensure s <= n/2
|
|
79
|
+
* @param {Object} sig - Signature object to canonicalize
|
|
80
|
+
* @returns {Object} - New signature object with canonical s
|
|
81
|
+
*/
|
|
82
|
+
function canonicalize (sig) {
|
|
83
|
+
if (!sig || !sig.r || !sig.s) {
|
|
84
|
+
throw new Error('Invalid signature object')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const r = BN.isBN(sig.r) ? sig.r : new BN(sig.r)
|
|
88
|
+
const s = BN.isBN(sig.s) ? sig.s : new BN(sig.s)
|
|
89
|
+
|
|
90
|
+
let canonicalS = s
|
|
91
|
+
if (s.gt(nh)) {
|
|
92
|
+
canonicalS = n.sub(s)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
r: r,
|
|
97
|
+
s: canonicalS
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = {
|
|
102
|
+
smartVerify,
|
|
103
|
+
isCanonical,
|
|
104
|
+
canonicalize,
|
|
105
|
+
constants: {
|
|
106
|
+
n: n,
|
|
107
|
+
nh: nh
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var bsv = require('../../')
|
|
4
|
+
|
|
5
|
+
var PublicKey = bsv.PublicKey
|
|
6
|
+
var Hash = bsv.crypto.Hash
|
|
7
|
+
var $ = bsv.util.preconditions
|
|
8
|
+
var aesjs = require('aes-js')
|
|
9
|
+
var CBC = aesjs.ModeOfOperation.cbc
|
|
10
|
+
var Random = bsv.crypto.Random
|
|
11
|
+
|
|
12
|
+
var AESCBC = function AESCBC () {}
|
|
13
|
+
|
|
14
|
+
AESCBC.encrypt = function (messagebuf, cipherkeybuf, ivbuf) {
|
|
15
|
+
$.checkArgument(messagebuf)
|
|
16
|
+
$.checkArgument(cipherkeybuf)
|
|
17
|
+
$.checkArgument(ivbuf)
|
|
18
|
+
ivbuf = ivbuf || Random.getRandomBuffer(128 / 8)
|
|
19
|
+
messagebuf = aesjs.padding.pkcs7.pad(messagebuf)
|
|
20
|
+
var aesCbc = new CBC(cipherkeybuf, ivbuf)
|
|
21
|
+
var ctbuf = aesCbc.encrypt(messagebuf)
|
|
22
|
+
var encbuf = Buffer.concat([ivbuf, ctbuf])
|
|
23
|
+
return encbuf
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
AESCBC.decrypt = function (encbuf, cipherkeybuf) {
|
|
27
|
+
$.checkArgument(encbuf)
|
|
28
|
+
$.checkArgument(cipherkeybuf)
|
|
29
|
+
var ivbuf = encbuf.slice(0, 128 / 8)
|
|
30
|
+
var ctbuf = encbuf.slice(128 / 8)
|
|
31
|
+
var aesCbc = new CBC(cipherkeybuf, ivbuf)
|
|
32
|
+
var messagebuf = aesCbc.decrypt(ctbuf)
|
|
33
|
+
messagebuf = aesjs.padding.pkcs7.strip(messagebuf)
|
|
34
|
+
return Buffer.from(messagebuf)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// http://en.wikipedia.org/wiki/Integrated_Encryption_Scheme
|
|
38
|
+
var ECIES = function ECIES (opts) {
|
|
39
|
+
if (!(this instanceof ECIES)) {
|
|
40
|
+
return new ECIES()
|
|
41
|
+
}
|
|
42
|
+
this.opts = opts || {}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
ECIES.prototype.privateKey = function (privateKey) {
|
|
46
|
+
$.checkArgument(privateKey, 'no private key provided')
|
|
47
|
+
|
|
48
|
+
this._privateKey = privateKey || null
|
|
49
|
+
|
|
50
|
+
return this
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
ECIES.prototype.publicKey = function (publicKey) {
|
|
54
|
+
$.checkArgument(publicKey, 'no public key provided')
|
|
55
|
+
|
|
56
|
+
this._publicKey = publicKey || null
|
|
57
|
+
|
|
58
|
+
return this
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
var cachedProperty = function (name, getter) {
|
|
62
|
+
var cachedName = '_' + name
|
|
63
|
+
Object.defineProperty(ECIES.prototype, name, {
|
|
64
|
+
configurable: false,
|
|
65
|
+
enumerable: true,
|
|
66
|
+
get: function () {
|
|
67
|
+
var value = this[cachedName]
|
|
68
|
+
if (!value) {
|
|
69
|
+
value = this[cachedName] = getter.apply(this)
|
|
70
|
+
}
|
|
71
|
+
return value
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
cachedProperty('Rbuf', function () {
|
|
77
|
+
return this._privateKey.publicKey.toDER(true)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
cachedProperty('kEkM', function () {
|
|
81
|
+
var r = this._privateKey.bn
|
|
82
|
+
var KB = this._publicKey.point
|
|
83
|
+
var P = KB.mul(r)
|
|
84
|
+
var S = P.getX()
|
|
85
|
+
var Sbuf = S.toBuffer({ size: 32 })
|
|
86
|
+
return Hash.sha512(Sbuf)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
cachedProperty('kE', function () {
|
|
90
|
+
return this.kEkM.slice(0, 32)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
cachedProperty('kM', function () {
|
|
94
|
+
return this.kEkM.slice(32, 64)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
// Encrypts the message (String or Buffer).
|
|
98
|
+
// Optional `ivbuf` contains 16-byte Buffer to be used in AES-CBC.
|
|
99
|
+
// By default, `ivbuf` is computed deterministically from message and private key using HMAC-SHA256.
|
|
100
|
+
// Deterministic IV enables end-to-end test vectors for alternative implementations.
|
|
101
|
+
// Note that identical messages have identical ciphertexts. If your protocol does not include some
|
|
102
|
+
// kind of a sequence identifier inside the message *and* it is important to not allow attacker to learn
|
|
103
|
+
// that message is repeated, then you should use custom IV.
|
|
104
|
+
// For random IV, pass `Random.getRandomBuffer(16)` for the second argument.
|
|
105
|
+
ECIES.prototype.encrypt = function (message, ivbuf) {
|
|
106
|
+
if (!Buffer.isBuffer(message)) message = Buffer.from(message)
|
|
107
|
+
if (ivbuf === undefined) {
|
|
108
|
+
ivbuf = Hash.sha256hmac(message, this._privateKey.toBuffer()).slice(0, 16)
|
|
109
|
+
}
|
|
110
|
+
var c = AESCBC.encrypt(message, this.kE, ivbuf)
|
|
111
|
+
var d = Hash.sha256hmac(c, this.kM)
|
|
112
|
+
if (this.opts.shortTag) d = d.slice(0, 4)
|
|
113
|
+
var encbuf
|
|
114
|
+
if (this.opts.noKey) {
|
|
115
|
+
encbuf = Buffer.concat([c, d])
|
|
116
|
+
} else {
|
|
117
|
+
encbuf = Buffer.concat([this.Rbuf, c, d])
|
|
118
|
+
}
|
|
119
|
+
return encbuf
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
ECIES.prototype.decrypt = function (encbuf) {
|
|
123
|
+
$.checkArgument(encbuf)
|
|
124
|
+
var offset = 0
|
|
125
|
+
var tagLength = 32
|
|
126
|
+
if (this.opts.shortTag) {
|
|
127
|
+
tagLength = 4
|
|
128
|
+
}
|
|
129
|
+
if (!this.opts.noKey) {
|
|
130
|
+
var pub
|
|
131
|
+
switch (encbuf[0]) {
|
|
132
|
+
case 4:
|
|
133
|
+
pub = encbuf.slice(0, 65)
|
|
134
|
+
break
|
|
135
|
+
case 3:
|
|
136
|
+
case 2:
|
|
137
|
+
pub = encbuf.slice(0, 33)
|
|
138
|
+
break
|
|
139
|
+
default:
|
|
140
|
+
throw new Error('Invalid type: ' + encbuf[0])
|
|
141
|
+
}
|
|
142
|
+
this._publicKey = PublicKey.fromDER(pub)
|
|
143
|
+
offset += pub.length
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
var c = encbuf.slice(offset, encbuf.length - tagLength)
|
|
147
|
+
var d = encbuf.slice(encbuf.length - tagLength, encbuf.length)
|
|
148
|
+
|
|
149
|
+
var d2 = Hash.sha256hmac(c, this.kM)
|
|
150
|
+
if (this.opts.shortTag) d2 = d2.slice(0, 4)
|
|
151
|
+
|
|
152
|
+
var equal = true
|
|
153
|
+
for (var i = 0; i < d.length; i++) {
|
|
154
|
+
equal &= (d[i] === d2[i])
|
|
155
|
+
}
|
|
156
|
+
if (!equal) {
|
|
157
|
+
throw new Error('Invalid checksum')
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return AESCBC.decrypt(c, this.kE)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
module.exports = ECIES
|