All modes available on "tiny" builds

This commit is contained in:
Miguel Terron 2025-06-28 14:14:02 +12:00
parent 77ff5a24f9
commit e7fc09b4a4
3 changed files with 194 additions and 103 deletions

View file

@ -1,4 +1,3 @@
---@diagnostic disable: lowercase-global
-- Test RSA key pair generation
local function test_rsa_keypair_generation()
local priv_key, pub_key = crypto.generateKeyPair("rsa", 2048)
@ -678,14 +677,14 @@ end
local function run_tests()
Log(kLogVerbose,"Testing RSA keypair generation...")
test_rsa_keypair_generation()
Log(kLogVerbose,"Testing RSA signing and verification...")
test_rsa_signing_verification()
Log(kLogVerbose,"Testing RSA-PSS signing and verification...")
test_rsapss_signing_verification()
Log(kLogVerbose,"Testing RSA encryption and decryption...")
test_rsa_encryption_decryption()
Log(kLogVerbose,"Testing RSA key size variations...")
test_rsa_key_sizes()
Log(kLogVerbose,"Testing RSA signing and verification...")
test_rsa_signing_verification()
Log(kLogVerbose,"Testing RSA encryption and decryption...")
test_rsa_encryption_decryption()
Log(kLogVerbose,"Testing RSA-PSS signing and verification...")
test_rsapss_signing_verification()
Log(kLogVerbose,"Testing ECDSA keypair generation...")
test_ecdsa_keypair_generation()
@ -753,4 +752,5 @@ local function run_tests()
end
EXIT = 70
os.exit(run_tests())

View file

