diff --git a/.gitmodules b/.gitmodules index aefae6c91..29ebb8a14 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,7 @@ [submodule "vendor/QR-Code-generator"] path = vendor/QR-Code-generator url = https://github.com/nayuki/QR-Code-generator.git +[submodule "vendor/secp256k1-zkp"] + path = vendor/secp256k1-zkp + url = https://github.com/romanz/secp256k1-zkp.git + branch = zkp-trezor diff --git a/SConscript.firmware b/SConscript.firmware index fcc9dee2f..ac98b0ea3 100644 --- a/SConscript.firmware +++ b/SConscript.firmware @@ -6,6 +6,7 @@ CCFLAGS_MOD = '' CPPPATH_MOD = [] CPPDEFINES_MOD = [] SOURCE_MOD = [] +SOURCE_MOD_SECP256K1_ZKP = [] PYOPT = '1' @@ -85,6 +86,31 @@ SOURCE_MOD += [ 'vendor/trezor-crypto/sha3.c', ] +# libsecp256k1-zkp +CPPPATH_MOD += [ + 'vendor/secp256k1-zkp', + 'vendor/secp256k1-zkp/src', + 'vendor/secp256k1-zkp/include', +] +CPPDEFINES_MOD += [ + 'SECP256K1_BUILD', + 'USE_NUM_NONE', + 'USE_FIELD_INV_BUILTIN', + 'USE_SCALAR_INV_BUILTIN', + 'USE_FIELD_10X26', + 'USE_SCALAR_8X32', + 'USE_ECMULT_STATIC_PRECOMPUTATION', + 'USE_EXTERNAL_DEFAULT_CALLBACKS', + ('ECMULT_WINDOW_SIZE', '8'), + 'ENABLE_MODULE_GENERATOR', + 'ENABLE_MODULE_RANGEPROOF', + 'ENABLE_MODULE_RECOVERY', + 'ENABLE_MODULE_ECDH', +] +SOURCE_MOD_SECP256K1_ZKP += [ + 'vendor/secp256k1-zkp/src/secp256k1.c', +] + # modtrezorio SOURCE_MOD += [ 'embed/extmod/modtrezorio/modtrezorio.c', @@ -403,12 +429,34 @@ source_mpyc = env.FrozenCFile( env.Depends(source_mpyc, qstr_generated) +# +# static secp256-zkp ecmult context +# + +host_env = Environment(ENV=os.environ) +host_env.Replace( + CC=os.getenv('CC_FOR_BUILD') or 'gcc', + COPT='-O2', + CPPPATH='vendor/secp256k1-zkp', +) +gen_context = host_env.Program( + target='vendor/secp256k1-zkp/gen_context', + source='vendor/secp256k1-zkp/src/gen_context.c', +) + +secp256k1_zkp_ecmult_static_context = host_env.Command( + target='vendor/secp256k1-zkp/src/ecmult_static_context.h', + source='vendor/secp256k1-zkp/gen_context', + action='cd ${SOURCE.dir}; ./gen_context', +) + # # Program objects # obj_program = [] obj_program.extend(env.Object(source=SOURCE_MOD)) +obj_program.extend(env.Object(source=SOURCE_MOD_SECP256K1_ZKP, CCFLAGS='$CCFLAGS -Wno-unused-function')) obj_program.extend(env.Object(source=SOURCE_FIRMWARE)) obj_program.extend(env.Object(source=SOURCE_MICROPYTHON)) obj_program.extend(env.Object(source=SOURCE_MICROPYTHON_SPEED, COPT='-O3')) @@ -435,6 +483,7 @@ obj_program.extend( ' $SOURCE $TARGET', )) env.Depends(obj_program, qstr_generated) +env.Depends(obj_program, secp256k1_zkp_ecmult_static_context) program_elf = env.Command( target='firmware.elf', diff --git a/SConscript.unix b/SConscript.unix index 3ba197eaa..deaf3ed4b 100644 --- a/SConscript.unix +++ b/SConscript.unix @@ -5,6 +5,7 @@ import os CCFLAGS_MOD = '' CPPPATH_MOD = [] CPPDEFINES_MOD = [] +SOURCE_MOD_SECP256K1_ZKP = [] SOURCE_MOD = [] LIBS_MOD = [] @@ -82,6 +83,31 @@ SOURCE_MOD += [ 'vendor/trezor-crypto/nem.c', ] +# libsecp256k1-zkp +CPPPATH_MOD += [ + 'vendor/secp256k1-zkp/', + 'vendor/secp256k1-zkp/src', + 'vendor/secp256k1-zkp/include', +] +CPPDEFINES_MOD += [ + 'SECP256K1_BUILD', + 'USE_NUM_NONE', + 'USE_FIELD_INV_BUILTIN', + 'USE_SCALAR_INV_BUILTIN', + 'USE_FIELD_10X26', + 'USE_SCALAR_8X32', + 'USE_ECMULT_STATIC_PRECOMPUTATION', + 'USE_EXTERNAL_DEFAULT_CALLBACKS', + ('ECMULT_WINDOW_SIZE', '8'), + 'ENABLE_MODULE_GENERATOR', + 'ENABLE_MODULE_RANGEPROOF', + 'ENABLE_MODULE_RECOVERY', + 'ENABLE_MODULE_ECDH', +] +SOURCE_MOD_SECP256K1_ZKP += [ + 'vendor/secp256k1-zkp/src/secp256k1.c', +] + # modtrezorio SOURCE_MOD += [ 'embed/extmod/modtrezorio/modtrezorio.c', @@ -346,16 +372,39 @@ qstr_generated = env.GenerateQstrDefs( env.Ignore(qstr_collected, qstr_generated) +# +# static secp256-zkp ecmult context +# + +host_env = Environment(ENV=os.environ) +host_env.Replace( + CC=os.getenv('CC_FOR_BUILD') or 'gcc', + COPT='-O2', + CPPPATH='vendor/secp256k1-zkp', +) +gen_context = host_env.Program( + target='vendor/secp256k1-zkp/gen_context', + source='vendor/secp256k1-zkp/src/gen_context.c', +) + +secp256k1_zkp_ecmult_static_context = host_env.Command( + target='vendor/secp256k1-zkp/src/ecmult_static_context.h', + source='vendor/secp256k1-zkp/gen_context', + action='cd ${SOURCE.dir}; ./gen_context', +) + # # Program objects # obj_program = [] obj_program += env.Object(source=SOURCE_MOD) +obj_program += env.Object(source=SOURCE_MOD_SECP256K1_ZKP, CCFLAGS='$CCFLAGS -Wno-unused-function') obj_program += env.Object(source=SOURCE_MICROPYTHON) obj_program += env.Object(source=SOURCE_UNIX) env.Depends(obj_program, qstr_generated) +env.Depends(obj_program, secp256k1_zkp_ecmult_static_context) program = env.Command( target='micropython', diff --git a/embed/extmod/modtrezorcrypto/modtrezorcrypto-secp256k1.h b/embed/extmod/modtrezorcrypto/modtrezorcrypto-secp256k1.h index 647ef5f26..cb9cd6f5c 100644 --- a/embed/extmod/modtrezorcrypto/modtrezorcrypto-secp256k1.h +++ b/embed/extmod/modtrezorcrypto/modtrezorcrypto-secp256k1.h @@ -19,8 +19,8 @@ #include "py/objstr.h" -#include "ecdsa.h" -#include "secp256k1.h" +#include "vendor/trezor-crypto/ecdsa.h" +#include "vendor/trezor-crypto/secp256k1.h" /// package: trezorcrypto.secp256k1 diff --git a/embed/extmod/modtrezorcrypto/modtrezorcrypto-secp256k1_zkp.h b/embed/extmod/modtrezorcrypto/modtrezorcrypto-secp256k1_zkp.h new file mode 100644 index 000000000..45e6ff11e --- /dev/null +++ b/embed/extmod/modtrezorcrypto/modtrezorcrypto-secp256k1_zkp.h @@ -0,0 +1,307 @@ +/* + * This file is part of the TREZOR project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "py/objstr.h" + +#include "vendor/secp256k1-zkp/include/secp256k1.h" +#include "vendor/secp256k1-zkp/include/secp256k1_ecdh.h" +#include "vendor/secp256k1-zkp/include/secp256k1_preallocated.h" +#include "vendor/secp256k1-zkp/include/secp256k1_recovery.h" + +// The minimum buffer size can vary in future secp256k1-zkp revisions. +// It can always be determined by a call to +// secp256k1_context_preallocated_size(...) as below. +STATIC uint8_t g_buffer[(1UL << (ECMULT_WINDOW_SIZE + 4)) + 208] = {0}; + +void secp256k1_default_illegal_callback_fn(const char *str, void *data) { + (void)data; + mp_raise_ValueError(str); + return; +} + +void secp256k1_default_error_callback_fn(const char *str, void *data) { + (void)data; + mp_raise_msg(&mp_type_RuntimeError, str); + return; +} + +STATIC const secp256k1_context *mod_trezorcrypto_secp256k1_context(void) { + static secp256k1_context *ctx; + if (ctx == NULL) { + size_t sz = secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN | + SECP256K1_CONTEXT_VERIFY); + if (sz > sizeof g_buffer) { + mp_raise_ValueError("secp256k1 context is too large"); + } + void *buf = (void *)g_buffer; + ctx = secp256k1_context_preallocated_create( + buf, SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + uint8_t rand[32]; + random_buffer(rand, 32); + int ret = secp256k1_context_randomize(ctx, rand); + if (ret != 1) { + mp_raise_msg(&mp_type_RuntimeError, "secp256k1_context_randomize failed"); + } + } + return ctx; +} + +/// def generate_secret() -> bytes: +/// ''' +/// Generate secret key. +/// ''' +STATIC mp_obj_t mod_trezorcrypto_secp256k1_zkp_generate_secret() { + uint8_t out[32]; + for (;;) { + random_buffer(out, 32); + // check whether secret > 0 && secret < curve_order + if (0 == memcmp(out, + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00", + 32)) + continue; + if (0 <= memcmp(out, + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFE\xBA\xAE\xDC\xE6\xAF\x48\xA0\x3B\xBF\xD2" + "\x5E\x8C\xD0\x36\x41\x41", + 32)) + continue; + break; + } + return mp_obj_new_bytes(out, sizeof(out)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0( + mod_trezorcrypto_secp256k1_zkp_generate_secret_obj, + mod_trezorcrypto_secp256k1_zkp_generate_secret); + +/// def publickey(secret_key: bytes, compressed: bool = True) -> bytes: +/// ''' +/// Computes public key from secret key. +/// ''' +STATIC mp_obj_t mod_trezorcrypto_secp256k1_zkp_publickey(size_t n_args, + const mp_obj_t *args) { + const secp256k1_context *ctx = mod_trezorcrypto_secp256k1_context(); + mp_buffer_info_t sk; + mp_get_buffer_raise(args[0], &sk, MP_BUFFER_READ); + secp256k1_pubkey pk; + if (sk.len != 32) { + mp_raise_ValueError("Invalid length of secret key"); + } + if (!secp256k1_ec_pubkey_create(ctx, &pk, (const unsigned char *)sk.buf)) { + mp_raise_ValueError("Invalid secret key"); + } + + bool compressed = n_args < 2 || args[1] == mp_const_true; + uint8_t out[65]; + size_t outlen = sizeof(out); + secp256k1_ec_pubkey_serialize( + ctx, out, &outlen, &pk, + compressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED); + return mp_obj_new_bytes(out, outlen); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( + mod_trezorcrypto_secp256k1_zkp_publickey_obj, 1, 2, + mod_trezorcrypto_secp256k1_zkp_publickey); + +/// def sign(secret_key: bytes, digest: bytes, compressed: bool = True) -> +/// bytes: +/// ''' +/// Uses secret key to produce the signature of the digest. +/// ''' +STATIC mp_obj_t mod_trezorcrypto_secp256k1_zkp_sign(size_t n_args, + const mp_obj_t *args) { + const secp256k1_context *ctx = mod_trezorcrypto_secp256k1_context(); + mp_buffer_info_t sk, dig; + mp_get_buffer_raise(args[0], &sk, MP_BUFFER_READ); + mp_get_buffer_raise(args[1], &dig, MP_BUFFER_READ); + bool compressed = n_args < 3 || args[2] == mp_const_true; + if (sk.len != 32) { + mp_raise_ValueError("Invalid length of secret key"); + } + if (dig.len != 32) { + mp_raise_ValueError("Invalid length of digest"); + } + secp256k1_ecdsa_recoverable_signature sig; + uint8_t out[65]; + int pby; + if (!secp256k1_ecdsa_sign_recoverable(ctx, &sig, (const uint8_t *)dig.buf, + (const uint8_t *)sk.buf, NULL, NULL)) { + mp_raise_ValueError("Signing failed"); + } + secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, &out[1], &pby, + &sig); + out[0] = 27 + pby + compressed * 4; + return mp_obj_new_bytes(out, sizeof(out)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( + mod_trezorcrypto_secp256k1_zkp_sign_obj, 2, 3, + mod_trezorcrypto_secp256k1_zkp_sign); + +/// def verify(public_key: bytes, signature: bytes, digest: bytes) -> bool: +/// ''' +/// Uses public key to verify the signature of the digest. +/// Returns True on success. +/// ''' +STATIC mp_obj_t mod_trezorcrypto_secp256k1_zkp_verify(mp_obj_t public_key, + mp_obj_t signature, + mp_obj_t digest) { + const secp256k1_context *ctx = mod_trezorcrypto_secp256k1_context(); + mp_buffer_info_t pk, sig, dig; + mp_get_buffer_raise(public_key, &pk, MP_BUFFER_READ); + mp_get_buffer_raise(signature, &sig, MP_BUFFER_READ); + mp_get_buffer_raise(digest, &dig, MP_BUFFER_READ); + if (pk.len != 33 && pk.len != 65) { + mp_raise_ValueError("Invalid length of public key"); + } + if (sig.len != 64 && sig.len != 65) { + mp_raise_ValueError("Invalid length of signature"); + } + int offset = sig.len - 64; + if (dig.len != 32) { + mp_raise_ValueError("Invalid length of digest"); + } + secp256k1_ecdsa_signature ec_sig; + if (!secp256k1_ecdsa_signature_parse_compact( + ctx, &ec_sig, (const uint8_t *)sig.buf + offset)) { + mp_raise_ValueError("Invalid signature"); + } + secp256k1_pubkey ec_pk; + if (!secp256k1_ec_pubkey_parse(ctx, &ec_pk, (const uint8_t *)pk.buf, + pk.len)) { + mp_raise_ValueError("Invalid public key"); + } + return mp_obj_new_bool(1 == secp256k1_ecdsa_verify(ctx, &ec_sig, + (const uint8_t *)dig.buf, + &ec_pk)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorcrypto_secp256k1_zkp_verify_obj, + mod_trezorcrypto_secp256k1_zkp_verify); + +/// def verify_recover(signature: bytes, digest: bytes) -> bytes: +/// ''' +/// Uses signature of the digest to verify the digest and recover the public +/// key. Returns public key on success, None on failure. +/// ''' +STATIC mp_obj_t mod_trezorcrypto_secp256k1_zkp_verify_recover( + mp_obj_t signature, mp_obj_t digest) { + const secp256k1_context *ctx = mod_trezorcrypto_secp256k1_context(); + mp_buffer_info_t sig, dig; + mp_get_buffer_raise(signature, &sig, MP_BUFFER_READ); + mp_get_buffer_raise(digest, &dig, MP_BUFFER_READ); + if (sig.len != 65) { + mp_raise_ValueError("Invalid length of signature"); + } + if (dig.len != 32) { + mp_raise_ValueError("Invalid length of digest"); + } + int recid = ((const uint8_t *)sig.buf)[0] - 27; + if (recid >= 8) { + mp_raise_ValueError("Invalid recid in signature"); + } + bool compressed = (recid >= 4); + recid &= 3; + + secp256k1_ecdsa_recoverable_signature ec_sig; + if (!secp256k1_ecdsa_recoverable_signature_parse_compact( + ctx, &ec_sig, (const uint8_t *)sig.buf + 1, recid)) { + mp_raise_ValueError("Invalid signature"); + } + secp256k1_pubkey pk; + if (!secp256k1_ecdsa_recover(ctx, &pk, &ec_sig, (const uint8_t *)dig.buf)) { + // Copying "return None" behaviour from secp256k1 module; but why do + // we do this rather than raising an exception? + return mp_const_none; + } + uint8_t out[65]; + size_t pklen = sizeof(out); + secp256k1_ec_pubkey_serialize( + ctx, out, &pklen, &pk, + compressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED); + return mp_obj_new_bytes(out, pklen); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2( + mod_trezorcrypto_secp256k1_zkp_verify_recover_obj, + mod_trezorcrypto_secp256k1_zkp_verify_recover); + +static int secp256k1_ecdh_hash_passthrough(uint8_t *output, const uint8_t *x, + const uint8_t *y, void *data) { + output[0] = 0x04; + memcpy(&output[1], x, 32); + memcpy(&output[33], y, 32); + (void)data; + return 1; +} + +/// def multiply(secret_key: bytes, public_key: bytes) -> bytes: +/// ''' +/// Multiplies point defined by public_key with scalar defined by +/// secret_key. Useful for ECDH. +/// ''' +STATIC mp_obj_t mod_trezorcrypto_secp256k1_zkp_multiply(mp_obj_t secret_key, + mp_obj_t public_key) { + const secp256k1_context *ctx = mod_trezorcrypto_secp256k1_context(); + mp_buffer_info_t sk, pk; + mp_get_buffer_raise(secret_key, &sk, MP_BUFFER_READ); + mp_get_buffer_raise(public_key, &pk, MP_BUFFER_READ); + if (sk.len != 32) { + mp_raise_ValueError("Invalid length of secret key"); + } + if (pk.len != 33 && pk.len != 65) { + mp_raise_ValueError("Invalid length of public key"); + } + secp256k1_pubkey ec_pk; + if (!secp256k1_ec_pubkey_parse(ctx, &ec_pk, (const uint8_t *)pk.buf, + pk.len)) { + mp_raise_ValueError("Invalid public key"); + } + uint8_t out[65]; + if (!secp256k1_ecdh(ctx, out, &ec_pk, (const uint8_t *)sk.buf, + secp256k1_ecdh_hash_passthrough, NULL)) { + mp_raise_ValueError("Multiply failed"); + } + return mp_obj_new_bytes(out, sizeof(out)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_secp256k1_zkp_multiply_obj, + mod_trezorcrypto_secp256k1_zkp_multiply); + +STATIC const mp_rom_map_elem_t + mod_trezorcrypto_secp256k1_zkp_globals_table[] = { + {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_secp256k1_zkp)}, + {MP_ROM_QSTR(MP_QSTR_generate_secret), + MP_ROM_PTR(&mod_trezorcrypto_secp256k1_zkp_generate_secret_obj)}, + {MP_ROM_QSTR(MP_QSTR_publickey), + MP_ROM_PTR(&mod_trezorcrypto_secp256k1_zkp_publickey_obj)}, + {MP_ROM_QSTR(MP_QSTR_sign), + MP_ROM_PTR(&mod_trezorcrypto_secp256k1_zkp_sign_obj)}, + {MP_ROM_QSTR(MP_QSTR_verify), + MP_ROM_PTR(&mod_trezorcrypto_secp256k1_zkp_verify_obj)}, + {MP_ROM_QSTR(MP_QSTR_verify_recover), + MP_ROM_PTR(&mod_trezorcrypto_secp256k1_zkp_verify_recover_obj)}, + {MP_ROM_QSTR(MP_QSTR_multiply), + MP_ROM_PTR(&mod_trezorcrypto_secp256k1_zkp_multiply_obj)}, +}; +STATIC MP_DEFINE_CONST_DICT(mod_trezorcrypto_secp256k1_zkp_globals, + mod_trezorcrypto_secp256k1_zkp_globals_table); + +STATIC const mp_obj_module_t mod_trezorcrypto_secp256k1_zkp_module = { + .base = {&mp_type_module}, + .globals = (mp_obj_dict_t *)&mod_trezorcrypto_secp256k1_zkp_globals, +}; diff --git a/embed/extmod/modtrezorcrypto/modtrezorcrypto.c b/embed/extmod/modtrezorcrypto/modtrezorcrypto.c index 227ee8817..361bc6ed4 100644 --- a/embed/extmod/modtrezorcrypto/modtrezorcrypto.c +++ b/embed/extmod/modtrezorcrypto/modtrezorcrypto.c @@ -44,6 +44,7 @@ #include "modtrezorcrypto-rfc6979.h" #include "modtrezorcrypto-ripemd160.h" #include "modtrezorcrypto-secp256k1.h" +#include "modtrezorcrypto-secp256k1_zkp.h" #include "modtrezorcrypto-sha1.h" #include "modtrezorcrypto-sha256.h" #include "modtrezorcrypto-sha3-256.h" @@ -79,6 +80,8 @@ STATIC const mp_rom_map_elem_t mp_module_trezorcrypto_globals_table[] = { MP_ROM_PTR(&mod_trezorcrypto_Ripemd160_type)}, {MP_ROM_QSTR(MP_QSTR_secp256k1), MP_ROM_PTR(&mod_trezorcrypto_secp256k1_module)}, + {MP_ROM_QSTR(MP_QSTR_secp256k1_zkp), + MP_ROM_PTR(&mod_trezorcrypto_secp256k1_zkp_module)}, {MP_ROM_QSTR(MP_QSTR_sha1), MP_ROM_PTR(&mod_trezorcrypto_Sha1_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)}, diff --git a/embed/firmware/memory_T.ld b/embed/firmware/memory_T.ld index e253c8ea3..7ca3c0ddc 100644 --- a/embed/firmware/memory_T.ld +++ b/embed/firmware/memory_T.ld @@ -44,6 +44,7 @@ SECTIONS { .flash2 : ALIGN(512) { build/firmware/frozen_mpy.o(.rodata*); + build/firmware/vendor/secp256k1-zkp/src/secp256k1.o(.rodata*); . = ALIGN(512); } >FLASH2 AT>FLASH2 diff --git a/src/trezor/crypto/curve.py b/src/trezor/crypto/curve.py index e5d0aa4e8..98d093ea5 100644 --- a/src/trezor/crypto/curve.py +++ b/src/trezor/crypto/curve.py @@ -1 +1,7 @@ -from trezorcrypto import curve25519, ed25519, nist256p1, secp256k1 # noqa: F401 +from trezorcrypto import ( # noqa: F401 + curve25519, + ed25519, + nist256p1, + secp256k1, + secp256k1_zkp, +) diff --git a/tests/test_trezor.crypto.curve.secp256k1.py b/tests/test_trezor.crypto.curve.secp256k1.py index bf0115e1d..2aec3be35 100644 --- a/tests/test_trezor.crypto.curve.secp256k1.py +++ b/tests/test_trezor.crypto.curve.secp256k1.py @@ -1,10 +1,10 @@ from common import * from trezor.crypto import random -from trezor.crypto.curve import secp256k1 +from trezor.crypto.curve import secp256k1, secp256k1_zkp - -class TestCryptoSecp256k1(unittest.TestCase): +class Secp256k1Common(object): + impl = None # vectors from https://crypto.stackexchange.com/questions/784/are-there-any-secp256k1-ecdsa-test-examples-available vectors = [ @@ -57,7 +57,7 @@ class TestCryptoSecp256k1(unittest.TestCase): def test_generate_secret(self): for _ in range(100): - sk = secp256k1.generate_secret() + sk = self.impl.generate_secret() self.assertTrue(len(sk) == 32) self.assertTrue(sk != b'\x00' * 32) self.assertTrue(sk < b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xBA\xAE\xDC\xE6\xAF\x48\xA0\x3B\xBF\xD2\x5E\x8C\xD0\x36\x41\x41') @@ -68,48 +68,78 @@ def test_publickey(self): if len(sk) < 64: sk = '0' * (64 - len(sk)) + sk pk = pk.lower() - pk65 = hexlify(secp256k1.publickey(unhexlify(sk), False)).decode() # uncompressed + pk65 = hexlify(self.impl.publickey(unhexlify(sk), False)).decode() # uncompressed self.assertEqual(str(pk65), '04' + pk) - pk33 = hexlify(secp256k1.publickey(unhexlify(sk))).decode() + pk33 = hexlify(self.impl.publickey(unhexlify(sk))).decode() if pk[-1] in '02468ace': self.assertEqual(pk33, '02' + pk[:64]) else: self.assertEqual(pk33, '03' + pk[:64]) def test_sign_verify_min_max(self): - sk = secp256k1.generate_secret() - pk = secp256k1.publickey(sk) + sk = self.impl.generate_secret() + pk = self.impl.publickey(sk) dig = bytes([1] + [0] * 31) - sig = secp256k1.sign(sk, dig) - self.assertTrue(secp256k1.verify(pk, sig, dig)) + sig = self.impl.sign(sk, dig) + self.assertTrue(self.impl.verify(pk, sig, dig)) dig = bytes([0] * 31 + [1]) - sig = secp256k1.sign(sk, dig) - self.assertTrue(secp256k1.verify(pk, sig, dig)) + sig = self.impl.sign(sk, dig) + self.assertTrue(self.impl.verify(pk, sig, dig)) dig = bytes([0xFF] * 32) - sig = secp256k1.sign(sk, dig) - self.assertTrue(secp256k1.verify(pk, sig, dig)) + sig = self.impl.sign(sk, dig) + self.assertTrue(self.impl.verify(pk, sig, dig)) def test_sign_verify_random(self): for _ in range(100): - sk = secp256k1.generate_secret() - pk = secp256k1.publickey(sk) + sk = self.impl.generate_secret() + pk = self.impl.publickey(sk) dig = random.bytes(32) - sig = secp256k1.sign(sk, dig) - self.assertTrue(secp256k1.verify(pk, sig, dig)) + sig = self.impl.sign(sk, dig) + self.assertTrue(self.impl.verify(pk, sig, dig)) def test_verify_recover(self): for compressed in [False, True]: for _ in range(100): - sk = secp256k1.generate_secret() - pk = secp256k1.publickey(sk, compressed) + sk = self.impl.generate_secret() + pk = self.impl.publickey(sk, compressed) dig = random.bytes(32) - sig = secp256k1.sign(sk, dig, compressed) - pk2 = secp256k1.verify_recover(sig, dig) + sig = self.impl.sign(sk, dig, compressed) + pk2 = self.impl.verify_recover(sig, dig) self.assertEqual(pk, pk2) + def test_ecdh(self): + for _ in range(100): + sk1 = self.impl.generate_secret() + pk1 = self.impl.publickey(sk1, False) + sk2 = self.impl.generate_secret() + pk2 = self.impl.publickey(sk2, True) + self.assertEqual(self.impl.multiply(sk1, pk2), self.impl.multiply(sk2, pk1)) + + (sk, pk) = self.vectors[0] + sk = hex(sk)[2:] + if len(sk) < 64: + sk = '0' * (64 - len(sk)) + sk + sk = unhexlify(sk) + pk = pk.lower() + pk33 = self.impl.publickey(sk) + pk65 = self.impl.publickey(sk, False) + + fixed_vector_hex = b"0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8" + fixed_vector1 = self.impl.multiply(sk, pk65) + fixed_vector2 = self.impl.multiply(sk, pk33) + self.assertEqual(fixed_vector1, fixed_vector2) + self.assertEqual(hexlify(fixed_vector1), fixed_vector_hex) + +class TestCryptoSecp256k1(Secp256k1Common, unittest.TestCase): + def __init__(self): + self.impl = secp256k1 + +class TestCryptoSecp256k1Zkp(Secp256k1Common, unittest.TestCase): + def __init__(self): + self.impl = secp256k1_zkp if __name__ == '__main__': unittest.main() diff --git a/vendor/secp256k1-zkp b/vendor/secp256k1-zkp new file mode 160000 index 000000000..93b157341 --- /dev/null +++ b/vendor/secp256k1-zkp @@ -0,0 +1 @@ +Subproject commit 93b157341bd73708592b832ba4c1ec4535d5efc8