Skip to content

Commit c7b2c8b

Browse files
committed
add support for dynamic decryption algorithms + version bump
1 parent 9720881 commit c7b2c8b

4 files changed

Lines changed: 57 additions & 18 deletions

File tree

lib/block_io.js

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ function BlockIo (config, pin, version, options) {
2525

2626
if (pin) {
2727
this.pin = pin;
28-
this.aesKey = helper.pinToKey(this.pin);
2928
}
3029

3130
if (options && typeof(options) == 'object') this._cloneOptions(options);
@@ -39,7 +38,6 @@ function BlockIo (config, pin, version, options) {
3938

4039
if (config.pin) {
4140
this.pin = config.pin;
42-
this.aesKey = helper.pinToKey(this.pin);
4341
}
4442

4543
if (config.options) this._cloneOptions(config.options);
@@ -174,24 +172,12 @@ BlockIo.prototype.create_and_sign_transaction = function (args, cb) {
174172

175173
if (Object.prototype.hasOwnProperty.call(data.data, 'user_key')) {
176174
if (this.keys[data.data.user_key.public_key] === undefined) {
177-
if (this.aesKey === undefined && args.pin === undefined) {
175+
if (this.pin === undefined && args.pin === undefined) {
178176
throw("Must either instantiate object with a PIN, or provide the PIN for create_and_sign_transaction to decrypt the private key");
179-
} else if (this.aesKey !== undefined || args.pin !== undefined) {
177+
} else if (this.pin !== undefined || args.pin !== undefined) {
180178
// the user provided the PIN here, so let's use it
181179

182-
let tempAesKey = undefined;
183-
184-
if (this.aesKey === undefined && args.pin !== undefined) {
185-
tempAesKey = helper.pinToKey(args.pin);
186-
} else {
187-
tempAesKey = this.aesKey;
188-
}
189-
190-
// we have the AES Key now
191-
// If we get here, Block.io's asking us to provide client-side signatures
192-
// since the user_key is provided, we want to decrypt the private key from it
193-
const encrypted_passphrase = data.data.user_key.encrypted_passphrase;
194-
let privkey = helper.extractKey(encrypted_passphrase, tempAesKey);
180+
let privkey = helper.dynamicExtractKey(data.data.user_key, this.pin || args.pin);
195181

196182
if (!(privkey instanceof ECKey))
197183
return r(new Error(ERR_PK_EXTR));

lib/helper.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,26 @@ Helper.pinToKey = function (pin, salt, iterations, hash_function, phase1_key_len
129129
return buf.toString('base64');
130130
};
131131

132+
Helper.dynamicExtractKey = function (user_key, pin) {
133+
// uses the appropriate algorithm to decrypt user's private key
134+
135+
// legacy
136+
let algorithm = JSON.parse("{\"pbkdf2_salt\":\"\",\"pbkdf2_iterations\":2048,\"pbkdf2_hash_function\":\"SHA256\",\"pbkdf2_phase1_key_length\":16,\"pbkdf2_phase2_key_length\":32,\"aes_iv\":null,\"ae\
137+
s_cipher\":\"AES-256-ECB\",\"aes_auth_tag\":null,\"aes_auth_data\":null}");
138+
139+
// we got the algorithm, so use that instead
140+
if (user_key.algorithm) algorithm = user_key.algorithm;
141+
142+
const aes_key = this.pinToKey(pin, algorithm.pbkdf2_salt, algorithm.pbkdf2_iterations, algorithm.pbkdf2_hash_function, algorithm.pbkdf2_phase1_key_length,
143+
algorithm.pbkdf2_phase2_key_length);
144+
const decrypted = this.decrypt(user_key.encrypted_passphrase, aes_key,
145+
algorithm.aes_iv, algorithm.aes_cipher,
146+
algorithm.aes_auth_tag,
147+
algorithm.aes_auth_data);
148+
149+
return ECKey.fromPassphrase(decrypted);
150+
};
151+
132152
Helper.extractKey = function (encrypted_data, b64_enc_key) {
133153
const decrypted = this.decrypt(encrypted_data, b64_enc_key);
134154
return ECKey.fromPassphrase(decrypted);

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"dogecoin",
1010
"wallet"
1111
],
12-
"version": "4.0.0",
12+
"version": "4.0.1",
1313
"preferGlobal": false,
1414
"homepage": "https://github.com/BlockIo/block_io-nodejs",
1515
"author": "Patrick Lodder <patrick.lodder@gmail.com> (https://github.com/patricklodder)",

test/unit/cryptohelper.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,39 @@ test('Encrypt using AES-256-GCM', t => {
9595

9696
});
9797

98+
test("DynamicExtractKey using AES-256-ECB", t => {
99+
t.plan(2)
100+
101+
let user_key = JSON.parse('{"encrypted_passphrase":"3wIJtPoC8KO6S7x6LtrN0g==","public_key":"02f87f787bffb30396984cb6b3a9d6830f32d5b656b3e39b0abe4f3b3c35d99323","algorithm":{"pbkdf2_salt":"","pbkdf2_iterations":2048,"pbkdf2_hash_function":"SHA256","pbkdf2_phase1_key_length":16,"pbkdf2_phase2_key_length":32,"aes_iv":null,"aes_cipher":"AES-256-ECB","aes_auth_tag":null,"aes_auth_data":null}}');
102+
103+
t.doesNotThrow(() => {
104+
let key = CryptoHelper.dynamicExtractKey(user_key, "deadbeef");
105+
t.equal(key.pub.toString('hex'), user_key.public_key, 'must return correct public key');
106+
}, undefined, 'does not throw any Errors');
107+
});
108+
109+
test("DynamicExtractKey using AES-256-CBC", t => {
110+
t.plan(2)
111+
112+
let user_key = JSON.parse('{"encrypted_passphrase":"LExu1rUAtIBOekslc328Lw==","public_key":"02f87f787bffb30396984cb6b3a9d6830f32d5b656b3e39b0abe4f3b3c35d99323","algorithm":{"pbkdf2_salt":"922445847c173e90667a19d90729e1fb","pbkdf2_iterations":500000,"pbkdf2_hash_function":"SHA256","pbkdf2_phase1_key_length":16,"pbkdf2_phase2_key_length":32,"aes_iv":"11bc22166c8cf8560e5fa7e5c622bb0f","aes_cipher":"AES-256-CBC","aes_auth_tag":null,"aes_auth_data":null}}');
113+
114+
t.doesNotThrow(() => {
115+
let key = CryptoHelper.dynamicExtractKey(user_key, "deadbeef");
116+
t.equal(key.pub.toString('hex'), user_key.public_key, 'must return correct public key');
117+
}, undefined, 'does not throw any Errors');
118+
});
119+
120+
test("DynamicExtractKey using AES-256-GCM", t => {
121+
t.plan(2)
122+
123+
let user_key = JSON.parse('{"encrypted_passphrase":"ELV56Z57KoA=","public_key":"02f87f787bffb30396984cb6b3a9d6830f32d5b656b3e39b0abe4f3b3c35d99323","algorithm":{"pbkdf2_salt":"922445847c173e90667a19d90729e1fb","pbkdf2_iterations":500000,"pbkdf2_hash_function":"SHA256","pbkdf2_phase1_key_length":16,"pbkdf2_phase2_key_length":32,"aes_iv":"a57414b88b67f977829cbdca","aes_cipher":"AES-256-GCM","aes_auth_tag":"adeb7dfe53027bdda5824dc524d5e55a","aes_auth_data":""}}');
124+
125+
t.doesNotThrow(() => {
126+
let key = CryptoHelper.dynamicExtractKey(user_key, "deadbeef");
127+
t.equal(key.pub.toString('hex'), user_key.public_key, 'must return correct public key');
128+
}, undefined, 'does not throw any Errors');
129+
});
130+
98131
test('Satoshis from value string', t => {
99132
t.plan(4);
100133

0 commit comments

Comments
 (0)