Complete reference for the public API shipped in v0.7.1. All symbols live
in the global Crypto object (ncrypto::CryptoEngine) unless noted.
Primary header: #include <NiusCrypto.h>
Topic shortcuts (same library, narrower includes):
| Header | Use when |
|---|---|
<NiusCrypto.h> |
Everything (recommended) |
<Aes.h> |
AES-CBC / CTR / GCM only |
<Sha.h> |
SHA-256 / 384 / 512, HKDF |
<Hmac.h> |
HMAC-SHA-256 |
<Random.h> |
random() |
<Ecc.h> |
ECDSA, ECDH, X25519, Ed25519 |
<Rsa.h> |
RSA-2048 |
See also: ARCHITECTURE.md (layers and backends), VENDORING.md (CC310 binaries), VALIDATION.md (hardware test log).
- Quick start
- Types, constants, and helpers
- Lifecycle and backend selection
- Backend capability matrix
- Global limitations
- Random number generation
- Hashing (SHA-256 / SHA-384 / SHA-512)
- HMAC-SHA-256
- HKDF-SHA-256
- AES-128 (CBC, CTR, GCM)
- ChaCha20-Poly1305 AEAD
- ECDSA and ECDH (NIST P-256)
- X25519 (Curve25519 ECDH)
- Ed25519
- RSA-2048 PKCS#1 v1.5 + SHA-256
- Packet structs and reset()
- Friendly aliases and v0.7 removals
- Advanced: direct backend access
- Error handling patterns
#include <NiusCrypto.h>
void setup() {
Serial.begin(115200);
while (!Serial) { delay(10); }
if (!Crypto.begin()) {
Serial.println(F("Crypto backend failed"));
return;
}
Serial.print(F("backend: "));
Serial.println(Crypto.backendName());
uint8_t digest[NIUS_SHA256_BYTES];
CryptoStatus s = Crypto.sha256((const uint8_t*)"hello", 5, digest);
if (NIUS_OK(s)) {
Serial.println(F("SHA-256 OK"));
} else {
Serial.print(F("error: "));
Serial.println(cryptoStatusName(s));
}
}
void loop() {}Every operation returns CryptoStatus. Check with NIUS_OK(status) or
cryptoOk(status).
| Value | Meaning |
|---|---|
Ok |
Operation succeeded |
HardwareMissing |
No backend could be started (rare with Prefer::Auto) |
NotStarted |
Crypto.begin() was not called or failed |
BadParam |
Null pointer, zero length where forbidden, wrong block size, invalid RSA handle, … |
InternalError |
Backend or hardware reported failure |
Unsupported |
Active backend cannot perform this primitive |
AuthFailed |
AEAD tag mismatch or signature verification failed |
CryptoStatus s = Crypto.sha256(data, len, out);
if (s == CryptoStatus::AuthFailed) { /* AEAD open or verify */ }
Serial.println(cryptoStatusName(s));Use these in uint8_t buf[...] declarations (from <NiusCrypto.h>):
| Macro | Bytes | Used for |
|---|---|---|
NIUS_SHA256_BYTES |
32 | SHA-256 digest, HMAC output, P-256 ECDSA hash input |
NIUS_SHA384_BYTES |
48 | SHA-384 digest |
NIUS_SHA512_BYTES |
64 | SHA-512 digest |
NIUS_AES128_KEY |
16 | AES-128 key |
NIUS_AES_BLOCK |
16 | AES block / CBC IV |
NIUS_GCM_IV |
12 | AES-GCM nonce (NIST SP 800-38D) |
NIUS_GCM_TAG |
16 | AES-GCM authentication tag |
NIUS_CHACHA_KEY |
32 | ChaCha20-Poly1305 key |
NIUS_CHACHA_NONCE |
12 | ChaCha20-Poly1305 nonce (RFC 8439) |
NIUS_CHACHA_TAG |
16 | Poly1305 tag |
NIUS_P256_PRIV |
32 | P-256 private scalar d |
NIUS_P256_PUB |
64 | P-256 public point X‖Y (uncompressed, no 0x04 prefix) |
NIUS_P256_SIG |
64 | ECDSA signature R‖S |
NIUS_P256_SHARED |
32 | ECDH shared secret |
NIUS_X25519_KEY |
32 | X25519 private or public key |
NIUS_ED25519_SEED |
32 | Ed25519 seed |
NIUS_ED25519_PUB |
32 | Ed25519 public key |
NIUS_ED25519_SECRET |
64 | Ed25519 secret (CC310 layout: seed ‖ pub) |
NIUS_ED25519_SIG |
64 | Ed25519 signature |
NIUS_RSA2048_MOD |
256 | RSA-2048 modulus buffer |
NIUS_RSA2048_SIG |
256 | RSA-2048 signature |
NIUS_RSA_MAX_EXP |
4 | RSA public exponent buffer |
All multi-byte integer fields for P-256 and RSA are big-endian unless noted. X25519 and Ed25519 keys use little-endian byte order (RFC 7748 / RFC 8032).
#define NIUS_OK(status) /* true when status == CryptoStatus::Ok */
bool cryptoOk(CryptoStatus s);
const char* cryptoStatusName(CryptoStatus s);struct RsaPublicKey {
uint8_t modulus[NIUS_RSA2048_MOD];
uint8_t exponent[NIUS_RSA_MAX_EXP];
uint16_t modLen; // actual modulus length (typically 256)
uint16_t expLen; // actual exponent length (typically 3 for 65537)
};
struct RsaKeyPair {
uint8_t slot; // backend slot id; kRsaInvalidSlot when cleared
RsaPublicKey pub; // exported public half
bool valid() const;
void clear(); // invalidate handle locally
void reset(); // alias for clear()
};Private RSA key material never leaves the CC310 backend slot. The sketch only holds a slot index plus the exported public key.
enum class Prefer : uint8_t { Auto, CC310, OnChip };
bool begin(Prefer prefer = Prefer::Auto);
void end();
bool started() const;
const char* backendName() const; // "CC310", "OnChip", or "none"
bool isHardwareAccelerated() const; // true for CC310| Method | Parameters | Returns | Description |
|---|---|---|---|
begin |
prefer — backend preference |
true if a backend started |
Brings up CC310 and/or OnChip backend |
end |
— | — | Shuts down backend; clears legacy RSA slot |
started |
— | true after successful begin |
|
backendName |
— | C string | Active backend name |
isHardwareAccelerated |
— | true when CC310 is active |
Basic usage:
Crypto.begin(); // Prefer::Auto (default)
Crypto.begin(CryptoEngine::Prefer::CC310); // fail if no vendored blob
Crypto.begin(CryptoEngine::Prefer::OnChip); // software / peripheral onlyLimitations:
Prefer::CC310returnsfalsewhen Nordic binaries are not vendored or CryptoCell does not power up.Prefer::Autoalmost always succeeds because the OnChip fallback needs no blob.- All crypto calls except none return
NotStartedifbegin()was not successful.
| API group | CC310 backend | OnChip fallback |
|---|---|---|
random |
CC310 TRNG (hardware) | nRF52840 RNG peripheral |
sha256 |
CC310 SHA-256 (hardware) | SoftSha256 (software) |
sha384 |
Oberon software (CC310 path) | Software (SoftSha512) |
sha512 |
CC310 SHA-512 (hardware) | Software (SoftSha512) |
hmacSha256 |
CC310 HMAC (hardware) | Software (SoftSha256) |
hkdfSha256 |
CC310 HKDF (hardware) | Software (SoftHkdf) |
aesCbcEncrypt |
CC310 AES-CBC (hardware) | ECB peripheral (encrypt path) |
aesCbcDecrypt |
CC310 AES-CBC (hardware) | ECB peripheral + software inverse |
aesCtr |
CC310 AES-CTR (hardware) | ECB peripheral |
sha256Stream / sha384Stream / sha512Stream |
Software streaming contexts | Software streaming contexts |
aesGcm* |
Oberon software | Software (SoftAesGcm) |
chachaPoly* |
Oberon software | Software (SoftChaChaPoly) |
ecdsa* / ecdh* |
CC310 ECC P-256 (hardware) | Unsupported |
x25519* |
CC310 Curve25519 (hardware) | Unsupported |
ed25519* |
CC310 Ed25519 (hardware) | Unsupported |
rsa* |
CC310 RSA-2048 (hardware) | Unsupported |
On the CC310 path, AES-GCM and ChaCha20-Poly1305 run in Nordic's compact
nrf_oberon library because the classic CRYS runtime does not expose those
AEAD primitives. Everything else listed as CC310 hardware uses the CryptoCell
310 accelerator via libnrf_cc310.a.
- Target: nRF52840 only (
architectures=nrf52inlibrary.properties). - Board package: Requires ArduinoNRF.
- CC310 binaries: Not redistributed; each developer runs
vendor/tools/setup_vendored.pylocally. Without them, link may fail unlessprecompiled/ldflagslines are removed fromlibrary.properties. - Float ABI: Vendored archives must be soft-float to match the ArduinoNRF core. Hard-float archives will not link or will fault at runtime.
- Backends are static singletons — no heap allocation inside the library.
- Packet structs are non-owning views; the caller supplies all buffers.
- Call crypto from
setup()/loop()or your own threads at application level. Do not call from ISRs above SoftDevice priority without external locking. - RSA: at most two live
RsaKeyPairhandles (two CC310 backend slots). CallrsaRelease()when done.
- AES-128 only (no AES-256 in this library).
- RSA-2048 only, PKCS#1 v1.5 padding with SHA-256 message digest.
- P-256 only for ECDSA/ECDH (no secp256k1 or other curves).
- No built-in padding for AES-CBC — input length must be a multiple of 16. Prefer AES-GCM or ChaCha20-Poly1305 for arbitrary-length authenticated data.
- Nonce / IV uniqueness is the caller's responsibility. Reusing a (key, nonce) pair under GCM or ChaCha20-Poly1305 breaks confidentiality.
Vendored libnrf_cc310.a and liboberon.a are covered by Nordic's 5-clause
license. They cannot be committed to git or shipped via Library Manager source
installs.
CryptoStatus random(uint8_t* buf, size_t len);| Parameter | Direction | Description |
|---|---|---|
buf |
out | Receives len random bytes |
len |
in | Number of bytes requested |
Returns: Ok, NotStarted, BadParam (null buf or len == 0), Unsupported,
InternalError.
Example:
uint8_t nonce[NIUS_GCM_IV];
if (NIUS_OK(Crypto.random(nonce, sizeof(nonce)))) {
// use nonce — must be unique per (key, message) under GCM
}Limitations: Entropy source depends on backend (TRNG vs RNG peripheral). This API does not block waiting for health tests beyond what the backend performs.
CryptoStatus sha256(const uint8_t* in, size_t len, uint8_t out[NIUS_SHA256_BYTES]);
CryptoStatus sha384(const uint8_t* in, size_t len, uint8_t out[NIUS_SHA384_BYTES]);
CryptoStatus sha512(const uint8_t* in, size_t len, uint8_t out[NIUS_SHA512_BYTES]);| Parameter | Description |
|---|---|
in |
Message bytes (nullptr allowed only when len == 0) |
len |
Message length |
out |
Receives fixed-size digest |
struct Sha256Message {
const uint8_t* data;
size_t dataLen;
uint8_t digest[NIUS_SHA256_BYTES];
void reset();
};
CryptoStatus sha256(Sha256Message& msg);Example (basic):
uint8_t digest[NIUS_SHA256_BYTES];
Crypto.sha256((const uint8_t*)"abc", 3, digest);Example (packet):
Sha256Message msg;
msg.data = (const uint8_t*)"abc";
msg.dataLen = 3;
Crypto.sha256(msg);
// msg.digest[] holds result
msg.reset();Limitations: On the CC310 path, sha384 uses Oberon software while
sha256/sha512 use the CryptoCell hash engine. On OnChip (v0.6+), all three
one-shot and streaming digest APIs are available via software implementations.
CryptoStatus hmacSha256(const uint8_t* key, size_t keyLen,
const uint8_t* msg, size_t msgLen,
uint8_t out[NIUS_SHA256_BYTES]);| Parameter | Description |
|---|---|
key, keyLen |
HMAC key (any length; nullptr only if keyLen == 0) |
msg, msgLen |
Message |
out |
32-byte MAC |
struct HmacMessage {
const uint8_t* key;
size_t keyLen;
const uint8_t* message;
size_t messageLen;
uint8_t mac[NIUS_SHA256_BYTES];
void reset();
};
CryptoStatus hmacSha256(HmacMessage& msg);Example:
static const uint8_t kKey[] = { /* ... */ };
static const uint8_t kMsg[] = "Test Using Larger Than Block-Size Key - Hash Key First";
HmacMessage mac;
mac.key = kKey;
mac.keyLen = sizeof(kKey);
mac.message = kMsg;
mac.messageLen = sizeof(kMsg) - 1; // exclude NUL if C string
Crypto.hmacSha256(mac);Limitations: When CC310 returns Unsupported for HMAC, CryptoEngine
automatically falls back to software HMAC via SoftSha256.
CryptoStatus hkdfSha256(const uint8_t* ikm, size_t ikmLen,
const uint8_t* salt, size_t saltLen,
const uint8_t* info, size_t infoLen,
uint8_t* okm, size_t okmLen);| Parameter | Description |
|---|---|
ikm |
Input keying material |
salt |
Optional salt (nullptr if saltLen == 0) |
info |
Optional context/application info (nullptr if infoLen == 0) |
okm |
Output keying material buffer |
okmLen |
Length of OKM to derive (must be > 0) |
Example (RFC 5869 test case 1):
static const uint8_t ikm[] = {0x0b, 0x0b, /* ... 22 bytes total */};
static const uint8_t salt[] = {0x00, 0x01, /* ... 13 bytes */};
static const uint8_t info[] = {0xf0, 0xf1, /* ... 10 bytes */};
uint8_t okm[42];
Crypto.hkdfSha256(ikm, sizeof(ikm), salt, sizeof(salt),
info, sizeof(info), okm, sizeof(okm));Limitations: CC310 only. RFC 5869 limits OKM length; extremely large
okmLen may fail with InternalError depending on backend limits.
CryptoStatus aesCbcEncrypt(const uint8_t key[NIUS_AES128_KEY],
const uint8_t iv[NIUS_AES_BLOCK],
const uint8_t* in, uint8_t* out, size_t len);
CryptoStatus aesCbcDecrypt(const uint8_t key[NIUS_AES128_KEY],
const uint8_t iv[NIUS_AES_BLOCK],
const uint8_t* in, uint8_t* out, size_t len);Requirements: len must be a multiple of 16. No PKCS#7 padding is applied.
struct AesCbcMessage {
uint8_t key[NIUS_AES128_KEY];
uint8_t iv[NIUS_AES_BLOCK];
const uint8_t* input;
size_t inputLen;
uint8_t* output;
void reset();
};
CryptoStatus aesCbcSeal(AesCbcMessage& msg); // encrypt
CryptoStatus aesCbcOpen(AesCbcMessage& msg); // decryptCryptoStatus aesCtr(const uint8_t key[NIUS_AES128_KEY],
const uint8_t iv[NIUS_AES_BLOCK],
const uint8_t* in, uint8_t* out, size_t len);Encrypt and decrypt are the same operation. len may be any value ≥ 0.
struct AesCtrMessage { /* key, iv, input, inputLen, output */ void reset(); };
CryptoStatus aesCtrTransform(AesCtrMessage& msg);CryptoStatus aesGcmEncrypt(const uint8_t key[NIUS_AES128_KEY],
const uint8_t iv[NIUS_GCM_IV],
const uint8_t* aad, size_t aadLen,
const uint8_t* in, uint8_t* out, size_t len,
uint8_t tag[NIUS_GCM_TAG]);
CryptoStatus aesGcmDecrypt(const uint8_t key[NIUS_AES128_KEY],
const uint8_t iv[NIUS_GCM_IV],
const uint8_t* aad, size_t aadLen,
const uint8_t* in, uint8_t* out, size_t len,
const uint8_t tag[NIUS_GCM_TAG]);| Parameter | Description |
|---|---|
iv |
12-byte nonce — must be unique per key |
aad |
Additional authenticated data (not encrypted) |
tag |
16-byte authentication tag (output on encrypt, input on decrypt) |
Returns on decrypt: AuthFailed when the tag does not match.
struct AesGcmMessage {
uint8_t key[NIUS_AES128_KEY];
uint8_t nonce[NIUS_GCM_IV];
const uint8_t* authenticatedData;
size_t authenticatedDataLen;
const uint8_t* input;
size_t inputLen;
uint8_t* output;
uint8_t authenticationTag[NIUS_GCM_TAG];
void reset();
};
CryptoStatus aesGcmSeal(AesGcmMessage& msg); // encrypt + tag
CryptoStatus aesGcmOpen(AesGcmMessage& msg); // decrypt + verify tagExample (seal then open):
AesGcmMessage gcm;
memcpy(gcm.key, myKey, NIUS_AES128_KEY);
Crypto.random(gcm.nonce, NIUS_GCM_IV);
gcm.input = plaintext;
gcm.inputLen = plainLen;
gcm.output = ciphertext;
Crypto.aesGcmSeal(gcm); // gcm.authenticationTag filled
gcm.input = ciphertext;
gcm.output = recovered;
CryptoStatus s = Crypto.aesGcmOpen(gcm);
if (s == CryptoStatus::AuthFailed) { /* tampered */ }
gcm.reset();Limitations:
| Topic | Detail |
|---|---|
| GCM on CC310 | Oberon software, not CRYS hardware |
| GCM on OnChip | Supported (v0.7.1+) via SoftAesGcm (96-bit IV) |
| CBC decrypt on OnChip | Supported (v0.6+) via ECB peripheral + software inverse |
| Empty plaintext | Allowed (len == 0); in/out may be null |
| In-place | Caller must ensure out does not overlap in unless backend allows it; use separate buffers to be safe |
Same semantics as AES-GCM but with RFC 8439 parameters.
CryptoStatus chachaPolyEncrypt(
const uint8_t key[NIUS_CHACHA_KEY],
const uint8_t nonce[NIUS_CHACHA_NONCE],
const uint8_t* aad, size_t aadLen,
const uint8_t* in, uint8_t* out, size_t len,
uint8_t tag[NIUS_CHACHA_TAG]);
CryptoStatus chachaPolyDecrypt(/* same parameters; tag is const on decrypt */);struct ChaChaPolyMessage { /* key, nonce, aad, input, output, tag */ void reset(); };
CryptoStatus chachaPolySeal(ChaChaPolyMessage& msg);
CryptoStatus chachaPolyOpen(ChaChaPolyMessage& msg);chacha20Poly1305Encrypt(...) // same as chachaPolyEncrypt
chacha20Poly1305Decrypt(...)
chacha20Poly1305Seal(ChaChaPolyMessage&)
chacha20Poly1305Open(ChaChaPolyMessage&)Limitations: CC310 uses Oberon; OnChip uses SoftChaChaPoly (RFC 8439). Nonce must be
unique per key.
CC310 only. All P-256 integers are 32-byte big-endian scalars/points.
CryptoStatus ecdsaGenerateKey(uint8_t priv[NIUS_P256_PRIV],
uint8_t pub[NIUS_P256_PUB]);CryptoStatus ecdsaSign(const uint8_t priv[NIUS_P256_PRIV],
const uint8_t hash[NIUS_SHA256_BYTES],
uint8_t sig[NIUS_P256_SIG]);
CryptoStatus ecdsaVerify(const uint8_t pub[NIUS_P256_PUB],
const uint8_t hash[NIUS_SHA256_BYTES],
const uint8_t sig[NIUS_P256_SIG]);hash must be exactly 32 bytes (typically SHA-256 of the message).
CryptoStatus ecdsaSignMessage(const uint8_t priv[NIUS_P256_PRIV],
const uint8_t* msg, size_t msgLen,
uint8_t sig[NIUS_P256_SIG]);
CryptoStatus ecdsaVerifyMessage(const uint8_t pub[NIUS_P256_PUB],
const uint8_t* msg, size_t msgLen,
const uint8_t sig[NIUS_P256_SIG]);Internally hashes with SHA-256 then signs/verifies.
struct EcdsaMessage {
uint8_t privateKey[NIUS_P256_PRIV];
uint8_t publicKey[NIUS_P256_PUB];
const uint8_t* payload; // message bytes
size_t payloadLen;
const uint8_t* hashOverride; // advanced: skip SHA-256 when non-null
uint8_t signature[NIUS_P256_SIG];
void reset();
};
CryptoStatus ecdsaGenerateKey(EcdsaMessage& msg);
CryptoStatus ecdsaSign(EcdsaMessage& msg);
CryptoStatus ecdsaVerify(EcdsaMessage& msg);Basic example:
EcdsaMessage ecdsa;
ecdsa.payload = message;
ecdsa.payloadLen = messageLen;
Crypto.ecdsaGenerateKey(ecdsa);
Crypto.ecdsaSign(ecdsa);
Crypto.ecdsaVerify(ecdsa);
ecdsa.reset();Advanced — sign a precomputed digest:
uint8_t digest[NIUS_SHA256_BYTES];
Crypto.sha256(message, messageLen, digest);
EcdsaMessage ecdsa;
memcpy(ecdsa.privateKey, priv, NIUS_P256_PRIV);
ecdsa.hashOverride = digest;
Crypto.ecdsaSign(ecdsa);CryptoStatus ecdhShared(const uint8_t priv[NIUS_P256_PRIV],
const uint8_t peerPub[NIUS_P256_PUB],
uint8_t shared[NIUS_P256_SHARED]);Example (two-party key agreement):
uint8_t aPriv[NIUS_P256_PRIV], aPub[NIUS_P256_PUB];
uint8_t bPriv[NIUS_P256_PRIV], bPub[NIUS_P256_PUB];
uint8_t sAB[NIUS_P256_SHARED], sBA[NIUS_P256_SHARED];
Crypto.ecdsaGenerateKey(aPriv, aPub);
Crypto.ecdsaGenerateKey(bPriv, bPub);
Crypto.ecdhShared(aPriv, bPub, sAB);
Crypto.ecdhShared(bPriv, aPub, sBA);
// sAB and sBA are identicalLimitations:
- OnChip: all P-256 APIs return
Unsupported. - Verification failure returns
AuthFailed(invalid signature). - No key validation (point on curve) beyond what CRYS performs internally.
CC310 only. 32-byte keys, little-endian.
CryptoStatus x25519GenerateKey(uint8_t priv[NIUS_X25519_KEY],
uint8_t pub[NIUS_X25519_KEY]);
CryptoStatus x25519Shared(const uint8_t priv[NIUS_X25519_KEY],
const uint8_t peerPub[NIUS_X25519_KEY],
uint8_t shared[NIUS_X25519_KEY]);Example:
uint8_t alicePriv[NIUS_X25519_KEY], alicePub[NIUS_X25519_KEY];
uint8_t bobPriv[NIUS_X25519_KEY], bobPub[NIUS_X25519_KEY];
uint8_t s1[NIUS_X25519_KEY], s2[NIUS_X25519_KEY];
Crypto.x25519GenerateKey(alicePriv, alicePub);
Crypto.x25519GenerateKey(bobPriv, bobPub);
Crypto.x25519Shared(alicePriv, bobPub, s1);
Crypto.x25519Shared(bobPriv, alicePub, s2);Limitations: OnChip returns Unsupported. Caller should hash shared
(e.g. with HKDF) before using as an application key.
CC310 only.
| Buffer | Size | Content |
|---|---|---|
seed |
32 | Random or deterministic seed |
pub |
32 | Public key |
secret |
64 | CRYS layout: seed ‖ publicKey |
sig |
64 | Signature |
CryptoStatus ed25519GenerateKey(uint8_t secret[NIUS_ED25519_SECRET],
uint8_t pub[NIUS_ED25519_PUB]);
CryptoStatus ed25519DeriveFromSeed(const uint8_t seed[NIUS_ED25519_SEED],
uint8_t secret[NIUS_ED25519_SECRET],
uint8_t pub[NIUS_ED25519_PUB]);
CryptoStatus ed25519Sign(const uint8_t secret[NIUS_ED25519_SECRET],
const uint8_t* msg, size_t msgLen,
uint8_t sig[NIUS_ED25519_SIG]);
CryptoStatus ed25519Verify(const uint8_t pub[NIUS_ED25519_PUB],
const uint8_t* msg, size_t msgLen,
const uint8_t sig[NIUS_ED25519_SIG]);
CryptoStatus ed25519SignFromSeed(const uint8_t seed[NIUS_ED25519_SEED],
const uint8_t* msg, size_t msgLen,
uint8_t sig[NIUS_ED25519_SIG]);ed25519SignFromSeed derives the full secret internally, then signs.
struct Ed25519Message {
uint8_t secret[NIUS_ED25519_SECRET];
uint8_t publicKey[NIUS_ED25519_PUB];
bool useSeed; // advanced path
uint8_t seed[NIUS_ED25519_SEED];
const uint8_t* payload;
size_t payloadLen;
uint8_t signature[NIUS_ED25519_SIG];
void reset();
};
CryptoStatus ed25519GenerateKey(Ed25519Message& msg);
CryptoStatus ed25519Sign(Ed25519Message& msg);
CryptoStatus ed25519Verify(Ed25519Message& msg);Basic example:
Ed25519Message ed;
ed.payload = message;
ed.payloadLen = messageLen;
Crypto.ed25519GenerateKey(ed);
Crypto.ed25519Sign(ed);
Crypto.ed25519Verify(ed);
ed.reset();Advanced — sign from stored seed only:
Ed25519Message ed;
memcpy(ed.seed, storedSeed, NIUS_ED25519_SEED);
ed.useSeed = true;
ed.payload = message;
ed.payloadLen = messageLen;
Crypto.ed25519Sign(ed);
// ed.publicKey is NOT filled by sign-from-seed; set it for verifyLimitations: OnChip returns Unsupported. When using useSeed, you must
supply the correct publicKey for verification (derive with
ed25519DeriveFromSeed if needed).
CC310 only. Signatures are 256 bytes. Message is hashed with SHA-256 inside the backend (PKCS#1 DigestInfo for SHA-256).
CryptoStatus rsaGenerateKeyPair(RsaKeyPair* key); // alias: rsaGenerate
CryptoStatus rsaSignWithKeyPair(const RsaKeyPair* key,
const uint8_t* msg, size_t msgLen,
uint8_t sig[NIUS_RSA2048_SIG]); // alias: rsaSign
CryptoStatus rsaVerifyWithKeyPair(const RsaKeyPair* key, ...); // alias: rsaVerify
CryptoStatus rsaVerifyWithPublicKey(const RsaPublicKey* pub, ...); // alias: rsaVerifyWithPubKey
CryptoStatus rsaExportPublicKey(const RsaKeyPair* key, RsaPublicKey* out); // alias: rsaExportPublic
CryptoStatus rsaReleaseKeyPair(RsaKeyPair* key); // alias: rsaReleaseExample:
RsaKeyPair key;
Crypto.rsaGenerate(&key);
uint8_t sig[NIUS_RSA2048_SIG];
Crypto.rsaSign(&key, message, messageLen, sig);
Crypto.rsaVerify(&key, message, messageLen, sig);
RsaPublicKey exported;
Crypto.rsaExportPublic(&key, &exported);
Crypto.rsaVerifyWithPubKey(&exported, message, messageLen, sig);
Crypto.rsaRelease(&key); // free backend slotLimitations:
| Topic | Detail |
|---|---|
| Key size | 2048-bit modulus only |
| Padding | PKCS#1 v1.5 with SHA-256 |
| Live keys | Maximum 2 concurrent RsaKeyPair handles |
| Private key export | Not supported — material stays in CC310 slot |
| OnChip | All RSA APIs return Unsupported |
| Verify failure | Returns AuthFailed |
All packet structs in CryptoPackets.h are non-owning views. Each provides:
void reset(); // *this = Struct{}; zeroes all fields| Struct | Seal / transform | Open / verify | Other operations |
|---|---|---|---|
AesGcmMessage |
aesGcmSeal |
aesGcmOpen |
|
AesCbcMessage |
aesCbcSeal |
aesCbcOpen |
|
AesCtrMessage |
aesCtrTransform |
(same) | |
ChaChaPolyMessage |
chachaPolySeal |
chachaPolyOpen |
|
HmacMessage |
— | — | hmacSha256(msg) |
Sha256Message |
— | — | sha256(msg) |
EcdsaMessage |
— | — | ecdsaGenerateKey, ecdsaSign, ecdsaVerify |
Ed25519Message |
— | — | ed25519GenerateKey, ed25519Sign, ed25519Verify |
RsaKeyPair::reset() clears the handle locally (does not free the backend
slot — use rsaRelease() for that).
When to call reset():
- Before reusing a struct for a different operation
- After handling sensitive intermediate state (best-effort zeroing; not guaranteed secure wipe of backend-internal key material)
| Preferred | Alias |
|---|---|
rsaGenerateKeyPair |
rsaGenerate |
rsaSignWithKeyPair |
rsaSign |
rsaVerifyWithKeyPair |
rsaVerify |
rsaVerifyWithPublicKey |
rsaVerifyWithPubKey |
rsaExportPublicKey |
rsaExportPublic |
rsaReleaseKeyPair |
rsaRelease |
chachaPolySeal / Open |
chacha20Poly1305Seal / Open |
chachaPolyEncrypt / Decrypt |
chacha20Poly1305Encrypt / Decrypt |
Message-level helpers remain available alongside packet structs:
ecdsaSignMessage/ecdsaVerifyMessageed25519SignFromSeed
Implicit RSA slot-0 APIs (rsa2048GenerateKey, rsaPkcs1Sha256Sign,
rsaPkcs1Sha256Verify, rsa2048ExportPubKey, rsaPkcs1Sha256VerifyPub) were
removed. Use explicit RsaKeyPair handles (rsaGenerate, rsaSign, …).
CryptoBackend* Crypto.backend() const;Returns the active backend pointer, or nullptr if not started. Intended for
capability probes or future extensions — most sketches should use CryptoEngine
methods only.
Do not delete or replace the backend pointer.
if (!Crypto.isHardwareAccelerated()) {
// ECDSA, RSA, GCM, etc. will return Unsupported
}CryptoStatus s = Crypto.aesGcmOpen(gcm);
switch (s) {
case CryptoStatus::Ok:
break;
case CryptoStatus::AuthFailed:
Serial.println(F("tag mismatch"));
break;
case CryptoStatus::Unsupported:
Serial.println(F("need CC310 backend"));
break;
default:
Serial.println(cryptoStatusName(s));
break;
}void logCrypto(const __FlashStringHelper* op, CryptoStatus s) {
Serial.print(op);
Serial.print(F(": "));
Serial.println(cryptoStatusName(s));
}| Sketch | APIs demonstrated |
|---|---|
CryptoSelfTest |
All primitives, KAT vectors |
Backends |
begin, capability differences |
RandomBytes |
random |
Sha256, HmacSha256, HkdfSha256 |
Hash and MAC |
Aes, ChaChaPoly1305 |
Symmetric AEAD |
EcdsaSignVerify |
EcdsaMessage |
Ed25519SignVerify |
Ed25519Message, seed path |
EcdhKeyExchange |
ecdhShared |
RsaSignExport |
RsaKeyPair, export, release |
BleCryptoStress |
CC310 under NimBLE load |
SdCryptoSmoke |
CC310 with USB CDC active |
X25519KeyExchange |
X25519Message |
KeyStorage |
seed / RSA pub persistence patterns |
if (Crypto.supports(CryptoCapability::AesGcm)) { /* ... */ }
cryptoCapabilityName(CryptoCapability::HkdfSha256);Streaming hash (software, all backends):
Sha256Context ctx;
Crypto.sha256Begin(ctx);
Crypto.sha256Update(ctx, chunk, len);
Crypto.sha256Finish(ctx, digest);
ctx.reset();Crypto.secureEqual(tagA, tagB, 16);
Crypto.wipe(secretBuf, secretLen);
size_t padded = Crypto.pkcs7Pad(plain, plainLen, out, outCap);
size_t unpadded = Crypto.pkcs7Unpad(buf, paddedLen);RsaPrivateKeyImport material;
// fill material.modulus, privateExponent, publicExponent + lengths
RsaKeyPair key;
Crypto.rsaImport(&key, &material);
Crypto.rsaPssSign(&key, msg, msgLen, sig);
Crypto.rsaPssVerifyWithPubKey(&exportedPub, msg, msgLen, sig);SelfTestReport r = Crypto.runSelfTest();
// r.passed, r.failed, r.skipped, r.ok()Sha384Message, Sha512Message, HkdfMessage, X25519Message — same
reset() pattern as existing packets.
if (st == CryptoStatus::InternalError) {
int32_t crys = Crypto.lastBackendError();
}See ONCHIP_BUILD.md and library.properties.onchip.
Document version: v0.7.1 — matches library.properties version.