From 5b41280a3c7859e6d16f02bc9095c1bef042cba4 Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 15 Jun 2021 10:20:36 +0300 Subject: [PATCH 1/9] Cleanup package.json --- .gitignore | 1 + package.json | 80 ++++++++++++++++++++++++++-------------------------- 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/.gitignore b/.gitignore index 9e73c4df..11da88ae 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ out out-tests out-browser out-browser-tests +node_modules/ \ No newline at end of file diff --git a/package.json b/package.json index daff44d3..fecbfbf9 100644 --- a/package.json +++ b/package.json @@ -27,48 +27,48 @@ "author": "ElrondNetwork", "license": "GPL-3.0-or-later", "dependencies": { - "@babel/runtime": "^7.11.2", - "@elrondnetwork/bls-wasm": "^0.3.3", + "@babel/runtime": "7.11.2", + "@elrondnetwork/bls-wasm": "0.3.3", "@elrondnetwork/hw-app-elrond": "0.2.0", - "@ledgerhq/hw-transport-u2f": "^5.28.0", - "@ledgerhq/hw-transport-webusb": "^5.28.0", - "@walletconnect/client": "^1.4.1", - "abort-controller": "^3.0.0", - "axios": "^0.21.1", - "bech32": "^1.1.4", - "bignumber.js": "^9.0.0", - "bip39": "^3.0.2", - "blake2b": "^2.1.3", - "ed25519-hd-key": "^1.1.2", - "json-bigint": "^1.0.0", - "json-duplicate-key-handle": "^1.0.0", - "keccak": "^3.0.1", - "platform": "^1.3.6", - "protobufjs": "^6.10.2", - "scryptsy": "^2.1.0", - "tweetnacl": "^1.0.3", - "uuid": "^8.3.2" + "@ledgerhq/hw-transport-u2f": "5.28.0", + "@ledgerhq/hw-transport-webusb": "5.28.0", + "@walletconnect/client": "1.4.1", + "abort-controller": "3.0.0", + "axios": "0.21.1", + "bech32": "1.1.4", + "bignumber.js": "9.0.1", + "bip39": "3.0.2", + "blake2b": "2.1.3", + "ed25519-hd-key": "1.1.2", + "json-bigint": "1.0.0", + "json-duplicate-key-handle": "1.0.0", + "keccak": "3.0.1", + "platform": "1.3.6", + "protobufjs": "6.10.2", + "scryptsy": "2.1.0", + "tweetnacl": "1.0.3", + "uuid": "8.3.2" }, "devDependencies": { - "@types/assert": "^1.4.6", - "@types/chai": "^4.2.11", - "@types/ledgerhq__hw-transport-u2f": "^4.21.2", - "@types/ledgerhq__hw-transport-webusb": "^4.70.1", - "@types/mocha": "^7.0.2", - "@types/node": "^13.13.2", - "@types/platform": "^1.3.3", - "@types/protobufjs": "^6.0.0", - "@types/scryptsy": "^2.0.0", - "@types/uuid": "^8.3.0", - "assert": "^2.0.0", - "browserify": "^17.0.0", - "buffer": "^6.0.3", - "chai": "^4.2.0", - "mocha": "^7.1.1", - "tinyify": "^3.0.0", - "ts-node": "^9.1.1", - "tslint": "^6.1.3", - "typedoc": "^0.20.36", - "typescript": "^4.1.2" + "@types/assert": "1.4.6", + "@types/chai": "4.2.11", + "@types/ledgerhq__hw-transport-u2f": "4.21.2", + "@types/ledgerhq__hw-transport-webusb": "4.70.1", + "@types/mocha": "7.0.2", + "@types/node": "13.13.2", + "@types/platform": "1.3.3", + "@types/protobufjs": "6.0.0", + "@types/scryptsy": "2.0.0", + "@types/uuid": "8.3.0", + "assert": "2.0.0", + "browserify": "17.0.0", + "buffer": "6.0.3", + "chai": "4.2.0", + "mocha": "7.1.1", + "tinyify": "3.0.0", + "ts-node": "9.1.1", + "tslint": "6.1.3", + "typedoc": "0.20.36", + "typescript": "4.1.2" } } From 96f1f173c3b5c5b580c009ff9b507f42c8ad80d6 Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 15 Jun 2021 10:26:56 +0300 Subject: [PATCH 2/9] Update package lock --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index bde09766..2a44179f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1059,9 +1059,9 @@ "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==" }, "bignumber.js": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", - "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", + "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==" }, "binary": { "version": "0.3.0", From 3b968d270d54d83a3e2057769ba73a5640ee760f Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 15 Jun 2021 10:32:45 +0300 Subject: [PATCH 3/9] Add empty line --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 11da88ae..c2f17950 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ out out-tests out-browser out-browser-tests -node_modules/ \ No newline at end of file +node_modules/ From 0ddb68b31f4c878baab2e48dde2d2f306f958f3f Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 15 Jun 2021 10:57:02 +0300 Subject: [PATCH 4/9] Revert "Cleanup package.json" --- .gitignore | 1 - package-lock.json | 6 ++-- package.json | 80 +++++++++++++++++++++++------------------------ 3 files changed, 43 insertions(+), 44 deletions(-) diff --git a/.gitignore b/.gitignore index c2f17950..9e73c4df 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,3 @@ out out-tests out-browser out-browser-tests -node_modules/ diff --git a/package-lock.json b/package-lock.json index 2a44179f..bde09766 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1059,9 +1059,9 @@ "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==" }, "bignumber.js": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", - "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==" + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" }, "binary": { "version": "0.3.0", diff --git a/package.json b/package.json index fecbfbf9..daff44d3 100644 --- a/package.json +++ b/package.json @@ -27,48 +27,48 @@ "author": "ElrondNetwork", "license": "GPL-3.0-or-later", "dependencies": { - "@babel/runtime": "7.11.2", - "@elrondnetwork/bls-wasm": "0.3.3", + "@babel/runtime": "^7.11.2", + "@elrondnetwork/bls-wasm": "^0.3.3", "@elrondnetwork/hw-app-elrond": "0.2.0", - "@ledgerhq/hw-transport-u2f": "5.28.0", - "@ledgerhq/hw-transport-webusb": "5.28.0", - "@walletconnect/client": "1.4.1", - "abort-controller": "3.0.0", - "axios": "0.21.1", - "bech32": "1.1.4", - "bignumber.js": "9.0.1", - "bip39": "3.0.2", - "blake2b": "2.1.3", - "ed25519-hd-key": "1.1.2", - "json-bigint": "1.0.0", - "json-duplicate-key-handle": "1.0.0", - "keccak": "3.0.1", - "platform": "1.3.6", - "protobufjs": "6.10.2", - "scryptsy": "2.1.0", - "tweetnacl": "1.0.3", - "uuid": "8.3.2" + "@ledgerhq/hw-transport-u2f": "^5.28.0", + "@ledgerhq/hw-transport-webusb": "^5.28.0", + "@walletconnect/client": "^1.4.1", + "abort-controller": "^3.0.0", + "axios": "^0.21.1", + "bech32": "^1.1.4", + "bignumber.js": "^9.0.0", + "bip39": "^3.0.2", + "blake2b": "^2.1.3", + "ed25519-hd-key": "^1.1.2", + "json-bigint": "^1.0.0", + "json-duplicate-key-handle": "^1.0.0", + "keccak": "^3.0.1", + "platform": "^1.3.6", + "protobufjs": "^6.10.2", + "scryptsy": "^2.1.0", + "tweetnacl": "^1.0.3", + "uuid": "^8.3.2" }, "devDependencies": { - "@types/assert": "1.4.6", - "@types/chai": "4.2.11", - "@types/ledgerhq__hw-transport-u2f": "4.21.2", - "@types/ledgerhq__hw-transport-webusb": "4.70.1", - "@types/mocha": "7.0.2", - "@types/node": "13.13.2", - "@types/platform": "1.3.3", - "@types/protobufjs": "6.0.0", - "@types/scryptsy": "2.0.0", - "@types/uuid": "8.3.0", - "assert": "2.0.0", - "browserify": "17.0.0", - "buffer": "6.0.3", - "chai": "4.2.0", - "mocha": "7.1.1", - "tinyify": "3.0.0", - "ts-node": "9.1.1", - "tslint": "6.1.3", - "typedoc": "0.20.36", - "typescript": "4.1.2" + "@types/assert": "^1.4.6", + "@types/chai": "^4.2.11", + "@types/ledgerhq__hw-transport-u2f": "^4.21.2", + "@types/ledgerhq__hw-transport-webusb": "^4.70.1", + "@types/mocha": "^7.0.2", + "@types/node": "^13.13.2", + "@types/platform": "^1.3.3", + "@types/protobufjs": "^6.0.0", + "@types/scryptsy": "^2.0.0", + "@types/uuid": "^8.3.0", + "assert": "^2.0.0", + "browserify": "^17.0.0", + "buffer": "^6.0.3", + "chai": "^4.2.0", + "mocha": "^7.1.1", + "tinyify": "^3.0.0", + "ts-node": "^9.1.1", + "tslint": "^6.1.3", + "typedoc": "^0.20.36", + "typescript": "^4.1.2" } } From 83ecaefe267c2c7834621501bc77a8091d21d265 Mon Sep 17 00:00:00 2001 From: Cristi Corcoveanu Date: Fri, 18 Jun 2021 14:41:41 +0300 Subject: [PATCH 5/9] implemented encryption component --- .gitignore | 3 + src/crypto/constants.ts | 5 ++ src/crypto/decryptor.ts | 27 +++++++ src/crypto/derivationParams.ts | 36 ++++++++++ src/crypto/encrypt.spec.ts | 13 ++++ src/crypto/encryptedData.ts | 46 ++++++++++++ src/crypto/encryptor.ts | 30 ++++++++ src/crypto/index.ts | 5 ++ src/crypto/randomness.ts | 15 ++++ src/index.ts | 1 + src/walletcore/userWallet.ts | 125 +++++++++------------------------ src/walletcore/users.spec.ts | 3 +- 12 files changed, 215 insertions(+), 94 deletions(-) create mode 100644 src/crypto/constants.ts create mode 100644 src/crypto/decryptor.ts create mode 100644 src/crypto/derivationParams.ts create mode 100644 src/crypto/encrypt.spec.ts create mode 100644 src/crypto/encryptedData.ts create mode 100644 src/crypto/encryptor.ts create mode 100644 src/crypto/index.ts create mode 100644 src/crypto/randomness.ts diff --git a/.gitignore b/.gitignore index 9e73c4df..50409beb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ out out-tests out-browser out-browser-tests + +# generated by bob +lib/ diff --git a/src/crypto/constants.ts b/src/crypto/constants.ts new file mode 100644 index 00000000..a9fe61a5 --- /dev/null +++ b/src/crypto/constants.ts @@ -0,0 +1,5 @@ +// In a future PR, improve versioning infrastructure for key-file objects in erdjs. +export const Version = 4; +export const CipherAlgorithm = "aes-128-ctr"; +export const DigestAlgorithm = "sha256"; +export const KeyDerivationFunction = "scrypt"; diff --git a/src/crypto/decryptor.ts b/src/crypto/decryptor.ts new file mode 100644 index 00000000..35baace9 --- /dev/null +++ b/src/crypto/decryptor.ts @@ -0,0 +1,27 @@ +import crypto from "crypto"; +import {EncryptedData} from "./encryptedData"; +import * as errors from "../errors"; +import { DigestAlgorithm } from "./constants"; + +export class Decryptor { + public static decrypt(data: EncryptedData, password: string): Buffer { + const kdfparams = data.kdfparams; + const salt = Buffer.from(data.salt, "hex"); + const iv = Buffer.from(data.iv, "hex"); + const ciphertext = Buffer.from(data.ciphertext, "hex"); + const derivedKey = kdfparams.generateDerivedKey(Buffer.from(password), salt); + const derivedKeyFirstHalf = derivedKey.slice(0, 16); + const derivedKeySecondHalf = derivedKey.slice(16, 32); + + const computedMAC = crypto.createHmac(DigestAlgorithm, derivedKeySecondHalf).update(ciphertext).digest(); + const actualMAC = data.mac; + + if (computedMAC.toString("hex") !== actualMAC) { + throw new errors.ErrWallet("MAC mismatch, possibly wrong password"); + } + + const decipher = crypto.createDecipheriv(data.cipher, derivedKeyFirstHalf, iv); + + return Buffer.concat([decipher.update(ciphertext), decipher.final()]); + } +} diff --git a/src/crypto/derivationParams.ts b/src/crypto/derivationParams.ts new file mode 100644 index 00000000..134a9534 --- /dev/null +++ b/src/crypto/derivationParams.ts @@ -0,0 +1,36 @@ +import scryptsy from "scryptsy"; + +export class ScryptKeyDerivationParams { + /** + * numIterations + */ + n = 4096; + + /** + * memFactor + */ + r = 8; + + /** + * pFactor + */ + p = 1; + + dklen = 32; + + constructor(n = 4096, r = 8, p = 1, dklen = 32) { + this.n = n; + this. r = r; + this.p = p; + this.dklen = dklen; + } + + /** + * Will take about: + * - 80-90 ms in Node.js, on a i3-8100 CPU @ 3.60GHz + * - 350-360 ms in browser (Firefox), on a i3-8100 CPU @ 3.60GHz + */ + public generateDerivedKey(password: Buffer, salt: Buffer): Buffer { + return scryptsy(password, salt, this.n, this.r, this.p, this.dklen); + } +} diff --git a/src/crypto/encrypt.spec.ts b/src/crypto/encrypt.spec.ts new file mode 100644 index 00000000..bd15c3f9 --- /dev/null +++ b/src/crypto/encrypt.spec.ts @@ -0,0 +1,13 @@ +import { assert } from "chai"; +import { Encryptor } from "./encryptor"; +import { Decryptor } from "./decryptor"; + +describe("test address", () => { + it("encrypts/decrypts", () => { + const sensitiveData = Buffer.from("my mnemonic"); + const encryptedData = Encryptor.encrypt(sensitiveData, "password123"); + const decryptedBuffer = Decryptor.decrypt(encryptedData, "password123"); + + assert.equal(sensitiveData.toString('hex'), decryptedBuffer.toString('hex')); + }); +}); \ No newline at end of file diff --git a/src/crypto/encryptedData.ts b/src/crypto/encryptedData.ts new file mode 100644 index 00000000..7477ca65 --- /dev/null +++ b/src/crypto/encryptedData.ts @@ -0,0 +1,46 @@ +import { ScryptKeyDerivationParams } from "./derivationParams"; + +export class EncryptedData { + id: string; + version: number; + cipher: string; + ciphertext: string; + iv: string; + kdf: string; + kdfparams: ScryptKeyDerivationParams; + salt: string; + mac: string; + + constructor(data: Omit) { + this.id = data.id; + this.version = data.version; + this.ciphertext = data.ciphertext; + this.iv = data.iv; + this.cipher = data.cipher; + this.kdf = data.kdf; + this.kdfparams = data.kdfparams; + this.mac = data.mac; + this.salt = data.salt; + } + + toJSON(): any { + return { + version: this.version, + id: this.id, + crypto: { + ciphertext: this.ciphertext, + cipherparams: { iv: this.iv }, + cipher: this.cipher, + kdf: this.kdf, + kdfparams: { + dklen: this.kdfparams.dklen, + salt: this.salt, + n: this.kdfparams.n, + r: this.kdfparams.r, + p: this.kdfparams.p + }, + mac: this.mac, + } + }; + } +} \ No newline at end of file diff --git a/src/crypto/encryptor.ts b/src/crypto/encryptor.ts new file mode 100644 index 00000000..e329dbb9 --- /dev/null +++ b/src/crypto/encryptor.ts @@ -0,0 +1,30 @@ +import crypto from "crypto"; +import { Randomness } from "./randomness"; +import { ScryptKeyDerivationParams } from "./derivationParams"; +import { CipherAlgorithm, DigestAlgorithm, Version, KeyDerivationFunction } from "./constants"; +import {EncryptedData} from "./encryptedData"; + +export class Encryptor { + public static encrypt(data: Buffer, password: string, randomness: Randomness = new Randomness()): EncryptedData { + const kdParams = new ScryptKeyDerivationParams(); + const derivedKey = kdParams.generateDerivedKey(Buffer.from(password), randomness.salt); + const derivedKeyFirstHalf = derivedKey.slice(0, 16); + const derivedKeySecondHalf = derivedKey.slice(16, 32); + const cipher = crypto.createCipheriv(CipherAlgorithm, derivedKeyFirstHalf, randomness.iv); + + const ciphertext = Buffer.concat([cipher.update(data), cipher.final()]); + const mac = crypto.createHmac(DigestAlgorithm, derivedKeySecondHalf).update(ciphertext).digest(); + + return new EncryptedData({ + version: Version, + id: randomness.id, + ciphertext: ciphertext.toString('hex'), + iv: randomness.iv.toString('hex'), + cipher: CipherAlgorithm, + kdf: KeyDerivationFunction, + kdfparams: kdParams, + mac: mac.toString('hex'), + salt: randomness.salt.toString('hex') + }); + } +} diff --git a/src/crypto/index.ts b/src/crypto/index.ts new file mode 100644 index 00000000..d674cb91 --- /dev/null +++ b/src/crypto/index.ts @@ -0,0 +1,5 @@ +export * from "./constants"; +export * from "./encryptor"; +export * from "./decryptor"; +export * from "./encryptedData"; +export * from "./randomness"; diff --git a/src/crypto/randomness.ts b/src/crypto/randomness.ts new file mode 100644 index 00000000..f045dc67 --- /dev/null +++ b/src/crypto/randomness.ts @@ -0,0 +1,15 @@ +import nacl from "tweetnacl"; +import {v4 as uuidv4} from "uuid"; +const crypto = require("crypto"); + +export class Randomness { + salt: Buffer; + iv: Buffer; + id: string; + + constructor(init?: Partial) { + this.salt = init?.salt || Buffer.from(nacl.randomBytes(32)); + this.iv = init?.iv || Buffer.from(nacl.randomBytes(16)); + this.id = init?.id || uuidv4({ random: crypto.randomBytes(16) }); + } +} diff --git a/src/index.ts b/src/index.ts index 8114d3ff..a82257ff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,6 +15,7 @@ export * from "./apiProvider"; export * from "./scArgumentsParser"; export * from "./esdtHelpers"; +export * from "./crypto"; export * from "./walletcore"; export * from "./nullSigner"; diff --git a/src/walletcore/userWallet.ts b/src/walletcore/userWallet.ts index d42c468a..22d58182 100644 --- a/src/walletcore/userWallet.ts +++ b/src/walletcore/userWallet.ts @@ -1,22 +1,10 @@ -import * as errors from "../errors"; -import nacl from "tweetnacl"; import { UserPublicKey, UserSecretKey } from "./userKeys"; -const crypto = require("crypto"); -import { v4 as uuidv4 } from "uuid"; -import scryptsy from "scryptsy"; - -// In a future PR, improve versioning infrastructure for key-file objects in erdjs. -const Version = 4; -const CipherAlgorithm = "aes-128-ctr"; -const DigestAlgorithm = "sha256"; -const KeyDerivationFunction = "scrypt"; +import { EncryptedData, Encryptor, Decryptor, CipherAlgorithm, Version, KeyDerivationFunction, Randomness } from "../crypto"; +import {ScryptKeyDerivationParams} from "../crypto/derivationParams"; export class UserWallet { private readonly publicKey: UserPublicKey; - private readonly randomness: Randomness; - private readonly ciphertext: Buffer; - private readonly mac: Buffer; - private readonly kdfparams: ScryptKeyDerivationParams; + private readonly encryptedData: EncryptedData; /** * Copied from: https://github.com/ElrondNetwork/elrond-core-js/blob/v1.28.0/src/account.js#L76 @@ -30,21 +18,9 @@ export class UserWallet { * passed through a password-based key derivation function (kdf). */ constructor(secretKey: UserSecretKey, password: string, randomness: Randomness = new Randomness()) { - const kdParams = new ScryptKeyDerivationParams(); - const derivedKey = UserWallet.generateDerivedKey(Buffer.from(password), randomness.salt, kdParams); - const derivedKeyFirstHalf = derivedKey.slice(0, 16); - const derivedKeySecondHalf = derivedKey.slice(16, 32); - const cipher = crypto.createCipheriv(CipherAlgorithm, derivedKeyFirstHalf, randomness.iv); - const text = Buffer.concat([secretKey.valueOf(), secretKey.generatePublicKey().valueOf()]); - const ciphertext = Buffer.concat([cipher.update(text), cipher.final()]); - const mac = crypto.createHmac(DigestAlgorithm, derivedKeySecondHalf).update(ciphertext).digest(); - + this.encryptedData = Encryptor.encrypt(text, password, randomness); this.publicKey = secretKey.generatePublicKey(); - this.randomness = randomness; - this.ciphertext = ciphertext; - this.mac = mac; - this.kdfparams = kdParams; } /** @@ -58,24 +34,9 @@ export class UserWallet { * From an encrypted keyfile, given the password, loads the secret key and the public key. */ static decryptSecretKey(keyFileObject: any, password: string): UserSecretKey { - const kdfparams = keyFileObject.crypto.kdfparams; - const salt = Buffer.from(kdfparams.salt, "hex"); - const iv = Buffer.from(keyFileObject.crypto.cipherparams.iv, "hex"); - const ciphertext = Buffer.from(keyFileObject.crypto.ciphertext, "hex"); - const derivedKey = UserWallet.generateDerivedKey(Buffer.from(password), salt, kdfparams); - const derivedKeyFirstHalf = derivedKey.slice(0, 16); - const derivedKeySecondHalf = derivedKey.slice(16, 32); - - const computedMAC = crypto.createHmac(DigestAlgorithm, derivedKeySecondHalf).update(ciphertext).digest(); - const actualMAC = keyFileObject.crypto.mac; - - if (computedMAC.toString("hex") !== actualMAC) { - throw new errors.ErrWallet("MAC mismatch, possibly wrong password"); - } - - const decipher = crypto.createDecipheriv(keyFileObject.crypto.cipher, derivedKeyFirstHalf, iv); + const encryptedData = UserWallet.ecFromJSON(keyFileObject) - let text = Buffer.concat([decipher.update(ciphertext), decipher.final()]); + let text = Decryptor.decrypt(encryptedData, password); while (text.length < 32) { let zeroPadding = Buffer.from([0x00]); text = Buffer.concat([zeroPadding, text]); @@ -85,14 +46,23 @@ export class UserWallet { return new UserSecretKey(seed); } - /** - * Will take about: - * - 80-90 ms in Node.js, on a i3-8100 CPU @ 3.60GHz - * - 350-360 ms in browser (Firefox), on a i3-8100 CPU @ 3.60GHz - */ - private static generateDerivedKey(password: Buffer, salt: Buffer, kdfparams: ScryptKeyDerivationParams): Buffer { - const derivedKey = scryptsy(password, salt, kdfparams.n, kdfparams.r, kdfparams.p, kdfparams.dklen); - return derivedKey; + static ecFromJSON(keyfileObject: any): EncryptedData { + return new EncryptedData({ + version: Version, + id: keyfileObject.id, + cipher: keyfileObject.crypto.cipher, + ciphertext: keyfileObject.crypto.ciphertext, + iv: keyfileObject.crypto.cipherparams.iv, + kdf: keyfileObject.crypto.kdf, + kdfparams: new ScryptKeyDerivationParams( + keyfileObject.crypto.kdfparams.n, + keyfileObject.crypto.kdfparams.r, + keyfileObject.crypto.kdfparams.p, + keyfileObject.crypto.kdfparams.dklen + ), + salt: keyfileObject.crypto.kdfparams.salt, + mac: keyfileObject.crypto.mac, + }); } /** @@ -101,54 +71,23 @@ export class UserWallet { toJSON(): any { return { version: Version, - id: this.randomness.id, + id: this.encryptedData.id, address: this.publicKey.hex(), bech32: this.publicKey.toAddress().toString(), crypto: { - ciphertext: this.ciphertext.toString("hex"), - cipherparams: { iv: this.randomness.iv.toString("hex") }, + ciphertext: this.encryptedData.ciphertext, + cipherparams: { iv: this.encryptedData.iv }, cipher: CipherAlgorithm, kdf: KeyDerivationFunction, kdfparams: { - dklen: this.kdfparams.dklen, - salt: this.randomness.salt.toString("hex"), - n: this.kdfparams.n, - r: this.kdfparams.r, - p: this.kdfparams.p + dklen: this.encryptedData.kdfparams.dklen, + salt: this.encryptedData.salt, + n: this.encryptedData.kdfparams.n, + r: this.encryptedData.kdfparams.r, + p: this.encryptedData.kdfparams.p }, - mac: this.mac.toString("hex"), + mac: this.encryptedData.mac, } }; } } - -class ScryptKeyDerivationParams { - /** - * numIterations - */ - n = 4096; - - /** - * memFactor - */ - r = 8; - - /** - * pFactor - */ - p = 1; - - dklen = 32; -} - -export class Randomness { - salt: Buffer; - iv: Buffer; - id: string; - - constructor(init?: Partial) { - this.salt = init?.salt || Buffer.from(nacl.randomBytes(32)); - this.iv = init?.iv || Buffer.from(nacl.randomBytes(16)); - this.id = init?.id || uuidv4({ random: crypto.randomBytes(16) }); - } -} diff --git a/src/walletcore/users.spec.ts b/src/walletcore/users.spec.ts index 78cfa160..4f57c11f 100644 --- a/src/walletcore/users.spec.ts +++ b/src/walletcore/users.spec.ts @@ -3,7 +3,8 @@ import { assert } from "chai"; import { TestWallets } from "../testutils"; import { UserSecretKey } from "./userKeys"; import { Mnemonic } from "./mnemonic"; -import { UserWallet, Randomness } from "./userWallet"; +import { UserWallet } from "./userWallet"; +import { Randomness } from "../crypto"; import { Address } from "../address"; import { UserSigner } from "./userSigner"; import { Transaction } from "../transaction"; From b5ca90a52e07fb6e50481b0d31216d3bfddf28e0 Mon Sep 17 00:00:00 2001 From: Cristi Corcoveanu Date: Tue, 22 Jun 2021 13:49:27 +0300 Subject: [PATCH 6/9] add json decoed function for EncryptedData --- src/crypto/encrypt.spec.ts | 9 +++++++++ src/crypto/encryptedData.ts | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/crypto/encrypt.spec.ts b/src/crypto/encrypt.spec.ts index bd15c3f9..9b7eaf0a 100644 --- a/src/crypto/encrypt.spec.ts +++ b/src/crypto/encrypt.spec.ts @@ -1,6 +1,7 @@ import { assert } from "chai"; import { Encryptor } from "./encryptor"; import { Decryptor } from "./decryptor"; +import { EncryptedData } from "./encryptedData"; describe("test address", () => { it("encrypts/decrypts", () => { @@ -10,4 +11,12 @@ describe("test address", () => { assert.equal(sensitiveData.toString('hex'), decryptedBuffer.toString('hex')); }); + + it("encodes/decodes kdfparams", () => { + const sensitiveData = Buffer.from("my mnemonic"); + const encryptedData = Encryptor.encrypt(sensitiveData, "password123"); + const decodedData = EncryptedData.fromJSON(encryptedData.toJSON()); + + assert.deepEqual(decodedData, encryptedData, "invalid decoded data"); + }); }); \ No newline at end of file diff --git a/src/crypto/encryptedData.ts b/src/crypto/encryptedData.ts index 7477ca65..14ff97ae 100644 --- a/src/crypto/encryptedData.ts +++ b/src/crypto/encryptedData.ts @@ -43,4 +43,23 @@ export class EncryptedData { } }; } + + static fromJSON(data: any): EncryptedData { + return new EncryptedData({ + version: data.version, + id: data.id, + ciphertext: data.crypto.ciphertext, + iv: data.crypto.cipherparams.iv, + cipher: data.crypto.cipher, + kdf: data.crypto.kdf, + kdfparams: new ScryptKeyDerivationParams( + data.crypto.kdfparams.n, + data.crypto.kdfparams.r, + data.crypto.kdfparams.p, + data.crypto.kdfparams.dklen, + ), + salt: data.crypto.kdfparams.salt, + mac: data.crypto.mac, + }); + } } \ No newline at end of file From 00c99bb8e44252ab2c126c17fd5b245c8d69dd16 Mon Sep 17 00:00:00 2001 From: Cristi Corcoveanu Date: Tue, 29 Jun 2021 14:06:15 +0300 Subject: [PATCH 7/9] added empty line --- src/crypto/encrypt.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/encrypt.spec.ts b/src/crypto/encrypt.spec.ts index 9b7eaf0a..b076b311 100644 --- a/src/crypto/encrypt.spec.ts +++ b/src/crypto/encrypt.spec.ts @@ -19,4 +19,4 @@ describe("test address", () => { assert.deepEqual(decodedData, encryptedData, "invalid decoded data"); }); -}); \ No newline at end of file +}); From 2fb57a89f9d79a7c6a1908f623e0931a4211455f Mon Sep 17 00:00:00 2001 From: Cristi Corcoveanu Date: Tue, 29 Jun 2021 14:07:26 +0300 Subject: [PATCH 8/9] added empty line --- src/crypto/encryptedData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/encryptedData.ts b/src/crypto/encryptedData.ts index 14ff97ae..b882a570 100644 --- a/src/crypto/encryptedData.ts +++ b/src/crypto/encryptedData.ts @@ -62,4 +62,4 @@ export class EncryptedData { mac: data.crypto.mac, }); } -} \ No newline at end of file +} From 711ba5ec1c7b1e07772e9d531e1aedc9b385e900 Mon Sep 17 00:00:00 2001 From: Cristi Corcoveanu Date: Thu, 1 Jul 2021 10:37:18 +0300 Subject: [PATCH 9/9] typo fix --- src/walletcore/userWallet.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/walletcore/userWallet.ts b/src/walletcore/userWallet.ts index 22d58182..c0f7064d 100644 --- a/src/walletcore/userWallet.ts +++ b/src/walletcore/userWallet.ts @@ -34,7 +34,7 @@ export class UserWallet { * From an encrypted keyfile, given the password, loads the secret key and the public key. */ static decryptSecretKey(keyFileObject: any, password: string): UserSecretKey { - const encryptedData = UserWallet.ecFromJSON(keyFileObject) + const encryptedData = UserWallet.edFromJSON(keyFileObject) let text = Decryptor.decrypt(encryptedData, password); while (text.length < 32) { @@ -46,7 +46,7 @@ export class UserWallet { return new UserSecretKey(seed); } - static ecFromJSON(keyfileObject: any): EncryptedData { + static edFromJSON(keyfileObject: any): EncryptedData { return new EncryptedData({ version: Version, id: keyfileObject.id,