@ -746,7 +746,7 @@ function EscapeHtml(str) end
---@param path string?
function LaunchBrowser(path) end
---@param ip uint32
---@param ip integer|string
---@return string # a string describing the IP address. This is currently Class A granular. It can tell you if traffic originated from private networks, ARIN, APNIC, DOD, etc.
---@nodiscard
function CategorizeIp(ip) end
@ -1142,10 +1142,10 @@ function FormatHttpDateTime(seconds) end
--- Turns integer like `0x01020304` into a string like `"1.2.3.4"`. See also
--- `ParseIp` for the inverse operation.
---@param uint32 integer
---@param ip integer
---@return string
---@nodiscard
function FormatIp(uint32) end
function FormatIp(ip) end
--- Returns client ip4 address and port, e.g. `0x01020304`,`31337` would represent
--- `1.2.3.4:31337`. This is the same as `GetClientAddr` except it will use the
@ -1363,25 +1363,25 @@ function HidePath(prefix) end
---@nodiscard
function IsHiddenPath(path) end
---@param uint32 integer
---@param ip integer|string|string
---@return boolean # `true` if IP address is not a private network (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`) and is not localhost (`127.0.0.0/8`).
--- Note: we intentionally regard TEST-NET IPs as public.
---@nodiscard
function IsPublicIp(uint32) end
function IsPublicIp(ip) end
---@param uint32 integer
---@param ip integer|string|string
---@return boolean # `true` if IP address is part of a private network (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16).
---@nodiscard
function IsPrivateIp(uint32) end
function IsPrivateIp(ip) end
---@return boolean # `true` if the client IP address (returned by GetRemoteAddr) is part of the localhost network (127.0.0.0/8).
---@nodiscard
function IsLoopbackClient() end
---@param uint32 integer
---@param ip integer|string|string
---@return boolean # true if IP address is part of the localhost network (127.0.0.0/8).
---@nodiscard
function IsLoopbackIp(uint32) end
function IsLoopbackIp(ip) end
---@param path string
---@return boolean # `true` if ZIP artifact at path is stored on disk using DEFLATE compression.
@ -1615,7 +1615,7 @@ function GetCryptoHash(name, payload, key) end
--- to the system-configured DNS resolution service. Please note that in MODE=tiny
--- the HOSTS.TXT and DNS resolution isn't included, and therefore an IP must be
--- provided.
---@param ip integer
---@param ip integer|string|string
---@overload fun(host:string)
function ProgramAddr(ip) end
@ -1669,8 +1669,8 @@ function ProgramTimeout(milliseconds) end
--- Hard-codes the port number on which to listen, which can be any number in the
--- range `1..65535`, or alternatively `0` to ask the operating system to choose a
--- port, which may be revealed later on by `GetServerAddr` or the `-z` flag to stdout.
---@param uint16 integer
function ProgramPort(uint16) end
---@param port integer
function ProgramPort(port) end
--- Sets the maximum HTTP message payload size in bytes. The
--- default is very conservatively set to 65536 so this is
@ -2169,7 +2169,7 @@ function bin(int) end
--- unspecified format describing the error. Calls to this function may be wrapped
--- in `assert()` if an exception is desired.
---@param hostname string
---@return uint32 ip uint32
---@return string
---@nodiscard
---@overload fun(hostname: string): nil, error: string
function ResolveIp(hostname) end
@ -2183,7 +2183,7 @@ function ResolveIp(hostname) end
--- The network interface addresses used by the host machine are always
--- considered trustworthy, e.g. 127.0.0.1. This may change soon, if we
--- decide to export a `GetHostIps()` API which queries your NIC devices.
---@param ip integer
---@param ip integer|string
---@return boolean
function IsTrustedIp(ip) end
@ -2213,7 +2213,7 @@ function IsTrustedIp(ip) end
---
--- Although you might want consider trusting redbean's open source
--- freedom embracing solution to DDOS protection instead!
---@param ip integer
---@param ip integer|string
---@param cidr integer?
function ProgramTrustedIp(ip, cidr) end
@ -8051,85 +8051,70 @@ kUrlLatin1 = nil
--- This module provides cryptographic operations.
--- The crypto module for cryptographic operations
crypto = {}
--- Signs a message using the specified key type.
--- Supported types: "rsa", "rsa-pss", "ecdsa"
---@param type "rsa"|"rsa-pss"|"rsapss"|"ecdsa"
---@param key string PEM-encoded private key
---@param message string
---@param hash? string Hash algorithm ("sha256", "sha384", "sha512"). Default: "sha256"
---@return string signature
---@overload fun(type: string, key: string, message: string, hash?: string): nil, error: string
function crypto.sign(type, key, message, hash) end
--- Converts a PEM-encoded key to JWK format
---@param pem string PEM-encoded key
---@return table?, string? JWK table or nil on error
---@return string? error message
function crypto.convertPemToJwk(pem) end
--- Verifies a signature using the specified key type.
--- Supported types: "rsa", "rsa-pss", "ecdsa"
---@param type "rsa"|"rsa-pss"|"rsapss"|"ecdsa"
---@param key string PEM-encoded public key
---@param message string
---@param signature string
---@param hash? string Hash algorithm ("sha256", "sha384", "sha512"). Default: "sha256"
---@return boolean valid
function crypto.verify(type, key, message, signature, hash) end
--- Generates a Certificate Signing Request (CSR)
---@param key_pem string PEM-encoded private key
---@param subject_name string? X.509 subject name
---@param san_list string? Subject Alternative Names
---@return string?, string? CSR in PEM format or nil on error and error message
function crypto.generateCsr(key_pem, subject_name, san_list) end
--- Encrypts data using the specified cipher.
--- Supported ciphers: "rsa", "aes"
---@param cipher "rsa"|"aes"
---@param key string PEM-encoded public key (RSA) or raw key (AES)
---@param plaintext string
---@param options? table Options for AES: { mode="cbc"|"gcm"|"ctr", iv=string, aad=string }
---@return string ciphertext, string? iv, string? tag
---@overload fun(cipher: string, key: string, plaintext: string, options?: table): nil, error: string
function crypto.encrypt(cipher, key, plaintext, options) end
--- Signs data using a private key
---@param key_type string "rsa" or "ecdsa"
---@param private_key string PEM-encoded private key
---@param message string Data to sign
---@param hash_algo string? Hash algorithm (default: SHA-256)
---@return string?, string? Signature or nil on error and error message
function crypto.sign(key_type, private_key, message, hash_algo) end
--- Decrypts data using the specified cipher.
--- Supported ciphers: "rsa", "aes"
---@param cipher "rsa"|"aes"
---@param key string PEM-encoded private key (RSA) or raw key (AES)
---@param ciphertext string
---@param options? table Options for AES: { mode="cbc"|"gcm"|"ctr", iv=string, tag=string, aad=string }
---@return string plaintext
---@overload fun(cipher: string, key: string, ciphertext: string, options?: table): nil, error: string
function crypto.decrypt(cipher, key, ciphertext, options) end
--- Verifies a signature
---@param key_type string "rsa" or "ecdsa"
---@param public_key string PEM-encoded public key
---@param message string Original message
---@param signature string Signature to verify
---@param hash_algo string? Hash algorithm (default: SHA-256)
---@return boolean?, string? True if valid or nil on error and error message
function crypto.verify(key_type, public_key, message, signature, hash_algo) end
--- Generates a key pair.
--- For RSA: bits = 2048 or 4096.
--- For ECDSA: curve = "secp256r1", "secp384r1", "secp521r1", "curve25519"
--- For AES: bits = 128, 192, or 256.
---@param type "rsa"|"ecdsa"|"aes"
---@param param? integer|string For RSA: bits; for ECDSA: curve name; for AES: bits
---@return string private_key, string public_key|nil
---@overload fun(type: string, param?: integer|string): nil, error: string
function crypto.generateKeyPair(type, param) end
--- Encrypts data
---@param cipher_type string "rsa" or "aes"
---@param key string Public key or symmetric key
---@param plaintext string Data to encrypt
---@param options table Table with optional parameters:
--- options.mode string? AES mode: "cbc", "gcm", "ctr" (default: "cbc")
--- options.iv string? Initialization Vector for AES
--- options.aad string? Additional data for AES-GCM
---@return string? Encrypted data or nil on error
---@return string? IV or error message
---@return string? Authentication tag for GCM mode
function crypto.encrypt(cipher_type, key, plaintext, options) end
--- Converts a JWK (JSON Web Key, as a Lua table) to PEM format.
---@param jwk table
---@return string pem
---@overload fun(jwk: table): nil, error: string
function crypto.convertJwkToPem(jwk) end
--- Decrypts data
---@param cipher_type string "rsa" or "aes"
---@param key string Private key or symmetric key
---@param ciphertext string Data to decrypt
---@param options table Table with optional parameters:
--- options.iv string? Initialization Vector for AES
--- options.mode string? AES mode: "cbc", "gcm", "ctr" (default: "cbc")
--- options.tag string? Authentication tag for AES-GCM
--- options.aad string? Additional data for AES-GCM
---@return string?, string? Decrypted data or nil on error and error message
function crypto.decrypt(cipher_type, key, ciphertext, options) end
--- Converts a PEM key to JWK (JSON Web Key, as a Lua table).
---@param pem string
---@param claims? table Additional claims to merge (RFC7517 fields)
---@return table jwk
---@overload fun(pem: string, claims?: table): nil, error: string
function crypto.convertPemToJwk(pem, claims) end
--- Generates a Certificate Signing Request (CSR) from a PEM key.
---@param key string PEM-encoded private key
---@param subject string Subject name (e.g., "CN=example.com")
---@param sans? string Subject Alternative Names (SANs) as a comma-separated string
---@return string csr_pem
---@overload fun(key: string, subject: string, sans?: string): nil, error: string
function crypto.generateCsr(key, subject, sans) end
-- AES options table for encrypt/decrypt:
---@class CryptoAesOptions
---@field mode? "cbc"|"gcm"|"ctr"
---@field iv? string
---@field tag? string
---@field aad? string
--- Generates cryptographic keys
---@param key_type string? "rsa", "ecdsa", or "aes"
---@param key_size_or_curve number|string? Key size or curve name
---@return string? Private key or nil on error
---@return string? Public key (nil for AES) or error message
function crypto.generatekeypair(key_type, key_size_or_curve) end
--[[

View file

@ -50,9 +50,7 @@ static const curve_map_t supported_curves[] = {
{"P521", MBEDTLS_ECP_DP_SECP521R1}, //
{"P-521", MBEDTLS_ECP_DP_SECP521R1}, //
{"curve25519", MBEDTLS_ECP_DP_CURVE25519}, //
#ifndef TINY
{"curve448", MBEDTLS_ECP_DP_CURVE448}, //
#endif
{"curve448", MBEDTLS_ECP_DP_CURVE448}, //
{NULL, 0}};
// List available curves
@ -198,6 +196,24 @@ cleanup:
return ret;
}
// Ciphers
typedef struct {
const char *name;
mbedtls_cipher_id_t id;
} ciphers_map_t;
static const ciphers_map_t supported_ciphers[] = {
{"AES-128-CBC", MBEDTLS_CIPHER_AES_128_CBC}, //
{"AES-192-CBC", MBEDTLS_CIPHER_AES_192_CBC}, //
{"AES-256-CBC", MBEDTLS_CIPHER_AES_256_CBC}, //
{"AES-128-CTR", MBEDTLS_CIPHER_AES_128_CTR}, //
{"AES-192-CTR", MBEDTLS_CIPHER_AES_192_CTR}, //
{"AES-256-CTR", MBEDTLS_CIPHER_AES_256_CTR}, //
{"AES-128-GCM", MBEDTLS_CIPHER_AES_128_GCM}, //
{"AES-192-GCM", MBEDTLS_CIPHER_AES_192_GCM}, //
{"AES-256-GCM", MBEDTLS_CIPHER_AES_256_GCM}, //
{NULL, 0}};
// Strong RNG using mbedtls_entropy_context and mbedtls_ctr_drbg_context
int GenerateRandom(void *ctx, unsigned char *output, size_t len) {
static mbedtls_entropy_context entropy;
@ -2350,14 +2366,10 @@ static int LuaCryptoSign(lua_State *L) {
if (strcasecmp(dtype, "rsa") == 0) {
return LuaRSASign(L);
}
#ifndef TINY
else if (strcasecmp(dtype, "rsa-pss") == 0 ||
strcasecmp(dtype, "rsapss") == 0) {
} else if (strcasecmp(dtype, "rsa-pss") == 0 ||
strcasecmp(dtype, "rsapss") == 0) {
return LuaRSAPSSSign(L);
}
#endif
else if (strcasecmp(dtype, "ecdsa") == 0) {
} else if (strcasecmp(dtype, "ecdsa") == 0) {
return LuaECDSASign(L);
} else {
return luaL_error(L, "Unsupported signature type: %s", dtype);
@ -2433,6 +2445,99 @@ static int LuaCryptoGenerateKeyPair(lua_State *L) {
}
}
// Returns a Lua table array of supported digests and ciphers (strings),
// depending on the type argument:
// "ciphers" - returns list of ciphers supported by crypto.encrypt and
// crypto.decrypt "digests" - returns list of digests in supported_digests
// "curves" - returns list of curves in supported_curves
// If no argument is provided, returns a table with all three types
static int LuaList(lua_State *L) {
// Create a new table to hold the result
lua_newtable(L);
// No argument provided - return all types in a structured table
if (lua_isnoneornil(L, 1)) {
// Create subtable for digests
lua_pushstring(L, "digests");
lua_newtable(L);
const digest_map_t *digest = supported_digests;
int i = 1;
while (digest->name != NULL) {
lua_pushstring(L, digest->name);
lua_rawseti(L, -2, i++);
digest++;
}
lua_settable(L, -3);
// Create subtable for curves
lua_pushstring(L, "curves");
lua_newtable(L);
const curve_map_t *curve = supported_curves;
i = 1;
while (curve->name != NULL) {
lua_pushstring(L, curve->name);
lua_rawseti(L, -2, i++);
curve++;
}
lua_settable(L, -3);
// Create subtable for ciphers
lua_pushstring(L, "ciphers");
lua_newtable(L);
const ciphers_map_t *cipher = supported_ciphers;
i = 1;
while (cipher->name != NULL) {
lua_pushstring(L, cipher->name);
lua_rawseti(L, -2, i++);
cipher++;
}
lua_settable(L, -3);
return 1;
}
// Argument provided - handle specific type
const char *type = luaL_checkstring(L, 1);
if (strcasecmp(type, "curves") == 0) {
// List all available curves
const curve_map_t *curve = supported_curves;
int i = 1;
while (curve->name != NULL) {
lua_pushstring(L, curve->name);
lua_rawseti(L, -2, i++);
curve++;
}
} else if (strcasecmp(type, "digests") == 0) {
// List all available digests
const digest_map_t *digest = supported_digests;
int i = 1;
while (digest->name != NULL) {
lua_pushstring(L, digest->name);
lua_rawseti(L, -2, i++);
digest++;
}
} else if (strcasecmp(type, "ciphers") == 0) {
// List all available ciphers
const ciphers_map_t *cipher = supported_ciphers;
int i = 1;
while (cipher->name != NULL) {
lua_pushstring(L, cipher->name);
lua_rawseti(L, -2, i++);
cipher++;
}
} else {
// Invalid type, return empty table
lua_pushstring(L, "Invalid type. Use 'ciphers', 'digests', or 'curves'");
lua_setfield(L, -2, "error");
}
return 1; // Return the table
}
static const luaL_Reg kLuaCrypto[] = {
{"sign", LuaCryptoSign}, //
{"verify", LuaCryptoVerify}, //
@ -2442,6 +2547,7 @@ static const luaL_Reg kLuaCrypto[] = {
{"convertJwkToPem", LuaConvertJwkToPem}, //
{"convertPemToJwk", LuaConvertPemToJwk}, //
{"generateCsr", LuaGenerateCSR}, //
{"list", LuaList}, //
{0}, //
};