From de620cc8d44e8adeefe5bc4828353a79562db968 Mon Sep 17 00:00:00 2001 From: bill Date: Fri, 27 May 2022 17:51:00 +0800 Subject: [PATCH 1/6] add key import from file --- test/resources/test_keyfile.dat | 2 ++ test/unit/KeyPair.test.ts | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 test/resources/test_keyfile.dat diff --git a/test/resources/test_keyfile.dat b/test/resources/test_keyfile.dat new file mode 100644 index 0000000..1d397a1 --- /dev/null +++ b/test/resources/test_keyfile.dat @@ -0,0 +1,2 @@ +U2FsdGVkX1+zZPlLL1zR8ac9kCp+lHWGsjpUwBINwhpnTJWlu4TctG/Zha/8Mx0ZXjMbb73KZN+N/pBawfTmmw== + diff --git a/test/unit/KeyPair.test.ts b/test/unit/KeyPair.test.ts index 5f03ed5..653b9b4 100644 --- a/test/unit/KeyPair.test.ts +++ b/test/unit/KeyPair.test.ts @@ -33,6 +33,8 @@ const PRIVATE_TEST_KEY_PASSWORD = 'secret' const SIGN_HASH_TEXT = '5bb1ce718241bfec110552b86bb7cccf0d95b8a5f462fbf6dff7c48543622ba5' const SIGN_TEXT = '0x7eceffab47295be3891ea745838a99102bfaf525ec43632366c7ec3f54db4822b5d581573aecde94c420554f963baebbf412e4304ad8636886ddfa7b1049f70e' +const TEST_KEY_FILE = 'test/resources/test_keyfile.dat' + describe('KeyPair class tests', () => { describe('Key pair from PEM to new format', async () => { it('should import and export a new encrypted format', async () => { @@ -74,7 +76,7 @@ describe('KeyPair class tests', () => { }) }) - describe('Export keys', () => { + describe('Import and Export keys', () => { it('should export/import key pair key too text', async () => { const keyPair = await KeyPair.create() const text = keyPair.exportToString(PRIVATE_TEST_KEY_PASSWORD) @@ -94,6 +96,13 @@ describe('KeyPair class tests', () => { assert.equal(keyPairSaved.publicKeyAPI, keyPair.publicKeyAPI) fs.unlinkSync(filename) }) + it('should import key from test key file', async () => { + const keyPair = await KeyPair.importFromString(PRIVATE_ENCRYPTED_KEY, PRIVATE_TEST_KEY_PASSWORD) + assert(keyPair) + + const keyPairSaved = await KeyPair.importFromFile(TEST_KEY_FILE, PRIVATE_TEST_KEY_PASSWORD) + assert.equal(keyPairSaved.publicKeyAPI, keyPair.publicKeyAPI) + }) }) describe('Sign text', () => { it('should sign a standard hash text to produce a confirmed signed result', async () => { From 48c8e2c44a3a9ee4c0d7ac7b576c31336cae7a8a Mon Sep 17 00:00:00 2001 From: bill Date: Mon, 30 May 2022 10:29:04 +0800 Subject: [PATCH 2/6] update util functions and more tests --- CHANGELOG.md | 5 +++++ src/KeyPair.ts | 12 ++++++------ src/Utils.ts | 43 ++++++++++++++++++++++++++--------------- test/unit/Utils.test.ts | 21 +++++++++++++++++++- 4 files changed, 58 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c8a2ce..a348384 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## Release 0.2.11 ++ can import/export private key from openssl standard encrypted base64 format ++ At the moment PEM PKCS8 format not supported ++ utils function 'hexToByteArray', 'isHexString', 'wordArrayToByteArray' and 'byteArrayToWordArray' to support the new crypto library + ## Release 0.2.10 + removed cypto, pem-file packages and replaced with @noble/ed25519 and crypto-key-composer diff --git a/src/KeyPair.ts b/src/KeyPair.ts index 6da696b..48fd2b6 100644 --- a/src/KeyPair.ts +++ b/src/KeyPair.ts @@ -39,10 +39,10 @@ export class KeyPair { return new KeyPair(publicKey, privateKey) } /** - * Imports a keypair from a PKCS8 fromated text string. You need to pass the correct password, to decrypt + * Imports a keypair from a encrypted base64 text string. You need to pass the correct password, to decrypt * the private key stored in the text string. * - * @param text PKCS8 fromated text with the private key encrypted. + * @param text Encrpted base64 fromated text with the private key encrypted. * @param password Password to decrypt the private key. * @param publicKeyText Optional public key encoded in PEM format, if non provided, the public key * can be obtained from the private key. @@ -57,7 +57,7 @@ export class KeyPair { } /** - * Imports a private key file. The key file is in the format as PKCS8 text. The private key is encrypted. + * Imports a private key file. The key file is in the format as a encrpted base64 text. The private key is encrypted. * * @param filename Filename containing the encrypted private key. * @param password Password to decrypt the private key. @@ -75,11 +75,11 @@ export class KeyPair { } /** - * Export the keypair to a PKCS8 fromatted text string. The private key is encrypted using the provided password. + * Export the keypair to a encrypt base64 fromatted text string. The private key is encrypted using the provided password. * * @param password Password to encrypt the private key. * - * @returns The encrpted private key as a PKCS8 formatted string. + * @returns The encrpted private key as a encrypted base64 formatted string. * */ public exportToString(password: string): string { @@ -88,7 +88,7 @@ export class KeyPair { } /** - * Same as `exportToText` this writes the exported tex to a file. + * Same as `exportToText` this writes the exported text to a file. * * @param password Password to encrypt the private key. * diff --git a/src/Utils.ts b/src/Utils.ts index 1dd9fa0..921c1b6 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -12,21 +12,7 @@ interface IWordArray { words: Uint32Array sigBytes: number } -export function hexToBytes(hex: string): Uint8Array { - if (typeof hex !== 'string') { - throw new TypeError('hexToBytes: expected string, got ' + typeof hex) - } - if (hex.length % 2) throw new Error('hexToBytes: received invalid unpadded hex') - const array = new Uint8Array(hex.length / 2) - for (let i = 0; i < array.length; i++) { - const j = i * 2 - const hexByte = hex.slice(j, j + 2) - const byte = Number.parseInt(hexByte, 16) - if (Number.isNaN(byte) || byte < 0) throw new Error('Invalid byte sequence') - array[i] = byte - } - return array -} + /** * Return true if the number or string is an address value. This does not check the network for a valid @@ -125,7 +111,7 @@ export function remove0xPrefix(value: string): string { export function toPublicKeyChecksum(publicKey: string): string { const publicKeyClean = remove0xPrefix(publicKey).toLowerCase() let result = '0x' - const hashData = sha3_256(hexToBytes(publicKeyClean)) + const hashData = sha3_256(hexToByteArray(publicKeyClean)) for (let index = 0; index < hashData.length && index < publicKeyClean.length; index++) { if (parseInt(hashData.charAt(index), 16) > 7) { result = result.concat(publicKeyClean.charAt(index).toUpperCase()) @@ -163,8 +149,33 @@ export function isPublicKeyChecksum(publicKey: string): boolean { return remove0xPrefix(publicKey) && remove0xPrefix(publicKey) == remove0xPrefix(toPublicKeyChecksum(publicKey)) } +/** + * Returns true if the string is a hex string + * + */ +export function isHexSring(hex: string): boolean { + return hex !== null && hex.match(/^[0-9a-f]+$/gi) !== null +} + +/** + * Returns a Uint8Array of a hex string. The hex string must only contain + * characters from 0 - 9 and a-f + */ +export function hexToByteArray(hex: string): Uint8Array { + if ( !isHexSring(hex)) { + throw TypeError(`the hex string ${hex} contains non hex characters`) + } + const pairs = hex.match(/[0-9a-f]{2}/ig) + const values = pairs.map((p) => { + return parseInt(p, 16) + }) + return new Uint8Array(values) +} + + /* * Code originally copied from crypto-js for conversion Latin1 to and from WordArray + * but changed to return/input Uint8Array * */ export function wordArrayToByteArray(wordArray: IWordArray): Uint8Array { diff --git a/test/unit/Utils.test.ts b/test/unit/Utils.test.ts index a9576ba..bcc8886 100644 --- a/test/unit/Utils.test.ts +++ b/test/unit/Utils.test.ts @@ -4,7 +4,7 @@ */ -import { assert } from 'chai' +import { assert, expect } from 'chai' import { randomInt, randomBytes } from 'crypto' import { Account } from '../../src/Account' @@ -13,6 +13,7 @@ import { isAddress, isPublicKey, isPublicKeyChecksum, + hexToByteArray, prefix0x, remove0xPrefix, toAddress, @@ -22,6 +23,12 @@ import { } from '../../src/Utils' const PUBLIC_ADDRESS = '0x5288fec4153b702430771dfac8aed0b21cafca4344dae0d47b97f0bf532b3306' +const PUBLIC_ADDRESS_BYTES = [ + 0x52, 0x88, 0xFE, 0xC4, 0x15, 0x3B, 0x70, 0x24, + 0x30, 0x77, 0x1D, 0xFA, 0xC8, 0xAE, 0xD0, 0xB2, + 0x1C, 0xAF, 0xCA, 0x43, 0x44, 0xDA, 0xE0, 0xD4, + 0x7B, 0x97, 0xF0, 0xBF, 0x53, 0x2B, 0x33, 0x06 +] const PUBLIC_ADDRESS_CHECHKSUM = '0x5288Fec4153b702430771DFAC8AeD0B21CAFca4344daE0d47B97F0bf532b3306' @@ -128,6 +135,18 @@ describe('Utils module', () => { }) }) + describe('hexToBytes', () => { + it('should convert a hex string to Uint8Array', () => { + expect(() => hexToByteArray(PUBLIC_ADDRESS)).to.throw(TypeError, /contains non hex characters/) + + let result = hexToByteArray(remove0xPrefix(PUBLIC_ADDRESS)) + assert.equal(Uint8Array.from(PUBLIC_ADDRESS_BYTES).toString(), result.toString()) + + result = hexToByteArray(remove0xPrefix(PUBLIC_ADDRESS_CHECHKSUM)) + assert.equal(Uint8Array.from(PUBLIC_ADDRESS_BYTES).toString(), result.toString()) + + }) + }) describe('Convert Uint8Array to and from WordArray for crypto-js', () => { it('should convert to word array', () => { const testLength = 64 From d0de6703c12399cb5ebc2e83ad48b72770a3ed08 Mon Sep 17 00:00:00 2001 From: bill Date: Mon, 30 May 2022 10:31:47 +0800 Subject: [PATCH 3/6] fix lint --- src/Utils.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Utils.ts b/src/Utils.ts index 921c1b6..4e97049 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -13,7 +13,6 @@ interface IWordArray { sigBytes: number } - /** * Return true if the number or string is an address value. This does not check the network for a valid * address, but just checks to see if it is a number @@ -162,17 +161,16 @@ export function isHexSring(hex: string): boolean { * characters from 0 - 9 and a-f */ export function hexToByteArray(hex: string): Uint8Array { - if ( !isHexSring(hex)) { + if (!isHexSring(hex)) { throw TypeError(`the hex string ${hex} contains non hex characters`) } - const pairs = hex.match(/[0-9a-f]{2}/ig) + const pairs = hex.match(/[0-9a-f]{2}/gi) const values = pairs.map((p) => { return parseInt(p, 16) }) return new Uint8Array(values) } - /* * Code originally copied from crypto-js for conversion Latin1 to and from WordArray * but changed to return/input Uint8Array From b9c368211aea6a012643486e15b38162e81e3602 Mon Sep 17 00:00:00 2001 From: bill Date: Mon, 30 May 2022 10:33:09 +0800 Subject: [PATCH 4/6] 0.2.11 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 98455c9..ed4d890 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@convex-dev/convex-api-js", - "version": "0.2.10", + "version": "0.2.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@convex-dev/convex-api-js", - "version": "0.2.10", + "version": "0.2.11", "license": "Apache-2.0", "dependencies": { "@noble/ed25519": "^1.6.0", diff --git a/package.json b/package.json index ceb5903..a449969 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@convex-dev/convex-api-js", - "version": "0.2.10", + "version": "0.2.11", "description": "JavaScript client library to access Convex Network", "main": "dist/index.js", "types": "dist/index.d.ts", From a7fe29309e8750cb76b790c385154c68a87a2bce Mon Sep 17 00:00:00 2001 From: bill Date: Mon, 30 May 2022 11:02:42 +0800 Subject: [PATCH 5/6] export new util functions --- CHANGELOG.md | 3 +++ src/Utils.ts | 4 ++-- src/index.ts | 4 ++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a348384..6381ae8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Change Log +## Release 0.2.12 ++ Export new util functions + ## Release 0.2.11 + can import/export private key from openssl standard encrypted base64 format + At the moment PEM PKCS8 format not supported diff --git a/src/Utils.ts b/src/Utils.ts index 4e97049..6b22656 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -152,7 +152,7 @@ export function isPublicKeyChecksum(publicKey: string): boolean { * Returns true if the string is a hex string * */ -export function isHexSring(hex: string): boolean { +export function isHexString(hex: string): boolean { return hex !== null && hex.match(/^[0-9a-f]+$/gi) !== null } @@ -161,7 +161,7 @@ export function isHexSring(hex: string): boolean { * characters from 0 - 9 and a-f */ export function hexToByteArray(hex: string): Uint8Array { - if (!isHexSring(hex)) { + if (!isHexString(hex)) { throw TypeError(`the hex string ${hex} contains non hex characters`) } const pairs = hex.match(/[0-9a-f]{2}/gi) diff --git a/src/index.ts b/src/index.ts index f0d5f10..740bc03 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,5 +21,9 @@ export { prefix0x, remove0xPrefix, toPublicKeyChecksum, + isHexString, + hexToByteArray, + wordArrayToByteArray, + byteArrayToWordArray, } from './Utils' export { Registry } from './Registry' From ec29c6566f51ea20e35f49c4baab5067a93fc189 Mon Sep 17 00:00:00 2001 From: bill Date: Mon, 30 May 2022 11:04:21 +0800 Subject: [PATCH 6/6] 0.2.12 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index ed4d890..0210209 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@convex-dev/convex-api-js", - "version": "0.2.11", + "version": "0.2.12", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@convex-dev/convex-api-js", - "version": "0.2.11", + "version": "0.2.12", "license": "Apache-2.0", "dependencies": { "@noble/ed25519": "^1.6.0", diff --git a/package.json b/package.json index a449969..12c6771 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@convex-dev/convex-api-js", - "version": "0.2.11", + "version": "0.2.12", "description": "JavaScript client library to access Convex Network", "main": "dist/index.js", "types": "dist/index.d.ts",