|
cryptnox-sdk-arduino 1.0.0
Arduino library for Cryptnox Hardware Wallet
|
Implements the Cryptnox secure channel protocol over NFC. More...
#include <CW_SecureChannel.h>
Public Member Functions | |
| CW_SecureChannel (CW_NfcTransport &driver, CW_Logger &logger, CW_CryptoProvider &crypto, CW_Platform &platform) | |
| Construct a CW_SecureChannel. | |
| CW_SecureChannel (const CW_SecureChannel &)=delete | |
| CW_SecureChannel & | operator= (const CW_SecureChannel &)=delete |
| bool | begin () |
| Initialize the NFC transport module. | |
| bool | inListPassiveTarget () |
| Detect a passive NFC target (ISO-DEP card). | |
| void | resetReader () |
| Reset the NFC reader hardware. | |
| bool | printFirmwareVersion () |
| Print the NFC reader firmware version to the logger. | |
| bool | selectApdu () |
| Send the SELECT APDU to activate the Cryptnox application. | |
| bool | getCardCertificate (uint8_t *cardCertificate, uint8_t &cardCertificateLength) |
| Retrieve the card's ephemeral public key via GET CARD CERTIFICATE. | |
| bool | extractCardEphemeralKey (const uint8_t *cardCertificate, uint8_t *cardEphemeralPubKey, uint8_t *fullEphemeralPubKey65=NULL) |
| Extract the card's ephemeral EC P-256 public key from a certificate. | |
| bool | openSecureChannel (uint8_t *salt, uint8_t *clientPublicKey, uint8_t *clientPrivateKey, CW_Curve sessionCurve) |
| Send OPEN SECURE CHANNEL and retrieve the session salt. | |
| bool | mutuallyAuthenticate (CW_SecureSession &session, const uint8_t *salt, uint8_t *clientPublicKey, const uint8_t *clientPrivateKey, CW_Curve sessionCurve, const uint8_t *cardEphemeralPubKey) |
| Perform ECDH derivation and MUTUALLY AUTHENTICATE with the card. | |
| bool | getManufacturerCertificate (uint8_t *cert, uint16_t &certLen) |
| Retrieve the manufacturer certificate stored in card flash. | |
| bool | preFetchManufacturerCert () |
| Fetch and cache the manufacturer certificate before getCardCertificate(). | |
| uint8_t | verifyCertificateChain (const uint8_t *cardCert, uint8_t cardCertLen) |
| Verify the full card certificate chain against the trusted CA. | |
| bool | aesCbcEncrypt (CW_SecureSession &session, const uint8_t apdu[], uint16_t apduLength, const uint8_t data[], uint16_t dataLength, uint8_t *decryptedOutput=NULL, uint16_t *decryptedOutputLength=NULL) |
| AES-CBC encrypt + MAC, send APDU, and decrypt response. | |
| bool | aesCbcDecrypt (const CW_SecureSession &session, uint8_t *response, size_t responseLen, uint8_t *macValue, uint8_t *decryptedOutput=NULL, uint16_t *decryptedOutputLength=NULL) |
| Verify MAC and decrypt an encrypted APDU response. | |
| bool | checkStatusWord (const uint8_t *response, uint16_t responseLength, uint8_t sw1Expected, uint8_t sw2Expected) |
| Verify the SW1/SW2 status word at the end of an APDU response. | |
Private Member Functions | |
| bool | verifyEcdsaSha256 (const uint8_t *pubKey64, const uint8_t *message, uint16_t msgLen, const uint8_t *derSig, uint8_t derSigLen) |
Static Private Member Functions | |
| static bool | parseDerSigToRaw (const uint8_t *der, uint8_t derLen, uint8_t *raw64) |
Private Attributes | |
| CW_NfcTransport & | _driver |
| NFC transport for APDU exchange. | |
| CW_Logger & | _logger |
| Logging interface. | |
| CW_CryptoProvider & | _crypto |
| Crypto operations (AES, SHA, ECDH, RNG). | |
| CW_Platform & | _platform |
| Platform abstraction (sleep_ms). | |
| uint8_t | _lastNonce [CW_CERT_NONCE_SIZE] |
| Nonce sent in the last getCardCertificate() call; checked in verifyCertificateChain(). | |
| uint16_t | _cachedMfCertLen |
| Non-zero when s_mfCertBuf holds a valid pre-fetched manufacturer certificate. | |
Implements the Cryptnox secure channel protocol over NFC.
Handles all low-level APDU exchanges required to establish and use a secure session with the Cryptnox smart card:
CW_SecureChannel is composed inside CryptnoxWallet and is not intended to be used directly by application code.
Definition at line 64 of file CW_SecureChannel.h.
| CW_SecureChannel::CW_SecureChannel | ( | CW_NfcTransport & | driver, |
| CW_Logger & | logger, | ||
| CW_CryptoProvider & | crypto, | ||
| CW_Platform & | platform ) |
Construct a CW_SecureChannel.
| driver | Reference to the NFC transport. |
| logger | Reference to the logging interface. |
| crypto | Reference to the crypto provider. |
| platform | Reference to the platform abstraction (for sleep_ms). |
Definition at line 87 of file CW_SecureChannel.cpp.
References _cachedMfCertLen, _crypto, _driver, _lastNonce, _logger, _platform, and platform.
Referenced by CW_SecureChannel(), and operator=().
|
delete |
References CW_SecureChannel().
| bool CW_SecureChannel::aesCbcDecrypt | ( | const CW_SecureSession & | session, |
| uint8_t * | response, | ||
| size_t | responseLen, | ||
| uint8_t * | macValue, | ||
| uint8_t * | decryptedOutput = NULL, | ||
| uint16_t * | decryptedOutputLength = NULL ) |
Verify MAC and decrypt an encrypted APDU response.
Internal helper called from aesCbcEncrypt — exposed for the fuzz harness. Verifies the response MAC against Kmac, then decrypts the payload with Kenc using the supplied IV (which is the MAC of the sent request, by protocol).
| [in,out] | session | Secure session. |
| [in] | response | Encrypted response buffer (MAC || cipher || SW). |
| [in] | responseLen | Response length. |
| [in] | macValue | MAC of the request — used as decrypt IV. |
| [out] | decryptedOutput | Optional plaintext output buffer. |
| [out] | decryptedOutputLength | Optional plaintext output length. |
Definition at line 625 of file CW_SecureChannel.cpp.
References _crypto, _logger, AES_BLOCK_SIZE, CW_SecureSession::aesKey, F, HEX, CW_SecureSession::macKey, s_apduBuf, s_dataBuf, s_macBuf, CW_Utils::safe_memcpy(), CW_Utils::secure_compare(), and CW_Utils::secure_wipe().
Referenced by aesCbcEncrypt().
| bool CW_SecureChannel::aesCbcEncrypt | ( | CW_SecureSession & | session, |
| const uint8_t | apdu[], | ||
| uint16_t | apduLength, | ||
| const uint8_t | data[], | ||
| uint16_t | dataLength, | ||
| uint8_t * | decryptedOutput = NULL, | ||
| uint16_t * | decryptedOutputLength = NULL ) |
AES-CBC encrypt + MAC, send APDU, and decrypt response.
Performs one secure messaging round-trip: pads and encrypts data with Kenc using the current IV; computes a CMAC over (header || cipher) with Kmac; sends the wrapped APDU; on the response, verifies the MAC and decrypts the payload. The new IV for the next call is taken from the last cipher block (rolling IV).
| [in,out] | session | Secure session (Kenc / Kmac / IV). |
| [in] | apdu | APDU header (CLA, INS, P1, P2). |
| [in] | apduLength | Header length (must be 4). |
| [in] | data | Plaintext payload. |
| [in] | dataLength | Plaintext length (≤ CW_USER_DATA_PAGE_SIZE). |
| [out] | decryptedOutput | Optional buffer for the decrypted response payload. |
| [out] | decryptedOutputLength | Optional pointer to receive the decrypted payload length. |
session must be the output of a successful mutuallyAuthenticate call. session.iv. On any failure the IV may be left in an undefined state — treat the session as broken and tear it down with CW_SecureSession::clear. s_apduBuf, s_macBuf, s_dataBuf). Not safe to call concurrently from multiple tasks; serialise at the application level.One secure-messaging round-trip is built in five stages, reusing module- private scratch buffers (s_apduBuf, s_macBuf, s_dataBuf) to keep the call-site stack frame small:
data so the length is a multiple of the AES block size.IV update — on success, the last 16 bytes of the response ciphertext become the new session IV (rolling IV). On any failure path the IV is left in an undefined state, which is why the caller must treat a false return as a dead session.
Definition at line 501 of file CW_SecureChannel.cpp.
References _crypto, _driver, _logger, AES_BLOCK_SIZE, aesCbcDecrypt(), CW_SecureSession::aesKey, APDU_LC_LEN, checkStatusWord(), CW_SecureSession::clear(), CW_IV_SIZE, F, HEX, INPUT_BUFFER_LIMIT, CW_SecureSession::iv, MAC_APDU_LEN, CW_SecureSession::macKey, MAX_MAC_DATA_LEN, s_apduBuf, s_dataBuf, s_macBuf, CW_Utils::safe_memcpy(), CW_Utils::secure_wipe(), and SEND_APDU_MAX_LEN.
| bool CW_SecureChannel::begin | ( | ) |
Initialize the NFC transport module.
Definition at line 100 of file CW_SecureChannel.cpp.
References _driver.
| bool CW_SecureChannel::checkStatusWord | ( | const uint8_t * | response, |
| uint16_t | responseLength, | ||
| uint8_t | sw1Expected, | ||
| uint8_t | sw2Expected ) |
Verify the SW1/SW2 status word at the end of an APDU response.
| response | APDU response buffer. |
| responseLength | Response length. |
| sw1Expected | Expected SW1 byte. |
| sw2Expected | Expected SW2 byte. |
Definition at line 120 of file CW_SecureChannel.cpp.
References _logger, F, and HEX.
Referenced by aesCbcEncrypt(), getCardCertificate(), getManufacturerCertificate(), mutuallyAuthenticate(), openSecureChannel(), and selectApdu().
| bool CW_SecureChannel::extractCardEphemeralKey | ( | const uint8_t * | cardCertificate, |
| uint8_t * | cardEphemeralPubKey, | ||
| uint8_t * | fullEphemeralPubKey65 = NULL ) |
Extract the card's ephemeral EC P-256 public key from a certificate.
| [in] | cardCertificate | Raw certificate bytes. |
| [out] | cardEphemeralPubKey | 64-byte key (X||Y, no 0x04 prefix) for ECDH. |
| [out] | fullEphemeralPubKey65 | Optional 65-byte key including 0x04 prefix. |
Definition at line 232 of file CW_SecureChannel.cpp.
| bool CW_SecureChannel::getCardCertificate | ( | uint8_t * | cardCertificate, |
| uint8_t & | cardCertificateLength ) |
Retrieve the card's ephemeral public key via GET CARD CERTIFICATE.
Sends a random challenge nonce to the card and stores it internally. The nonce echo check is performed inside verifyCertificateChain() to ensure replay protection is coupled with signature verification.
| [out] | cardCertificate | Buffer to receive the raw certificate bytes. |
| [out] | cardCertificateLength | Actual certificate length (bytes). |
Definition at line 184 of file CW_SecureChannel.cpp.
References _crypto, _driver, _lastNonce, _logger, checkStatusWord(), F, GETCARDCERTIFICATE_IN_BYTES, RANDOM_BYTES, RESPONSE_GETCARDCERTIFICATE_IN_BYTES, RESPONSE_STATUS_WORDS_IN_BYTES, and CW_Utils::safe_memcpy().
| bool CW_SecureChannel::getManufacturerCertificate | ( | uint8_t * | cert, |
| uint16_t & | certLen ) |
Retrieve the manufacturer certificate stored in card flash.
| [out] | cert | Buffer to receive the raw DER certificate bytes. |
| [out] | certLen | Actual certificate length written. |
Definition at line 1098 of file CW_SecureChannel.cpp.
References _driver, _logger, checkStatusWord(), CW_MANUF_CERT_MAX_BYTES, F, RESPONSE_GETMANUFACTURERCERT_PAGE_IN_BYTES, RESPONSE_STATUS_WORDS_IN_BYTES, and CW_Utils::safe_memcpy().
Referenced by preFetchManufacturerCert(), and verifyCertificateChain().
| bool CW_SecureChannel::inListPassiveTarget | ( | ) |
Detect a passive NFC target (ISO-DEP card).
Definition at line 104 of file CW_SecureChannel.cpp.
References _driver.
| bool CW_SecureChannel::mutuallyAuthenticate | ( | CW_SecureSession & | session, |
| const uint8_t * | salt, | ||
| uint8_t * | clientPublicKey, | ||
| const uint8_t * | clientPrivateKey, | ||
| CW_Curve | sessionCurve, | ||
| const uint8_t * | cardEphemeralPubKey ) |
Perform ECDH derivation and MUTUALLY AUTHENTICATE with the card.
Final step of the secure channel handshake:
| [out] | session | Secure session populated with derived keys + initial IV. |
| [in] | salt | 32-byte salt from openSecureChannel. |
| [in] | clientPublicKey | 64-byte client public key. |
| [in] | clientPrivateKey | 32-byte client private key. |
| [in] | sessionCurve | ECC curve. |
| [in] | cardEphemeralPubKey | 64-byte card ephemeral public key. |
session has Kenc, Kmac, and rolling IV ready for aesCbcEncrypt. On false: session is left untouched and must not be used. clientPrivateKey, salt) must be wiped after this call.Cryptographic flow:
session and wipe sharedSecret from the stack.Failure modes that cause an early-exit with a wiped session:
Definition at line 334 of file CW_SecureChannel.cpp.
References _crypto, _driver, _logger, AES_BLOCK_SIZE, CW_SecureSession::aesKey, APDU_HEADER_LEN, APDU_LC_LEN, checkStatusWord(), CW_SecureSession::clear(), COMMON_PAIRING_DATA, CW_AESKEY_SIZE, CW_IV_SIZE, CW_MACKEY_SIZE, F, CW_SecureSession::iv, CW_SecureSession::macKey, REQUEST_MUTUALLYAUTHENTICATE_IN_BYTES, RESPONSE_MUTUALLYAUTHENTICATE_IN_BYTES, CW_Utils::safe_memcpy(), and CW_Utils::secure_wipe().
| bool CW_SecureChannel::openSecureChannel | ( | uint8_t * | salt, |
| uint8_t * | clientPublicKey, | ||
| uint8_t * | clientPrivateKey, | ||
| CW_Curve | sessionCurve ) |
Send OPEN SECURE CHANNEL and retrieve the session salt.
Generates a client EC key pair via the crypto provider and sends the public key to the card; the card responds with a 32-byte salt that later feeds into Kenc / Kmac derivation in mutuallyAuthenticate.
| [out] | salt | 32-byte session salt returned by the card. |
| [out] | clientPublicKey | 64-byte freshly generated client public key. |
| [out] | clientPrivateKey | 32-byte freshly generated client private key. |
| [in] | sessionCurve | ECC curve for key generation (secp256r1). |
clientPrivateKey is sensitive — the caller MUST wipe it via CW_Utils::secure_wipe on every exit path. Definition at line 265 of file CW_SecureChannel.cpp.
References _crypto, _driver, _logger, checkStatusWord(), CLIENT_PUBLIC_KEY_SIZE, F, OPENSECURECHANNEL_SALT_IN_BYTES, RESPONSE_OPENSECURECHANNEL_IN_BYTES, and CW_Utils::safe_memcpy().
|
delete |
References CW_SecureChannel().
|
staticprivate |
Definition at line 1018 of file CW_SecureChannel.cpp.
References CW_Utils::safe_memcpy().
Referenced by DerFuzzTarget::parseDerSigToRaw(), and verifyEcdsaSha256().
| bool CW_SecureChannel::preFetchManufacturerCert | ( | ) |
Fetch and cache the manufacturer certificate before getCardCertificate().
The Cryptnox card state machine advances after GET_CARD_CERTIFICATE (INS=F8) and will not respond to GET_MANUFACTURER_CERTIFICATE (INS=F7) after that point. Call this method immediately after selectApdu() and before getCardCertificate() so that verifyCertificateChain() can use the cached copy without an APDU.
Definition at line 1194 of file CW_SecureChannel.cpp.
References _cachedMfCertLen, getManufacturerCertificate(), and s_mfCertBuf.
| bool CW_SecureChannel::printFirmwareVersion | ( | ) |
Print the NFC reader firmware version to the logger.
Definition at line 112 of file CW_SecureChannel.cpp.
References _driver.
| void CW_SecureChannel::resetReader | ( | ) |
Reset the NFC reader hardware.
Definition at line 108 of file CW_SecureChannel.cpp.
References _driver.
| bool CW_SecureChannel::selectApdu | ( | ) |
Send the SELECT APDU to activate the Cryptnox application.
Definition at line 155 of file CW_SecureChannel.cpp.
References _driver, _logger, checkStatusWord(), F, and RESPONSE_SELECT_IN_BYTES.
| uint8_t CW_SecureChannel::verifyCertificateChain | ( | const uint8_t * | cardCert, |
| uint8_t | cardCertLen ) |
Verify the full card certificate chain against the trusted CA.
Walks the cached manufacturer certificate (fetched earlier by preFetchManufacturerCert), verifies its ECDSA signature against each entry in CW_TRUSTED_CA_KEYS, then verifies the card's ephemeral certificate against the manufacturer public key. Also checks that the challenge nonce sent in getCardCertificate was echoed back inside the card certificate.
| [in] | cardCert | Raw card certificate bytes (typically 146 bytes). |
| [in] | cardCertLen | Length of cardCert. |
CW_CERT_* result codes:| CW_CERT_OK | Chain verified end-to-end. |
| CW_CERT_FORMAT_ERROR | Malformed certificate / unexpected TLV. |
| CW_CERT_NONCE_MISMATCH | Card did not echo the challenge nonce. |
| CW_CERT_CARD_SIG_INVALID | Card cert ECDSA signature failed verification. |
| CW_CERT_MANUF_SIG_INVALID | Manufacturer cert signature does not match any trusted CA key. |
| CW_CERT_KEY_NOT_FOUND | Device public-key OID not found in the certificate. |
Two-step ECDSA chain walk against the pinned trusted CAs in CW_TRUSTED_CA_KEYS (currently a single secp256r1 key, CW_CA_DLT_PUBKEY):
cardCert is parsed for the device's ephemeral public key, the manufacturer ECDSA signature over that key, and the echoed challenge nonce. The nonce is compared (constant-time) against the value stored by getCardCertificate; mismatch immediately returns CW_CERT_NONCE_MISMATCH to defeat replay. The signature is then verified against the manufacturer public key from step 1.Both signatures are SHA-256-then-ECDSA over the relevant TBS bytes. Any TLV parsing error short-circuits with CW_CERT_FORMAT_ERROR rather than touching the verifier — DER parsing is the largest attack surface here and is independently fuzzed (see fuzz/fuzz_der.cpp).
Definition at line 1227 of file CW_SecureChannel.cpp.
References _cachedMfCertLen, _lastNonce, _logger, CW_CERT_CARD_SIG_INVALID, CW_CERT_FORMAT_ERROR, CW_CERT_KEY_NOT_FOUND, CW_CERT_MANUF_SIG_INVALID, CW_CERT_NONCE_MISMATCH, CW_CERT_NONCE_SIZE, CW_CERT_OK, CW_TRUSTED_CA_COUNT, CW_TRUSTED_CA_KEYS, derWalkMfCert(), F, getManufacturerCertificate(), s_mfCertBuf, CW_Utils::secure_compare(), CW_Utils::secure_wipe(), and verifyEcdsaSha256().
|
private |
Definition at line 1082 of file CW_SecureChannel.cpp.
References _crypto, CW_CURVE_SECP256R1, and parseDerSigToRaw().
Referenced by verifyCertificateChain().
|
private |
Non-zero when s_mfCertBuf holds a valid pre-fetched manufacturer certificate.
Definition at line 322 of file CW_SecureChannel.h.
Referenced by CW_SecureChannel(), preFetchManufacturerCert(), and verifyCertificateChain().
|
private |
Crypto operations (AES, SHA, ECDH, RNG).
Definition at line 315 of file CW_SecureChannel.h.
Referenced by aesCbcDecrypt(), aesCbcEncrypt(), CW_SecureChannel(), getCardCertificate(), mutuallyAuthenticate(), openSecureChannel(), and verifyEcdsaSha256().
|
private |
NFC transport for APDU exchange.
Definition at line 313 of file CW_SecureChannel.h.
Referenced by aesCbcEncrypt(), begin(), CW_SecureChannel(), getCardCertificate(), getManufacturerCertificate(), inListPassiveTarget(), mutuallyAuthenticate(), openSecureChannel(), printFirmwareVersion(), resetReader(), and selectApdu().
|
private |
Nonce sent in the last getCardCertificate() call; checked in verifyCertificateChain().
Definition at line 319 of file CW_SecureChannel.h.
Referenced by CW_SecureChannel(), getCardCertificate(), and verifyCertificateChain().
|
private |
Logging interface.
Definition at line 314 of file CW_SecureChannel.h.
Referenced by aesCbcDecrypt(), aesCbcEncrypt(), checkStatusWord(), CW_SecureChannel(), getCardCertificate(), getManufacturerCertificate(), mutuallyAuthenticate(), openSecureChannel(), selectApdu(), and verifyCertificateChain().
|
private |
Platform abstraction (sleep_ms).
Definition at line 316 of file CW_SecureChannel.h.
Referenced by CW_SecureChannel().