Skip to content

Commit

Permalink
add support for dynamic decryption algorithms + version bump
Browse files Browse the repository at this point in the history
  • Loading branch information
doersf committed Jun 7, 2021
1 parent 9720881 commit c7b2c8b
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 18 deletions.
20 changes: 3 additions & 17 deletions lib/block_io.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ function BlockIo (config, pin, version, options) {

if (pin) {
this.pin = pin;
this.aesKey = helper.pinToKey(this.pin);
}

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

if (config.pin) {
this.pin = config.pin;
this.aesKey = helper.pinToKey(this.pin);
}

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

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

let tempAesKey = undefined;

if (this.aesKey === undefined && args.pin !== undefined) {
tempAesKey = helper.pinToKey(args.pin);
} else {
tempAesKey = this.aesKey;
}

// we have the AES Key now
// If we get here, Block.io's asking us to provide client-side signatures
// since the user_key is provided, we want to decrypt the private key from it
const encrypted_passphrase = data.data.user_key.encrypted_passphrase;
let privkey = helper.extractKey(encrypted_passphrase, tempAesKey);
let privkey = helper.dynamicExtractKey(data.data.user_key, this.pin || args.pin);

if (!(privkey instanceof ECKey))
return r(new Error(ERR_PK_EXTR));
Expand Down
20 changes: 20 additions & 0 deletions lib/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,26 @@ Helper.pinToKey = function (pin, salt, iterations, hash_function, phase1_key_len
return buf.toString('base64');
};

Helper.dynamicExtractKey = function (user_key, pin) {
// uses the appropriate algorithm to decrypt user's private key

// legacy
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\
s_cipher\":\"AES-256-ECB\",\"aes_auth_tag\":null,\"aes_auth_data\":null}");

// we got the algorithm, so use that instead
if (user_key.algorithm) algorithm = user_key.algorithm;

const aes_key = this.pinToKey(pin, algorithm.pbkdf2_salt, algorithm.pbkdf2_iterations, algorithm.pbkdf2_hash_function, algorithm.pbkdf2_phase1_key_length,
algorithm.pbkdf2_phase2_key_length);
const decrypted = this.decrypt(user_key.encrypted_passphrase, aes_key,
algorithm.aes_iv, algorithm.aes_cipher,
algorithm.aes_auth_tag,
algorithm.aes_auth_data);

return ECKey.fromPassphrase(decrypted);
};

Helper.extractKey = function (encrypted_data, b64_enc_key) {
const decrypted = this.decrypt(encrypted_data, b64_enc_key);
return ECKey.fromPassphrase(decrypted);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"dogecoin",
"wallet"
],
"version": "4.0.0",
"version": "4.0.1",
"preferGlobal": false,
"homepage": "https://github.com/BlockIo/block_io-nodejs",
"author": "Patrick Lodder <[email protected]> (https://github.com/patricklodder)",
Expand Down
33 changes: 33 additions & 0 deletions test/unit/cryptohelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,39 @@ test('Encrypt using AES-256-GCM', t => {

});

test("DynamicExtractKey using AES-256-ECB", t => {
t.plan(2)

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}}');

t.doesNotThrow(() => {
let key = CryptoHelper.dynamicExtractKey(user_key, "deadbeef");
t.equal(key.pub.toString('hex'), user_key.public_key, 'must return correct public key');
}, undefined, 'does not throw any Errors');
});

test("DynamicExtractKey using AES-256-CBC", t => {
t.plan(2)

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}}');

t.doesNotThrow(() => {
let key = CryptoHelper.dynamicExtractKey(user_key, "deadbeef");
t.equal(key.pub.toString('hex'), user_key.public_key, 'must return correct public key');
}, undefined, 'does not throw any Errors');
});

test("DynamicExtractKey using AES-256-GCM", t => {
t.plan(2)

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":""}}');

t.doesNotThrow(() => {
let key = CryptoHelper.dynamicExtractKey(user_key, "deadbeef");
t.equal(key.pub.toString('hex'), user_key.public_key, 'must return correct public key');
}, undefined, 'does not throw any Errors');
});

test('Satoshis from value string', t => {
t.plan(4);

Expand Down

0 comments on commit c7b2c8b

Please sign in to comment.