cryptnox-sdk-arduino 1.0.0
Arduino library for Cryptnox Hardware Wallet
Loading...
Searching...
No Matches
CryptnoxWallet.cpp
Go to the documentation of this file.
1/*
2 * SPDX-License-Identifier: LGPL-3.0-or-later
3 * Copyright (c) 2026 Cryptnox SA
4 */
5
15
16/* NOTE: Do NOT include <Arduino.h> here — this is a platform-independent file.
17 * Arduino compatibility shims (F(), HEX, delay) are provided via
18 * platform_compat.h which is pulled in transitively through CryptnoxWallet.h. */
19#include "CryptnoxWallet.h"
20#include "CW_Utils.h"
21
22/******************************************************************
23 * Constructor
24 ******************************************************************/
25
26// cppcheck-suppress misra-c2012-12.3 -- C++: member initializer-list commas are not the comma operator
29 : _logger(logger), _platform(platform), _secure(driver, logger, crypto, platform) {
30}
31
32/******************************************************************
33 * Public methods
34 ******************************************************************/
35
37 bool ret = _secure.begin();
38 if (ret) {
40 }
41 return ret;
42}
43
45 bool ret = false;
46 session.clear(); /* CRIT-04: clear any stale keys from a previous or partial session */
47
48 for (uint8_t attempt = 0U; (attempt < CW_CONNECT_MAX_ATTEMPTS) && (ret == false); attempt++) {
49 if (attempt > 0U) {
50 session.clear(); /* CRIT-04: clear partial keys left by a failed attempt before retrying */
51#if CW_DEBUG_LOGGING
52 _logger.print(F("Retrying card connection (attempt "));
53 _logger.print((uint8_t)(attempt + 1U));
54 _logger.println(F(")..."));
55#endif
56 _secure.resetReader();
57 _platform.sleep_ms(200U);
58 }
59
60 if (_secure.inListPassiveTarget()) {
61 _platform.sleep_ms(200U);
62 if (establishSecureChannel(session)) {
63 ret = true;
64 }
65 }
66 }
67
68 if (!ret) {
69 session.clear(); /* CRIT-04: clear any partial keys from the final failed attempt */
70 }
71
72 return ret;
73}
74
76 bool ret = false;
77
78 /* Declare all sensitive stack buffers at function entry so they can be
79 * wiped on every exit path (H-01, M-02). */
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 };
86 CW_Curve sessionCurve = CW_CURVE_SECP256R1;
87
88 if (_secure.selectApdu()) {
89 /* Fetch the manufacturer certificate BEFORE getCardCertificate().
90 * The Cryptnox card state machine advances after GET_CARD_CERTIFICATE
91 * (INS=F8) and will not respond to GET_MANUFACTURER_CERTIFICATE (INS=F7)
92 * after that point. Pre-fetching here caches the cert inside
93 * CW_SecureChannel so that verifyCertificateChain() can use it without
94 * issuing another APDU. */
95 if (!_secure.preFetchManufacturerCert()) {
96#if CW_DEBUG_LOGGING
97 _logger.println(F("Failed to pre-fetch manufacturer certificate"));
98#endif
99 } else {
100 if (_secure.getCardCertificate(cardCertificate, cardCertificateLength)) {
101 uint8_t certResult = _secure.verifyCertificateChain(cardCertificate,
102 cardCertificateLength);
103 if (certResult != CW_CERT_OK) {
104#if CW_DEBUG_LOGGING
105 _logger.print(F("Card authenticity check failed (code 0x"));
106 _logger.print(certResult, HEX);
107 _logger.println(F("). Aborting."));
108#endif
109 } else {
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)) {
116#if CW_DEBUG_LOGGING
117 _logger.println(F("Secure channel established"));
118#endif
119 ret = true;
120 } else {
121#if CW_DEBUG_LOGGING
122 _logger.println(F("Mutual authentication failed"));
123#endif
124 }
125 } else {
126#if CW_DEBUG_LOGGING
127 _logger.println(F("Failed to open secure channel"));
128#endif
129 }
130 } else {
131#if CW_DEBUG_LOGGING
132 _logger.println(F("Failed to extract card ephemeral key"));
133#endif
134 }
135 }
136 } else {
137#if CW_DEBUG_LOGGING
138 _logger.println(F("Failed to get card certificate"));
139#endif
140 }
141 } /* end preFetchManufacturerCert else */
142 } else {
143#if CW_DEBUG_LOGGING
144 _logger.println(F("Failed to select Cryptnox application"));
145#endif
146 }
147
148 /* Wipe all sensitive ephemeral key material on every exit path (H-01, M-02). */
149 CW_Utils::secure_wipe(clientPrivateKey, sizeof(clientPrivateKey));
150 CW_Utils::secure_wipe(openSecureChannelSalt, sizeof(openSecureChannelSalt));
151 CW_Utils::secure_wipe(clientPublicKey, sizeof(clientPublicKey));
152 CW_Utils::secure_wipe(cardEphemeralPubKey, sizeof(cardEphemeralPubKey));
153 CW_Utils::secure_wipe(cardCertificate, sizeof(cardCertificate));
154
155 return ret;
156}
157
159 if (isSecureChannelOpen(session)) {
160 session.clear();
161 }
162 _secure.resetReader();
163}
164
166 bool ret = false;
167 if (!isSecureChannelOpen(session)) {
168#if CW_DEBUG_LOGGING
169 _logger.println(F("Error: Secure channel not open. Cannot get card info."));
170#endif
171 return false;
172 }
173 uint8_t data[] = { 0x00U };
174 uint8_t apdu[] = { 0x80U, 0xFAU, 0x00U, 0x00U };
175
176 uint8_t decrypted[255U] = { 0U };
177 uint16_t decryptedLen = 0U;
178
179 ret = _secure.aesCbcEncrypt(session, apdu, sizeof(apdu),
180 data, sizeof(data),
181 decrypted, &decryptedLen);
182
183 if (ret && (info != NULL)) {
184 /* Response layout (Cryptnox basic_g1 spec):
185 * [byte0] [name_len(1)] [name(name_len)]
186 * [email_len(1)] [email(email_len)] [... more fields ...]
187 * byte0 = unused/flags. */
188 ret = false;
189 if (decryptedLen >= 4U) {
190 uint16_t pos = 1U;
191 uint8_t nameLen = decrypted[pos];
192 pos += 1U;
193 if ((nameLen <= CW_CARD_NAME_MAX_LEN) &&
194 ((uint16_t)(pos + nameLen + 1U) <= decryptedLen)) {
195 (void)CW_Utils::safe_memcpy(reinterpret_cast<uint8_t*>(info->name),
196 sizeof(info->name),
197 decrypted + pos, nameLen);
198 info->name[nameLen] = '\0';
199 pos += nameLen;
200
201 uint8_t emailLen = decrypted[pos];
202 pos += 1U;
203 if ((emailLen <= CW_CARD_EMAIL_MAX_LEN) &&
204 ((uint16_t)(pos + emailLen) <= decryptedLen)) {
205 (void)CW_Utils::safe_memcpy(reinterpret_cast<uint8_t*>(info->email),
206 sizeof(info->email),
207 decrypted + pos, emailLen);
208 info->email[emailLen] = '\0';
209 ret = true;
210 }
211 }
212 }
213 }
214
215 CW_Utils::secure_wipe(decrypted, sizeof(decrypted));
216 return ret;
217}
218
219bool CryptnoxWallet::verifyPin(CW_SecureSession& session, const uint8_t* pin, uint8_t pinLength) {
220 bool ret = false;
221 if (!isSecureChannelOpen(session)) {
222#if CW_DEBUG_LOGGING
223 _logger.println(F("Error: Secure channel not open. Cannot verify PIN."));
224#endif
225 }
226 else if ((pin == NULL) || (pinLength < CW_MIN_PIN_LENGTH) || (pinLength > CW_MAX_PIN_LENGTH)) {
227#if CW_DEBUG_LOGGING
228 _logger.println(F("Error: Invalid PIN (must be 4-9 digits)."));
229#endif
230 }
231 else {
232 uint8_t paddedPin[CW_MAX_PIN_LENGTH] = { 0U };
233 (void)CW_Utils::safe_memcpy(paddedPin, sizeof(paddedPin), pin, pinLength);
234 uint8_t apdu[] = { 0x80U, 0x20U, 0x00U, 0x00U };
235 ret = _secure.aesCbcEncrypt(session, apdu, sizeof(apdu), paddedPin, CW_MAX_PIN_LENGTH);
236 CW_Utils::secure_wipe(paddedPin, sizeof(paddedPin));
237 }
238 return ret;
239}
240
242 const uint8_t* data, uint16_t dataLength) {
243 bool ret = false;
244
245 if (!isSecureChannelOpen(session)) {
246#if CW_DEBUG_LOGGING
247 _logger.println(F("Error: Secure channel not open. Cannot write user data."));
248#endif
249 }
250 else if ((data == NULL) || (dataLength == 0U)) {
251#if CW_DEBUG_LOGGING
252 _logger.println(F("Error: Invalid data for write user data."));
253#endif
254 }
255 else {
256 uint16_t offset = 0U;
257 uint8_t page = 0U;
258 ret = true;
259
260 while ((offset < dataLength) && ret) {
261 uint16_t chunkSize = dataLength - offset;
262 if (chunkSize > CW_USER_DATA_PAGE_SIZE) {
263 chunkSize = CW_USER_DATA_PAGE_SIZE;
264 }
265
266 uint8_t apdu[] = { 0x80U, 0xFCU, slot, page };
267
268#if CW_DEBUG_LOGGING
269 _logger.print(F("Writing user data page "));
270 _logger.print(page);
271 _logger.print(F(" ("));
272 _logger.print(chunkSize);
273 _logger.println(F(" bytes)..."));
274#endif
275
276 if (!_secure.aesCbcEncrypt(session, apdu, sizeof(apdu), data + offset, chunkSize)) {
277#if CW_DEBUG_LOGGING
278 _logger.print(F("Error: Write user data failed on page "));
279 _logger.println(page);
280#endif
281 ret = false;
282 }
283 else {
284 offset += chunkSize;
285 page++;
286 }
287 }
288 }
289
290 return ret;
291}
292
294 CW_SignResult result;
295
296 if (validateSignRequest(request, result)) {
298 uint16_t dataLength = 0U;
299
300 buildSignPayload(request, data, dataLength);
301
302 uint8_t derResponse[255U] = { 0U };
303 uint16_t derLength = 0U;
304
305 if (sendSignApdu(request, data, dataLength, derResponse, derLength, result)) {
306 if (extractRawSignature(derResponse, derLength, result)) {
308 result.errorCode = CW_OK;
309 }
310 }
311 CW_Utils::secure_wipe(data, sizeof(data));
312 CW_Utils::secure_wipe(derResponse, sizeof(derResponse));
313 }
314
315 return result;
316}
317
318/******************************************************************
319 * Static public methods
320 ******************************************************************/
321
322bool CryptnoxWallet::parseDerSignature(const uint8_t* der, uint8_t derLength,
323 uint8_t* r, uint8_t& rLength,
324 uint8_t* s, uint8_t& sLength) {
325 bool ret = false;
326
327 if ((der == NULL) || (derLength < 6U) || (r == NULL) || (s == NULL)) {
328 }
329 else if (der[0] != CW_DER_TAG_SEQUENCE) {
330 }
331 else {
332 uint8_t pos = 2U;
333
334 if (der[pos] != CW_DER_TAG_INTEGER) {
335 }
336 else {
337 pos++;
338 rLength = der[pos];
339 pos++;
340 if ((rLength > 33U) || ((pos + rLength) > derLength)) {
341 }
342 else {
343 (void)CW_Utils::safe_memcpy(r, 33U, der + pos, rLength);
344 pos += rLength;
345
346 if ((pos >= derLength) || (der[pos] != CW_DER_TAG_INTEGER)) {
347 }
348 else {
349 pos++;
350 sLength = der[pos];
351 pos++;
352 if ((sLength > 33U) || ((pos + sLength) > derLength)) {
353 }
354 else {
355 (void)CW_Utils::safe_memcpy(s, 33U, der + pos, sLength);
356 ret = true;
357 }
358 }
359 }
360 }
361 }
362
363 return ret;
364}
365
366/******************************************************************
367 * Private methods
368 ******************************************************************/
369
371 uint8_t acc = 0U;
372 for (uint8_t i = 0U; i < CW_AESKEY_SIZE; i++) {
373 acc |= session.aesKey[i];
374 }
375 return (acc != 0U);
376}
377
379 return _secure.printFirmwareVersion();
380}
381
383 bool ret = false;
384
385 if (!isSecureChannelOpen(request.session)) {
386#if CW_DEBUG_LOGGING
387 _logger.println(F("Error: Secure channel not open. Cannot sign."));
388#endif
390 }
391 else if ((request.hash == NULL) || (request.hashLength == 0U)) {
392#if CW_DEBUG_LOGGING
393 _logger.println(F("Error: Invalid parameters for sign."));
394#endif
396 }
397 else if (request.hashLength > CW_HASH_SIZE) {
398#if CW_DEBUG_LOGGING
399 _logger.println(F("Error: Hash too large."));
400#endif
402 }
403 else if ((request.pinLessMode) && (request.keyType != CW_SIGN_PINLESS_K1)) {
404#if CW_DEBUG_LOGGING
405 _logger.println(F("Error: PIN-less mode requires CW_SIGN_PINLESS_K1 key type."));
406#endif
408 }
409 else {
410 ret = true;
411
412 if (!request.pinLessMode) {
413 uint8_t pinLength = 0U;
414 for (uint8_t i = 0U; i < CW_MAX_PIN_LENGTH; i++) {
415 if (request.pin[i] == 0U) { break; }
416 pinLength++;
417 }
418 if ((pinLength > 0U) && (pinLength < CW_MIN_PIN_LENGTH)) {
419#if CW_DEBUG_LOGGING
420 _logger.println(F("Error: PIN too short (must be 4-9 digits)."));
421#endif
423 ret = false;
424 }
425 }
426 }
427
428 return ret;
429}
430
432 uint8_t* data, uint16_t& dataLength) {
433 const size_t kDataBufSize = static_cast<size_t>(CW_HASH_SIZE) + static_cast<size_t>(CW_MAX_DERIVE_PATH_LENGTH) + static_cast<size_t>(CW_MAX_PIN_LENGTH);
434 dataLength = request.hashLength;
435 (void)CW_Utils::safe_memcpy(data, kDataBufSize, request.hash, request.hashLength);
436
437 if ((request.keyType == CW_SIGN_DERIVE_K1 || request.keyType == CW_SIGN_DERIVE_R1) &&
438 (request.derivePath != NULL) && (request.derivePathLength > 0U)) {
439 (void)CW_Utils::safe_memcpy(data + dataLength, kDataBufSize - static_cast<size_t>(dataLength), request.derivePath, request.derivePathLength);
440 dataLength += request.derivePathLength;
441 }
442
443 if (!request.pinLessMode) {
444 uint8_t pinLength = 0U;
445 for (uint8_t i = 0U; i < CW_MAX_PIN_LENGTH; i++) {
446 if (request.pin[i] == 0U) { break; }
447 pinLength++;
448 }
449 if (pinLength > 0U) {
450 (void)CW_Utils::safe_memcpy(data + dataLength, kDataBufSize - static_cast<size_t>(dataLength), request.pin, CW_MAX_PIN_LENGTH);
451 dataLength += CW_MAX_PIN_LENGTH;
452 }
453 }
454}
455
456bool CryptnoxWallet::sendSignApdu(CW_SignRequest& request, const uint8_t* data,
457 uint16_t dataLength, uint8_t* derResponse,
458 uint16_t& derLength, CW_SignResult& result) {
459 bool ret = false;
460 uint8_t apdu[] = { 0x80U, 0xC0U, request.keyType, request.signatureType };
461
462#if CW_DEBUG_LOGGING
463 _logger.println(F("Sending SIGN APDU..."));
464#endif
465
466 if (_secure.aesCbcEncrypt(request.session, apdu, sizeof(apdu), data, dataLength,
467 derResponse, &derLength)) {
468 ret = true;
469 }
470 else {
471#if CW_DEBUG_LOGGING
472 _logger.println(F("Sign APDU failed."));
473#endif
475 }
476
477 return ret;
478}
479
480bool CryptnoxWallet::extractRawSignature(const uint8_t* derResponse, uint16_t derLength,
481 CW_SignResult& result) {
482 bool ret = false;
483
484 if ((derLength < 2U) || (derResponse[0] != CW_DER_TAG_SEQUENCE)) {
485#if CW_DEBUG_LOGGING
486 _logger.println(F("Error: Invalid signature data (missing DER SEQUENCE tag)."));
487#endif
488 result.errorCode = CW_NOK;
489 }
490 else {
491 uint8_t derContentLength = derResponse[1];
492 uint8_t derTotalLength = 2U + derContentLength;
493
494 if (derTotalLength > derLength) {
495#if CW_DEBUG_LOGGING
496 _logger.println(F("Error: DER signature length exceeds response."));
497#endif
498 result.errorCode = CW_NOK;
499 }
500 else {
501 uint8_t r[33U] = { 0U };
502 uint8_t s[33U] = { 0U };
503 uint8_t rLen = 0U;
504 uint8_t sLen = 0U;
505
506 if (!parseDerSignature(derResponse, derTotalLength, r, rLen, s, sLen)) {
507#if CW_DEBUG_LOGGING
508 _logger.println(F("Error: Failed to parse DER signature."));
509#endif
510 result.errorCode = CW_NOK;
511 }
512 else {
513 memset(result.signature, 0U, CW_RAW_SIGNATURE_SIZE);
514
515 if (rLen > 0U) {
516 uint8_t rSrc = 0U;
517 uint8_t rDstLen = 32U;
518 if ((rLen == 33U) && (r[0] == 0x00U)) { rSrc = 1U; rLen = 32U; }
519 if (rLen <= rDstLen) {
520 (void)CW_Utils::safe_memcpy(result.signature + (rDstLen - rLen), static_cast<size_t>(rDstLen + rLen), r + rSrc, rLen);
521 }
522 }
523
524 if (sLen > 0U) {
525 uint8_t sSrc = 0U;
526 uint8_t sDstLen = 32U;
527 if ((sLen == 33U) && (s[0] == 0x00U)) { sSrc = 1U; sLen = 32U; }
528 if (sLen <= sDstLen) {
529 (void)CW_Utils::safe_memcpy(result.signature + 32U + (sDstLen - sLen), static_cast<size_t>(sLen), s + sSrc, sLen);
530 }
531 }
532
533 ret = true;
534 }
535 CW_Utils::secure_wipe(r, sizeof(r));
536 CW_Utils::secure_wipe(s, sizeof(s));
537 }
538 }
539
540 return ret;
541}
542
543void CryptnoxWallet::debugPrintSignature(const uint8_t* signature) {
544#if CW_DEBUG_LOGGING
545 _logger.print(F("Signature ("));
546 _logger.print((uint8_t)CW_RAW_SIGNATURE_SIZE);
547 _logger.println(F(" bytes):"));
548 for (uint8_t i = 0U; i < CW_RAW_SIGNATURE_SIZE; i++) {
549 _logger.print(F("0x"));
550 if (signature[i] < 0x10U) { _logger.print(F("0")); }
551 _logger.print(signature[i], HEX);
552 _logger.print(F(" "));
553 if (((i + 1U) % 16U == 0U) && ((i + 1U) != CW_RAW_SIGNATURE_SIZE)) { _logger.println(); }
554 }
555 _logger.println();
556#else
557 (void)signature;
558#endif
559}
ArduinoPlatform platform
#define CW_SIGN_PINLESS_K1
Definition CW_Defs.h:89
#define CW_NOK
Definition CW_Defs.h:81
#define CW_RAW_SIGNATURE_SIZE
Definition CW_Defs.h:107
#define CW_CONNECT_MAX_ATTEMPTS
Definition CW_Defs.h:113
#define CW_SIGN_KEY_TOO_SHORT_WITH_PINLESS_MODE
Definition CW_Defs.h:104
#define CW_USER_DATA_PAGE_SIZE
Definition CW_Defs.h:112
#define CW_SIGN_DERIVE_R1
Definition CW_Defs.h:88
#define CW_HASH_SIZE
Definition CW_Defs.h:108
#define CW_OK
Definition CW_Defs.h:80
#define CW_SIGN_NO_KEY_LOADED
Definition CW_Defs.h:102
#define CW_DER_TAG_SEQUENCE
Definition CW_Defs.h:120
#define CW_CERT_OK
Definition CW_Defs.h:127
#define CW_AESKEY_SIZE
Definition CW_Defs.h:75
#define CW_MIN_PIN_LENGTH
Definition CW_Defs.h:110
#define CW_SIGN_DERIVE_K1
Definition CW_Defs.h:87
#define CW_SIGN_PIN_INCORRECT
Definition CW_Defs.h:103
#define CW_INVALID_SESSION
Definition CW_Defs.h:82
#define CW_MAX_PIN_LENGTH
Definition CW_Defs.h:111
#define CW_MAX_DERIVE_PATH_LENGTH
Definition CW_Defs.h:109
#define CW_SIGN_KEY_TOO_SHORT
Definition CW_Defs.h:101
#define CW_DER_TAG_INTEGER
Definition CW_Defs.h:121
@ CW_CURVE_SECP256R1
Definition CW_Defs.h:152
Platform-independent security and memory utilities.
Abstract interface for cryptographic operations used by CW_SecureChannel.
Abstract interface for serial/debug output.
Definition CW_Logger.h:48
Abstract interface for NFC transport operations.
Abstract interface for platform-specific operations used by the SDK.
Definition CW_Platform.h:39
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.
Definition CW_Utils.cpp:50
static void secure_wipe(uint8_t *buf, size_t len)
Securely zero a buffer, guaranteed not to be optimised away.
Definition CW_Utils.cpp:37
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.
Definition CW_Defs.h:151
#define F(string_literal)
#define HEX
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.
Definition CW_Defs.h:168
void clear()
Securely clear all session keys and IV.
Definition CW_Defs.h:181
uint8_t aesKey[CW_AESKEY_SIZE]
Definition CW_Defs.h:169
Request parameters for CryptnoxWallet::sign.
uint8_t signatureType
CW_SecureSession & session
uint8_t derivePathLength
const uint8_t * hash
const uint8_t * derivePath
uint8_t pin[CW_MAX_PIN_LENGTH]
Result of CryptnoxWallet::sign.
uint8_t signature[CW_RAW_SIGNATURE_SIZE]