diff --git a/.npmignore b/.npmignore index 5a17c29..a15f8af 100644 --- a/.npmignore +++ b/.npmignore @@ -8,4 +8,5 @@ !/LICENSE !/README.md !/package.json +!/dist/**/* !/ellipticcurve/**/* diff --git a/README.md b/README.md index 9d43d8f..273eef1 100644 --- a/README.md +++ b/README.md @@ -1,164 +1,76 @@ -## A lightweight and fast ECDSA implementation +# ECDSA Node TypeScript -### Overview +A TypeScript implementation of the Elliptic Curve Digital Signature Algorithm (ECDSA). -This is a pure JS implementation of the Elliptic Curve Digital Signature Algorithm. It is compatible with OpenSSL and uses elegant math such as Jacobian Coordinates to speed up the ECDSA on pure JS. +## Features -### Installation +- Full TypeScript support with type definitions +- OpenSSL-compatible implementation +- Fast and efficient using Jacobian coordinates +- Supports secp256k1 and prime256v1 (P-256) curves +- Comprehensive test suite -To install StarkBank`s ECDSA for Node JS, run: +## Installation -```sh -npm install starkbank-ecdsa +```bash +npm install ecdsa-node-ts +# or +yarn add ecdsa-node-ts ``` -### Curves +## Usage -We currently support `secp256k1`, but it's super easy to add more curves to the project. Just add them on `curve.js` +```typescript +import { PrivateKey, Ecdsa } from "ecdsa-node-ts"; -### Speed +// Generate new private key +const privateKey = new PrivateKey(); -We ran a test on Node 13.1.0 on a MAC Pro i5 2019. The libraries ran 100 times and showed the average times displayed bellow: +// Get public key +const publicKey = privateKey.publicKey(); -| Library | sign | verify | -| ------------------ |:-------------:| -------:| -| [crypto] | 0.5ms | 1.0ms | -| starkbank-ecdsa | 6.3ms | 15.0ms | +// Create message +const message = "My message"; +// Create signature +const signature = Ecdsa.sign(message, privateKey); -### Sample Code - -How to sign a json message for [Stark Bank]: - -```js -var ellipticcurve = require("starkbank-ecdsa"); -var Ecdsa = ellipticcurve.Ecdsa; -var PrivateKey = ellipticcurve.PrivateKey; - -// Generate privateKey from PEM string -var privateKey = PrivateKey.fromPem("-----BEGIN EC PARAMETERS-----\nBgUrgQQACg==\n-----END EC PARAMETERS-----\n-----BEGIN EC PRIVATE KEY-----\nMHQCAQEEIODvZuS34wFbt0X53+P5EnSj6tMjfVK01dD1dgDH02RzoAcGBSuBBAAK\noUQDQgAE/nvHu/SQQaos9TUljQsUuKI15Zr5SabPrbwtbfT/408rkVVzq8vAisbB\nRmpeRREXj5aog/Mq8RrdYy75W9q/Ig==\n-----END EC PRIVATE KEY-----\n"); - -// Create message from json -let message = JSON.stringify({ - "transfers": [ - { - "amount": 100000000, - "taxId": "594.739.480-42", - "name": "Daenerys Targaryen Stormborn", - "bankCode": "341", - "branchCode": "2201", - "accountNumber": "76543-8", - "tags": ["daenerys", "targaryen", "transfer-1-external-id"] - } - ] -}); - -signature = Ecdsa.sign(message, privateKey); - -// Generate Signature in base64. This result can be sent to Stark Bank in header as Digital-Signature parameter -console.log(signature.toBase64()); - -// To double check if message matches the signature -let publicKey = privateKey.publicKey(); - -console.log(Ecdsa.verify(message, signature, publicKey)); -``` - -Simple use: - -```js -var ellipticcurve = require("starkbank-ecdsa"); -var Ecdsa = ellipticcurve.Ecdsa; -var PrivateKey = ellipticcurve.PrivateKey; - -// Generate new Keys -let privateKey = new PrivateKey(); -let publicKey = privateKey.publicKey(); - -let message = "My test message"; - -// Generate Signature -let signature = Ecdsa.sign(message, privateKey); - -// Verify if signature is valid -console.log(Ecdsa.verify(message, signature, publicKey)); +// Verify signature +const verified = Ecdsa.verify(message, signature, publicKey); +console.log(verified); // true ``` -### OpenSSL +### Working with PEM files -This library is compatible with OpenSSL, so you can use it to generate keys: +```typescript +// Import keys from PEM +const privateKeyPem = File.read("privateKey.pem"); +const privateKey = PrivateKey.fromPem(privateKeyPem); -``` -openssl ecparam -name secp256k1 -genkey -out privateKey.pem -openssl ec -in privateKey.pem -pubout -out publicKey.pem -``` - -Create a message.txt file and sign it: +const publicKeyPem = File.read("publicKey.pem"); +const publicKey = PublicKey.fromPem(publicKeyPem); -``` -openssl dgst -sha256 -sign privateKey.pem -out signatureDer.txt message.txt +// Export keys to PEM +const pemPrivate = privateKey.toPem(); +const pemPublic = publicKey.toPem(); ``` -To verify, do this: +## Development -```js -var ellipticcurve = require("starkbank-ecdsa"); -var Ecdsa = ellipticcurve.Ecdsa; -var Signature = ellipticcurve.Signature; -var PublicKey = ellipticcurve.PublicKey; -var File = ellipticcurve.utils.File; +```bash +# Install dependencies +yarn install -let publicKeyPem = File.read("publicKey.pem"); -let signatureDer = File.read("signatureDer.txt", "binary"); -let message = File.read("message.txt"); +# Build +yarn build -let publicKey = PublicKey.fromPem(publicKeyPem); -let signature = Signature.fromDer(signatureDer); +# Run tests +yarn test -console.log(Ecdsa.verify(message, signature, publicKey)); +# Clean build files +yarn clean ``` -You can also verify it on terminal: - -``` -openssl dgst -sha256 -verify publicKey.pem -signature signatureDer.txt message.txt -``` - -NOTE: If you want to create a Digital Signature to use in the [Stark Bank], you need to convert the binary signature to base64. - -``` -openssl base64 -in signatureDer.txt -out signatureBase64.txt -``` - -You can do the same with this library: - -```js -var ellipticcurve = require("starkbank-ecdsa"); -var Signature = ellipticcurve.Signature; -var File = ellipticcurve.utils.File; - -let signatureDer = File.read("signatureDer.txt", "binary"); - -let signature = Signature.fromDer(signatureDer); - -console.log(signature.toBase64()); -``` - -[Stark Bank]: https://starkbank.com - -### Run all unit tests -Run tests in [Mocha framework] - -```sh -node test -``` - -or - -```sh -./node_modules/mocha/bin/mocha -``` +## License -[Mocha framework]: https://mochajs.org/#getting-started -[crypto]: https://nodejs.org/api/crypto.html -[ecdsa]: https://www.npmjs.com/package/ecdsa +MIT License diff --git a/dist/ellipticcurve/curve.d.ts b/dist/ellipticcurve/curve.d.ts new file mode 100644 index 0000000..2d7fa9d --- /dev/null +++ b/dist/ellipticcurve/curve.d.ts @@ -0,0 +1,23 @@ +import { BigInteger } from "big-integer"; +import { Point } from "./point"; +export declare class CurveFp { + A: BigInteger; + B: BigInteger; + P: BigInteger; + N: BigInteger; + G: Point; + name: string; + nistName: string | null; + private _oid; + constructor(A: BigInteger, B: BigInteger, P: BigInteger, N: BigInteger, Gx: BigInteger, Gy: BigInteger, name: string, oid: number[], nistName?: string | null); + contains(p: Point): boolean; + length(): number; + get oid(): number[]; +} +export declare const secp256k1: CurveFp; +export declare const prime256v1: CurveFp; +export declare const p256: CurveFp; +export declare const supportedCurves: CurveFp[]; +export declare const curvesByOid: { + [key: string]: CurveFp; +}; diff --git a/dist/ellipticcurve/curve.js b/dist/ellipticcurve/curve.js new file mode 100644 index 0000000..a8e8248 --- /dev/null +++ b/dist/ellipticcurve/curve.js @@ -0,0 +1,53 @@ +"use strict"; +// +// Elliptic Curve Equation +// +// y^2 = x^3 + A*x + B (mod P) +// +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.curvesByOid = exports.supportedCurves = exports.p256 = exports.prime256v1 = exports.secp256k1 = exports.CurveFp = void 0; +const big_integer_1 = __importDefault(require("big-integer")); +const point_1 = require("./point"); +const integer_1 = require("./utils/integer"); +class CurveFp { + constructor(A, B, P, N, Gx, Gy, name, oid, nistName = null) { + this.A = A; + this.B = B; + this.P = P; + this.N = N; + this.G = new point_1.Point(Gx, Gy); + this.name = name; + this.nistName = nistName; + this._oid = oid; + } + contains(p) { + if (p.x.lesser(0) || p.x.greater(this.P.minus(1))) { + return false; + } + if (p.y.lesser(0) || p.y.greater(this.P.minus(1))) { + return false; + } + if (!(0, integer_1.modulo)(p.y.pow(2).minus(p.x.pow(3).add(this.A.multiply(p.x)).add(this.B)), this.P).equals(0)) { + return false; + } + return true; + } + length() { + return Math.floor((1 + this.N.toString(16).length) / 2); + } + get oid() { + return [...this._oid]; + } +} +exports.CurveFp = CurveFp; +exports.secp256k1 = new CurveFp((0, big_integer_1.default)("0000000000000000000000000000000000000000000000000000000000000000", 16), (0, big_integer_1.default)("0000000000000000000000000000000000000000000000000000000000000007", 16), (0, big_integer_1.default)("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", 16), (0, big_integer_1.default)("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16), (0, big_integer_1.default)("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", 16), (0, big_integer_1.default)("483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", 16), "secp256k1", [1, 3, 132, 0, 10]); +exports.prime256v1 = new CurveFp((0, big_integer_1.default)("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), (0, big_integer_1.default)("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16), (0, big_integer_1.default)("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16), (0, big_integer_1.default)("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16), (0, big_integer_1.default)("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16), (0, big_integer_1.default)("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16), "prime256v1", [1, 2, 840, 10045, 3, 1, 7], "P-256"); +exports.p256 = exports.prime256v1; +exports.supportedCurves = [exports.secp256k1, exports.prime256v1]; +exports.curvesByOid = {}; +exports.supportedCurves.forEach((curve) => { + exports.curvesByOid[curve.oid.join(".")] = curve; +}); diff --git a/dist/ellipticcurve/ecdsa.d.ts b/dist/ellipticcurve/ecdsa.d.ts new file mode 100644 index 0000000..fb710e0 --- /dev/null +++ b/dist/ellipticcurve/ecdsa.d.ts @@ -0,0 +1,8 @@ +import { BigInteger } from "big-integer"; +import { Signature } from "./signature"; +import { PrivateKey } from "./privateKey"; +import { PublicKey } from "./publicKey"; +type HashFunction = (message: string | number[] | Uint8Array) => string; +export declare function sign(message: string, privateKey: PrivateKey, hashfunc?: HashFunction, randNum?: BigInteger): Signature; +export declare function verify(message: string, signature: Signature, publicKey: PublicKey, hashfunc?: HashFunction): boolean; +export {}; diff --git a/dist/ellipticcurve/ecdsa.js b/dist/ellipticcurve/ecdsa.js new file mode 100644 index 0000000..1552e94 --- /dev/null +++ b/dist/ellipticcurve/ecdsa.js @@ -0,0 +1,83 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.sign = sign; +exports.verify = verify; +const js_sha256_1 = require("js-sha256"); +const big_integer_1 = __importDefault(require("big-integer")); +const EcdsaMath = __importStar(require("./math")); +const signature_1 = require("./signature"); +const BinaryAscii = __importStar(require("./utils/binary")); +const Integer = __importStar(require("./utils/integer")); +const randomInteger = Integer.between; +const modulo = Integer.modulo; +function sign(message, privateKey, hashfunc = js_sha256_1.sha256, randNum) { + let hashMessage = hashfunc(message); + let numberMessage = BinaryAscii.numberFromHex(hashMessage); + let curve = privateKey.curve; + if (!randNum) { + randNum = randomInteger((0, big_integer_1.default)(1), curve.N.minus(1)); + } + let randSignPoint = EcdsaMath.multiply(curve.G, randNum, curve.N, curve.A, curve.P); + let r = modulo(randSignPoint.x, curve.N); + let s = modulo(numberMessage + .add(r.multiply(privateKey.secret)) + .multiply(EcdsaMath.inv(randNum, curve.N)), curve.N); + return new signature_1.Signature(r, s); +} +function verify(message, signature, publicKey, hashfunc = js_sha256_1.sha256) { + let hashMessage = hashfunc(message); + let numberMessage = BinaryAscii.numberFromHex(hashMessage); + let curve = publicKey.curve; + let sigR = signature.r; + let sigS = signature.s; + if (sigR.lesser(1) || sigR.greaterOrEquals(curve.N)) { + return false; + } + if (sigS.lesser(1) || sigS.greaterOrEquals(curve.N)) { + return false; + } + let inv = EcdsaMath.inv(sigS, curve.N); + let u1 = EcdsaMath.multiply(curve.G, modulo(numberMessage.multiply(inv), curve.N), curve.N, curve.A, curve.P); + let u2 = EcdsaMath.multiply(publicKey.point, modulo(sigR.multiply(inv), curve.N), curve.N, curve.A, curve.P); + let v = EcdsaMath.add(u1, u2, curve.A, curve.P); + if (v.isAtInfinity()) { + return false; + } + return v.x.mod(curve.N).eq(sigR); +} diff --git a/dist/ellipticcurve/math.d.ts b/dist/ellipticcurve/math.d.ts new file mode 100644 index 0000000..17c421a --- /dev/null +++ b/dist/ellipticcurve/math.d.ts @@ -0,0 +1,5 @@ +import { Point } from "./point"; +import { BigInteger } from "big-integer"; +export declare function multiply(p: Point, n: BigInteger, N: BigInteger, A: BigInteger, P: BigInteger): Point; +export declare function add(p: Point, q: Point, A: BigInteger, P: BigInteger): Point; +export declare function inv(x: BigInteger, n: BigInteger): BigInteger; diff --git a/dist/ellipticcurve/math.js b/dist/ellipticcurve/math.js new file mode 100644 index 0000000..3c29b84 --- /dev/null +++ b/dist/ellipticcurve/math.js @@ -0,0 +1,112 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.multiply = multiply; +exports.add = add; +exports.inv = inv; +const point_1 = require("./point"); +const integer_1 = require("./utils/integer"); +const big_integer_1 = __importDefault(require("big-integer")); +function multiply(p, n, N, A, P) { + // Fast way to multiply point and scalar in elliptic curves + return fromJacobian(jacobianMultiply(toJacobian(p), n, N, A, P), P); +} +function add(p, q, A, P) { + // Fast way to add two points in elliptic curves + return fromJacobian(jacobianAdd(toJacobian(p), toJacobian(q), A, P), P); +} +function inv(x, n) { + // Extended Euclidean Algorithm. It's the 'division' in elliptic curves + if (x.eq(0)) { + return (0, big_integer_1.default)(0); + } + let lm = (0, big_integer_1.default)(1); + let hm = (0, big_integer_1.default)(0); + let low = (0, integer_1.modulo)(x, n); + let high = n; + let r, nm, newLow; + while (low.greater(1)) { + r = high.over(low); // bigint division floors result automatically + nm = hm.minus(lm.multiply(r)); + newLow = high.minus(low.multiply(r)); + high = low; + hm = lm; + low = newLow; + lm = nm; + } + return (0, integer_1.modulo)(lm, n); +} +function toJacobian(p) { + // Convert point to Jacobian coordinates + return new point_1.Point(p.x, p.y, (0, big_integer_1.default)(1)); +} +function fromJacobian(p, P) { + // Convert point back from Jacobian coordinates + let z = inv(p.z, P); + return new point_1.Point((0, integer_1.modulo)(p.x.multiply(z.pow(2)), P), (0, integer_1.modulo)(p.y.multiply(z.pow(3)), P)); +} +function jacobianDouble(p, A, P) { + // Double a point in elliptic curves + if (p.y.equals(0)) { + return new point_1.Point((0, big_integer_1.default)(0), (0, big_integer_1.default)(0), (0, big_integer_1.default)(0)); + } + let ysq = (0, integer_1.modulo)(p.y.pow(2), P); + let S = (0, integer_1.modulo)(p.x.multiply(ysq).multiply(4), P); + let M = (0, integer_1.modulo)(p.x + .pow(2) + .multiply(3) + .add(A.multiply(p.z.pow(4))), P); + let nx = (0, integer_1.modulo)(M.pow(2).minus(S.multiply(2)), P); + let ny = (0, integer_1.modulo)(M.multiply(S.minus(nx)).minus(ysq.pow(2).multiply(8)), P); + let nz = (0, integer_1.modulo)(p.y.multiply(p.z).multiply(2), P); + return new point_1.Point(nx, ny, nz); +} +function jacobianAdd(p, q, A, P) { + // Add two points in elliptic curves + if (p.y.equals(0)) { + return q; + } + if (q.y.equals(0)) { + return p; + } + let U1 = (0, integer_1.modulo)(p.x.multiply(q.z.pow(2)), P); + let U2 = (0, integer_1.modulo)(q.x.multiply(p.z.pow(2)), P); + let S1 = (0, integer_1.modulo)(p.y.multiply(q.z.pow(3)), P); + let S2 = (0, integer_1.modulo)(q.y.multiply(p.z.pow(3)), P); + if (U1.eq(U2)) { + if (S1.neq(S2)) { + return new point_1.Point((0, big_integer_1.default)(0), (0, big_integer_1.default)(0), (0, big_integer_1.default)(1)); + } + return jacobianDouble(p, A, P); + } + let H = U2.minus(U1); + let R = S2.minus(S1); + let H2 = (0, integer_1.modulo)(H.multiply(H), P); + let H3 = (0, integer_1.modulo)(H.multiply(H2), P); + let U1H2 = (0, integer_1.modulo)(U1.multiply(H2), P); + let nx = (0, integer_1.modulo)(R.pow(2).minus(H3).minus(U1H2.multiply(2)), P); + let ny = (0, integer_1.modulo)(R.multiply(U1H2.minus(nx)).minus(S1.multiply(H3)), P); + let nz = (0, integer_1.modulo)(H.multiply(p.z).multiply(q.z), P); + return new point_1.Point(nx, ny, nz); +} +function jacobianMultiply(p, n, N, A, P) { + // Multiply point and scalar in elliptic curves + if (p.y.equals(0) || n.eq(0)) { + return new point_1.Point((0, big_integer_1.default)(0), (0, big_integer_1.default)(0), (0, big_integer_1.default)(1)); + } + if (n.eq(1)) { + return p; + } + if (n.lesser(0) || n.greaterOrEquals(N)) { + return jacobianMultiply(p, (0, integer_1.modulo)(n, N), N, A, P); + } + if ((0, integer_1.modulo)(n, (0, big_integer_1.default)(2)).eq(0)) { + return jacobianDouble(jacobianMultiply(p, n.over(2), N, A, P), A, P); + } + if ((0, integer_1.modulo)(n, (0, big_integer_1.default)(2)).eq(1)) { + return jacobianAdd(jacobianDouble(jacobianMultiply(p, n.over(2), N, A, P), A, P), p, A, P); + } + throw new Error(`logical failure: p: ${p}, n: ${n}, N: ${N}, A: ${A}, P: ${P}`); +} diff --git a/dist/ellipticcurve/point.d.ts b/dist/ellipticcurve/point.d.ts new file mode 100644 index 0000000..90096c5 --- /dev/null +++ b/dist/ellipticcurve/point.d.ts @@ -0,0 +1,8 @@ +import { BigInteger } from "big-integer"; +export declare class Point { + x: BigInteger; + y: BigInteger; + z: BigInteger; + constructor(x?: BigInteger, y?: BigInteger, z?: BigInteger); + isAtInfinity(): boolean; +} diff --git a/dist/ellipticcurve/point.js b/dist/ellipticcurve/point.js new file mode 100644 index 0000000..607eb87 --- /dev/null +++ b/dist/ellipticcurve/point.js @@ -0,0 +1,18 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Point = void 0; +const big_integer_1 = __importDefault(require("big-integer")); +class Point { + constructor(x = (0, big_integer_1.default)(0), y = (0, big_integer_1.default)(0), z = (0, big_integer_1.default)(0)) { + this.x = x; + this.y = y; + this.z = z; + } + isAtInfinity() { + return this.y.equals(0); + } +} +exports.Point = Point; diff --git a/dist/ellipticcurve/privateKey.d.ts b/dist/ellipticcurve/privateKey.d.ts new file mode 100644 index 0000000..2c639e6 --- /dev/null +++ b/dist/ellipticcurve/privateKey.d.ts @@ -0,0 +1,15 @@ +import { PublicKey } from "./publicKey"; +import * as EcdsaCurve from "./curve"; +import { BigInteger } from "big-integer"; +export declare class PrivateKey { + curve: EcdsaCurve.CurveFp; + secret: BigInteger; + constructor(curve?: EcdsaCurve.CurveFp, secret?: BigInteger); + publicKey(): PublicKey; + toString(): string; + toDer(): Buffer; + toPem(): string; + static fromPem(string: string): PrivateKey; + static fromDer(data: Buffer | string): PrivateKey; + static fromString(data: Buffer | string, curve?: EcdsaCurve.CurveFp): PrivateKey; +} diff --git a/dist/ellipticcurve/privateKey.js b/dist/ellipticcurve/privateKey.js new file mode 100644 index 0000000..a9e477c --- /dev/null +++ b/dist/ellipticcurve/privateKey.js @@ -0,0 +1,124 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PrivateKey = void 0; +const publicKey_1 = require("./publicKey"); +const RandomInteger = __importStar(require("./utils/integer")); +const BinaryAscii = __importStar(require("./utils/binary")); +const EcdsaCurve = __importStar(require("./curve")); +const EcdsaMath = __importStar(require("./math")); +const big_integer_1 = __importDefault(require("big-integer")); +const der = __importStar(require("./utils/der")); +const hexAt = "\x00"; +class PrivateKey { + constructor(curve = EcdsaCurve.secp256k1, secret) { + this.curve = curve; + if (secret) { + this.secret = secret; + } + else { + this.secret = RandomInteger.between((0, big_integer_1.default)(1), curve.N.minus(1)); + } + } + publicKey() { + let curve = this.curve; + let publicPoint = EcdsaMath.multiply(curve.G, this.secret, curve.N, curve.A, curve.P); + return new publicKey_1.PublicKey(publicPoint, curve); + } + toString() { + return BinaryAscii.stringFromNumber(this.secret, this.curve.length()); + } + toDer() { + let encodedPublicKey = this.publicKey().toString(true); + return der.encodeSequence(der.encodeInteger((0, big_integer_1.default)(1)), der.encodeOctetString(this.toString()), der.encodeConstructed(0, der.encodeOid(this.curve.oid)), der.encodeConstructed(1, der.encodeBitstring(encodedPublicKey))); + } + toPem() { + return der.toPem(this.toDer(), "EC PRIVATE KEY"); + } + static fromPem(string) { + // Extract the private key part from the PEM file + const privateKeyMatch = string.match(/-----BEGIN EC PRIVATE KEY-----\n([^-]+)\n-----END EC PRIVATE KEY-----/); + if (!privateKeyMatch) { + throw new Error("Invalid PEM format: EC PRIVATE KEY section not found"); + } + const privateKeyPem = privateKeyMatch[1]; + return this.fromDer(Buffer.from(privateKeyPem, "base64")); + } + static fromDer(data) { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + let [s1, empty] = der.removeSequence(buf); + if (empty.length > 0) { + throw new Error("trailing junk after DER private key: " + + BinaryAscii.hexFromBinary(empty)); + } + let [one, rest] = der.removeInteger(s1); + if (!one.eq(1)) { + throw new Error("expected '1' at start of DER private key, got " + one); + } + let [privateKeyStr, t1] = der.removeOctetString(rest); + let [tag, curveOidStr, t2] = der.removeConstructed(t1); + if (tag !== 0) { + throw new Error("expected tag 0 in DER private key, got " + tag); + } + let [oidCurve, empty2] = der.removeObject(curveOidStr); + if (empty2.length > 0) { + throw new Error("trailing junk after DER private key curve_oid: " + + BinaryAscii.hexFromBinary(empty2)); + } + let curve = EcdsaCurve.curvesByOid[oidCurve.join(".")]; + if (!curve) { + let supportedCurvesNames = EcdsaCurve.supportedCurves.map((x) => x.name); + throw new Error("Unknown curve with oid " + + oidCurve.join(".") + + ". Only the following are available: " + + supportedCurvesNames); + } + if (privateKeyStr.length < curve.length()) { + privateKeyStr = Buffer.concat([ + Buffer.alloc(curve.length() - privateKeyStr.length, 0), + privateKeyStr, + ]); + } + return this.fromString(privateKeyStr, curve); + } + static fromString(data, curve = EcdsaCurve.secp256k1) { + const str = Buffer.isBuffer(data) ? data.toString("binary") : data; + return new PrivateKey(curve, BinaryAscii.numberFromString(str)); + } +} +exports.PrivateKey = PrivateKey; diff --git a/dist/ellipticcurve/publicKey.d.ts b/dist/ellipticcurve/publicKey.d.ts new file mode 100644 index 0000000..953bd4a --- /dev/null +++ b/dist/ellipticcurve/publicKey.d.ts @@ -0,0 +1,13 @@ +import * as EcdsaCurve from "./curve"; +import { Point } from "./point"; +export declare class PublicKey { + point: Point; + curve: EcdsaCurve.CurveFp; + constructor(point: Point, curve: EcdsaCurve.CurveFp); + toString(encoded?: boolean): Buffer; + toDer(): Buffer; + toPem(): string; + static fromPem(string: string): PublicKey; + static fromDer(data: Buffer | string): PublicKey; + static fromString(data: Buffer | string, curve?: EcdsaCurve.CurveFp, validatePoint?: boolean): PublicKey; +} diff --git a/dist/ellipticcurve/publicKey.js b/dist/ellipticcurve/publicKey.js new file mode 100644 index 0000000..b8aed46 --- /dev/null +++ b/dist/ellipticcurve/publicKey.js @@ -0,0 +1,126 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PublicKey = void 0; +const BinaryAscii = __importStar(require("./utils/binary")); +const EcdsaCurve = __importStar(require("./curve")); +const point_1 = require("./point"); +const der = __importStar(require("./utils/der")); +const Math = __importStar(require("./math")); +class PublicKey { + constructor(point, curve) { + this.point = point; + this.curve = curve; + } + toString(encoded = false) { + let xString = BinaryAscii.stringFromNumber(this.point.x, this.curve.length()); + let yString = BinaryAscii.stringFromNumber(this.point.y, this.curve.length()); + if (encoded) { + return Buffer.concat([ + Buffer.from([0x00, 0x04]), + Buffer.from(xString, "binary"), + Buffer.from(yString, "binary"), + ]); + } + return Buffer.concat([ + Buffer.from(xString, "binary"), + Buffer.from(yString, "binary"), + ]); + } + toDer() { + const ecOid = der.encodeOid([1, 2, 840, 10045, 2, 1]); + const curveOid = der.encodeOid(this.curve.oid); + const encodeEcAndOid = der.encodeSequence(ecOid, curveOid); + const encodedPoint = der.encodeBitstring(this.toString(true)); + return der.encodeSequence(encodeEcAndOid, encodedPoint); + } + toPem() { + return der.toPem(this.toDer(), "PUBLIC KEY"); + } + static fromPem(string) { + return this.fromDer(der.fromPem(string)); + } + static fromDer(data) { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + let [s1, empty] = der.removeSequence(buf); + if (empty.length > 0) { + throw new Error("trailing junk after DER public key: " + + BinaryAscii.hexFromBinary(empty)); + } + let [s2, pointBitString] = der.removeSequence(s1); + let [, rest] = der.removeObject(s2); + let [oidCurve, empty2] = der.removeObject(rest); + if (empty2.length > 0) { + throw new Error("trailing junk after DER public key objects: " + + BinaryAscii.hexFromBinary(empty2)); + } + let curve = EcdsaCurve.curvesByOid[oidCurve.join(".")]; + if (!curve) { + let supportedCurvesNames = EcdsaCurve.supportedCurves.map((x) => x.name); + throw new Error("Unknown curve with oid " + + oidCurve.join(".") + + ". Only the following are available: " + + supportedCurvesNames); + } + let [pointStr, empty3] = der.removeBitString(pointBitString); + if (empty3.length > 0) { + throw new Error("trailing junk after public key point-string: " + + BinaryAscii.hexFromBinary(empty3)); + } + return this.fromString(pointStr.slice(2), curve); + } + static fromString(data, curve = EcdsaCurve.secp256k1, validatePoint = true) { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + let baseLen = curve.length(); + let xs = buf.slice(0, baseLen); + let ys = buf.slice(baseLen); + let p = new point_1.Point(BinaryAscii.numberFromString(xs.toString("binary")), BinaryAscii.numberFromString(ys.toString("binary"))); + let publicKey = new PublicKey(p, curve); + if (!validatePoint) { + return publicKey; + } + if (p.isAtInfinity()) { + throw new Error("Public Key point is at infinity"); + } + if (!curve.contains(p)) { + throw new Error(`point (${p.x},${p.y}) is not valid for curve ${curve.name}`); + } + if (!Math.multiply(p, curve.N, curve.N, curve.A, curve.P).isAtInfinity()) { + throw new Error(`Point (${p.x},${p.y} * ${curve.name}.N is not at infinity`); + } + return publicKey; + } +} +exports.PublicKey = PublicKey; diff --git a/dist/ellipticcurve/signature.d.ts b/dist/ellipticcurve/signature.d.ts new file mode 100644 index 0000000..346638e --- /dev/null +++ b/dist/ellipticcurve/signature.d.ts @@ -0,0 +1,10 @@ +import { BigInteger } from "big-integer"; +export declare class Signature { + r: BigInteger; + s: BigInteger; + constructor(r: BigInteger, s: BigInteger); + toDer(): Buffer; + toBase64(): string; + static fromDer(data: Buffer | string): Signature; + static fromBase64(string: string): Signature; +} diff --git a/dist/ellipticcurve/signature.js b/dist/ellipticcurve/signature.js new file mode 100644 index 0000000..7db4611 --- /dev/null +++ b/dist/ellipticcurve/signature.js @@ -0,0 +1,71 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Signature = void 0; +const BinaryAscii = __importStar(require("./utils/binary")); +const Base64 = __importStar(require("./utils/base")); +const der = __importStar(require("./utils/der")); +class Signature { + constructor(r, s) { + this.r = r; + this.s = s; + } + toDer() { + const rEncoded = der.encodeInteger(this.r); + const sEncoded = der.encodeInteger(this.s); + return der.encodeSequence(rEncoded, sEncoded); + } + toBase64() { + return Base64.encode(this.toDer()); + } + static fromDer(data) { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + let [rs, empty] = der.removeSequence(buf); + if (empty.length > 0) { + throw new Error("trailing junk after DER signature: " + BinaryAscii.hexFromBinary(empty)); + } + let [r, rest] = der.removeInteger(rs); + let [s, empty2] = der.removeInteger(rest); + if (empty2.length > 0) { + throw new Error("trailing junk after DER numbers: " + BinaryAscii.hexFromBinary(empty2)); + } + return new Signature(r, s); + } + static fromBase64(string) { + let derString = Base64.decode(string); + return this.fromDer(derString); + } +} +exports.Signature = Signature; diff --git a/dist/ellipticcurve/utils/base.d.ts b/dist/ellipticcurve/utils/base.d.ts new file mode 100644 index 0000000..f466212 --- /dev/null +++ b/dist/ellipticcurve/utils/base.d.ts @@ -0,0 +1,2 @@ +export declare function decode(string: string): Buffer; +export declare function encode(data: string | Buffer): string; diff --git a/dist/ellipticcurve/utils/base.js b/dist/ellipticcurve/utils/base.js new file mode 100644 index 0000000..b72c41a --- /dev/null +++ b/dist/ellipticcurve/utils/base.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.decode = decode; +exports.encode = encode; +function decode(string) { + return Buffer.from(string, "base64"); +} +function encode(data) { + if (Buffer.isBuffer(data)) { + return data.toString("base64"); + } + return Buffer.from(data, "binary").toString("base64"); +} diff --git a/dist/ellipticcurve/utils/binary.d.ts b/dist/ellipticcurve/utils/binary.d.ts new file mode 100644 index 0000000..7c5aeed --- /dev/null +++ b/dist/ellipticcurve/utils/binary.d.ts @@ -0,0 +1,6 @@ +import bigInt from "big-integer"; +export declare function hexFromBinary(data: string | Buffer): string; +export declare function binaryFromHex(data: string): Buffer; +export declare function numberFromString(string: string): bigInt.BigInteger; +export declare function numberFromHex(string: string): bigInt.BigInteger; +export declare function stringFromNumber(number: bigInt.BigInteger, length: number): string; diff --git a/ellipticcurve/utils/binary.js b/dist/ellipticcurve/utils/binary.js similarity index 67% rename from ellipticcurve/utils/binary.js rename to dist/ellipticcurve/utils/binary.js index 9a07884..21c0822 100644 --- a/ellipticcurve/utils/binary.js +++ b/dist/ellipticcurve/utils/binary.js @@ -1,62 +1,48 @@ -const BigInt = require("big-integer"); - - -var hexFromBinary = function (data) { +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.hexFromBinary = hexFromBinary; +exports.binaryFromHex = binaryFromHex; +exports.numberFromString = numberFromString; +exports.numberFromHex = numberFromHex; +exports.stringFromNumber = stringFromNumber; +const big_integer_1 = __importDefault(require("big-integer")); +function hexFromBinary(data) { // Return the hexadecimal representation of the binary data. Every byte of data is converted into the // corresponding 2-digit hex representation. The resulting string is therefore twice as long as the length of data. - // :param data: binary // :return: hexadecimal string - + if (Buffer.isBuffer(data)) { + return data.toString("hex"); + } return Buffer.from(data, "binary").toString("hex"); } - - -var binaryFromHex = function (data) { +function binaryFromHex(data) { // Return the binary data represented by the hexadecimal string hexstr. This function is the inverse of b2a_hex(). // hexstr must contain an even number of hexadecimal digits (which can be upper or lower case), otherwise a TypeError is raised. - // :param data: hexadecimal string // :return: binary - - return Buffer.from(data, "hex").toString('binary'); + return Buffer.from(data, "hex"); } - - -var numberFromString = function (string) { +function numberFromString(string) { // Get a number representation of a string - // :param String to be converted in a number // :return: Number in hex from string - return numberFromHex(hexFromBinary(string.toString())); } - - -var numberFromHex = function (string) { - return BigInt(string, 16); +function numberFromHex(string) { + return (0, big_integer_1.default)(string, 16); } - - -var stringFromNumber = function (number, length) { +function stringFromNumber(number, length) { // Get a string representation of a number - // :param number to be converted in a string // :param length max number of character for the string // :return: hexadecimal string - let result = number.toString(16); - while (result.length < 2 * length) { result = "0" + result; } - - return binaryFromHex(result); + return binaryFromHex(result).toString("binary"); } - - -exports.hexFromBinary = hexFromBinary; -exports.binaryFromHex = binaryFromHex; -exports.numberFromString = numberFromString; -exports.numberFromHex = numberFromHex; -exports.stringFromNumber = stringFromNumber; diff --git a/dist/ellipticcurve/utils/der.d.ts b/dist/ellipticcurve/utils/der.d.ts new file mode 100644 index 0000000..be035ec --- /dev/null +++ b/dist/ellipticcurve/utils/der.d.ts @@ -0,0 +1,15 @@ +import { BigInteger } from "big-integer"; +export declare function encodeSequence(...args: (Buffer | string)[]): Buffer; +export declare function encodeInteger(x: BigInteger): Buffer; +export declare function encodeOid(pieces: number[]): Buffer; +export declare function encodeBitstring(t: Buffer | string): Buffer; +export declare function encodeOctetString(t: Buffer | string): Buffer; +export declare function encodeConstructed(tag: number, value: Buffer | string): Buffer; +export declare function removeSequence(data: Buffer | string): [Buffer, Buffer]; +export declare function removeInteger(data: Buffer | string): [BigInteger, Buffer]; +export declare function removeObject(data: Buffer | string): [number[], Buffer]; +export declare function removeBitString(data: Buffer | string): [Buffer, Buffer]; +export declare function removeOctetString(data: Buffer | string): [Buffer, Buffer]; +export declare function removeConstructed(data: Buffer | string): [number, Buffer, Buffer]; +export declare function fromPem(pem: string): Buffer; +export declare function toPem(der: Buffer | string, name: string): string; diff --git a/dist/ellipticcurve/utils/der.js b/dist/ellipticcurve/utils/der.js new file mode 100644 index 0000000..9689e81 --- /dev/null +++ b/dist/ellipticcurve/utils/der.js @@ -0,0 +1,272 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.encodeSequence = encodeSequence; +exports.encodeInteger = encodeInteger; +exports.encodeOid = encodeOid; +exports.encodeBitstring = encodeBitstring; +exports.encodeOctetString = encodeOctetString; +exports.encodeConstructed = encodeConstructed; +exports.removeSequence = removeSequence; +exports.removeInteger = removeInteger; +exports.removeObject = removeObject; +exports.removeBitString = removeBitString; +exports.removeOctetString = removeOctetString; +exports.removeConstructed = removeConstructed; +exports.fromPem = fromPem; +exports.toPem = toPem; +const Base64 = __importStar(require("./base")); +const BinaryAscii = __importStar(require("./binary")); +const big_integer_1 = __importDefault(require("big-integer")); +const hexAt = Buffer.from([0x00]); +const hexB = Buffer.from([0x02]); +const hexC = Buffer.from([0x03]); +const hexD = Buffer.from([0x04]); +const hexF = Buffer.from([0x06]); +const hex0 = Buffer.from([0x30]); +const hex31 = 0x1f; +const hex127 = 0x7f; +const hex129 = 0xa0; +const hex160 = 0x80; +const hex224 = 0xe0; +const bytesHex0 = hex0; +const bytesHexB = hexB; +const bytesHexC = hexC; +const bytesHexD = hexD; +const bytesHexF = hexF; +function encodeSequence(...args) { + const buffers = args.map((arg) => Buffer.isBuffer(arg) ? arg : Buffer.from(arg, "binary")); + let totalLength = buffers.reduce((acc, curr) => acc + curr.length, 0); + return Buffer.concat([hex0, _encodeLength(totalLength), ...buffers]); +} +function encodeInteger(x) { + if (x.lesser(0)) { + throw new Error("x cannot be negative"); + } + let t = x.toString(16); + if (t.length % 2) { + t = "0" + t; + } + let xBinary = BinaryAscii.binaryFromHex(t); + let num = xBinary[0]; + if (num <= hex127) { + return Buffer.concat([hexB, Buffer.from([xBinary.length]), xBinary]); + } + return Buffer.concat([ + hexB, + Buffer.from([xBinary.length + 1]), + hexAt, + xBinary, + ]); +} +function encodeOid(pieces) { + let [first, second, ...rest] = pieces; + if (first > 2) { + throw new Error("first has to be <= 2"); + } + if (second > 39) { + throw new Error("second has to be <= 39"); + } + let encodedPieces = rest.map(_encodeNumber); + let firstByte = Buffer.from([40 * first + second]); + let body = Buffer.concat([ + firstByte, + ...encodedPieces.map((p) => Buffer.from(p)), + ]); + return Buffer.concat([hexF, _encodeLength(body.length), body]); +} +function encodeBitstring(t) { + const data = Buffer.isBuffer(t) ? t : Buffer.from(t, "binary"); + return Buffer.concat([hexC, _encodeLength(data.length), data]); +} +function encodeOctetString(t) { + const data = Buffer.isBuffer(t) ? t : Buffer.from(t, "binary"); + return Buffer.concat([hexD, _encodeLength(data.length), data]); +} +function encodeConstructed(tag, value) { + const data = Buffer.isBuffer(value) ? value : Buffer.from(value, "binary"); + return Buffer.concat([ + Buffer.from([hex129 + tag]), + _encodeLength(data.length), + data, + ]); +} +function removeSequence(data) { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + _checkSequenceError(buf, bytesHex0, "30"); + let [length, lengthLen] = _readLength(buf.slice(1)); + let endSeq = 1 + lengthLen + length; + return [buf.slice(1 + lengthLen, endSeq), buf.slice(endSeq)]; +} +function removeInteger(data) { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + _checkSequenceError(buf, bytesHexB, "02"); + let [length, lengthLen] = _readLength(buf.slice(1)); + let numberBytes = buf.slice(1 + lengthLen, 1 + lengthLen + length); + let rest = buf.slice(1 + lengthLen + length); + if (numberBytes[0] >= hex160) { + throw new Error("nBytes must be < 160"); + } + return [(0, big_integer_1.default)(numberBytes.toString("hex"), 16), rest]; +} +function removeObject(data) { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + _checkSequenceError(buf, bytesHexF, "06"); + let [length, lengthLen] = _readLength(buf.slice(1)); + let body = buf.slice(1 + lengthLen, 1 + lengthLen + length); + let rest = buf.slice(1 + lengthLen + length); + let numbers = []; + let remaining = body; + while (remaining.length > 0) { + let [n, lengthLength] = _readNumber(remaining); + numbers.push(n); + remaining = remaining.slice(lengthLength); + } + let n0 = numbers.shift(); + let first = Math.floor(n0 / 40); + let second = n0 - 40 * first; + return [[first, second, ...numbers], rest]; +} +function removeBitString(data) { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + _checkSequenceError(buf, bytesHexC, "03"); + let [length, lengthLen] = _readLength(buf.slice(1)); + let body = buf.slice(1 + lengthLen, 1 + lengthLen + length); + let rest = buf.slice(1 + lengthLen + length); + return [body, rest]; +} +function removeOctetString(data) { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + _checkSequenceError(buf, bytesHexD, "04"); + let [length, lengthLen] = _readLength(buf.slice(1)); + let body = buf.slice(1 + lengthLen, 1 + lengthLen + length); + let rest = buf.slice(1 + lengthLen + length); + return [body, rest]; +} +function removeConstructed(data) { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + let s0 = buf[0]; + if ((s0 & hex224) != hex129) { + throw new Error("wanted constructed tag (0xa0-0xbf), got 0x" + s0.toString(16)); + } + let tag = s0 & hex31; + let [length, lengthLen] = _readLength(buf.slice(1)); + let body = buf.slice(1 + lengthLen, 1 + lengthLen + length); + let rest = buf.slice(1 + lengthLen + length); + return [tag, body, rest]; +} +function fromPem(pem) { + let stripped = pem + .split("\n") + .filter((line) => !line.startsWith("-----")) + .join("") + .trim(); + return Base64.decode(stripped); +} +function toPem(der, name) { + let b64 = Base64.encode(der); + let lines = [`-----BEGIN ${name}-----\n`]; + for (let start = 0; start <= b64.length; start += 64) { + lines.push(b64.slice(start, start + 64) + "\n"); + } + lines.push(`-----END ${name}-----\n`); + return lines.join(""); +} +function _encodeLength(length) { + if (length < 0) { + throw new Error("length cannot be negative"); + } + if (length < hex160) { + return Buffer.from([length]); + } + let hexLength = length.toString(16); + if (hexLength.length % 2) { + hexLength = "0" + hexLength; + } + let lengthBytes = Buffer.from(hexLength, "hex"); + return Buffer.concat([ + Buffer.from([hex160 + lengthBytes.length]), + lengthBytes, + ]); +} +function _encodeNumber(n) { + if (n < 0) { + throw new Error("n cannot be negative"); + } + if (n === 0) { + return Buffer.from([0]); + } + let l = []; + while (n > 0) { + l.unshift(n & hex127); + n = n >> 7; + } + for (let i = 0; i < l.length - 1; i++) { + l[i] = l[i] | hex160; + } + return Buffer.from(l); +} +function _readLength(buf) { + let num = buf[0]; + if (!(num & hex160)) { + return [num, 1]; + } + let lengthLen = num - hex160; + let numberBytes = buf.slice(1, 1 + lengthLen); + return [parseInt(numberBytes.toString("hex"), 16), 1 + lengthLen]; +} +function _readNumber(buf) { + let num = buf[0]; + let length = 1; + let n = num & hex127; + while (num & hex160) { + buf = buf.slice(1); + num = buf[0]; + n = (n << 7) | (num & hex127); + length += 1; + } + return [n, length]; +} +function _checkSequenceError(buf, start, expected) { + if (buf[0] !== start[0]) { + throw new Error(`wanted sequence starting with ${expected}`); + } +} +function _extractFirstInt(buf) { + return buf[0]; +} diff --git a/dist/ellipticcurve/utils/file.d.ts b/dist/ellipticcurve/utils/file.d.ts new file mode 100644 index 0000000..6b25489 --- /dev/null +++ b/dist/ellipticcurve/utils/file.d.ts @@ -0,0 +1 @@ +export declare function read(path: string, encoding?: BufferEncoding): string; diff --git a/dist/ellipticcurve/utils/file.js b/dist/ellipticcurve/utils/file.js new file mode 100644 index 0000000..d960878 --- /dev/null +++ b/dist/ellipticcurve/utils/file.js @@ -0,0 +1,40 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.read = read; +const fs = __importStar(require("fs")); +function read(path, encoding = "utf-8") { + return fs.readFileSync(path, encoding); +} diff --git a/dist/ellipticcurve/utils/integer.d.ts b/dist/ellipticcurve/utils/integer.d.ts new file mode 100644 index 0000000..079a6be --- /dev/null +++ b/dist/ellipticcurve/utils/integer.d.ts @@ -0,0 +1,3 @@ +import { BigInteger } from "big-integer"; +export declare function modulo(x: BigInteger, n: BigInteger): BigInteger; +export declare function between(minimum: BigInteger, maximum: BigInteger): BigInteger; diff --git a/dist/ellipticcurve/utils/integer.js b/dist/ellipticcurve/utils/integer.js new file mode 100644 index 0000000..8b1ee9f --- /dev/null +++ b/dist/ellipticcurve/utils/integer.js @@ -0,0 +1,117 @@ +"use strict"; +// based on random-number-csprng: https://www.npmjs.com/package/random-number-csprng +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.modulo = modulo; +exports.between = between; +const big_integer_1 = __importDefault(require("big-integer")); +const crypto = __importStar(require("crypto")); +function modulo(x, n) { + let mod = x.divmod(n).remainder; + if (mod.lesser(0)) { + mod = mod.add(n); + } + return mod; +} +function calculateParameters(range) { + /* This does the equivalent of: + * + * bitsNeeded = Math.ceil(Math.log2(range)); + * bytesNeeded = Math.ceil(bitsNeeded / 8); + * mask = Math.pow(2, bitsNeeded) - 1; + * + * ... however, it implements it as bitwise operations, to sidestep any + * possible implementation errors regarding floating point numbers in + * JavaScript runtimes. This is an easier solution than assessing each + * runtime and architecture individually. + */ + let bitsNeeded = 0; + let bytesNeeded = 0; + let mask = (0, big_integer_1.default)(1); + while (range.greater(0)) { + if (bitsNeeded % 8 === 0) { + bytesNeeded += 1; + } + bitsNeeded += 1; + mask = mask.shiftLeft(1).or(1); /* 0x00001111 -> 0x00011111 */ + range = range.shiftRight(1); /* 0x01000000 -> 0x00100000 */ + } + return { bitsNeeded, bytesNeeded, mask }; +} +function between(minimum, maximum) { + if (!crypto || !crypto.randomBytes) { + throw new Error("No suitable random number generator available. Ensure that your runtime is linked against OpenSSL (or an equivalent) correctly."); + } + if (maximum.lesserOrEquals(minimum)) { + throw new Error("The maximum value must be higher than the minimum value."); + } + let range = maximum.minus(minimum); + let { bitsNeeded, bytesNeeded, mask } = calculateParameters(range); + let randomBytes = crypto.randomBytes(bytesNeeded); + let randomValue = (0, big_integer_1.default)(0); + /* Turn the random bytes into an integer, using bitwise operations. */ + for (let i = (0, big_integer_1.default)(0); i.lesser(bytesNeeded); i = i.add(1)) { + randomValue = randomValue.or((0, big_integer_1.default)(randomBytes[i.toJSNumber()]).shiftLeft((0, big_integer_1.default)(8).multiply(i))); + } + /* We apply the mask to reduce the amount of attempts we might need + * to make to get a number that is in range. This is somewhat like + * the commonly used 'modulo trick', but without the bias: + * + * "Let's say you invoke secure_rand(0, 60). When the other code + * generates a random integer, you might get 243. If you take + * (243 & 63)-- noting that the mask is 63-- you get 51. Since + * 51 is less than 60, we can return this without bias. If we + * got 255, then 255 & 63 is 63. 63 > 60, so we try again. + * + * The purpose of the mask is to reduce the number of random + * numbers discarded for the sake of ensuring an unbiased + * distribution. In the example above, 243 would discard, but + * (243 & 63) is in the range of 0 and 60." + * + * (Source: Scott Arciszewski) + */ + randomValue = randomValue.and(mask); + if (randomValue.lesserOrEquals(range)) { + /* We've been working with 0 as a starting point, so we need to + * add the `minimum` here. */ + return minimum.add(randomValue); + } + /* Outside of the acceptable range, throw it away and try again. + * We don't try any modulo tricks, as this would introduce bias. */ + return between(minimum, maximum); +} diff --git a/dist/ellipticcurve/utils/utils.d.ts b/dist/ellipticcurve/utils/utils.d.ts new file mode 100644 index 0000000..12719e1 --- /dev/null +++ b/dist/ellipticcurve/utils/utils.d.ts @@ -0,0 +1,2 @@ +import * as File from "./file"; +export { File }; diff --git a/dist/ellipticcurve/utils/utils.js b/dist/ellipticcurve/utils/utils.js new file mode 100644 index 0000000..0657ce6 --- /dev/null +++ b/dist/ellipticcurve/utils/utils.js @@ -0,0 +1,38 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.File = void 0; +const File = __importStar(require("./file")); +exports.File = File; diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..0158190 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,6 @@ +import { Signature } from "./ellipticcurve/signature"; +import { PublicKey } from "./ellipticcurve/publicKey"; +import { PrivateKey } from "./ellipticcurve/privateKey"; +import * as Ecdsa from "./ellipticcurve/ecdsa"; +import * as utils from "./ellipticcurve/utils/utils"; +export { Signature, PublicKey, PrivateKey, Ecdsa, utils }; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..45fc249 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,46 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.utils = exports.Ecdsa = exports.PrivateKey = exports.PublicKey = exports.Signature = void 0; +const signature_1 = require("./ellipticcurve/signature"); +Object.defineProperty(exports, "Signature", { enumerable: true, get: function () { return signature_1.Signature; } }); +const publicKey_1 = require("./ellipticcurve/publicKey"); +Object.defineProperty(exports, "PublicKey", { enumerable: true, get: function () { return publicKey_1.PublicKey; } }); +const privateKey_1 = require("./ellipticcurve/privateKey"); +Object.defineProperty(exports, "PrivateKey", { enumerable: true, get: function () { return privateKey_1.PrivateKey; } }); +const Ecdsa = __importStar(require("./ellipticcurve/ecdsa")); +exports.Ecdsa = Ecdsa; +const utils = __importStar(require("./ellipticcurve/utils/utils")); +exports.utils = utils; diff --git a/dist/test/message.txt b/dist/test/message.txt new file mode 100644 index 0000000..3211ff2 --- /dev/null +++ b/dist/test/message.txt @@ -0,0 +1 @@ +This is a test message diff --git a/dist/test/privateKey.pem b/dist/test/privateKey.pem new file mode 100644 index 0000000..39d3aaa --- /dev/null +++ b/dist/test/privateKey.pem @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BgUrgQQACg== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHQCAQEEIDODNlJus6PY4GYI5yARoZ3Udq/Gf4yclRO4eB176anvoAcGBSuBBAAK +oUQDQgAEZB4OGj5p8jKOeSbz0DhoopXR0nIpiAEjYNkmM7/ua2dvyMRCJIuDMGQ6 +jKZXlvn4L4FqaU1ZhXFKIo55Oj3O4w== +-----END EC PRIVATE KEY----- diff --git a/dist/test/publicKey.pem b/dist/test/publicKey.pem new file mode 100644 index 0000000..9a502e2 --- /dev/null +++ b/dist/test/publicKey.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEZB4OGj5p8jKOeSbz0DhoopXR0nIpiAEj +YNkmM7/ua2dvyMRCJIuDMGQ6jKZXlvn4L4FqaU1ZhXFKIo55Oj3O4w== +-----END PUBLIC KEY----- diff --git a/dist/test/signatureDer.txt b/dist/test/signatureDer.txt new file mode 100644 index 0000000..77b958a Binary files /dev/null and b/dist/test/signatureDer.txt differ diff --git a/dist/test/test.d.ts b/dist/test/test.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/test/test.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/test/test.js b/dist/test/test.js new file mode 100644 index 0000000..10eff09 --- /dev/null +++ b/dist/test/test.js @@ -0,0 +1,202 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const assert_1 = require("assert"); +const big_integer_1 = __importDefault(require("big-integer")); +const Ecdsa = __importStar(require("../ellipticcurve/ecdsa")); +const privateKey_1 = require("../ellipticcurve/privateKey"); +const publicKey_1 = require("../ellipticcurve/publicKey"); +const signature_1 = require("../ellipticcurve/signature"); +const File = __importStar(require("../ellipticcurve/utils/file")); +const path = __importStar(require("path")); +const TEST_DIR = path.join(__dirname, ".."); +describe("ECDSA test", function () { + describe("#testVerifyRightMessage()", function () { + it("should confirm authenticity", function () { + let privateKey = new privateKey_1.PrivateKey(); + let publicKey = privateKey.publicKey(); + let message = "This is the right message"; + let signature = Ecdsa.sign(message, privateKey); + assert_1.strict.equal(Ecdsa.verify(message, signature, publicKey), true); + }); + }); + describe("#testVerifyWrongMessage()", function () { + it("should deny authenticity", function () { + let privateKey = new privateKey_1.PrivateKey(); + let publicKey = privateKey.publicKey(); + let message1 = "This is the right message"; + let message2 = "This is the wrong message"; + let signature = Ecdsa.sign(message1, privateKey); + assert_1.strict.equal(Ecdsa.verify(message2, signature, publicKey), false); + }); + }); + describe("#testZeroSignature()", function () { + it("should deny authenticity", function () { + let privateKey = new privateKey_1.PrivateKey(); + let publicKey = privateKey.publicKey(); + let message = "This is the right message"; + assert_1.strict.equal(Ecdsa.verify(message, new signature_1.Signature((0, big_integer_1.default)(0), (0, big_integer_1.default)(0)), publicKey), false); + }); + }); +}); +describe("openSSL test", function () { + describe("#testAssign()", function () { + it("should read and verify PEM file", function () { + // Generated by: openssl ecparam -name secp256k1 -genkey -out privateKey.pem + let privateKeyPem = File.read(path.join(TEST_DIR, "test", "privateKey.pem")); + let privateKey = privateKey_1.PrivateKey.fromPem(privateKeyPem); + let message = File.read(path.join(TEST_DIR, "test", "message.txt")); + let signature = Ecdsa.sign(message, privateKey); + let publicKey = privateKey.publicKey(); + assert_1.strict.equal(Ecdsa.verify(message, signature, publicKey), true); + }); + }); + describe("#testVerifySignature()", function () { + it("should read and verify signature file", function () { + // openssl ec -in privateKey.pem -pubout -out publicKey.pem + let publicKeyPem = File.read(path.join(TEST_DIR, "test", "publicKey.pem")); + // openssl dgst -sha256 -sign privateKey.pem -out signature.binary message.txt + let signatureDer = File.read(path.join(TEST_DIR, "test", "signatureDer.txt"), "binary"); + let message = File.read(path.join(TEST_DIR, "test", "message.txt")); + let publicKey = publicKey_1.PublicKey.fromPem(publicKeyPem); + let signature = signature_1.Signature.fromDer(signatureDer); + assert_1.strict.equal(Ecdsa.verify(message, signature, publicKey), true); + }); + }); +}); +describe("PrivateKey test", function () { + describe("#testPemConversion()", function () { + it("should validate PEM generation and convertion", function () { + let privateKey1 = new privateKey_1.PrivateKey(); + let pem = privateKey1.toPem(); + let privateKey2 = privateKey_1.PrivateKey.fromPem(pem); + assert_1.strict.equal(String(privateKey1.secret), String(privateKey2.secret)); + assert_1.strict.equal(String(privateKey1.curve), String(privateKey2.curve)); + }); + }); + describe("#testDerConversion()", function () { + it("should validate DER generation and convertion", function () { + let privateKey1 = new privateKey_1.PrivateKey(); + let der = privateKey1.toDer(); + let privateKey2 = privateKey_1.PrivateKey.fromDer(der); + assert_1.strict.equal(String(privateKey1.secret), String(privateKey2.secret)); + assert_1.strict.equal(String(privateKey1.curve), String(privateKey2.curve)); + }); + }); + describe("#testStringConversion()", function () { + it("should validate private-key-string generation and convertion", function () { + let privateKey1 = new privateKey_1.PrivateKey(); + let string = privateKey1.toString(); + let privateKey2 = privateKey_1.PrivateKey.fromString(string); + assert_1.strict.equal(String(privateKey1.secret), String(privateKey2.secret)); + assert_1.strict.equal(String(privateKey1.curve), String(privateKey2.curve)); + }); + }); +}); +describe("PublicKey test", function () { + describe("#testPemConversion()", function () { + it("should validate PEM generation and convertion", function () { + let privateKey = new privateKey_1.PrivateKey(); + let publicKey1 = privateKey.publicKey(); + let pem = publicKey1.toPem(); + let publicKey2 = publicKey_1.PublicKey.fromPem(pem); + assert_1.strict.equal(String(publicKey1.point.x), String(publicKey2.point.x)); + assert_1.strict.equal(String(publicKey1.point.y), String(publicKey2.point.y)); + assert_1.strict.equal(publicKey1.curve, publicKey2.curve); + }); + }); + describe("#testDerConversion()", function () { + it("should validate DER generation and convertion", function () { + let privateKey = new privateKey_1.PrivateKey(); + let publicKey1 = privateKey.publicKey(); + let der = publicKey1.toDer(); + let publicKey2 = publicKey_1.PublicKey.fromDer(der); + assert_1.strict.equal(String(publicKey1.point.x), String(publicKey2.point.x)); + assert_1.strict.equal(String(publicKey1.point.y), String(publicKey2.point.y)); + assert_1.strict.equal(publicKey1.curve, publicKey2.curve); + }); + }); + describe("#testStringConversion()", function () { + it("should validate private-key-string generation and convertion", function () { + let privateKey = new privateKey_1.PrivateKey(); + let publicKey1 = privateKey.publicKey(); + let string = publicKey1.toString(); + let publicKey2 = publicKey_1.PublicKey.fromString(string); + assert_1.strict.equal(String(publicKey1.point.x), String(publicKey2.point.x)); + assert_1.strict.equal(String(publicKey1.point.y), String(publicKey2.point.y)); + assert_1.strict.equal(publicKey1.curve, publicKey2.curve); + }); + }); +}); +describe("Signature test", function () { + describe("#testDerConversion()", function () { + it("should validate DER signature generation and convertion", function () { + let privateKey = new privateKey_1.PrivateKey(); + let message = "This is a text message"; + let signature1 = Ecdsa.sign(message, privateKey); + let der = signature1.toDer(); + let signature2 = signature_1.Signature.fromDer(der); + assert_1.strict.equal(String(signature1.r), String(signature2.r)); + assert_1.strict.equal(String(signature1.s), String(signature2.s)); + }); + }); + describe("#testBase64Conversion()", function () { + it("should validate Base64 signature generation and convertion", function () { + let privateKey = new privateKey_1.PrivateKey(); + let message = "This is a text message"; + let signature1 = Ecdsa.sign(message, privateKey); + let base64 = signature1.toBase64(); + let signature2 = signature_1.Signature.fromBase64(base64); + assert_1.strict.equal(String(signature1.r), String(signature2.r)); + assert_1.strict.equal(String(signature1.s), String(signature2.s)); + }); + }); + describe("#testExternalRandNum()", function () { + it("should confirm authenticity and same signature", function () { + let privateKey = new privateKey_1.PrivateKey(); + let publicKey = privateKey.publicKey(); + let message = "This is a message"; + let signature1 = Ecdsa.sign(message, privateKey, undefined, (0, big_integer_1.default)(123)); + let signature2 = Ecdsa.sign(message, privateKey, undefined, (0, big_integer_1.default)(123)); + assert_1.strict.equal(Ecdsa.verify(message, signature1, publicKey), true); + assert_1.strict.equal(Ecdsa.verify(message, signature2, publicKey), true); + assert_1.strict.equal(signature1.r.toString(), signature2.r.toString()); + assert_1.strict.equal(signature1.s.toString(), signature2.s.toString()); + }); + }); +}); diff --git a/ellipticcurve/curve.js b/ellipticcurve/curve.js deleted file mode 100644 index 2817c0d..0000000 --- a/ellipticcurve/curve.js +++ /dev/null @@ -1,86 +0,0 @@ -// -// Elliptic Curve Equation -// -// y^2 = x^3 + A*x + B (mod P) -// - -const BigInt = require("big-integer"); -const Point = require("./point").Point; -const modulo = require("./utils/integer").modulo; - - -class CurveFp { - constructor(A, B, P, N, Gx, Gy, name, oid, nistName=null) { - this.A = A; - this.B = B; - this.P = P; - this.N = N; - this.G = new Point(Gx, Gy); - this.name = name; - this.nistName = nistName; - this._oid = oid; - }; - - contains(p) { - if (p.x < 0 || p.x > this.P.minus(1)) { - return false; - } - if (p.y < 0 || p.y > this.P.minus(1)) { - return false; - } - if (!modulo(((p.y.pow(2)).minus((p.x.pow(3)).add(this.A.multiply(p.x)).add(this.B))), this.P).equals(0)) { - return false; - } - return true; - }; - - length() { - return Math.floor((1 + this.N.toString(16).length) / 2); - }; - - get oid() { - return this._oid.slice(); - } -}; - - -let secp256k1 = new CurveFp( - BigInt("0000000000000000000000000000000000000000000000000000000000000000", 16), - BigInt("0000000000000000000000000000000000000000000000000000000000000007", 16), - BigInt("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", 16), - BigInt("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16), - BigInt("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", 16), - BigInt("483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", 16), - "secp256k1", - [1, 3, 132, 0, 10] -); - -let prime256v1 = new CurveFp( - BigInt("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), - BigInt("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16), - BigInt("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16), - BigInt("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16), - BigInt("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16), - BigInt("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16), - "prime256v1", - [1, 2, 840, 10045, 3, 1, 7], - "P-256" -); - -let p256 = prime256v1; - -let supportedCurves = [ - secp256k1, - prime256v1, -]; - -let curvesByOid = {}; -supportedCurves.forEach((curve) => {curvesByOid[curve.oid] = curve}); - - -exports.CurveFp = CurveFp; -exports.curvesByOid = curvesByOid; -exports.secp256k1 = secp256k1 -exports.prime256v1 = prime256v1 -exports.p256 = p256 -exports.supportedCurves = supportedCurves; diff --git a/ellipticcurve/ecdsa.js b/ellipticcurve/ecdsa.js deleted file mode 100644 index 21f86a6..0000000 --- a/ellipticcurve/ecdsa.js +++ /dev/null @@ -1,51 +0,0 @@ -const sha256 = require("js-sha256"); -const BigInt = require("big-integer"); - -const EcdsaMath = require("./math"); -const Signature = require("./signature").Signature; -const BinaryAscii = require("./utils/binary"); -const Integer = require("./utils/integer"); -const randomInteger = Integer.between; -const modulo = Integer.modulo; - - -exports.sign = function (message, privateKey, hashfunc = null, randNum = null) { - if (hashfunc == null) { - hashfunc = sha256; - } - let hashMessage = hashfunc(message); - let numberMessage = BinaryAscii.numberFromHex(hashMessage); - let curve = privateKey.curve; - if (randNum == null) { - randNum = randomInteger(BigInt(1), curve.N.minus(1)); - } - let randSignPoint = EcdsaMath.multiply(curve.G, randNum, curve.N, curve.A, curve.P); - let r = modulo(randSignPoint.x, curve.N); - let s = modulo((numberMessage.add(r.multiply(privateKey.secret)).multiply(EcdsaMath.inv(randNum, curve.N))), curve.N); - return new Signature(r, s); -}; - - -exports.verify = function (message, signature, publicKey, hashfunc=sha256) { - let hashMessage = hashfunc(message); - let numberMessage = BinaryAscii.numberFromHex(hashMessage); - let curve = publicKey.curve; - let sigR = signature.r; - let sigS = signature.s; - - if (sigR < 1 || sigR >= curve.N) { - return false; - } - if (sigS < 1 || sigS >= curve.N) { - return false; - } - - let inv = EcdsaMath.inv(sigS, curve.N); - let u1 = EcdsaMath.multiply(curve.G, modulo((numberMessage.multiply(inv)), curve.N), curve.N, curve.A, curve.P); - let u2 = EcdsaMath.multiply(publicKey.point, modulo((sigR.multiply(inv)), curve.N), curve.N, curve.A, curve.P); - let v = EcdsaMath.add(u1, u2, curve.A, curve.P); - if (v.isAtInfinity()) { - return false; - } - return v.x.mod(curve.N).eq(sigR); -}; diff --git a/ellipticcurve/math.js b/ellipticcurve/math.js deleted file mode 100644 index a85fe7d..0000000 --- a/ellipticcurve/math.js +++ /dev/null @@ -1,190 +0,0 @@ -const Point = require("./point").Point; -const modulo = require("./utils/integer").modulo; -const BigInt = require("big-integer"); - - -var multiply = function (p, n, N, A, P) { - // Fast way to multily point and scalar in elliptic curves - - // :param p: First Point to mutiply - // :param n: Scalar to mutiply - // :param N: Order of the elliptic curve - // :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p) - // :param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p) - // :return: Point that represents the sum of First and Second Point - - return fromJacobian(jacobianMultiply(toJacobian(p), n, N, A, P), P); -}; - - -var add = function (p, q, A, P) { - // Fast way to add two points in elliptic curves - - // :param p: First Point you want to add - // :param q: Second Point you want to add - // :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p) - // :param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p) - // :return: Point that represents the sum of First and Second Point - - return fromJacobian(jacobianAdd(toJacobian(p), toJacobian(q), A, P), P); -}; - - -var inv = function (x, n) { - // Extended Euclidean Algorithm. It's the 'division' in elliptic curves - - // :param x: Divisor - // :param n: Mod for division - // :return: Value representing the division - - if (x.eq(0)) { - return BigInt(0); - }; - - let lm = BigInt(1); - let hm = BigInt(0); - - let low = modulo(x, n); - let high = n; - let r, nm, newLow; - - while (low.greater(1)) { - r = high.over(low); // bigint division floors result automaticaly - - nm = hm.minus(lm.multiply(r)); - newLow = high.minus(low.multiply(r)); - - high = low; - hm = lm; - low = newLow; - lm = nm; - }; - - return modulo(lm, n); -}; - - -var toJacobian = function (p) { - // Convert point to Jacobian coordinates - - // :param p: First Point you want to add - // :return: Point in Jacobian coordinates - - return new Point(p.x, p.y, BigInt(1)); -}; - - -var fromJacobian = function (p, P) { - // Convert point back from Jacobian coordinates - - // :param p: First Point you want to add - // :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p) - // :return: Point in default coordinates - - var z = inv(p.z, P); - - var point = new Point( - modulo(p.x.multiply(z.pow(2)), P), - modulo(p.y.multiply(z.pow(3)), P) - ); - - return point; -}; - - -var jacobianDouble = function (p, A, P) { - // Double a point in elliptic curves - - // :param p: Point you want to double - // :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p) - // :param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p) - // :return: Point that represents the sum of First and Second Point - - if (p.y == 0) { - return new Point(BigInt(0), BigInt(0), BigInt(0)); - }; - let ysq = modulo((p.y.pow(2)), P); - let S = modulo((p.x.multiply(ysq).multiply(4)), P); - let M = modulo((((p.x.pow(2)).multiply(3)).add(A.multiply(p.z.pow(4)))), P); - let nx = modulo(((M.pow(2)).minus(S.multiply(2))), P); - let ny = modulo((M.multiply(S.minus(nx)).minus((ysq.pow(2)).multiply(8))), P); - let nz = modulo((p.y.multiply(p.z).multiply(2)), P); - - return new Point(nx, ny, nz); -}; - - -var jacobianAdd = function (p, q, A, P) { - // Add two points in elliptic curves - - // :param p: First Point you want to add - // :param q: Second Point you want to add - // :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p) - // :param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p) - // :return: Point that represents the sum of First and Second Point - - if (p.y == 0) { - return q; - }; - if (q.y == 0) { - return p; - }; - - let U1 = modulo(p.x.multiply(q.z.pow(2)), P); - let U2 = modulo(q.x.multiply(p.z.pow(2)), P); - let S1 = modulo(p.y.multiply(q.z.pow(3)), P); - let S2 = modulo(q.y.multiply(p.z.pow(3)), P); - - if (U1.eq(U2)) { - if (S1.neq(S2)) { - return Point(BigInt(0), BigInt(0), BigInt(1)); - }; - return jacobianDouble(p, A, P); - }; - - let H = U2.minus(U1); - let R = S2.minus(S1); - let H2 = modulo((H.multiply(H)), P); - let H3 = modulo((H.multiply(H2)), P); - let U1H2 = modulo((U1.multiply(H2)), P); - let nx = modulo(((R.pow(2)).minus(H3).minus(U1H2.multiply(2))), P); - let ny = modulo((R.multiply(U1H2.minus(nx)).minus(S1.multiply(H3))), P); - let nz = modulo((H.multiply(p.z).multiply(q.z)), P); - - return new Point(nx, ny, nz); -}; - - -var jacobianMultiply = function (p, n, N, A, P) { - // Multily point and scalar in elliptic curves - - // :param p: First Point to mutiply - // :param n: Scalar to mutiply - // :param N: Order of the elliptic curve - // :param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p) - // :param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p) - // :return: Point that represents the sum of First and Second Point - - if (p.y.eq(0) | n.eq(0)) { - return new Point(BigInt(0), BigInt(0), BigInt(1)); - }; - if (n.eq(1)) { - return p; - }; - if (n.lesser(0) | n.greaterOrEquals(N)) { - return jacobianMultiply(p, modulo(n, N), N, A, P); - }; - if (modulo(n, 2).eq(0)) { - return jacobianDouble(jacobianMultiply(p, n.over(2), N, A, P), A, P); // bigint division floors result automaticaly - }; - if (modulo(n, 2).eq(1)) { - return jacobianAdd(jacobianDouble(jacobianMultiply(p, n.over(2), N, A, P), A, P), p, A, P); // bigint division floors result automaticaly - }; - - throw new Error("logical failure: p: " + p + ", n: " + n + ", N: " + N + ", A: " + A + ", P: " + P); -}; - - -exports.multiply = multiply; -exports.add = add; -exports.inv = inv; diff --git a/ellipticcurve/point.js b/ellipticcurve/point.js deleted file mode 100644 index 2df80cc..0000000 --- a/ellipticcurve/point.js +++ /dev/null @@ -1,17 +0,0 @@ -const BigInt = require("big-integer"); - - -class Point { - constructor (x=BigInt(0), y=BigInt(0), z=BigInt(0)) { - this.x = x; - this.y = y; - this.z = z; - } - - isAtInfinity() { - return this.y == 0; - } -} - - -exports.Point = Point; diff --git a/ellipticcurve/privateKey.js b/ellipticcurve/privateKey.js deleted file mode 100644 index fcd469e..0000000 --- a/ellipticcurve/privateKey.js +++ /dev/null @@ -1,112 +0,0 @@ -const PublicKey = require("./publicKey").PublicKey; -const RandomInteger = require("./utils/integer"); -const BinaryAscii = require("./utils/binary"); -const EcdsaCurve = require("./curve"); -const EcdsaMath = require("./math"); -const BigInt = require("big-integer"); -const der = require("./utils/der"); - -const hexAt = "\x00"; - - -class PrivateKey { - constructor(curve=EcdsaCurve.secp256k1, secret=null) { - this.curve = curve; - if (secret) { - this.secret = secret; - } else { - this.secret = RandomInteger.between(BigInt(1), curve.N.minus(1)); - } - }; - - publicKey () { - let curve = this.curve; - let publicPoint = EcdsaMath.multiply(curve.G, this.secret, curve.N, curve.A, curve.P); - return new PublicKey(publicPoint, curve); - }; - - toString () { - return BinaryAscii.stringFromNumber(this.secret, this.curve.length()); - }; - - toDer () { - let encodedPublicKey = this.publicKey().toString(true); - - return der.encodeSequence( - der.encodeInteger(BigInt(1)), - der.encodeOctetString(this.toString()), - der.encodeConstructed(0, der.encodeOid(this.curve.oid)), - der.encodeConstructed(1, der.encodeBitstring(encodedPublicKey)) - ); - } - - toPem () { - return der.toPem(this.toDer(), "EC PRIVATE KEY"); - }; - - static fromPem (string) { - let privateKeyPem = string.split("-----BEGIN EC PRIVATE KEY-----")[1]; - return this.fromDer(der.fromPem(privateKeyPem)); - }; - - static fromDer(string) { - let result = der.removeSequence(string); - let t = result[0]; - let empty = result[1]; - if (empty) { - throw new Error("trailing junk after DER private key: " + BinaryAscii.hexFromBinary(empty)); - }; - - result = der.removeInteger(t); - let one = result[0]; - t = result[1]; - if (one != 1) { - throw new Error("expected '1' at start of DER private key, got " + one); - }; - - result = der.removeOctetString(t); - let privateKeyStr = result[0]; - t = result[1]; - - result = der.removeConstructed(t); - let tag = result[0]; - let curveOidStr = result[1]; - t = result[2]; - - if (tag != 0) { - throw new Error("expected tag 0 in DER private key, got " + tag); - }; - - result = der.removeObject(curveOidStr); - let oidCurve = result[0]; - empty = result[1]; - - if (empty) { - throw new Error("trailing junk after DER private key curve_oid: " + BinaryAscii.hexFromBinary(empty)); - }; - - let curve = EcdsaCurve.curvesByOid[oidCurve]; - - if (!curve) { - let supportedCurvesNames = []; - EcdsaCurve.supportedCurves.forEach((x) => {supportedCurvesNames.push(x.name)}) - throw new Error( - "Unknown curve with oid " + oidCurve - + ". Only the following are available: " + supportedCurvesNames - ); - }; - - if (privateKeyStr.length < curve.length()) { - privateKeyStr = hexAt.repeat(curve.length() - privateKeyStr.length) + privateKeyStr; - }; - - return this.fromString(privateKeyStr, curve); - }; - - static fromString (string, curve=EcdsaCurve.secp256k1) { - return new PrivateKey(curve, BinaryAscii.numberFromString(string)); - }; -}; - - -exports.PrivateKey = PrivateKey; diff --git a/ellipticcurve/publicKey.js b/ellipticcurve/publicKey.js deleted file mode 100644 index e19f7db..0000000 --- a/ellipticcurve/publicKey.js +++ /dev/null @@ -1,108 +0,0 @@ -const BinaryAscii = require("./utils/binary"); -const EcdsaCurve = require("./curve"); -const Point = require("./point").Point; -const der = require("./utils/der"); -const Math = require("./math"); - - -class PublicKey { - - constructor (point, curve) { - this.point = point; - this.curve = curve; - }; - - toString (encoded=false) { - let xString = BinaryAscii.stringFromNumber(this.point.x, this.curve.length()); - let yString = BinaryAscii.stringFromNumber(this.point.y, this.curve.length()); - if (encoded) { - return "\x00\x04" + xString + yString; - } - return xString + yString; - }; - - toDer () { - let encodeEcAndOid = der.encodeSequence(der.encodeOid([1, 2, 840, 10045, 2, 1]), der.encodeOid(this.curve.oid)); - - return der.encodeSequence(encodeEcAndOid, der.encodeBitstring(this.toString(true))) - }; - - toPem () { - return der.toPem(this.toDer(), "PUBLIC KEY") - }; - - static fromPem (string) { - return this.fromDer(der.fromPem(string)); - }; - - static fromDer (string) { - let result = der.removeSequence(string); - let s1 = result[0]; - let empty = result[1]; - if (empty) { - throw new Error("trailing junk after DER public key: " + BinaryAscii.hexFromBinary(empty)); - }; - - result = der.removeSequence(s1); - let s2 = result[0]; - let pointBitString = result[1]; - - result = der.removeObject(s2); - let rest = result[1]; - - result = der.removeObject(rest); - let oidCurve = result[0]; - empty = result[1]; - if (empty) { - throw new Error("trailing junk after DER public key objects: " + BinaryAscii.hexFromBinary(empty)); - }; - - let curve = EcdsaCurve.curvesByOid[oidCurve]; - if (!curve) { - let supportedCurvesNames = []; - EcdsaCurve.supportedCurves.forEach((x) => {supportedCurvesNames.push(x.name)}) - throw new Error( - "Unknown curve with oid " - + oidCurve - + ". Only the following are available: " - + supportedCurvesNames - ); - }; - - result = der.removeBitString(pointBitString); - let pointStr = result[0]; - empty = result[1]; - if (empty) { - throw new Error("trailing junk after public key point-string: " + BinaryAscii.hexFromBinary(empty)); - }; - - return this.fromString(pointStr.slice(2), curve); - }; - - static fromString (string, curve=EcdsaCurve.secp256k1, validatePoint=true) { - let baseLen = curve.length(); - - let xs = string.slice(null, baseLen); - let ys = string.slice(baseLen); - - let p = new Point(BinaryAscii.numberFromString(xs), BinaryAscii.numberFromString(ys)); - - let publicKey = new PublicKey(p, curve); - if (!validatePoint) { - return publicKey; - } - if (p.isAtInfinity()) { - throw new Error("Public Key point is at infinity"); - } - if (!curve.contains(p)) { - throw new Error("point (" + p.x + "," + p.y + ") is not valid for curve " + curve.name); - } - if (!Math.multiply(p, curve.N, curve.N, curve.A, curve.P).isAtInfinity()) { - throw new Error("Point (" + p.x + "," + p.y + " * " + curve.name + ".N is not at infinity"); - } - return publicKey - }; -}; - - -exports.PublicKey = PublicKey; diff --git a/ellipticcurve/signature.js b/ellipticcurve/signature.js deleted file mode 100644 index 46498ed..0000000 --- a/ellipticcurve/signature.js +++ /dev/null @@ -1,50 +0,0 @@ -const BinaryAscii = require("./utils/binary") -const Base64 = require("./utils/base") -const der = require("./utils/der") - - -class Signature { - constructor (r, s) { - this.r = r; - this.s = s; - } - - toDer () { - return der.encodeSequence(der.encodeInteger(this.r), der.encodeInteger(this.s)); - } - - toBase64 () { - return Base64.encode(this.toDer()); - } - - static fromDer (string) { - let result = der.removeSequence(string); - let rs = result[0]; - let empty = result[1]; - if (empty) { - throw new Error("trailing junk after DER signature: " + BinaryAscii.hexFromBinary(empty)); - } - - result = der.removeInteger(rs); - let r = result[0]; - let rest = result[1]; - - result = der.removeInteger(rest); - let s = result[0]; - empty = result[1]; - - if (empty) { - throw new Error("trailing junk after DER numbers: " + BinaryAscii.hexFromBinary(empty)); - } - - return new Signature(r, s) - } - - static fromBase64 (string) { - let derString = Base64.decode(string); - return this.fromDer(derString); - } -} - - -exports.Signature = Signature; diff --git a/ellipticcurve/utils/base.js b/ellipticcurve/utils/base.js deleted file mode 100644 index b11ef28..0000000 --- a/ellipticcurve/utils/base.js +++ /dev/null @@ -1,8 +0,0 @@ -exports.decode = function (string) { - return Buffer.from(string, "base64").toString("binary"); -}; - - -exports.encode = function (string) { - return Buffer.from(string, "binary").toString("base64"); -}; diff --git a/ellipticcurve/utils/der.js b/ellipticcurve/utils/der.js deleted file mode 100644 index 284021e..0000000 --- a/ellipticcurve/utils/der.js +++ /dev/null @@ -1,337 +0,0 @@ -const Base64 = require("./base"); -const BinaryAscii = require("./binary"); -const modulo = require("./integer").modulo; -const BigInt = require("big-integer"); - - -const hexAt = "\x00"; -const hexB = "\x02"; -const hexC = "\x03"; -const hexD = "\x04"; -const hexF = "\x06"; -const hex0 = "\x30"; - -const hex31 = 0x1f; -const hex127 = 0x7f; -const hex129 = 0xa0; -const hex160 = 0x80; -const hex224 = 0xe0; - -const bytesHex0 = Buffer.from(hex0).toString('binary'); -const bytesHexB = Buffer.from(hexB).toString('binary'); -const bytesHexC = Buffer.from(hexC).toString('binary'); -const bytesHexD = Buffer.from(hexD).toString('binary'); -const bytesHexF = Buffer.from(hexF).toString('binary'); - - -exports.encodeSequence = function () { - let sequence = []; - let totalLengthLen = 0; - for (let i = 0; i < arguments.length; i++) { - sequence.push(arguments[i]); - totalLengthLen += arguments[i].length; - } - return hex0 + _encodeLength(totalLengthLen) + sequence.join(""); -} - - -exports.encodeInteger = function (x) { - if (x.lesser(0)) { - throw new Error("x cannot be negative"); - } - - let t = x.toString(16); - - if (t.length % 2) { - t = "0" + t; - } - - x = BinaryAscii.binaryFromHex(t); - - let num; - if (typeof(x[0]) === "number") { - num = x[0]; - } else { - num = x.charCodeAt(0); - } - - if (num <= hex127) { - return hexB + String.fromCharCode(x.length) + x; - } - return hexB + String.fromCharCode(x.length + 1) + hexAt + x; -} - - -exports.encodeOid = function (pieces) { - let first = pieces.shift(); - let second = pieces.shift(); - - if (first > 2) { - throw new Error("first has to be <= 2"); - } - if (second > 39) { - throw new Error("second has to be <= 39"); - } - - let encodedPieces = []; - - pieces.forEach((x) => {encodedPieces.push(_encodeNumber(x))}) - - let body = [String.fromCharCode(40 * first + second)].concat(encodedPieces).join(""); - - return hexF + _encodeLength(body.length) + body; -} - - -exports.encodeBitstring = function (t) { - return hexC + _encodeLength(t.length) + t; -} - - -exports.encodeOctetString = function (t) { - return hexD + _encodeLength(t.length) + t; -} - - -exports.encodeConstructed = function (tag, value) { - return String.fromCharCode(hex129 + tag) + _encodeLength(value.length) + value; -} - - -exports.removeSequence = function (string) { - _checkSequenceError(string, bytesHex0, "03"); - - let result = _readLength(string.slice(1)); - let length = result[0]; - let lengthLen = result[1]; - - let endSeq = 1 + lengthLen + length; - - return [string.slice(1 + lengthLen, endSeq), string.slice(endSeq)]; -} - - -exports.removeInteger = function (string) { - _checkSequenceError(string, bytesHexB, "02"); - - let result = _readLength(string.slice(1)); - let length = result[0]; - let lengthLen = result[1]; - - let numberBytes = string.slice(1 + lengthLen, 1 + lengthLen + length); - let rest = string.slice(1 + lengthLen + length); - let nBytes; - if (typeof(numberBytes[0]) === "number") { - nBytes = numberBytes[0]; - } else { - nBytes = numberBytes.charCodeAt(0); - } - - if (nBytes >= hex160) { - throw new Error("nBytes must be < 160"); - } - - return [BigInt(BinaryAscii.hexFromBinary(numberBytes), 16), rest]; -} - - -exports.removeObject = function (string) { - _checkSequenceError(string, bytesHexF, "06"); - - let result = _readLength(string.slice(1)); - let length = result[0]; - let lengthLen = result[1]; - - let body = string.slice(1 + lengthLen, 1 + lengthLen + length); - let rest = string.slice(1 + lengthLen + length); - - let numbers = []; - while (body) { - let results = _readNumber(body); - let n = results[0]; - let lengthLength = results[1]; - numbers.push(n); - body = body.slice(lengthLength); - } - - let n0 = numbers.shift(); - - let first = Math.floor(n0 / 40); - let second = n0 - (40 * first); - numbers = [first, second].concat(numbers); - - return [numbers, rest]; -} - - -exports.removeBitString = function (string) { - _checkSequenceError(string, bytesHexC, "03"); - - let result = _readLength(string.slice(1)); - let length = result[0]; - let lengthLen = result[1]; - - let body = string.slice(1 + lengthLen, 1 + lengthLen + length); - let rest = string.slice(1 + lengthLen + length); - - return [body, rest]; -} - - -exports.removeOctetString = function (string) { - _checkSequenceError(string, bytesHexD, "04"); - - let result = _readLength(string.slice(1)); - let length = result[0]; - let lengthLen = result[1]; - - let body = string.slice(1 + lengthLen, 1 + lengthLen + length); - let rest = string.slice(1 + lengthLen + length); - - return [body, rest]; -} - - -exports.removeConstructed = function (string) { - let s0 = _extractFirstInt(string); - if ((s0 & hex224) != hex129) { - throw new Error("wanted constructed tag (0xa0-0xbf), got 0x" + s0); - } - - let tag = s0 & hex31 - let result = _readLength(string.slice(1)); - let length = result[0]; - let lengthLen = result[1]; - - let body = string.slice(1 + lengthLen, 1 + lengthLen + length); - let rest = string.slice(1 + lengthLen + length); - - return [tag, body, rest]; -} - - -exports.fromPem = function (pem) { - let split = pem.split("\n"); - let stripped = ""; - - for (let i = 0; i < split.length; i++) { - if (!split[i].startsWith("-----")) { - stripped += split[i].trim(); - } - } - - return Base64.decode(stripped); -} - - -exports.toPem = function (der, name) { - let b64 = Base64.encode(der); - let lines = [("-----BEGIN " + name + "-----\n")]; - - for (let start = 0; start <= b64.length; start += 64) { - lines.push(b64.slice(start, start + 64) + "\n") - } - lines.push("-----END " + name + "-----\n"); - - return lines.join(""); -} - - -function _encodeLength (length) { - if (length < 0) { - throw new Error("length cannot be negative"); - } - - if (length < hex160) { - return String.fromCharCode(length); - } - - let s = length.toString(16); - - if (modulo(s.length, 2)) { - s = "0" + s; - } - - s = BinaryAscii.binaryFromHex(s); - let lengthLen = s.length; - - return String.fromCharCode(hex160 | lengthLen) + toString(lengthLen); -} - - -function _encodeNumber (n) { - let b128Digits = []; - while (n) { - b128Digits.unshift((n & hex127) | hex160); - n >>= 7; - } - - if (!b128Digits.length) { - b128Digits.push(0); - } - - b128Digits[b128Digits.length - 1] &= hex127; - - let encodedDigits = []; - b128Digits.forEach((d) => {encodedDigits.push(String.fromCharCode(d))}) - - return encodedDigits.join(""); -} - - -function _readLength (string) { - let num = _extractFirstInt(string); - if (!(num & hex160)) { - return [(num & hex127), 1]; - } - - let lengthLen = num & hex127; - - if (lengthLen > string.length - 1) { - throw new Error("ran out of length bytes"); - } - - return [parseInt(BinaryAscii.hexFromBinary(string.slice(1, 1 + lengthLen)), 16), 1 + lengthLen]; -} - - -function _readNumber (string) { - let number = 0; - let lengthLen = 0; - let d; - while (true) { - if (lengthLen > string.length) { - throw new Error("ran out of length bytes"); - } - - number <<= 7; - - d = string.charAt(lengthLen); - if (typeof(d) !== "number") { - d = d.charCodeAt(0); - } - - number += (d & hex127); - lengthLen += 1; - if (!(d & hex160)) { - break; - } - } - - return [number, lengthLen]; -} - - -function _checkSequenceError(string, start, expected) { - if (!string.startsWith(start)) { - throw new Error("wanted sequence (0x" + expected + "), got 0x" + _extractFirstInt(string).toString(16)); - } -} - - -function _extractFirstInt(string) { - if (typeof(string[0]) === "number") { - return string[0]; - } - return string.charCodeAt(0); -} diff --git a/ellipticcurve/utils/file.js b/ellipticcurve/utils/file.js deleted file mode 100644 index 19d4a39..0000000 --- a/ellipticcurve/utils/file.js +++ /dev/null @@ -1,6 +0,0 @@ -const fs = require("fs"); - - -exports.read = function(path, encoding='utf-8') { - return fs.readFileSync(path, encoding); -}; diff --git a/ellipticcurve/utils/integer.js b/ellipticcurve/utils/integer.js deleted file mode 100644 index 9e16213..0000000 --- a/ellipticcurve/utils/integer.js +++ /dev/null @@ -1,113 +0,0 @@ -// based on random-number-csprng: https://www.npmjs.com/package/random-number-csprng - -const BigInt = require("big-integer"); -const crypto = require("crypto"); - - -function modulo(x, n) { - let mod = x.divmod(n).remainder; - - if (mod.lesser(0)) { - mod = mod.add(n); - } - - return mod; -} - - -function calculateParameters(range) { - /* This does the equivalent of: - * - * bitsNeeded = Math.ceil(Math.log2(range)); - * bytesNeeded = Math.ceil(bitsNeeded / 8); - * mask = Math.pow(2, bitsNeeded) - 1; - * - * ... however, it implements it as bitwise operations, to sidestep any - * possible implementation errors regarding floating point numbers in - * JavaScript runtimes. This is an easier solution than assessing each - * runtime and architecture individually. - */ - - let bitsNeeded = 0; - let bytesNeeded = 0; - let mask = BigInt(1); - - while (range.greater(0)) { - if (bitsNeeded % 8 === 0) { - bytesNeeded += 1; - } - - bitsNeeded += 1; - mask = mask.shiftLeft(1).or(1); /* 0x00001111 -> 0x00011111 */ - - range = range.shiftRight(1); /* 0x01000000 -> 0x00100000 */ - } - - return {bitsNeeded, bytesNeeded, mask}; -} - - -function secureRandomNumber(minimum, maximum) { // bigint, bigint - if (crypto == null || crypto.randomBytes == null) { - throw new Error("No suitable random number generator available. Ensure that your runtime is linked against OpenSSL (or an equivalent) correctly."); - }; - - if (maximum.lesserOrEquals(minimum)) { - throw new Error("The maximum value must be higher than the minimum value.") - }; - - /* We hardcode the values for the following: - * - * https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER - * https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER - * - * ... as Babel does not appear to transpile them, despite being ES6. - */ - - let range = maximum.minus(minimum); - - let {bitsNeeded, bytesNeeded, mask} = calculateParameters(range); - - let randomBytes = crypto.randomBytes(bytesNeeded); - - var randomValue = BigInt(0); - - /* Turn the random bytes into an integer, using bitwise operations. */ - for (let i = BigInt(0); i.lesser(bytesNeeded); i = i.add(1)) { - randomValue = randomValue.or(BigInt(randomBytes[i]).shiftLeft(BigInt(8).multiply(i))); - } - - /* We apply the mask to reduce the amount of attempts we might need - * to make to get a number that is in range. This is somewhat like - * the commonly used 'modulo trick', but without the bias: - * - * "Let's say you invoke secure_rand(0, 60). When the other code - * generates a random integer, you might get 243. If you take - * (243 & 63)-- noting that the mask is 63-- you get 51. Since - * 51 is less than 60, we can return this without bias. If we - * got 255, then 255 & 63 is 63. 63 > 60, so we try again. - * - * The purpose of the mask is to reduce the number of random - * numbers discarded for the sake of ensuring an unbiased - * distribution. In the example above, 243 would discard, but - * (243 & 63) is in the range of 0 and 60." - * - * (Source: Scott Arciszewski) - */ - - randomValue = randomValue.and(mask); - - if (randomValue.lesserOrEquals(range)) { - /* We've been working with 0 as a starting point, so we need to - * add the `minimum` here. */ - return minimum.add(randomValue); - } - - /* Outside of the acceptable range, throw it away and try again. - * We don't try any modulo tricks, as this would introduce bias. */ - return secureRandomNumber(minimum, maximum); -}; - - -exports.between = secureRandomNumber; -exports.modulo = modulo; diff --git a/ellipticcurve/utils/utils.js b/ellipticcurve/utils/utils.js deleted file mode 100644 index 33302da..0000000 --- a/ellipticcurve/utils/utils.js +++ /dev/null @@ -1 +0,0 @@ -exports.File = require("./file") \ No newline at end of file diff --git a/index.js b/index.js deleted file mode 100644 index 7621cd5..0000000 --- a/index.js +++ /dev/null @@ -1,5 +0,0 @@ -exports.Signature = require("./ellipticcurve/signature").Signature; -exports.PublicKey = require("./ellipticcurve/publicKey").PublicKey; -exports.PrivateKey = require("./ellipticcurve/privateKey").PrivateKey; -exports.Ecdsa = require("./ellipticcurve/ecdsa"); -exports.utils = require("./ellipticcurve/utils/utils"); diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 2d86228..0000000 --- a/package-lock.json +++ /dev/null @@ -1,1635 +0,0 @@ -{ - "name": "starkbank-ecdsa", - "version": "1.1.5", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "starkbank-ecdsa", - "version": "1.1.5", - "license": "MIT License", - "dependencies": { - "big-integer": "^1.6.48", - "js-sha256": "^0.9.0" - }, - "devDependencies": { - "mocha": "^9.1.3" - } - }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/big-integer": { - "version": "1.6.48", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", - "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/js-sha256": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", - "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", - "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "4.2.1", - "ms": "2.1.3", - "nanoid": "3.3.1", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "big-integer": { - "version": "1.6.48", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", - "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==" - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "dependencies": { - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "js-sha256": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", - "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", - "dev": true, - "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "4.2.1", - "ms": "2.1.3", - "nanoid": "3.3.1", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - } - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } - } -} diff --git a/package.json b/package.json index 6c09864..c15ce52 100644 --- a/package.json +++ b/package.json @@ -1,39 +1,46 @@ { - "name": "starkbank-ecdsa", - "version": "1.1.5", - "description": "fast openSSL-compatible implementation of the Elliptic Curve Digital Signature Algorithm (ECDSA)", - "main": "index.js", + "name": "ecdsa-node-ts", + "version": "1.0.2", + "description": "TypeScript implementation of the Elliptic Curve Digital Signature Algorithm (ECDSA)", + "main": "dist/index.js", + "types": "dist/index.d.ts", "directories": { - "lib": "ellipticcurve", + "lib": "src/ellipticcurve", "test": "test" }, "scripts": { - "test": "mocha" + "build": "tsc", + "prepare": "npm run build", + "test": "tsc && mocha dist/test", + "clean": "rm -rf dist" }, "repository": { "type": "git", - "url": "git+https://github.com/starkbank/ecdsa-node.git" + "url": "git+https://github.com/quartoavista/ecdsa-node-ts.git" }, "keywords": [ "ecdsa", "signature", + "typescript", "fast", "jacobian", - "stark", - "starkbank", "openbanking" ], - "author": "Stark Bank", - "license": "MIT License", + "author": "quartoavista", + "license": "MIT", "bugs": { - "url": "https://github.com/starkbank/ecdsa-node/issues" + "url": "https://github.com/quartoavista/ecdsa-node-ts/issues" }, - "homepage": "https://github.com/starkbank/ecdsa-node#readme", + "homepage": "https://github.com/quartoavista/ecdsa-node-ts#readme", "dependencies": { "big-integer": "^1.6.48", "js-sha256": "^0.9.0" }, "devDependencies": { - "mocha": "^9.1.3" + "@types/big-integer": "^0.0.35", + "@types/mocha": "^10.0.10", + "@types/node": "^22.13.1", + "mocha": "^9.1.3", + "typescript": "^5.7.3" } } diff --git a/src/ellipticcurve/curve.ts b/src/ellipticcurve/curve.ts new file mode 100644 index 0000000..65bbbe1 --- /dev/null +++ b/src/ellipticcurve/curve.ts @@ -0,0 +1,135 @@ +// +// Elliptic Curve Equation +// +// y^2 = x^3 + A*x + B (mod P) +// + +import bigInt, { BigInteger } from "big-integer"; +import { Point } from "./point"; +import { modulo } from "./utils/integer"; + +export class CurveFp { + A: BigInteger; + B: BigInteger; + P: BigInteger; + N: BigInteger; + G: Point; + name: string; + nistName: string | null; + private _oid: number[]; + + constructor( + A: BigInteger, + B: BigInteger, + P: BigInteger, + N: BigInteger, + Gx: BigInteger, + Gy: BigInteger, + name: string, + oid: number[], + nistName: string | null = null + ) { + this.A = A; + this.B = B; + this.P = P; + this.N = N; + this.G = new Point(Gx, Gy); + this.name = name; + this.nistName = nistName; + this._oid = oid; + } + + contains(p: Point): boolean { + if (p.x.lesser(0) || p.x.greater(this.P.minus(1))) { + return false; + } + if (p.y.lesser(0) || p.y.greater(this.P.minus(1))) { + return false; + } + if ( + !modulo( + p.y.pow(2).minus(p.x.pow(3).add(this.A.multiply(p.x)).add(this.B)), + this.P + ).equals(0) + ) { + return false; + } + return true; + } + + length(): number { + return Math.floor((1 + this.N.toString(16).length) / 2); + } + + get oid(): number[] { + return [...this._oid]; + } +} + +export const secp256k1 = new CurveFp( + bigInt( + "0000000000000000000000000000000000000000000000000000000000000000", + 16 + ), + bigInt( + "0000000000000000000000000000000000000000000000000000000000000007", + 16 + ), + bigInt( + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + 16 + ), + bigInt( + "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + 16 + ), + bigInt( + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + 16 + ), + bigInt( + "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + 16 + ), + "secp256k1", + [1, 3, 132, 0, 10] +); + +export const prime256v1 = new CurveFp( + bigInt( + "ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", + 16 + ), + bigInt( + "5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", + 16 + ), + bigInt( + "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", + 16 + ), + bigInt( + "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", + 16 + ), + bigInt( + "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", + 16 + ), + bigInt( + "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", + 16 + ), + "prime256v1", + [1, 2, 840, 10045, 3, 1, 7], + "P-256" +); + +export const p256 = prime256v1; + +export const supportedCurves = [secp256k1, prime256v1]; + +export const curvesByOid: { [key: string]: CurveFp } = {}; +supportedCurves.forEach((curve) => { + curvesByOid[curve.oid.join(".")] = curve; +}); diff --git a/src/ellipticcurve/ecdsa.ts b/src/ellipticcurve/ecdsa.ts new file mode 100644 index 0000000..268b946 --- /dev/null +++ b/src/ellipticcurve/ecdsa.ts @@ -0,0 +1,87 @@ +import { sha256 } from "js-sha256"; +import bigInt, { BigInteger } from "big-integer"; +import * as EcdsaMath from "./math"; +import { Signature } from "./signature"; +import * as BinaryAscii from "./utils/binary"; +import * as Integer from "./utils/integer"; +import { PrivateKey } from "./privateKey"; +import { PublicKey } from "./publicKey"; + +const randomInteger = Integer.between; +const modulo = Integer.modulo; + +type HashFunction = (message: string | number[] | Uint8Array) => string; + +export function sign( + message: string, + privateKey: PrivateKey, + hashfunc: HashFunction = sha256, + randNum?: BigInteger +): Signature { + let hashMessage = hashfunc(message); + let numberMessage = BinaryAscii.numberFromHex(hashMessage); + let curve = privateKey.curve; + + if (!randNum) { + randNum = randomInteger(bigInt(1), curve.N.minus(1)); + } + + let randSignPoint = EcdsaMath.multiply( + curve.G, + randNum, + curve.N, + curve.A, + curve.P + ); + let r = modulo(randSignPoint.x, curve.N); + let s = modulo( + numberMessage + .add(r.multiply(privateKey.secret)) + .multiply(EcdsaMath.inv(randNum, curve.N)), + curve.N + ); + + return new Signature(r, s); +} + +export function verify( + message: string, + signature: Signature, + publicKey: PublicKey, + hashfunc: HashFunction = sha256 +): boolean { + let hashMessage = hashfunc(message); + let numberMessage = BinaryAscii.numberFromHex(hashMessage); + let curve = publicKey.curve; + let sigR = signature.r; + let sigS = signature.s; + + if (sigR.lesser(1) || sigR.greaterOrEquals(curve.N)) { + return false; + } + if (sigS.lesser(1) || sigS.greaterOrEquals(curve.N)) { + return false; + } + + let inv = EcdsaMath.inv(sigS, curve.N); + let u1 = EcdsaMath.multiply( + curve.G, + modulo(numberMessage.multiply(inv), curve.N), + curve.N, + curve.A, + curve.P + ); + let u2 = EcdsaMath.multiply( + publicKey.point, + modulo(sigR.multiply(inv), curve.N), + curve.N, + curve.A, + curve.P + ); + let v = EcdsaMath.add(u1, u2, curve.A, curve.P); + + if (v.isAtInfinity()) { + return false; + } + return v.x.mod(curve.N).eq(sigR); +} diff --git a/src/ellipticcurve/math.ts b/src/ellipticcurve/math.ts new file mode 100644 index 0000000..51bfb92 --- /dev/null +++ b/src/ellipticcurve/math.ts @@ -0,0 +1,151 @@ +import { Point } from "./point"; +import { modulo } from "./utils/integer"; +import bigInt, { BigInteger } from "big-integer"; + +export function multiply( + p: Point, + n: BigInteger, + N: BigInteger, + A: BigInteger, + P: BigInteger +): Point { + // Fast way to multiply point and scalar in elliptic curves + return fromJacobian(jacobianMultiply(toJacobian(p), n, N, A, P), P); +} + +export function add(p: Point, q: Point, A: BigInteger, P: BigInteger): Point { + // Fast way to add two points in elliptic curves + return fromJacobian(jacobianAdd(toJacobian(p), toJacobian(q), A, P), P); +} + +export function inv(x: BigInteger, n: BigInteger): BigInteger { + // Extended Euclidean Algorithm. It's the 'division' in elliptic curves + if (x.eq(0)) { + return bigInt(0); + } + + let lm = bigInt(1); + let hm = bigInt(0); + + let low = modulo(x, n); + let high = n; + let r: BigInteger, nm: BigInteger, newLow: BigInteger; + + while (low.greater(1)) { + r = high.over(low); // bigint division floors result automatically + + nm = hm.minus(lm.multiply(r)); + newLow = high.minus(low.multiply(r)); + + high = low; + hm = lm; + low = newLow; + lm = nm; + } + + return modulo(lm, n); +} + +function toJacobian(p: Point): Point { + // Convert point to Jacobian coordinates + return new Point(p.x, p.y, bigInt(1)); +} + +function fromJacobian(p: Point, P: BigInteger): Point { + // Convert point back from Jacobian coordinates + let z = inv(p.z, P); + + return new Point( + modulo(p.x.multiply(z.pow(2)), P), + modulo(p.y.multiply(z.pow(3)), P) + ); +} + +function jacobianDouble(p: Point, A: BigInteger, P: BigInteger): Point { + // Double a point in elliptic curves + if (p.y.equals(0)) { + return new Point(bigInt(0), bigInt(0), bigInt(0)); + } + + let ysq = modulo(p.y.pow(2), P); + let S = modulo(p.x.multiply(ysq).multiply(4), P); + let M = modulo( + p.x + .pow(2) + .multiply(3) + .add(A.multiply(p.z.pow(4))), + P + ); + let nx = modulo(M.pow(2).minus(S.multiply(2)), P); + let ny = modulo(M.multiply(S.minus(nx)).minus(ysq.pow(2).multiply(8)), P); + let nz = modulo(p.y.multiply(p.z).multiply(2), P); + + return new Point(nx, ny, nz); +} + +function jacobianAdd(p: Point, q: Point, A: BigInteger, P: BigInteger): Point { + // Add two points in elliptic curves + if (p.y.equals(0)) { + return q; + } + if (q.y.equals(0)) { + return p; + } + + let U1 = modulo(p.x.multiply(q.z.pow(2)), P); + let U2 = modulo(q.x.multiply(p.z.pow(2)), P); + let S1 = modulo(p.y.multiply(q.z.pow(3)), P); + let S2 = modulo(q.y.multiply(p.z.pow(3)), P); + + if (U1.eq(U2)) { + if (S1.neq(S2)) { + return new Point(bigInt(0), bigInt(0), bigInt(1)); + } + return jacobianDouble(p, A, P); + } + + let H = U2.minus(U1); + let R = S2.minus(S1); + let H2 = modulo(H.multiply(H), P); + let H3 = modulo(H.multiply(H2), P); + let U1H2 = modulo(U1.multiply(H2), P); + let nx = modulo(R.pow(2).minus(H3).minus(U1H2.multiply(2)), P); + let ny = modulo(R.multiply(U1H2.minus(nx)).minus(S1.multiply(H3)), P); + let nz = modulo(H.multiply(p.z).multiply(q.z), P); + + return new Point(nx, ny, nz); +} + +function jacobianMultiply( + p: Point, + n: BigInteger, + N: BigInteger, + A: BigInteger, + P: BigInteger +): Point { + // Multiply point and scalar in elliptic curves + if (p.y.equals(0) || n.eq(0)) { + return new Point(bigInt(0), bigInt(0), bigInt(1)); + } + if (n.eq(1)) { + return p; + } + if (n.lesser(0) || n.greaterOrEquals(N)) { + return jacobianMultiply(p, modulo(n, N), N, A, P); + } + if (modulo(n, bigInt(2)).eq(0)) { + return jacobianDouble(jacobianMultiply(p, n.over(2), N, A, P), A, P); + } + if (modulo(n, bigInt(2)).eq(1)) { + return jacobianAdd( + jacobianDouble(jacobianMultiply(p, n.over(2), N, A, P), A, P), + p, + A, + P + ); + } + + throw new Error( + `logical failure: p: ${p}, n: ${n}, N: ${N}, A: ${A}, P: ${P}` + ); +} diff --git a/src/ellipticcurve/point.ts b/src/ellipticcurve/point.ts new file mode 100644 index 0000000..85b8762 --- /dev/null +++ b/src/ellipticcurve/point.ts @@ -0,0 +1,21 @@ +import bigInt, { BigInteger } from "big-integer"; + +export class Point { + x: BigInteger; + y: BigInteger; + z: BigInteger; + + constructor( + x: BigInteger = bigInt(0), + y: BigInteger = bigInt(0), + z: BigInteger = bigInt(0) + ) { + this.x = x; + this.y = y; + this.z = z; + } + + isAtInfinity(): boolean { + return this.y.equals(0); + } +} diff --git a/src/ellipticcurve/privateKey.ts b/src/ellipticcurve/privateKey.ts new file mode 100644 index 0000000..1960012 --- /dev/null +++ b/src/ellipticcurve/privateKey.ts @@ -0,0 +1,128 @@ +import { PublicKey } from "./publicKey"; +import * as RandomInteger from "./utils/integer"; +import * as BinaryAscii from "./utils/binary"; +import * as EcdsaCurve from "./curve"; +import * as EcdsaMath from "./math"; +import bigInt, { BigInteger } from "big-integer"; +import * as der from "./utils/der"; + +const hexAt = "\x00"; + +export class PrivateKey { + curve: EcdsaCurve.CurveFp; + secret: BigInteger; + + constructor( + curve: EcdsaCurve.CurveFp = EcdsaCurve.secp256k1, + secret?: BigInteger + ) { + this.curve = curve; + if (secret) { + this.secret = secret; + } else { + this.secret = RandomInteger.between(bigInt(1), curve.N.minus(1)); + } + } + + publicKey(): PublicKey { + let curve = this.curve; + let publicPoint = EcdsaMath.multiply( + curve.G, + this.secret, + curve.N, + curve.A, + curve.P + ); + return new PublicKey(publicPoint, curve); + } + + toString(): string { + return BinaryAscii.stringFromNumber(this.secret, this.curve.length()); + } + + toDer(): Buffer { + let encodedPublicKey = this.publicKey().toString(true); + + return der.encodeSequence( + der.encodeInteger(bigInt(1)), + der.encodeOctetString(this.toString()), + der.encodeConstructed(0, der.encodeOid(this.curve.oid)), + der.encodeConstructed(1, der.encodeBitstring(encodedPublicKey)) + ); + } + + toPem(): string { + return der.toPem(this.toDer(), "EC PRIVATE KEY"); + } + + static fromPem(string: string): PrivateKey { + // Extract the private key part from the PEM file + const privateKeyMatch = string.match( + /-----BEGIN EC PRIVATE KEY-----\n([^-]+)\n-----END EC PRIVATE KEY-----/ + ); + if (!privateKeyMatch) { + throw new Error("Invalid PEM format: EC PRIVATE KEY section not found"); + } + const privateKeyPem = privateKeyMatch[1]; + return this.fromDer(Buffer.from(privateKeyPem, "base64")); + } + + static fromDer(data: Buffer | string): PrivateKey { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + let [s1, empty] = der.removeSequence(buf); + if (empty.length > 0) { + throw new Error( + "trailing junk after DER private key: " + + BinaryAscii.hexFromBinary(empty) + ); + } + + let [one, rest] = der.removeInteger(s1); + if (!one.eq(1)) { + throw new Error("expected '1' at start of DER private key, got " + one); + } + + let [privateKeyStr, t1] = der.removeOctetString(rest); + let [tag, curveOidStr, t2] = der.removeConstructed(t1); + + if (tag !== 0) { + throw new Error("expected tag 0 in DER private key, got " + tag); + } + + let [oidCurve, empty2] = der.removeObject(curveOidStr); + if (empty2.length > 0) { + throw new Error( + "trailing junk after DER private key curve_oid: " + + BinaryAscii.hexFromBinary(empty2) + ); + } + + let curve = EcdsaCurve.curvesByOid[oidCurve.join(".")]; + if (!curve) { + let supportedCurvesNames = EcdsaCurve.supportedCurves.map((x) => x.name); + throw new Error( + "Unknown curve with oid " + + oidCurve.join(".") + + ". Only the following are available: " + + supportedCurvesNames + ); + } + + if (privateKeyStr.length < curve.length()) { + privateKeyStr = Buffer.concat([ + Buffer.alloc(curve.length() - privateKeyStr.length, 0), + privateKeyStr, + ]); + } + + return this.fromString(privateKeyStr, curve); + } + + static fromString( + data: Buffer | string, + curve: EcdsaCurve.CurveFp = EcdsaCurve.secp256k1 + ): PrivateKey { + const str = Buffer.isBuffer(data) ? data.toString("binary") : data; + return new PrivateKey(curve, BinaryAscii.numberFromString(str)); + } +} diff --git a/src/ellipticcurve/publicKey.ts b/src/ellipticcurve/publicKey.ts new file mode 100644 index 0000000..3904fc9 --- /dev/null +++ b/src/ellipticcurve/publicKey.ts @@ -0,0 +1,133 @@ +import * as BinaryAscii from "./utils/binary"; +import * as EcdsaCurve from "./curve"; +import { Point } from "./point"; +import * as der from "./utils/der"; +import * as Math from "./math"; + +export class PublicKey { + point: Point; + curve: EcdsaCurve.CurveFp; + + constructor(point: Point, curve: EcdsaCurve.CurveFp) { + this.point = point; + this.curve = curve; + } + + toString(encoded: boolean = false): Buffer { + let xString = BinaryAscii.stringFromNumber( + this.point.x, + this.curve.length() + ); + let yString = BinaryAscii.stringFromNumber( + this.point.y, + this.curve.length() + ); + if (encoded) { + return Buffer.concat([ + Buffer.from([0x00, 0x04]), + Buffer.from(xString, "binary"), + Buffer.from(yString, "binary"), + ]); + } + return Buffer.concat([ + Buffer.from(xString, "binary"), + Buffer.from(yString, "binary"), + ]); + } + + toDer(): Buffer { + const ecOid = der.encodeOid([1, 2, 840, 10045, 2, 1]); + const curveOid = der.encodeOid(this.curve.oid); + const encodeEcAndOid = der.encodeSequence(ecOid, curveOid); + const encodedPoint = der.encodeBitstring(this.toString(true)); + + return der.encodeSequence(encodeEcAndOid, encodedPoint); + } + + toPem(): string { + return der.toPem(this.toDer(), "PUBLIC KEY"); + } + + static fromPem(string: string): PublicKey { + return this.fromDer(der.fromPem(string)); + } + + static fromDer(data: Buffer | string): PublicKey { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + let [s1, empty] = der.removeSequence(buf); + if (empty.length > 0) { + throw new Error( + "trailing junk after DER public key: " + + BinaryAscii.hexFromBinary(empty) + ); + } + + let [s2, pointBitString] = der.removeSequence(s1); + let [, rest] = der.removeObject(s2); + let [oidCurve, empty2] = der.removeObject(rest); + + if (empty2.length > 0) { + throw new Error( + "trailing junk after DER public key objects: " + + BinaryAscii.hexFromBinary(empty2) + ); + } + + let curve = EcdsaCurve.curvesByOid[oidCurve.join(".")]; + if (!curve) { + let supportedCurvesNames = EcdsaCurve.supportedCurves.map((x) => x.name); + throw new Error( + "Unknown curve with oid " + + oidCurve.join(".") + + ". Only the following are available: " + + supportedCurvesNames + ); + } + + let [pointStr, empty3] = der.removeBitString(pointBitString); + if (empty3.length > 0) { + throw new Error( + "trailing junk after public key point-string: " + + BinaryAscii.hexFromBinary(empty3) + ); + } + + return this.fromString(pointStr.slice(2), curve); + } + + static fromString( + data: Buffer | string, + curve: EcdsaCurve.CurveFp = EcdsaCurve.secp256k1, + validatePoint: boolean = true + ): PublicKey { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + let baseLen = curve.length(); + + let xs = buf.slice(0, baseLen); + let ys = buf.slice(baseLen); + + let p = new Point( + BinaryAscii.numberFromString(xs.toString("binary")), + BinaryAscii.numberFromString(ys.toString("binary")) + ); + + let publicKey = new PublicKey(p, curve); + if (!validatePoint) { + return publicKey; + } + if (p.isAtInfinity()) { + throw new Error("Public Key point is at infinity"); + } + if (!curve.contains(p)) { + throw new Error( + `point (${p.x},${p.y}) is not valid for curve ${curve.name}` + ); + } + if (!Math.multiply(p, curve.N, curve.N, curve.A, curve.P).isAtInfinity()) { + throw new Error( + `Point (${p.x},${p.y} * ${curve.name}.N is not at infinity` + ); + } + return publicKey; + } +} diff --git a/src/ellipticcurve/signature.ts b/src/ellipticcurve/signature.ts new file mode 100644 index 0000000..dfd2c2c --- /dev/null +++ b/src/ellipticcurve/signature.ts @@ -0,0 +1,50 @@ +import * as BinaryAscii from "./utils/binary"; +import * as Base64 from "./utils/base"; +import * as der from "./utils/der"; +import bigInt, { BigInteger } from "big-integer"; + +export class Signature { + r: BigInteger; + s: BigInteger; + + constructor(r: BigInteger, s: BigInteger) { + this.r = r; + this.s = s; + } + + toDer(): Buffer { + const rEncoded = der.encodeInteger(this.r); + const sEncoded = der.encodeInteger(this.s); + return der.encodeSequence(rEncoded, sEncoded); + } + + toBase64(): string { + return Base64.encode(this.toDer()); + } + + static fromDer(data: Buffer | string): Signature { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + let [rs, empty] = der.removeSequence(buf); + if (empty.length > 0) { + throw new Error( + "trailing junk after DER signature: " + BinaryAscii.hexFromBinary(empty) + ); + } + + let [r, rest] = der.removeInteger(rs); + let [s, empty2] = der.removeInteger(rest); + + if (empty2.length > 0) { + throw new Error( + "trailing junk after DER numbers: " + BinaryAscii.hexFromBinary(empty2) + ); + } + + return new Signature(r, s); + } + + static fromBase64(string: string): Signature { + let derString = Base64.decode(string); + return this.fromDer(derString); + } +} diff --git a/src/ellipticcurve/utils/base.ts b/src/ellipticcurve/utils/base.ts new file mode 100644 index 0000000..730baae --- /dev/null +++ b/src/ellipticcurve/utils/base.ts @@ -0,0 +1,10 @@ +export function decode(string: string): Buffer { + return Buffer.from(string, "base64"); +} + +export function encode(data: string | Buffer): string { + if (Buffer.isBuffer(data)) { + return data.toString("base64"); + } + return Buffer.from(data, "binary").toString("base64"); +} diff --git a/src/ellipticcurve/utils/binary.ts b/src/ellipticcurve/utils/binary.ts new file mode 100644 index 0000000..e85731e --- /dev/null +++ b/src/ellipticcurve/utils/binary.ts @@ -0,0 +1,56 @@ +import bigInt from "big-integer"; + +export function hexFromBinary(data: string | Buffer): string { + // Return the hexadecimal representation of the binary data. Every byte of data is converted into the + // corresponding 2-digit hex representation. The resulting string is therefore twice as long as the length of data. + + // :param data: binary + // :return: hexadecimal string + + if (Buffer.isBuffer(data)) { + return data.toString("hex"); + } + return Buffer.from(data, "binary").toString("hex"); +} + +export function binaryFromHex(data: string): Buffer { + // Return the binary data represented by the hexadecimal string hexstr. This function is the inverse of b2a_hex(). + // hexstr must contain an even number of hexadecimal digits (which can be upper or lower case), otherwise a TypeError is raised. + + // :param data: hexadecimal string + // :return: binary + + return Buffer.from(data, "hex"); +} + +export function numberFromString(string: string): bigInt.BigInteger { + // Get a number representation of a string + + // :param String to be converted in a number + // :return: Number in hex from string + + return numberFromHex(hexFromBinary(string.toString())); +} + +export function numberFromHex(string: string): bigInt.BigInteger { + return bigInt(string, 16); +} + +export function stringFromNumber( + number: bigInt.BigInteger, + length: number +): string { + // Get a string representation of a number + + // :param number to be converted in a string + // :param length max number of character for the string + // :return: hexadecimal string + + let result = number.toString(16); + + while (result.length < 2 * length) { + result = "0" + result; + } + + return binaryFromHex(result).toString("binary"); +} diff --git a/src/ellipticcurve/utils/der.ts b/src/ellipticcurve/utils/der.ts new file mode 100644 index 0000000..7ec1ccb --- /dev/null +++ b/src/ellipticcurve/utils/der.ts @@ -0,0 +1,289 @@ +import * as Base64 from "./base"; +import * as BinaryAscii from "./binary"; +import { modulo } from "./integer"; +import bigInt, { BigInteger } from "big-integer"; + +const hexAt = Buffer.from([0x00]); +const hexB = Buffer.from([0x02]); +const hexC = Buffer.from([0x03]); +const hexD = Buffer.from([0x04]); +const hexF = Buffer.from([0x06]); +const hex0 = Buffer.from([0x30]); + +const hex31 = 0x1f; +const hex127 = 0x7f; +const hex129 = 0xa0; +const hex160 = 0x80; +const hex224 = 0xe0; + +const bytesHex0 = hex0; +const bytesHexB = hexB; +const bytesHexC = hexC; +const bytesHexD = hexD; +const bytesHexF = hexF; + +export function encodeSequence(...args: (Buffer | string)[]): Buffer { + const buffers = args.map((arg) => + Buffer.isBuffer(arg) ? arg : Buffer.from(arg, "binary") + ); + let totalLength = buffers.reduce((acc, curr) => acc + curr.length, 0); + return Buffer.concat([hex0, _encodeLength(totalLength), ...buffers]); +} + +export function encodeInteger(x: BigInteger): Buffer { + if (x.lesser(0)) { + throw new Error("x cannot be negative"); + } + + let t = x.toString(16); + if (t.length % 2) { + t = "0" + t; + } + + let xBinary = BinaryAscii.binaryFromHex(t); + let num = xBinary[0]; + + if (num <= hex127) { + return Buffer.concat([hexB, Buffer.from([xBinary.length]), xBinary]); + } + return Buffer.concat([ + hexB, + Buffer.from([xBinary.length + 1]), + hexAt, + xBinary, + ]); +} + +export function encodeOid(pieces: number[]): Buffer { + let [first, second, ...rest] = pieces; + + if (first > 2) { + throw new Error("first has to be <= 2"); + } + if (second > 39) { + throw new Error("second has to be <= 39"); + } + + let encodedPieces = rest.map(_encodeNumber); + let firstByte = Buffer.from([40 * first + second]); + let body = Buffer.concat([ + firstByte, + ...encodedPieces.map((p) => Buffer.from(p)), + ]); + + return Buffer.concat([hexF, _encodeLength(body.length), body]); +} + +export function encodeBitstring(t: Buffer | string): Buffer { + const data = Buffer.isBuffer(t) ? t : Buffer.from(t, "binary"); + return Buffer.concat([hexC, _encodeLength(data.length), data]); +} + +export function encodeOctetString(t: Buffer | string): Buffer { + const data = Buffer.isBuffer(t) ? t : Buffer.from(t, "binary"); + return Buffer.concat([hexD, _encodeLength(data.length), data]); +} + +export function encodeConstructed(tag: number, value: Buffer | string): Buffer { + const data = Buffer.isBuffer(value) ? value : Buffer.from(value, "binary"); + return Buffer.concat([ + Buffer.from([hex129 + tag]), + _encodeLength(data.length), + data, + ]); +} + +export function removeSequence(data: Buffer | string): [Buffer, Buffer] { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + _checkSequenceError(buf, bytesHex0, "30"); + + let [length, lengthLen] = _readLength(buf.slice(1)); + let endSeq = 1 + lengthLen + length; + + return [buf.slice(1 + lengthLen, endSeq), buf.slice(endSeq)]; +} + +export function removeInteger(data: Buffer | string): [BigInteger, Buffer] { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + _checkSequenceError(buf, bytesHexB, "02"); + + let [length, lengthLen] = _readLength(buf.slice(1)); + let numberBytes = buf.slice(1 + lengthLen, 1 + lengthLen + length); + let rest = buf.slice(1 + lengthLen + length); + + if (numberBytes[0] >= hex160) { + throw new Error("nBytes must be < 160"); + } + + return [bigInt(numberBytes.toString("hex"), 16), rest]; +} + +export function removeObject(data: Buffer | string): [number[], Buffer] { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + _checkSequenceError(buf, bytesHexF, "06"); + + let [length, lengthLen] = _readLength(buf.slice(1)); + let body = buf.slice(1 + lengthLen, 1 + lengthLen + length); + let rest = buf.slice(1 + lengthLen + length); + + let numbers: number[] = []; + let remaining = body; + while (remaining.length > 0) { + let [n, lengthLength] = _readNumber(remaining); + numbers.push(n); + remaining = remaining.slice(lengthLength); + } + + let n0 = numbers.shift()!; + let first = Math.floor(n0 / 40); + let second = n0 - 40 * first; + + return [[first, second, ...numbers], rest]; +} + +export function removeBitString(data: Buffer | string): [Buffer, Buffer] { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + _checkSequenceError(buf, bytesHexC, "03"); + + let [length, lengthLen] = _readLength(buf.slice(1)); + let body = buf.slice(1 + lengthLen, 1 + lengthLen + length); + let rest = buf.slice(1 + lengthLen + length); + + return [body, rest]; +} + +export function removeOctetString(data: Buffer | string): [Buffer, Buffer] { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + _checkSequenceError(buf, bytesHexD, "04"); + + let [length, lengthLen] = _readLength(buf.slice(1)); + let body = buf.slice(1 + lengthLen, 1 + lengthLen + length); + let rest = buf.slice(1 + lengthLen + length); + + return [body, rest]; +} + +export function removeConstructed( + data: Buffer | string +): [number, Buffer, Buffer] { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); + let s0 = buf[0]; + if ((s0 & hex224) != hex129) { + throw new Error( + "wanted constructed tag (0xa0-0xbf), got 0x" + s0.toString(16) + ); + } + + let tag = s0 & hex31; + let [length, lengthLen] = _readLength(buf.slice(1)); + let body = buf.slice(1 + lengthLen, 1 + lengthLen + length); + let rest = buf.slice(1 + lengthLen + length); + + return [tag, body, rest]; +} + +export function fromPem(pem: string): Buffer { + let stripped = pem + .split("\n") + .filter((line) => !line.startsWith("-----")) + .join("") + .trim(); + + return Base64.decode(stripped); +} + +export function toPem(der: Buffer | string, name: string): string { + let b64 = Base64.encode(der); + let lines = [`-----BEGIN ${name}-----\n`]; + + for (let start = 0; start <= b64.length; start += 64) { + lines.push(b64.slice(start, start + 64) + "\n"); + } + lines.push(`-----END ${name}-----\n`); + + return lines.join(""); +} + +function _encodeLength(length: number): Buffer { + if (length < 0) { + throw new Error("length cannot be negative"); + } + + if (length < hex160) { + return Buffer.from([length]); + } + + let hexLength = length.toString(16); + if (hexLength.length % 2) { + hexLength = "0" + hexLength; + } + + let lengthBytes = Buffer.from(hexLength, "hex"); + return Buffer.concat([ + Buffer.from([hex160 + lengthBytes.length]), + lengthBytes, + ]); +} + +function _encodeNumber(n: number): Buffer { + if (n < 0) { + throw new Error("n cannot be negative"); + } + + if (n === 0) { + return Buffer.from([0]); + } + + let l: number[] = []; + while (n > 0) { + l.unshift(n & hex127); + n = n >> 7; + } + + for (let i = 0; i < l.length - 1; i++) { + l[i] = l[i] | hex160; + } + + return Buffer.from(l); +} + +function _readLength(buf: Buffer): [number, number] { + let num = buf[0]; + + if (!(num & hex160)) { + return [num, 1]; + } + + let lengthLen = num - hex160; + let numberBytes = buf.slice(1, 1 + lengthLen); + return [parseInt(numberBytes.toString("hex"), 16), 1 + lengthLen]; +} + +function _readNumber(buf: Buffer): [number, number] { + let num = buf[0]; + let length = 1; + let n = num & hex127; + + while (num & hex160) { + buf = buf.slice(1); + num = buf[0]; + n = (n << 7) | (num & hex127); + length += 1; + } + + return [n, length]; +} + +function _checkSequenceError( + buf: Buffer, + start: Buffer, + expected: string +): void { + if (buf[0] !== start[0]) { + throw new Error(`wanted sequence starting with ${expected}`); + } +} + +function _extractFirstInt(buf: Buffer): number { + return buf[0]; +} diff --git a/src/ellipticcurve/utils/file.ts b/src/ellipticcurve/utils/file.ts new file mode 100644 index 0000000..4ba3ab5 --- /dev/null +++ b/src/ellipticcurve/utils/file.ts @@ -0,0 +1,5 @@ +import * as fs from "fs"; + +export function read(path: string, encoding: BufferEncoding = "utf-8"): string { + return fs.readFileSync(path, encoding); +} diff --git a/src/ellipticcurve/utils/integer.ts b/src/ellipticcurve/utils/integer.ts new file mode 100644 index 0000000..3281221 --- /dev/null +++ b/src/ellipticcurve/utils/integer.ts @@ -0,0 +1,108 @@ +// based on random-number-csprng: https://www.npmjs.com/package/random-number-csprng + +import bigInt, { BigInteger } from "big-integer"; +import * as crypto from "crypto"; + +export function modulo(x: BigInteger, n: BigInteger): BigInteger { + let mod = x.divmod(n).remainder; + + if (mod.lesser(0)) { + mod = mod.add(n); + } + + return mod; +} + +interface Parameters { + bitsNeeded: number; + bytesNeeded: number; + mask: BigInteger; +} + +function calculateParameters(range: BigInteger): Parameters { + /* This does the equivalent of: + * + * bitsNeeded = Math.ceil(Math.log2(range)); + * bytesNeeded = Math.ceil(bitsNeeded / 8); + * mask = Math.pow(2, bitsNeeded) - 1; + * + * ... however, it implements it as bitwise operations, to sidestep any + * possible implementation errors regarding floating point numbers in + * JavaScript runtimes. This is an easier solution than assessing each + * runtime and architecture individually. + */ + + let bitsNeeded = 0; + let bytesNeeded = 0; + let mask = bigInt(1); + + while (range.greater(0)) { + if (bitsNeeded % 8 === 0) { + bytesNeeded += 1; + } + + bitsNeeded += 1; + mask = mask.shiftLeft(1).or(1); /* 0x00001111 -> 0x00011111 */ + + range = range.shiftRight(1); /* 0x01000000 -> 0x00100000 */ + } + + return { bitsNeeded, bytesNeeded, mask }; +} + +export function between(minimum: BigInteger, maximum: BigInteger): BigInteger { + if (!crypto || !crypto.randomBytes) { + throw new Error( + "No suitable random number generator available. Ensure that your runtime is linked against OpenSSL (or an equivalent) correctly." + ); + } + + if (maximum.lesserOrEquals(minimum)) { + throw new Error("The maximum value must be higher than the minimum value."); + } + + let range = maximum.minus(minimum); + + let { bitsNeeded, bytesNeeded, mask } = calculateParameters(range); + + let randomBytes = crypto.randomBytes(bytesNeeded); + + let randomValue = bigInt(0); + + /* Turn the random bytes into an integer, using bitwise operations. */ + for (let i = bigInt(0); i.lesser(bytesNeeded); i = i.add(1)) { + randomValue = randomValue.or( + bigInt(randomBytes[i.toJSNumber()]).shiftLeft(bigInt(8).multiply(i)) + ); + } + + /* We apply the mask to reduce the amount of attempts we might need + * to make to get a number that is in range. This is somewhat like + * the commonly used 'modulo trick', but without the bias: + * + * "Let's say you invoke secure_rand(0, 60). When the other code + * generates a random integer, you might get 243. If you take + * (243 & 63)-- noting that the mask is 63-- you get 51. Since + * 51 is less than 60, we can return this without bias. If we + * got 255, then 255 & 63 is 63. 63 > 60, so we try again. + * + * The purpose of the mask is to reduce the number of random + * numbers discarded for the sake of ensuring an unbiased + * distribution. In the example above, 243 would discard, but + * (243 & 63) is in the range of 0 and 60." + * + * (Source: Scott Arciszewski) + */ + + randomValue = randomValue.and(mask); + + if (randomValue.lesserOrEquals(range)) { + /* We've been working with 0 as a starting point, so we need to + * add the `minimum` here. */ + return minimum.add(randomValue); + } + + /* Outside of the acceptable range, throw it away and try again. + * We don't try any modulo tricks, as this would introduce bias. */ + return between(minimum, maximum); +} diff --git a/src/ellipticcurve/utils/utils.ts b/src/ellipticcurve/utils/utils.ts new file mode 100644 index 0000000..55b63f3 --- /dev/null +++ b/src/ellipticcurve/utils/utils.ts @@ -0,0 +1,3 @@ +import * as File from "./file"; + +export { File }; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..be02e1a --- /dev/null +++ b/src/index.ts @@ -0,0 +1,7 @@ +import { Signature } from "./ellipticcurve/signature"; +import { PublicKey } from "./ellipticcurve/publicKey"; +import { PrivateKey } from "./ellipticcurve/privateKey"; +import * as Ecdsa from "./ellipticcurve/ecdsa"; +import * as utils from "./ellipticcurve/utils/utils"; + +export { Signature, PublicKey, PrivateKey, Ecdsa, utils }; diff --git a/src/test/message.txt b/src/test/message.txt new file mode 100644 index 0000000..3211ff2 --- /dev/null +++ b/src/test/message.txt @@ -0,0 +1 @@ +This is a test message diff --git a/src/test/privateKey.pem b/src/test/privateKey.pem new file mode 100644 index 0000000..39d3aaa --- /dev/null +++ b/src/test/privateKey.pem @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BgUrgQQACg== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHQCAQEEIDODNlJus6PY4GYI5yARoZ3Udq/Gf4yclRO4eB176anvoAcGBSuBBAAK +oUQDQgAEZB4OGj5p8jKOeSbz0DhoopXR0nIpiAEjYNkmM7/ua2dvyMRCJIuDMGQ6 +jKZXlvn4L4FqaU1ZhXFKIo55Oj3O4w== +-----END EC PRIVATE KEY----- diff --git a/src/test/publicKey.pem b/src/test/publicKey.pem new file mode 100644 index 0000000..9a502e2 --- /dev/null +++ b/src/test/publicKey.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEZB4OGj5p8jKOeSbz0DhoopXR0nIpiAEj +YNkmM7/ua2dvyMRCJIuDMGQ6jKZXlvn4L4FqaU1ZhXFKIo55Oj3O4w== +-----END PUBLIC KEY----- diff --git a/src/test/signatureDer.txt b/src/test/signatureDer.txt new file mode 100644 index 0000000..77b958a Binary files /dev/null and b/src/test/signatureDer.txt differ diff --git a/src/test/test.ts b/src/test/test.ts new file mode 100644 index 0000000..e71e1aa --- /dev/null +++ b/src/test/test.ts @@ -0,0 +1,190 @@ +import { strict as assert } from "assert"; +import bigInt from "big-integer"; +import * as Ecdsa from "../ellipticcurve/ecdsa"; +import { PrivateKey } from "../ellipticcurve/privateKey"; +import { PublicKey } from "../ellipticcurve/publicKey"; +import { Signature } from "../ellipticcurve/signature"; +import * as File from "../ellipticcurve/utils/file"; +import * as path from "path"; + +const TEST_DIR = path.join(__dirname, ".."); + +describe("ECDSA test", function () { + describe("#testVerifyRightMessage()", function () { + it("should confirm authenticity", function () { + let privateKey = new PrivateKey(); + let publicKey = privateKey.publicKey(); + let message = "This is the right message"; + let signature = Ecdsa.sign(message, privateKey); + + assert.equal(Ecdsa.verify(message, signature, publicKey), true); + }); + }); + describe("#testVerifyWrongMessage()", function () { + it("should deny authenticity", function () { + let privateKey = new PrivateKey(); + let publicKey = privateKey.publicKey(); + let message1 = "This is the right message"; + let message2 = "This is the wrong message"; + let signature = Ecdsa.sign(message1, privateKey); + + assert.equal(Ecdsa.verify(message2, signature, publicKey), false); + }); + }); + describe("#testZeroSignature()", function () { + it("should deny authenticity", function () { + let privateKey = new PrivateKey(); + let publicKey = privateKey.publicKey(); + let message = "This is the right message"; + + assert.equal( + Ecdsa.verify(message, new Signature(bigInt(0), bigInt(0)), publicKey), + false + ); + }); + }); +}); +describe("openSSL test", function () { + describe("#testAssign()", function () { + it("should read and verify PEM file", function () { + // Generated by: openssl ecparam -name secp256k1 -genkey -out privateKey.pem + let privateKeyPem = File.read( + path.join(TEST_DIR, "test", "privateKey.pem") + ); + let privateKey = PrivateKey.fromPem(privateKeyPem); + let message = File.read(path.join(TEST_DIR, "test", "message.txt")); + let signature = Ecdsa.sign(message, privateKey); + let publicKey = privateKey.publicKey(); + + assert.equal(Ecdsa.verify(message, signature, publicKey), true); + }); + }); + describe("#testVerifySignature()", function () { + it("should read and verify signature file", function () { + // openssl ec -in privateKey.pem -pubout -out publicKey.pem + let publicKeyPem = File.read( + path.join(TEST_DIR, "test", "publicKey.pem") + ); + // openssl dgst -sha256 -sign privateKey.pem -out signature.binary message.txt + let signatureDer = File.read( + path.join(TEST_DIR, "test", "signatureDer.txt"), + "binary" + ); + let message = File.read(path.join(TEST_DIR, "test", "message.txt")); + let publicKey = PublicKey.fromPem(publicKeyPem); + let signature = Signature.fromDer(signatureDer); + + assert.equal(Ecdsa.verify(message, signature, publicKey), true); + }); + }); +}); +describe("PrivateKey test", function () { + describe("#testPemConversion()", function () { + it("should validate PEM generation and convertion", function () { + let privateKey1 = new PrivateKey(); + let pem = privateKey1.toPem(); + let privateKey2 = PrivateKey.fromPem(pem); + + assert.equal(String(privateKey1.secret), String(privateKey2.secret)); + assert.equal(String(privateKey1.curve), String(privateKey2.curve)); + }); + }); + describe("#testDerConversion()", function () { + it("should validate DER generation and convertion", function () { + let privateKey1 = new PrivateKey(); + let der = privateKey1.toDer(); + let privateKey2 = PrivateKey.fromDer(der); + + assert.equal(String(privateKey1.secret), String(privateKey2.secret)); + assert.equal(String(privateKey1.curve), String(privateKey2.curve)); + }); + }); + describe("#testStringConversion()", function () { + it("should validate private-key-string generation and convertion", function () { + let privateKey1 = new PrivateKey(); + let string = privateKey1.toString(); + let privateKey2 = PrivateKey.fromString(string); + + assert.equal(String(privateKey1.secret), String(privateKey2.secret)); + assert.equal(String(privateKey1.curve), String(privateKey2.curve)); + }); + }); +}); +describe("PublicKey test", function () { + describe("#testPemConversion()", function () { + it("should validate PEM generation and convertion", function () { + let privateKey = new PrivateKey(); + let publicKey1 = privateKey.publicKey(); + let pem = publicKey1.toPem(); + let publicKey2 = PublicKey.fromPem(pem); + + assert.equal(String(publicKey1.point.x), String(publicKey2.point.x)); + assert.equal(String(publicKey1.point.y), String(publicKey2.point.y)); + assert.equal(publicKey1.curve, publicKey2.curve); + }); + }); + describe("#testDerConversion()", function () { + it("should validate DER generation and convertion", function () { + let privateKey = new PrivateKey(); + let publicKey1 = privateKey.publicKey(); + let der = publicKey1.toDer(); + let publicKey2 = PublicKey.fromDer(der); + + assert.equal(String(publicKey1.point.x), String(publicKey2.point.x)); + assert.equal(String(publicKey1.point.y), String(publicKey2.point.y)); + assert.equal(publicKey1.curve, publicKey2.curve); + }); + }); + describe("#testStringConversion()", function () { + it("should validate private-key-string generation and convertion", function () { + let privateKey = new PrivateKey(); + let publicKey1 = privateKey.publicKey(); + let string = publicKey1.toString(); + let publicKey2 = PublicKey.fromString(string); + + assert.equal(String(publicKey1.point.x), String(publicKey2.point.x)); + assert.equal(String(publicKey1.point.y), String(publicKey2.point.y)); + assert.equal(publicKey1.curve, publicKey2.curve); + }); + }); +}); +describe("Signature test", function () { + describe("#testDerConversion()", function () { + it("should validate DER signature generation and convertion", function () { + let privateKey = new PrivateKey(); + let message = "This is a text message"; + let signature1 = Ecdsa.sign(message, privateKey); + let der = signature1.toDer(); + let signature2 = Signature.fromDer(der); + + assert.equal(String(signature1.r), String(signature2.r)); + assert.equal(String(signature1.s), String(signature2.s)); + }); + }); + describe("#testBase64Conversion()", function () { + it("should validate Base64 signature generation and convertion", function () { + let privateKey = new PrivateKey(); + let message = "This is a text message"; + let signature1 = Ecdsa.sign(message, privateKey); + let base64 = signature1.toBase64(); + let signature2 = Signature.fromBase64(base64); + + assert.equal(String(signature1.r), String(signature2.r)); + assert.equal(String(signature1.s), String(signature2.s)); + }); + }); + describe("#testExternalRandNum()", function () { + it("should confirm authenticity and same signature", function () { + let privateKey = new PrivateKey(); + let publicKey = privateKey.publicKey(); + let message = "This is a message"; + let signature1 = Ecdsa.sign(message, privateKey, undefined, bigInt(123)); + let signature2 = Ecdsa.sign(message, privateKey, undefined, bigInt(123)); + + assert.equal(Ecdsa.verify(message, signature1, publicKey), true); + assert.equal(Ecdsa.verify(message, signature2, publicKey), true); + assert.equal(signature1.r.toString(), signature2.r.toString()); + assert.equal(signature1.s.toString(), signature2.s.toString()); + }); + }); +}); diff --git a/test/message.txt b/test/message.txt deleted file mode 100644 index 3f07b4e..0000000 --- a/test/message.txt +++ /dev/null @@ -1,9 +0,0 @@ -{ - "transaction": { - "amount": 50000, - "receiver": "", - "description": "Sample transfer between workspaces", - "externalId": "123456", - "tags": ["john", "smith"] - } -} \ No newline at end of file diff --git a/test/privateKey.pem b/test/privateKey.pem deleted file mode 100644 index 94d5f8d..0000000 --- a/test/privateKey.pem +++ /dev/null @@ -1,8 +0,0 @@ ------BEGIN EC PARAMETERS----- -BgUrgQQACg== ------END EC PARAMETERS----- ------BEGIN EC PRIVATE KEY----- -MHQCAQEEIODvZuS34wFbt0X53+P5EnSj6tMjfVK01dD1dgDH02RzoAcGBSuBBAAK -oUQDQgAE/nvHu/SQQaos9TUljQsUuKI15Zr5SabPrbwtbfT/408rkVVzq8vAisbB -RmpeRREXj5aog/Mq8RrdYy75W9q/Ig== ------END EC PRIVATE KEY----- diff --git a/test/publicKey.pem b/test/publicKey.pem deleted file mode 100644 index 90b0f74..0000000 --- a/test/publicKey.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PUBLIC KEY----- -MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE/nvHu/SQQaos9TUljQsUuKI15Zr5SabP -rbwtbfT/408rkVVzq8vAisbBRmpeRREXj5aog/Mq8RrdYy75W9q/Ig== ------END PUBLIC KEY----- diff --git a/test/signatureDer.txt b/test/signatureDer.txt deleted file mode 100644 index 1995dd4..0000000 Binary files a/test/signatureDer.txt and /dev/null differ diff --git a/test/test.js b/test/test.js deleted file mode 100644 index 0cebcdd..0000000 --- a/test/test.js +++ /dev/null @@ -1,179 +0,0 @@ -const assert = require("assert"); -const BigInt = require("big-integer"); - -const Ecdsa = require("../ellipticcurve/ecdsa"); -const PrivateKey = require("../ellipticcurve/privateKey").PrivateKey; -const PublicKey = require("../ellipticcurve/publicKey").PublicKey; -const Signature = require("../ellipticcurve/signature").Signature; -const File = require("../ellipticcurve/utils/file"); - - -describe("ECDSA test", function() { - describe("#testVerifyRightMessage()", function() { - it("should confirm authenticity", function() { - let privateKey = new PrivateKey(); - let publicKey = privateKey.publicKey(); - let message = "This is the right message"; - let signature = Ecdsa.sign(message, privateKey); - - assert.equal(Ecdsa.verify(message, signature, publicKey), true); - }); - }); - describe("#testVerifyWrongMessage()", function() { - it("should deny authenticity", function() { - let privateKey = new PrivateKey(); - let publicKey = privateKey.publicKey(); - let message1 = "This is the right message"; - let message2 = "This is the wrong message"; - let signature = Ecdsa.sign(message1, privateKey); - - assert.equal(Ecdsa.verify(message2, signature, publicKey), false); - }); - }); - describe("#testZeroSignature()", function() { - it("should deny authenticity", function() { - let privateKey = new PrivateKey(); - let publicKey = privateKey.publicKey(); - let message = "This is the right message"; - - assert.equal(Ecdsa.verify(message, new Signature(0, 0), publicKey), false); - }); - }); -}); -describe("openSSL test", function() { - describe("#testAssign()", function() { - it("should read and verify PEM file", function() { - // Generated by: openssl ecparam -name secp256k1 -genkey -out privateKey.pem - let privateKeyPem = File.read("test/privateKey.pem"); - let privateKey = PrivateKey.fromPem(privateKeyPem); - let message = File.read("test/message.txt"); - let signature = Ecdsa.sign(message, privateKey); - let publicKey = privateKey.publicKey(); - - assert.equal(Ecdsa.verify(message, signature, publicKey), true); - }); - }); - describe("#testVerifySignature()", function() { - it("should read and verify signature file", function() { - // openssl ec -in privateKey.pem -pubout -out publicKey.pem - let publicKeyPem = File.read("test/publicKey.pem"); - // openssl dgst -sha256 -sign privateKey.pem -out signature.binary message.txt - let signatureDer = File.read("test/signatureDer.txt", "binary"); - let message = File.read("test/message.txt"); - let publicKey = PublicKey.fromPem(publicKeyPem); - let signature = Signature.fromDer(signatureDer); - - assert.equal(Ecdsa.verify(message, signature, publicKey), true); - }); - }); -}); -describe("PrivateKey test", function() { - describe("#testPemConversion()", function() { - it("should validate PEM generation and convertion", function() { - let privateKey1 = new PrivateKey(); - let pem = privateKey1.toPem(); - let privateKey2 = PrivateKey.fromPem(pem); - - assert.equal(String(privateKey1.secret), String(privateKey2.secret)); - assert.equal(String(privateKey1.curve), String(privateKey2.curve)); - }); - }); - describe("#testDerConversion()", function() { - it("should validate DER generation and convertion", function() { - let privateKey1 = new PrivateKey(); - let der = privateKey1.toDer(); - let privateKey2 = PrivateKey.fromDer(der); - - assert.equal(String(privateKey1.secret), String(privateKey2.secret)); - assert.equal(String(privateKey1.curve), String(privateKey2.curve)); - }); - }); - describe("#testStringConversion()", function() { - it("should validate private-key-string generation and convertion", function() { - let privateKey1 = new PrivateKey(); - let string = privateKey1.toString(); - let privateKey2 = PrivateKey.fromString(string); - - assert.equal(String(privateKey1.secret), String(privateKey2.secret)); - assert.equal(String(privateKey1.curve), String(privateKey2.curve)); - }); - }); -}); -describe("PublicKey test", function() { - describe("#testPemConversion()", function() { - it("should validate PEM generation and convertion", function() { - let privateKey = new PrivateKey(); - let publicKey1 = privateKey.publicKey(); - let pem = publicKey1.toPem(); - let publicKey2 = PublicKey.fromPem(pem); - - assert.equal(String(publicKey1.point.x), String(publicKey2.point.x)); - assert.equal(String(publicKey1.point.y), String(publicKey2.point.y)); - assert.equal(publicKey1.curve, publicKey2.curve); - }); - }); - describe("#testDerConversion()", function() { - it("should validate DER generation and convertion", function() { - let privateKey = new PrivateKey(); - let publicKey1 = privateKey.publicKey(); - let der = publicKey1.toDer(); - let publicKey2 = PublicKey.fromDer(der); - - assert.equal(String(publicKey1.point.x), String(publicKey2.point.x)); - assert.equal(String(publicKey1.point.y), String(publicKey2.point.y)); - assert.equal(publicKey1.curve, publicKey2.curve); - }); - }); - describe("#testStringConversion()", function() { - it("should validate private-key-string generation and convertion", function() { - let privateKey = new PrivateKey(); - let publicKey1 = privateKey.publicKey(); - let string = publicKey1.toString(); - let publicKey2 = PublicKey.fromString(string); - - assert.equal(String(publicKey1.point.x), String(publicKey2.point.x)); - assert.equal(String(publicKey1.point.y), String(publicKey2.point.y)); - assert.equal(publicKey1.curve, publicKey2.curve); - }); - }); -}); -describe("Signature test", function() { - describe("#testDerConversion()", function() { - it("should validate DER signature generation and convertion", function() { - let privateKey = new PrivateKey(); - let message = "This is a text message"; - let signature1 = Ecdsa.sign(message, privateKey); - let der = signature1.toDer(); - let signature2 = Signature.fromDer(der); - - assert.equal(String(signature1.r), String(signature2.r)); - assert.equal(String(signature1.s), String(signature2.s)); - }); - }); - describe("#testBase64Conversion()", function() { - it("should validate Base64 signature generation and convertion", function() { - let privateKey = new PrivateKey(); - let message = "This is a text message"; - let signature1 = Ecdsa.sign(message, privateKey); - let base64 = signature1.toBase64(); - let signature2 = Signature.fromBase64(base64); - - assert.equal(String(signature1.r), String(signature2.r)); - assert.equal(String(signature1.s), String(signature2.s)); - }); - }); - describe("#testExternalRandNum()", function () { - it("should confirm authenticity and same signature", function () { - let privateKey = new PrivateKey(); - let publicKey = privateKey.publicKey(); - let message = "This is a message"; - let signature_1 = Ecdsa.sign(message, privateKey, null, BigInt(123)); - let signature_2 = Ecdsa.sign(message, privateKey, null, BigInt(123)); - - assert.equal(Ecdsa.verify(message, signature_1, publicKey), true); - assert.equal(Ecdsa.verify(message, signature_2, publicKey), true); - assert.equal(signature_1.r.value, signature_2.r.value); - assert.equal(signature_1.s.value, signature_2.s.value); - }); - }); -}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..45ef6bd --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es2018", + "module": "commonjs", + "declaration": true, + "outDir": "./dist", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "test"] +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..a16bf4b --- /dev/null +++ b/yarn.lock @@ -0,0 +1,583 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/big-integer@^0.0.35": + version "0.0.35" + resolved "https://registry.yarnpkg.com/@types/big-integer/-/big-integer-0.0.35.tgz#3d1eafd37e155c356bbda70517551924a5022e0c" + integrity sha512-tE/BMXeKkGCaqLeGkmJJN6TOVrIJFPvbk5rzhbnScqRW3k6QmelN2ukMvd5KpujY7R1X1vIh6V31r/ht84TBYg== + dependencies: + big-integer "*" + +"@types/mocha@^10.0.10": + version "10.0.10" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.10.tgz#91f62905e8d23cbd66225312f239454a23bebfa0" + integrity sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q== + +"@types/node@^22.13.1": + version "22.13.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.1.tgz#a2a3fefbdeb7ba6b89f40371842162fac0934f33" + integrity sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew== + dependencies: + undici-types "~6.20.0" + +"@ungap/promise-all-settled@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" + integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +big-integer@*, big-integer@^1.6.48: + version "1.6.52" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" + integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +camelcase@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chokidar@3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +debug@4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== + dependencies: + ms "2.1.2" + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +escalade@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +js-sha256@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" + integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== + +js-yaml@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +minimatch@4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" + integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^3.0.4: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +mocha@^9.1.3: + version "9.2.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9" + integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g== + dependencies: + "@ungap/promise-all-settled" "1.1.2" + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.3" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.2.0" + growl "1.10.5" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "4.2.1" + ms "2.1.3" + nanoid "3.3.1" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + which "2.0.2" + workerpool "6.2.0" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" + integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-json-comments@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +typescript@^5.7.3: + version "5.7.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.3.tgz#919b44a7dbb8583a9b856d162be24a54bf80073e" + integrity sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw== + +undici-types@~6.20.0: + version "6.20.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" + integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== + +which@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +workerpool@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" + integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==