@smartledger/bsv 5.1.0 → 5.3.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/CHANGELOG.md +73 -0
- package/README.md +42 -42
- package/bsv-covenant.min.js +18 -10
- package/bsv-ecies.min.js +13 -5
- package/bsv-gdaf.min.js +18 -10
- package/bsv-ltp.min.js +18 -10
- package/bsv-smartcontract.min.js +18 -10
- package/bsv.bundle.js +18 -10
- package/bsv.min.js +18 -10
- package/docs/MODULE_REFERENCE_COMPLETE.md +27 -27
- package/docs/advanced/UTXO_MANAGER_GUIDE.md +1 -1
- package/docs/getting-started/INSTALLATION.md +23 -23
- package/docs/getting-started/QUICK_START.md +7 -7
- package/docs/migration/FROM_BSV_1_5_6.md +5 -5
- package/index.js +0 -2
- package/lib/crypto/elliptic-fixed.js +86 -50
- package/lib/crypto/point.js +164 -103
- package/lib/ecies/bitcore-ecies.js +53 -82
- package/lib/ecies/electrum-ecies.js +33 -73
- package/package.json +4 -2
package/lib/crypto/point.js
CHANGED
|
@@ -1,50 +1,108 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
var BN = require('./bn')
|
|
4
|
+
var { secp256k1 } = require('@noble/curves/secp256k1.js')
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
// secp256k1 point + curve order from the audited @noble/curves implementation.
|
|
7
|
+
// This module preserves the (elliptic-derived) Point API the rest of the
|
|
8
|
+
// codebase depends on — mul/add/mulAdd/getX/getY/.x/.y/eq/isInfinity/validate,
|
|
9
|
+
// the static getG/getN/fromX/fromBuffer helpers — so ECDSA and key handling are
|
|
10
|
+
// unchanged. Only the underlying EC math moved from `elliptic` to `@noble`.
|
|
11
|
+
var NoblePoint = secp256k1.Point
|
|
12
|
+
var N_BIG = NoblePoint.Fn.ORDER
|
|
13
|
+
|
|
14
|
+
function bnToBig (bn) {
|
|
15
|
+
// Coordinates and scalars in this codebase are always non-negative.
|
|
16
|
+
return BigInt('0x' + (bn.toString(16) || '0'))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function bigToBN (big) {
|
|
20
|
+
var h = big.toString(16)
|
|
21
|
+
if (h.length % 2) h = '0' + h
|
|
22
|
+
return new BN(h, 16)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function toBig (v) {
|
|
26
|
+
if (v == null) throw new Error('Invalid Point')
|
|
27
|
+
if (typeof v === 'bigint') return v
|
|
28
|
+
if (typeof v === 'number') return BigInt(v)
|
|
29
|
+
if (typeof v === 'string') return BigInt(v.indexOf('0x') === 0 ? v : '0x' + v)
|
|
30
|
+
if (typeof v.toString === 'function') return bnToBig(v) // BN
|
|
31
|
+
throw new Error('Invalid Point')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Wrap a raw @noble point as a Point instance without re-validating (used by
|
|
35
|
+
// internal results of mul/add/getG/fromX which are already valid).
|
|
36
|
+
function wrap (noblePoint) {
|
|
37
|
+
var p = Object.create(Point.prototype)
|
|
38
|
+
p._p = noblePoint
|
|
39
|
+
return p
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Derive the on-curve @noble point at the given X with the requested Y parity.
|
|
43
|
+
// Throws if X is not a valid curve x-coordinate. Low-level (no Point.validate),
|
|
44
|
+
// so it is safe to call from validate() without recursion.
|
|
45
|
+
function nobleFromX (odd, x) {
|
|
46
|
+
// Build the compressed encoding via hex string padding (not BN.toBuffer) so we
|
|
47
|
+
// don't perturb BN.toBuffer call counts the rest of the codebase may rely on.
|
|
48
|
+
var xhex = toBig(x).toString(16).padStart(64, '0')
|
|
49
|
+
return NoblePoint.fromHex((odd ? '03' : '02') + xhex)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check that (xb, yb) — raw bigint coordinates — lie on the curve, producing
|
|
53
|
+
// the same error messages the previous (elliptic) implementation did. Operates
|
|
54
|
+
// on raw coordinates so it works even for inputs @noble's fromAffine rejects
|
|
55
|
+
// outright (e.g. y = 0).
|
|
56
|
+
function checkOnCurve (xb, yb) {
|
|
57
|
+
var p2
|
|
58
|
+
try {
|
|
59
|
+
p2 = nobleFromX((yb & 1n) === 1n, xb)
|
|
60
|
+
} catch (e) {
|
|
61
|
+
throw new Error('Point does not lie on the curve')
|
|
62
|
+
}
|
|
63
|
+
if (p2.toAffine().y !== yb) {
|
|
64
|
+
throw new Error('Invalid y value for curve.')
|
|
65
|
+
}
|
|
66
|
+
}
|
|
9
67
|
|
|
10
68
|
/**
|
|
11
|
-
* Instantiate a valid secp256k1 Point from the X and Y coordinates.
|
|
12
|
-
* is just an extension of the secp256k1 code from the library "elliptic" by
|
|
13
|
-
* Fedor Indutny. It includes a few extra features that are useful in Bitcoin.
|
|
14
|
-
*
|
|
69
|
+
* Instantiate a valid secp256k1 Point from the X and Y coordinates.
|
|
15
70
|
* @param {BN|String} x - The X coordinate
|
|
16
71
|
* @param {BN|String} y - The Y coordinate
|
|
17
|
-
* @
|
|
18
|
-
* @augments elliptic.curve.point
|
|
19
|
-
* @throws {Error} A validation error if exists
|
|
20
|
-
* @returns {Point} An instance of Point
|
|
72
|
+
* @returns {Point}
|
|
21
73
|
* @constructor
|
|
22
74
|
*/
|
|
23
|
-
|
|
75
|
+
function Point (x, y, isRed) {
|
|
76
|
+
if (!(this instanceof Point)) {
|
|
77
|
+
return new Point(x, y, isRed)
|
|
78
|
+
}
|
|
79
|
+
var xb, yb
|
|
24
80
|
try {
|
|
25
|
-
|
|
81
|
+
xb = toBig(x)
|
|
82
|
+
yb = toBig(y)
|
|
26
83
|
} catch (e) {
|
|
27
84
|
throw new Error('Invalid Point')
|
|
28
85
|
}
|
|
29
|
-
|
|
30
|
-
|
|
86
|
+
// Validate on the raw coordinates first (gives the specific curve/y errors and
|
|
87
|
+
// works for coordinates @noble's fromAffine rejects, e.g. y = 0).
|
|
88
|
+
checkOnCurve(xb, yb)
|
|
89
|
+
try {
|
|
90
|
+
this._p = NoblePoint.fromAffine({ x: xb, y: yb })
|
|
91
|
+
} catch (e) {
|
|
92
|
+
throw new Error('Invalid Point')
|
|
93
|
+
}
|
|
94
|
+
return this
|
|
31
95
|
}
|
|
32
96
|
|
|
33
|
-
Point.prototype = Object.getPrototypeOf(ec.curve.point())
|
|
34
|
-
|
|
35
97
|
/**
|
|
36
|
-
*
|
|
37
|
-
* Instantiate a valid secp256k1 Point from only the X coordinate. This is
|
|
38
|
-
* useful to rederive a full point from the compressed form of a point.
|
|
39
|
-
*
|
|
98
|
+
* Instantiate a valid secp256k1 Point from only the X coordinate.
|
|
40
99
|
* @param {boolean} odd - If the Y coordinate is odd
|
|
41
100
|
* @param {BN|String} x - The X coordinate
|
|
42
|
-
* @
|
|
43
|
-
* @returns {Point} An instance of Point
|
|
101
|
+
* @returns {Point}
|
|
44
102
|
*/
|
|
45
103
|
Point.fromX = function fromX (odd, x) {
|
|
46
104
|
try {
|
|
47
|
-
var point =
|
|
105
|
+
var point = wrap(nobleFromX(odd, x))
|
|
48
106
|
} catch (e) {
|
|
49
107
|
throw new Error('Invalid X')
|
|
50
108
|
}
|
|
@@ -53,89 +111,111 @@ Point.fromX = function fromX (odd, x) {
|
|
|
53
111
|
}
|
|
54
112
|
|
|
55
113
|
/**
|
|
56
|
-
*
|
|
57
|
-
* Will return a secp256k1 ECDSA base point.
|
|
58
|
-
*
|
|
59
|
-
* @link https://en.bitcoin.it/wiki/Secp256k1
|
|
60
|
-
* @returns {Point} An instance of the base point.
|
|
114
|
+
* @returns {Point} the secp256k1 base (generator) point.
|
|
61
115
|
*/
|
|
62
116
|
Point.getG = function getG () {
|
|
63
|
-
return
|
|
117
|
+
return wrap(NoblePoint.BASE)
|
|
64
118
|
}
|
|
65
119
|
|
|
66
120
|
/**
|
|
67
|
-
*
|
|
68
|
-
* Will return the max of range of valid private keys as governed by the
|
|
69
|
-
* secp256k1 ECDSA standard.
|
|
70
|
-
*
|
|
71
|
-
* @link https://en.bitcoin.it/wiki/Private_key#Range_of_valid_ECDSA_private_keys
|
|
72
|
-
* @returns {BN} A BN instance of the number of points on the curve
|
|
121
|
+
* @returns {BN} the curve order n.
|
|
73
122
|
*/
|
|
74
123
|
Point.getN = function getN () {
|
|
75
|
-
return
|
|
124
|
+
return bigToBN(N_BIG)
|
|
76
125
|
}
|
|
77
126
|
|
|
78
|
-
if (!Point.prototype._getX) { Point.prototype._getX = Point.prototype.getX }
|
|
79
|
-
|
|
80
127
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* @returns {BN} A BN instance of the X coordinate
|
|
128
|
+
* @returns {BN} the X coordinate of the Point.
|
|
84
129
|
*/
|
|
85
130
|
Point.prototype.getX = function getX () {
|
|
86
|
-
return
|
|
131
|
+
return bigToBN(this._p.toAffine().x)
|
|
87
132
|
}
|
|
88
133
|
|
|
89
|
-
if (!Point.prototype._getY) { Point.prototype._getY = Point.prototype.getY }
|
|
90
|
-
|
|
91
134
|
/**
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* @returns {BN} A BN instance of the Y coordinate
|
|
135
|
+
* @returns {BN} the Y coordinate of the Point.
|
|
95
136
|
*/
|
|
96
137
|
Point.prototype.getY = function getY () {
|
|
97
|
-
return
|
|
138
|
+
return bigToBN(this._p.toAffine().y)
|
|
98
139
|
}
|
|
99
140
|
|
|
141
|
+
// `.x` / `.y` are accessed directly in a few places (e.g. ECDSA r = Q.x.umod(N)).
|
|
142
|
+
Object.defineProperty(Point.prototype, 'x', {
|
|
143
|
+
configurable: true,
|
|
144
|
+
enumerable: true,
|
|
145
|
+
get: function () { return this.getX() }
|
|
146
|
+
})
|
|
147
|
+
Object.defineProperty(Point.prototype, 'y', {
|
|
148
|
+
configurable: true,
|
|
149
|
+
enumerable: true,
|
|
150
|
+
get: function () { return this.getY() }
|
|
151
|
+
})
|
|
152
|
+
|
|
100
153
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* @
|
|
104
|
-
*
|
|
105
|
-
*
|
|
154
|
+
* Scalar multiply this point. Every valid secp256k1 point has order n
|
|
155
|
+
* (cofactor 1), so k*P == (k mod n)*P; reducing mod n lets us always use
|
|
156
|
+
* @noble's *constant-time* `multiply` (important for the secret signing nonce)
|
|
157
|
+
* and naturally handles the boundary scalars used by validation / recovery
|
|
158
|
+
* (e.g. n itself reduces to 0 -> the point at infinity).
|
|
159
|
+
* @param {BN} k
|
|
160
|
+
* @returns {Point}
|
|
106
161
|
*/
|
|
107
|
-
Point.prototype.
|
|
108
|
-
|
|
109
|
-
|
|
162
|
+
Point.prototype.mul = function mul (k) {
|
|
163
|
+
var s = ((toBig(k) % N_BIG) + N_BIG) % N_BIG
|
|
164
|
+
if (s === 0n || this._p.is0()) {
|
|
165
|
+
return wrap(NoblePoint.ZERO)
|
|
110
166
|
}
|
|
167
|
+
return wrap(this._p.multiply(s))
|
|
168
|
+
}
|
|
111
169
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
170
|
+
/**
|
|
171
|
+
* @param {Point} p
|
|
172
|
+
* @returns {Point} this + p
|
|
173
|
+
*/
|
|
174
|
+
Point.prototype.add = function add (p) {
|
|
175
|
+
return wrap(this._p.add(p._p))
|
|
176
|
+
}
|
|
118
177
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
178
|
+
/**
|
|
179
|
+
* @returns {Point} k1*this + k2*p2 (used by ECDSA verification).
|
|
180
|
+
*/
|
|
181
|
+
Point.prototype.mulAdd = function mulAdd (k1, p2, k2) {
|
|
182
|
+
return this.mul(k1).add(p2.mul(k2))
|
|
183
|
+
}
|
|
122
184
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
185
|
+
/**
|
|
186
|
+
* @param {Point} p
|
|
187
|
+
* @returns {boolean}
|
|
188
|
+
*/
|
|
189
|
+
Point.prototype.eq = function eq (p) {
|
|
190
|
+
return this._p.equals(p._p)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* @returns {boolean}
|
|
195
|
+
*/
|
|
196
|
+
Point.prototype.isInfinity = function isInfinity () {
|
|
197
|
+
return this._p.is0()
|
|
198
|
+
}
|
|
127
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Will determine if the point is valid.
|
|
202
|
+
* @returns {Point} this
|
|
203
|
+
*/
|
|
204
|
+
Point.prototype.validate = function validate () {
|
|
205
|
+
if (this.isInfinity()) {
|
|
206
|
+
throw new Error('Point cannot be equal to Infinity')
|
|
207
|
+
}
|
|
208
|
+
var a = this._p.toAffine()
|
|
209
|
+
checkOnCurve(a.x, a.y)
|
|
210
|
+
// secp256k1 has cofactor 1, so on-curve implies order n; the old explicit
|
|
211
|
+
// n*P == infinity check is therefore redundant.
|
|
128
212
|
return this
|
|
129
213
|
}
|
|
130
214
|
|
|
131
215
|
/**
|
|
132
|
-
* A "compressed" format point
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
* computation to rederive the full point.
|
|
136
|
-
*
|
|
137
|
-
* @param {Point} point An instance of Point.
|
|
138
|
-
* @returns {Buffer} A compressed point in the form of a buffer.
|
|
216
|
+
* A "compressed" format point: the X coordinate plus a parity byte.
|
|
217
|
+
* @param {Point} point
|
|
218
|
+
* @returns {Buffer}
|
|
139
219
|
*/
|
|
140
220
|
Point.pointToCompressed = function pointToCompressed (point) {
|
|
141
221
|
var xbuf = point.getX().toBuffer({ size: 32 })
|
|
@@ -152,17 +232,15 @@ Point.pointToCompressed = function pointToCompressed (point) {
|
|
|
152
232
|
}
|
|
153
233
|
|
|
154
234
|
/**
|
|
155
|
-
* Converts a compressed buffer into a point.
|
|
156
|
-
*
|
|
157
235
|
* @param {Buffer} buf A compressed point.
|
|
158
|
-
* @returns {Point}
|
|
236
|
+
* @returns {Point}
|
|
159
237
|
*/
|
|
160
238
|
Point.pointFromCompressed = function (buf) {
|
|
161
239
|
if (buf.length !== 33) {
|
|
162
240
|
throw new Error('invalid buffer length')
|
|
163
241
|
}
|
|
164
|
-
|
|
165
|
-
|
|
242
|
+
var prefix = buf[0]
|
|
243
|
+
var odd
|
|
166
244
|
if (prefix === 0x03) {
|
|
167
245
|
odd = true
|
|
168
246
|
} else if (prefix === 0x02) {
|
|
@@ -171,45 +249,28 @@ Point.pointFromCompressed = function (buf) {
|
|
|
171
249
|
throw new Error('invalid value of compressed prefix')
|
|
172
250
|
}
|
|
173
251
|
|
|
174
|
-
|
|
175
|
-
let x = BN.fromBuffer(xbuf)
|
|
252
|
+
var x = BN.fromBuffer(buf.slice(1, 33))
|
|
176
253
|
return Point.fromX(odd, x)
|
|
177
254
|
}
|
|
178
255
|
|
|
179
256
|
/**
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
* @returns {Buffer} A compressed point.
|
|
257
|
+
* @returns {Buffer} this point as a compressed buffer.
|
|
183
258
|
*/
|
|
184
259
|
Point.prototype.toBuffer = function () {
|
|
185
260
|
return Point.pointToCompressed(this)
|
|
186
261
|
}
|
|
187
262
|
|
|
188
263
|
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
* @returns {string} A compressed point as a hex string.
|
|
264
|
+
* @returns {string} this point as a compressed hex string.
|
|
192
265
|
*/
|
|
193
266
|
Point.prototype.toHex = function () {
|
|
194
267
|
return this.toBuffer().toString('hex')
|
|
195
268
|
}
|
|
196
269
|
|
|
197
|
-
/**
|
|
198
|
-
* Converts a compressed buffer into a point.
|
|
199
|
-
*
|
|
200
|
-
* @param {Buffer} buf A compressed point.
|
|
201
|
-
* @returns {Point} A Point.
|
|
202
|
-
*/
|
|
203
270
|
Point.fromBuffer = function (buf) {
|
|
204
271
|
return Point.pointFromCompressed(buf)
|
|
205
272
|
}
|
|
206
273
|
|
|
207
|
-
/**
|
|
208
|
-
* Converts a compressed buffer into a point.
|
|
209
|
-
*
|
|
210
|
-
* @param {Buffer} hex A compressed point as a hex string.
|
|
211
|
-
* @returns {Point} A Point.
|
|
212
|
-
*/
|
|
213
274
|
Point.fromHex = function (hex) {
|
|
214
275
|
return Point.fromBuffer(Buffer.from(hex, 'hex'))
|
|
215
276
|
}
|
|
@@ -1,25 +1,38 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// Bitcore ECIES, backed by the audited @noble suite (secp256k1 ECDH, SHA-2,
|
|
4
|
+
// HMAC, AES-CBC) instead of the elliptic/aes-js primitives. The wire format is
|
|
5
|
+
// unchanged and byte-compatible with prior versions (locked by the golden
|
|
6
|
+
// known-answer vector in test/ecies/bitcore-ecies.js). The scheme is unchanged:
|
|
7
|
+
//
|
|
8
|
+
// S = X coord of (senderPriv * recipientPub) (ECDH, secp256k1)
|
|
9
|
+
// kE||kM = SHA-512(S) (KDF)
|
|
10
|
+
// c = IV || AES-256-CBC_PKCS7(kE, IV, message)
|
|
11
|
+
// d = HMAC-SHA256(kM, c) (encrypt-then-MAC)
|
|
12
|
+
// out = [Rpub] || c || d
|
|
13
|
+
//
|
|
14
|
+
// Public API is identical to bitcore-ecies so it is a drop-in: it accepts the
|
|
15
|
+
// same bsv PrivateKey/PublicKey objects. Only key *decoding* touches bsv; all
|
|
16
|
+
// cryptography is @noble.
|
|
17
|
+
|
|
18
|
+
var { secp256k1 } = require('@noble/curves/secp256k1.js')
|
|
19
|
+
var { sha512, sha256 } = require('@noble/hashes/sha2.js')
|
|
20
|
+
var { hmac } = require('@noble/hashes/hmac.js')
|
|
21
|
+
var { cbc } = require('@noble/ciphers/aes.js')
|
|
4
22
|
|
|
23
|
+
var bsv = require('../../')
|
|
5
24
|
var PublicKey = bsv.PublicKey
|
|
6
|
-
var Hash = bsv.crypto.Hash
|
|
7
25
|
var $ = bsv.util.preconditions
|
|
8
|
-
var aesjs = require('aes-js')
|
|
9
|
-
var CBC = aesjs.ModeOfOperation.cbc
|
|
10
26
|
var Random = bsv.crypto.Random
|
|
11
27
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
28
|
+
function buf (u8) { return Buffer.from(u8.buffer, u8.byteOffset, u8.byteLength) }
|
|
29
|
+
function hmacSha256 (key, data) { return buf(hmac(sha256, key, data)) }
|
|
30
|
+
|
|
31
|
+
// Constant-time buffer comparison (see bitcore-ecies for rationale).
|
|
15
32
|
function constantTimeEqual (a, b) {
|
|
16
|
-
if (a.length !== b.length)
|
|
17
|
-
return false
|
|
18
|
-
}
|
|
33
|
+
if (a.length !== b.length) return false
|
|
19
34
|
var diff = 0
|
|
20
|
-
for (var i = 0; i < a.length; i++)
|
|
21
|
-
diff |= a[i] ^ b[i]
|
|
22
|
-
}
|
|
35
|
+
for (var i = 0; i < a.length; i++) diff |= a[i] ^ b[i]
|
|
23
36
|
return diff === 0
|
|
24
37
|
}
|
|
25
38
|
|
|
@@ -30,11 +43,8 @@ AESCBC.encrypt = function (messagebuf, cipherkeybuf, ivbuf) {
|
|
|
30
43
|
$.checkArgument(cipherkeybuf)
|
|
31
44
|
$.checkArgument(ivbuf)
|
|
32
45
|
ivbuf = ivbuf || Random.getRandomBuffer(128 / 8)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
var ctbuf = aesCbc.encrypt(messagebuf)
|
|
36
|
-
var encbuf = Buffer.concat([ivbuf, ctbuf])
|
|
37
|
-
return encbuf
|
|
46
|
+
var ct = cbc(Uint8Array.from(cipherkeybuf), Uint8Array.from(ivbuf)).encrypt(Uint8Array.from(messagebuf))
|
|
47
|
+
return Buffer.concat([Buffer.from(ivbuf), buf(ct)])
|
|
38
48
|
}
|
|
39
49
|
|
|
40
50
|
AESCBC.decrypt = function (encbuf, cipherkeybuf) {
|
|
@@ -42,33 +52,24 @@ AESCBC.decrypt = function (encbuf, cipherkeybuf) {
|
|
|
42
52
|
$.checkArgument(cipherkeybuf)
|
|
43
53
|
var ivbuf = encbuf.slice(0, 128 / 8)
|
|
44
54
|
var ctbuf = encbuf.slice(128 / 8)
|
|
45
|
-
var
|
|
46
|
-
|
|
47
|
-
messagebuf = aesjs.padding.pkcs7.strip(messagebuf)
|
|
48
|
-
return Buffer.from(messagebuf)
|
|
55
|
+
var msg = cbc(Uint8Array.from(cipherkeybuf), Uint8Array.from(ivbuf)).decrypt(Uint8Array.from(ctbuf))
|
|
56
|
+
return buf(msg)
|
|
49
57
|
}
|
|
50
58
|
|
|
51
|
-
// http://en.wikipedia.org/wiki/Integrated_Encryption_Scheme
|
|
52
59
|
var ECIES = function ECIES (opts) {
|
|
53
|
-
if (!(this instanceof ECIES))
|
|
54
|
-
return new ECIES()
|
|
55
|
-
}
|
|
60
|
+
if (!(this instanceof ECIES)) return new ECIES()
|
|
56
61
|
this.opts = opts || {}
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
ECIES.prototype.privateKey = function (privateKey) {
|
|
60
65
|
$.checkArgument(privateKey, 'no private key provided')
|
|
61
|
-
|
|
62
66
|
this._privateKey = privateKey || null
|
|
63
|
-
|
|
64
67
|
return this
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
ECIES.prototype.publicKey = function (publicKey) {
|
|
68
71
|
$.checkArgument(publicKey, 'no public key provided')
|
|
69
|
-
|
|
70
72
|
this._publicKey = publicKey || null
|
|
71
|
-
|
|
72
73
|
return this
|
|
73
74
|
}
|
|
74
75
|
|
|
@@ -79,79 +80,53 @@ var cachedProperty = function (name, getter) {
|
|
|
79
80
|
enumerable: true,
|
|
80
81
|
get: function () {
|
|
81
82
|
var value = this[cachedName]
|
|
82
|
-
if (!value)
|
|
83
|
-
value = this[cachedName] = getter.apply(this)
|
|
84
|
-
}
|
|
83
|
+
if (!value) value = this[cachedName] = getter.apply(this)
|
|
85
84
|
return value
|
|
86
85
|
}
|
|
87
86
|
})
|
|
88
87
|
}
|
|
89
88
|
|
|
89
|
+
// Sender's compressed public key (R), via @noble from the raw private scalar.
|
|
90
90
|
cachedProperty('Rbuf', function () {
|
|
91
|
-
|
|
91
|
+
var priv = this._privateKey.bn.toBuffer({ size: 32 })
|
|
92
|
+
return buf(secp256k1.getPublicKey(Uint8Array.from(priv), true))
|
|
92
93
|
})
|
|
93
94
|
|
|
95
|
+
// ECDH + KDF: S = X(priv * pub); kE||kM = SHA-512(S).
|
|
94
96
|
cachedProperty('kEkM', function () {
|
|
95
|
-
var
|
|
96
|
-
var
|
|
97
|
-
var
|
|
98
|
-
var
|
|
99
|
-
|
|
100
|
-
return Hash.sha512(Sbuf)
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
cachedProperty('kE', function () {
|
|
104
|
-
return this.kEkM.slice(0, 32)
|
|
97
|
+
var priv = this._privateKey.bn.toBuffer({ size: 32 })
|
|
98
|
+
var pub = this._publicKey.toDER() // SEC-encoded; @noble accepts either form
|
|
99
|
+
var shared = secp256k1.getSharedSecret(Uint8Array.from(priv), Uint8Array.from(pub), true)
|
|
100
|
+
var Sbuf = shared.slice(1) // drop the 02/03 prefix -> 32-byte X coordinate
|
|
101
|
+
return buf(sha512(Sbuf))
|
|
105
102
|
})
|
|
106
103
|
|
|
107
|
-
cachedProperty('
|
|
108
|
-
|
|
109
|
-
})
|
|
104
|
+
cachedProperty('kE', function () { return this.kEkM.slice(0, 32) })
|
|
105
|
+
cachedProperty('kM', function () { return this.kEkM.slice(32, 64) })
|
|
110
106
|
|
|
111
|
-
// Encrypts the message (String or Buffer).
|
|
112
|
-
// Optional `ivbuf` contains 16-byte Buffer to be used in AES-CBC.
|
|
113
|
-
// By default, `ivbuf` is computed deterministically from message and private key using HMAC-SHA256.
|
|
114
|
-
// Deterministic IV enables end-to-end test vectors for alternative implementations.
|
|
115
|
-
// Note that identical messages have identical ciphertexts. If your protocol does not include some
|
|
116
|
-
// kind of a sequence identifier inside the message *and* it is important to not allow attacker to learn
|
|
117
|
-
// that message is repeated, then you should use custom IV.
|
|
118
|
-
// For random IV, pass `Random.getRandomBuffer(16)` for the second argument.
|
|
119
107
|
ECIES.prototype.encrypt = function (message, ivbuf) {
|
|
120
108
|
if (!Buffer.isBuffer(message)) message = Buffer.from(message)
|
|
121
109
|
if (ivbuf === undefined) {
|
|
122
|
-
ivbuf =
|
|
110
|
+
ivbuf = hmacSha256(this._privateKey.toBuffer(), message).slice(0, 16)
|
|
123
111
|
}
|
|
124
112
|
var c = AESCBC.encrypt(message, this.kE, ivbuf)
|
|
125
|
-
var d =
|
|
113
|
+
var d = hmacSha256(this.kM, c)
|
|
126
114
|
if (this.opts.shortTag) d = d.slice(0, 4)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
encbuf = Buffer.concat([c, d])
|
|
130
|
-
} else {
|
|
131
|
-
encbuf = Buffer.concat([this.Rbuf, c, d])
|
|
132
|
-
}
|
|
133
|
-
return encbuf
|
|
115
|
+
if (this.opts.noKey) return Buffer.concat([c, d])
|
|
116
|
+
return Buffer.concat([this.Rbuf, c, d])
|
|
134
117
|
}
|
|
135
118
|
|
|
136
119
|
ECIES.prototype.decrypt = function (encbuf) {
|
|
137
120
|
$.checkArgument(encbuf)
|
|
138
121
|
var offset = 0
|
|
139
|
-
var tagLength = 32
|
|
140
|
-
if (this.opts.shortTag) {
|
|
141
|
-
tagLength = 4
|
|
142
|
-
}
|
|
122
|
+
var tagLength = this.opts.shortTag ? 4 : 32
|
|
143
123
|
if (!this.opts.noKey) {
|
|
144
124
|
var pub
|
|
145
125
|
switch (encbuf[0]) {
|
|
146
|
-
case 4:
|
|
147
|
-
pub = encbuf.slice(0, 65)
|
|
148
|
-
break
|
|
126
|
+
case 4: pub = encbuf.slice(0, 65); break
|
|
149
127
|
case 3:
|
|
150
|
-
case 2:
|
|
151
|
-
|
|
152
|
-
break
|
|
153
|
-
default:
|
|
154
|
-
throw new Error('Invalid type: ' + encbuf[0])
|
|
128
|
+
case 2: pub = encbuf.slice(0, 33); break
|
|
129
|
+
default: throw new Error('Invalid type: ' + encbuf[0])
|
|
155
130
|
}
|
|
156
131
|
this._publicKey = PublicKey.fromDER(pub)
|
|
157
132
|
offset += pub.length
|
|
@@ -160,13 +135,9 @@ ECIES.prototype.decrypt = function (encbuf) {
|
|
|
160
135
|
var c = encbuf.slice(offset, encbuf.length - tagLength)
|
|
161
136
|
var d = encbuf.slice(encbuf.length - tagLength, encbuf.length)
|
|
162
137
|
|
|
163
|
-
var d2 =
|
|
138
|
+
var d2 = hmacSha256(this.kM, c)
|
|
164
139
|
if (this.opts.shortTag) d2 = d2.slice(0, 4)
|
|
165
|
-
|
|
166
|
-
// Constant-time MAC comparison (encrypt-then-MAC verification).
|
|
167
|
-
if (!constantTimeEqual(d, d2)) {
|
|
168
|
-
throw new Error('Invalid checksum')
|
|
169
|
-
}
|
|
140
|
+
if (!constantTimeEqual(d, d2)) throw new Error('Invalid checksum')
|
|
170
141
|
|
|
171
142
|
return AESCBC.decrypt(c, this.kE)
|
|
172
143
|
}
|