@smartledger/bsv 5.1.0 → 5.2.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.
@@ -1,15 +1,24 @@
1
1
  'use strict'
2
2
 
3
+ // Electrum BIE1 ECIES, backed by the audited @noble suite. Wire format unchanged
4
+ // and byte-compatible with prior versions (locked by the golden known-answer
5
+ // vector in test/ecies/electrum-ecies.js).
6
+
3
7
  var bsv = require('../../')
4
8
 
5
9
  var PublicKey = bsv.PublicKey
6
10
  var PrivateKey = bsv.PrivateKey
7
- var Hash = bsv.crypto.Hash
8
11
  var $ = bsv.util.preconditions
9
12
  var bitcoreECIES = require('./bitcore-ecies')
10
13
  var errors = require('./errors')
11
- var aesjs = require('aes-js')
12
- var CBC = aesjs.ModeOfOperation.cbc
14
+
15
+ var { secp256k1 } = require('@noble/curves/secp256k1.js')
16
+ var { sha512, sha256 } = require('@noble/hashes/sha2.js')
17
+ var { hmac } = require('@noble/hashes/hmac.js')
18
+ var { cbc } = require('@noble/ciphers/aes.js')
19
+
20
+ function buf (u8) { return Buffer.from(u8.buffer, u8.byteOffset, u8.byteLength) }
21
+ function sha256hmac (data, key) { return buf(hmac(sha256, key, data)) }
13
22
 
14
23
  var AESCBC = function AESCBC () {}
15
24
 
@@ -19,10 +28,7 @@ AESCBC.encrypt = function (messagebuf, keybuf, ivbuf) {
19
28
  $.checkArgument(ivbuf)
20
29
  $.checkArgument(keybuf.length === 16, 'keybuf length must be 16')
21
30
  $.checkArgument(ivbuf.length === 16, 'ivbuf length must be 16')
22
- messagebuf = aesjs.padding.pkcs7.pad(messagebuf)
23
- var aesCbc = new CBC(keybuf, ivbuf)
24
- var encryptedBytes = aesCbc.encrypt(messagebuf)
25
- return Buffer.from(encryptedBytes)
31
+ return buf(cbc(Uint8Array.from(keybuf), Uint8Array.from(ivbuf)).encrypt(Uint8Array.from(messagebuf)))
26
32
  }
27
33
 
