chore(core): divide `chacha20poly1305` into `_encrypt` and `_decrypt` classes
What changed, and why it matters
This commit is a routine code cleanup (chore) that splits one combined ChaCha20-Poly1305 encryption/decryption class into two separate classes: one for encryption and one for decryption. It does not fix a security bug, add a new feature, or change cryptographic behavior. All existing callers are updated to use the appropriate new class. The change is purely structural and makes the API clearer.
No security action required. Treat as normal maintenance. Reviewers may optionally verify that all former `chacha20poly1305` decrypt usages now pass the expected MAC to `finish()` and that no stale imports remain.
Security signals we found
No security-relevant signal: refactor only
API split removes dual-use encrypt/decrypt state machine
MAC verification path still uses constant-time comparison (consteq)
No changes to key/nonce handling, padding, or cryptographic primitives
Evidence from the diff
The C MicroPython module modtrezorcrypto-chacha20poly1305.h is refactored from a single chacha20poly1305 class with ENCRYPTING/DECRYPTING states into chacha20poly1305_encrypt and chacha20poly1305_decrypt classes sharing a common PROCESSING state. The old unified finish(expected_mac=None) is replaced by finish() returning the MAC for encryption and finish(expected_mac) verifying the MAC (returning None) for decryption. The underlying rfc7539_auth, rfc7539_finish, and consteq calls remain unchanged. Python callers in benchmarks, Monero signing, WebAuthn credential storage, and tests are updated to import and use the correct class. The commit message is chore(core): ... with [no changelog], indicating no security or user-facing change.
Changed components
core/embed/upymod/modtrezorcrypto/modtrezorcrypto-chacha20poly1305.hcore/embed/upymod/modtrezorcrypto/modtrezorcrypto.ccore/mocks/generated/trezorcrypto/__init__.pyicore/src/apps/benchmark/benchmarks.pycore/src/apps/monero/signing/step_09_sign_input.pycore/src/apps/monero/xmr/chacha_poly.pycore/src/apps/webauthn/credential.pycore/src/trezor/crypto/__init__.pycore/tests/test_apps.monero.proto.pycore/tests/test_trezor.crypto.chacha20poly1305.pyInspect captured patch +195 / −116
diff --git a/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-chacha20poly1305.h b/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-chacha20poly1305.h
index ddb0e91a..615952d7 100644
--- a/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-chacha20poly1305.h
+++ b/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-chacha20poly1305.h
@@ -25,9 +25,9 @@
/// package: trezorcrypto.__init__
-/// class chacha20poly1305:
+/// class chacha20poly1305_encrypt:
/// """
-/// ChaCha20Poly1305 context.
+/// ChaCha20Poly1305 context for encryption.
/// """
typedef struct _mp_obj_ChaCha20Poly1305_t {
mp_obj_base_t base;
@@ -35,15 +35,14 @@ typedef struct _mp_obj_ChaCha20Poly1305_t {
int64_t alen, plen;
enum {
INIT,
- ENCRYPTING,
- DECRYPTING,
+ PROCESSING,
FINISHED,
} state;
} mp_obj_ChaCha20Poly1305_t;
/// def __init__(self, key: AnyBytes, nonce: AnyBytes) -> None:
/// """
-/// Initialize the ChaCha20 + Poly1305 context for encryption or decryption
+/// Initialize the ChaCha20 + Poly1305 context for encryption
/// using a 32 byte key and 12 byte nonce as in the RFC 7539 style.
/// """
STATIC mp_obj_t mod_trezorcrypto_ChaCha20Poly1305_make_new(
@@ -69,6 +68,28 @@ STATIC mp_obj_t mod_trezorcrypto_ChaCha20Poly1305_make_new(
return MP_OBJ_FROM_PTR(o);
}
+/// def auth(self, data: AnyBytes) -> None:
+/// """
+/// Include authenticated data in the Poly1305 MAC using the RFC 7539
+/// style with 16 byte padding. This must only be called once and prior
+/// to encryption.
+/// """
+STATIC mp_obj_t mod_trezorcrypto_ChaCha20Poly1305_auth(mp_obj_t self,
+ mp_obj_t data) {
+ mp_obj_ChaCha20Poly1305_t *o = MP_OBJ_TO_PTR(self);
+ if (o->state != INIT) {
+ mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Invalid state."));
+ }
+ o->state = PROCESSING;
+ mp_buffer_info_t in = {0};
+ mp_get_buffer_raise(data, &in, MP_BUFFER_READ);
+ rfc7539_auth(&(o->ctx), in.buf, in.len);
+ o->alen += in.len;
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_ChaCha20Poly1305_auth_obj,
+ mod_trezorcrypto_ChaCha20Poly1305_auth);
+
/// def encrypt(self, data: AnyBytes) -> bytes:
/// """
/// Encrypt data (length of data must be divisible by 64 except for the
@@ -77,10 +98,10 @@ STATIC mp_obj_t mod_trezorcrypto_ChaCha20Poly1305_make_new(
STATIC mp_obj_t mod_trezorcrypto_ChaCha20Poly1305_encrypt(mp_obj_t self,
mp_obj_t data) {
mp_obj_ChaCha20Poly1305_t *o = MP_OBJ_TO_PTR(self);
- if (o->state != INIT && o->state != ENCRYPTING) {
+ if (o->state != INIT && o->state != PROCESSING) {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Invalid state."));
}
- o->state = ENCRYPTING;
+ o->state = PROCESSING;
mp_buffer_info_t in = {0};
mp_get_buffer_raise(data, &in, MP_BUFFER_READ);
vstr_t vstr = {0};
@@ -92,6 +113,45 @@ STATIC mp_obj_t mod_trezorcrypto_ChaCha20Poly1305_encrypt(mp_obj_t self,
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_ChaCha20Poly1305_encrypt_obj,
mod_trezorcrypto_ChaCha20Poly1305_encrypt);
+/// def finish(self) -> bytes:
+/// """
+/// Compute RFC 7539-style Poly1305 MAC.
+/// """
+STATIC mp_obj_t
+mod_trezorcrypto_ChaCha20Poly1305_encrypt_finish(mp_obj_t self) {
+ mp_obj_ChaCha20Poly1305_t *o = MP_OBJ_TO_PTR(self);
+ if (o->state != INIT && o->state != PROCESSING) {
+ mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Invalid state."));
+ }
+
+ o->state = FINISHED;
+ vstr_t mac = {0};
+ vstr_init_len(&mac, 16);
+ rfc7539_finish(&(o->ctx), o->alen, o->plen, (uint8_t *)mac.buf);
+ return mp_obj_new_str_from_vstr(&mp_type_bytes, &mac);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(
+ mod_trezorcrypto_ChaCha20Poly1305_encrypt_finish_obj,
+ mod_trezorcrypto_ChaCha20Poly1305_encrypt_finish);
+
+/// class chacha20poly1305_decrypt:
+/// """
+/// ChaCha20Poly1305 context for decryption.
+/// """
+
+/// def __init__(self, key: AnyBytes, nonce: AnyBytes) -> None:
+/// """
+/// Initialize the ChaCha20 + Poly1305 context for decryption
+/// using a 32 byte key and 12 byte nonce as in the RFC 7539 style.
+/// """
+
+/// def auth(self, data: AnyBytes) -> None:
+/// """
+/// Include authenticated data in the Poly1305 MAC using the RFC 7539
+/// style with 16 byte padding. This must only be called once and prior
+/// to decryption.
+/// """
+
/// def decrypt(self, data: AnyBytes) -> bytes:
/// """
/// Decrypt data (length of data must be divisible by 64 except for the
@@ -100,10 +160,10 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_ChaCha20Poly1305_encrypt_obj,
STATIC mp_obj_t mod_trezorcrypto_ChaCha20Poly1305_decrypt(mp_obj_t self,
mp_obj_t data) {
mp_obj_ChaCha20Poly1305_t *o = MP_OBJ_TO_PTR(self);
- if (o->state != INIT && o->state != DECRYPTING) {
+ if (o->state != INIT && o->state != PROCESSING) {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Invalid state."));
}
- o->state = DECRYPTING;
+ o->state = PROCESSING;
mp_buffer_info_t in = {0};
mp_get_buffer_raise(data, &in, MP_BUFFER_READ);
vstr_t vstr = {0};
@@ -115,66 +175,38 @@ STATIC mp_obj_t mod_trezorcrypto_ChaCha20Poly1305_decrypt(mp_obj_t self,
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_ChaCha20Poly1305_decrypt_obj,
mod_trezorcrypto_ChaCha20Poly1305_decrypt);
-/// def auth(self, data: AnyBytes) -> None:
+/// def finish(self, expected_mac: AnyBytes) -> None:
/// """
-/// Include authenticated data in the Poly1305 MAC using the RFC 7539
-/// style with 16 byte padding. This must only be called once and prior
-/// to encryption or decryption.
+/// Verify RFC 7539-style Poly1305 MAC.
/// """
-STATIC mp_obj_t mod_trezorcrypto_ChaCha20Poly1305_auth(mp_obj_t self,
- mp_obj_t data) {
+STATIC mp_obj_t mod_trezorcrypto_ChaCha20Poly1305_decrypt_finish(
+ mp_obj_t self, mp_obj_t expected_mac) {
mp_obj_ChaCha20Poly1305_t *o = MP_OBJ_TO_PTR(self);
- if (o->state != INIT && o->state != ENCRYPTING && o->state != DECRYPTING) {
+ if (o->state != INIT && o->state != PROCESSING) {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Invalid state."));
}
- mp_buffer_info_t in = {0};
- mp_get_buffer_raise(data, &in, MP_BUFFER_READ);
- rfc7539_auth(&(o->ctx), in.buf, in.len);
- o->alen += in.len;
- return mp_const_none;
-}
-STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_ChaCha20Poly1305_auth_obj,
- mod_trezorcrypto_ChaCha20Poly1305_auth);
-
-/// def finish(self, expected_mac: AnyBytes | None = None) -> bytes:
-/// """
-/// Compute RFC 7539-style Poly1305 MAC. The `expected_mac` is required when
-/// decrypting.
-/// """
-STATIC mp_obj_t mod_trezorcrypto_ChaCha20Poly1305_finish(size_t n_args,
- const mp_obj_t *args) {
- mp_obj_ChaCha20Poly1305_t *o = MP_OBJ_TO_PTR(args[0]);
- if (o->state != INIT && o->state != ENCRYPTING && o->state != DECRYPTING) {
- mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Invalid state."));
- }
- if (n_args == 1 && o->state == DECRYPTING) {
- mp_raise_msg(
- &mp_type_RuntimeError,
- MP_ERROR_TEXT("Argument `expected_mac` is required when decrypting."));
- }
o->state = FINISHED;
+ mp_buffer_info_t exp_mac = {0};
+ mp_get_buffer_raise(expected_mac, &exp_mac, MP_BUFFER_READ);
+ if (exp_mac.len != 16) {
+ mp_raise_ValueError(MP_ERROR_TEXT(
+ "Invalid length of the expected mac. It has to be 16 bytes."));
+ }
vstr_t mac = {0};
vstr_init_len(&mac, 16);
rfc7539_finish(&(o->ctx), o->alen, o->plen, (uint8_t *)mac.buf);
- if (n_args == 2) {
- mp_buffer_info_t expected_mac = {0};
- mp_get_buffer_raise(args[1], &expected_mac, MP_BUFFER_READ);
- if (expected_mac.len != 16) {
- mp_raise_ValueError(MP_ERROR_TEXT(
- "Invalid length of the expected mac. It has to be 16 bytes."));
- }
- if (!consteq((uint8_t *)mac.buf, mac.len, (uint8_t *)expected_mac.buf,
- expected_mac.len)) {
- mp_raise_msg(&mp_type_RuntimeError,
- MP_ERROR_TEXT("Authentication failed."));
- }
+
+ if (!consteq(mac.buf, exp_mac.buf, exp_mac.len)) {
+ mp_raise_msg(&mp_type_RuntimeError,
+ MP_ERROR_TEXT("Authentication failed."));
}
- return mp_obj_new_str_from_vstr(&mp_type_bytes, &mac);
+
+ return mp_const_none;
}
-STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(
- mod_trezorcrypto_ChaCha20Poly1305_finish_obj, 1, 2,
- mod_trezorcrypto_ChaCha20Poly1305_finish);
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(
+ mod_trezorcrypto_ChaCha20Poly1305_decrypt_finish_obj,
+ mod_trezorcrypto_ChaCha20Poly1305_decrypt_finish);
STATIC mp_obj_t mod_trezorcrypto_ChaCha20Poly1305___del__(mp_obj_t self) {
mp_obj_ChaCha20Poly1305_t *o = MP_OBJ_TO_PTR(self);
@@ -187,25 +219,47 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_ChaCha20Poly1305___del___obj,
mod_trezorcrypto_ChaCha20Poly1305___del__);
STATIC const mp_rom_map_elem_t
- mod_trezorcrypto_ChaCha20Poly1305_locals_dict_table[] = {
+ mod_trezorcrypto_ChaCha20Poly1305Encrypt_locals_dict_table[] = {
{MP_ROM_QSTR(MP_QSTR_encrypt),
MP_ROM_PTR(&mod_trezorcrypto_ChaCha20Poly1305_encrypt_obj)},
+ {MP_ROM_QSTR(MP_QSTR_auth),
+ MP_ROM_PTR(&mod_trezorcrypto_ChaCha20Poly1305_auth_obj)},
+ {MP_ROM_QSTR(MP_QSTR_finish),
+ MP_ROM_PTR(&mod_trezorcrypto_ChaCha20Poly1305_encrypt_finish_obj)},
+ {MP_ROM_QSTR(MP_QSTR___del__),
+ MP_ROM_PTR(&mod_trezorcrypto_ChaCha20Poly1305___del___obj)},
+};
+STATIC MP_DEFINE_CONST_DICT(
+ mod_trezorcrypto_ChaCha20Poly1305Encrypt_locals_dict,
+ mod_trezorcrypto_ChaCha20Poly1305Encrypt_locals_dict_table);
+
+STATIC const mp_rom_map_elem_t
+ mod_trezorcrypto_ChaCha20Poly1305Decrypt_locals_dict_table[] = {
{MP_ROM_QSTR(MP_QSTR_decrypt),
MP_ROM_PTR(&mod_trezorcrypto_ChaCha20Poly1305_decrypt_obj)},
{MP_ROM_QSTR(MP_QSTR_auth),
MP_ROM_PTR(&mod_trezorcrypto_ChaCha20Poly1305_auth_obj)},
{MP_ROM_QSTR(MP_QSTR_finish),
- MP_ROM_PTR(&mod_trezorcrypto_ChaCha20Poly1305_finish_obj)},
+ MP_ROM_PTR(&mod_trezorcrypto_ChaCha20Poly1305_decrypt_finish_obj)},
{MP_ROM_QSTR(MP_QSTR___del__),
MP_ROM_PTR(&mod_trezorcrypto_ChaCha20Poly1305___del___obj)},
};
STATIC MP_DEFINE_CONST_DICT(
- mod_trezorcrypto_ChaCha20Poly1305_locals_dict,
- mod_trezorcrypto_ChaCha20Poly1305_locals_dict_table);
+ mod_trezorcrypto_ChaCha20Poly1305Decrypt_locals_dict,
+ mod_trezorcrypto_ChaCha20Poly1305Decrypt_locals_dict_table);
+
+STATIC const mp_obj_type_t mod_trezorcrypto_ChaCha20Poly1305Encrypt_type = {
+ {&mp_type_type},
+ .name = MP_QSTR_chacha20poly1305_encrypt,
+ .make_new = mod_trezorcrypto_ChaCha20Poly1305_make_new,
+ .locals_dict =
+ (void *)&mod_trezorcrypto_ChaCha20Poly1305Encrypt_locals_dict,
+};
-STATIC const mp_obj_type_t mod_trezorcrypto_ChaCha20Poly1305_type = {
+STATIC const mp_obj_type_t mod_trezorcrypto_ChaCha20Poly1305Decrypt_type = {
{&mp_type_type},
- .name = MP_QSTR_ChaCha20Poly1305,
+ .name = MP_QSTR_chacha20poly1305_decrypt,
.make_new = mod_trezorcrypto_ChaCha20Poly1305_make_new,
- .locals_dict = (void *)&mod_trezorcrypto_ChaCha20Poly1305_locals_dict,
+ .locals_dict =
+ (void *)&mod_trezorcrypto_ChaCha20Poly1305Decrypt_locals_dict,
};
diff --git a/core/embed/upymod/modtrezorcrypto/modtrezorcrypto.c b/core/embed/upymod/modtrezorcrypto/modtrezorcrypto.c
index 0c2d836e..43c00564 100644
--- a/core/embed/upymod/modtrezorcrypto/modtrezorcrypto.c
+++ b/core/embed/upymod/modtrezorcrypto/modtrezorcrypto.c
@@ -101,8 +101,10 @@ STATIC const mp_rom_map_elem_t mp_module_trezorcrypto_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR_cardano),
MP_ROM_PTR(&mod_trezorcrypto_cardano_module)},
#endif
- {MP_ROM_QSTR(MP_QSTR_chacha20poly1305),
- MP_ROM_PTR(&mod_trezorcrypto_ChaCha20Poly1305_type)},
+ {MP_ROM_QSTR(MP_QSTR_chacha20poly1305_decrypt),
+ MP_ROM_PTR(&mod_trezorcrypto_ChaCha20Poly1305Decrypt_type)},
+ {MP_ROM_QSTR(MP_QSTR_chacha20poly1305_encrypt),
+ MP_ROM_PTR(&mod_trezorcrypto_ChaCha20Poly1305Encrypt_type)},
{MP_ROM_QSTR(MP_QSTR_crc), MP_ROM_PTR(&mod_trezorcrypto_crc_module)},
{MP_ROM_QSTR(MP_QSTR_curve25519),
MP_ROM_PTR(&mod_trezorcrypto_curve25519_module)},
diff --git a/core/mocks/generated/trezorcrypto/__init__.pyi b/core/mocks/generated/trezorcrypto/__init__.pyi
index b89216ec..f18e4039 100644
--- a/core/mocks/generated/trezorcrypto/__init__.pyi
+++ b/core/mocks/generated/trezorcrypto/__init__.pyi
@@ -197,40 +197,64 @@ class blake2s:
# upymod/modtrezorcrypto/modtrezorcrypto-chacha20poly1305.h
-class chacha20poly1305:
+class chacha20poly1305_encrypt:
"""
- ChaCha20Poly1305 context.
+ ChaCha20Poly1305 context for encryption.
"""
def __init__(self, key: AnyBytes, nonce: AnyBytes) -> None:
"""
- Initialize the ChaCha20 + Poly1305 context for encryption or decryption
+ Initialize the ChaCha20 + Poly1305 context for encryption
using a 32 byte key and 12 byte nonce as in the RFC 7539 style.
"""
+ def auth(self, data: AnyBytes) -> None:
+ """
+ Include authenticated data in the Poly1305 MAC using the RFC 7539
+ style with 16 byte padding. This must only be called once and prior
+ to encryption.
+ """
+
def encrypt(self, data: AnyBytes) -> bytes:
"""
Encrypt data (length of data must be divisible by 64 except for the
final value).
"""
- def decrypt(self, data: AnyBytes) -> bytes:
+ def finish(self) -> bytes:
"""
- Decrypt data (length of data must be divisible by 64 except for the
- final value).
+ Compute RFC 7539-style Poly1305 MAC.
+ """
+
+
+# upymod/modtrezorcrypto/modtrezorcrypto-chacha20poly1305.h
+class chacha20poly1305_decrypt:
+ """
+ ChaCha20Poly1305 context for decryption.
+ """
+
+ def __init__(self, key: AnyBytes, nonce: AnyBytes) -> None:
+ """
+ Initialize the ChaCha20 + Poly1305 context for decryption
+ using a 32 byte key and 12 byte nonce as in the RFC 7539 style.
"""
def auth(self, data: AnyBytes) -> None:
"""
Include authenticated data in the Poly1305 MAC using the RFC 7539
style with 16 byte padding. This must only be called once and prior
- to encryption or decryption.
+ to decryption.
+ """
+
+ def decrypt(self, data: AnyBytes) -> bytes:
+ """
+ Decrypt data (length of data must be divisible by 64 except for the
+ final value).
"""
- def finish(self, expected_mac: AnyBytes | None = None) -> bytes:
+ def finish(self, expected_mac: AnyBytes) -> None:
"""
- Compute RFC 7539-style Poly1305 MAC. The `expected_mac` is required when
- decrypting.
+ Verify RFC 7539-style Poly1305 MAC.
"""
diff --git a/core/src/apps/benchmark/benchmarks.py b/core/src/apps/benchmark/benchmarks.py
index 2b2c43af..a7ceb5d6 100644
--- a/core/src/apps/benchmark/benchmarks.py
+++ b/core/src/apps/benchmark/benchmarks.py
@@ -1,4 +1,10 @@
-from trezor.crypto import aes, aesgcm_decrypt, aesgcm_encrypt, chacha20poly1305
+from trezor.crypto import (
+ aes,
+ aesgcm_decrypt,
+ aesgcm_encrypt,
+ chacha20poly1305_decrypt,
+ chacha20poly1305_encrypt,
+)
from trezor.crypto.curve import curve25519, ed25519, nist256p1, secp256k1
from trezor.crypto.hashlib import (
blake2b,
@@ -75,10 +81,10 @@ benchmarks = {
lambda: aesgcm_decrypt(random_bytes(32), random_bytes(16)), 16
),
"crypto/cipher/chacha20poly1305/encrypt": EncryptBenchmark(
- lambda: chacha20poly1305(random_bytes(32), random_bytes(12)), 64
+ lambda: chacha20poly1305_encrypt(random_bytes(32), random_bytes(12)), 64
),
"crypto/cipher/chacha20poly1305/decrypt": DecryptBenchmark(
- lambda: chacha20poly1305(random_bytes(32), random_bytes(12)), 64
+ lambda: chacha20poly1305_decrypt(random_bytes(32), random_bytes(12)), 64
),
"crypto/curve/secp256k1/sign": SignBenchmark(secp256k1),
"crypto/curve/secp256k1/verify": VerifyBenchmark(secp256k1),
diff --git a/core/src/apps/monero/signing/step_09_sign_input.py b/core/src/apps/monero/signing/step_09_sign_input.py
index 6003c96c..7c25565d 100644
--- a/core/src/apps/monero/signing/step_09_sign_input.py
+++ b/core/src/apps/monero/signing/step_09_sign_input.py
@@ -220,7 +220,7 @@ def _protect_signature(state: State, mg_buffer: list[bytes]) -> list[bytes]:
After protocol finishes without error, opening_key is sent to the
host.
"""
- from trezor.crypto import chacha20poly1305, random
+ from trezor.crypto import chacha20poly1305_encrypt, random
from apps.monero.signing import offloading_keys
@@ -235,7 +235,7 @@ def _protect_signature(state: State, mg_buffer: list[bytes]) -> list[bytes]:
state.opening_key, state.current_input_index, False
)
- cipher = chacha20poly1305(key, nonce)
+ cipher = chacha20poly1305_encrypt(key, nonce)
# cipher.update() input has to be 512 bit long (besides the last block).
# Thus we go over mg_buffer and buffer 512 bit input blocks before
diff --git a/core/src/apps/monero/xmr/chacha_poly.py b/core/src/apps/monero/xmr/chacha_poly.py
index ef3e9e0e..b36e69ce 100644
--- a/core/src/apps/monero/xmr/chacha_poly.py
+++ b/core/src/apps/monero/xmr/chacha_poly.py
@@ -1,4 +1,4 @@
-from trezor.crypto import chacha20poly1305 as ChaCha20Poly1305
+from trezor.crypto import chacha20poly1305_decrypt, chacha20poly1305_encrypt
def encrypt(key: bytes, plaintext: bytes, associated_data: bytes | None = None):
@@ -8,7 +8,7 @@ def encrypt(key: bytes, plaintext: bytes, associated_data: bytes | None = None):
from trezor.crypto import random
nonce = random.bytes(12)
- cipher = ChaCha20Poly1305(key, nonce)
+ cipher = chacha20poly1305_encrypt(key, nonce)
if associated_data:
cipher.auth(associated_data)
ciphertext = cipher.encrypt(plaintext)
@@ -26,7 +26,7 @@ def _decrypt(
ChaCha20Poly1305 decryption
"""
- cipher = ChaCha20Poly1305(key, iv)
+ cipher = chacha20poly1305_decrypt(key, iv)
if associated_data:
cipher.auth(associated_data)
exp_tag, ciphertext = ciphertext[-16:], ciphertext[:-16]
diff --git a/core/src/apps/webauthn/credential.py b/core/src/apps/webauthn/credential.py
index 720d5120..44571c1c 100644
--- a/core/src/apps/webauthn/credential.py
+++ b/core/src/apps/webauthn/credential.py
@@ -5,7 +5,14 @@ from ubinascii import hexlify
import storage.device as storage_device
from trezor import utils
-from trezor.crypto import chacha20poly1305, der, hashlib, hmac, random
+from trezor.crypto import (
+ chacha20poly1305_decrypt,
+ chacha20poly1305_encrypt,
+ der,
+ hashlib,
+ hmac,
+ random,
+)
from trezor.crypto.curve import ed25519, nist256p1
from apps.common import cbor, seed
@@ -169,7 +176,7 @@ class Fido2Credential(Credential):
[b"SLIP-0022", _CRED_ID_VERSION, b"Encryption key"]
).key()
iv = random.bytes(12)
- ctx = chacha20poly1305(key, iv)
+ ctx = chacha20poly1305_encrypt(key, iv)
ctx.auth(self.rp_id_hash)
ciphertext = ctx.encrypt(cbor.encode(data))
tag = ctx.finish()
@@ -193,7 +200,7 @@ class Fido2Credential(Credential):
tag = cred_id[-16:]
if rp_id_hash is None:
- ctx = chacha20poly1305(key, iv)
+ ctx = chacha20poly1305_decrypt(key, iv)
data = ctx.decrypt(ciphertext)
try:
rp_id = cbor.decode(data)[_CRED_ID_RP_ID]
@@ -201,7 +208,7 @@ class Fido2Credential(Credential):
raise ValueError from e # CBOR decoding failed
rp_id_hash = hashlib.sha256(rp_id).digest()
- ctx = chacha20poly1305(key, iv)
+ ctx = chacha20poly1305_decrypt(key, iv)
ctx.auth(rp_id_hash)
data = ctx.decrypt(ciphertext)
try:
diff --git a/core/src/trezor/crypto/__init__.py b/core/src/trezor/crypto/__init__.py
index 79e2bab0..ee3fbc7d 100644
--- a/core/src/trezor/crypto/__init__.py
+++ b/core/src/trezor/crypto/__init__.py
@@ -2,7 +2,8 @@ from trezorcrypto import ( # noqa: F401
aes,
bip32,
bip39,
- chacha20poly1305,
+ chacha20poly1305_decrypt,
+ chacha20poly1305_encrypt,
crc,
hmac,
pbkdf2,
diff --git a/core/tests/test_apps.monero.proto.py b/core/tests/test_apps.monero.proto.py
index ddc11e81..775eff5d 100644
--- a/core/tests/test_apps.monero.proto.py
+++ b/core/tests/test_apps.monero.proto.py
@@ -4,7 +4,7 @@ from common import * # isort:skip
if not utils.BITCOIN_ONLY:
import ubinascii
- from trezor.crypto import chacha20poly1305
+ from trezor.crypto import chacha20poly1305_decrypt, chacha20poly1305_encrypt
from apps.monero.signing import offloading_keys, step_09_sign_input
from apps.monero.signing.state import State
@@ -68,17 +68,16 @@ class TestMoneroProto(unittest.TestCase):
iv = offloading_keys.key_signature(mst, st.current_input_index, True)[:12]
key = offloading_keys.key_signature(mst, st.current_input_index, False)
- cipher = chacha20poly1305(key, iv)
+ cipher = chacha20poly1305_encrypt(key, iv)
ciphertext = cipher.encrypt(b"".join(mg_buff_b))
ciphertext += cipher.finish()
self.assertEqual(b"".join(mg_res), ciphertext)
- cipher = chacha20poly1305(key, iv)
+ cipher = chacha20poly1305_decrypt(key, iv)
ciphertext = b"".join(mg_res)
exp_tag, ciphertext = ciphertext[-16:], ciphertext[:-16]
plaintext = cipher.decrypt(ciphertext)
- tag = cipher.finish(exp_tag)
- self.assertEqual(tag, exp_tag)
+ self.assertIsNone(cipher.finish(exp_tag))
self.assertEqual(plaintext, b"".join(mg_buff_b))
diff --git a/core/tests/test_trezor.crypto.chacha20poly1305.py b/core/tests/test_trezor.crypto.chacha20poly1305.py
index c1a8f7a2..4ae22ed8 100644
--- a/core/tests/test_trezor.crypto.chacha20poly1305.py
+++ b/core/tests/test_trezor.crypto.chacha20poly1305.py
@@ -1,7 +1,7 @@
# flake8: noqa: F403,F405
from common import * # isort:skip
-from trezor.crypto import chacha20poly1305
+from trezor.crypto import chacha20poly1305_decrypt, chacha20poly1305_encrypt
class TestCryptoChaCha20Poly1305(unittest.TestCase):
@@ -31,14 +31,14 @@ class TestCryptoChaCha20Poly1305(unittest.TestCase):
for vector in self.vectors:
plaintext, _, key, nonce, ciphertext, _ = map(unhexlify, vector)
- ctx = chacha20poly1305(key, nonce)
+ ctx = chacha20poly1305_encrypt(key, nonce)
out = ctx.encrypt(plaintext)
self.assertEqual(out, ciphertext)
def test_chacha20_decrypt(self):
for vector in self.vectors:
plaintext, _, key, nonce, ciphertext, _ = map(unhexlify, vector)
- ctx = chacha20poly1305(key, nonce)
+ ctx = chacha20poly1305_decrypt(key, nonce)
out = ctx.decrypt(ciphertext)
self.assertEqual(out, plaintext)
@@ -46,7 +46,7 @@ class TestCryptoChaCha20Poly1305(unittest.TestCase):
for vector in self.vectors:
plaintext, aad, key, nonce, ciphertext, tag = map(unhexlify, vector)
- ctx = chacha20poly1305(key, nonce)
+ ctx = chacha20poly1305_encrypt(key, nonce)
ctx.auth(aad)
out = ctx.encrypt(plaintext)
self.assertEqual(out, ciphertext)
@@ -57,25 +57,11 @@ class TestCryptoChaCha20Poly1305(unittest.TestCase):
for vector in self.vectors:
plaintext, aad, key, nonce, ciphertext, tag = map(unhexlify, vector)
- ctx = chacha20poly1305(key, nonce)
+ ctx = chacha20poly1305_decrypt(key, nonce)
ctx.auth(aad)
out = ctx.decrypt(ciphertext)
self.assertEqual(out, plaintext)
- out = ctx.finish(tag)
- self.assertEqual(out, tag)
-
- def test_chacha20poly1305_missing_expected_mac(self):
- for vector in self.vectors:
- _, aad, key, nonce, ciphertext, _ = map(unhexlify, vector)
-
- ctx = chacha20poly1305(key, nonce)
- ctx.auth(aad)
- ctx.decrypt(ciphertext)
- with self.assertRaises(RuntimeError) as e:
- ctx.finish()
- self.assertEqual(
- e.value.value, "Argument `expected_mac` is required when decrypting."
- )
+ self.assertIsNone(ctx.finish(tag))
def test_chacha20poly1305_invalid_mac_len(self):
for vector in self.vectors:
@@ -88,7 +74,7 @@ class TestCryptoChaCha20Poly1305(unittest.TestCase):
_mac + _mac,
]
for mac in invalid_macs:
- ctx = chacha20poly1305(key, nonce)
+ ctx = chacha20poly1305_decrypt(key, nonce)
ctx.auth(aad)
ctx.decrypt(ciphertext)
with self.assertRaises(ValueError) as e:
@@ -103,7 +89,7 @@ class TestCryptoChaCha20Poly1305(unittest.TestCase):
for vector in self.vectors:
_, aad, key, nonce, ciphertext, _ = map(unhexlify, vector)
- ctx = chacha20poly1305(key, nonce)
+ ctx = chacha20poly1305_decrypt(key, nonce)
ctx.auth(aad)
ctx.decrypt(ciphertext)
with self.assertRaises(RuntimeError) as e:
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.