Skip to content

Commit 45363cc

Browse files
authored
Merge pull request #15 from SpringRole/develop
v0.2.0
2 parents c747e86 + e66fda5 commit 45363cc

File tree

7 files changed

+2249
-2479
lines changed

7 files changed

+2249
-2479
lines changed

.babelrc

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,3 @@
11
{
2-
"presets": ["@babel/env"],
3-
"plugins": [
4-
"@babel/plugin-proposal-export-default-from",
5-
"@babel/plugin-proposal-export-namespace-from",
6-
[
7-
"@babel/plugin-transform-runtime",
8-
{
9-
"helpers": true,
10-
"regenerator": true
11-
}
12-
]
13-
]
2+
"presets": ["@babel/env", "minify"]
143
}

README.md

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ SpringWallet - A simple wallet for flexible identity management for your fronten
1212

1313
1. Install `springwallet` with `npm`.
1414

15-
```npm install springwallet --save``` or ```yarn add springwallet```
15+
```npm install @springrole/springwallet --save``` or ```yarn add @springrole/springwallet```
1616

1717
2. Import springwallet into your project.
1818

1919
```js
20-
import SpringWallet from '@springrole/springwallet';
20+
import { SpringWallet } from '@springrole/springwallet';
2121
```
2222
3. Generate 12 words random mnemonic
2323

@@ -28,7 +28,7 @@ SpringWallet - A simple wallet for flexible identity management for your fronten
2828

