Skip to content

Commit fe72708

Browse files
authored
Merge pull request #1 from Convex-Dev/remove-crypto-key-composer
Remove crypto key composer
2 parents f2d7371 + ed96260 commit fe72708

File tree

8 files changed

+172
-502
lines changed

8 files changed

+172
-502
lines changed

package-lock.json

Lines changed: 36 additions & 382 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@
5151
"dependencies": {
5252
"@noble/ed25519": "^1.6.0",
5353
"cross-fetch": "^3.1.5",
54-
"crypto-key-composer": "^0.1.3",
55-
"sha3": "^2.1.4",
54+
"crypto-js": "^4.1.1",
55+
"js-sha3": "^0.8.0",
5656
"url-join": "^4.0.1"
5757
},
5858
"devDependencies": {

src/KeyPair.ts

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
*/
77

88
import * as ed25519 from '@noble/ed25519'
9-
import { composePrivateKey, decomposePrivateKey } from 'crypto-key-composer'
9+
import cryptojs from 'crypto-js'
1010

11-
import { toPublicKeyChecksum, remove0xPrefix } from './Utils'
11+
import { toPublicKeyChecksum, remove0xPrefix, byteArrayToWordArray, wordArrayToByteArray } from './Utils'
1212

1313
export class KeyPair {
1414
readonly privateKey: Uint8Array // private key data
@@ -31,10 +31,13 @@ export class KeyPair {
3131
*/
3232
public static async create(): Promise<KeyPair> {
3333
const privateKey = ed25519.utils.randomPrivateKey()
34+
return await KeyPair.createFromPrivateKey(privateKey)
35+
}
36+
37+
public static async createFromPrivateKey(privateKey: Uint8Array): Promise<KeyPair> {
3438
const publicKey = await ed25519.getPublicKey(privateKey)
3539
return new KeyPair(publicKey, privateKey)
3640
}
37-
3841
/**
3942
* Imports a keypair from a PKCS8 fromated text string. You need to pass the correct password, to decrypt
4043
* the private key stored in the text string.
@@ -47,17 +50,10 @@ export class KeyPair {
4750
* @returns an KeyPair object with the private and public key pairs.
4851
*
4952
*/
50-
public static async importFromString(text: string, password: string, publicKeyText?: string): Promise<KeyPair> {
51-
const privateKeyData = decomposePrivateKey(text, {
52-
format: 'pkcs8-pem',
53-
password: password,
54-
})
55-
const privateKey = privateKeyData.keyData.seed
56-
let publicKey = await ed25519.getPublicKey(privateKey)
57-
if (publicKeyText) {
58-
publicKey = await ed25519.getPublicKey(privateKey)
59-
}
60-
return new KeyPair(publicKey, privateKey)
53+
public static async importFromString(text: string, password: string): Promise<KeyPair> {
54+
const privateKeyWords = cryptojs.AES.decrypt(text, password, { format: cryptojs.format.OpenSSL })
55+
const privateKey = wordArrayToByteArray(privateKeyWords)
56+
return await KeyPair.createFromPrivateKey(privateKey)
6157
}
6258

6359
/**
@@ -87,21 +83,8 @@ export class KeyPair {
8783
*
8884
*/
8985
public exportToString(password: string): string {
90-
return composePrivateKey(
91-
{
92-
format: 'pkcs8-pem',
93-
keyAlgorithm: {
94-
id: 'ed25519',
95-
},
96-
keyData: {
97-
seed: this.privateKey,
98-
},
99-
},
100-
{
101-
format: 'pkcs8-pem',
102-
password: password,
103-
}
104-
)
86+
const data = cryptojs.AES.encrypt(byteArrayToWordArray(this.privateKey), password, { format: cryptojs.format.OpenSSL })
87+
return data.toString()
10588
}
10689

10790
/**

src/Utils.ts

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,30 @@
44
*
55
*/
66

7-
import { SHA3 } from 'sha3'
7+
import cryptojs from 'crypto-js'
8+
import { sha3_256 } from 'js-sha3'
89
import { Account } from './Account'
910

11+
interface IWordArray {
12+
words: Uint32Array
13+
sigBytes: number
14+
}
15+
export function hexToBytes(hex: string): Uint8Array {
16+
if (typeof hex !== 'string') {
17+
throw new TypeError('hexToBytes: expected string, got ' + typeof hex)
18+
}
19+
if (hex.length % 2) throw new Error('hexToBytes: received invalid unpadded hex')
20+
const array = new Uint8Array(hex.length / 2)
21+
for (let i = 0; i < array.length; i++) {
22+
const j = i * 2
23+
const hexByte = hex.slice(j, j + 2)
24+
const byte = Number.parseInt(hexByte, 16)
25+
if (Number.isNaN(byte) || byte < 0) throw new Error('Invalid byte sequence')
26+
array[i] = byte
27+
}
28+
return array
29+
}
30+
1031
/**
1132
* Return true if the number or string is an address value. This does not check the network for a valid
1233
* address, but just checks to see if it is a number
@@ -104,9 +125,7 @@ export function remove0xPrefix(value: string): string {
104125
export function toPublicKeyChecksum(publicKey: string): string {
105126
const publicKeyClean = remove0xPrefix(publicKey).toLowerCase()
106127
let result = '0x'
107-
const hash = new SHA3(256)
108-
hash.update(Buffer.from(publicKeyClean, 'hex'))
109-
const hashData = hash.digest('hex')
128+
const hashData = sha3_256(hexToBytes(publicKeyClean))
110129
for (let index = 0; index < hashData.length && index < publicKeyClean.length; index++) {
111130
if (parseInt(hashData.charAt(index), 16) > 7) {
112131
result = result.concat(publicKeyClean.charAt(index).toUpperCase())
@@ -143,3 +162,28 @@ export function isPublicKey(publicKey: string): boolean {
143162
export function isPublicKeyChecksum(publicKey: string): boolean {
144163
return remove0xPrefix(publicKey) && remove0xPrefix(publicKey) == remove0xPrefix(toPublicKeyChecksum(publicKey))
145164
}
165+
166+
/*
167+
* Code originally copied from crypto-js for conversion Latin1 to and from WordArray
168+
*
169+
*/
170+
export function wordArrayToByteArray(wordArray: IWordArray): Uint8Array {
171+
const words = wordArray.words
172+
const sigBytes = wordArray.sigBytes
173+
// Convert from WordArray object to Uint8Array
174+
const result = new Uint8Array(sigBytes)
175+
for (let i = 0; i < sigBytes; i++) {
176+
const value = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff
177+
result[i] = value
178+
}
179+
return result
180+
}
181+
182+
export function byteArrayToWordArray(data: Uint8Array): IWordArray {
183+
// Convert Uint8Array to a word array object
184+
const words = []
185+
for (let i = 0; i < data.length; i++) {
186+
words[i >>> 2] |= (data[i] & 0xff) << (24 - (i % 4) * 8)
187+
}
188+
return new cryptojs.lib.WordArray.init(words)
189+
}

test/integration/API.test.ts

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,8 @@ import { randomBytes } from 'crypto'
1212
import { API, KeyPair, Account } from '../../src'
1313
import { isAddress, toAddress } from '../../src/Utils'
1414

15-
const PRIVATE_TEST_KEY_TEXT = `
16-
-----BEGIN ENCRYPTED PRIVATE KEY-----
17-
MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAi3qm1zgjCO5gICCAAw
18-
DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEENjvj1nzc0Qy22L+Zi+n7yIEQMLW
19-
o++Jzwlcg3PbW1Y2PxicdFHM3dBOgTWmGsvfZiLhSxTluXTNRCZ8ZLL5pi7JWtCl
20-
JAr4iFzPLkM18YEP2ZE=
21-
-----END ENCRYPTED PRIVATE KEY-----
22-
`
15+
16+
const PRIVATE_ENCRYPTED_KEY = 'U2FsdGVkX1+zZPlLL1zR8ac9kCp+lHWGsjpUwBINwhpnTJWlu4TctG/Zha/8Mx0ZXjMbb73KZN+N/pBawfTmmw=='
2317

2418
const PRIVATE_TEST_KEY_PASSWORD = 'secret'
2519

@@ -29,6 +23,7 @@ const TOPUP_AMOUNT = 10000000
2923
describe('API Class', () => {
3024
describe('requestFunds', async () => {
3125
it('should request funds from a new account', async () => {
26+
3227
const convex = API.create(CONVEX_URL)
3328
const keyPair = await KeyPair.create()
3429
const account = await convex.createAccount(keyPair)
@@ -39,7 +34,7 @@ describe('API Class', () => {
3934

4035
it('should request funds from the test account', async () => {
4136
const convex = API.create(CONVEX_URL)
42-
const importKeyPair = await KeyPair.importFromString(PRIVATE_TEST_KEY_TEXT, PRIVATE_TEST_KEY_PASSWORD)
37+
const importKeyPair = await KeyPair.importFromString(PRIVATE_ENCRYPTED_KEY, PRIVATE_TEST_KEY_PASSWORD)
4338
const account = await convex.createAccount(importKeyPair)
4439
const amount = 1000000
4540
const result = await convex.requestFunds(amount, account)
@@ -58,7 +53,7 @@ describe('API Class', () => {
5853

5954
it('should return a valid checksum address from a query', async () => {
6055
const convex = API.create(CONVEX_URL)
61-
const importKeyPair = await KeyPair.importFromString(PRIVATE_TEST_KEY_TEXT, PRIVATE_TEST_KEY_PASSWORD)
56+
const importKeyPair = await KeyPair.importFromString(PRIVATE_ENCRYPTED_KEY, PRIVATE_TEST_KEY_PASSWORD)
6257
const account = await convex.createAccount(importKeyPair)
6358
const address = account.address
6459
const result = await convex.query(`(address #${address})`, account)
@@ -103,7 +98,7 @@ describe('API Class', () => {
10398
let account
10499
before( async () => {
105100
convex = API.create(CONVEX_URL)
106-
const importKeyPair = await KeyPair.importFromString(PRIVATE_TEST_KEY_TEXT, PRIVATE_TEST_KEY_PASSWORD)
101+
const importKeyPair = await KeyPair.importFromString(PRIVATE_ENCRYPTED_KEY, PRIVATE_TEST_KEY_PASSWORD)
107102
account = await convex.createAccount(importKeyPair)
108103
await convex.topupAccount(account)
109104
})
@@ -151,7 +146,7 @@ describe('API Class', () => {
151146
let functionAddress
152147
before( async () => {
153148
convex = API.create(CONVEX_URL)
154-
const importKeyPair = await KeyPair.importFromString(PRIVATE_TEST_KEY_TEXT, PRIVATE_TEST_KEY_PASSWORD)
149+
const importKeyPair = await KeyPair.importFromString(PRIVATE_ENCRYPTED_KEY, PRIVATE_TEST_KEY_PASSWORD)
155150
account = await convex.createAccount(importKeyPair)
156151
await convex.topupAccount(account)
157152
functionName = 'test-storage'
@@ -204,7 +199,7 @@ describe('API Class', () => {
204199
let accountName
205200
before( async () => {
206201
convex = API.create(CONVEX_URL)
207-
importKeyPair = await KeyPair.importFromString(PRIVATE_TEST_KEY_TEXT, PRIVATE_TEST_KEY_PASSWORD)
202+
importKeyPair = await KeyPair.importFromString(PRIVATE_ENCRYPTED_KEY, PRIVATE_TEST_KEY_PASSWORD)
208203
accountName = 'test.convex-api.' + randomBytes(4).toString('hex')
209204
})
210205
it('should fail to reslove a new named account', async() => {
@@ -224,7 +219,7 @@ describe('API Class', () => {
224219
let newAccount: Account
225220
before( async () => {
226221
convex = API.create(CONVEX_URL)
227-
importKeyPair = await KeyPair.importFromString(PRIVATE_TEST_KEY_TEXT, PRIVATE_TEST_KEY_PASSWORD)
222+
importKeyPair = await KeyPair.importFromString(PRIVATE_ENCRYPTED_KEY, PRIVATE_TEST_KEY_PASSWORD)
228223
accountName = 'test.convex-api.' + randomBytes(4).toString('hex')
229224
})
230225
it('should setup and setup/create a new named account', async() => {
@@ -278,7 +273,7 @@ describe('API Class', () => {
278273
let account
279274
before( async () => {
280275
convex = API.create(CONVEX_URL)
281-
const importKeyPair = await KeyPair.importFromString(PRIVATE_TEST_KEY_TEXT, PRIVATE_TEST_KEY_PASSWORD)
276+
const importKeyPair = await KeyPair.importFromString(PRIVATE_ENCRYPTED_KEY, PRIVATE_TEST_KEY_PASSWORD)
282277
account = await convex.createAccount(importKeyPair)
283278
await convex.requestFunds(TOPUP_AMOUNT, account)
284279
})
@@ -300,7 +295,7 @@ describe('API Class', () => {
300295
let accountFrom
301296
before( async () => {
302297
convex = API.create(CONVEX_URL)
303-
const importKeyPair = await KeyPair.importFromString(PRIVATE_TEST_KEY_TEXT, PRIVATE_TEST_KEY_PASSWORD)
298+
const importKeyPair = await KeyPair.importFromString(PRIVATE_ENCRYPTED_KEY, PRIVATE_TEST_KEY_PASSWORD)
304299
accountFrom = await convex.createAccount(importKeyPair)
305300
await convex.topupAccount(accountFrom)
306301
})
@@ -322,7 +317,7 @@ describe('API Class', () => {
322317
let account
323318
before( async () => {
324319
convex = API.create(CONVEX_URL)
325-
const importKeyPair = await KeyPair.importFromString(PRIVATE_TEST_KEY_TEXT, PRIVATE_TEST_KEY_PASSWORD)
320+
const importKeyPair = await KeyPair.importFromString(PRIVATE_ENCRYPTED_KEY, PRIVATE_TEST_KEY_PASSWORD)
326321
account = await convex.createAccount(importKeyPair)
327322
await convex.topupAccount(account)
328323
})
@@ -348,7 +343,7 @@ describe('API Class', () => {
348343
let importKeyPair
349344
before( async () => {
350345
convex = API.create(CONVEX_URL)
351-
importKeyPair = await KeyPair.importFromString(PRIVATE_TEST_KEY_TEXT, PRIVATE_TEST_KEY_PASSWORD)
346+
importKeyPair = await KeyPair.importFromString(PRIVATE_ENCRYPTED_KEY, PRIVATE_TEST_KEY_PASSWORD)
352347
})
353348
it('should run multiple createAccount test', async () => {
354349
const requestCount = 20

test/unit/BasicCrypto.test.ts

Lines changed: 0 additions & 64 deletions
This file was deleted.

0 commit comments

Comments
 (0)