diff --git a/docs/guides/error-handling.md b/docs/guides/error-handling.md index 755d220cc6..afce0bf6de 100644 --- a/docs/guides/error-handling.md +++ b/docs/guides/error-handling.md @@ -68,11 +68,9 @@ BaseError └───CryptographyError │ │ InvalidChecksumError │ │ InvalidDerivationPathError -│ │ InvalidKeyError │ │ InvalidPasswordError │ │ MerkleTreeHashMismatchError │ │ MissingNodeInTreeError -│ │ UnsupportedAlgorithmError │ │ NotHardenedSegmentError │ │ UnknownNodeLengthError │ │ UnknownPathNibbleError diff --git a/package-lock.json b/package-lock.json index 662ccdaf72..2bf34a8639 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,6 +46,7 @@ "@commitlint/cli": "^16.1.0", "@commitlint/config-conventional": "^16.0.0", "@types/aes-js": "^3.1.1", + "@types/argon2-browser": "^1.18.1", "@types/bn.js": "^5.1.0", "@types/bs58": "^4.0.1", "@types/chai": "^4.3.0", @@ -53,6 +54,7 @@ "@types/mocha": "^9.1.0", "@types/node": "^17.0.17", "@types/sha.js": "^2.4.0", + "@types/uuid": "^8.3.4", "@typescript-eslint/eslint-plugin": "^5.11.0", "@typescript-eslint/parser": "^5.11.0", "babel-loader": "^8.2.3", @@ -2841,6 +2843,12 @@ "integrity": "sha512-SDSGgXT3LRCH6qMWk8OHT1vLSVNuHNvCpKCx2/TYtQMbMGGgxJC9fspwSkQjqzRagrWnCrxuLL3jMNXLXHHvSw==", "dev": true }, + "node_modules/@types/argon2-browser": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@types/argon2-browser/-/argon2-browser-1.18.1.tgz", + "integrity": "sha512-PZffP/CqH9m2kovDSRQMfMMxUC3V98I7i7/caa0RB0/nvsXzYbL9bKyqZpNMFmLFGZslROlG1R60ONt7abrwlA==", + "dev": true + }, "node_modules/@types/bn.js": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", @@ -2971,6 +2979,12 @@ "@types/node": "*" } }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.11.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.11.0.tgz", @@ -14094,6 +14108,12 @@ "integrity": "sha512-SDSGgXT3LRCH6qMWk8OHT1vLSVNuHNvCpKCx2/TYtQMbMGGgxJC9fspwSkQjqzRagrWnCrxuLL3jMNXLXHHvSw==", "dev": true }, + "@types/argon2-browser": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@types/argon2-browser/-/argon2-browser-1.18.1.tgz", + "integrity": "sha512-PZffP/CqH9m2kovDSRQMfMMxUC3V98I7i7/caa0RB0/nvsXzYbL9bKyqZpNMFmLFGZslROlG1R60ONt7abrwlA==", + "dev": true + }, "@types/bn.js": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", @@ -14224,6 +14244,12 @@ "@types/node": "*" } }, + "@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { "version": "5.11.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.11.0.tgz", diff --git a/package.json b/package.json index b9049f4a62..44b796a60b 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "@commitlint/cli": "^16.1.0", "@commitlint/config-conventional": "^16.0.0", "@types/aes-js": "^3.1.1", + "@types/argon2-browser": "^1.18.1", "@types/bn.js": "^5.1.0", "@types/bs58": "^4.0.1", "@types/chai": "^4.3.0", @@ -81,6 +82,7 @@ "@types/mocha": "^9.1.0", "@types/node": "^17.0.17", "@types/sha.js": "^2.4.0", + "@types/uuid": "^8.3.4", "@typescript-eslint/eslint-plugin": "^5.11.0", "@typescript-eslint/parser": "^5.11.0", "babel-loader": "^8.2.3", diff --git a/src/typings/@aeternity__argon2-browser/index.d.ts b/src/typings/@aeternity__argon2-browser/index.d.ts new file mode 100644 index 0000000000..9a8fda7964 --- /dev/null +++ b/src/typings/@aeternity__argon2-browser/index.d.ts @@ -0,0 +1,3 @@ +declare module '@aeternity/argon2-browser/dist/argon2-bundled.min.js' { + export * from 'argon2-browser' +} diff --git a/src/typings/@aeternity__uuid/index.d.ts b/src/typings/@aeternity__uuid/index.d.ts new file mode 100644 index 0000000000..f4e0102b14 --- /dev/null +++ b/src/typings/@aeternity__uuid/index.d.ts @@ -0,0 +1,3 @@ +declare module '@aeternity/uuid' { + export * from 'uuid' +} diff --git a/src/utils/bytes.ts b/src/utils/bytes.ts index 425c76ff99..db4be21449 100644 --- a/src/utils/bytes.ts +++ b/src/utils/bytes.ts @@ -72,3 +72,7 @@ export function str2buf (str: string, enc?: BufferEncoding): Buffer { enc ?? (isHex(str) ? 'hex' : undefined) ?? (isBase64(str) ? 'base64' : undefined) ) } + +export const bytesToHex = (b: Uint8Array): string => Buffer.from(b).toString('hex') + +export const hexToBytes = (s: string): Uint8Array => Buffer.from(s, 'hex') diff --git a/src/utils/crypto.ts b/src/utils/crypto.ts index b2aa6c9bac..d705f23483 100644 --- a/src/utils/crypto.ts +++ b/src/utils/crypto.ts @@ -36,14 +36,13 @@ const Ecb = aesjs.ModeOfOperation.ecb /** * Generate address from secret key * @rtype (secret: String) => tx: Promise[String] - * @param {String | Buffer} secret - Private key + * @param {String | Uint8Array} secret - Private key * @return {String} Public key */ -export function getAddressFromPriv (secret: string | Buffer): string { - const secretBuffer = Buffer.isBuffer(secret) ? secret : str2buf(secret) +export function getAddressFromPriv (secret: string | Uint8Array): string { + const secretBuffer = typeof secret === 'string' ? str2buf(secret) : secret const keys = nacl.sign.keyPair.fromSecretKey(secretBuffer) - const publicBuffer = Buffer.from(keys.publicKey) - return encode(publicBuffer, 'ak') + return encode(keys.publicKey, 'ak') } /** diff --git a/src/utils/errors.ts b/src/utils/errors.ts index dc0718257c..e71df8d426 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -448,16 +448,9 @@ export class InvalidDerivationPathError extends CryptographyError { } } -export class InvalidKeyError extends CryptographyError { - constructor (message: string) { - super(message) - this.name = 'InvalidKeyError' - } -} - export class InvalidPasswordError extends CryptographyError { - constructor (message: string) { - super(message) + constructor () { + super('Invalid password or nonce') this.name = 'InvalidPasswordError' } } @@ -476,13 +469,6 @@ export class MissingNodeInTreeError extends CryptographyError { } } -export class UnsupportedAlgorithmError extends CryptographyError { - constructor (algo: string) { - super(algo + ' is not available') - this.name = 'UnsupportedAlgorithmError' - } -} - export class NotHardenedSegmentError extends CryptographyError { constructor (message: string) { super(message) diff --git a/src/utils/keystore.js b/src/utils/keystore.js deleted file mode 100644 index b85e841a7c..0000000000 --- a/src/utils/keystore.js +++ /dev/null @@ -1,213 +0,0 @@ -import nacl from 'tweetnacl' -import { v4 as uuid } from '@aeternity/uuid' -import { ArgonType, hash } from '@aeternity/argon2-browser/dist/argon2-bundled.min.js' -import { getAddressFromPriv } from './crypto' -import { str2buf } from './bytes' -import { - ArgumentError, InvalidKeyError, UnsupportedAlgorithmError, InvalidPasswordError -} from './errors' - -/** - * KeyStore module - * @module @aeternity/aepp-sdk/es/utils/keystore - * @example import { recover } from '@aeternity/aepp-sdk' - * @example const { recover } = require('@aeternity/aepp-sdk') - */ - -const DEFAULTS = { - crypto: { - secret_type: 'ed25519', - symmetric_alg: 'xsalsa20-poly1305', - kdf: 'argon2id', - kdf_params: { - memlimit_kib: 65536, - opslimit: 3, - parallelism: 1 - } - } -} - -// DERIVED KEY PART -const DERIVED_KEY_FUNCTIONS = { - argon2id: deriveKeyUsingArgon2id -} - -export async function deriveKeyUsingArgon2id (pass, salt, options) { - const { memlimit_kib: mem, opslimit: time } = options.kdf_params - - const result = (await hash({ - hashLen: 32, - pass, - salt, - time, - mem, - type: ArgonType.Argon2id - })).hash - return Buffer.from(result) -} - -// CRYPTO PART -const CRYPTO_FUNCTIONS = { - 'xsalsa20-poly1305': { encrypt: encryptXsalsa20Poly1305, decrypt: decryptXsalsa20Poly1305 } -} - -function encryptXsalsa20Poly1305 ({ plaintext, key, nonce }) { - return nacl.secretbox(plaintext, nonce, key) -} - -function decryptXsalsa20Poly1305 ({ ciphertext, key, nonce }) { - const res = nacl.secretbox.open(ciphertext, nonce, key) - if (!res) throw new InvalidPasswordError('Invalid password or nonce') - return res -} - -/** - * Symmetric private key encryption using secret (derived) key. - * @param {Buffer|string} plaintext Data to be encrypted. - * @param {Buffer|string} key Secret key. - * @param {Buffer|string} nonce Randomly generated nonce. - * @param {string=} algo Encryption algorithm (default: DEFAULTS.crypto.symmetric_alg). - * @return {Buffer} Encrypted data. - */ -function encrypt (plaintext, key, nonce, algo = DEFAULTS.crypto.symmetric_alg) { - if (!CRYPTO_FUNCTIONS[algo]) throw new UnsupportedAlgorithmError(algo) - return CRYPTO_FUNCTIONS[algo].encrypt({ plaintext, nonce, key }) -} - -/** - * Symmetric private key decryption using secret (derived) key. - * @param {Buffer|Uint8Array} ciphertext Data to be decrypted. - * @param {Buffer|Uint8Array} key Secret key. - * @param {Buffer|Uint8Array} nonce Nonce from key-object. - * @param {string=} algo Encryption algorithm. - * @return {Buffer} Decrypted data. - */ -function decrypt (ciphertext, key, nonce, algo) { - if (!CRYPTO_FUNCTIONS[algo]) throw new UnsupportedAlgorithmError(algo) - return CRYPTO_FUNCTIONS[algo].decrypt({ ciphertext, nonce, key }) -} - -/** - * Derive secret key from password with key derivation function. - * @param {string} password User-supplied password. - * @param {Buffer|Uint8Array} nonce Randomly generated nonce. - * @param {Object=} options Encryption parameters. - * @param {string=} options.kdf Key derivation function (default: DEFAULTS.crypto.kdf). - * @param {Object=} options.kdf_params KDF parameters (default: DEFAULTS.crypto.kdf_params). - * @return {Buffer} Secret key derived from password. - */ -async function deriveKey (password, nonce, options = { - kdf_params: DEFAULTS.crypto.kdf_params, - kdf: DEFAULTS.crypto.kdf -}) { - if (!nonce) throw new ArgumentError('nonce', 'provided', nonce) - if (password == null) throw new ArgumentError('password', 'provided', password) - - if (!Object.prototype.hasOwnProperty.call(DERIVED_KEY_FUNCTIONS, options.kdf)) { - throw new UnsupportedAlgorithmError(options.kdf) - } - - return DERIVED_KEY_FUNCTIONS[options.kdf](password, nonce, options) -} - -/** - * Assemble key data object in secret-storage format. - * @param {Buffer} name Key name. - * @param {Buffer} derivedKey Password-derived secret key. - * @param {Buffer} privateKey Private key. - * @param {Buffer} nonce Randomly generated 24byte nonce. - * @param {Buffer} salt Randomly generated 16byte salt. - * @param {Object=} options Encryption parameters. - * @param {string=} options.kdf Key derivation function (default: argon2id). - * @param {string=} options.cipher Symmetric cipher (default: constants.cipher). - * @param {Object=} options.kdf_params KDF parameters (default: constants.). - * @return {Object} - */ -function marshal (name, derivedKey, privateKey, nonce, salt, options = {}) { - const opt = Object.assign({}, DEFAULTS.crypto, options) - return Object.assign( - { name, version: 1, public_key: getAddressFromPriv(privateKey), id: uuid() }, - { - crypto: Object.assign( - { - secret_type: opt.secret_type, - symmetric_alg: opt.symmetric_alg, - ciphertext: Buffer.from(encrypt(Buffer.from(privateKey), derivedKey, nonce, opt.symmetric_alg)).toString('hex'), - cipher_params: { nonce: Buffer.from(nonce).toString('hex') } - }, - { kdf: opt.kdf, kdf_params: { ...opt.kdf_params, salt: Buffer.from(salt).toString('hex') } } - ) - } - ) -} - -/** - * Recover plaintext private key from secret-storage key object. - * @param {String} password Keystore object password. - * @param {Object} keyObject Keystore object. - * @return {Buffer} Plaintext private key. - */ -export async function recover (password, keyObject) { - validateKeyObj(keyObject) - const nonce = Uint8Array.from(str2buf(keyObject.crypto.cipher_params.nonce)) - const salt = Uint8Array.from(str2buf(keyObject.crypto.kdf_params.salt)) - const kdfParams = keyObject.crypto.kdf_params - const kdf = keyObject.crypto.kdf - - const key = await decrypt( - Uint8Array.from(str2buf(keyObject.crypto.ciphertext)), - await deriveKey(password, salt, { kdf, kdf_params: kdfParams }), - nonce, - keyObject.crypto.symmetric_alg - ) - if (!key) throw new InvalidPasswordError('Invalid password') - - if (Buffer.from(key).length === 64) return Buffer.from(key).toString('hex') - return Buffer.from(key).toString('utf-8') -} - -/** - * Export private key to keystore secret-storage format. - * @param {String} name Key name. - * @param {String} password User-supplied password. - * @param {String|Buffer} privateKey Private key as hex-string or a Buffer. - * @param {Buffer} nonce Randomly generated 24byte nonce. - * @param {Buffer} salt Randomly generated 16byte salt. - * @param {Object=} options Encryption parameters. - * @param {String=} options.kdf Key derivation function (default: pbkdf2). - * @param {String=} options.cipher Symmetric cipher (default: constants.cipher). - * @param {Object=} options.kdfparams KDF parameters (default: constants.). - * @return {Object} - */ -export async function dump ( - name, - password, - privateKey, - nonce = nacl.randomBytes(24), - salt = nacl.randomBytes(16), - options = {} -) { - const opt = Object.assign({}, DEFAULTS.crypto, options) - return marshal( - name, - await deriveKey(password, salt, opt), - typeof privateKey === 'string' ? Buffer.from(privateKey, 'hex') : privateKey, - nonce, - salt, - opt - ) -} - -export function validateKeyObj (obj) { - const root = ['crypto', 'id', 'version', 'public_key'] - const cryptoKeys = ['cipher_params', 'ciphertext', 'symmetric_alg', 'kdf', 'kdf_params'] - - const missingRootKeys = root.filter(key => !Object.prototype.hasOwnProperty.call(obj, key)) - if (missingRootKeys.length) throw new InvalidKeyError(`Invalid key file format. Require properties: ${missingRootKeys}`) - - const missingCryptoKeys = cryptoKeys - .filter(key => !Object.prototype.hasOwnProperty.call(obj.crypto, key)) - if (missingCryptoKeys.length) throw new InvalidKeyError(`Invalid key file format. Require properties: ${missingCryptoKeys}`) - - return true -} diff --git a/src/utils/keystore.ts b/src/utils/keystore.ts new file mode 100644 index 0000000000..138ee12291 --- /dev/null +++ b/src/utils/keystore.ts @@ -0,0 +1,187 @@ +import nacl from 'tweetnacl' +import { v4 as uuid } from '@aeternity/uuid' +import { ArgonType, hash } from '@aeternity/argon2-browser/dist/argon2-bundled.min.js' +import { getAddressFromPriv } from './crypto' +import { bytesToHex, hexToBytes } from './bytes' +import { InvalidPasswordError } from './errors' + +/** + * KeyStore module + * @module @aeternity/aepp-sdk/es/utils/keystore + * @example import { recover } from '@aeternity/aepp-sdk' + * @example const { recover } = require('@aeternity/aepp-sdk') + */ + +const DERIVED_KEY_FUNCTIONS = { + async argon2id ( + pass: string | Uint8Array, + salt: string | Uint8Array, + params: Partial + ): Promise { + const { memlimit_kib: mem, opslimit: time } = params + + return (await hash({ + hashLen: 32, + pass, + salt, + time, + mem, + type: ArgonType.Argon2id + })).hash + } +} + +const CRYPTO_FUNCTIONS = { + 'xsalsa20-poly1305': { + encrypt: nacl.secretbox, + decrypt (...args: Parameters): Uint8Array { + const res = nacl.secretbox.open(...args) + if (res == null) throw new InvalidPasswordError() + return res + } + } +} + +export interface Keystore { + name: string + version: 1 + public_key: string + id: string + crypto: { + secret_type: 'ed25519' + symmetric_alg: keyof typeof CRYPTO_FUNCTIONS + ciphertext: string + cipher_params: { + nonce: string + } + kdf: keyof typeof DERIVED_KEY_FUNCTIONS + kdf_params: { + memlimit_kib: number + opslimit: number + parallelism: number + salt: string + } + } +} + +const CRYPTO_DEFAULTS = { + secret_type: 'ed25519', + symmetric_alg: 'xsalsa20-poly1305', + kdf: 'argon2id', + kdf_params: { + memlimit_kib: 65536, + opslimit: 3, + parallelism: 1 + } +} as const + +/** + * Symmetric private key encryption using secret (derived) key. + * @param {Uint8Array} plaintext Data to be encrypted. + * @param {Uint8Array} key Secret key. + * @param {Uint8Array} nonce Randomly generated nonce. + * @param {String} [algo=xsalsa20-poly1305] Encryption algorithm. + * @return {Uint8Array} Encrypted data. + */ +function encrypt ( + plaintext: Uint8Array, + key: Uint8Array, + nonce: Uint8Array, + algo: keyof typeof CRYPTO_FUNCTIONS = CRYPTO_DEFAULTS.symmetric_alg +): Uint8Array { + return CRYPTO_FUNCTIONS[algo].encrypt(plaintext, nonce, key) +} + +/** + * Symmetric private key decryption using secret (derived) key. + * @param {Uint8Array} ciphertext Data to be decrypted. + * @param {Uint8Array} key Secret key. + * @param {Uint8Array} nonce Nonce from key-object. + * @param {String} [algo=xsalsa20-poly1305] Encryption algorithm. + * @return {Buffer} Decrypted data. + */ +function decrypt ( + ciphertext: Uint8Array, + key: Uint8Array, + nonce: Uint8Array, + algo: keyof typeof CRYPTO_FUNCTIONS = CRYPTO_DEFAULTS.symmetric_alg +): Uint8Array { + return CRYPTO_FUNCTIONS[algo].decrypt(ciphertext, nonce, key) +} + +/** + * Derive secret key from password with key derivation function. + * @param {String|Uint8Array} password User-supplied password. + * @param {String|Uint8Array} nonce Randomly generated nonce. + * @param {String} kdf Key derivation function. + * @param {Object} kdfParams KDF parameters. + * @return {Uint8Array} Secret key derived from password. + */ +async function deriveKey ( + password: string | Uint8Array, + nonce: string | Uint8Array, + kdf: Keystore['crypto']['kdf'], + kdfParams: Omit +): Promise { + return await DERIVED_KEY_FUNCTIONS[kdf](password, nonce, kdfParams) +} + +/** + * Recover plaintext private key from secret-storage key object. + * @param {String|Uint8Array} password Keystore object password. + * @param {Object} keystore Keystore object. + * @return {Buffer} Plaintext private key. + */ +export async function recover ( + password: string | Uint8Array, { crypto }: Keystore +): Promise { + const salt = hexToBytes(crypto.kdf_params.salt) + return bytesToHex(decrypt( + hexToBytes(crypto.ciphertext), + await deriveKey(password, salt, crypto.kdf, crypto.kdf_params), + hexToBytes(crypto.cipher_params.nonce), + crypto.symmetric_alg + )) +} + +/** + * Export private key to keystore secret-storage format. + * @param {String} name Key name. + * @param {String|Uint8Array} password User-supplied password. + * @param {String|Uint8Array} privateKey Private key as hex-string or a Buffer. + * @param {Buffer} nonce Randomly generated 24byte nonce. + * @param {Buffer} salt Randomly generated 16byte salt. + * @param {Partial} [options] Encryption parameters. + * @param {String} [options.kdf=argon2id] Key derivation function. + * @param {Object} [options.kdf_params] KDF parameters. + * @return {Object} + */ +export async function dump ( + name: string, + password: string | Uint8Array, + privateKey: string | Uint8Array, + nonce: Uint8Array = nacl.randomBytes(24), + salt: Uint8Array = nacl.randomBytes(16), + options?: Partial +): Promise { + const opt = { ...CRYPTO_DEFAULTS, ...options } + const derivedKey = await deriveKey(password, salt, opt.kdf, opt.kdf_params) + const payload = typeof privateKey === 'string' ? hexToBytes(privateKey) : privateKey + return { + name, + version: 1, + public_key: getAddressFromPriv(payload), + id: uuid(), + crypto: { + secret_type: opt.secret_type, + symmetric_alg: opt.symmetric_alg, + ciphertext: bytesToHex(encrypt(payload, derivedKey, nonce, opt.symmetric_alg)), + cipher_params: { nonce: bytesToHex(nonce) }, + kdf: opt.kdf, + kdf_params: { + ...opt.kdf_params, + salt: bytesToHex(salt) + } + } + } +} diff --git a/test/unit/keystore.js b/test/unit/keystore.ts similarity index 74% rename from test/unit/keystore.js rename to test/unit/keystore.ts index 01308c6eb8..169a8ca326 100644 --- a/test/unit/keystore.js +++ b/test/unit/keystore.ts @@ -15,31 +15,13 @@ * PERFORMANCE OF THIS SOFTWARE. */ -import '../' +import '..' import { describe, it } from 'mocha' import { expect } from 'chai' -import { dump, recover, validateKeyObj } from '../../src/utils/keystore' +import { dump, recover, Keystore } from '../../src/utils/keystore' import { getAddressFromPriv } from '../../src/utils/crypto' -import { InvalidKeyError, InvalidPasswordError } from '../../src/utils/errors' +import { InvalidPasswordError } from '../../src/utils/errors' -const invalidKeystore = { - name: 'test', - version: 1, - public_key: 'ak_2wc5GeyFTxYEqusWH8UizUQDj6i53ow7fF9WXEPtYVvSHT45xd', - id: 'ea6b7079-924e-456c-8100-6305e7235d65', - crypto: { - secret_type: 'ed25519', - cipher_params: { - nonce: 'fecd060551378963f5d4d1aa264b665225360775fcd5fa5a' - }, - kdf: 'argon2id', - kdf_params: { - memlimit: 1024, - opslimit: 3, - salt: 'aa0885ba58e497ea83cd663d1dd4d002' - } - } -} const password = 'test' const secretKey = Buffer.from('35bdc4b31d75aebea2693760a2c96afe87d99dc571ddc4666db0ac8a2b59b30ef1e0c4e567f3d08eff8330c57d70ad457e9f31fa221e14fcc851273ec9af50ae', 'hex') const address = getAddressFromPriv(secretKey) @@ -64,12 +46,11 @@ const keystoreStatic = { } describe('Keystore', () => { - let keystore + let keystore: Keystore it('dump account to keystore object', async () => { keystore = await dump('test', password, secretKey) expect(keystore.public_key).to.be.equal(address) - validateKeyObj(keystore).should.be.equal(true) }) it('dump accepts hex', async () => { @@ -83,9 +64,6 @@ describe('Keystore', () => { it('restore account from keystore object', async () => expect(await recover(password, keystore)).to.be.equal(secretKey.toString('hex'))) - it('use invalid keystore json', () => expect(recover(password, invalidKeystore)) - .to.be.rejectedWith(InvalidKeyError, 'Invalid key file format. Require properties: ciphertext,symmetric_alg')) - - it('use invalid keystore password', () => expect(recover(password + 1, keystore)) + it('use invalid keystore password', () => expect(recover(password + '1', keystore)) .to.be.rejectedWith(InvalidPasswordError, 'Invalid password or nonce')) })