2929
```js
3030
async function createWallet(plainTextMnemonic, password) {
31-
const encryptedMnemonic = await encryptMnemonic(plainTextMnemonic, password); // encrypting mnemonic
31+
const encryptedMnemonic = await SpringWallet.encryptMnemonic(plainTextMnemonic, password); // encrypting mnemonic
3232
const wallet = await SpringWallet.initializeWalletFromMnemonic(plainTextMnemonic); // initializing wallet
3333
const address = wallet.getChecksumAddressString(); // wallet address
3434
const key = wallet.getPrivateKey().toString('hex'); // private key
@@ -51,7 +51,7 @@ SpringWallet - A simple wallet for flexible identity management for your fronten
5151
async function unlockWallet(encryptedMnemonic, password) {
5252
let plainTextMnemonic;
5353
try {
54-
plainTextMnemonic = await decryptMnemonic(encryptedMnemonic, password);
54+
plainTextMnemonic = await SpringWallet.decryptMnemonic(encryptedMnemonic, password);
5555
} catch {
5656
return false;
5757
}
@@ -78,10 +78,9 @@ SpringWallet - A simple wallet for flexible identity management for your fronten
7878
7979
```js
8080
async function changeWalletPassword(address, encryptedMnemonic, oldPassword, newPassword) {
81-
const mnemonicPhrase = await decryptMnemonic(encryptedMnemonic, oldPassword);
82-
const newEncryptedMnemonic = await encryptMnemonic(mnemonicPhrase, newPassword);
83-
const status = await updateEncryptedMnemonic(address, newEncryptedMnemonic);
84-
return status;
81+
const mnemonicPhrase = await SpringWallet.decryptMnemonic(encryptedMnemonic, oldPassword);
82+
const newEncryptedMnemonic = await SpringWallet.encryptMnemonic(mnemonicPhrase, newPassword);
83+
return true;
8584
}
8685
```
8786
**NOTE** This will decrypt mnemonic with old password and reencrypts it using new password which will create new encrypted mnemonic
@@ -90,11 +89,10 @@ SpringWallet - A simple wallet for flexible identity management for your fronten
9089
9190
```js
9291
async function resetWalletPassword(plainTextMnemonic, newPassword) {
93-
const newEncryptedMnemonic = await encryptMnemonic(plainTextMnemonic, newPassword);
92+
const newEncryptedMnemonic = await SpringWallet.encryptMnemonic(plainTextMnemonic, newPassword);
9493
const wallet = await SpringWallet.initializeWalletFromMnemonic(plainTextMnemonic);
9594
const walletAddress = wallet.getChecksumAddressString();
96-
const status = await updateEncryptedMnemonic(walletAddress, newEncryptedMnemonic);
97-
return status;
95+
return true;
9896
}
9997
```
10098

package.json

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"name": "@springrole/springwallet",
3-
"version": "0.1.7-beta.7",
3+
"version": "0.2.0",
44
"description": "Wallet for SpringRole users",
55
"main": "dist/index.js",
66
"scripts": {
7-
"build": "./node_modules/.bin/babel src --out-dir dist",
8-
"lint": "./node_modules/.bin/eslint .",
9-
"lint:fix": "./node_modules/.bin/eslint --fix .",
10-
"format": "./node_modules/.bin/prettier --ignore-path .gitignore --write ./{*.json,**/*.json}"
7+
"build": "babel src --out-dir dist",
8+
"lint": "eslint .",
9+
"lint:fix": "eslint --fix .",
10+
"format": "prettier --ignore-path .gitignore --config .prettierrc --write \"**/*.{js,json}\""
1111
},
1212
"repository": {
1313
"type": "git",
@@ -37,24 +37,21 @@
3737
},
3838
"homepage": "https://github.com/SpringRole/SpringWallet#readme",
3939
"dependencies": {
40-
"bip39": "^3.0.2",
41-
"ethereumjs-wallet": "^0.6.3",
42-
"web3-provider-engine": "^15.0.3"
40+
"bip39": "^3.0.3",
41+
"ethereumjs-wallet": "^1.0.1",
42+
"web3-provider-engine": "^16.0.1"
4343
},
4444
"devDependencies": {
45-
"@babel/cli": "^7.6.0",
46-
"@babel/core": "^7.6.0",
47-
"@babel/plugin-proposal-export-default-from": "^7.5.2",
48-
"@babel/plugin-proposal-export-namespace-from": "^7.5.2",
49-
"@babel/plugin-transform-runtime": "^7.6.0",
50-
"@babel/preset-env": "^7.6.0",
51-
"@babel/runtime": "^7.6.3",
52-
"eslint": "^6.3.0",
53-
"eslint-config-prettier": "^6.2.0",
54-
"eslint-plugin-prettier": "^3.1.0",
55-
"husky": "^3.1.0",
56-
"lint-staged": "^9.5.0",
57-
"prettier": "^1.18.2"
45+
"@babel/cli": "^7.12.10",
46+
"@babel/core": "^7.12.10",
47+
"@babel/preset-env": "^7.12.11",
48+
"babel-preset-minify": "^0.5.1",
49+
"eslint": "^7.18.0",
50+
"eslint-config-prettier": "^7.1.0",
51+
"eslint-plugin-prettier": "^3.3.1",
52+
"husky": "^4.3.8",
53+
"lint-staged": "^10.5.3",
54+
"prettier": "^2.2.1"
5855
},
5956
"husky": {
6057
"hooks": {
@@ -63,12 +60,10 @@
6360
},
6461
"lint-staged": {
6562
"*.js": [
66-
"./node_modules/.bin/eslint --ignore-pattern '!.eslintrc.js --fix",
67-
"git add"
63+
"eslint --ignore-pattern '!.eslintrc.js --fix"
6864
],
6965
"*.{json,css}": [
70-
"./node_modules/.bin/prettier --write",
71-
"git add"
66+
"prettier --write"
7267
]
7368
}
7469
}

src/index.js

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import * as bip39 from 'bip39';
2-
import crypto from 'crypto';
3-
import * as HDKey from 'ethereumjs-wallet/hdkey';
1+
import { generateMnemonic, validateMnemonic, mnemonicToSeed } from 'bip39';
2+
import { randomBytes } from 'crypto';
3+
import { hdkey } from 'ethereumjs-wallet';
44
import Web3ProviderEngine from 'web3-provider-engine';
55
import FixtureSubprovider from 'web3-provider-engine/subproviders/fixture.js';
6+
import NonceSubprovider from 'web3-provider-engine/subproviders/nonce-tracker.js';
67
import RpcSubprovider from 'web3-provider-engine/subproviders/rpc.js';
8+
import WebSocketSubProvider from 'web3-provider-engine/subproviders/websocket.js';
79
import HookedWalletSubprovider from 'web3-provider-engine/subproviders/hooked-wallet-ethtx.js';
810
import networkConfig from './networkConfig';
911
import { encryptMnemonic, decryptMnemonic } from './utils/encryption';
@@ -14,15 +16,13 @@ const MNEMONIC_PATH = "m/44'/60'/0'/0/0";
1416
/**
1517
* `SpringWallet` class
1618
*/
17-
export default class SpringWallet {
19+
export class SpringWallet {
1820
/**
19-
* @param {Object|String} network
21+
* @param {Object} network
2022
* @constructor
2123
*/
2224
constructor(network) {
23-
if (!network) {
24-
throw new Error("'network' not defined");
25-
}
25+
if (!network) throw new Error("'network' not defined");
2626
this.network = networkConfig(network);
2727
this.walletAddress = this.initWalletAddress();
2828
this.privateKey = this.initPrivateKey();
@@ -49,7 +49,7 @@ export default class SpringWallet {
4949
const engine = new Web3ProviderEngine();
5050
engine.addProvider(
5151
new FixtureSubprovider({
52-
web3_clientVersion: 'SpringWallet/v0.1.7/javascript',
52+
web3_clientVersion: 'SpringWallet/v0.2.0/javascript',
5353
net_listening: true,
5454
eth_hashrate: '0x00',
5555
eth_mining: false,
@@ -58,9 +58,9 @@ export default class SpringWallet {
5858
);
5959

6060
opts.getPrivateKey = (address, cb) => {
61-
if (address.toLowerCase() === this.walletAddress.toLowerCase()) {
61+
if (address.toLowerCase() == this.walletAddress.toLowerCase()) {
6262
if (this.privateKey) {
63-
cb(null, this.privateKey);
63+
cb(null, Buffer.from(this.privateKey, 'hex'));
6464
} else if (this.wallet) {
6565
const privKey = this.wallet.getPrivateKey().toString('hex');
6666
cb(null, Buffer.from(privKey, 'hex'));
@@ -84,11 +84,14 @@ export default class SpringWallet {
8484
cb(false, [address]);
8585
};
8686

87+
engine.addProvider(new NonceSubprovider());
8788
engine.addProvider(new HookedWalletSubprovider(opts));
88-
engine.addProvider(new RpcSubprovider(opts));
89-
engine.on('error', (error) => {
90-
console.error(error);
91-
});
89+
if (opts && opts.rpcUrl && opts.rpcUrl.indexOf && opts.rpcUrl.indexOf('wss://') == 0) {
90+
engine.addProvider(new WebSocketSubProvider(opts));
91+
} else {
92+
engine.addProvider(new RpcSubprovider(opts));
93+
}
94+
9295
engine.start();
9396
return engine;
9497
}
@@ -99,7 +102,7 @@ export default class SpringWallet {
99102
* @returns {String} mnemonic
100103
*/
101104
static generateMnemonic() {
102-
return bip39.generateMnemonic(128, crypto.randomBytes);
105+
return generateMnemonic(128, randomBytes);
103106
}
104107

105108
/**
@@ -109,7 +112,7 @@ export default class SpringWallet {
109112
* @returns {Boolean}
110113
*/
111114
static isValidMnemonic(phrase) {
112-
return bip39.validateMnemonic(phrase);
115+
return validateMnemonic(phrase);
113116
}
114117

115118
/**
@@ -166,11 +169,9 @@ export default class SpringWallet {
166169
* @returns wallet instance
167170
*/
168171
static async initializeWalletFromMnemonic(mnemonic) {
169-
const hdKey = await HDKey.fromMasterSeed(bip39.mnemonicToSeedSync(mnemonic));
170-
const wallet = await hdKey
171-
.derivePath(MNEMONIC_PATH)
172-
.deriveChild(0)
173-
.getWallet();
172+
const seed = mnemonicToSeed(mnemonic);
173+
const hdKey = await hdkey.fromMasterSeed(seed);
174+
const wallet = await hdKey.derivePath(MNEMONIC_PATH).deriveChild(0).getWallet();
174175
return wallet;
175176
}
176177

src/networkConfig.js

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,11 @@
1-
const networks = {
2-
mainnet: {
3-
rpcUrl: 'https://mainnet.infura.io/v3/607d0ccc6e364affa61439c855e1188a',
4-
chainId: '1'
5-
},
6-
maticAlpha: {
7-
rpcUrl: 'https://alpha.ethereum.matic.network',
8-
chainId: '4626'
9-
},
10-
maticBeta: {
11-
rpcUrl: 'https://beta.matic.network',
12-
chainId: '15001'
13-
}
14-
};
15-
161
export default function networkConfig(network) {
17-
// TODO: Add infura mainnet prod url
18-
const nObj = typeof network === 'string' ? Object.assign({}, networks[network]) : network;
19-
20-
if (typeof nObj !== 'object') {
2+
if (typeof network !== 'object') {
213
throw new Error("illegal 'network' parameter");
224
}
235

24-
if (!nObj.rpcUrl) {
6+
if (!network.rpcUrl) {
257
throw new Error("'rpcUrl' is not defined");
268
}
279

28-
return nObj;
10+
return network;
2911
}

src/utils/encryption.js

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
import * as bip39 from 'bip39';
2-
import * as crypto from 'crypto';
1+
import { promisify } from 'util';
2+
import { validateMnemonic, mnemonicToEntropy, entropyToMnemonic } from 'bip39';
3+
import { pbkdf2, randomBytes, createCipheriv, createHmac, createDecipheriv, createHash } from 'crypto';
4+
5+
const asyncPbkdf2 = promisify(pbkdf2);
36

47
/**
58
* Encrypt a mnemonic using password
@@ -8,25 +11,25 @@ import * as crypto from 'crypto';
811
* @returns {Promise<String>} hex-encoded string of the encrypted mnemonic
912
*/
1013
export async function encryptMnemonic(phrase, password) {
11-
if (!bip39.validateMnemonic(phrase)) {
14+
if (!validateMnemonic(phrase)) {
1215
throw new Error('Not a valid bip39 mnemonic');
1316
}
1417

15-
const plaintextBuffer = Buffer.from(bip39.mnemonicToEntropy(phrase), 'hex');
18+
const plaintextBuffer = Buffer.from(mnemonicToEntropy(phrase), 'hex');
1619

1720
// AES-128-CBC with SHA256 HMAC
18-
const salt = crypto.randomBytes(16);
19-
const keysAndIV = crypto.pbkdf2Sync(password, salt, 100000, 48, 'sha512');
21+
const salt = randomBytes(16);
22+
const keysAndIV = await asyncPbkdf2(password, salt, 100000, 48, 'sha512');
2023
const encKey = keysAndIV.slice(0, 16);
2124
const macKey = keysAndIV.slice(16, 32);
2225
const iv = keysAndIV.slice(32, 48);
2326

24-
const cipher = crypto.createCipheriv('aes-128-cbc', encKey, iv);
27+
const cipher = createCipheriv('aes-128-cbc', encKey, iv);
2528

2629
const cipherText = Buffer.concat([cipher.update(plaintextBuffer), cipher.final()]);
2730

2831
const hmacPayload = Buffer.concat([salt, cipherText]);
29-
const hmac = crypto.createHmac('sha256', macKey);
32+
const hmac = createHmac('sha256', macKey);
3033
hmac.write(hmacPayload);
3134
const hmacDigest = hmac.digest();
3235
const payload = Buffer.concat([salt, hmacDigest, cipherText]);
@@ -49,37 +52,29 @@ export async function decryptMnemonic(encryptedMnemonic, password) {
4952
const cipherText = dataBuffer.slice(48);
5053

5154
const hmacPayload = Buffer.concat([salt, cipherText]);
52-
const keysAndIV = crypto.pbkdf2Sync(password, salt, 100000, 48, 'sha512');
55+
const keysAndIV = await asyncPbkdf2(password, salt, 100000, 48, 'sha512');
5356
const encKey = keysAndIV.slice(0, 16);
5457
const macKey = keysAndIV.slice(16, 32);
5558
const iv = keysAndIV.slice(32, 48);
5659

57-
const decipher = crypto.createDecipheriv('aes-128-cbc', encKey, iv);
60+
const decipher = createDecipheriv('aes-128-cbc', encKey, iv);
5861
const plaintextBuffer = Buffer.concat([decipher.update(cipherText), decipher.final()]);
5962

6063
const hmac = crypto.createHmac('sha256', macKey);
6164
hmac.write(hmacPayload);
6265

6366
const hmacDigest = hmac.digest();
64-
const hmacSigHash = crypto
65-
.createHash('sha256')
66-
.update(hmacSig)
67-
.digest()
68-
.toString('hex');
69-
70-
const hmacDigestHash = crypto
71-
.createHash('sha256')
72-
.update(hmacDigest)
73-
.digest()
74-
.toString('hex');
67+
const hmacSigHash = createHash('sha256').update(hmacSig).digest().toString('hex');
68+
69+
const hmacDigestHash = createHash('sha256').update(hmacDigest).digest().toString('hex');
7570

7671
if (hmacSigHash !== hmacDigestHash) {
7772
throw new Error('Wrong password (HMAC mismatch)');
7873
}
7974

80-
const mnemonic = bip39.entropyToMnemonic(plaintextBuffer);
75+
const mnemonic = entropyToMnemonic(plaintextBuffer);
8176

82-
if (!bip39.validateMnemonic(mnemonic)) {
77+
if (!validateMnemonic(mnemonic)) {
8378
throw new Error('Wrong password (invalid plaintext)');
8479
}
8580

0 commit comments

Comments
 (0)