// SPDX-FileCopyrightText: © 2022—2026 Vladimir Zorin // SPDX-License-Identifier: LicenseRef-OWL-1.0-or-later // Licensed under OWL v1.0+. See LICENSE. /* * LITLS -- Lilush TLS Stack * Public C API for cryptographic primitives. * * All functions use the litls_ prefix. No heap allocation in the core * C API -- callers provide output buffers. The Lua binding layer * (litls_lua.c) may heap-allocate for buffers exceeding 64 KB. * Returns 0 on success, negative on error unless otherwise noted. */ #ifndef LITLS_H #define LITLS_H #include #include /* ── SHA-1 (legacy, for git metadata only) ────────────── */ #define LITLS_SHA1_DIGEST_SIZE 20 void litls_sha1(const uint8_t *data, size_t len, uint8_t out[20]); /* ── SHA-256 ──────────────────────────────────────────── */ #define LITLS_SHA256_DIGEST_SIZE 32 #define LITLS_SHA256_BLOCK_SIZE 64 typedef struct { uint32_t state[8]; uint64_t bitlen; uint8_t data[64]; uint32_t datalen; } litls_sha256_ctx; void litls_sha256_init(litls_sha256_ctx *ctx); void litls_sha256_update(litls_sha256_ctx *ctx, const uint8_t *data, size_t len); void litls_sha256_final(litls_sha256_ctx *ctx, uint8_t out[32]); void litls_sha256(const uint8_t *data, size_t len, uint8_t out[32]); /* ── SHA-512 / SHA-384 ────────────────────────────────── */ #define LITLS_SHA512_DIGEST_SIZE 64 #define LITLS_SHA512_BLOCK_SIZE 128 #define LITLS_SHA384_DIGEST_SIZE 48 #define LITLS_SHA384_BLOCK_SIZE 128 typedef struct { uint64_t state[8]; uint64_t bitlen[2]; uint8_t data[128]; uint32_t datalen; } litls_sha512_ctx; typedef litls_sha512_ctx litls_sha384_ctx; void litls_sha512_init(litls_sha512_ctx *ctx); void litls_sha512_update(litls_sha512_ctx *ctx, const uint8_t *data, size_t len); void litls_sha512_final(litls_sha512_ctx *ctx, uint8_t out[64]); void litls_sha512(const uint8_t *data, size_t len, uint8_t out[64]); void litls_sha384_init(litls_sha384_ctx *ctx); void litls_sha384_update(litls_sha384_ctx *ctx, const uint8_t *data, size_t len); void litls_sha384_final(litls_sha384_ctx *ctx, uint8_t out[48]); void litls_sha384(const uint8_t *data, size_t len, uint8_t out[48]); /* ── HMAC ──────────────────────────────────────────────── */ void litls_hmac_sha256(const uint8_t *key, size_t key_len, const uint8_t *msg, size_t msg_len, uint8_t out[32]); void litls_hmac_sha384(const uint8_t *key, size_t key_len, const uint8_t *msg, size_t msg_len, uint8_t out[48]); /* ── HKDF (RFC 5869) ──────────────────────────────────── */ void litls_hkdf_extract_sha256(const uint8_t *salt, size_t salt_len, const uint8_t *ikm, size_t ikm_len, uint8_t prk[32]); int litls_hkdf_expand_sha256(const uint8_t prk[32], const uint8_t *info, size_t info_len, uint8_t *okm, size_t okm_len); void litls_hkdf_extract_sha384(const uint8_t *salt, size_t salt_len, const uint8_t *ikm, size_t ikm_len, uint8_t prk[48]); int litls_hkdf_expand_sha384(const uint8_t prk[48], const uint8_t *info, size_t info_len, uint8_t *okm, size_t okm_len); /* ── ChaCha20 (RFC 8439) ──────────────────────────────── */ void litls_chacha20_block(const uint8_t key[32], const uint8_t nonce[12], uint32_t counter, uint8_t out[64]); void litls_chacha20_encrypt(const uint8_t key[32], const uint8_t nonce[12], uint32_t counter, const uint8_t *in, size_t len, uint8_t *out); /* ── Poly1305 (RFC 8439) ──────────────────────────────── */ void litls_poly1305_auth(const uint8_t key[32], const uint8_t *msg, size_t len, uint8_t tag[16]); /* Streaming Poly1305 API */ typedef struct { uint32_t r[5]; /* clamped key in 26-bit limbs */ uint32_t s5[4]; /* r[i] * 5 for reduction */ uint32_t h[5]; /* accumulator in 26-bit limbs */ uint32_t pad[4]; /* one-time pad (s) */ uint8_t buf[16]; /* partial block buffer */ size_t buf_len; /* bytes in partial block */ } litls_poly1305_ctx; void litls_poly1305_init(litls_poly1305_ctx *ctx, const uint8_t key[32]); void litls_poly1305_update(litls_poly1305_ctx *ctx, const uint8_t *data, size_t len); void litls_poly1305_final(litls_poly1305_ctx *ctx, uint8_t tag[16]); /* ── ChaCha20-Poly1305 AEAD (RFC 8439) ────────────────── */ int litls_chacha20_poly1305_encrypt(const uint8_t key[32], const uint8_t nonce[12], const uint8_t *aad, size_t aad_len, const uint8_t *plaintext, size_t pt_len, uint8_t *ciphertext, uint8_t tag[16]); int litls_chacha20_poly1305_decrypt(const uint8_t key[32], const uint8_t nonce[12], const uint8_t *aad, size_t aad_len, const uint8_t *ciphertext, size_t ct_len, const uint8_t tag[16], uint8_t *plaintext); /* ── AES-GCM AEAD ─────────────────────────────────────── */ int litls_aes128_gcm_encrypt(const uint8_t key[16], const uint8_t nonce[12], const uint8_t *aad, size_t aad_len, const uint8_t *plaintext, size_t pt_len, uint8_t *ciphertext, uint8_t tag[16]); int litls_aes128_gcm_decrypt(const uint8_t key[16], const uint8_t nonce[12], const uint8_t *aad, size_t aad_len, const uint8_t *ciphertext, size_t ct_len, const uint8_t tag[16], uint8_t *plaintext); int litls_aes256_gcm_encrypt(const uint8_t key[32], const uint8_t nonce[12], const uint8_t *aad, size_t aad_len, const uint8_t *plaintext, size_t pt_len, uint8_t *ciphertext, uint8_t tag[16]); int litls_aes256_gcm_decrypt(const uint8_t key[32], const uint8_t nonce[12], const uint8_t *aad, size_t aad_len, const uint8_t *ciphertext, size_t ct_len, const uint8_t tag[16], uint8_t *plaintext); /* ── X25519 ────────────────────────────────────────────── */ int litls_x25519(uint8_t shared[32], const uint8_t private_key[32], const uint8_t peer_public[32]); void litls_x25519_base(uint8_t public_key[32], const uint8_t private_key[32]); /* ── Ed25519 ───────────────────────────────────────────── */ void litls_ed25519_keypair(uint8_t public_key[32], uint8_t secret_key[64], const uint8_t seed[32]); int litls_ed25519_sign(uint8_t sig[64], const uint8_t *msg, size_t msg_len, const uint8_t public_key[32], const uint8_t secret_key[64]); int litls_ed25519_verify(const uint8_t sig[64], const uint8_t *msg, size_t msg_len, const uint8_t public_key[32]); /* Parse an unencrypted OpenSSH ed25519 private key (binary, after base64 decode). * Returns 0 on success, -2 if encrypted, -4 if not ed25519, -1 on parse error. */ int litls_openssh_parse_ed25519(const uint8_t *data, size_t data_len, uint8_t seed[32], uint8_t public_key[32]); /* Convert Ed25519 public key to X25519 (Montgomery u-coordinate). * Used for ECDH key agreement with Ed25519 keys (e.g. SSH keys). */ int litls_ed25519_to_x25519_pk(uint8_t x25519_pk[32], const uint8_t ed25519_pk[32]); /* Convert Ed25519 seed (32-byte private key) to X25519 private key. * Applies SHA-512 + clamping, same as Ed25519 key expansion. */ int litls_ed25519_to_x25519_sk(uint8_t x25519_sk[32], const uint8_t ed25519_seed[32]); /* ── ECDSA P-256 ───────────────────────────────────────── */ int litls_p256_keygen(uint8_t private_key[32], uint8_t public_key[65]); int litls_p256_ecdsa_sign(const uint8_t *hash, size_t hash_len, const uint8_t private_key[32], uint8_t *sig, size_t *sig_len); int litls_p256_ecdsa_verify(const uint8_t *hash, size_t hash_len, const uint8_t public_key[65], const uint8_t *sig, size_t sig_len); /* ── ECDSA P-384 (verify only, for X.509 chain validation) ── */ int litls_p384_ecdsa_verify(const uint8_t *hash, size_t hash_len, const uint8_t public_key[97], const uint8_t *sig, size_t sig_len); /* ── RSA verify ────────────────────────────────────────── */ #define LITLS_HASH_SHA256 4 #define LITLS_HASH_SHA384 5 #define LITLS_HASH_SHA512 6 int litls_rsa_pkcs1_verify(const uint8_t *hash, size_t hash_len, int hash_id, const uint8_t *sig, size_t sig_len, const uint8_t *n, size_t n_len, const uint8_t *e, size_t e_len); int litls_rsa_pss_sha256_verify(const uint8_t *msg_hash, const uint8_t *sig, size_t sig_len, const uint8_t *n, size_t n_len, const uint8_t *e, size_t e_len); /* ── Base64 (RFC 4648) ─────────────────────────────────── */ size_t litls_base64_encode(const uint8_t *in, size_t in_len, char *out, size_t out_cap); int litls_base64_decode(const char *in, size_t in_len, uint8_t *out, size_t *out_len); size_t litls_base64url_encode(const uint8_t *in, size_t in_len, char *out, size_t out_cap); int litls_base64url_decode(const char *in, size_t in_len, uint8_t *out, size_t *out_len); /* ── CSPRNG ────────────────────────────────────────────── */ int litls_random_bytes(uint8_t *buf, size_t len); /* ── Utilities ─────────────────────────────────────────── */ int litls_secure_memcmp(const uint8_t *a, const uint8_t *b, size_t len); /* Zero memory in a way that cannot be optimized away by the compiler. */ void litls_secure_zero(void *buf, size_t len); /* ── X.509 Certificate Parsing ─────────────────────────── */ #define LITLS_X509_MAX_CN_LEN 256 #define LITLS_X509_MAX_SANS 32 #define LITLS_X509_MAX_SAN_LEN 256 /* Signature algorithm identifiers */ #define LITLS_SIG_UNKNOWN 0 #define LITLS_SIG_ECDSA_SHA256 1 #define LITLS_SIG_ECDSA_SHA384 2 #define LITLS_SIG_RSA_SHA256 3 #define LITLS_SIG_RSA_SHA384 4 #define LITLS_SIG_RSA_SHA512 5 #define LITLS_SIG_ED25519 6 /* Key type identifiers */ #define LITLS_KEY_UNKNOWN 0 #define LITLS_KEY_ECDSA_P256 1 #define LITLS_KEY_RSA 2 #define LITLS_KEY_ECDSA_P384 3 #define LITLS_KEY_ED25519 4 /* KeyUsage bits (RFC 5280 §4.2.1.3). Only keyCertSign is currently enforced; * the others are decoded for completeness and future use. Bit 0 corresponds * to digitalSignature in the encoded BIT STRING (the MSB of the first byte), * matching the numeric bit positions listed in the RFC. */ #define LITLS_KU_DIGITAL_SIGNATURE (1u << 0) #define LITLS_KU_NON_REPUDIATION (1u << 1) #define LITLS_KU_KEY_ENCIPHERMENT (1u << 2) #define LITLS_KU_DATA_ENCIPHERMENT (1u << 3) #define LITLS_KU_KEY_AGREEMENT (1u << 4) #define LITLS_KU_KEY_CERT_SIGN (1u << 5) #define LITLS_KU_CRL_SIGN (1u << 6) #define LITLS_KU_ENCIPHER_ONLY (1u << 7) #define LITLS_KU_DECIPHER_ONLY (1u << 8) typedef struct { char common_name[LITLS_X509_MAX_CN_LEN]; size_t common_name_len; int64_t not_before; /* Unix timestamp */ int64_t not_after; /* Unix timestamp */ struct { char name[LITLS_X509_MAX_SAN_LEN]; size_t len; } sans[LITLS_X509_MAX_SANS]; int san_count; int sig_algo; /* LITLS_SIG_* */ int key_type; /* LITLS_KEY_* */ uint8_t pubkey[512]; /* EC point (65B) or RSA modulus (up to 4096-bit) */ size_t pubkey_len; uint8_t rsa_e[8]; /* RSA public exponent */ size_t rsa_e_len; const uint8_t *signature; /* Points into input DER buffer */ size_t signature_len; const uint8_t *tbs; /* Points into input DER buffer */ size_t tbs_len; /* X.509 v3 extension summary (RFC 5280). Fields default to "absent". */ uint8_t basic_constraints_present; /* 1 if BasicConstraints extension seen */ uint8_t is_ca; /* CA=TRUE within BasicConstraints */ int16_t path_len_constraint; /* -1 if absent; only meaningful when is_ca */ uint8_t key_usage_present; /* 1 if KeyUsage extension seen */ uint16_t key_usage; /* LITLS_KU_* bitmask */ uint8_t eku_present; /* 1 if ExtendedKeyUsage extension seen */ uint8_t eku_server_auth; /* id-kp-serverAuth (1.3.6.1.5.5.7.3.1) seen */ uint8_t eku_any; /* anyExtendedKeyUsage (2.5.29.37.0) seen */ uint8_t unknown_critical_extension; /* 1 if any unrecognised extension was marked critical */ } litls_x509_cert_info; /* Parse a DER-encoded X.509 certificate. Returns 0 on success. */ int litls_x509_parse(const uint8_t *der, size_t der_len, litls_x509_cert_info *info); /* Convert a DER-encoded ECDSA signature to raw r||s. * component_size is 32 for P-256 (64-byte output) or 48 for P-384 (96-byte output). * Returns 0 on success. */ int litls_ecdsa_sig_der_to_raw(const uint8_t *der_sig, size_t der_len, uint8_t *raw, size_t component_size); /* Convert raw r||s ECDSA signature to DER-encoded ECDSA-Sig-Value. * raw_len must be 64 (P-256). Returns 0 on success. */ int litls_ecdsa_sig_raw_to_der(const uint8_t *raw, size_t raw_len, uint8_t *der, size_t der_cap, size_t *der_len); /* ── X.509 Chain Validation ────────────────────────────── */ #define LITLS_X509_MAX_CHAIN 8 #define LITLS_X509_MAX_ANCHORS 256 #define LITLS_X509_OK 0 #define LITLS_X509_ERR_PARSE -1 #define LITLS_X509_ERR_SIGNATURE -2 #define LITLS_X509_ERR_EXPIRED -3 #define LITLS_X509_ERR_NO_ANCHOR -4 #define LITLS_X509_ERR_CHAIN -5 #define LITLS_X509_ERR_CONSTRAINT -6 /* BasicConstraints/KeyUsage/EKU/pathLen violation */ #define LITLS_X509_ERR_HOSTNAME -7 /* leaf SAN/CN does not match expected hostname */ typedef struct { char common_name[LITLS_X509_MAX_CN_LEN]; int key_type; uint8_t pubkey[512]; size_t pubkey_len; uint8_t rsa_e[8]; size_t rsa_e_len; } litls_x509_trust_anchor; /* Validate a certificate chain (leaf-first) against trust anchors. * now=0 skips time validation. Returns LITLS_X509_OK or error code. */ int litls_x509_verify_chain(const uint8_t **chain_ders, const size_t *chain_der_lens, int chain_count, const litls_x509_trust_anchor *anchors, int anchor_count, int64_t now); /* Load trust anchors from a PEM CA bundle. * Returns number of anchors loaded, or negative on error. */ int litls_x509_load_anchors(const uint8_t *pem_data, size_t pem_len, litls_x509_trust_anchor *anchors, int max_anchors); /* Verify that `hostname` matches the leaf certificate's identity per RFC 6125. * * - If the leaf has any SAN dNSName entries, only SANs are consulted (CN is * not used as a fallback). Matches browser behaviour and RFC 6125 §6.4.4. * - If the leaf has no SANs, the Subject CN is used. * - Comparisons are ASCII case-insensitive. * - Wildcards: only a single leftmost-label '*' is accepted (e.g. * "*.example.com"). The '*' matches exactly one DNS label; partial-label * wildcards ("f*.example.com") and wildcards in non-leftmost positions * are rejected. * - IP literal hostnames are not supported (LITLS only parses dNSName); * the call will fail closed. * * Returns LITLS_X509_OK on match, LITLS_X509_ERR_HOSTNAME otherwise. */ int litls_x509_verify_hostname(const litls_x509_cert_info *leaf, const char *hostname); /* Decode a single PEM block to DER. Returns 0 on success. */ int litls_pem_decode(const uint8_t *pem, size_t pem_len, uint8_t *der_out, size_t *der_out_len); /* Encode an EC P-256 private key to SEC1 PEM format. * priv: 32-byte scalar, pub: 65-byte X.963 uncompressed point. * Returns 0 on success. */ int litls_ec_privkey_to_pem(const uint8_t priv[32], const uint8_t pub[65], char *pem_out, size_t pem_cap, size_t *pem_len); /* Parse a PEM-encoded private key (EC PRIVATE KEY or PKCS#8). * Supports ECDSA P-256 and Ed25519 keys. * Sets *key_type to LITLS_KEY_ECDSA_P256 or LITLS_KEY_ED25519. * For P-256: private_key is 32 bytes, public_key is 65 bytes (uncompressed). * For Ed25519: private_key is 64 bytes (expanded secret), public_key is 32 bytes. * Returns 0 on success. */ int litls_parse_private_key_pem(const uint8_t *pem, size_t pem_len, int *key_type, uint8_t *private_key, size_t *private_key_len, uint8_t *public_key, size_t *public_key_len); /* ── PKCS#10 CSR Generation ────────────────────────────── */ #define LITLS_CSR_MAX_SIZE 4096 typedef struct { const char *domain; const char **sans; int san_count; const uint8_t *private_key; /* 32 bytes, P-256 */ const uint8_t *public_key; /* 65 bytes, X.963 uncompressed */ } litls_csr_params; /* Generate a DER-encoded PKCS#10 CSR (ECDSA P-256 + SHA-256). * Returns DER length on success, negative on error. */ int litls_csr_generate(const litls_csr_params *params, uint8_t *der_out, size_t der_cap); #endif /* LITLS_H */