52 _logger.print(
F(
"Retrying card connection (attempt "));
53 _logger.print((uint8_t)(attempt + 1U));
60 if (
_secure.inListPassiveTarget()) {
80 uint8_t cardCertificate[146U] = { 0U };
81 uint8_t cardCertificateLength = 0U;
82 uint8_t cardEphemeralPubKey[64U] = { 0U };
83 uint8_t openSecureChannelSalt[32U] = { 0U };
84 uint8_t clientPrivateKey[32U] = { 0U };
85 uint8_t clientPublicKey[64U] = { 0U };
95 if (!
_secure.preFetchManufacturerCert()) {
97 _logger.println(
F(
"Failed to pre-fetch manufacturer certificate"));
100 if (
_secure.getCardCertificate(cardCertificate, cardCertificateLength)) {
101 uint8_t certResult =
_secure.verifyCertificateChain(cardCertificate,
102 cardCertificateLength);
105 _logger.print(
F(
"Card authenticity check failed (code 0x"));
110 if (
_secure.extractCardEphemeralKey(cardCertificate, cardEphemeralPubKey)) {
111 if (
_secure.openSecureChannel(openSecureChannelSalt, clientPublicKey,
112 clientPrivateKey, sessionCurve)) {
113 if (
_secure.mutuallyAuthenticate(session, openSecureChannelSalt,
114 clientPublicKey, clientPrivateKey,
115 sessionCurve, cardEphemeralPubKey)) {
117 _logger.println(
F(
"Secure channel established"));
122 _logger.println(
F(
"Mutual authentication failed"));
127 _logger.println(
F(
"Failed to open secure channel"));
132 _logger.println(
F(
"Failed to extract card ephemeral key"));
138 _logger.println(
F(
"Failed to get card certificate"));
144 _logger.println(
F(
"Failed to select Cryptnox application"));
169 _logger.println(
F(
"Error: Secure channel not open. Cannot get card info."));
173 uint8_t data[] = { 0x00U };
174 uint8_t apdu[] = { 0x80U, 0xFAU, 0x00U, 0x00U };
176 uint8_t decrypted[255U] = { 0U };
177 uint16_t decryptedLen = 0U;
179 ret =
_secure.aesCbcEncrypt(session, apdu,
sizeof(apdu),
181 decrypted, &decryptedLen);
183 if (ret && (info != NULL)) {
189 if (decryptedLen >= 4U) {
191 uint8_t nameLen = decrypted[pos];
194 ((uint16_t)(pos + nameLen + 1U) <= decryptedLen)) {
197 decrypted + pos, nameLen);
198 info->
name[nameLen] =
'\0';
201 uint8_t emailLen = decrypted[pos];
204 ((uint16_t)(pos + emailLen) <= decryptedLen)) {
207 decrypted + pos, emailLen);
208 info->
email[emailLen] =
'\0';
223 _logger.println(
F(
"Error: Secure channel not open. Cannot verify PIN."));
228 _logger.println(
F(
"Error: Invalid PIN (must be 4-9 digits)."));
234 uint8_t apdu[] = { 0x80U, 0x20U, 0x00U, 0x00U };
242 const uint8_t* data, uint16_t dataLength) {
247 _logger.println(
F(
"Error: Secure channel not open. Cannot write user data."));
250 else if ((data == NULL) || (dataLength == 0U)) {
252 _logger.println(
F(
"Error: Invalid data for write user data."));
256 uint16_t offset = 0U;
260 while ((offset < dataLength) && ret) {
261 uint16_t chunkSize = dataLength - offset;
266 uint8_t apdu[] = { 0x80U, 0xFCU, slot, page };
269 _logger.print(
F(
"Writing user data page "));
276 if (!
_secure.aesCbcEncrypt(session, apdu,
sizeof(apdu), data + offset, chunkSize)) {
278 _logger.print(
F(
"Error: Write user data failed on page "));
298 uint16_t dataLength = 0U;
302 uint8_t derResponse[255U] = { 0U };
303 uint16_t derLength = 0U;
305 if (
sendSignApdu(request, data, dataLength, derResponse, derLength, result)) {
323 uint8_t* r, uint8_t& rLength,
324 uint8_t* s, uint8_t& sLength) {
327 if ((der == NULL) || (derLength < 6U) || (r == NULL) || (s == NULL)) {
340 if ((rLength > 33U) || ((pos + rLength) > derLength)) {
352 if ((sLength > 33U) || ((pos + sLength) > derLength)) {
379 return _secure.printFirmwareVersion();
387 _logger.println(
F(
"Error: Secure channel not open. Cannot sign."));
393 _logger.println(
F(
"Error: Invalid parameters for sign."));
399 _logger.println(
F(
"Error: Hash too large."));
405 _logger.println(
F(
"Error: PIN-less mode requires CW_SIGN_PINLESS_K1 key type."));
413 uint8_t pinLength = 0U;
415 if (request.
pin[i] == 0U) {
break; }
420 _logger.println(
F(
"Error: PIN too short (must be 4-9 digits)."));
432 uint8_t* data, uint16_t& dataLength) {
444 uint8_t pinLength = 0U;
446 if (request.
pin[i] == 0U) {
break; }
449 if (pinLength > 0U) {
457 uint16_t dataLength, uint8_t* derResponse,
463 _logger.println(
F(
"Sending SIGN APDU..."));
466 if (
_secure.aesCbcEncrypt(request.
session, apdu,
sizeof(apdu), data, dataLength,
467 derResponse, &derLength)) {
472 _logger.println(
F(
"Sign APDU failed."));
486 _logger.println(
F(
"Error: Invalid signature data (missing DER SEQUENCE tag)."));
491 uint8_t derContentLength = derResponse[1];
492 uint8_t derTotalLength = 2U + derContentLength;
494 if (derTotalLength > derLength) {
496 _logger.println(
F(
"Error: DER signature length exceeds response."));
501 uint8_t r[33U] = { 0U };
502 uint8_t s[33U] = { 0U };
508 _logger.println(
F(
"Error: Failed to parse DER signature."));
517 uint8_t rDstLen = 32U;
518 if ((rLen == 33U) && (r[0] == 0x00U)) { rSrc = 1U; rLen = 32U; }
519 if (rLen <= rDstLen) {
526 uint8_t sDstLen = 32U;
527 if ((sLen == 33U) && (s[0] == 0x00U)) { sSrc = 1U; sLen = 32U; }
528 if (sLen <= sDstLen) {
550 if (signature[i] < 0x10U) {
_logger.print(
F(
"0")); }
#define CW_SIGN_PINLESS_K1
#define CW_RAW_SIGNATURE_SIZE
#define CW_CONNECT_MAX_ATTEMPTS
#define CW_SIGN_KEY_TOO_SHORT_WITH_PINLESS_MODE
#define CW_USER_DATA_PAGE_SIZE
#define CW_SIGN_DERIVE_R1
#define CW_SIGN_NO_KEY_LOADED
#define CW_DER_TAG_SEQUENCE
#define CW_MIN_PIN_LENGTH
#define CW_SIGN_DERIVE_K1
#define CW_SIGN_PIN_INCORRECT
#define CW_INVALID_SESSION
#define CW_MAX_PIN_LENGTH
#define CW_MAX_DERIVE_PATH_LENGTH
#define CW_SIGN_KEY_TOO_SHORT
#define CW_DER_TAG_INTEGER
Platform-independent security and memory utilities.
Abstract interface for cryptographic operations used by CW_SecureChannel.
Abstract interface for serial/debug output.
Abstract interface for NFC transport operations.
static bool safe_memcpy(uint8_t *dst, size_t dstSize, const uint8_t *src, size_t count)
Safe memcpy — validates pointers, sizes, and checks for overlap.
static void secure_wipe(uint8_t *buf, size_t len)
Securely zero a buffer, guaranteed not to be optimised away.
static bool parseDerSignature(const uint8_t *der, uint8_t derLength, uint8_t *r, uint8_t &rLength, uint8_t *s, uint8_t &sLength)
Parse a DER-encoded ECDSA signature to extract raw r and s values.
CW_Platform & _platform
Platform abstraction (sleep_ms).
bool isSecureChannelOpen(const CW_SecureSession &session) const
void debugPrintSignature(const uint8_t *signature)
CW_SecureChannel _secure
Owned secure channel.
CryptnoxWallet(CW_NfcTransport &driver, CW_Logger &logger, CW_CryptoProvider &crypto, CW_Platform &platform)
Construct a CryptnoxWallet.
bool writeUserData(CW_SecureSession &session, uint8_t slot, const uint8_t *data, uint16_t dataLength)
Write data to a user memory slot, paginating in CW_USER_DATA_PAGE_SIZE chunks.
CW_Logger & _logger
Logging interface.
CW_SignResult sign(CW_SignRequest &request)
Sign a 32-byte digest using a card-resident key.
void disconnect(CW_SecureSession &session)
Disconnect and securely clear the session.
bool extractRawSignature(const uint8_t *derResponse, uint16_t derLength, CW_SignResult &result)
bool getCardInfo(CW_SecureSession &session, CW_CardInfo *info=NULL)
Send a secured GET CARD INFO APDU (0x80FA0000) and optionally decode the owner name/email from the re...
bool establishSecureChannel(CW_SecureSession &session)
Establish a secure channel (SELECT → certificate → ECDH → mutual auth).
bool connect(CW_SecureSession &session)
Connect to the Cryptnox card and establish a secure channel.
bool printPN532FirmwareVersion()
bool validateSignRequest(const CW_SignRequest &request, CW_SignResult &result)
bool begin()
Initialize the NFC module via the underlying transport driver.
bool sendSignApdu(CW_SignRequest &request, const uint8_t *data, uint16_t dataLength, uint8_t *derResponse, uint16_t &derLength, CW_SignResult &result)
bool verifyPin(CW_SecureSession &session, const uint8_t *pin, uint8_t pinLength)
Verify the PIN code on the card.
void buildSignPayload(const CW_SignRequest &request, uint8_t *data, uint16_t &dataLength)
High-level API for interacting with a Cryptnox Hardware Wallet over NFC.
#define CW_CARD_EMAIL_MAX_LEN
Max email length stored on a Cryptnox card (per card spec).
#define CW_CARD_NAME_MAX_LEN
Max name length stored on a Cryptnox card (per card spec).
CW_Curve
Portable curve identifier used throughout the SDK.
Subset of the Cryptnox card info returned by APDU 0x80FA0000.
char name[CW_CARD_NAME_MAX_LEN+1U]
char email[CW_CARD_EMAIL_MAX_LEN+1U]
Holds cryptographic session state for reentrant secure channel operations.
void clear()
Securely clear all session keys and IV.
uint8_t aesKey[CW_AESKEY_SIZE]
Request parameters for CryptnoxWallet::sign.
CW_SecureSession & session
const uint8_t * derivePath
uint8_t pin[CW_MAX_PIN_LENGTH]
Result of CryptnoxWallet::sign.
uint8_t signature[CW_RAW_SIGNATURE_SIZE]