diff --git a/extmod/modtrezorcrypto/modtrezorcrypto-pbkdf2.h b/extmod/modtrezorcrypto/modtrezorcrypto-pbkdf2.h new file mode 100644 index 0000000000..74d9e599a3 --- /dev/null +++ b/extmod/modtrezorcrypto/modtrezorcrypto-pbkdf2.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) Pavol Rusnak, SatoshiLabs + * + * Licensed under Microsoft Reference Source License (Ms-RSL) + * see LICENSE.md file for details + */ + +#include "py/objstr.h" + +#include "trezor-crypto/pbkdf2.h" + +// class Pbkdf2(object): +typedef struct _mp_obj_Pbkdf2_t { + mp_obj_base_t base; + union { + PBKDF2_HMAC_SHA256_CTX ctx256; + PBKDF2_HMAC_SHA512_CTX ctx512; + }; + int prf; +} mp_obj_Pbkdf2_t; + +STATIC mp_obj_t mod_TrezorCrypto_Pbkdf2_update(mp_obj_t self, mp_obj_t data); + +// def Pbkdf2.__init__(self, prf: str, password: bytes, salt: bytes, iterations: int=None) +STATIC mp_obj_t mod_TrezorCrypto_Pbkdf2_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 3, 4, false); + mp_obj_Pbkdf2_t *o = m_new_obj(mp_obj_Pbkdf2_t); + o->base.type = type; + + mp_buffer_info_t prf; + mp_get_buffer_raise(args[0], &prf, MP_BUFFER_READ); + mp_buffer_info_t password; + mp_get_buffer_raise(args[1], &password, MP_BUFFER_READ); + mp_buffer_info_t salt; + mp_get_buffer_raise(args[2], &salt, MP_BUFFER_READ); + + o->prf = 0; + if (prf.len == 11 && memcmp(prf.buf, "hmac-sha256", prf.len) == 0) { + pbkdf2_hmac_sha256_Init(&(o->ctx256), password.buf, password.len, salt.buf, salt.len); + o->prf = 256; + } else + if (prf.len == 11 && memcmp(prf.buf, "hmac-sha512", prf.len) == 0) { + pbkdf2_hmac_sha512_Init(&(o->ctx512), password.buf, password.len, salt.buf, salt.len); + o->prf = 512; + } else + if (o->prf == 0) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "Invalid PRF")); + } + // constructor called with iterations as fourth parameter + if (n_args > 3) { + mod_TrezorCrypto_Pbkdf2_update(MP_OBJ_FROM_PTR(o), args[3]); + } + return MP_OBJ_FROM_PTR(o); +} + +// def Pbkdf2.update(self, iterations: int) -> None +STATIC mp_obj_t mod_TrezorCrypto_Pbkdf2_update(mp_obj_t self, mp_obj_t iterations) { + mp_obj_Pbkdf2_t *o = MP_OBJ_TO_PTR(self); + uint32_t iter = mp_obj_get_int(iterations); + if (o->prf == 256) { + pbkdf2_hmac_sha256_Update(&(o->ctx256), iter); + } + if (o->prf == 512) { + pbkdf2_hmac_sha512_Update(&(o->ctx512), iter); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_TrezorCrypto_Pbkdf2_update_obj, mod_TrezorCrypto_Pbkdf2_update); + +// def Pbkdf2.key(self) -> bytes +STATIC mp_obj_t mod_TrezorCrypto_Pbkdf2_key(mp_obj_t self) { + mp_obj_Pbkdf2_t *o = MP_OBJ_TO_PTR(self); + vstr_t vstr; + if (o->prf == 256) { + vstr_init_len(&vstr, SHA256_DIGEST_LENGTH); + memcpy(vstr.buf, o->ctx256.f, SHA256_DIGEST_LENGTH); + } + if (o->prf == 512) { + vstr_init_len(&vstr, SHA512_DIGEST_LENGTH); + memcpy(vstr.buf, o->ctx512.f, SHA512_DIGEST_LENGTH); + } + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_TrezorCrypto_Pbkdf2_key_obj, mod_TrezorCrypto_Pbkdf2_key); + +// def Pbkdf2.__del__(self) -> None +STATIC mp_obj_t mod_TrezorCrypto_Pbkdf2___del__(mp_obj_t self) { + mp_obj_Pbkdf2_t *o = MP_OBJ_TO_PTR(self); + memset(&(o->ctx256), 0, sizeof(PBKDF2_HMAC_SHA256_CTX)); + memset(&(o->ctx512), 0, sizeof(PBKDF2_HMAC_SHA512_CTX)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_TrezorCrypto_Pbkdf2___del___obj, mod_TrezorCrypto_Pbkdf2___del__); + +// Pbkdf2 stuff + +STATIC const mp_rom_map_elem_t mod_TrezorCrypto_Pbkdf2_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&mod_TrezorCrypto_Pbkdf2_update_obj) }, + { MP_ROM_QSTR(MP_QSTR_key), MP_ROM_PTR(&mod_TrezorCrypto_Pbkdf2_key_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mod_TrezorCrypto_Pbkdf2___del___obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(mod_TrezorCrypto_Pbkdf2_locals_dict, mod_TrezorCrypto_Pbkdf2_locals_dict_table); + +STATIC const mp_obj_type_t mod_TrezorCrypto_Pbkdf2_type = { + { &mp_type_type }, + .name = MP_QSTR_Pbkdf2, + .make_new = mod_TrezorCrypto_Pbkdf2_make_new, + .locals_dict = (void*)&mod_TrezorCrypto_Pbkdf2_locals_dict, +}; diff --git a/extmod/modtrezorcrypto/modtrezorcrypto-pbkdf2_hmac.h b/extmod/modtrezorcrypto/modtrezorcrypto-pbkdf2_hmac.h deleted file mode 100644 index 193408264e..0000000000 --- a/extmod/modtrezorcrypto/modtrezorcrypto-pbkdf2_hmac.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) Pavol Rusnak, SatoshiLabs - * - * Licensed under Microsoft Reference Source License (Ms-RSL) - * see LICENSE.md file for details - */ - -#include "py/objstr.h" - -#include "trezor-crypto/pbkdf2.h" - -// def pbkdf2_hmac(hash_name: str, password: bytes, salt: bytes, iterations: int, dklen:int=None) -> bytes -STATIC mp_obj_t mod_TrezorCrypto_pbkdf2_hmac(size_t n_args, const mp_obj_t *args) { - mp_buffer_info_t hash_name; - mp_get_buffer_raise(args[0], &hash_name, MP_BUFFER_READ); - mp_buffer_info_t password; - mp_get_buffer_raise(args[1], &password, MP_BUFFER_READ); - mp_buffer_info_t salt; - mp_get_buffer_raise(args[2], &salt, MP_BUFFER_READ); - mp_int_t iterations = mp_obj_get_int(args[3]); - mp_int_t dklen = (n_args > 4) ? mp_obj_get_int(args[4]) : 0; - - int digestsize = 0; - if (hash_name.len == 6 && memcmp(hash_name.buf, "sha256", hash_name.len) == 0) { - digestsize = 32; - } else - if (hash_name.len == 6 && memcmp(hash_name.buf, "sha512", hash_name.len) == 0) { - digestsize = 64; - } - if (digestsize == 0) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "Invalid hash_name")); - } - if (dklen == 0) { - dklen = digestsize; - } - vstr_t vstr; - vstr_init_len(&vstr, dklen); - if (digestsize == 32) { - pbkdf2_hmac_sha256(password.buf, password.len, salt.buf, salt.len, iterations, (uint8_t *)vstr.buf, dklen, NULL); - } - if (digestsize == 64) { - pbkdf2_hmac_sha512(password.buf, password.len, salt.buf, salt.len, iterations, (uint8_t *)vstr.buf, dklen, NULL); - } - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_TrezorCrypto_pbkdf2_hmac_obj, 4, 5, mod_TrezorCrypto_pbkdf2_hmac); diff --git a/extmod/modtrezorcrypto/modtrezorcrypto.c b/extmod/modtrezorcrypto/modtrezorcrypto.c index 14f37d02ce..c200d584a9 100644 --- a/extmod/modtrezorcrypto/modtrezorcrypto.c +++ b/extmod/modtrezorcrypto/modtrezorcrypto.c @@ -16,7 +16,7 @@ #if MICROPY_PY_TREZORCRYPTO #include "modtrezorcrypto-ed25519.h" -#include "modtrezorcrypto-pbkdf2_hmac.h" +#include "modtrezorcrypto-pbkdf2.h" #include "modtrezorcrypto-ripemd160.h" #include "modtrezorcrypto-nist256p1.h" #include "modtrezorcrypto-secp256k1.h" @@ -31,13 +31,13 @@ STATIC const mp_rom_map_elem_t mp_module_TrezorCrypto_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_TrezorCrypto) }, { MP_ROM_QSTR(MP_QSTR_Ed25519), MP_ROM_PTR(&mod_TrezorCrypto_Ed25519_type) }, { MP_ROM_QSTR(MP_QSTR_Nist256p1), MP_ROM_PTR(&mod_TrezorCrypto_Nist256p1_type) }, + { MP_ROM_QSTR(MP_QSTR_Pbkdf2), MP_ROM_PTR(&mod_TrezorCrypto_Pbkdf2_type) }, { MP_ROM_QSTR(MP_QSTR_Ripemd160), MP_ROM_PTR(&mod_TrezorCrypto_Ripemd160_type) }, { MP_ROM_QSTR(MP_QSTR_Secp256k1), MP_ROM_PTR(&mod_TrezorCrypto_Secp256k1_type) }, { MP_ROM_QSTR(MP_QSTR_Sha256), MP_ROM_PTR(&mod_TrezorCrypto_Sha256_type) }, { MP_ROM_QSTR(MP_QSTR_Sha512), MP_ROM_PTR(&mod_TrezorCrypto_Sha512_type) }, { MP_ROM_QSTR(MP_QSTR_Sha3_256), MP_ROM_PTR(&mod_TrezorCrypto_Sha3_256_type) }, { MP_ROM_QSTR(MP_QSTR_Sha3_512), MP_ROM_PTR(&mod_TrezorCrypto_Sha3_512_type) }, - { MP_ROM_QSTR(MP_QSTR_pbkdf2_hmac), MP_ROM_PTR(&mod_TrezorCrypto_pbkdf2_hmac_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_TrezorCrypto_globals, mp_module_TrezorCrypto_globals_table); diff --git a/src/tests/test_crypto_pbkdf2.py b/src/tests/test_crypto_pbkdf2.py index 0c8eb38162..fb458e8006 100644 --- a/src/tests/test_crypto_pbkdf2.py +++ b/src/tests/test_crypto_pbkdf2.py @@ -4,40 +4,105 @@ sys.path.append('../lib') import unittest from ubinascii import unhexlify -from trezor.crypto import hashlib -from trezor.crypto import hmac +from trezor.crypto import pbkdf2 class TestCryptoPbkdf2(unittest.TestCase): # vectors from https://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors - def test_hmac_sha256(self): + def test_pbkdf2_hmac_sha256(self): P = b'password' S = b'salt' - dk = hashlib.pbkdf2_hmac('sha256', P, S, 1) + dk = pbkdf2('hmac-sha256', P, S, 1).key() self.assertEqual(dk, unhexlify('120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b')) - dk = hashlib.pbkdf2_hmac('sha256', P, S, 2) + dk = pbkdf2('hmac-sha256', P, S, 2).key() self.assertEqual(dk, unhexlify('ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43')) - dk = hashlib.pbkdf2_hmac('sha256', P, S, 4096) + dk = pbkdf2('hmac-sha256', P, S, 4096).key() self.assertEqual(dk, unhexlify('c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a')) P = b'passwordPASSWORDpassword' S = b'saltSALTsaltSALTsaltSALTsaltSALTsalt' - dk = hashlib.pbkdf2_hmac('sha256', P, S, 4096, 40) - self.assertEqual(dk, unhexlify('348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1c635518c7dac47e9')) + dk = pbkdf2('hmac-sha256', P, S, 4096).key() + self.assertEqual(dk, unhexlify('348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1')) - # vectors from https://stackoverflow.com/questions/15593184/pbkdf2-hmac-sha-512-test-vectors - def test_hmac_sha512(self): + def test_pbkdf2_hmac_sha256_update(self): P = b'password' S = b'salt' - dk = hashlib.pbkdf2_hmac('sha512', P, S, 1) + p = pbkdf2('hmac-sha256', P, S) + p.update(1) + dk = p.key() + self.assertEqual(dk, unhexlify('120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b')) + p = pbkdf2('hmac-sha256', P, S) + p.update(1) + p.update(1) + dk = p.key() + self.assertEqual(dk, unhexlify('ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43')) + p = pbkdf2('hmac-sha256', P, S) + for i in range(32): + p.update(128) + dk = p.key() + self.assertEqual(dk, unhexlify('c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a')) + P = b'passwordPASSWORDpassword' + S = b'saltSALTsaltSALTsaltSALTsaltSALTsalt' + p = pbkdf2('hmac-sha256', P, S) + for i in range(64): + p.update(64) + dk = p.key() + self.assertEqual(dk, unhexlify('348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1')) + + # vectors from https://stackoverflow.com/questions/15593184/pbkdf2-hmac-sha-512-test-vectors + def test_pbkdf2_hmac_sha512(self): + P = b'password' + S = b'salt' + dk = pbkdf2('hmac-sha512', P, S, 1).key() self.assertEqual(dk, unhexlify('867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252c02d470a285a0501bad999bfe943c08f050235d7d68b1da55e63f73b60a57fce')) - dk = hashlib.pbkdf2_hmac('sha512', P, S, 2) + dk = pbkdf2('hmac-sha512', P, S, 2).key() self.assertEqual(dk, unhexlify('e1d9c16aa681708a45f5c7c4e215ceb66e011a2e9f0040713f18aefdb866d53cf76cab2868a39b9f7840edce4fef5a82be67335c77a6068e04112754f27ccf4e')) - dk = hashlib.pbkdf2_hmac('sha512', P, S, 4096) + dk = pbkdf2('hmac-sha512', P, S, 4096).key() self.assertEqual(dk, unhexlify('d197b1b33db0143e018b12f3d1d1479e6cdebdcc97c5c0f87f6902e072f457b5143f30602641b3d55cd335988cb36b84376060ecd532e039b742a239434af2d5')) P = b'passwordPASSWORDpassword' S = b'saltSALTsaltSALTsaltSALTsaltSALTsalt' - dk = hashlib.pbkdf2_hmac('sha512', P, S, 4096) + dk = pbkdf2('hmac-sha512', P, S, 4096).key() self.assertEqual(dk, unhexlify('8c0511f4c6e597c6ac6315d8f0362e225f3c501495ba23b868c005174dc4ee71115b59f9e60cd9532fa33e0f75aefe30225c583a186cd82bd4daea9724a3d3b8')) + def test_pbkdf2_hmac_sha512_update(self): + P = b'password' + S = b'salt' + p = pbkdf2('hmac-sha512', P, S) + p.update(1) + dk = p.key() + self.assertEqual(dk, unhexlify('867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252c02d470a285a0501bad999bfe943c08f050235d7d68b1da55e63f73b60a57fce')) + p = pbkdf2('hmac-sha512', P, S) + p.update(1) + p.update(1) + dk = p.key() + self.assertEqual(dk, unhexlify('e1d9c16aa681708a45f5c7c4e215ceb66e011a2e9f0040713f18aefdb866d53cf76cab2868a39b9f7840edce4fef5a82be67335c77a6068e04112754f27ccf4e')) + p = pbkdf2('hmac-sha512', P, S) + for i in range(32): + p.update(128) + dk = p.key() + self.assertEqual(dk, unhexlify('d197b1b33db0143e018b12f3d1d1479e6cdebdcc97c5c0f87f6902e072f457b5143f30602641b3d55cd335988cb36b84376060ecd532e039b742a239434af2d5')) + P = b'passwordPASSWORDpassword' + S = b'saltSALTsaltSALTsaltSALTsaltSALTsalt' + p = pbkdf2('hmac-sha512', P, S) + for i in range(64): + p.update(64) + dk = p.key() + self.assertEqual(dk, unhexlify('8c0511f4c6e597c6ac6315d8f0362e225f3c501495ba23b868c005174dc4ee71115b59f9e60cd9532fa33e0f75aefe30225c583a186cd82bd4daea9724a3d3b8')) + + def test_key_multi(self): + P = b'password' + S = b'salt' + p = pbkdf2('hmac-sha256', P, S, 16) + k0 = p.key() + k1 = p.key() + k2 = p.key() + self.assertEqual(k0, k1) + self.assertEqual(k0, k2) + p = pbkdf2('hmac-sha512', P, S, 16) + k0 = p.key() + k1 = p.key() + k2 = p.key() + self.assertEqual(k0, k1) + self.assertEqual(k0, k2) + if __name__ == '__main__': unittest.main() diff --git a/src/trezor/crypto/__init__.py b/src/trezor/crypto/__init__.py index e69de29bb2..40daa898aa 100644 --- a/src/trezor/crypto/__init__.py +++ b/src/trezor/crypto/__init__.py @@ -0,0 +1 @@ +from TrezorCrypto import Pbkdf2 as pbkdf2 diff --git a/src/trezor/crypto/hashlib.py b/src/trezor/crypto/hashlib.py index c7b26cb49b..3f8708dab6 100644 --- a/src/trezor/crypto/hashlib.py +++ b/src/trezor/crypto/hashlib.py @@ -3,4 +3,3 @@ from TrezorCrypto import Sha256 as sha256 from TrezorCrypto import Sha512 as sha512 from TrezorCrypto import Sha3_256 as sha3_256 from TrezorCrypto import Sha3_512 as sha3_512 -from TrezorCrypto import pbkdf2_hmac diff --git a/vendor/trezor-crypto b/vendor/trezor-crypto index b8ec5567ba..242a5de275 160000 --- a/vendor/trezor-crypto +++ b/vendor/trezor-crypto @@ -1 +1 @@ -Subproject commit b8ec5567ba701c77379f2111774456eb18b98790 +Subproject commit 242a5de275d36e2df6792c921a9cd7d8c8e8933b