Cryptography
AXL’s cryptographic primitives are spread across a few headers by
dependency, not gathered behind one libcrypto-style include. This
page is the map.
Two tiers:
Standalone primitives have no external dependency and are available in every build — hashing, HMAC, randomness, and Authenticode image verification.
mbedTLS-backed primitives require an
AXL_TLS=1build (which links the vendored mbedTLS): TLS and public-key signature verification. WithoutAXL_TLS=1they fail closed (seeaxl_pk_available()/axl_tls_available()).
<axl/axl-crypto.h> is the home for mbedTLS-backed primitives beyond
TLS; it grows on demand as consumers need them (today: public-key
signature verification).
Primitive |
Header |
Backend |
Reference |
|---|---|---|---|
Hashing & checksums (MD5, SHA-1, SHA-256, CRC-32, Adler-32) |
|
standalone |
|
HMAC (RFC 2104) |
|
standalone |
|
Cryptographic random bytes ( |
|
standalone |
|
Deterministic PRNG ( |
|
standalone |
|
Authenticode image-signature verification |
|
standalone |
|
TLS 1.2 server / client |
|
needs |
|
Public-key signature verification (ECDSA-P256) |
|
needs |
below |
Public-key signature verification
axl_pk_verify checks a detached signature over a message against a
public key the consumer ships — the building block for signed firmware
updates, signed config blobs, or Secure-Boot-style image checks. It is
pure verification: there is no signing side (the private key is held
offline by the vendor and never ships).
AXL_PK_ECDSA_P256 (ECDSA over NIST P-256 with SHA-256) is supported:
the public key is a DER SubjectPublicKeyInfo, the signature is a DER
ECDSA signature, and the message is hashed with SHA-256 internally.
AXL_PK_ED25519 is reserved but unsupported by the current mbedTLS
build (which does not enable PSA crypto) and returns AXL_ERR.
Any non-AXL_OK result means “not verified, untrusted” — fail closed.
Use axl_pk_available() to distinguish “verification not compiled in”
from “signature invalid”.
Defines
-
AXL_AEAD_NONCE_LEN
Required nonce length, all algorithms.
-
AXL_AEAD_TAG_LEN
Authentication tag length.
-
AXL_CIPHER_CTR_IV_LEN
AES-CTR initial counter block size.
Typedefs
-
typedef struct AxlPkKey AxlPkKey
An opaque public-key key pair (or public-only key).
Holds a private key (from axl_pk_keygen / axl_pk_key_load_private) or a public key only (from axl_pk_key_load_public). Free with axl_pk_key_free().
Enums
-
enum AxlPkAlg
Public-key signature algorithm selector.
axl-crypto.h:
Generic public-key signature verification. A detached-signature verifier over the mbedTLS that AXL links when built with AXL_TLS=1.
This is a low-level cryptographic primitive — the building block for verifying signed firmware updates, signed configuration blobs, or Secure-Boot-style image checks against a public key the consumer ships. There is no signing side: signing is done offline by the vendor and the private key never ships in the binary.
Optional — like axl-tls.h
, the real implementation requires AXL_TLS=1 at build time. Without it, axl_pk_available() returns false and axl_pk_verify() returns AXL_ERR (fail-closed). Use axl_pk_available() to distinguish “verification not compiled in” from “signature
invalid”.
// pubkey: DER SubjectPublicKeyInfo baked into the image at build time. // sig: detached DER ECDSA signature over msg (ECDSA-with-SHA-256). if (axl_pk_verify(AXL_PK_ECDSA_P256, pubkey, pubkey_len, msg, msg_len, sig, sig_len) == AXL_OK) { // signature is valid — msg is authentic }The numeric values are part of the contract (a consumer may persist the choice alongside a signed blob); new algorithms only extend the range.
Values:
-
enumerator AXL_PK_ED25519
Ed25519 — reserved; NOT supported by the current mbedTLS build (requires PSA crypto, which AXL does not enable). axl_pk_verify() returns AXL_ERR for this algorithm. Because this is value 0, a zero-initialized selector fails closed (nothing verifies) — always set AXL_PK_ECDSA_P256 explicitly.
-
enumerator AXL_PK_ECDSA_P256
ECDSA over NIST P-256 (prime256v1) with SHA-256.
-
enumerator AXL_PK_RSA
RSA with PKCS#1 v1.5 over SHA-256 (rsa-sha2-256). Keygen produces a 3072-bit key. Supported by the key-handle API (axl_pk_keygen / _sign / _verify); NOT by the raw-bytes axl_pk_verify() below.
-
enumerator AXL_PK_ECDSA_P384
ECDSA over NIST P-384 (secp384r1) with SHA-384 (the hash follows the curve). Like P-256 it is supported by the key-handle API (axl_pk_keygen / _sign / _verify); NOT by the raw-bytes axl_pk_verify(). Its AXL_PK_SIG_RAW signature is r||s = 96 bytes (48-byte order each).
-
enumerator AXL_PK_ED25519
-
enum AxlPkSigFormat
Detached ECDSA signature byte layout.
Values:
-
enumerator AXL_PK_SIG_DER
ASN.1 DER: SEQUENCE { INTEGER r, INTEGER s } (OpenSSL / X.509 form). The format the raw-bytes axl_pk_verify() expects.
-
enumerator AXL_PK_SIG_RAW
Fixed-width r || s, each big-endian and left-padded to the curve order size (64 bytes for P-256, 96 for P-384). The form SSH, JWS, and COSE use. Ignored for RSA (its signature has a single encoding).
-
enumerator AXL_PK_SIG_DER
-
enum AxlAeadAlg
AEAD algorithm selector.
Values:
-
enumerator AXL_AEAD_AES_128_GCM
AES-128-GCM (16-byte key).
-
enumerator AXL_AEAD_AES_256_GCM
AES-256-GCM (32-byte key).
-
enumerator AXL_AEAD_CHACHA20_POLY1305
ChaCha20-Poly1305 (32-byte key).
-
enumerator AXL_AEAD_AES_128_GCM
-
enum AxlEcdhAlg
ECDH curve selector.
Values:
-
enumerator AXL_ECDH_P256
NIST P-256. Public key is the uncompressed SEC1 point 0x04||X||Y (65 bytes); shared secret is the X coordinate (32 bytes).
-
enumerator AXL_ECDH_X25519
X25519 (Curve25519). Public key and shared secret are 32 bytes each (RFC 7748).
-
enumerator AXL_ECDH_P256
Functions
-
bool axl_pk_available(void)
Whether public-key signature verification was compiled in.
True only when AXL was built with AXL_TLS=1 (which links mbedTLS). When false, axl_pk_verify() always returns AXL_ERR regardless of input — callers that must fail closed on a missing crypto backend can branch on this to log the distinction.
- Returns:
true if axl_pk_verify() can verify signatures.
-
int axl_pk_verify(AxlPkAlg alg, const uint8_t *pubkey, size_t pubkey_len, const uint8_t *msg, size_t msg_len, const uint8_t *sig, size_t sig_len)
Verify a detached signature over a message with a public key.
Pure verification — there is no signing side in this API.
Encodings (for AXL_PK_ECDSA_P256):
pubkeyis a DER-encoded SubjectPublicKeyInfo for a P-256 key (the output ofopenssl ec -pubout -outform DER). PEM is not accepted — convert to DER first.sigis a DER-encoded ECDSA signature: SEQUENCE { INTEGER r, INTEGER s } (the output ofopenssl dgst -sha256 -sign). It must contain exactly the signature, with no trailing bytes.msgis the raw message bytes; this function computes SHA-256(msg) internally (ECDSA-with-SHA-256).msgmay be NULL only whenmsg_lenis 0.
AXL_PK_ED25519 is reserved and unsupported by this build; it returns AXL_ERR.
Operates only on public inputs; there is no secret to leak. Any parse or verify failure is reported uniformly as AXL_ERR — a caller must treat every non-AXL_OK result as “not verified, untrusted” and fail closed. Do NOT branch security decisions on the reason for failure; the only distinction the API offers is axl_pk_available(), which separates “verification not compiled in” from “invalid”.
- Parameters:
alg – signature algorithm
pubkey – public key bytes (encoding per
alg)pubkey_len – public key length in bytes (> 0)
msg – message bytes (NULL iff
msg_len== 0)msg_len – message length in bytes
sig – detached signature bytes
sig_len – signature length in bytes (> 0)
- Returns:
AXL_OK if the signature is valid for
msgunderpubkey; AXL_ERR otherwise — invalid signature, malformed key/signature, NULL/zero-length key or signature, unsupported algorithm, or verification not compiled in (see axl_pk_available()).
-
AxlPkKey *axl_pk_keygen(AxlPkAlg alg)
Generate a new key pair.
AXL_PK_ECDSA_P256 generates a NIST P-256 key; AXL_PK_ECDSA_P384 a NIST P-384 key; AXL_PK_RSA generates a 3072-bit RSA key (slower — seconds on some firmware, but a one-time cost for a persisted host key). AXL_PK_ED25519 is unsupported and returns NULL.
- Parameters:
alg – key algorithm to generate
- Returns:
a new private key handle (caller frees with axl_pk_key_free), or NULL on failure / unsupported algorithm / TLS not compiled in.
-
AxlPkKey *axl_pk_key_load_private(const uint8_t *der, size_t len)
Load a private key from its PKCS#8 DER encoding.
deris an unencrypted PKCS#8 PrivateKeyInfo (the output ofopenssl pkcs8 -topk8 -nocrypt -outform DER, and of axl_pk_key_get_private_der()).- Parameters:
der – PKCS#8 DER private key
len – length in bytes (> 0)
- Returns:
a new key handle, or NULL on malformed input / unsupported key type / TLS not compiled in.
-
AxlPkKey *axl_pk_key_load_public(const uint8_t *der, size_t len)
Load a public key from its SubjectPublicKeyInfo DER encoding.
Same encoding the raw-bytes axl_pk_verify() accepts. The resulting handle can verify but not sign.
- Parameters:
der – SubjectPublicKeyInfo DER public key
len – length in bytes (> 0)
- Returns:
a new key handle, or NULL on malformed input / unsupported key type / TLS not compiled in.
-
int axl_pk_key_get_private_der(const AxlPkKey *key, uint8_t *out, size_t *len)
Serialize a key’s private half as PKCS#8 DER.
Output-buffer protocol (shared by axl_pk_key_get_public_der and axl_pk_key_sign):
out== NULL: a size query — writes the exact required size to*lenand returns AXL_OK.out!= NULL:*lenis the buffer capacity on entry. On success*lenis set to the bytes written. If the buffer is too small (including*len== 0), returns AXL_ERR, sets*lento the required size, and leavesoutuntouched — so the caller can re-call with a buffer of that size (the same*lenmay be passed straight through from a prior size query).
Fails if the handle has no private key.
- Parameters:
key – key handle (must hold a private key)
out – output buffer, or NULL to size-query
len – [in/out] buffer capacity / bytes written
- Returns:
AXL_OK on success; AXL_ERR on a public-only key, a too-small buffer, bad args, or TLS not compiled in.
-
int axl_pk_key_get_public_der(const AxlPkKey *key, uint8_t *out, size_t *len)
Serialize a key’s public half as SubjectPublicKeyInfo DER.
Output-buffer protocol as in axl_pk_key_get_private_der().
- Parameters:
key – key handle
out – output buffer, or NULL to size-query
len – [in/out] buffer capacity / bytes written
- Returns:
AXL_OK on success; AXL_ERR on a too-small buffer, bad args, or TLS not compiled in.
-
AxlPkAlg axl_pk_key_alg(const AxlPkKey *key)
Report a key handle’s algorithm.
- Parameters:
key – key handle
- Returns:
the AxlPkAlg of
key(AXL_PK_ECDSA_P256, AXL_PK_ECDSA_P384, or AXL_PK_RSA). Returns AXL_PK_ED25519 (the reserved zero value) for a NULL handle.
-
int axl_pk_key_sign(const AxlPkKey *key, const uint8_t *msg, size_t msg_len, AxlPkSigFormat fmt, uint8_t *sig, size_t *sig_len)
Sign a message with a private key.
Computes SHA-256(
msg) and signs it. For ECDSA,fmtselects the signature byte layout (AXL_PK_SIG_RAW for SSH/JWS/COSE, AXL_PK_SIG_DER for X.509-style); for RSAfmtis ignored.Uses the output-buffer protocol of axl_pk_key_get_private_der() on
sig/sig_len(passsig== NULL to size-query). Note ECDSA DER signatures are variable-length (r/s leading-zero trimming), so a size query reports a safe upper bound; AXL_PK_SIG_RAW is fixed-width (64 bytes for P-256).- Parameters:
key – signing key (must hold a private key)
msg – message bytes (NULL iff
msg_len== 0)msg_len – message length in bytes
fmt – ECDSA signature layout (ignored for RSA)
sig – output buffer, or NULL to size-query
sig_len – [in/out] buffer capacity / bytes written
- Returns:
AXL_OK on success; AXL_ERR on a public-only key, a too-small buffer, bad args, or TLS not compiled in.
-
int axl_pk_key_verify(const AxlPkKey *key, const uint8_t *msg, size_t msg_len, AxlPkSigFormat fmt, const uint8_t *sig, size_t sig_len)
Verify a detached signature with a key handle.
The handle-based peer of the raw-bytes axl_pk_verify(): verifies a signature over SHA-256(
msg) usingkey(private or public). For ECDSA,fmtmust match how the signature was produced, and for AXL_PK_SIG_RAWsig_lenmust be exactly the curve’s r||s size (64 bytes for P-256). For RSA,fmtis ignored.Any non-AXL_OK result means “not verified, untrusted” — fail closed.
- Parameters:
key – verifying key
msg – message bytes (NULL iff
msg_len== 0)msg_len – message length in bytes
fmt – ECDSA signature layout (ignored for RSA)
sig – detached signature bytes
sig_len – signature length in bytes (> 0)
- Returns:
AXL_OK if the signature is valid; AXL_ERR otherwise.
-
void axl_pk_key_free(AxlPkKey *key)
Free a key handle. NULL-safe. Zeroizes private key material.
- Parameters:
key – key handle to free
-
int axl_aead_seal(AxlAeadAlg alg, const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t nonce_len, const uint8_t *aad, size_t aad_len, const uint8_t *plaintext, size_t pt_len, uint8_t *ciphertext, uint8_t *tag, size_t tag_len)
Encrypt and authenticate a message (AEAD seal).
Encrypts
plaintextand computes an authentication tag over the ciphertext andaad. The ciphertext is the same length as the plaintext; the tag is returned separately.ciphertextmay aliasplaintextfor in-place encryption.plaintext/aadmay be NULL only when their length is 0.key_lenmust matchalg(16 for AES-128, 32 for AES-256 and ChaCha20-Poly1305).nonce_lenmust be AXL_AEAD_NONCE_LEN andtag_lenAXL_AEAD_TAG_LEN. The caller MUST NOT reuse a (key,nonce) pair across messages.- Parameters:
alg – AEAD algorithm
key – key (length per
alg)key_len – key length in bytes
nonce – nonce (AXL_AEAD_NONCE_LEN bytes)
nonce_len – nonce length (must be AXL_AEAD_NONCE_LEN)
aad – associated data (NULL iff aad_len == 0)
aad_len – associated-data length
plaintext – plaintext (NULL iff pt_len == 0)
pt_len – plaintext length
ciphertext – [out] ciphertext, pt_len bytes (may alias plaintext)
tag – [out] tag (AXL_AEAD_TAG_LEN bytes)
tag_len – tag buffer length (must be AXL_AEAD_TAG_LEN)
- Returns:
AXL_OK on success; AXL_ERR on bad args, a key/nonce/tag length mismatch, or TLS not compiled in.
-
int axl_aead_open(AxlAeadAlg alg, const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t nonce_len, const uint8_t *aad, size_t aad_len, const uint8_t *ciphertext, size_t ct_len, const uint8_t *tag, size_t tag_len, uint8_t *plaintext)
Verify and decrypt a message (AEAD open).
Checks the tag over
ciphertextandaadand, only if it is valid, writes the plaintext. On any authentication failure returns AXL_ERR and writes no plaintext (fail closed) — a caller must treat AXL_ERR as “not authentic, discard”.plaintextmay aliasciphertext.Length requirements match axl_aead_seal().
- Parameters:
alg – AEAD algorithm
key – key (length per
alg)key_len – key length in bytes
nonce – nonce (AXL_AEAD_NONCE_LEN bytes)
nonce_len – nonce length (must be AXL_AEAD_NONCE_LEN)
aad – associated data (NULL iff aad_len == 0)
aad_len – associated-data length
ciphertext – ciphertext (NULL iff ct_len == 0)
ct_len – ciphertext length
tag – tag (AXL_AEAD_TAG_LEN bytes)
tag_len – tag length (must be AXL_AEAD_TAG_LEN)
plaintext – [out] plaintext, ct_len bytes (may alias ciphertext)
- Returns:
AXL_OK if the tag is valid and decryption succeeded; AXL_ERR otherwise (bad tag, bad args, length mismatch, or TLS not built).
-
AxlEcdh *axl_ecdh_new(AxlEcdhAlg alg)
Generate an ephemeral ECDH key pair.
- Parameters:
alg – curve to generate a key pair on
- Returns:
a new context (caller frees with axl_ecdh_free), or NULL on a bad algorithm, RNG failure, or TLS not compiled in.
-
int axl_ecdh_get_public(AxlEcdh *e, uint8_t *out, size_t *len)
Export this context’s public key.
The encoding is per
alg(SEC1 uncompressed point for P-256, the 32-byte u-coordinate for X25519). Uses the size-query / output-buffer protocol:out== NULL writes the required size to*len; otherwise*lenis the capacity, set on success to the bytes written, and on a too-small buffer returns AXL_ERR with*lenset to the required size.- Parameters:
e – ECDH context
out – [out] public key, or NULL to size-query
len – [in/out] buffer capacity / bytes written
- Returns:
AXL_OK on success; AXL_ERR on a too-small buffer, bad args, or TLS not compiled in.
-
int axl_ecdh_compute(AxlEcdh *e, const uint8_t *peer_pub, size_t peer_len, uint8_t *out, size_t *len)
Compute the shared secret from a peer’s public key.
peer_pubis the peer’s public key in the same encoding axl_ecdh_get_public() produces. The shared secret is 32 bytes for both curves (P-256 X coordinate; X25519 shared u). Output uses the size-query / output-buffer protocol of axl_ecdh_get_public().The raw secret MUST be passed through a KDF/hash before use as a key (it is not uniformly random). Returns AXL_ERR on an invalid peer key (e.g. not on the curve) — fail closed.
- Parameters:
e – ECDH context
peer_pub – peer public key (encoding per the curve)
peer_len – peer public key length
out – [out] shared secret, or NULL to size-query
len – [in/out] buffer capacity / bytes written
- Returns:
AXL_OK on success; AXL_ERR on a bad peer key, too-small buffer, bad args, or TLS not compiled in.
-
void axl_ecdh_free(AxlEcdh *e)
Free an ECDH context. NULL-safe. Zeroizes the private key.
- Parameters:
e – context to free
-
AxlCipher *axl_cipher_ctr_new(AxlCipherAlg alg, const uint8_t *key, size_t key_len, const uint8_t *iv)
Create an AES-CTR context.
key_lenmust matchalg(16 for AES-128, 32 for AES-256).ivis the 16-byte initial counter block. A (key, IV) pair must never be reused across streams.- Parameters:
alg – AES-CTR key size
key – key (length per
alg)key_len – key length in bytes
iv – AXL_CIPHER_CTR_IV_LEN-byte counter block
- Returns:
a new context (caller frees with axl_cipher_free), or NULL on a key-length mismatch, bad args, or TLS not compiled in.
-
int axl_cipher_ctr_xcrypt(AxlCipher *c, const uint8_t *in, size_t len, uint8_t *out)
Encrypt or decrypt
lenbytes, advancing the keystream.CTR is symmetric — the same call encrypts and decrypts.
outmay aliasinfor in-place operation.lenneed not be a multiple of the block size; the keystream position carries across calls so a stream may be processed in arbitrary chunks.in/outmay be NULL only whenlenis 0.- Parameters:
c – cipher context
in – input bytes (NULL iff len == 0)
len – number of bytes
out – [out] output bytes, len bytes (may alias in)
- Returns:
AXL_OK on success; AXL_ERR on bad args or TLS not compiled in.