28
34
  AESCBC.decrypt = function (encbuf, keybuf, ivbuf) {
@@ -31,35 +37,14 @@ AESCBC.decrypt = function (encbuf, keybuf, ivbuf) {
31
37
  $.checkArgument(ivbuf)
32
38
  $.checkArgument(keybuf.length === 16, 'keybuf length must be 16')
33
39
  $.checkArgument(ivbuf.length === 16, 'ivbuf length must be 16')
34
- var aesCbc = new CBC(keybuf, ivbuf)
35
- var decryptedBytes = aesCbc.decrypt(encbuf)
36
- return Buffer.from(aesjs.padding.pkcs7.strip(decryptedBytes))
40
+ return buf(cbc(Uint8Array.from(keybuf), Uint8Array.from(ivbuf)).decrypt(Uint8Array.from(encbuf)))
37
41
  }
38
42
 
39
- // Electrum BIE1 ECIES
40
- // Difference from Original Bitcore ECIES:
41
- // BIE1:
42
- // 1.Secret S is compressed P(33 bytes), while Bitcore's Secret is Px(32 bytes). See ivkEkM.
43
- // 2.use AES-128-CBC instead of AES-256-CBC.
44
- // 3.IV is retrived from first 16 bytes of Hashed Secret Buffer. See iv
45
- // 4.key Encryption(kE) is retrived from 17-32 bytes of Hashed Secret Buffer, instead of 1-32 bytes. See kE
46
- // 5.a magic word 'BIE1' is used to identify message type.
47
- // 6.ephemeral key is introduced, so you can encrypt messages only with public key.
48
- // 7.HMAC is whole message.
49
- // Notice:
50
- // Electrum does not support shortTag nor noKey
51
- // Do NOT use those 2 options if you are to send a message to Electrum
52
- // Encrypted message is NOT recoverable if you use ephemeral key
53
- // Security:
54
- //
55
-
56
- // Default algorithm is set to BIE1, however original Bitcore ECIES is still preserved.
57
43
  var ECIES = function ECIES (opts, algorithm = 'BIE1') {
58
44
  if (algorithm !== 'BIE1') throw new errors.UnsupportAlgorithm(algorithm)
59
45
  if (!(this instanceof ECIES)) {
60
46
  return new ECIES(opts, algorithm)
61
47
  }
62
- // use ephemeral key if privateKey is not set.
63
48
  this._privateKey = new bsv.PrivateKey()
64
49
  this.opts = opts || {}
65
50
  this.opts.ephemeralKey = true
@@ -67,19 +52,15 @@ var ECIES = function ECIES (opts, algorithm = 'BIE1') {
67
52
 
68
53
  ECIES.prototype.privateKey = function (privateKey) {
69
54
  $.checkArgument(PrivateKey.isValid(privateKey), 'no private key provided')
70
-
71
55
  this._privateKey = PrivateKey(privateKey.toHex()) || null
72
56
  this.opts.ephemeralKey = false
73
-
74
57
  return this
75
58
  }
76
59
 
77
60
  ECIES.prototype.publicKey = function (publicKey) {
78
61
  $.checkArgument(PublicKey.isValid(publicKey), 'no public key provided')
79
-
80
62
  this._publicKey = PublicKey(publicKey.toString()) || null
81
63
  if (this._publicKey != null) this.opts.fixedPublicKey = true
82
-
83
64
  return this
84
65
  }
85
66
 
@@ -97,31 +78,21 @@ var defineProperty = function (name, getter) {
97
78
  }
98
79
 
99
80
  defineProperty('Rbuf', function () {
100
- return this._privateKey.publicKey.toDER(true)
81
+ var priv = this._privateKey.bn.toBuffer({ size: 32 })
82
+ return buf(secp256k1.getPublicKey(Uint8Array.from(priv), true))
101
83
  })
102
84
 
103
85
  defineProperty('ivkEkM', function () {
104
- var r = this._privateKey.bn
105
- var KB = this._publicKey.point
106
- var P = KB.mul(r)
107
- var S = PublicKey(P)
108
- var Sbuf = S.toBuffer()
109
- return Hash.sha512(Sbuf)
86
+ var priv = this._privateKey.bn.toBuffer({ size: 32 })
87
+ var pub = this._publicKey.toDER()
88
+ var Sbuf = secp256k1.getSharedSecret(Uint8Array.from(priv), Uint8Array.from(pub), true)
89
+ return buf(sha512(Sbuf))
110
90
  })
111
91
 
112
- defineProperty('iv', function () {
113
- return this.ivkEkM.slice(0, 16)
114
- })
115
-
116
- defineProperty('kE', function () {
117
- return this.ivkEkM.slice(16, 32)
118
- })
92
+ defineProperty('iv', function () { return this.ivkEkM.slice(0, 16) })
93
+ defineProperty('kE', function () { return this.ivkEkM.slice(16, 32) })
94
+ defineProperty('kM', function () { return this.ivkEkM.slice(32, 64) })
119
95
 
120
- defineProperty('kM', function () {
121
- return this.ivkEkM.slice(32, 64)
122
- })
123
-
124
- // Encrypts the message (String or Buffer).
125
96
  ECIES.prototype.encrypt = function (message) {
126
97
  if (!Buffer.isBuffer(message)) message = Buffer.from(message)
127
98
  var ciphertext = AESCBC.encrypt(message, this.kE, this.iv)
@@ -132,48 +103,37 @@ ECIES.prototype.encrypt = function (message) {
132
103
  } else {
133
104
  encbuf = Buffer.concat([BIE1, this.Rbuf, ciphertext])
134
105
  }
135
- var hmac = Hash.sha256hmac(encbuf, this.kM)
136
- if (this.opts.shortTag) hmac = hmac.slice(0, 4)
137
- return Buffer.concat([encbuf, hmac])
106
+ var tag = sha256hmac(encbuf, this.kM)
107
+ if (this.opts.shortTag) tag = tag.slice(0, 4)
108
+ return Buffer.concat([encbuf, tag])
138
109
  }
139
110
 
140
111
  ECIES.prototype.decrypt = function (encbuf) {
141
112
  $.checkArgument(Buffer.isBuffer(encbuf), 'ciphetext must be a buffer')
142
- var tagLength = 32
113
+ var tagLength = this.opts.shortTag ? 4 : 32
143
114
  var offset = 4
144
- if (this.opts.shortTag) {
145
- tagLength = 4
146
- }
147
115
  var magic = encbuf.slice(0, 4)
148
116
  if (!magic.equals(Buffer.from('BIE1'))) {
149
117
  throw new errors.DecryptionError('Invalid Magic')
150
118
  }
151
119
  if (!this.opts.noKey) {
152
- var pub
153
- // BIE1 use compressed public key, length is always 33.
154
- pub = encbuf.slice(4, 37)
120
+ var pub = encbuf.slice(4, 37)
155
121
  if (this.opts.fixedPublicKey) console.log('Notice: Overriding PublicKey in message. Consider use "noKey" option if you are not sending message to electrum and do not want to use ephemeral key')
156
122
  else this._publicKey = PublicKey.fromDER(pub)
157
123
  offset = 37
158
124
  }
159
125
 
160
126
  var ciphertext = encbuf.slice(offset, encbuf.length - tagLength)
161
- var hmac = encbuf.slice(encbuf.length - tagLength, encbuf.length)
127
+ var tag = encbuf.slice(encbuf.length - tagLength, encbuf.length)
162
128
 
163
- var hmac2 = Hash.sha256hmac(encbuf.slice(0, encbuf.length - tagLength), this.kM)
164
- if (this.opts.shortTag) hmac2 = hmac2.slice(0, 4)
129
+ var tag2 = sha256hmac(encbuf.slice(0, encbuf.length - tagLength), this.kM)
130
+ if (this.opts.shortTag) tag2 = tag2.slice(0, 4)
165
131
 
166
- // Constant-time tag comparison. Buffer.equals() short-circuits on the first
167
- // differing byte, which leaks how many leading bytes matched and enables a
168
- // forgery oracle on the MAC; compare every byte unconditionally instead
169
- // (mirrors the loop in bitcore-ecies.js).
170
- if (hmac.length !== hmac2.length) {
132
+ if (tag.length !== tag2.length) {
171
133
  throw new errors.DecryptionError('Invalid checksum')
172
134
  }
173
135
  var equal = 0
174
- for (var i = 0; i < hmac2.length; i++) {
175
- equal |= (hmac[i] ^ hmac2[i])
176
- }
136
+ for (var i = 0; i < tag2.length; i++) equal |= (tag[i] ^ tag2[i])
177
137
  if (equal !== 0) {
178
138
  throw new errors.DecryptionError('Invalid checksum')
179
139
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartledger/bsv",
3
- "version": "5.1.0",
3
+ "version": "5.2.0",
4
4
  "description": "🚀 Complete Bitcoin SV development framework with legally-recognizable DID:web + W3C VC-JWT toolkit, Legal Token Protocol (LTP), Global Digital Attestation Framework (GDAF), StatusList2021 revocation, and 16 flexible loading options. Standards-based credentials with ES256/ES256K support, on-chain BSV anchoring, and comprehensive Bitcoin SV API. Perfect for legal tokens, verifiable credentials, DeFi, smart contracts, and secure Bitcoin applications.",
5
5
  "author": "SmartLedger Technology <hello@smartledger.technology> (https://smartledger.technology)",
6
6
  "homepage": "https://github.com/codenlighten/smartledger-bsv#readme",
@@ -191,6 +191,9 @@
191
191
  "request": "browser-request"
192
192
  },
193
193
  "dependencies": {
194
+ "@noble/ciphers": "2.2.0",
195
+ "@noble/curves": "2.2.0",
196
+ "@noble/hashes": "2.2.0",
194
197
  "aes-js": "^3.1.2",
195
198
  "bn.js": "=4.11.9",
196
199
  "bs58": "=4.0.1",