From 8d1109986c485e2325eb541e0177281ebc395f29 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 8 Nov 2016 15:37:48 +0100 Subject: [PATCH] trezor.crypto: curve.nist256p1 and curve.secp256k1 now sign/verify 256-bit digests, not arbitrary length messages --- .../modtrezorcrypto-nist256p1.h | 30 +++++++++---------- .../modtrezorcrypto-secp256k1.h | 30 +++++++++---------- mocks/trezor/config.py | 6 ++++ mocks/trezor/crypto/.mock-generated | 0 mocks/trezor/crypto/HDNode.py | 6 ++++ mocks/trezor/crypto/__init__.py | 0 mocks/trezor/crypto/curve/nist256p1.py | 8 ++--- mocks/trezor/crypto/curve/secp256k1.py | 8 ++--- tests/test_trezor.crypto.curve.nist256p1.py | 24 ++++++++++++--- tests/test_trezor.crypto.curve.secp256k1.py | 24 ++++++++++++--- 10 files changed, 90 insertions(+), 46 deletions(-) create mode 100644 mocks/trezor/crypto/.mock-generated create mode 100644 mocks/trezor/crypto/__init__.py diff --git a/extmod/modtrezorcrypto/modtrezorcrypto-nist256p1.h b/extmod/modtrezorcrypto/modtrezorcrypto-nist256p1.h index bf829af4c..1c00006aa 100644 --- a/extmod/modtrezorcrypto/modtrezorcrypto-nist256p1.h +++ b/extmod/modtrezorcrypto/modtrezorcrypto-nist256p1.h @@ -62,24 +62,24 @@ STATIC mp_obj_t mod_TrezorCrypto_Nist256p1_publickey(size_t n_args, const mp_obj } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_TrezorCrypto_Nist256p1_publickey_obj, 2, 3, mod_TrezorCrypto_Nist256p1_publickey); -/// def trezor.crypto.curve.nist256p1.sign(secret_key: bytes, message: bytes) -> bytes: +/// def trezor.crypto.curve.nist256p1.sign(secret_key: bytes, digest: bytes) -> bytes: /// ''' -/// Uses secret key to produce the signature of message. +/// Uses secret key to produce the signature of the digest. /// ''' -STATIC mp_obj_t mod_TrezorCrypto_Nist256p1_sign(mp_obj_t self, mp_obj_t secret_key, mp_obj_t message) { - mp_buffer_info_t sk, msg; +STATIC mp_obj_t mod_TrezorCrypto_Nist256p1_sign(mp_obj_t self, mp_obj_t secret_key, mp_obj_t digest) { + mp_buffer_info_t sk, dig; mp_get_buffer_raise(secret_key, &sk, MP_BUFFER_READ); - mp_get_buffer_raise(message, &msg, MP_BUFFER_READ); + mp_get_buffer_raise(digest, &dig, MP_BUFFER_READ); if (sk.len != 32) { mp_raise_ValueError("Invalid length of secret key"); } - if (msg.len == 0) { - mp_raise_ValueError("Empty data to sign"); + if (dig.len != 32) { + mp_raise_ValueError("Invalid length of digest"); } vstr_t vstr; vstr_init_len(&vstr, 65); uint8_t pby; - if (0 != ecdsa_sign(&nist256p1, (const uint8_t *)sk.buf, (const uint8_t *)msg.buf, msg.len, (uint8_t *)vstr.buf, &pby, NULL)) { // TODO: is_canonical + if (0 != ecdsa_sign_digest(&nist256p1, (const uint8_t *)sk.buf, (const uint8_t *)dig.buf, (uint8_t *)vstr.buf, &pby, NULL)) { // TODO: is_canonical mp_raise_ValueError("Signing failed"); } (void)pby; @@ -87,26 +87,26 @@ STATIC mp_obj_t mod_TrezorCrypto_Nist256p1_sign(mp_obj_t self, mp_obj_t secret_k } STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_TrezorCrypto_Nist256p1_sign_obj, mod_TrezorCrypto_Nist256p1_sign); -/// def trezor.crypto.curve.nist256p1.verify(public_key: bytes, signature: bytes, message: bytes) -> bool: +/// def trezor.crypto.curve.nist256p1.verify(public_key: bytes, signature: bytes, digest: bytes) -> bool: /// ''' -/// Uses public key to verify the signature of the message +/// Uses public key to verify the signature of the digest. /// Returns True on success. /// ''' STATIC mp_obj_t mod_TrezorCrypto_Nist256p1_verify(size_t n_args, const mp_obj_t *args) { - mp_buffer_info_t pk, sig, msg; + mp_buffer_info_t pk, sig, dig; mp_get_buffer_raise(args[1], &pk, MP_BUFFER_READ); mp_get_buffer_raise(args[2], &sig, MP_BUFFER_READ); - mp_get_buffer_raise(args[3], &msg, MP_BUFFER_READ); + mp_get_buffer_raise(args[3], &dig, MP_BUFFER_READ); if (pk.len != 33 && pk.len != 65) { mp_raise_ValueError("Invalid length of public key"); } if (sig.len != 65) { mp_raise_ValueError("Invalid length of signature"); } - if (msg.len == 0) { - mp_raise_ValueError("Empty data to verify"); + if (dig.len != 32) { + mp_raise_ValueError("Invalid length of digest"); } - return mp_obj_new_bool(0 == ecdsa_verify(&nist256p1, (const uint8_t *)pk.buf, (const uint8_t *)sig.buf, (const uint8_t *)msg.buf, msg.len)); + return mp_obj_new_bool(0 == ecdsa_verify_digest(&nist256p1, (const uint8_t *)pk.buf, (const uint8_t *)sig.buf, (const uint8_t *)dig.buf)); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_TrezorCrypto_Nist256p1_verify_obj, 4, 4, mod_TrezorCrypto_Nist256p1_verify); diff --git a/extmod/modtrezorcrypto/modtrezorcrypto-secp256k1.h b/extmod/modtrezorcrypto/modtrezorcrypto-secp256k1.h index e1f3e4994..c2ace29dd 100644 --- a/extmod/modtrezorcrypto/modtrezorcrypto-secp256k1.h +++ b/extmod/modtrezorcrypto/modtrezorcrypto-secp256k1.h @@ -62,24 +62,24 @@ STATIC mp_obj_t mod_TrezorCrypto_Secp256k1_publickey(size_t n_args, const mp_obj } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_TrezorCrypto_Secp256k1_publickey_obj, 2, 3, mod_TrezorCrypto_Secp256k1_publickey); -/// def trezor.crypto.curve.secp256k1.sign(secret_key: bytes, message: bytes) -> bytes: +/// def trezor.crypto.curve.secp256k1.sign(secret_key: bytes, digest: bytes) -> bytes: /// ''' -/// Uses secret key to produce the signature of message. +/// Uses secret key to produce the signature of the digest. /// ''' -STATIC mp_obj_t mod_TrezorCrypto_Secp256k1_sign(mp_obj_t self, mp_obj_t secret_key, mp_obj_t message) { - mp_buffer_info_t sk, msg; +STATIC mp_obj_t mod_TrezorCrypto_Secp256k1_sign(mp_obj_t self, mp_obj_t secret_key, mp_obj_t digest) { + mp_buffer_info_t sk, dig; mp_get_buffer_raise(secret_key, &sk, MP_BUFFER_READ); - mp_get_buffer_raise(message, &msg, MP_BUFFER_READ); + mp_get_buffer_raise(digest, &dig, MP_BUFFER_READ); if (sk.len != 32) { mp_raise_ValueError("Invalid length of secret key"); } - if (msg.len == 0) { - mp_raise_ValueError("Empty data to sign"); + if (dig.len != 32) { + mp_raise_ValueError("Invalid length of digest"); } vstr_t vstr; vstr_init_len(&vstr, 65); uint8_t pby; - if (0 != ecdsa_sign(&secp256k1, (const uint8_t *)sk.buf, (const uint8_t *)msg.buf, msg.len, (uint8_t *)vstr.buf, &pby, NULL)) { // TODO: is_canonical + if (0 != ecdsa_sign_digest(&secp256k1, (const uint8_t *)sk.buf, (const uint8_t *)dig.buf, (uint8_t *)vstr.buf, &pby, NULL)) { // TODO: is_canonical mp_raise_ValueError("Signing failed"); } (void)pby; @@ -87,26 +87,26 @@ STATIC mp_obj_t mod_TrezorCrypto_Secp256k1_sign(mp_obj_t self, mp_obj_t secret_k } STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_TrezorCrypto_Secp256k1_sign_obj, mod_TrezorCrypto_Secp256k1_sign); -/// def trezor.crypto.curve.secp256k1.verify(public_key: bytes, signature: bytes, message: bytes) -> bool: +/// def trezor.crypto.curve.secp256k1.verify(public_key: bytes, signature: bytes, digest: bytes) -> bool: /// ''' -/// Uses public key to verify the signature of the message +/// Uses public key to verify the signature of the digest. /// Returns True on success. /// ''' STATIC mp_obj_t mod_TrezorCrypto_Secp256k1_verify(size_t n_args, const mp_obj_t *args) { - mp_buffer_info_t pk, sig, msg; + mp_buffer_info_t pk, sig, dig; mp_get_buffer_raise(args[1], &pk, MP_BUFFER_READ); mp_get_buffer_raise(args[2], &sig, MP_BUFFER_READ); - mp_get_buffer_raise(args[3], &msg, MP_BUFFER_READ); + mp_get_buffer_raise(args[3], &dig, MP_BUFFER_READ); if (pk.len != 33 && pk.len != 65) { mp_raise_ValueError("Invalid length of public key"); } if (sig.len != 65) { mp_raise_ValueError("Invalid length of signature"); } - if (msg.len == 0) { - mp_raise_ValueError("Empty data to verify"); + if (dig.len != 32) { + mp_raise_ValueError("Invalid length of digest"); } - return mp_obj_new_bool(0 == ecdsa_verify(&secp256k1, (const uint8_t *)pk.buf, (const uint8_t *)sig.buf, (const uint8_t *)msg.buf, msg.len)); + return mp_obj_new_bool(0 == ecdsa_verify_digest(&secp256k1, (const uint8_t *)pk.buf, (const uint8_t *)sig.buf, (const uint8_t *)dig.buf)); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_TrezorCrypto_Secp256k1_verify_obj, 4, 4, mod_TrezorCrypto_Secp256k1_verify); diff --git a/mocks/trezor/config.py b/mocks/trezor/config.py index 37033d5d4..6e1466de5 100644 --- a/mocks/trezor/config.py +++ b/mocks/trezor/config.py @@ -11,3 +11,9 @@ def set(app: int, key: int, value: bytes) -> None: Sets a value of given key for given app. Returns True on success. ''' + +# ../extmod/modtrezorconfig/modtrezorconfig.c +def wipe() -> None: + ''' + Erases the whole config (use with caution!) + ''' diff --git a/mocks/trezor/crypto/.mock-generated b/mocks/trezor/crypto/.mock-generated new file mode 100644 index 000000000..e69de29bb diff --git a/mocks/trezor/crypto/HDNode.py b/mocks/trezor/crypto/HDNode.py index dfb28f06f..98f853965 100644 --- a/mocks/trezor/crypto/HDNode.py +++ b/mocks/trezor/crypto/HDNode.py @@ -23,6 +23,12 @@ def serialize_private() -> str: Serialize the private info HD node to base58 string. ''' +# ../extmod/modtrezorcrypto/modtrezorcrypto-bip32.h +def clone() -> HDNode: + ''' + Returns a copy of the HD node. + ''' + # ../extmod/modtrezorcrypto/modtrezorcrypto-bip32.h def depth() -> int: ''' diff --git a/mocks/trezor/crypto/__init__.py b/mocks/trezor/crypto/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/mocks/trezor/crypto/curve/nist256p1.py b/mocks/trezor/crypto/curve/nist256p1.py index f4b14138f..0375e2c2c 100644 --- a/mocks/trezor/crypto/curve/nist256p1.py +++ b/mocks/trezor/crypto/curve/nist256p1.py @@ -12,15 +12,15 @@ def publickey(secret_key: bytes, compressed: bool=True) -> bytes: ''' # ../extmod/modtrezorcrypto/modtrezorcrypto-nist256p1.h -def sign(secret_key: bytes, message: bytes) -> bytes: +def sign(secret_key: bytes, digest: bytes) -> bytes: ''' - Uses secret key to produce the signature of message. + Uses secret key to produce the signature of the digest. ''' # ../extmod/modtrezorcrypto/modtrezorcrypto-nist256p1.h -def verify(public_key: bytes, signature: bytes, message: bytes) -> bool: +def verify(public_key: bytes, signature: bytes, digest: bytes) -> bool: ''' - Uses public key to verify the signature of the message + Uses public key to verify the signature of the digest. Returns True on success. ''' diff --git a/mocks/trezor/crypto/curve/secp256k1.py b/mocks/trezor/crypto/curve/secp256k1.py index ab23fecf6..1b9681b64 100644 --- a/mocks/trezor/crypto/curve/secp256k1.py +++ b/mocks/trezor/crypto/curve/secp256k1.py @@ -12,15 +12,15 @@ def publickey(secret_key: bytes, compressed: bool=True) -> bytes: ''' # ../extmod/modtrezorcrypto/modtrezorcrypto-secp256k1.h -def sign(secret_key: bytes, message: bytes) -> bytes: +def sign(secret_key: bytes, digest: bytes) -> bytes: ''' - Uses secret key to produce the signature of message. + Uses secret key to produce the signature of the digest. ''' # ../extmod/modtrezorcrypto/modtrezorcrypto-secp256k1.h -def verify(public_key: bytes, signature: bytes, message: bytes) -> bool: +def verify(public_key: bytes, signature: bytes, digest: bytes) -> bool: ''' - Uses public key to verify the signature of the message + Uses public key to verify the signature of the digest. Returns True on success. ''' diff --git a/tests/test_trezor.crypto.curve.nist256p1.py b/tests/test_trezor.crypto.curve.nist256p1.py index 145d75d76..818202b4c 100644 --- a/tests/test_trezor.crypto.curve.nist256p1.py +++ b/tests/test_trezor.crypto.curve.nist256p1.py @@ -83,13 +83,29 @@ class TestCryptoNist256p1(unittest.TestCase): else: self.assertEqual(pk33, '03' + pk[:64]) + def test_sign_verify_min_max(self): + sk = nist256p1.generate_secret() + pk = nist256p1.publickey(sk) + + dig = bytes([1] + [0]*31) + sig = nist256p1.sign(sk, dig) + self.assertTrue(nist256p1.verify(pk, sig, dig)) + + dig = bytes([0]*31 + [1]) + sig = nist256p1.sign(sk, dig) + self.assertTrue(nist256p1.verify(pk, sig, dig)) + + dig = bytes([0xFF]*32) + sig = nist256p1.sign(sk, dig) + self.assertTrue(nist256p1.verify(pk, sig, dig)) + def test_sign_verify_random(self): - for l in range(1, 300): + for _ in range(100): sk = nist256p1.generate_secret() pk = nist256p1.publickey(sk) - msg = random.bytes(l) - sig = nist256p1.sign(sk, msg) - self.assertTrue(nist256p1.verify(pk, sig, msg)) + dig = random.bytes(32) + sig = nist256p1.sign(sk, dig) + self.assertTrue(nist256p1.verify(pk, sig, dig)) if __name__ == '__main__': unittest.main() diff --git a/tests/test_trezor.crypto.curve.secp256k1.py b/tests/test_trezor.crypto.curve.secp256k1.py index a74c9ade9..6ceff9e70 100644 --- a/tests/test_trezor.crypto.curve.secp256k1.py +++ b/tests/test_trezor.crypto.curve.secp256k1.py @@ -76,13 +76,29 @@ class TestCryptoSecp256k1(unittest.TestCase): else: self.assertEqual(pk33, '03' + pk[:64]) + def test_sign_verify_min_max(self): + sk = secp256k1.generate_secret() + pk = secp256k1.publickey(sk) + + dig = bytes([1] + [0]*31) + sig = secp256k1.sign(sk, dig) + self.assertTrue(secp256k1.verify(pk, sig, dig)) + + dig = bytes([0]*31 + [1]) + sig = secp256k1.sign(sk, dig) + self.assertTrue(secp256k1.verify(pk, sig, dig)) + + dig = bytes([0xFF]*32) + sig = secp256k1.sign(sk, dig) + self.assertTrue(secp256k1.verify(pk, sig, dig)) + def test_sign_verify_random(self): - for l in range(1, 300): + for _ in range(100): sk = secp256k1.generate_secret() pk = secp256k1.publickey(sk) - msg = random.bytes(l) - sig = secp256k1.sign(sk, msg) - self.assertTrue(secp256k1.verify(pk, sig, msg)) + dig = random.bytes(32) + sig = secp256k1.sign(sk, dig) + self.assertTrue(secp256k1.verify(pk, sig, dig)) if __name__ == '__main__': unittest.main()