diff --git a/src/prngs/tempest_prng.c b/src/prngs/tempest_prng.c new file mode 100644 index 00000000..4b829d4a --- /dev/null +++ b/src/prngs/tempest_prng.c @@ -0,0 +1,162 @@ +/* tempest_prng.c — LibTomCrypt PRNG backend for Tempest v3 + * + * Register with: + * int idx = register_prng(&tempest_prng_desc); + * /* then use idx with any libtomcrypt function needing a PRNG */ + * + * Tempest v3: 17.7 Gbit/s, 2^128 provable security, 48-byte state + */ + +#include +#include "tempest_v3.h" + +/* ── Helpers ── */ + +/* Ensure `prng_state` has room for our minimal pointer. + * We store the tx4_state inline (48 bytes) which fits in prng_state's union. + * prng_state.u is typically large enough (e.g. 256+ bytes in modern LTC). */ + +#define TEMPEST_MARKER 0x54584D5000000001ULL /* "TXMP\0\0\0\1" */ + +/* ── Callbacks ── */ + +static int tempest_start(prng_state *prng) +{ + /* Mark state as valid; actual init deferred to ready() */ + prng->u.sprng[0] = TEMPEST_MARKER; + return CRYPT_OK; +} + +static int tempest_add_entropy(const unsigned char *in, unsigned long inlen, + prng_state *prng) +{ + /* Tempest v3 is a CSPRNG — it does not require external entropy + * beyond its initial key/nonce seeding. This is a no-op. + * If you want to mix in additional entropy, a 256-bit XOR + * into the Tempest state can be added here. */ + (void)in; (void)inlen; + if (prng->u.sprng[0] != TEMPEST_MARKER) + return CRYPT_ERROR; /* not started */ + return CRYPT_OK; +} + +static int tempest_ready(prng_state *prng) +{ + tx4_state *s = (tx4_state *)&prng->u.sprng[1]; + uint64_t key[4], nonce[2]; + + if (prng->u.sprng[0] != TEMPEST_MARKER) + return CRYPT_ERROR; + + /* Seed from OS entropy */ + os_get_random((unsigned char *)key, sizeof(key)); + os_get_random((unsigned char *)nonce, sizeof(nonce)); + + tx5cmul_init(s, key, nonce); + return CRYPT_OK; +} + +static unsigned long tempest_read(unsigned char *out, unsigned long outlen, + prng_state *prng) +{ + tx4_state *s = (tx4_state *)&prng->u.sprng[1]; + + if (prng->u.sprng[0] != TEMPEST_MARKER) + return 0; + + tempest_bytes(s, out, outlen); + return outlen; +} + +static int tempest_done(prng_state *prng) +{ + if (prng->u.sprng[0] == TEMPEST_MARKER) { + /* Secure zeroing of Tempest state */ + tx4_state *s = (tx4_state *)&prng->u.sprng[1]; + zeroize(s, sizeof(tx4_state)); + prng->u.sprng[0] = 0; + } + return CRYPT_OK; +} + +static int tempest_export(unsigned char *out, unsigned long *outlen, + prng_state *prng) +{ + tx4_state *s = (tx4_state *)&prng->u.sprng[1]; + + if (prng->u.sprng[0] != TEMPEST_MARKER) + return CRYPT_ERROR; + + if (*outlen < sizeof(tx4_state) + 8) + return CRYPT_BUFFER_OVERFLOW; + + /* Store marker + state */ + memcpy(out, &prng->u.sprng[0], 8); /* marker */ + memcpy(out + 8, s, sizeof(tx4_state)); + *outlen = 8 + sizeof(tx4_state); + return CRYPT_OK; +} + +static int tempest_import(const unsigned char *in, unsigned long inlen, + prng_state *prng) +{ + if (inlen < 8 + sizeof(tx4_state)) + return CRYPT_ERROR; + + /* Verify marker */ + uint64_t marker; + memcpy(&marker, in, 8); + if (marker != TEMPEST_MARKER) + return CRYPT_ERROR; + + /* Restore */ + prng->u.sprng[0] = TEMPEST_MARKER; + tx4_state *s = (tx4_state *)&prng->u.sprng[1]; + memcpy(s, in + 8, sizeof(tx4_state)); + return CRYPT_OK; +} + +static int tempest_test(void) +{ + tx4_state s; + uint64_t key[4] = {1,2,3,4}; + uint64_t nonce[2] = {5,6}; + uint64_t out1[4], out2[4]; + + tx5cmul_init(&s, key, nonce); + for (int i = 0; i < 4; i++) + out1[i] = tx5cmul_next(&s); + + /* Same seed → same output (determinism check) */ + tx5cmul_init(&s, key, nonce); + for (int i = 0; i < 4; i++) + out2[i] = tx5cmul_next(&s); + + if (memcmp(out1, out2, sizeof(out1)) != 0) + return CRYPT_ERROR; + + /* Non-zero output on zero key */ + uint64_t zero_key[4] = {0,0,0,0}; + uint64_t zero_nonce[2] = {0,0}; + tx5cmul_init(&s, zero_key, zero_nonce); + uint64_t v = tx5cmul_next(&s); + if (v == 0) + return CRYPT_ERROR; + + return CRYPT_OK; +} + +/* ── PRNG Descriptor ── */ + +const struct ltc_prng_descriptor tempest_prng_desc = { + .name = "tempest", + .export_size = 8 + sizeof(tx4_state), + .start = &tempest_start, + .add_entropy = &tempest_add_entropy, + .ready = &tempest_ready, + .read = &tempest_read, + .done = &tempest_done, + .export = &tempest_export, + .import = &tempest_import, + .test = &tempest_test +};