mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-11 07:50:57 +00:00
Merge branch 'release/22.08'
This commit is contained in:
commit
c962d3520b
@ -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 @@
|
||||
Updated secp256k1-zkp.
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
1
python/.changelog.d/noissue.removed
Normal file
1
python/.changelog.d/noissue.removed
Normal file
@ -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
|
||||
|
||||
|
||||
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.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)
|
||||
|
||||
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_sign(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)
|
||||
|
||||
global_pk = cosi.combine_keys([c0.pubkey, c1.pubkey, c2.pubkey])
|
||||
global_R = cosi.combine_keys([c0.commitment, c1.commitment, c2.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
|
||||
|
||||
sig = cosi.combine_sig(global_R, [sig0.signature, sig1.signature, sig2.signature])
|
||||
|
||||
cosi.verify_combined(sig, digest, global_pk)
|
||||
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_compat(client: Client):
|
||||
digest = sha256(b"this is not a pipe").digest()
|
||||
remote_commit = cosi.commit(client, parse_path("m/10018h/0h"), digest)
|
||||
def test_cosi_sign2(client: Client):
|
||||
# Two party signature.
|
||||
remote_commit = cosi.commit(client, parse_path("m/10018h/1h"))
|
||||
|
||||
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_nonce, local_commitment = cosi.get_nonce(local_privkey, DIGEST, 42)
|
||||
|
||||
global_pk = cosi.combine_keys([remote_commit.pubkey, local_pubkey])
|
||||
global_R = cosi.combine_keys([remote_commit.commitment, local_commitment])
|
||||
|
||||
remote_sig = cosi.sign(
|
||||
client, parse_path("m/10018h/0h"), digest, global_R, global_pk
|
||||
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
|
||||
DIGEST, local_privkey, global_pk, local_nonce, global_R
|
||||
)
|
||||
sig = cosi.combine_sig(global_R, [remote_sig.signature, local_sig])
|
||||
signature = cosi.combine_sig(global_R, [remote_sig.signature, local_sig])
|
||||
|
||||
cosi.verify_combined(sig, digest, global_pk)
|
||||
cosi.verify_combined(signature, DIGEST, global_pk)
|
||||
|
||||
|
||||
def test_cosi_sign3(client: Client):
|
||||
# Three party signature.
|
||||
remote_commit = cosi.commit(client, parse_path("m/10018h/2h"))
|
||||
|
||||
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_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_pubkey1, local_pubkey2])
|
||||
global_R = cosi.combine_keys(
|
||||
[remote_commit.commitment, local_commitment1, local_commitment2]
|
||||
)
|
||||
|
||||
remote_sig = cosi.sign(
|
||||
client, parse_path("m/10018h/2h"), DIGEST, global_R, global_pk
|
||||
)
|
||||
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]
|
||||
)
|
||||
|
||||
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…
Reference in New Issue
Block a user