From 8054073993ed757dd8c21a28695f33573f8bfa5a Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Tue, 1 Apr 2025 15:37:09 +0400 Subject: [PATCH 01/12] generateSignature --- src/node-binance-api.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/node-binance-api.ts b/src/node-binance-api.ts index d64ae1fa..ea7810b3 100644 --- a/src/node-binance-api.ts +++ b/src/node-binance-api.ts @@ -11,6 +11,11 @@ import { HttpsProxyAgent } from 'https-proxy-agent'; import { SocksProxyAgent } from 'socks-proxy-agent'; // @ts-ignore import nodeFetch from 'node-fetch'; +// @ts-ignore +import { ed25519 } from '@noble/curves/ed25519'; +// @ts-ignore +import { base64 } from '@scure/base'; + // @ts-ignore import zip from 'lodash.zipobject'; import stringHash from 'string-hash'; @@ -615,7 +620,9 @@ export default class Binance { if (!data.recvWindow) data.recvWindow = this.Options.recvWindow; const query = method === 'POST' && noDataInSignature ? '' : this.makeQueryString(data); - const signature = crypto.createHmac('sha256', this.Options.APISECRET).update(query).digest('hex'); // set the HMAC hash header + + const signature = this.generateSignature(query); + if (method === 'POST') { const opt = this.reqObjPOST( url, @@ -638,6 +645,20 @@ export default class Binance { } } + generateSignature(query: string) { + const APISECRET = this.Options.APISECRET || this.APISECRET; + let signature = ''; + if (APISECRET.includes ('PRIVATE KEY')) { + const encodedQuery = new TextEncoder().encode(query); + const signatureInit = ed25519.sign (encodedQuery, APISECRET); + signature = base64.encode (signatureInit); + signature = encodeURIComponent (signature); + } else { + signature = crypto.createHmac('sha256', this.Options.APISECRET).update(query).digest('hex'); // set the HMAC hash header + } + return signature; + } + // --- ENDPOINTS --- // /** From 1e584698ff01b76152a08c34b3ca3489dc1f0a0f Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Tue, 1 Apr 2025 15:37:37 +0400 Subject: [PATCH 02/12] fix array --- src/node-binance-api.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/node-binance-api.ts b/src/node-binance-api.ts index ea7810b3..67e8785e 100644 --- a/src/node-binance-api.ts +++ b/src/node-binance-api.ts @@ -645,12 +645,27 @@ export default class Binance { } } + unarmorKey(a:string):number[] { + const m = /-----BEGIN [^-]+-----\n([A-Za-z0-9+\/=\s]+)\n-----END [^-]+-----|begin-base64[^\n]+\n([A-Za-z0-9+\/=\s]+)====/.exec(a); + if (m) { + if (m[1]) { + a = m[1]; + } else if (m[2]) { + a = m[2]; + } else { + throw new Error("RegExp out of sync"); + } + } + return base64.decode(a); + } + generateSignature(query: string) { const APISECRET = this.Options.APISECRET || this.APISECRET; let signature = ''; if (APISECRET.includes ('PRIVATE KEY')) { + const privateKey = new Uint8Array (this.unarmorKey (APISECRET).slice (16)); const encodedQuery = new TextEncoder().encode(query); - const signatureInit = ed25519.sign (encodedQuery, APISECRET); + const signatureInit = ed25519.sign (encodedQuery, privateKey); signature = base64.encode (signatureInit); signature = encodeURIComponent (signature); } else { From 55d795e0a16ff2eeeb943af1e410643790a850cb Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Tue, 1 Apr 2025 15:38:18 +0400 Subject: [PATCH 03/12] chore(deps): noble-curve & scure-base --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 47129817..0cbc45f4 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ } }, "dependencies": { + "@noble/curves": "^1.8.1", + "@scure/base": "^1.2.4", "https-proxy-agent": "^7.0.0", "json-bigint": "^1.0.0", "lodash.zipobject": "^4.1.3", From bdeb88068e20bd31968d9ac3eda091f87c6f5b6c Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Tue, 1 Apr 2025 15:48:01 +0400 Subject: [PATCH 04/12] futures support --- src/node-binance-api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node-binance-api.ts b/src/node-binance-api.ts index 67e8785e..325b33ac 100644 --- a/src/node-binance-api.ts +++ b/src/node-binance-api.ts @@ -508,7 +508,7 @@ export default class Binance { data.timestamp += this.timeOffset; } query = this.makeQueryString(data); - data.signature = crypto.createHmac('sha256', this.APISECRET).update(query).digest('hex'); // HMAC hash header + data.signature = this.generateSignature(query); opt.url = `${url}?${query}&signature=${data.signature}`; } (opt as any).qs = data; From 0789d52389a4b9927ba54f078a275ed4932da7a9 Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Tue, 1 Apr 2025 22:09:29 +0400 Subject: [PATCH 05/12] RSA support added --- src/node-binance-api.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/node-binance-api.ts b/src/node-binance-api.ts index 325b33ac..33670941 100644 --- a/src/node-binance-api.ts +++ b/src/node-binance-api.ts @@ -1,7 +1,7 @@ import WebSocket from 'ws'; // import request from 'request'; -import crypto from 'crypto'; +import crypto, { createSign } from 'crypto'; import file from 'fs'; import url from 'url'; import JSONbig from 'json-bigint'; @@ -663,11 +663,17 @@ export default class Binance { const APISECRET = this.Options.APISECRET || this.APISECRET; let signature = ''; if (APISECRET.includes ('PRIVATE KEY')) { - const privateKey = new Uint8Array (this.unarmorKey (APISECRET).slice (16)); - const encodedQuery = new TextEncoder().encode(query); - const signatureInit = ed25519.sign (encodedQuery, privateKey); - signature = base64.encode (signatureInit); - signature = encodeURIComponent (signature); + // if less than safe 150 length, then it can't be RSA key + if (APISECRET.length < 150) { + const privateKey = new Uint8Array (this.unarmorKey (APISECRET).slice (16)); + const encodedQuery = new TextEncoder().encode(query); + const signatureInit = ed25519.sign (encodedQuery, privateKey); + signature = base64.encode (signatureInit); + } else { + const signed = createSign('RSA-SHA256').update(query); + signature = signed.sign(APISECRET, 'base64'); + signature = encodeURIComponent (signature); + } } else { signature = crypto.createHmac('sha256', this.Options.APISECRET).update(query).digest('hex'); // set the HMAC hash header } From 96dfb2b1604f5c16325443af4b7760dc16742f22 Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Tue, 1 Apr 2025 22:10:59 +0400 Subject: [PATCH 06/12] eslint fix --- src/node-binance-api.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node-binance-api.ts b/src/node-binance-api.ts index 33670941..6eebae8b 100644 --- a/src/node-binance-api.ts +++ b/src/node-binance-api.ts @@ -646,6 +646,7 @@ export default class Binance { } unarmorKey(a:string):number[] { + // eslint-disable-next-line no-useless-escape const m = /-----BEGIN [^-]+-----\n([A-Za-z0-9+\/=\s]+)\n-----END [^-]+-----|begin-base64[^\n]+\n([A-Za-z0-9+\/=\s]+)====/.exec(a); if (m) { if (m[1]) { From 456f312eeb088800a82682306a730acdce776b53 Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Tue, 1 Apr 2025 22:34:22 +0400 Subject: [PATCH 07/12] length --- src/node-binance-api.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node-binance-api.ts b/src/node-binance-api.ts index 6eebae8b..c9786e06 100644 --- a/src/node-binance-api.ts +++ b/src/node-binance-api.ts @@ -664,8 +664,8 @@ export default class Binance { const APISECRET = this.Options.APISECRET || this.APISECRET; let signature = ''; if (APISECRET.includes ('PRIVATE KEY')) { - // if less than safe 150 length, then it can't be RSA key - if (APISECRET.length < 150) { + // if less than the below length, then it can't be RSA key + if (APISECRET.length < 500) { const privateKey = new Uint8Array (this.unarmorKey (APISECRET).slice (16)); const encodedQuery = new TextEncoder().encode(query); const signatureInit = ed25519.sign (encodedQuery, privateKey); From b7d2e73f7bc5769a973106e045e24a8a258aaada Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 2 Apr 2025 15:27:28 +0100 Subject: [PATCH 08/12] some updates --- src/node-binance-api.ts | 19 +++++++++++-------- src/types.ts | 1 + 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/node-binance-api.ts b/src/node-binance-api.ts index c9786e06..4a9429fa 100644 --- a/src/node-binance-api.ts +++ b/src/node-binance-api.ts @@ -66,6 +66,7 @@ export default class Binance { APIKEY: string = undefined; APISECRET: string = undefined; + PRIVATEKEY: string = undefined; test = false; timeOffset: number = 0; @@ -661,19 +662,21 @@ export default class Binance { } generateSignature(query: string) { - const APISECRET = this.Options.APISECRET || this.APISECRET; + const secret = this.APISECRET || this.PRIVATEKEY; let signature = ''; - if (APISECRET.includes ('PRIVATE KEY')) { + if (secret.includes ('PRIVATE KEY')) { // if less than the below length, then it can't be RSA key - if (APISECRET.length < 500) { - const privateKey = new Uint8Array (this.unarmorKey (APISECRET).slice (16)); + if (secret.length > 120) { + // RSA key + const signed = createSign('RSA-SHA256').update(query); + signature = signed.sign(secret, 'base64'); + signature = encodeURIComponent (signature); + } else { + // Ed25519 key + const privateKey = new Uint8Array (base64.decode (secret).slice (16)); const encodedQuery = new TextEncoder().encode(query); const signatureInit = ed25519.sign (encodedQuery, privateKey); signature = base64.encode (signatureInit); - } else { - const signed = createSign('RSA-SHA256').update(query); - signature = signed.sign(APISECRET, 'base64'); - signature = encodeURIComponent (signature); } } else { signature = crypto.createHmac('sha256', this.Options.APISECRET).update(query).digest('hex'); // set the HMAC hash header diff --git a/src/types.ts b/src/types.ts index 5e5eb697..61bc23d9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -125,6 +125,7 @@ export type Callback = (...args: any) => any; export interface IConstructorArgs { APIKEY: string; APISECRET: string; + PRIVATEKEY: string; // when using RSA/EDCSA keys recvWindow: number; useServerTime: boolean; reconnect: boolean; From 8ea1941f7aa2119c97aca132c3c252016a8b45ad Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 2 Apr 2025 15:37:29 +0100 Subject: [PATCH 09/12] no dependencies implementation --- package.json | 2 -- src/node-binance-api.ts | 46 ++++++++++++++++++----------------------- 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index 0cbc45f4..47129817 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,6 @@ } }, "dependencies": { - "@noble/curves": "^1.8.1", - "@scure/base": "^1.2.4", "https-proxy-agent": "^7.0.0", "json-bigint": "^1.0.0", "lodash.zipobject": "^4.1.3", diff --git a/src/node-binance-api.ts b/src/node-binance-api.ts index 4a9429fa..b3575d30 100644 --- a/src/node-binance-api.ts +++ b/src/node-binance-api.ts @@ -1,7 +1,7 @@ import WebSocket from 'ws'; // import request from 'request'; -import crypto, { createSign } from 'crypto'; +import crypto from 'crypto'; import file from 'fs'; import url from 'url'; import JSONbig from 'json-bigint'; @@ -11,10 +11,6 @@ import { HttpsProxyAgent } from 'https-proxy-agent'; import { SocksProxyAgent } from 'socks-proxy-agent'; // @ts-ignore import nodeFetch from 'node-fetch'; -// @ts-ignore -import { ed25519 } from '@noble/curves/ed25519'; -// @ts-ignore -import { base64 } from '@scure/base'; // @ts-ignore import zip from 'lodash.zipobject'; @@ -646,37 +642,35 @@ export default class Binance { } } - unarmorKey(a:string):number[] { - // eslint-disable-next-line no-useless-escape - const m = /-----BEGIN [^-]+-----\n([A-Za-z0-9+\/=\s]+)\n-----END [^-]+-----|begin-base64[^\n]+\n([A-Za-z0-9+\/=\s]+)====/.exec(a); - if (m) { - if (m[1]) { - a = m[1]; - } else if (m[2]) { - a = m[2]; - } else { - throw new Error("RegExp out of sync"); - } - } - return base64.decode(a); - } - generateSignature(query: string) { const secret = this.APISECRET || this.PRIVATEKEY; let signature = ''; if (secret.includes ('PRIVATE KEY')) { // if less than the below length, then it can't be RSA key + let keyObject: crypto.KeyObject; + try { + const privateKeyObj: crypto.PrivateKeyInput = { key: secret }; + keyObject = crypto.createPrivateKey(privateKeyObj); + } catch { + throw new Error( + 'Invalid private key. Please provide a valid RSA or ED25519 private key.' + ); + } + if (secret.length > 120) { // RSA key - const signed = createSign('RSA-SHA256').update(query); - signature = signed.sign(secret, 'base64'); + // const signed = createSign('RSA-SHA256').update(query); + signature = crypto + .sign('RSA-SHA256', Buffer.from(query), keyObject) + .toString('base64'); signature = encodeURIComponent (signature); } else { // Ed25519 key - const privateKey = new Uint8Array (base64.decode (secret).slice (16)); - const encodedQuery = new TextEncoder().encode(query); - const signatureInit = ed25519.sign (encodedQuery, privateKey); - signature = base64.encode (signatureInit); + // const privateKey = new Uint8Array (base64.decode (secret).slice (16)); + // const encodedQuery = new TextEncoder().encode(query); + // const signatureInit = ed25519.sign (encodedQuery, privateKey); + // signature = base64.encode (signatureInit); + signature = crypto.sign(null, Buffer.from(query), keyObject).toString('base64'); } } else { signature = crypto.createHmac('sha256', this.Options.APISECRET).update(query).digest('hex'); // set the HMAC hash header From bf293004951918a102684d26c83575fcd8815c67 Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 2 Apr 2025 15:45:04 +0100 Subject: [PATCH 10/12] remove comments --- src/node-binance-api.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/node-binance-api.ts b/src/node-binance-api.ts index b3575d30..847f27b4 100644 --- a/src/node-binance-api.ts +++ b/src/node-binance-api.ts @@ -619,7 +619,7 @@ export default class Binance { const query = method === 'POST' && noDataInSignature ? '' : this.makeQueryString(data); const signature = this.generateSignature(query); - + if (method === 'POST') { const opt = this.reqObjPOST( url, @@ -659,17 +659,12 @@ export default class Binance { if (secret.length > 120) { // RSA key - // const signed = createSign('RSA-SHA256').update(query); signature = crypto .sign('RSA-SHA256', Buffer.from(query), keyObject) .toString('base64'); signature = encodeURIComponent (signature); } else { // Ed25519 key - // const privateKey = new Uint8Array (base64.decode (secret).slice (16)); - // const encodedQuery = new TextEncoder().encode(query); - // const signatureInit = ed25519.sign (encodedQuery, privateKey); - // signature = base64.encode (signatureInit); signature = crypto.sign(null, Buffer.from(query), keyObject).toString('base64'); } } else { From 56aab92f372b1ebb8a0b6bfee16a323e18c71f51 Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 2 Apr 2025 15:49:23 +0100 Subject: [PATCH 11/12] support pk password --- src/node-binance-api.ts | 7 +++++++ src/types.ts | 1 + 2 files changed, 8 insertions(+) diff --git a/src/node-binance-api.ts b/src/node-binance-api.ts index 847f27b4..58904523 100644 --- a/src/node-binance-api.ts +++ b/src/node-binance-api.ts @@ -63,6 +63,7 @@ export default class Binance { APIKEY: string = undefined; APISECRET: string = undefined; PRIVATEKEY: string = undefined; + PRIVATEKEYPASSWORD: string = undefined; test = false; timeOffset: number = 0; @@ -189,6 +190,8 @@ export default class Binance { if (this.Options.APIKEY) this.APIKEY = this.Options.APIKEY; if (this.Options.APISECRET) this.APISECRET = this.Options.APISECRET; + if (this.Options.PRIVATEKEY) this.PRIVATEKEY = this.Options.PRIVATEKEY; + if (this.Options.PRIVATEKEYPASSWORD) this.PRIVATEKEYPASSWORD = this.Options.PRIVATEKEYPASSWORD; if (this.Options.test) this.test = true; if (this.Options.headers) this.headers = this.Options.Headers; if (this.Options.domain) this.domain = this.Options.domain; @@ -651,6 +654,10 @@ export default class Binance { try { const privateKeyObj: crypto.PrivateKeyInput = { key: secret }; keyObject = crypto.createPrivateKey(privateKeyObj); + + if (this.PRIVATEKEYPASSWORD) { + privateKeyObj.passphrase = this.PRIVATEKEYPASSWORD; + } } catch { throw new Error( 'Invalid private key. Please provide a valid RSA or ED25519 private key.' diff --git a/src/types.ts b/src/types.ts index 61bc23d9..aac51aa7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -126,6 +126,7 @@ export interface IConstructorArgs { APIKEY: string; APISECRET: string; PRIVATEKEY: string; // when using RSA/EDCSA keys + PRIVATEKEYPASSWORD: string; // when using RSA/EDCSA keys recvWindow: number; useServerTime: boolean; reconnect: boolean; From 6006a6dd1e23747c1612c1139e28c137f0a67770 Mon Sep 17 00:00:00 2001 From: carlosmiei <43336371+carlosmiei@users.noreply.github.com> Date: Wed, 2 Apr 2025 16:12:54 +0100 Subject: [PATCH 12/12] crypto tests --- .github/workflows/js.yml | 2 ++ README.md | 4 ++-- package.json | 1 + src/node-binance-api.ts | 13 +++++++---- tests/crypto.test.ts | 50 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 tests/crypto.test.ts diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml index 39b91841..e90eb945 100644 --- a/.github/workflows/js.yml +++ b/.github/workflows/js.yml @@ -27,6 +27,8 @@ jobs: run: npm run lint - name: Build run: npm run build + - name: Crypto Tests + run: npm run crypto-tests - name: Static Tests (TS ESM) run: npm run ts-test-static - name: Static Tests (JS CJS) diff --git a/README.md b/README.md index 955c4c55..297568eb 100644 --- a/README.md +++ b/README.md @@ -57,12 +57,12 @@ Actively maintained, typed, and safe SDK for the Binance REST APIs and Websocket ### Features - Spot, Margin, Futures and Delivery API -- Portfolio Margin API *\*soon*\* - Testnet support - Proxy support (REST and WS) - Customizable HTTP headers - Customizable request parameters -- RSA/ECDSA support *\*soon*\* +- RSA/ECDSA support +- Portfolio Margin API *\*soon*\* - Websocket handling with automatic reconnection - RecvWindow and automatic timestamps generation - Ability to call any endpoint, even if not supported directly by the library diff --git a/package.json b/package.json index 47129817..54d2a9b7 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "ts-test-live": "mocha ./tests/binance-class-live.test.ts", "ts-test-static": "mocha ./tests/binance-class-static.test.ts", "test-cjs": "node ./tests/cjs-test.cjs", + "crypto-tests": "mocha ./tests/crypto.test.ts", "ws-tests": "mocha ./tests/binance-class-ws.test.ts", "test-debug": "mocha --inspect-brk", "lint": "eslint src/", diff --git a/src/node-binance-api.ts b/src/node-binance-api.ts index 58904523..612ef0e6 100644 --- a/src/node-binance-api.ts +++ b/src/node-binance-api.ts @@ -645,7 +645,7 @@ export default class Binance { } } - generateSignature(query: string) { + generateSignature(query: string, encode = true) { const secret = this.APISECRET || this.PRIVATEKEY; let signature = ''; if (secret.includes ('PRIVATE KEY')) { @@ -653,14 +653,16 @@ export default class Binance { let keyObject: crypto.KeyObject; try { const privateKeyObj: crypto.PrivateKeyInput = { key: secret }; - keyObject = crypto.createPrivateKey(privateKeyObj); if (this.PRIVATEKEYPASSWORD) { privateKeyObj.passphrase = this.PRIVATEKEYPASSWORD; } - } catch { + + keyObject = crypto.createPrivateKey(privateKeyObj); + + } catch (e){ throw new Error( - 'Invalid private key. Please provide a valid RSA or ED25519 private key.' + 'Invalid private key. Please provide a valid RSA or ED25519 private key. ' + e.toString() ); } @@ -669,7 +671,8 @@ export default class Binance { signature = crypto .sign('RSA-SHA256', Buffer.from(query), keyObject) .toString('base64'); - signature = encodeURIComponent (signature); + if (encode) signature = encodeURIComponent (signature); + return signature; } else { // Ed25519 key signature = crypto.sign(null, Buffer.from(query), keyObject).toString('base64'); diff --git a/tests/crypto.test.ts b/tests/crypto.test.ts new file mode 100644 index 00000000..3b1c5149 --- /dev/null +++ b/tests/crypto.test.ts @@ -0,0 +1,50 @@ +import Binance from '../src/node-binance-api'; +import { assert } from 'chai'; + + +const testCases = [ + { + "description": "Unencrypted PKCS8 ed22519 private key", + "private_key": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIPQmzwVKJETqVd7L9E/DFbkvrOigy1tLL+9QF0mSn6dV\n-----END PRIVATE KEY-----\n", + "password": undefined, + "expected_signature": "a4Pm3p02D2HXtNfo3DBaVCe9Ov7kledewgYtGjekotFmZ5wXa3mC5AtLB7CpAphyNjeyovIuDP+9fyjYmsojCw==", + }, + { + "description": "Unencrypted PKCS8 ed22519 private key in bytes", + "private_key": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIPQmzwVKJETqVd7L9E/DFbkvrOigy1tLL+9QF0mSn6dV\n-----END PRIVATE KEY-----\n", + "password": undefined, + "expected_signature": "a4Pm3p02D2HXtNfo3DBaVCe9Ov7kledewgYtGjekotFmZ5wXa3mC5AtLB7CpAphyNjeyovIuDP+9fyjYmsojCw==", + }, + { + "description": "Encrypted PKCS8 RSA private key", + "private_key": "-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFNTBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQWW+iEMYYCPUntrPq\nZ2RCMAICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEIw3ViSuTp8JeN43\n5VGlHt0EggTQBvEzd2w2F561CzU+MDouZDOPj4RTIStC471z0/bxTgYqH3gYchoe\nOfi2lsLuD8B+ivIRuXB8GT66BIseIOMV8t/tiMe97rFI/cV4h6DrBO1xlmSrBG97\nvFF9qPA5yPRlrHtWKkGxhXteNVsT3w/7Y7KsulO/gA2KpsOElMElOhUP462Yd0Wl\nOxAIV3+knl2niozws2Kq3EdzTF3N6hlavUPryiU/w4RRsPN5qgjchVVLq/sYRYhx\nN8uWJbkjhCcHsULkD5KkdgddR0VOhpQPXIdY+gPkSBJq1ltRWy/TYdXiU2fEBNZW\nhFUVrxnS76+u2R3vukY2IAX8zTC6h2AbCBG+r4XXzgk/l/4peySKHsPQRzQ0in39\na9o5sctOmUNeD4uJ6cClXDdqyEwXhnPmRKZjJ8qeH4D9wl7HOG7iQsYiyfJe/igi\nFEXVRZOtLBdbwX45rU6wiWWjxzY+mDnw4BXE31ZBPwgtoh+CLTyK8NI8LnCV/CgO\nzOY4sm/KDWmbfTTZjLSdYRFj7wEpOdUWjZ13viDFZqnmy/o1auvLmBcqbRrCyW+B\nOMI7aHE0mZ/52vEFQYU1tH0BxMmRfWXUCJj0TjwxDY6BQmmW4YlhsrgGNekLFDo1\n6phFd0pA4UPqGXfNLzHp1dtLhUEb4YzcpDn+HMzMf1gfez7qeqU28nNFg/AwwqHZ\nTWdGclCFjiah7SfvOslob4vdLGwkUhgCBKQUQoU1DltX2GOgIv9SNY3q6X0NwdZG\nL5gqk225WVUwIRzmi5nfUEXlbaTvyHg3BuGedUKJ91IhRCW1ZjvU8GQcfVsu8bse\nTCKMdr7wi/zEZXSldCza6vL4m3tmBLtWkHVOW8bcDWvoVwRswbFHfleHzckl7EeC\n9C4TRa66gA5UOv14SrpC8noQUNpSegg+1KI4BSNvwaheiSUqjQbisb0qYCxML0ZP\nmQodwVsXG6LYo+Y6y6CpHbT7UYkfa59q/CGOZByL1bEzzgd98ZHwjihOjHVaV6sY\nBW018AvGxr7kjEU4LNqIteydTp0o31ZJN/qK78w5EQFfJxfImrx/E4nYKtg4higj\nKOQCgJALKIveidqQEFsbGWsulYrMXwnu0nPThofR1D8eCJZpdTxvOh2nIrNrAeY8\nZMAwG1uQos5A0yEZ1auHxz+rb4errnk92OnVlWnElf1TwwlkFFNLdNDl8VpiMP40\n6en9VtlOfgH8AwB03WsoeuEQsxYTIcRKWZZPRsLx3hd0BsOw0FcYDSX2XIGPkVVW\niYf9hzFSQsWV3d6utloIm4nG8XONfNaRimGECbUSZyHZimrO1m4Gga5pE3LKuDri\nJKR2lR7b6XPR7+FS+lG1zq5KY7onAVQY1oABfTjpJRju6pQGWt70hairo6EaVC3u\nrBy8UkLwBbfDuigSvsVk+sF2+Ic0IzX6IniU0F5kMe+MKqGB4aicXP6FFGBpPFTe\nv6yHD+DYAu1rnlXrqmFL50CfutTF78uPPJ9D2Sm0DcGPFj+6IrCigj48uxoHR9Qb\nFeNzfsmVwoFAWWq/MpkPbX6Aql8ddCbpMxDUUkybwVV9rJmEMTLil44FrxKAKFhP\n0Av7JeFvdz15pfnf/IQ3IOvVhHGFChFS13sbYSvFHMQF3P0BiyvjhBI=\n-----END ENCRYPTED PRIVATE KEY-----\n", + "password": "testpwd", + "expected_signature": "S4l9IONXGHIdt4NjwmpCIhawDTitjUQls73d+mi0HJTSbTGyn95NabX5hC9+n6HsTqLcWPvxKgTvLFMnTaf6Jxl+xwQMbu9/6mw88KF7i1pEQizerKcr91rPUPVBQ4OY10Q018QEamIAymRgo/eoRYSm7CqCdeibGyO0XfXZBaJnVGFJ9hgrPIwSKHgeUnfK8qMenULvL0qKMEJ6ziYPiqh7k9xX3xIV7lGIpokk+ekqlFd01f/Lov45osJCFuccJO4xuUUZewZnVGF7Uw6Rim3UsKhXKZUN9WZWa5RT+dpBIJ5DTBIXBSvowwj3GZC3j+XvWw8Sn0Ls9836l89BXw==", + }, + { + "description": "Encrypted PKCS8 RSA private key in bytes", + "private_key": "-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFNTBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQWW+iEMYYCPUntrPq\nZ2RCMAICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEIw3ViSuTp8JeN43\n5VGlHt0EggTQBvEzd2w2F561CzU+MDouZDOPj4RTIStC471z0/bxTgYqH3gYchoe\nOfi2lsLuD8B+ivIRuXB8GT66BIseIOMV8t/tiMe97rFI/cV4h6DrBO1xlmSrBG97\nvFF9qPA5yPRlrHtWKkGxhXteNVsT3w/7Y7KsulO/gA2KpsOElMElOhUP462Yd0Wl\nOxAIV3+knl2niozws2Kq3EdzTF3N6hlavUPryiU/w4RRsPN5qgjchVVLq/sYRYhx\nN8uWJbkjhCcHsULkD5KkdgddR0VOhpQPXIdY+gPkSBJq1ltRWy/TYdXiU2fEBNZW\nhFUVrxnS76+u2R3vukY2IAX8zTC6h2AbCBG+r4XXzgk/l/4peySKHsPQRzQ0in39\na9o5sctOmUNeD4uJ6cClXDdqyEwXhnPmRKZjJ8qeH4D9wl7HOG7iQsYiyfJe/igi\nFEXVRZOtLBdbwX45rU6wiWWjxzY+mDnw4BXE31ZBPwgtoh+CLTyK8NI8LnCV/CgO\nzOY4sm/KDWmbfTTZjLSdYRFj7wEpOdUWjZ13viDFZqnmy/o1auvLmBcqbRrCyW+B\nOMI7aHE0mZ/52vEFQYU1tH0BxMmRfWXUCJj0TjwxDY6BQmmW4YlhsrgGNekLFDo1\n6phFd0pA4UPqGXfNLzHp1dtLhUEb4YzcpDn+HMzMf1gfez7qeqU28nNFg/AwwqHZ\nTWdGclCFjiah7SfvOslob4vdLGwkUhgCBKQUQoU1DltX2GOgIv9SNY3q6X0NwdZG\nL5gqk225WVUwIRzmi5nfUEXlbaTvyHg3BuGedUKJ91IhRCW1ZjvU8GQcfVsu8bse\nTCKMdr7wi/zEZXSldCza6vL4m3tmBLtWkHVOW8bcDWvoVwRswbFHfleHzckl7EeC\n9C4TRa66gA5UOv14SrpC8noQUNpSegg+1KI4BSNvwaheiSUqjQbisb0qYCxML0ZP\nmQodwVsXG6LYo+Y6y6CpHbT7UYkfa59q/CGOZByL1bEzzgd98ZHwjihOjHVaV6sY\nBW018AvGxr7kjEU4LNqIteydTp0o31ZJN/qK78w5EQFfJxfImrx/E4nYKtg4higj\nKOQCgJALKIveidqQEFsbGWsulYrMXwnu0nPThofR1D8eCJZpdTxvOh2nIrNrAeY8\nZMAwG1uQos5A0yEZ1auHxz+rb4errnk92OnVlWnElf1TwwlkFFNLdNDl8VpiMP40\n6en9VtlOfgH8AwB03WsoeuEQsxYTIcRKWZZPRsLx3hd0BsOw0FcYDSX2XIGPkVVW\niYf9hzFSQsWV3d6utloIm4nG8XONfNaRimGECbUSZyHZimrO1m4Gga5pE3LKuDri\nJKR2lR7b6XPR7+FS+lG1zq5KY7onAVQY1oABfTjpJRju6pQGWt70hairo6EaVC3u\nrBy8UkLwBbfDuigSvsVk+sF2+Ic0IzX6IniU0F5kMe+MKqGB4aicXP6FFGBpPFTe\nv6yHD+DYAu1rnlXrqmFL50CfutTF78uPPJ9D2Sm0DcGPFj+6IrCigj48uxoHR9Qb\nFeNzfsmVwoFAWWq/MpkPbX6Aql8ddCbpMxDUUkybwVV9rJmEMTLil44FrxKAKFhP\n0Av7JeFvdz15pfnf/IQ3IOvVhHGFChFS13sbYSvFHMQF3P0BiyvjhBI=\n-----END ENCRYPTED PRIVATE KEY-----\n", + "password": "testpwd", + "expected_signature": "S4l9IONXGHIdt4NjwmpCIhawDTitjUQls73d+mi0HJTSbTGyn95NabX5hC9+n6HsTqLcWPvxKgTvLFMnTaf6Jxl+xwQMbu9/6mw88KF7i1pEQizerKcr91rPUPVBQ4OY10Q018QEamIAymRgo/eoRYSm7CqCdeibGyO0XfXZBaJnVGFJ9hgrPIwSKHgeUnfK8qMenULvL0qKMEJ6ziYPiqh7k9xX3xIV7lGIpokk+ekqlFd01f/Lov45osJCFuccJO4xuUUZewZnVGF7Uw6Rim3UsKhXKZUN9WZWa5RT+dpBIJ5DTBIXBSvowwj3GZC3j+XvWw8Sn0Ls9836l89BXw==", + }, +] + +describe('Test crypto signature', function () { + + it('RSA and ed22519 tests ', function () { + + const dataQuery = 'price=50000&quantity=1&side=BUY&symbol=BTCUSDT×tamp=1631234567890&type=LIMIT' + + for (const testCase of testCases) { + const binance = new Binance({ + APISECRET: testCase.private_key, + PRIVATEKEYPASSWORD: testCase.password, + }) + + const signature = binance.generateSignature(dataQuery, false); + assert.equal(signature, testCase.expected_signature, testCase.description); + } + + }); + +}); \ No newline at end of file