|
| 1 | +const secp256k1 = require('@noble/secp256k1') |
| 2 | +const { sha256 } = require('@noble/hashes/sha2') |
| 3 | +const { hmac } = require('@noble/hashes/hmac') |
| 4 | + |
| 5 | +if (!secp256k1.utils.hmacSha256Sync) { |
| 6 | + secp256k1.utils.hmacSha256Sync = (key, ...msgs) => hmac(sha256, key, secp256k1.utils.concatBytes(...msgs)) |
| 7 | +} |
| 8 | +if (!secp256k1.utils.sha256Sync) { |
| 9 | + secp256k1.utils.sha256Sync = (...msgs) => sha256(secp256k1.utils.concatBytes(...msgs)) |
| 10 | +} |
| 11 | + |
| 12 | +function writePublicKey(output, point) { |
| 13 | + const buf = point.toRawBytes(output.length === 33) |
| 14 | + if (output.length !== buf.length) return 1 |
| 15 | + output.set(buf) |
| 16 | + return 0 |
| 17 | +} |
| 18 | + |
| 19 | +function toBig(arr) { |
| 20 | + // args already typechecked in ./lib/index.js |
| 21 | + return BigInt('0x' + secp256k1.utils.bytesToHex(arr)) |
| 22 | +} |
| 23 | + |
| 24 | +const _0n = BigInt(0) |
| 25 | +const _1n = BigInt(1) |
| 26 | + |
| 27 | +let elliptic // used for signing with nonce function and/or non-32 byte extra entropy data |
| 28 | + |
| 29 | +module.exports = { |
| 30 | + contextRandomize () { |
| 31 | + return 0 |
| 32 | + }, |
| 33 | + |
| 34 | + privateKeyVerify (seckey) { |
| 35 | + return secp256k1.utils.isValidPrivateKey(seckey) ? 0 : 1 |
| 36 | + }, |
| 37 | + |
| 38 | + // Validation matches ./elliptic.js |
| 39 | + // Doesn't fail on out of bounds values, normalize them |
| 40 | + privateKeyNegate (seckey) { |
| 41 | + const res = secp256k1.utils.mod(secp256k1.CURVE.n - toBig(seckey), secp256k1.CURVE.n) |
| 42 | + |
| 43 | + const buf = secp256k1.utils._bigintTo32Bytes(res) |
| 44 | + seckey.set(buf) |
| 45 | + |
| 46 | + return 0 |
| 47 | + }, |
| 48 | + |
| 49 | + // Validation matches ./elliptic.js |
| 50 | + privateKeyTweakAdd (seckey, tweak) { |
| 51 | + let res = toBig(tweak) |
| 52 | + if (res >= secp256k1.CURVE.n) return 1 |
| 53 | + |
| 54 | + res = secp256k1.utils.mod(res + toBig(seckey), secp256k1.CURVE.n) |
| 55 | + if (res === _0n) return 1 |
| 56 | + |
| 57 | + const buf = secp256k1.utils._bigintTo32Bytes(res) |
| 58 | + seckey.set(buf) |
| 59 | + |
| 60 | + return 0 |
| 61 | + }, |
| 62 | + |
| 63 | + // Validation matches ./elliptic.js |
| 64 | + privateKeyTweakMul (seckey, tweak) { |
| 65 | + let res = toBig(tweak) |
| 66 | + if (res >= secp256k1.CURVE.n || res === 0n) return 1 |
| 67 | + |
| 68 | + res = secp256k1.utils.mod(res * toBig(seckey), secp256k1.CURVE.n) |
| 69 | + |
| 70 | + const buf = secp256k1.utils._bigintTo32Bytes(res) |
| 71 | + seckey.set(buf) |
| 72 | + |
| 73 | + return 0 |
| 74 | + }, |
| 75 | + |
| 76 | + publicKeyVerify (pubkey) { |
| 77 | + try { |
| 78 | + return secp256k1.Point.fromHex(pubkey) ? 0 : 1 |
| 79 | + } catch (err) { |
| 80 | + return 1 |
| 81 | + } |
| 82 | + }, |
| 83 | + |
| 84 | + publicKeyCreate (output, seckey) { |
| 85 | + try { |
| 86 | + const publicKey = secp256k1.getPublicKey(seckey, output.length === 33) |
| 87 | + if (output.length !== publicKey.length) return 1 |
| 88 | + output.set(publicKey) |
| 89 | + return 0 |
| 90 | + } catch (err) { |
| 91 | + return 1 |
| 92 | + } |
| 93 | + }, |
| 94 | + |
| 95 | + publicKeyConvert (output, pubkey) { |
| 96 | + try { |
| 97 | + const publicKey = secp256k1.Point.fromHex(pubkey).toRawBytes(output.length === 33) |
| 98 | + if (output.length !== publicKey.length) return 1 |
| 99 | + output.set(publicKey) |
| 100 | + return 0 |
| 101 | + } catch (err) { |
| 102 | + return 1 |
| 103 | + } |
| 104 | + }, |
| 105 | + |
| 106 | + publicKeyNegate (output, pubkey) { |
| 107 | + let P |
| 108 | + try { |
| 109 | + P = secp256k1.Point.fromHex(pubkey) |
| 110 | + } catch (err) { |
| 111 | + return 1 |
| 112 | + } |
| 113 | + |
| 114 | + const point = P.negate() |
| 115 | + return writePublicKey(output, point) |
| 116 | + }, |
| 117 | + |
| 118 | + publicKeyCombine (output, pubkeys) { |
| 119 | + const points = new Array(pubkeys.length) |
| 120 | + for (let i = 0; i < pubkeys.length; ++i) { |
| 121 | + try { |
| 122 | + points[i] = secp256k1.Point.fromHex(pubkeys[i]) |
| 123 | + } catch (err) { |
| 124 | + return 1 |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | + let point = points[0] |
| 129 | + for (let i = 1; i < points.length; ++i) point = point.add(points[i]) |
| 130 | + if (point.equals(secp256k1.Point.ZERO)) return 2 |
| 131 | + return writePublicKey(output, point) |
| 132 | + }, |
| 133 | + |
| 134 | + publicKeyTweakAdd (output, pubkey, tweak) { |
| 135 | + let P |
| 136 | + try { |
| 137 | + P = secp256k1.Point.fromHex(pubkey) |
| 138 | + } catch (err) { |
| 139 | + return 1 |
| 140 | + } |
| 141 | + |
| 142 | + tweak = toBig(tweak) |
| 143 | + if (tweak >= secp256k1.CURVE.n) return 2 |
| 144 | + |
| 145 | + // returns a non-zero point or undefined |
| 146 | + const point = secp256k1.Point.BASE.multiplyAndAddUnsafe(P, tweak, _1n) // timing-unsafe, ok here |
| 147 | + if (!point) return 2 // returns undefined on ZERO |
| 148 | + return writePublicKey(output, point) |
| 149 | + }, |
| 150 | + |
| 151 | + publicKeyTweakMul (output, pubkey, tweak) { |
| 152 | + let P |
| 153 | + try { |
| 154 | + P = secp256k1.Point.fromHex(pubkey) |
| 155 | + } catch (err) { |
| 156 | + return 1 |
| 157 | + } |
| 158 | + |
| 159 | + tweak = toBig(tweak) |
| 160 | + if (tweak >= secp256k1.CURVE.n || tweak === _0n) return 2 |
| 161 | + |
| 162 | + const point = P.multiply(tweak) |
| 163 | + if (point.equals(secp256k1.Point.ZERO)) return 2 |
| 164 | + return writePublicKey(output, point) |
| 165 | + }, |
| 166 | + |
| 167 | + signatureNormalize (sig) { |
| 168 | + try { |
| 169 | + const signature = secp256k1.Signature.fromCompact(sig) |
| 170 | + if (signature.hasHighS()) { |
| 171 | + const normal = signature.normalizeS().toCompactRawBytes() |
| 172 | + sig.set(normal.subarray(32), 32) |
| 173 | + } |
| 174 | + } catch (err) { |
| 175 | + return 1 |
| 176 | + } |
| 177 | + |
| 178 | + return 0 |
| 179 | + }, |
| 180 | + |
| 181 | + signatureExport (obj, sig) { |
| 182 | + let der |
| 183 | + try { |
| 184 | + der = secp256k1.Signature.fromCompact(sig).toDERRawBytes() |
| 185 | + } catch (err) { |
| 186 | + return 1 |
| 187 | + } |
| 188 | + |
| 189 | + if (obj.output.length < der.length) return 1 |
| 190 | + |
| 191 | + obj.output.set(der) |
| 192 | + obj.outputlen = der.length |
| 193 | + return 0 |
| 194 | + }, |
| 195 | + |
| 196 | + signatureImport (output, sig) { |
| 197 | + try { |
| 198 | + buf = secp256k1.Signature.fromDER(sig).toCompactRawBytes() |
| 199 | + } catch (err) { |
| 200 | + return 1 |
| 201 | + } |
| 202 | + |
| 203 | + if (output.length !== buf.length) return 1 |
| 204 | + output.set(buf) |
| 205 | + return 0 |
| 206 | + }, |
| 207 | + |
| 208 | + ecdsaSign (obj, message, seckey, data, noncefn) { |
| 209 | + if (noncefn || data && data.length !== 32) { |
| 210 | + // Can we deprecate noncefn & drop it in next major? Also non-32 byte data |
| 211 | + if (!elliptic) elliptic = require('./elliptic.js') |
| 212 | + return elliptic.ecdsaSign(obj, message, seckey, data, noncefn) |
| 213 | + } |
| 214 | + |
| 215 | + let sig |
| 216 | + try { |
| 217 | + sig = secp256k1.signSync(message, seckey, { der: false, recovered: true, extraEntropy: data }) |
| 218 | + } catch (err) { |
| 219 | + return 1 |
| 220 | + } |
| 221 | + |
| 222 | + if (obj.signature.length !== sig[0].length) return 1 |
| 223 | + obj.signature.set(sig[0]) |
| 224 | + obj.recid = sig[1] |
| 225 | + return 0 |
| 226 | + }, |
| 227 | + |
| 228 | + // Complex logic to return correct error codes |
| 229 | + ecdsaVerify (sig, msg32, pubkey) { |
| 230 | + if (sig.subarray(0, 32).every((x) => x === 0)) return 3 |
| 231 | + if (sig.subarray(32, 64).every((x) => x === 0)) return 3 |
| 232 | + |
| 233 | + let signature |
| 234 | + try { |
| 235 | + signature = secp256k1.Signature.fromCompact(sig) |
| 236 | + } catch (err) { |
| 237 | + return 1 |
| 238 | + } |
| 239 | + if (signature.hasHighS()) return 3 |
| 240 | + |
| 241 | + let pub |
| 242 | + try { |
| 243 | + pub = secp256k1.Point.fromHex(pubkey) |
| 244 | + } catch (err) { |
| 245 | + return 2 |
| 246 | + } |
| 247 | + |
| 248 | + return secp256k1.verify(sig, msg32, pub) ? 0 : 3 |
| 249 | + }, |
| 250 | + |
| 251 | + // Complex logic to return correct error codes |
| 252 | + ecdsaRecover (output, sig, recid, msg32) { |
| 253 | + if (sig.subarray(0, 32).every((x) => x === 0)) return 2 |
| 254 | + if (sig.subarray(32, 64).every((x) => x === 0)) return 2 |
| 255 | + |
| 256 | + let signature |
| 257 | + try { |
| 258 | + signature = secp256k1.Signature.fromCompact(sig) |
| 259 | + } catch (err) { |
| 260 | + return 1 |
| 261 | + } |
| 262 | + |
| 263 | + let buf |
| 264 | + try { |
| 265 | + buf = secp256k1.recoverPublicKey(msg32, signature, recid, output.length === 33) |
| 266 | + } catch (err) { |
| 267 | + return 2 |
| 268 | + } |
| 269 | + |
| 270 | + if (output.length !== buf.length) return 1 |
| 271 | + output.set(buf) |
| 272 | + return 0 |
| 273 | + }, |
| 274 | + |
| 275 | + ecdh (output, pubkey, seckey, data, hashfn, xbuf, ybuf) { |
| 276 | + let pub |
| 277 | + try { |
| 278 | + pub = secp256k1.Point.fromHex(pubkey) |
| 279 | + } catch (err) { |
| 280 | + return 1 |
| 281 | + } |
| 282 | + |
| 283 | + const compressed = hashfn === undefined |
| 284 | + |
| 285 | + let point |
| 286 | + try { |
| 287 | + point = secp256k1.getSharedSecret(seckey, pub, compressed) |
| 288 | + } catch (err) { |
| 289 | + return 2 |
| 290 | + } |
| 291 | + |
| 292 | + if (hashfn === undefined) { |
| 293 | + output.set(sha256(point)) |
| 294 | + } else { |
| 295 | + if (!xbuf) xbuf = new Uint8Array(32) |
| 296 | + xbuf.set(point.subarray(1, 33)) |
| 297 | + |
| 298 | + if (!ybuf) ybuf = new Uint8Array(32) |
| 299 | + ybuf.set(point.subarray(33)) |
| 300 | + |
| 301 | + const hash = hashfn(xbuf, ybuf, data) |
| 302 | + const isValid = hash instanceof Uint8Array && hash.length === output.length |
| 303 | + if (!isValid) return 2 |
| 304 | + |
| 305 | + output.set(hash) |
| 306 | + return 0 |
| 307 | + } |
| 308 | + |
| 309 | + return 0 |
| 310 | + } |
| 311 | +} |
0 commit comments