Merge branch 'release/22.08'

pull/2445/head
Pavol Rusnak 2 years ago
commit c962d3520b
No known key found for this signature in database
GPG Key ID: 91F3B339B9A02A3D

@ -97,8 +97,8 @@ message ECDHSessionKey {
* @next Failure
*/
message CosiCommit {
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
optional bytes data = 2; // Data to be signed
repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
optional bytes data = 2 [deprecated=true]; // Data to be signed. Deprecated in 1.10.2, the field is not needed, since CoSi commitments are no longer deterministic.
}
/**

@ -1 +0,0 @@
Remove power-down power-up cycle from touch controller initialization in firmware

@ -1 +0,0 @@
Add model R emulator

@ -1 +0,0 @@
Add support for Monero HF15 features.

@ -1 +0,0 @@
Add basic Trezor Model R hardware support

@ -1 +0,0 @@
Show the fee rate on the singing confirmation screen.

@ -1 +0,0 @@
Updated secp256k1-zkp.

@ -1 +0,0 @@
Jump and stay in bootloader from firmware through SVC call reverse trampoline.

@ -1 +0,0 @@
Expose raw pixel access to Rust

@ -1 +0,0 @@
Add RGB LED for Model R

@ -1 +0,0 @@
Cardano internal refactors

@ -1 +0,0 @@
Boardloader capabilities structure

@ -1 +0,0 @@
Support for Cardano Babbage era transaction items

@ -1,2 +0,0 @@
Allow Cardano's `required_signers` in ordinary and multisig transactions
Allow Cardano's `datum_hash` in non-script outputs

@ -1 +0,0 @@
Add "Show All"/"Show Simple" choice to Cardano transaction signing

@ -1 +0,0 @@
Documentation for embedded C+Rust debugging

@ -1 +0,0 @@
Show thousands separator when displaying large amounts.

@ -1 +0,0 @@
Ensure correct order when verifying external inputs in Bitcoin signing.

@ -1 +0,0 @@
Fix Decred transaction weight calculation.

@ -1 +0,0 @@
Remove firmware dumping capability.

@ -1 +0,0 @@
Refactor and cleanup of Monero code.

@ -1 +0,0 @@
Removed support for obsolete Monero hardfork 12 and below

@ -1 +0,0 @@
_(Emulator)_ Emulator window will always react to shutdown events, even while waiting for USB packets.

@ -4,6 +4,39 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [2.5.2] (17th August 2022)
### Added
- Add model R emulator [#2230]
- Add support for Monero HF15 features. [#2232]
- Add basic Trezor Model R hardware support [#2243]
- Show the fee rate on the signing confirmation screen. [#2249]
- Jump and stay in bootloader from firmware through SVC call reverse trampoline. [#2284]
- Expose raw pixel access to Rust [#2297]
- Add RGB LED for Model R [#2300]
- Boardloader capabilities structure [#2324]
- Support for Cardano Babbage era transaction items [#2354]
- Add "Show All"/"Show Simple" choice to Cardano transaction signing [#2355]
- Documentation for embedded C+Rust debugging [#2380]
- Show thousands separator when displaying large amounts. [#2394]
### Changed
- Refactor and cleanup of Monero code. [#642]
- Remove power-down power-up cycle from touch controller initialization in firmware [#2130]
- Updated secp256k1-zkp. [#2261]
- Cardano internal refactors [#2313]
- Allow Cardano's `required_signers` in ordinary and multisig transactions
Allow Cardano's `datum_hash` in non-script outputs [#2354]
### Removed
- Removed support for obsolete Monero hardfork 12 and below [#642]
- Remove firmware dumping capability. [#2433]
### Fixed
- _(Emulator)_ Emulator window will always react to shutdown events, even while waiting for USB packets. [#973]
- Ensure correct order when verifying external inputs in Bitcoin signing. [#2415]
- Fix Decred transaction weight calculation. [#2422]
## 2.5.1 [18th May 2022]
@ -483,10 +516,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
[#24]: https://github.com/trezor/trezor-firmware/pull/24
[#379]: https://github.com/trezor/trezor-firmware/pull/379
[#642]: https://github.com/trezor/trezor-firmware/pull/642
[#741]: https://github.com/trezor/trezor-firmware/pull/741
[#800]: https://github.com/trezor/trezor-firmware/pull/800
[#948]: https://github.com/trezor/trezor-firmware/pull/948
[#958]: https://github.com/trezor/trezor-firmware/pull/958
[#973]: https://github.com/trezor/trezor-firmware/pull/973
[#982]: https://github.com/trezor/trezor-firmware/pull/982
[#1018]: https://github.com/trezor/trezor-firmware/pull/1018
[#1027]: https://github.com/trezor/trezor-firmware/pull/1027
@ -594,9 +629,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
[#2077]: https://github.com/trezor/trezor-firmware/pull/2077
[#2100]: https://github.com/trezor/trezor-firmware/pull/2100
[#2114]: https://github.com/trezor/trezor-firmware/pull/2114
[#2130]: https://github.com/trezor/trezor-firmware/pull/2130
[#2135]: https://github.com/trezor/trezor-firmware/pull/2135
[#2144]: https://github.com/trezor/trezor-firmware/pull/2144
[#2166]: https://github.com/trezor/trezor-firmware/pull/2166
[#2167]: https://github.com/trezor/trezor-firmware/pull/2167
[#2181]: https://github.com/trezor/trezor-firmware/pull/2181
[#2230]: https://github.com/trezor/trezor-firmware/pull/2230
[#2232]: https://github.com/trezor/trezor-firmware/pull/2232
[#2239]: https://github.com/trezor/trezor-firmware/pull/2239
[#2243]: https://github.com/trezor/trezor-firmware/pull/2243
[#2249]: https://github.com/trezor/trezor-firmware/pull/2249
[#2261]: https://github.com/trezor/trezor-firmware/pull/2261
[#2284]: https://github.com/trezor/trezor-firmware/pull/2284
[#2297]: https://github.com/trezor/trezor-firmware/pull/2297
[#2300]: https://github.com/trezor/trezor-firmware/pull/2300
[#2313]: https://github.com/trezor/trezor-firmware/pull/2313
[#2324]: https://github.com/trezor/trezor-firmware/pull/2324
[#2354]: https://github.com/trezor/trezor-firmware/pull/2354
[#2355]: https://github.com/trezor/trezor-firmware/pull/2355
[#2380]: https://github.com/trezor/trezor-firmware/pull/2380
[#2394]: https://github.com/trezor/trezor-firmware/pull/2394
[#2415]: https://github.com/trezor/trezor-firmware/pull/2415
[#2422]: https://github.com/trezor/trezor-firmware/pull/2422
[#2433]: https://github.com/trezor/trezor-firmware/pull/2433

@ -240,6 +240,25 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(
mod_trezorcrypto_ed25519_cosi_combine_signatures_obj,
mod_trezorcrypto_ed25519_cosi_combine_signatures);
/// def cosi_commit() -> tuple[bytes, bytes]:
/// """
/// Generate a nonce and commitment for the CoSi cosigning scheme.
/// """
STATIC mp_obj_t mod_trezorcrypto_ed25519_cosi_commit() {
vstr_t nonce = {0};
vstr_t commitment = {0};
vstr_init_len(&nonce, 32);
vstr_init_len(&commitment, 32);
ed25519_cosi_commit(*(ed25519_secret_key *)nonce.buf,
*(ed25519_public_key *)commitment.buf);
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
tuple->items[0] = mp_obj_new_str_from_vstr(&mp_type_bytes, &nonce);
tuple->items[1] = mp_obj_new_str_from_vstr(&mp_type_bytes, &commitment);
return MP_OBJ_FROM_PTR(tuple);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorcrypto_ed25519_cosi_commit_obj,
mod_trezorcrypto_ed25519_cosi_commit);
/// def cosi_sign(
/// secret_key: bytes,
/// message: bytes,
@ -272,12 +291,15 @@ STATIC mp_obj_t mod_trezorcrypto_ed25519_cosi_sign(size_t n_args,
}
vstr_t sig = {0};
vstr_init_len(&sig, sizeof(ed25519_cosi_signature));
;
ed25519_cosi_sign(msg.buf, msg.len, *(const ed25519_secret_key *)sk.buf,
*(const ed25519_secret_key *)nonce.buf,
*(const ed25519_public_key *)sigR.buf,
*(const ed25519_secret_key *)pk.buf,
*(ed25519_cosi_signature *)sig.buf);
if (0 != ed25519_cosi_sign(msg.buf, msg.len,
*(const ed25519_secret_key *)sk.buf,
*(const ed25519_secret_key *)nonce.buf,
*(const ed25519_public_key *)sigR.buf,
*(const ed25519_secret_key *)pk.buf,
*(ed25519_cosi_signature *)sig.buf)) {
vstr_clear(&sig);
mp_raise_ValueError("Signing failed");
}
return mp_obj_new_str_from_vstr(&mp_type_bytes, &sig);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(
@ -301,6 +323,8 @@ STATIC const mp_rom_map_elem_t mod_trezorcrypto_ed25519_globals_table[] = {
MP_ROM_PTR(&mod_trezorcrypto_ed25519_cosi_combine_publickeys_obj)},
{MP_ROM_QSTR(MP_QSTR_cosi_combine_signatures),
MP_ROM_PTR(&mod_trezorcrypto_ed25519_cosi_combine_signatures_obj)},
{MP_ROM_QSTR(MP_QSTR_cosi_commit),
MP_ROM_PTR(&mod_trezorcrypto_ed25519_cosi_commit_obj)},
{MP_ROM_QSTR(MP_QSTR_cosi_sign),
MP_ROM_PTR(&mod_trezorcrypto_ed25519_cosi_sign_obj)},
};

@ -53,6 +53,13 @@ def cosi_combine_signatures(R: bytes, signatures: list[bytes]) -> bytes:
"""
# extmod/modtrezorcrypto/modtrezorcrypto-ed25519.h
def cosi_commit() -> tuple[bytes, bytes]:
"""
Generate a nonce and commitment for the CoSi cosigning scheme.
"""
# extmod/modtrezorcrypto/modtrezorcrypto-ed25519.h
def cosi_sign(
secret_key: bytes,

@ -1928,13 +1928,11 @@ if TYPE_CHECKING:
class CosiCommit(protobuf.MessageType):
address_n: "list[int]"
data: "bytes | None"
def __init__(
self,
*,
address_n: "list[int] | None" = None,
data: "bytes | None" = None,
) -> None:
pass

@ -26,8 +26,7 @@ class TestCryptoEd25519Cosi(unittest.TestCase):
nonces = [None] * N
Rs = [None] * N
for j in range(N):
nonces[j] = ed25519.generate_secret()
Rs[j] = ed25519.publickey(nonces[j])
nonces[j], Rs[j] = ed25519.cosi_commit()
R = ed25519.cosi_combine_publickeys(Rs)

@ -18,6 +18,7 @@
#include "ed25519.h"
#include "ed25519-hash-custom.h"
#include "rand.h"
#include "memzero.h"
/*
@ -50,16 +51,34 @@ ED25519_FN(ed25519_publickey) (const ed25519_secret_key sk, ed25519_public_key p
}
void
ED25519_FN(ed25519_cosi_commit) (ed25519_secret_key nonce, ed25519_public_key commitment) {
bignum256modm r = {0};
ge25519 ALIGN(16) R;
unsigned char extnonce[64] = {0};
/* r = random512 mod L */
random_buffer(extnonce, sizeof(extnonce));
expand256_modm(r, extnonce, sizeof(extnonce));
memzero(&extnonce, sizeof(extnonce));
contract256_modm(nonce, r);
/* R = rB */
ge25519_scalarmult_base_niels(&R, ge25519_niels_base_multiples, r);
memzero(&r, sizeof(r));
ge25519_pack(commitment, &R);
}
int
ED25519_FN(ed25519_cosi_sign) (const unsigned char *m, size_t mlen, const ed25519_secret_key sk, const ed25519_secret_key nonce, const ed25519_public_key R, const ed25519_public_key pk, ed25519_cosi_signature sig) {
bignum256modm r = {0}, S = {0}, a = {0};
hash_512bits extsk = {0}, extnonce = {0}, hram = {0};
hash_512bits extsk = {0}, hram = {0};
ed25519_extsk(extsk, sk);
ed25519_extsk(extnonce, nonce);
/* r = nonce */
expand256_modm(r, extnonce, 32);
memzero(&extnonce, sizeof(extnonce));
/* r */
expand_raw256_modm(r, nonce);
if (!is_reduced256_modm(r))
return -1;
/* S = H(R,A,m).. */
ed25519_hram(hram, R, pk, m, mlen);
@ -77,6 +96,8 @@ ED25519_FN(ed25519_cosi_sign) (const unsigned char *m, size_t mlen, const ed2551
/* S = (r + H(R,A,m)a) mod L */
contract256_modm(sig, S);
return 0;
}
void
@ -155,7 +176,7 @@ ED25519_FN(ed25519_sign_open) (const unsigned char *m, size_t mlen, const ed2551
/* S */
expand_raw256_modm(S, RS + 32);
if (!is_reduced256_modm(S))
return -1;
return -1;
/* SB - H(R,A,m)A */
ge25519_double_scalarmult_vartime(&R, &A, hram, S);

@ -35,7 +35,8 @@ void curve25519_scalarmult_basepoint(curve25519_key mypublic, const curve25519_k
int ed25519_cosi_combine_publickeys(ed25519_public_key res, CONST ed25519_public_key *pks, size_t n);
void ed25519_cosi_combine_signatures(ed25519_signature res, const ed25519_public_key R, CONST ed25519_cosi_signature *sigs, size_t n);
void ed25519_cosi_sign(const unsigned char *m, size_t mlen, const ed25519_secret_key key, const ed25519_secret_key nonce, const ed25519_public_key R, const ed25519_public_key pk, ed25519_cosi_signature sig);
void ed25519_cosi_commit(ed25519_secret_key nonce, ed25519_public_key commitment);
int ed25519_cosi_sign(const unsigned char *m, size_t mlen, const ed25519_secret_key key, const ed25519_secret_key nonce, const ed25519_public_key R, const ed25519_public_key pk, ed25519_cosi_signature sig);
#if defined(__cplusplus)
}

@ -6885,8 +6885,7 @@ START_TEST(test_ed25519_cosi) {
/* phase 1: create nonces, commitments (R values) and combine commitments */
for (int j = 0; j < N; j++) {
generate_rfc6979(nonces[j], &rng);
ed25519_publickey(nonces[j], Rs[j]);
ed25519_cosi_commit(nonces[j], Rs[j]);
}
res = ed25519_cosi_combine_publickeys(R, Rs, N);
ck_assert_int_eq(res, 0);
@ -6894,7 +6893,9 @@ START_TEST(test_ed25519_cosi) {
MARK_SECRET_DATA(keys, sizeof(keys));
/* phase 2: sign and combine signatures */
for (int j = 0; j < N; j++) {
ed25519_cosi_sign(msg, sizeof(msg), keys[j], nonces[j], R, pk, sigs[j]);
res = ed25519_cosi_sign(msg, sizeof(msg), keys[j], nonces[j], R, pk,
sigs[j]);
ck_assert_int_eq(res, 0);
}
UNMARK_SECRET_DATA(sigs, sizeof(sigs));

@ -1 +0,0 @@
Show the fee rate on the singing confirmation screen.

@ -1 +0,0 @@
Show thousands separator when displaying large amounts.

@ -1 +0,0 @@
Fix rounding in fee rate computation.

@ -1 +0,0 @@
Remove firmware dumping capability.

@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [1.11.2] (17th August 2022)
### Added
- Show the fee rate on the signing confirmation screen. [#2249]
- Show thousands separator when displaying large amounts. [#2394]
### Changed
- Updated secp256k1-zkp. [#2261]
### Removed
- Remove firmware dumping capability. [#2433]
### Security
- Fix potential security issues in recovery workflow.
- Fix key extraction vulnerability in Cothority Collective Signing (CoSi).
- Fix nonce bias in CoSi signing.
## 1.11.1 [18th May 2022]
@ -543,3 +560,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
[#2144]: https://github.com/trezor/trezor-firmware/pull/2144
[#2181]: https://github.com/trezor/trezor-firmware/pull/2181
[#2239]: https://github.com/trezor/trezor-firmware/pull/2239
[#2249]: https://github.com/trezor/trezor-firmware/pull/2249
[#2261]: https://github.com/trezor/trezor-firmware/pull/2261
[#2394]: https://github.com/trezor/trezor-firmware/pull/2394
[#2422]: https://github.com/trezor/trezor-firmware/pull/2422
[#2433]: https://github.com/trezor/trezor-firmware/pull/2433

@ -44,7 +44,6 @@
#include "protect.h"
#include "recovery.h"
#include "reset.h"
#include "rfc6979.h"
#include "rng.h"
#include "secp256k1.h"
#include "signing.h"

@ -17,6 +17,10 @@
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
static uint8_t cosi_nonce[32] = {0};
static uint8_t cosi_commitment[32] = {0};
static bool cosi_nonce_is_set = false;
void fsm_msgCipherKeyValue(const CipherKeyValue *msg) {
CHECK_INITIALIZED
@ -256,8 +260,6 @@ void fsm_msgCosiCommit(const CosiCommit *msg) {
CHECK_INITIALIZED
CHECK_PARAM(msg->has_data, _("No data provided"));
CHECK_PIN
if (!fsm_checkCosiPath(msg->address_n_count, msg->address_n)) {
@ -265,30 +267,21 @@ void fsm_msgCosiCommit(const CosiCommit *msg) {
return;
}
layoutCosiCommitSign(msg->address_n, msg->address_n_count, msg->data.bytes,
msg->data.size, false);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
const HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n,
msg->address_n_count, NULL);
if (!node) return;
uint8_t nonce[32];
sha256_Raw(msg->data.bytes, msg->data.size, nonce);
rfc6979_state rng;
init_rfc6979(node->private_key, nonce, NULL, &rng);
generate_rfc6979(nonce, &rng);
if (!cosi_nonce_is_set) {
ed25519_cosi_commit(cosi_nonce, cosi_commitment);
cosi_nonce_is_set = true;
}
resp->has_commitment = true;
resp->has_pubkey = true;
resp->commitment.size = 32;
resp->pubkey.size = 32;
ed25519_publickey(nonce, resp->commitment.bytes);
memcpy(resp->commitment.bytes, cosi_commitment, sizeof(cosi_commitment));
ed25519_publickey(node->private_key, resp->pubkey.bytes);
msg_write(MessageType_MessageType_CosiCommitment, resp);
@ -306,6 +299,12 @@ void fsm_msgCosiSign(const CosiSign *msg) {
CHECK_PARAM(msg->has_global_pubkey && msg->global_pubkey.size == 32,
_("Invalid global pubkey"));
if (!cosi_nonce_is_set) {
fsm_sendFailure(FailureType_Failure_ProcessError, _("CoSi nonce not set"));
layoutHome();
return;
}
if (!fsm_checkCosiPath(msg->address_n_count, msg->address_n)) {
layoutHome();
return;
@ -313,8 +312,8 @@ void fsm_msgCosiSign(const CosiSign *msg) {
CHECK_PIN
layoutCosiCommitSign(msg->address_n, msg->address_n_count, msg->data.bytes,
msg->data.size, true);
layoutCosiSign(msg->address_n, msg->address_n_count, msg->data.bytes,
msg->data.size);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
@ -325,18 +324,16 @@ void fsm_msgCosiSign(const CosiSign *msg) {
msg->address_n_count, NULL);
if (!node) return;
uint8_t nonce[32];
sha256_Raw(msg->data.bytes, msg->data.size, nonce);
rfc6979_state rng;
init_rfc6979(node->private_key, nonce, NULL, &rng);
generate_rfc6979(nonce, &rng);
resp->signature.size = 32;
cosi_nonce_is_set = false;
ed25519_cosi_sign(msg->data.bytes, msg->data.size, node->private_key, nonce,
msg->global_commitment.bytes, msg->global_pubkey.bytes,
resp->signature.bytes);
msg_write(MessageType_MessageType_CosiSignature, resp);
if (ed25519_cosi_sign(msg->data.bytes, msg->data.size, node->private_key,
cosi_nonce, msg->global_commitment.bytes,
msg->global_pubkey.bytes, resp->signature.bytes) == 0) {
msg_write(MessageType_MessageType_CosiSignature, resp);
} else {
fsm_sendFailure(FailureType_Failure_FirmwareError, NULL);
}
memzero(cosi_nonce, sizeof(cosi_nonce));
layoutHome();
}

@ -1220,18 +1220,13 @@ static inline bool is_slip18(const uint32_t *address_n,
(address_n[1] & PATH_UNHARDEN_MASK) <= 9;
}
void layoutCosiCommitSign(const uint32_t *address_n, size_t address_n_count,
const uint8_t *data, uint32_t len, bool final_sign) {
char *desc = final_sign ? _("CoSi sign message?") : _("CoSi commit message?");
void layoutCosiSign(const uint32_t *address_n, size_t address_n_count,
const uint8_t *data, uint32_t len) {
char *desc = _("CoSi sign message?");
char desc_buf[32] = {0};
if (is_slip18(address_n, address_n_count)) {
if (final_sign) {
strlcpy(desc_buf, _("CoSi sign index #?"), sizeof(desc_buf));
desc_buf[16] = '0' + (address_n[1] & PATH_UNHARDEN_MASK);
} else {
strlcpy(desc_buf, _("CoSi commit index #?"), sizeof(desc_buf));
desc_buf[18] = '0' + (address_n[1] & PATH_UNHARDEN_MASK);
}
strlcpy(desc_buf, _("CoSi sign index #?"), sizeof(desc_buf));
desc_buf[16] = '0' + (address_n[1] & PATH_UNHARDEN_MASK);
desc = desc_buf;
}
char str[4][17] = {0};

@ -105,8 +105,8 @@ void layoutNEMTransferPayload(const uint8_t *payload, size_t length,
void layoutNEMMosaicDescription(const char *description);
void layoutNEMLevy(const NEMMosaicDefinition *definition, uint8_t network);
void layoutCosiCommitSign(const uint32_t *address_n, size_t address_n_count,
const uint8_t *data, uint32_t len, bool final_sign);
void layoutCosiSign(const uint32_t *address_n, size_t address_n_count,
const uint8_t *data, uint32_t len);
void layoutConfirmAutoLockDelay(uint32_t delay_ms);
void layoutConfirmSafetyChecks(SafetyCheckLevel safety_checks_level);

@ -6,7 +6,7 @@ CipherKeyValue.iv max_size:16
CipheredKeyValue.value max_size:1024
CosiCommit.address_n max_count:8
CosiCommit.data max_size:32
CosiCommit.data type:FT_IGNORE
CosiCommitment.commitment max_size:32
CosiCommitment.pubkey max_size:32

@ -37,12 +37,12 @@
/* number of words expected in the new seed */
static uint32_t word_count;
/* recovery mode:
* 0: not recovering
* 1: recover by scrambled plain text words
* 2: recover by matrix entry
*/
static int awaiting_word = 0;
/* recovery mode */
static enum {
RECOVERY_NONE = 0, // recovery not in progress
RECOVERY_SCRAMBLED = 1, // standard recovery by scrambled plain text words
RECOVERY_MATRIX = 2, // advanced recovery by matrix entry
} recovery_mode = RECOVERY_NONE;
/* True if we should not write anything back to config
* (can be used for testing seed for correctness).
@ -145,7 +145,7 @@ static void format_number(char *dest, int number) {
static void recovery_request(void) {
WordRequest resp = {0};
memzero(&resp, sizeof(WordRequest));
if (awaiting_word == 1) {
if (recovery_mode == RECOVERY_SCRAMBLED) {
resp.type = WordRequestType_WordRequestType_Plain;
} else if (word_index % 4 == 3) {
resp.type = WordRequestType_WordRequestType_Matrix6;
@ -217,7 +217,9 @@ static void recovery_done(void) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Invalid seed, are words in correct order?"));
}
awaiting_word = 0;
memzero(words, sizeof(words));
word_pincode = 0;
recovery_mode = RECOVERY_NONE;
layoutHome();
}
@ -476,6 +478,9 @@ void recovery_init(uint32_t _word_count, bool passphrase_protection,
bool _dry_run) {
if (_word_count != 12 && _word_count != 18 && _word_count != 24) return;
recovery_mode = RECOVERY_NONE;
word_pincode = 0;
word_index = 0;
word_count = _word_count;
enforce_wordlist = _enforce_wordlist;
dry_run = _dry_run;
@ -504,10 +509,9 @@ void recovery_init(uint32_t _word_count, bool passphrase_protection,
config_setU2FCounter(u2f_counter);
}
// Prefer matrix recovery if the host supports it.
if ((type & RecoveryDeviceType_RecoveryDeviceType_Matrix) != 0) {
awaiting_word = 2;
word_index = 0;
word_pincode = 0;
recovery_mode = RECOVERY_MATRIX;
next_matrix();
} else {
for (uint32_t i = 0; i < word_count; i++) {
@ -517,8 +521,7 @@ void recovery_init(uint32_t _word_count, bool passphrase_protection,
word_order[i] = 0;
}
random_permute(word_order, 24);
awaiting_word = 1;
word_index = 0;
recovery_mode = RECOVERY_SCRAMBLED;
next_word();
}
}
@ -527,29 +530,18 @@ static void recovery_scrambledword(const char *word) {
int index = -1;
if (enforce_wordlist) { // check if word is valid
index = mnemonic_find_word(word);
}
if (word_pos == 0) { // fake word
if (strcmp(word, fake_word) != 0) {
if (index < 0) { // not found
if (!dry_run) {
session_clear(true);
}
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Wrong word retyped"));
layoutHome();
fsm_sendFailure(FailureType_Failure_DataError,
_("Word not found in a wordlist"));
recovery_abort();
return;
}
} else { // real word
if (enforce_wordlist) {
if (index < 0) { // not found
if (!dry_run) {
session_clear(true);
}
fsm_sendFailure(FailureType_Failure_DataError,
_("Word not found in a wordlist"));
layoutHome();
return;
}
}
}
if (word_pos != 0) { // ignore fake words
strlcpy(words[word_pos - 1], word, sizeof(words[word_pos - 1]));
}
@ -565,11 +557,11 @@ static void recovery_scrambledword(const char *word) {
* for scrambled recovery.
*/
void recovery_word(const char *word) {
switch (awaiting_word) {
case 2:
switch (recovery_mode) {
case RECOVERY_MATRIX:
recovery_digit(word[0]);
break;
case 1:
case RECOVERY_SCRAMBLED:
recovery_scrambledword(word);
break;
default:
@ -582,9 +574,11 @@ void recovery_word(const char *word) {
/* Abort recovery.
*/
void recovery_abort(void) {
if (awaiting_word) {
memzero(words, sizeof(words));
word_pincode = 0;
if (recovery_mode != RECOVERY_NONE) {
layoutHome();
awaiting_word = 0;
recovery_mode = RECOVERY_NONE;
}
}

@ -0,0 +1 @@
Remove DATA parameter from trezorctl cosi commit.

@ -14,7 +14,8 @@
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
from typing import TYPE_CHECKING
import warnings
from typing import TYPE_CHECKING, Optional
import click
@ -35,14 +36,17 @@ def cli() -> None:
@cli.command()
@click.option("-n", "--address", required=True, help=PATH_HELP)
@click.argument("data")
@click.argument("data_deprecated", required=False)
@with_client
def commit(
client: "TrezorClient", address: str, data: str
client: "TrezorClient", address: str, data_deprecated: Optional[str]
) -> "messages.CosiCommitment":
"""Ask device to commit to CoSi signing."""
if data_deprecated is not None:
warnings.warn("'data' argument is deprecated")
address_n = tools.parse_path(address)
return cosi.commit(client, address_n, bytes.fromhex(data))
return cosi.commit(client, address_n)
@cli.command()

@ -14,8 +14,9 @@
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
import warnings
from functools import reduce
from typing import TYPE_CHECKING, Iterable, List, Tuple
from typing import TYPE_CHECKING, Iterable, List, Optional, Tuple
from . import _ed25519, messages
from .tools import expect
@ -141,8 +142,17 @@ def sign_with_privkey(
@expect(messages.CosiCommitment)
def commit(client: "TrezorClient", n: "Address", data: bytes) -> "MessageType":
return client.call(messages.CosiCommit(address_n=n, data=data))
def commit(
client: "TrezorClient", n: "Address", data: Optional[bytes] = None
) -> "MessageType":
if data is not None:
warnings.warn(
"'data' argument is deprecated",
DeprecationWarning,
stacklevel=2,
)
return client.call(messages.CosiCommit(address_n=n))
@expect(messages.CosiSignature)

@ -24,75 +24,87 @@ from trezorlib.tools import parse_path
pytestmark = pytest.mark.skip_t2
DIGEST = sha256(b"this is not a pipe").digest()
def test_cosi_commit(client: Client):
digest = sha256(b"this is a message").digest()
c0 = cosi.commit(client, parse_path("m/10018h/0h"), digest)
c1 = cosi.commit(client, parse_path("m/10018h/1h"), digest)
c2 = cosi.commit(client, parse_path("m/10018h/2h"), digest)
def test_cosi_pubkey(client: Client):
c0 = cosi.commit(client, parse_path("m/10018h/0h"))
c1 = cosi.commit(client, parse_path("m/10018h/1h"))
c2 = cosi.commit(client, parse_path("m/10018h/2h"))
assert c0.pubkey != c1.pubkey
assert c0.pubkey != c2.pubkey
assert c1.pubkey != c2.pubkey
assert c0.commitment != c1.commitment
assert c0.commitment != c2.commitment
assert c1.commitment != c2.commitment
digestb = sha256(b"this is a different message").digest()
c0b = cosi.commit(client, parse_path("m/10018h/0h"), digestb)
c1b = cosi.commit(client, parse_path("m/10018h/1h"), digestb)
c2b = cosi.commit(client, parse_path("m/10018h/2h"), digestb)
def test_cosi_nonce(client: Client):
# The nonce/commitment must change after each signing.
c0 = cosi.commit(client, parse_path("m/10018h/0h"))
cosi.sign(client, parse_path("m/10018h/0h"), DIGEST, c0.commitment, c0.pubkey)
c1 = cosi.commit(client, parse_path("m/10018h/0h"))
assert c0.commitment != c1.commitment
assert c0.pubkey == c0b.pubkey
assert c1.pubkey == c1b.pubkey
assert c2.pubkey == c2b.pubkey
assert c0.commitment != c0b.commitment
assert c1.commitment != c1b.commitment
assert c2.commitment != c2b.commitment
def test_cosi_sign1(client: Client):
# Single party signature.
commit = cosi.commit(client, parse_path("m/10018h/0h"))
sig = cosi.sign(
client, parse_path("m/10018h/0h"), DIGEST, commit.commitment, commit.pubkey
)
signature = cosi.combine_sig(commit.commitment, [sig.signature])
cosi.verify_combined(signature, DIGEST, commit.pubkey)
def test_cosi_sign(client: Client):
digest = sha256(b"this is a message").digest()
def test_cosi_sign2(client: Client):
# Two party signature.
remote_commit = cosi.commit(client, parse_path("m/10018h/1h"))
c0 = cosi.commit(client, parse_path("m/10018h/0h"), digest)
c1 = cosi.commit(client, parse_path("m/10018h/1h"), digest)
c2 = cosi.commit(client, parse_path("m/10018h/2h"), digest)
local_privkey = sha256(b"private key").digest()[:32]
local_pubkey = cosi.pubkey_from_privkey(local_privkey)
local_nonce, local_commitment = cosi.get_nonce(local_privkey, DIGEST, 42)
global_pk = cosi.combine_keys([c0.pubkey, c1.pubkey, c2.pubkey])
global_R = cosi.combine_keys([c0.commitment, c1.commitment, c2.commitment])
global_pk = cosi.combine_keys([remote_commit.pubkey, local_pubkey])
global_R = cosi.combine_keys([remote_commit.commitment, local_commitment])
# fmt: off
sig0 = cosi.sign(client, parse_path("m/10018h/0h"), digest, global_R, global_pk)
sig1 = cosi.sign(client, parse_path("m/10018h/1h"), digest, global_R, global_pk)
sig2 = cosi.sign(client, parse_path("m/10018h/2h"), digest, global_R, global_pk)
# fmt: on
remote_sig = cosi.sign(
client, parse_path("m/10018h/1h"), DIGEST, global_R, global_pk
)
local_sig = cosi.sign_with_privkey(
DIGEST, local_privkey, global_pk, local_nonce, global_R
)
signature = cosi.combine_sig(global_R, [remote_sig.signature, local_sig])
sig = cosi.combine_sig(global_R, [sig0.signature, sig1.signature, sig2.signature])
cosi.verify_combined(signature, DIGEST, global_pk)
cosi.verify_combined(sig, digest, global_pk)
def test_cosi_sign3(client: Client):
# Three party signature.
remote_commit = cosi.commit(client, parse_path("m/10018h/2h"))
def test_cosi_compat(client: Client):
digest = sha256(b"this is not a pipe").digest()
remote_commit = cosi.commit(client, parse_path("m/10018h/0h"), digest)
local_privkey1 = sha256(b"private key").digest()[:32]
local_pubkey1 = cosi.pubkey_from_privkey(local_privkey1)
local_nonce1, local_commitment1 = cosi.get_nonce(local_privkey1, DIGEST, 42)
local_privkey = sha256(b"private key").digest()[:32]
local_pubkey = cosi.pubkey_from_privkey(local_privkey)
local_nonce, local_commitment = cosi.get_nonce(local_privkey, digest, 42)
local_privkey2 = sha256(b"private key").digest()[:32]
local_pubkey2 = cosi.pubkey_from_privkey(local_privkey2)
local_nonce2, local_commitment2 = cosi.get_nonce(local_privkey2, DIGEST, 42)
global_pk = cosi.combine_keys([remote_commit.pubkey, local_pubkey])
global_R = cosi.combine_keys([remote_commit.commitment, local_commitment])
global_pk = cosi.combine_keys([remote_commit.pubkey, local_pubkey1, local_pubkey2])
global_R = cosi.combine_keys(
[remote_commit.commitment, local_commitment1, local_commitment2]
)
remote_sig = cosi.sign(
client, parse_path("m/10018h/0h"), digest, global_R, global_pk
client, parse_path("m/10018h/2h"), DIGEST, global_R, global_pk
)
local_sig = cosi.sign_with_privkey(
digest, local_privkey, global_pk, local_nonce, global_R
local_sig1 = cosi.sign_with_privkey(
DIGEST, local_privkey1, global_pk, local_nonce1, global_R
)
local_sig2 = cosi.sign_with_privkey(
DIGEST, local_privkey2, global_pk, local_nonce2, global_R
)
signature = cosi.combine_sig(
global_R, [remote_sig.signature, local_sig1, local_sig2]
)
sig = cosi.combine_sig(global_R, [remote_sig.signature, local_sig])
cosi.verify_combined(sig, digest, global_pk)
cosi.verify_combined(signature, DIGEST, global_pk)

@ -393,9 +393,11 @@
"T1_ethereum-test_signtx.py::test_signtx_eip1559[unknown_erc20]": "548c1f22918351e9cbcc1e16d8ba67bc2e7460b9a92cfc6c8bfa0a2b063e68da",
"T1_ethereum-test_signtx.py::test_signtx_eip1559_access_list": "f6c5f398d4e80fc8f93cf70e9b10de24b9a968db04dc6ea21b28d1a273f04ca1",
"T1_ethereum-test_signtx.py::test_signtx_eip1559_access_list_larger": "f6c5f398d4e80fc8f93cf70e9b10de24b9a968db04dc6ea21b28d1a273f04ca1",
"T1_misc-test_cosi.py::test_cosi_commit": "c943c92750d6072a3b272c1a9dca815ee7504293e4c1d85a76d5af9c2e415297",
"T1_misc-test_cosi.py::test_cosi_compat": "dfdc383ac1dd9f1bb63b52c8623d3788491e7a954f6f0374fe0963e823a5e0c5",
"T1_misc-test_cosi.py::test_cosi_sign": "54f589eaca23d0e1a055280920b9a40c7ba7a5b34779270090a8361113af4574",
"T1_misc-test_cosi.py::test_cosi_nonce": "6990c238036b79368fea1dc1e3e8871d7788322bbee7425d14c53623bc8182e8",
"T1_misc-test_cosi.py::test_cosi_pubkey": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_misc-test_cosi.py::test_cosi_sign1": "6990c238036b79368fea1dc1e3e8871d7788322bbee7425d14c53623bc8182e8",
"T1_misc-test_cosi.py::test_cosi_sign2": "0852e7433ec54a0ace301b683417107a9805095acd954010b665266503a79f36",
"T1_misc-test_cosi.py::test_cosi_sign3": "d04fca2d97f7117ffc79ae52d192086aba2f9c64e89db27e20a930e9048f1d81",
"T1_misc-test_msg_cipherkeyvalue.py::test_decrypt": "a849d9e11e222d1348ce3135680a2a61ed265692adeba4bd48af2fdc65207e61",
"T1_misc-test_msg_cipherkeyvalue.py::test_decrypt_badlen": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_misc-test_msg_cipherkeyvalue.py::test_encrypt": "e36d5b5db4b3733c9f484b149c0f966b3dd66e8b54e1b5bccf9da5cca7abed91",

Loading…
Cancel
Save