Skip to content

Commit 75a3bc9

Browse files
committed
implement support for dynamic decryption algorithms
1 parent 906f916 commit 75a3bc9

File tree

3 files changed

+59
-8
lines changed

3 files changed

+59
-8
lines changed

block_io/__init__.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,41 @@ class Helper:
104104
@staticmethod
105105
def pinToAesKey(pin, salt = "", iterations = 2048, hashfn = sha256, phase1_key_length = 16, phase2_key_length = 32):
106106
# use pbkdf2 magic
107-
# ( password, keylen, salt = "", itercount = 1024, hashfn = sha256 )
108107
ret = pbkdf2.pbkdf2(pin, phase1_key_length, salt, int(iterations/2), hashfn)
109108
ret = pbkdf2.pbkdf2(hexlify(ret), phase2_key_length, salt, int(iterations/2), hashfn)
110109
return hexlify(ret) # the encryption key
111110

111+
@staticmethod
112+
def dynamicExtractKey(user_key, pin):
113+
# uses the algorithm specified in the user_key object
114+
# if no algorithm specified, uses the following default (legacy)
115+
116+
algorithm = {
117+
"pbkdf2_salt": "",
118+
"pbkdf2_iterations": 2048,
119+
"pbkdf2_hash_function": "SHA256",
120+
"pbkdf2_phase1_key_length": 16,
121+
"pbkdf2_phase2_key_length": 32,
122+
"aes_iv": None,
123+
"aes_cipher": "AES-256-ECB",
124+
"aes_auth_tag": None,
125+
"aes_auth_data": None
126+
}
127+
128+
if 'algorithm' in user_key:
129+
algorithm = user_key['algorithm']
130+
131+
if algorithm['pbkdf2_hash_function'] != "SHA256":
132+
raise Exception("Unknown hash function specified. Are you using current version of this library?")
133+
134+
aes_key = BlockIo.Helper.pinToAesKey(pin, algorithm['pbkdf2_salt'], algorithm['pbkdf2_iterations'],
135+
sha256, algorithm['pbkdf2_phase1_key_length'], algorithm['pbkdf2_phase2_key_length'])
136+
137+
decrypted = BlockIo.Helper.decrypt(user_key['encrypted_passphrase'], aes_key,
138+
algorithm['aes_iv'], algorithm['aes_cipher'], algorithm['aes_auth_tag'])
139+
140+
return BlockIo.Key.from_passphrase(unhexlify(decrypted))
141+
112142
@staticmethod
113143
def extractKey(encrypted_data, enc_key_hex):
114144
# encryption key is in hex
@@ -304,15 +334,13 @@ def create_and_sign_transaction(self, prepare_data, keys = []):
304334
cur_key = PrivateKey(secret_exponent=int(cur_key_hex,16))
305335
self.private_keys[cur_key.get_public_key().to_hex(compressed=True)] = cur_key
306336

307-
if self.encryption_key is None and self.pin is None and 'user_key' in prepare_data['data']:
337+
if self.pin is None and 'user_key' in prepare_data['data'] and prepare_data['data']['user_key']['public_key'] not in self.private_keys:
308338
raise BlockIoUnknownError("No PIN provided to decrypt signer private key.")
309339

310-
if self.encryption_key is None and 'user_key' in prepare_data['data']:
311-
self.encryption_key = self.Helper.pinToAesKey(self.pin)
312-
313340
# decrypt the signer private key if we can
314-
if self.encryption_key is not None and 'user_key' in prepare_data['data']:
315-
key = self.Helper.extractKey(prepare_data['data']['user_key']['encrypted_passphrase'], self.encryption_key)
341+
if self.pin is not None and 'user_key' in prepare_data['data'] and prepare_data['data']['user_key']['public_key'] not in self.private_keys:
342+
343+
key = self.Helper.dynamicExtractKey(prepare_data['data']['user_key'], self.pin)
316344

317345
if (key.pubkey_hex().decode('utf-8') != prepare_data['data']['user_key']['public_key']):
318346
raise Exception("Expected pubkey=",prepare_data['data']['user_key']['public_key'],"but got pubkey=",key.pubkey_hex(),". Invalid PIN provided.")

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
long_description = fh.read()
55

66
setup(name='block-io',
7-
version='2.0.3',
7+
version='2.0.4',
88
description='The easiest way to integrate Bitcoin, Dogecoin and Litecoin in your applications. Sign up at Block.io for your API key.',
99
url='https://github.com/BlockIo/block_io-python',
1010
author='Atif Nazir',

tests/test_blockio.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import os
44
import unittest
55
import time
6+
import json
67

78
from struct import pack
89
from types import *
@@ -82,6 +83,28 @@ def test_aes256gcm_encryption(self):
8283
cleartext = BlockIo.Helper.decrypt(result['aes_cipher_text'], key, result['aes_iv'], result['aes_cipher'], result['aes_auth_tag'])
8384
self.assertEqual(cleartext, clear.encode('utf-8'))
8485

86+
def test_dynamic_extract_key_aes256ecb(self):
87+
88+
user_key = json.loads('{"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}}')
89+
90+
key = BlockIo.Helper.dynamicExtractKey(user_key, "deadbeef")
91+
self.assertEqual(key.pubkey_hex(), BlockIo.Key.from_passphrase(unhexlify("beadbeef")).pubkey_hex())
92+
93+
def test_dynamic_extract_key_aes256cbc(self):
94+
95+
user_key = json.loads('{"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}}')
96+
97+
key = BlockIo.Helper.dynamicExtractKey(user_key, "deadbeef")
98+
self.assertEqual(key.pubkey_hex(), BlockIo.Key.from_passphrase(unhexlify("beadbeef")).pubkey_hex())
99+
100+
def test_dynamic_extract_key_aes256gcm(self):
101+
102+
user_key = json.loads('{"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":""}}')
103+
104+
key = BlockIo.Helper.dynamicExtractKey(user_key, "deadbeef")
105+
self.assertEqual(key.pubkey_hex(), BlockIo.Key.from_passphrase(unhexlify("beadbeef")).pubkey_hex())
106+
107+
85108
class TestKeys(unittest.TestCase):
86109
def setUp(self):
87110
self.priv = "6b0e34587dece0ef042c4c7205ce6b3d4a64d0bc484735b9325f7971a0ead963"

0 commit comments

Comments
 (0)