2023-08-28 07:57:38 +00:00
|
|
|
import pytest
|
|
|
|
from cryptography import x509
|
|
|
|
from cryptography.hazmat.primitives import hashes
|
|
|
|
from cryptography.hazmat.primitives.asymmetric import ec
|
|
|
|
from cryptography.x509 import extensions as ext
|
|
|
|
|
2024-06-10 11:36:59 +00:00
|
|
|
from trezorlib import device, models
|
2024-12-02 14:49:04 +00:00
|
|
|
from trezorlib.debuglink import SessionDebugWrapper as Session
|
2023-08-28 07:57:38 +00:00
|
|
|
|
|
|
|
from ..common import compact_size
|
|
|
|
|
2024-09-02 11:01:16 +00:00
|
|
|
pytestmark = pytest.mark.models("safe")
|
2023-08-28 07:57:38 +00:00
|
|
|
|
2024-06-10 11:36:59 +00:00
|
|
|
ROOT_PUBLIC_KEY = {
|
|
|
|
models.T2B1: bytes.fromhex(
|
|
|
|
"047f77368dea2d4d61e989f474a56723c3212dacf8a808d8795595ef38441427c4389bc454f02089d7f08b873005e4c28d432468997871c0bf286fd3861e21e96a"
|
|
|
|
),
|
|
|
|
models.T3T1: bytes.fromhex(
|
|
|
|
"04e48b69cd7962068d3cca3bcc6b1747ef496c1e28b5529e34ad7295215ea161dbe8fb08ae0479568f9d2cb07630cb3e52f4af0692102da5873559e45e9fa72959"
|
|
|
|
),
|
2024-09-02 11:01:16 +00:00
|
|
|
models.T3B1: bytes.fromhex(
|
|
|
|
"047f77368dea2d4d61e989f474a56723c3212dacf8a808d8795595ef38441427c4389bc454f02089d7f08b873005e4c28d432468997871c0bf286fd3861e21e96a"
|
|
|
|
),
|
2024-06-10 11:36:59 +00:00
|
|
|
}
|
2023-08-28 07:57:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"challenge",
|
|
|
|
(
|
|
|
|
b"",
|
|
|
|
b"hello world",
|
|
|
|
b"\x00" * 1024,
|
|
|
|
bytes.fromhex(
|
|
|
|
"21f3d40e63c304d0312f62eb824113efd72ba1ee02bef6777e7f8a7b6f67ba16"
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
2024-12-02 14:49:04 +00:00
|
|
|
def test_authenticate_device(session: Session, challenge: bytes) -> None:
|
2023-08-28 07:57:38 +00:00
|
|
|
# NOTE Applications must generate a random challenge for each request.
|
|
|
|
|
|
|
|
# Issue an AuthenticateDevice challenge to Trezor.
|
2024-12-02 14:49:04 +00:00
|
|
|
proof = device.authenticate(session, challenge)
|
2023-08-28 07:57:38 +00:00
|
|
|
certs = [x509.load_der_x509_certificate(cert) for cert in proof.certificates]
|
|
|
|
|
|
|
|
# Verify the last certificate in the certificate chain against trust anchor.
|
|
|
|
root_public_key = ec.EllipticCurvePublicKey.from_encoded_point(
|
2024-12-02 14:49:04 +00:00
|
|
|
ec.SECP256R1(), ROOT_PUBLIC_KEY[session.model]
|
2023-08-28 07:57:38 +00:00
|
|
|
)
|
|
|
|
root_public_key.verify(
|
|
|
|
certs[-1].signature,
|
|
|
|
certs[-1].tbs_certificate_bytes,
|
|
|
|
certs[-1].signature_algorithm_parameters,
|
|
|
|
)
|
|
|
|
|
|
|
|
# Verify the certificate chain.
|
|
|
|
for cert, ca_cert in zip(certs, certs[1:]):
|
|
|
|
assert cert.issuer == ca_cert.subject
|
|
|
|
|
|
|
|
ca_basic_constraints = ca_cert.extensions.get_extension_for_class(
|
|
|
|
ext.BasicConstraints
|
|
|
|
).value
|
|
|
|
assert ca_basic_constraints.ca is True
|
|
|
|
|
|
|
|
try:
|
|
|
|
basic_constraints = cert.extensions.get_extension_for_class(
|
|
|
|
ext.BasicConstraints
|
|
|
|
).value
|
|
|
|
if basic_constraints.ca:
|
|
|
|
assert basic_constraints.path_length < ca_basic_constraints.path_length
|
|
|
|
except ext.ExtensionNotFound:
|
|
|
|
pass
|
|
|
|
|
|
|
|
ca_cert.public_key().verify(
|
|
|
|
cert.signature,
|
|
|
|
cert.tbs_certificate_bytes,
|
|
|
|
cert.signature_algorithm_parameters,
|
|
|
|
)
|
|
|
|
|
|
|
|
# Verify that the common name matches the Trezor model.
|
|
|
|
common_name = cert.subject.get_attributes_for_oid(x509.oid.NameOID.COMMON_NAME)[0]
|
2024-12-02 14:49:04 +00:00
|
|
|
if session.model == models.T3B1:
|
2024-09-02 11:01:16 +00:00
|
|
|
# XXX TODO replace as soon as we have T3B1 staging
|
|
|
|
internal_model = "T2B1"
|
|
|
|
else:
|
2024-12-02 14:49:04 +00:00
|
|
|
internal_model = session.model.internal_name
|
2024-09-02 11:01:16 +00:00
|
|
|
assert common_name.value.startswith(internal_model)
|
2023-08-28 07:57:38 +00:00
|
|
|
|
|
|
|
# Verify the signature of the challenge.
|
|
|
|
data = b"\x13AuthenticateDevice:" + compact_size(len(challenge)) + challenge
|
|
|
|
certs[0].public_key().verify(proof.signature, data, ec.ECDSA(hashes.SHA256()))
|