mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-26 16:18:22 +00:00
feat(core): CoSi collective signatures
This commit is contained in:
parent
701d75d6d8
commit
b12de5d861
1
core/.changelog.d/450.added
Normal file
1
core/.changelog.d/450.added
Normal file
@ -0,0 +1 @@
|
||||
CoSi collective signatures on Model T.
|
@ -341,6 +341,8 @@ apps.misc
|
||||
import apps.misc
|
||||
apps.misc.cipher_key_value
|
||||
import apps.misc.cipher_key_value
|
||||
apps.misc.cosi_commit
|
||||
import apps.misc.cosi_commit
|
||||
apps.misc.get_ecdh_session_key
|
||||
import apps.misc.get_ecdh_session_key
|
||||
apps.misc.get_entropy
|
||||
|
@ -1,6 +1,6 @@
|
||||
from micropython import const
|
||||
|
||||
from apps.common.paths import HARDENED, PathSchema
|
||||
from apps.common.paths import HARDENED, PathSchema, unharden # noqa: F401
|
||||
|
||||
_SLIP44_ID = const(1815)
|
||||
|
||||
@ -28,7 +28,3 @@ CHANGE_OUTPUT_STAKING_PATH_NAME = "Change output staking path"
|
||||
CERTIFICATE_PATH_NAME = "Certificate path"
|
||||
POOL_OWNER_STAKING_PATH_NAME = "Pool owner staking path"
|
||||
WITNESS_PATH_NAME = "Witness path"
|
||||
|
||||
|
||||
def unharden(item: int) -> int:
|
||||
return item ^ (item & HARDENED)
|
||||
|
@ -290,9 +290,6 @@ class PathSchema:
|
||||
components = ["m"]
|
||||
append = components.append # local_cache_attribute
|
||||
|
||||
def unharden(item: int) -> int:
|
||||
return item ^ (item & HARDENED)
|
||||
|
||||
for component in self.schema:
|
||||
if isinstance(component, Interval):
|
||||
a, b = component.min, component.max
|
||||
@ -378,3 +375,7 @@ def address_n_to_str(address_n: Iterable[int]) -> str:
|
||||
return "m"
|
||||
|
||||
return "m/" + "/".join(_path_item(i) for i in address_n)
|
||||
|
||||
|
||||
def unharden(item: int) -> int:
|
||||
return item ^ (item & HARDENED)
|
||||
|
73
core/src/apps/misc/cosi_commit.py
Normal file
73
core/src/apps/misc/cosi_commit.py
Normal file
@ -0,0 +1,73 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from trezor.enums import ButtonRequestType
|
||||
from trezor.messages import CosiCommitment, CosiSign, CosiSignature
|
||||
from trezor.wire import DataError
|
||||
|
||||
from apps.common.paths import PathSchema, unharden
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import CosiCommit
|
||||
from trezor.wire import Context
|
||||
|
||||
# This module implements the cosigner part of the CoSi collective signatures
|
||||
# as described in https://dedis.cs.yale.edu/dissent/papers/witness.pdf
|
||||
|
||||
|
||||
SCHEMA_SLIP18 = PathSchema.parse("m/10018'/address_index'/*'", slip44_id=())
|
||||
# SLIP-26: m/10026'/model'/type'/rotation_index'
|
||||
# - `model`: ASCII for 1, T, or R, or 0 for common things (keep the ASCII range open for future models).
|
||||
# - `type`: 0 = bootloader, 1 = vendorheader, 2 = firmware, 3 = definitions, 4 = reserved
|
||||
# - `rotation_index`: a fixed 0' for now
|
||||
SCHEMA_SLIP26 = PathSchema.parse("m/10026'/[0-127]'/[0-4]'/0'", slip44_id=())
|
||||
|
||||
|
||||
async def cosi_commit(ctx: Context, msg: CosiCommit) -> CosiSignature:
|
||||
import storage.cache as storage_cache
|
||||
from trezor.crypto.curve import ed25519
|
||||
from trezor.ui.layouts import confirm_blob
|
||||
from apps.common import paths
|
||||
from apps.common.keychain import get_keychain
|
||||
|
||||
keychain = await get_keychain(ctx, "ed25519", [SCHEMA_SLIP18, SCHEMA_SLIP26])
|
||||
await paths.validate_path(ctx, keychain, msg.address_n)
|
||||
|
||||
node = keychain.derive(msg.address_n)
|
||||
seckey = node.private_key()
|
||||
pubkey = ed25519.publickey(seckey)
|
||||
|
||||
if not storage_cache.is_set(storage_cache.APP_MISC_COSI_COMMITMENT):
|
||||
nonce, commitment = ed25519.cosi_commit()
|
||||
storage_cache.set(storage_cache.APP_MISC_COSI_NONCE, nonce)
|
||||
storage_cache.set(storage_cache.APP_MISC_COSI_COMMITMENT, commitment)
|
||||
commitment = storage_cache.get(storage_cache.APP_MISC_COSI_COMMITMENT)
|
||||
if commitment is None:
|
||||
raise RuntimeError
|
||||
|
||||
sign_msg = await ctx.call(
|
||||
CosiCommitment(commitment=commitment, pubkey=pubkey), CosiSign
|
||||
)
|
||||
|
||||
if sign_msg.address_n != msg.address_n:
|
||||
raise DataError("Mismatched address_n")
|
||||
|
||||
title = "CoSi sign message"
|
||||
if SCHEMA_SLIP18.match(sign_msg.address_n):
|
||||
index = unharden(msg.address_n[1])
|
||||
title = f"CoSi sign index {index}"
|
||||
|
||||
await confirm_blob(
|
||||
ctx, "cosi_sign", title, sign_msg.data, br_code=ButtonRequestType.ProtectCall
|
||||
)
|
||||
|
||||
# clear nonce from cache
|
||||
nonce = storage_cache.get(storage_cache.APP_MISC_COSI_NONCE)
|
||||
storage_cache.delete(storage_cache.APP_MISC_COSI_COMMITMENT)
|
||||
storage_cache.delete(storage_cache.APP_MISC_COSI_NONCE)
|
||||
if nonce is None:
|
||||
raise RuntimeError
|
||||
|
||||
signature = ed25519.cosi_sign(
|
||||
seckey, sign_msg.data, nonce, sign_msg.global_commitment, sign_msg.global_pubkey
|
||||
)
|
||||
return CosiSignature(signature=signature)
|
@ -84,6 +84,8 @@ def _find_message_handler_module(msg_type: int) -> str:
|
||||
return "apps.misc.cipher_key_value"
|
||||
if msg_type == MessageType.GetFirmwareHash:
|
||||
return "apps.misc.get_firmware_hash"
|
||||
if msg_type == MessageType.CosiCommit:
|
||||
return "apps.misc.cosi_commit"
|
||||
|
||||
if not utils.BITCOIN_ONLY:
|
||||
if msg_type == MessageType.SetU2FCounter:
|
||||
|
@ -31,6 +31,8 @@ APP_COMMON_SAFETY_CHECKS_TEMPORARY = const(1 | _SESSIONLESS_FLAG)
|
||||
STORAGE_DEVICE_EXPERIMENTAL_FEATURES = const(2 | _SESSIONLESS_FLAG)
|
||||
APP_COMMON_REQUEST_PIN_LAST_UNLOCK = const(3 | _SESSIONLESS_FLAG)
|
||||
APP_COMMON_BUSY_DEADLINE_MS = const(4 | _SESSIONLESS_FLAG)
|
||||
APP_MISC_COSI_NONCE = const(5 | _SESSIONLESS_FLAG)
|
||||
APP_MISC_COSI_COMMITMENT = const(6 | _SESSIONLESS_FLAG)
|
||||
|
||||
|
||||
# === Homescreen storage ===
|
||||
@ -137,6 +139,8 @@ class SessionlessCache(DataCache):
|
||||
1, # STORAGE_DEVICE_EXPERIMENTAL_FEATURES
|
||||
8, # APP_COMMON_REQUEST_PIN_LAST_UNLOCK
|
||||
8, # APP_COMMON_BUSY_DEADLINE_MS
|
||||
32, # APP_MISC_COSI_NONCE
|
||||
32, # APP_MISC_COSI_COMMITMENT
|
||||
)
|
||||
super().__init__()
|
||||
|
||||
|
@ -20,10 +20,9 @@ import pytest
|
||||
|
||||
from trezorlib import cosi
|
||||
from trezorlib.debuglink import TrezorClientDebugLink as Client
|
||||
from trezorlib.exceptions import TrezorFailure
|
||||
from trezorlib.tools import parse_path
|
||||
|
||||
pytestmark = pytest.mark.skip_t2
|
||||
|
||||
DIGEST = sha256(b"this is not a pipe").digest()
|
||||
|
||||
|
||||
@ -108,3 +107,12 @@ def test_cosi_sign3(client: Client):
|
||||
)
|
||||
|
||||
cosi.verify_combined(signature, DIGEST, global_pk)
|
||||
|
||||
|
||||
@pytest.mark.skip_t1
|
||||
def test_cosi_different_key(client: Client):
|
||||
with pytest.raises(TrezorFailure):
|
||||
commit = cosi.commit(client, parse_path("m/10018h/0h"))
|
||||
cosi.sign(
|
||||
client, parse_path("m/10018h/1h"), DIGEST, commit.commitment, commit.pubkey
|
||||
)
|
||||
|
@ -1408,6 +1408,12 @@
|
||||
"TT_ethereum-test_signtx.py::test_signtx_eip1559[unknown_erc20]": "2189a082fe612b0821072ab5260aca76e77dfeccb592c013409f1ef7812c8226",
|
||||
"TT_ethereum-test_signtx.py::test_signtx_eip1559_access_list": "14f159d0b62d7697bcbcd56ed41b8affa86ffea1d7aa6f2d0c3ce168fb990431",
|
||||
"TT_ethereum-test_signtx.py::test_signtx_eip1559_access_list_larger": "14f159d0b62d7697bcbcd56ed41b8affa86ffea1d7aa6f2d0c3ce168fb990431",
|
||||
"TT_misc-test_cosi.py::test_cosi_different_key": "f03b50df7f4a161078fa903c44f37272961b70358d4014d30a12888e1fd2caf1",
|
||||
"TT_misc-test_cosi.py::test_cosi_nonce": "c668c0d63bbf9875ea2c3d4073e9507fc89719a19ded85e6a64ecc03e8b0287d",
|
||||
"TT_misc-test_cosi.py::test_cosi_pubkey": "f03b50df7f4a161078fa903c44f37272961b70358d4014d30a12888e1fd2caf1",
|
||||
"TT_misc-test_cosi.py::test_cosi_sign1": "c668c0d63bbf9875ea2c3d4073e9507fc89719a19ded85e6a64ecc03e8b0287d",
|
||||
"TT_misc-test_cosi.py::test_cosi_sign2": "aea78f19619a1aac8072b41e1dfdade53ca3496af5f27801a9a929a0d65ecce6",
|
||||
"TT_misc-test_cosi.py::test_cosi_sign3": "efeed01748ee5797c446294fba491d819b3ea60f4238782154cc608558960c3d",
|
||||
"TT_misc-test_msg_cipherkeyvalue.py::test_decrypt": "120f9e8e4cb99d8fbd4fe5f4ce5d6a24e7aa98fafb2329a0fde01b6fa6656361",
|
||||
"TT_misc-test_msg_cipherkeyvalue.py::test_decrypt_badlen": "f03b50df7f4a161078fa903c44f37272961b70358d4014d30a12888e1fd2caf1",
|
||||
"TT_misc-test_msg_cipherkeyvalue.py::test_encrypt": "582b31d707b118bda01c9bd6ffab3b0a8d1ea6fa68583aa9b3032cd7921ae2c3",
|
||||
|
Loading…
Reference in New Issue
Block a user