mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-05-14 03:59:30 +00:00
80 lines
2.5 KiB
Python
80 lines
2.5 KiB
Python
from os import urandom
|
|
from ecdsa import SigningKey, VerifyingKey, ECDH, SECP256k1
|
|
|
|
from .. import constants
|
|
from ..card_interface import CardInterface
|
|
from ..crypto.aes import aes_cbc_encrypt
|
|
from ..crypto.generate_pairing_token import generate_pairing_token
|
|
from ..exceptions import NotSelectedError
|
|
from ..preconditions import require_selected
|
|
|
|
|
|
@require_selected
|
|
def init(
|
|
card: CardInterface,
|
|
pin: str | bytes,
|
|
puk: str | bytes,
|
|
pairing_secret: str | bytes
|
|
) -> None:
|
|
'''
|
|
Initializes a Keycard device with PIN, PUK, and pairing secret.
|
|
|
|
Establishes an ephemeral ECDH key exchange and sends encrypted
|
|
credentials to the card.
|
|
|
|
Args:
|
|
transport: The transport used to send APDU commands to the card.
|
|
card_public_key (bytes): The card's ECC public key, usually
|
|
retrieved via select().
|
|
pin (bytes): The personal identification number (PIN) as bytes.
|
|
puk (bytes): The personal unblocking key (PUK) as bytes.
|
|
pairing_secret (bytes): A 32-byte shared secret or a passphrase that
|
|
will be converted into one.
|
|
|
|
Raises:
|
|
NotSelectedError: If no card public key is provided.
|
|
ValueError: If the encrypted data exceeds a single APDU length.
|
|
APDUError: If the card returns a failure status word.
|
|
'''
|
|
if card.card_public_key is None:
|
|
raise NotSelectedError('Card not selected. Call select() first.')
|
|
|
|
if not isinstance(pin, bytes):
|
|
pin = pin.encode('ascii')
|
|
if not isinstance(puk, bytes):
|
|
puk = puk.encode('ascii')
|
|
if not isinstance(pairing_secret, bytes):
|
|
pairing_secret = generate_pairing_token(pairing_secret)
|
|
|
|
ephemeral_key = SigningKey.generate(curve=SECP256k1)
|
|
our_pubkey_bytes: bytes = \
|
|
ephemeral_key.verifying_key.to_string('uncompressed')
|
|
card_pubkey = VerifyingKey.from_string(
|
|
card.card_public_key,
|
|
curve=SECP256k1
|
|
)
|
|
ecdh = ECDH(
|
|
curve=SECP256k1,
|
|
private_key=ephemeral_key,
|
|
public_key=card_pubkey
|
|
)
|
|
shared_secret = ecdh.generate_sharedsecret_bytes()
|
|
|
|
plaintext: bytes = pin + puk + pairing_secret
|
|
iv: bytes = urandom(16)
|
|
ciphertext: bytes = aes_cbc_encrypt(shared_secret, iv, plaintext)
|
|
data: bytes = (
|
|
bytes([len(our_pubkey_bytes)])
|
|
+ our_pubkey_bytes
|
|
+ iv
|
|
+ ciphertext
|
|
)
|
|
|
|
if len(data) > 255:
|
|
raise ValueError('Data too long for single APDU')
|
|
|
|
card.send_apdu(
|
|
ins=constants.INS_INIT,
|
|
data=data
|
|
)
|