@@ -31,7 +31,7 @@ See the section [API](#API) below. | |||
length-ciphertext: <N> # KEM only | |||
length-shared-secret: <N> # KEM only | |||
length-signature: <N> # Signature only | |||
nistkat-sha256: sha256sum of 1st NIST KAT test case # KEM only | |||
nistkat-sha256: sha256sum of 1st NIST KAT test case # KEM and signature | |||
testvectors-sha256: sha256sum of output of testvectors # Signature only | |||
principal-submitter: Eve | |||
auxiliary-submitters: | |||
@@ -128,7 +128,7 @@ Regarding #2, adding the files to your project's build system, each implementati | |||
The following projects consume implementations from PQClean and provide their own wrappers around the implementations. | |||
Their integration strategies may serve as examples for your own projects. | |||
- **[pqcrypto crate](https://github.com/pqrust/pqcrypto)**: Rust integration that automatically generates wrappers from PQClean source code. | |||
- **[pqcrypto crate](https://github.com/rustpq/pqcrypto)**: Rust integration that automatically generates wrappers from PQClean source code. | |||
- **[mupq](https://github.com/mupq/)**: Runs the implementations from PQClean as reference implementations to compare with microcontroller-optimized code. | |||
- **[Open Quantum Safe](https://github.com/open-quantum-safe/)**: The Open Quantum Safe project integrates implementations from PQClean into their [liboqs](https://github.com/open-quantum-safe/liboqs) C library, which then exposes them via [C++](https://github.com/open-quantum-safe/liboqs-cpp), [C# / .NET](https://github.com/open-quantum-safe/liboqs-dotnet), and [Python](https://github.com/open-quantum-safe/liboqs-python) wrappers, as well as to forks of [OpenSSL](https://github.com/open-quantum-safe/openssl) and [OpenSSH](https://github.com/open-quantum-safe/openssh-portable). | |||
@@ -520,36 +520,36 @@ static void keccak_inc_squeeze(uint8_t *h, size_t outlen, | |||
} | |||
} | |||
void shake128_inc_init(uint64_t *s_inc) { | |||
keccak_inc_init(s_inc); | |||
void shake128_inc_init(shake128incctx *state) { | |||
keccak_inc_init(state->ctx); | |||
} | |||
void shake128_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen) { | |||
keccak_inc_absorb(s_inc, SHAKE128_RATE, input, inlen); | |||
void shake128_inc_absorb(shake128incctx *state, const uint8_t *input, size_t inlen) { | |||
keccak_inc_absorb(state->ctx, SHAKE128_RATE, input, inlen); | |||
} | |||
void shake128_inc_finalize(uint64_t *s_inc) { | |||
keccak_inc_finalize(s_inc, SHAKE128_RATE, 0x1F); | |||
void shake128_inc_finalize(shake128incctx *state) { | |||
keccak_inc_finalize(state->ctx, SHAKE128_RATE, 0x1F); | |||
} | |||
void shake128_inc_squeeze(uint8_t *output, size_t outlen, uint64_t *s_inc) { | |||
keccak_inc_squeeze(output, outlen, s_inc, SHAKE128_RATE); | |||
void shake128_inc_squeeze(uint8_t *output, size_t outlen, shake128incctx *state) { | |||
keccak_inc_squeeze(output, outlen, state->ctx, SHAKE128_RATE); | |||
} | |||
void shake256_inc_init(uint64_t *s_inc) { | |||
keccak_inc_init(s_inc); | |||
void shake256_inc_init(shake256incctx *state) { | |||
keccak_inc_init(state->ctx); | |||
} | |||
void shake256_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen) { | |||
keccak_inc_absorb(s_inc, SHAKE256_RATE, input, inlen); | |||
void shake256_inc_absorb(shake256incctx *state, const uint8_t *input, size_t inlen) { | |||
keccak_inc_absorb(state->ctx, SHAKE256_RATE, input, inlen); | |||
} | |||
void shake256_inc_finalize(uint64_t *s_inc) { | |||
keccak_inc_finalize(s_inc, SHAKE256_RATE, 0x1F); | |||
void shake256_inc_finalize(shake256incctx *state) { | |||
keccak_inc_finalize(state->ctx, SHAKE256_RATE, 0x1F); | |||
} | |||
void shake256_inc_squeeze(uint8_t *output, size_t outlen, uint64_t *s_inc) { | |||
keccak_inc_squeeze(output, outlen, s_inc, SHAKE256_RATE); | |||
void shake256_inc_squeeze(uint8_t *output, size_t outlen, shake256incctx *state) { | |||
keccak_inc_squeeze(output, outlen, state->ctx, SHAKE256_RATE); | |||
} | |||
@@ -564,8 +564,8 @@ void shake256_inc_squeeze(uint8_t *output, size_t outlen, uint64_t *s_inc) { | |||
* into s | |||
* - size_t inlen: length of input in bytes | |||
**************************************************/ | |||
void shake128_absorb(uint64_t *s, const uint8_t *input, size_t inlen) { | |||
keccak_absorb(s, SHAKE128_RATE, input, inlen, 0x1F); | |||
void shake128_absorb(shake128ctx *state, const uint8_t *input, size_t inlen) { | |||
keccak_absorb(state->ctx, SHAKE128_RATE, input, inlen, 0x1F); | |||
} | |||
/************************************************* | |||
@@ -578,10 +578,10 @@ void shake128_absorb(uint64_t *s, const uint8_t *input, size_t inlen) { | |||
* Arguments: - uint8_t *output: pointer to output blocks | |||
* - size_t nblocks: number of blocks to be squeezed | |||
* (written to output) | |||
* - uint64_t *s: pointer to input/output Keccak state | |||
* - shake128ctx *state: pointer to input/output Keccak state | |||
**************************************************/ | |||
void shake128_squeezeblocks(uint8_t *output, size_t nblocks, uint64_t *s) { | |||
keccak_squeezeblocks(output, nblocks, s, SHAKE128_RATE); | |||
void shake128_squeezeblocks(uint8_t *output, size_t nblocks, shake128ctx *state) { | |||
keccak_squeezeblocks(output, nblocks, state->ctx, SHAKE128_RATE); | |||
} | |||
/************************************************* | |||
@@ -590,13 +590,13 @@ void shake128_squeezeblocks(uint8_t *output, size_t nblocks, uint64_t *s) { | |||
* Description: Absorb step of the SHAKE256 XOF. | |||
* non-incremental, starts by zeroeing the state. | |||
* | |||
* Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state | |||
* Arguments: - shake256ctx *state: pointer to (uninitialized) output Keccak state | |||
* - const uint8_t *input: pointer to input to be absorbed | |||
* into s | |||
* - size_t inlen: length of input in bytes | |||
**************************************************/ | |||
void shake256_absorb(uint64_t *s, const uint8_t *input, size_t inlen) { | |||
keccak_absorb(s, SHAKE256_RATE, input, inlen, 0x1F); | |||
void shake256_absorb(shake256ctx *state, const uint8_t *input, size_t inlen) { | |||
keccak_absorb(state->ctx, SHAKE256_RATE, input, inlen, 0x1F); | |||
} | |||
/************************************************* | |||
@@ -609,10 +609,10 @@ void shake256_absorb(uint64_t *s, const uint8_t *input, size_t inlen) { | |||
* Arguments: - uint8_t *output: pointer to output blocks | |||
* - size_t nblocks: number of blocks to be squeezed | |||
* (written to output) | |||
* - uint64_t *s: pointer to input/output Keccak state | |||
* - shake256ctx *state: pointer to input/output Keccak state | |||
**************************************************/ | |||
void shake256_squeezeblocks(uint8_t *output, size_t nblocks, uint64_t *s) { | |||
keccak_squeezeblocks(output, nblocks, s, SHAKE256_RATE); | |||
void shake256_squeezeblocks(uint8_t *output, size_t nblocks, shake256ctx *state) { | |||
keccak_squeezeblocks(output, nblocks, state->ctx, SHAKE256_RATE); | |||
} | |||
/************************************************* | |||
@@ -629,16 +629,16 @@ void shake128(uint8_t *output, size_t outlen, | |||
const uint8_t *input, size_t inlen) { | |||
size_t nblocks = outlen / SHAKE128_RATE; | |||
uint8_t t[SHAKE128_RATE]; | |||
uint64_t s[25]; | |||
shake128ctx s; | |||
shake128_absorb(s, input, inlen); | |||
shake128_squeezeblocks(output, nblocks, s); | |||
shake128_absorb(&s, input, inlen); | |||
shake128_squeezeblocks(output, nblocks, &s); | |||
output += nblocks * SHAKE128_RATE; | |||
outlen -= nblocks * SHAKE128_RATE; | |||
if (outlen) { | |||
shake128_squeezeblocks(t, 1, s); | |||
shake128_squeezeblocks(t, 1, &s); | |||
for (size_t i = 0; i < outlen; ++i) { | |||
output[i] = t[i]; | |||
} | |||
@@ -659,35 +659,35 @@ void shake256(uint8_t *output, size_t outlen, | |||
const uint8_t *input, size_t inlen) { | |||
size_t nblocks = outlen / SHAKE256_RATE; | |||
uint8_t t[SHAKE256_RATE]; | |||
uint64_t s[25]; | |||
shake256ctx s; | |||
shake256_absorb(s, input, inlen); | |||
shake256_squeezeblocks(output, nblocks, s); | |||
shake256_absorb(&s, input, inlen); | |||
shake256_squeezeblocks(output, nblocks, &s); | |||
output += nblocks * SHAKE256_RATE; | |||
outlen -= nblocks * SHAKE256_RATE; | |||
if (outlen) { | |||
shake256_squeezeblocks(t, 1, s); | |||
shake256_squeezeblocks(t, 1, &s); | |||
for (size_t i = 0; i < outlen; ++i) { | |||
output[i] = t[i]; | |||
} | |||
} | |||
} | |||
void sha3_256_inc_init(uint64_t *s_inc) { | |||
keccak_inc_init(s_inc); | |||
void sha3_256_inc_init(sha3_256incctx *state) { | |||
keccak_inc_init(state->ctx); | |||
} | |||
void sha3_256_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen) { | |||
keccak_inc_absorb(s_inc, SHA3_256_RATE, input, inlen); | |||
void sha3_256_inc_absorb(sha3_256incctx *state, const uint8_t *input, size_t inlen) { | |||
keccak_inc_absorb(state->ctx, SHA3_256_RATE, input, inlen); | |||
} | |||
void sha3_256_inc_finalize(uint8_t *output, uint64_t *s_inc) { | |||
void sha3_256_inc_finalize(uint8_t *output, sha3_256incctx *state) { | |||
uint8_t t[SHA3_256_RATE]; | |||
keccak_inc_finalize(s_inc, SHA3_256_RATE, 0x06); | |||
keccak_inc_finalize(state->ctx, SHA3_256_RATE, 0x06); | |||
keccak_squeezeblocks(t, 1, s_inc, SHA3_256_RATE); | |||
keccak_squeezeblocks(t, 1, state->ctx, SHA3_256_RATE); | |||
for (size_t i = 0; i < 32; i++) { | |||
output[i] = t[i]; | |||
@@ -718,19 +718,19 @@ void sha3_256(uint8_t *output, const uint8_t *input, size_t inlen) { | |||
} | |||
} | |||
void sha3_512_inc_init(uint64_t *s_inc) { | |||
keccak_inc_init(s_inc); | |||
void sha3_512_inc_init(sha3_512incctx *state) { | |||
keccak_inc_init(state->ctx); | |||
} | |||
void sha3_512_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen) { | |||
keccak_inc_absorb(s_inc, SHA3_512_RATE, input, inlen); | |||
void sha3_512_inc_absorb(sha3_512incctx *state, const uint8_t *input, size_t inlen) { | |||
keccak_inc_absorb(state->ctx, SHA3_512_RATE, input, inlen); | |||
} | |||
void sha3_512_inc_finalize(uint8_t *output, uint64_t *s_inc) { | |||
void sha3_512_inc_finalize(uint8_t *output, sha3_512incctx *state) { | |||
uint8_t t[SHA3_512_RATE]; | |||
keccak_inc_finalize(s_inc, SHA3_512_RATE, 0x06); | |||
keccak_inc_finalize(state->ctx, SHA3_512_RATE, 0x06); | |||
keccak_squeezeblocks(t, 1, s_inc, SHA3_512_RATE); | |||
keccak_squeezeblocks(t, 1, state->ctx, SHA3_512_RATE); | |||
for (size_t i = 0; i < 32; i++) { | |||
output[i] = t[i]; | |||
@@ -9,22 +9,53 @@ | |||
#define SHA3_256_RATE 136 | |||
#define SHA3_512_RATE 72 | |||
void shake128_absorb(uint64_t *s, const uint8_t *input, size_t inlen); | |||
void shake128_squeezeblocks(uint8_t *output, size_t nblocks, uint64_t *s); | |||
// Context for incremental API | |||
typedef struct { | |||
uint64_t ctx[26]; | |||
} shake128incctx; | |||
void shake128_inc_init(uint64_t *s_inc); | |||
void shake128_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen); | |||
void shake128_inc_finalize(uint64_t *s_inc); | |||
void shake128_inc_squeeze(uint8_t *output, size_t outlen, uint64_t *s_inc); | |||
// Context for non-incremental API | |||
typedef struct { | |||
uint64_t ctx[25]; | |||
} shake128ctx; | |||
void shake256_absorb(uint64_t *s, const uint8_t *input, size_t inlen); | |||
void shake256_squeezeblocks(uint8_t *output, size_t nblocks, uint64_t *s); | |||
// Context for incremental API | |||
typedef struct { | |||
uint64_t ctx[26]; | |||
} shake256incctx; | |||
void shake256_inc_init(uint64_t *s_inc); | |||
void shake256_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen); | |||
void shake256_inc_finalize(uint64_t *s_inc); | |||
void shake256_inc_squeeze(uint8_t *output, size_t outlen, uint64_t *s_inc); | |||
// Context for non-incremental API | |||
typedef struct { | |||
uint64_t ctx[25]; | |||
} shake256ctx; | |||
// Context for incremental API | |||
typedef struct { | |||
uint64_t ctx[26]; | |||
} sha3_256incctx; | |||
// Context for incremental API | |||
typedef struct { | |||
uint64_t ctx[26]; | |||
} sha3_512incctx; | |||
void shake128_absorb(shake128ctx *state, const uint8_t *input, size_t inlen); | |||
void shake128_squeezeblocks(uint8_t *output, size_t nblocks, shake128ctx *state); | |||
void shake128_inc_init(shake128incctx *state); | |||
void shake128_inc_absorb(shake128incctx *state, const uint8_t *input, size_t inlen); | |||
void shake128_inc_finalize(shake128incctx *state); | |||
void shake128_inc_squeeze(uint8_t *output, size_t outlen, shake128incctx *state); | |||
void shake256_absorb(shake256ctx *state, const uint8_t *input, size_t inlen); | |||
void shake256_squeezeblocks(uint8_t *output, size_t nblocks, shake256ctx *state); | |||
void shake256_inc_init(shake256incctx *state); | |||
void shake256_inc_absorb(shake256incctx *state, const uint8_t *input, size_t inlen); | |||
void shake256_inc_finalize(shake256incctx *state); | |||
void shake256_inc_squeeze(uint8_t *output, size_t outlen, shake256incctx *state); | |||
void shake128(uint8_t *output, size_t outlen, | |||
const uint8_t *input, size_t inlen); | |||
@@ -32,15 +63,15 @@ void shake128(uint8_t *output, size_t outlen, | |||
void shake256(uint8_t *output, size_t outlen, | |||
const uint8_t *input, size_t inlen); | |||
void sha3_256_inc_init(uint64_t *s_inc); | |||
void sha3_256_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen); | |||
void sha3_256_inc_finalize(uint8_t *output, uint64_t *s_inc); | |||
void sha3_256_inc_init(sha3_256incctx *state); | |||
void sha3_256_inc_absorb(sha3_256incctx *state, const uint8_t *input, size_t inlen); | |||
void sha3_256_inc_finalize(uint8_t *output, sha3_256incctx *state); | |||
void sha3_256(uint8_t *output, const uint8_t *input, size_t inlen); | |||
void sha3_512_inc_init(uint64_t *s_inc); | |||
void sha3_512_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen); | |||
void sha3_512_inc_finalize(uint8_t *output, uint64_t *s_inc); | |||
void sha3_512_inc_init(sha3_512incctx *state); | |||
void sha3_512_inc_absorb(sha3_512incctx *state, const uint8_t *input, size_t inlen); | |||
void sha3_512_inc_finalize(uint8_t *output, sha3_512incctx *state); | |||
void sha3_512(uint8_t *output, const uint8_t *input, size_t inlen); | |||
@@ -492,73 +492,73 @@ static const uint8_t iv_512[64] = { | |||
0x6b, 0x5b, 0xe0, 0xcd, 0x19, 0x13, 0x7e, 0x21, 0x79 | |||
}; | |||
void sha224_inc_init(uint8_t *state) { | |||
void sha224_inc_init(sha224ctx *state) { | |||
for (size_t i = 0; i < 32; ++i) { | |||
state[i] = iv_224[i]; | |||
state->ctx[i] = iv_224[i]; | |||
} | |||
for (size_t i = 32; i < 40; ++i) { | |||
state[i] = 0; | |||
state->ctx[i] = 0; | |||
} | |||
} | |||
void sha256_inc_init(uint8_t *state) { | |||
void sha256_inc_init(sha256ctx *state) { | |||
for (size_t i = 0; i < 32; ++i) { | |||
state[i] = iv_256[i]; | |||
state->ctx[i] = iv_256[i]; | |||
} | |||
for (size_t i = 32; i < 40; ++i) { | |||
state[i] = 0; | |||
state->ctx[i] = 0; | |||
} | |||
} | |||
void sha384_inc_init(uint8_t *state) { | |||
void sha384_inc_init(sha384ctx *state) { | |||
for (size_t i = 0; i < 64; ++i) { | |||
state[i] = iv_384[i]; | |||
state->ctx[i] = iv_384[i]; | |||
} | |||
for (size_t i = 64; i < 72; ++i) { | |||
state[i] = 0; | |||
state->ctx[i] = 0; | |||
} | |||
} | |||
void sha512_inc_init(uint8_t *state) { | |||
void sha512_inc_init(sha512ctx *state) { | |||
for (size_t i = 0; i < 64; ++i) { | |||
state[i] = iv_512[i]; | |||
state->ctx[i] = iv_512[i]; | |||
} | |||
for (size_t i = 64; i < 72; ++i) { | |||
state[i] = 0; | |||
state->ctx[i] = 0; | |||
} | |||
} | |||
void sha256_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks) { | |||
uint64_t bytes = load_bigendian_64(state + 32); | |||
void sha256_inc_blocks(sha256ctx *state, const uint8_t *in, size_t inblocks) { | |||
uint64_t bytes = load_bigendian_64(state->ctx + 32); | |||
crypto_hashblocks_sha256(state, in, 64 * inblocks); | |||
crypto_hashblocks_sha256(state->ctx, in, 64 * inblocks); | |||
bytes += 64 * inblocks; | |||
store_bigendian_64(state + 32, bytes); | |||
store_bigendian_64(state->ctx + 32, bytes); | |||
} | |||
void sha224_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks) { | |||
sha256_inc_blocks(state, in, inblocks); | |||
void sha224_inc_blocks(sha224ctx *state, const uint8_t *in, size_t inblocks) { | |||
sha256_inc_blocks((sha256ctx*) state, in, inblocks); | |||
} | |||
void sha512_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks) { | |||
uint64_t bytes = load_bigendian_64(state + 64); | |||
void sha512_inc_blocks(sha512ctx *state, const uint8_t *in, size_t inblocks) { | |||
uint64_t bytes = load_bigendian_64(state->ctx + 64); | |||
crypto_hashblocks_sha256(state, in, 128 * inblocks); | |||
crypto_hashblocks_sha256(state->ctx, in, 128 * inblocks); | |||
bytes += 128 * inblocks; | |||
store_bigendian_64(state + 64, bytes); | |||
store_bigendian_64(state->ctx + 64, bytes); | |||
} | |||
void sha384_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks) { | |||
sha512_inc_blocks(state, in, inblocks); | |||
void sha384_inc_blocks(sha384ctx *state, const uint8_t *in, size_t inblocks) { | |||
sha512_inc_blocks((sha512ctx*) state, in, inblocks); | |||
} | |||
void sha256_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen) { | |||
void sha256_inc_finalize(uint8_t *out, sha256ctx *state, const uint8_t *in, size_t inlen) { | |||
uint8_t padded[128]; | |||
uint64_t bytes = load_bigendian_64(state + 32) + inlen; | |||
uint64_t bytes = load_bigendian_64(state->ctx + 32) + inlen; | |||
crypto_hashblocks_sha256(state, in, inlen); | |||
crypto_hashblocks_sha256(state->ctx, in, inlen); | |||
in += inlen; | |||
inlen &= 63; | |||
in -= inlen; | |||
@@ -580,7 +580,7 @@ void sha256_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t | |||
padded[61] = (uint8_t) (bytes >> 13); | |||
padded[62] = (uint8_t) (bytes >> 5); | |||
padded[63] = (uint8_t) (bytes << 3); | |||
crypto_hashblocks_sha256(state, padded, 64); | |||
crypto_hashblocks_sha256(state->ctx, padded, 64); | |||
} else { | |||
for (size_t i = inlen + 1; i < 120; ++i) { | |||
padded[i] = 0; | |||
@@ -593,27 +593,27 @@ void sha256_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t | |||
padded[125] = (uint8_t) (bytes >> 13); | |||
padded[126] = (uint8_t) (bytes >> 5); | |||
padded[127] = (uint8_t) (bytes << 3); | |||
crypto_hashblocks_sha256(state, padded, 128); | |||
crypto_hashblocks_sha256(state->ctx, padded, 128); | |||
} | |||
for (size_t i = 0; i < 32; ++i) { | |||
out[i] = state[i]; | |||
out[i] = state->ctx[i]; | |||
} | |||
} | |||
void sha224_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen) { | |||
sha256_inc_finalize(state, state, in, inlen); | |||
void sha224_inc_finalize(uint8_t *out, sha224ctx *state, const uint8_t *in, size_t inlen) { | |||
sha256_inc_finalize(state->ctx, (sha256ctx*)state, in, inlen); | |||
for (size_t i = 0; i < 28; ++i) { | |||
out[i] = state[i]; | |||
out[i] = state->ctx[i]; | |||
} | |||
} | |||
void sha512_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen) { | |||
void sha512_inc_finalize(uint8_t *out, sha512ctx *state, const uint8_t *in, size_t inlen) { | |||
uint8_t padded[256]; | |||
uint64_t bytes = load_bigendian_64(state + 64) + inlen; | |||
uint64_t bytes = load_bigendian_64(state->ctx + 64) + inlen; | |||
crypto_hashblocks_sha512(state, in, inlen); | |||
crypto_hashblocks_sha512(state->ctx, in, inlen); | |||
in += inlen; | |||
inlen &= 127; | |||
in -= inlen; | |||
@@ -636,7 +636,7 @@ void sha512_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t | |||
padded[125] = (uint8_t) (bytes >> 13); | |||
padded[126] = (uint8_t) (bytes >> 5); | |||
padded[127] = (uint8_t) (bytes << 3); | |||
crypto_hashblocks_sha512(state, padded, 128); | |||
crypto_hashblocks_sha512(state->ctx, padded, 128); | |||
} else { | |||
for (size_t i = inlen + 1; i < 247; ++i) { | |||
padded[i] = 0; | |||
@@ -650,46 +650,46 @@ void sha512_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t | |||
padded[253] = (uint8_t) (bytes >> 13); | |||
padded[254] = (uint8_t) (bytes >> 5); | |||
padded[255] = (uint8_t) (bytes << 3); | |||
crypto_hashblocks_sha512(state, padded, 256); | |||
crypto_hashblocks_sha512(state->ctx, padded, 256); | |||
} | |||
for (size_t i = 0; i < 64; ++i) { | |||
out[i] = state[i]; | |||
out[i] = state->ctx[i]; | |||
} | |||
} | |||
void sha384_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen) { | |||
sha512_inc_finalize(state, state, in, inlen); | |||
void sha384_inc_finalize(uint8_t *out, sha384ctx *state, const uint8_t *in, size_t inlen) { | |||
sha512_inc_finalize(state->ctx, (sha512ctx*)state, in, inlen); | |||
for (size_t i = 0; i < 48; ++i) { | |||
out[i] = state[i]; | |||
out[i] = state->ctx[i]; | |||
} | |||
} | |||
void sha224(uint8_t *out, const uint8_t *in, size_t inlen) { | |||
uint8_t state[40]; | |||
sha224ctx state; | |||
sha224_inc_init(state); | |||
sha224_inc_finalize(out, state, in, inlen); | |||
sha224_inc_init(&state); | |||
sha224_inc_finalize(out, &state, in, inlen); | |||
} | |||
void sha256(uint8_t *out, const uint8_t *in, size_t inlen) { | |||
uint8_t state[40]; | |||
sha256ctx state; | |||
sha256_inc_init(state); | |||
sha256_inc_finalize(out, state, in, inlen); | |||
sha256_inc_init(&state); | |||
sha256_inc_finalize(out, &state, in, inlen); | |||
} | |||
void sha384(uint8_t *out, const uint8_t *in, size_t inlen) { | |||
uint8_t state[72]; | |||
sha384ctx state; | |||
sha384_inc_init(state); | |||
sha384_inc_finalize(out, state, in, inlen); | |||
sha384_inc_init(&state); | |||
sha384_inc_finalize(out, &state, in, inlen); | |||
} | |||
void sha512(uint8_t *out, const uint8_t *in, size_t inlen) { | |||
uint8_t state[72]; | |||
sha512ctx state; | |||
sha512_inc_init(state); | |||
sha512_inc_finalize(out, state, in, inlen); | |||
sha512_inc_init(&state); | |||
sha512_inc_finalize(out, &state, in, inlen); | |||
} |
@@ -8,24 +8,40 @@ | |||
must be exactly 64 bytes each. | |||
Use the 'finalize' functions for any remaining bytes (possibly over 64). */ | |||
void sha224_inc_init(uint8_t *state); | |||
void sha224_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks); | |||
void sha224_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen); | |||
typedef struct { | |||
uint8_t ctx[40]; | |||
} sha224ctx; | |||
typedef struct { | |||
uint8_t ctx[40]; | |||
} sha256ctx; | |||
typedef struct { | |||
uint8_t ctx[72]; | |||
} sha384ctx; | |||
typedef struct { | |||
uint8_t ctx[72]; | |||
} sha512ctx; | |||
void sha224_inc_init(sha224ctx *state); | |||
void sha224_inc_blocks(sha224ctx *state, const uint8_t *in, size_t inblocks); | |||
void sha224_inc_finalize(uint8_t *out, sha224ctx *state, const uint8_t *in, size_t inlen); | |||
void sha224(uint8_t *out, const uint8_t *in, size_t inlen); | |||
void sha256_inc_init(uint8_t *state); | |||
void sha256_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks); | |||
void sha256_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen); | |||
void sha256_inc_init(sha256ctx *state); | |||
void sha256_inc_blocks(sha256ctx *state, const uint8_t *in, size_t inblocks); | |||
void sha256_inc_finalize(uint8_t *out, sha256ctx *state, const uint8_t *in, size_t inlen); | |||
void sha256(uint8_t *out, const uint8_t *in, size_t inlen); | |||
void sha384_inc_init(uint8_t *state); | |||
void sha384_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks); | |||
void sha384_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen); | |||
void sha384_inc_init(sha384ctx *state); | |||
void sha384_inc_blocks(sha384ctx *state, const uint8_t *in, size_t inblocks); | |||
void sha384_inc_finalize(uint8_t *out, sha384ctx *state, const uint8_t *in, size_t inlen); | |||
void sha384(uint8_t *out, const uint8_t *in, size_t inlen); | |||
void sha512_inc_init(uint8_t *state); | |||
void sha512_inc_blocks(uint8_t *state, const uint8_t *in, size_t inblocks); | |||
void sha512_inc_finalize(uint8_t *out, uint8_t *state, const uint8_t *in, size_t inlen); | |||
void sha512_inc_init(sha512ctx *state); | |||
void sha512_inc_blocks(sha512ctx *state, const uint8_t *in, size_t inblocks); | |||
void sha512_inc_finalize(uint8_t *out, sha512ctx *state, const uint8_t *in, size_t inlen); | |||
void sha512(uint8_t *out, const uint8_t *in, size_t inlen); | |||
#endif |
@@ -0,0 +1,138 @@ | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include "sp800-185.h" | |||
static size_t left_encode(uint8_t *encbuf, size_t value) { | |||
size_t n, i, v; | |||
for (v = value, n = 0; v && (n < sizeof(size_t)); n++, v >>= 8) { | |||
; /* empty */ | |||
} | |||
if (n == 0) { | |||
n = 1; | |||
} | |||
for (i = 1; i <= n; i++) { | |||
encbuf[i] = (uint8_t)(value >> (8 * (n-i))); | |||
} | |||
encbuf[0] = (uint8_t)n; | |||
return n + 1; | |||
} | |||
void cshake128_inc_init(shake128incctx *state, const uint8_t *name, size_t namelen, const uint8_t *cstm, size_t cstmlen) { | |||
uint8_t encbuf[sizeof(size_t)+1]; | |||
shake128_inc_init(state); | |||
shake128_inc_absorb(state, encbuf, left_encode(encbuf, SHAKE128_RATE)); | |||
shake128_inc_absorb(state, encbuf, left_encode(encbuf, namelen * 8)); | |||
shake128_inc_absorb(state, name, namelen); | |||
shake128_inc_absorb(state, encbuf, left_encode(encbuf, cstmlen * 8)); | |||
shake128_inc_absorb(state, cstm, cstmlen); | |||
if (state->ctx[25] != 0) { | |||
state->ctx[25] = SHAKE128_RATE - 1; | |||
encbuf[0] = 0; | |||
shake128_inc_absorb(state, encbuf, 1); | |||
} | |||
} | |||
void cshake128_inc_absorb(shake128incctx *state, const uint8_t *input, size_t inlen) { | |||
shake128_inc_absorb(state, input, inlen); | |||
} | |||
void cshake128_inc_finalize(shake128incctx *state) { | |||
state->ctx[state->ctx[25] >> 3] ^= (uint64_t)0x04 << (8 * (state->ctx[25] & 0x07)); | |||
state->ctx[(SHAKE128_RATE - 1) >> 3] ^= (uint64_t)128 << (8 * ((SHAKE128_RATE - 1) & 0x07)); | |||
state->ctx[25] = 0; | |||
} | |||
void cshake128_inc_squeeze(uint8_t *output, size_t outlen, shake128incctx *state) { | |||
shake128_inc_squeeze(output, outlen, state); | |||
} | |||
void cshake256_inc_init(shake256incctx *state, const uint8_t *name, size_t namelen, const uint8_t *cstm, size_t cstmlen) { | |||
uint8_t encbuf[sizeof(size_t)+1]; | |||
shake256_inc_init(state); | |||
shake256_inc_absorb(state, encbuf, left_encode(encbuf, SHAKE256_RATE)); | |||
shake256_inc_absorb(state, encbuf, left_encode(encbuf, namelen * 8)); | |||
shake256_inc_absorb(state, name, namelen); | |||
shake256_inc_absorb(state, encbuf, left_encode(encbuf, cstmlen * 8)); | |||
shake256_inc_absorb(state, cstm, cstmlen); | |||
if (state->ctx[25] != 0) { | |||
state->ctx[25] = SHAKE256_RATE - 1; | |||
encbuf[0] = 0; | |||
shake256_inc_absorb(state, encbuf, 1); | |||
} | |||
} | |||
void cshake256_inc_absorb(shake256incctx *state, const uint8_t *input, size_t inlen) { | |||
shake256_inc_absorb(state, input, inlen); | |||
} | |||
void cshake256_inc_finalize(shake256incctx *state) { | |||
state->ctx[state->ctx[25] >> 3] ^= (uint64_t)0x04 << (8 * (state->ctx[25] & 0x07)); | |||
state->ctx[(SHAKE256_RATE - 1) >> 3] ^= (uint64_t)128 << (8 * ((SHAKE256_RATE - 1) & 0x07)); | |||
state->ctx[25] = 0; | |||
} | |||
void cshake256_inc_squeeze(uint8_t *output, size_t outlen, shake256incctx *state) { | |||
shake256_inc_squeeze(output, outlen, state); | |||
} | |||
/************************************************* | |||
* Name: cshake128 | |||
* | |||
* Description: cSHAKE128 XOF with non-incremental API | |||
* | |||
* Arguments: - uint8_t *output: pointer to output | |||
* - size_t outlen: requested output length in bytes | |||
* - const uint8_t *name: pointer to function-name string | |||
* - size_t namelen: length of function-name string in bytes | |||
* - const uint8_t *cstm: pointer to non-empty customization string | |||
* - size_t cstmlen: length of customization string in bytes | |||
* - const uint8_t *input: pointer to input | |||
* - size_t inlen: length of input in bytes | |||
**************************************************/ | |||
void cshake128(uint8_t *output, size_t outlen, | |||
const uint8_t *name, size_t namelen, | |||
const uint8_t *cstm, size_t cstmlen, | |||
const uint8_t *input, size_t inlen) { | |||
shake128incctx state; | |||
cshake128_inc_init(&state, name, namelen, cstm, cstmlen); | |||
cshake128_inc_absorb(&state, input, inlen); | |||
cshake128_inc_finalize(&state); | |||
cshake128_inc_squeeze(output, outlen, &state); | |||
} | |||
/************************************************* | |||
* Name: cshake256 | |||
* | |||
* Description: cSHAKE256 XOF with non-incremental API | |||
* | |||
* Arguments: - uint8_t *output: pointer to output | |||
* - size_t outlen: requested output length in bytes | |||
* - const uint8_t *name: pointer to function-name string | |||
* - size_t namelen: length of function-name string in bytes | |||
* - const uint8_t *cstm: pointer to non-empty customization string | |||
* - size_t cstmlen: length of customization string in bytes | |||
* - const uint8_t *input: pointer to input | |||
* - size_t inlen: length of input in bytes | |||
**************************************************/ | |||
void cshake256(uint8_t *output, size_t outlen, | |||
const uint8_t *name, size_t namelen, | |||
const uint8_t *cstm, size_t cstmlen, | |||
const uint8_t *input, size_t inlen) { | |||
shake256incctx state; | |||
cshake256_inc_init(&state, name, namelen, cstm, cstmlen); | |||
cshake256_inc_absorb(&state, input, inlen); | |||
cshake256_inc_finalize(&state); | |||
cshake256_inc_squeeze(output, outlen, &state); | |||
} |
@@ -0,0 +1,23 @@ | |||
#ifndef SP800_185_H | |||
#define SP800_185_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include "fips202.h" | |||
void cshake128_inc_init(shake128incctx *state, const uint8_t *name, size_t namelen, const uint8_t *cstm, size_t cstmlen); | |||
void cshake128_inc_absorb(shake128incctx *state, const uint8_t *input, size_t inlen); | |||
void cshake128_inc_finalize(shake128incctx *state); | |||
void cshake128_inc_squeeze(uint8_t *output, size_t outlen, shake128incctx *state); | |||
void cshake128(uint8_t *output, size_t outlen, const uint8_t *name, size_t namelen, const uint8_t *cstm, size_t cstmlen, const uint8_t *input, size_t inlen); | |||
void cshake256_inc_init(shake256incctx *state, const uint8_t *name, size_t namelen, const uint8_t *cstm, size_t cstmlen); | |||
void cshake256_inc_absorb(shake256incctx *state, const uint8_t *input, size_t inlen); | |||
void cshake256_inc_finalize(shake256incctx *state); | |||
void cshake256_inc_squeeze(uint8_t *output, size_t outlen, shake256incctx *state); | |||
void cshake256(uint8_t *output, size_t outlen, const uint8_t *name, size_t namelen, const uint8_t* cstm, size_t cstmlen, const uint8_t *input, size_t inlen); | |||
#endif |
@@ -23,3 +23,5 @@ auxiliary-submitters: | |||
implementations: | |||
- name: clean | |||
version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/d5bbd0417ba111b08a959c0042a1dcc65fb14a89 | |||
- name: opt | |||
version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/d5bbd0417ba111b08a959c0042a1dcc65fb14a89 |
@@ -4,7 +4,7 @@ LIB=libfrodokem1344aes_clean.a | |||
HEADERS=api.h params.h common.h | |||
OBJECTS=kem.o matrix_aes.o noise.o util.o | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
@@ -0,0 +1,21 @@ | |||
MIT License | |||
Copyright (c) Microsoft Corporation. All rights reserved. | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE |
@@ -0,0 +1,19 @@ | |||
# This Makefile can be used with GNU Make or BSD Make | |||
LIB=libfrodokem1344aes_opt.a | |||
HEADERS=api.h params.h common.h | |||
OBJECTS=kem.o matrix_aes.o noise.o util.o | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
%.o: %.c $(HEADERS) | |||
$(CC) $(CFLAGS) -c -o $@ $< | |||
$(LIB): $(OBJECTS) | |||
$(AR) -r $@ $(OBJECTS) | |||
clean: | |||
$(RM) $(OBJECTS) | |||
$(RM) $(LIB) |
@@ -0,0 +1,19 @@ | |||
# This Makefile can be used with Microsoft Visual Studio's nmake using the command: | |||
# nmake /f Makefile.Microsoft_nmake | |||
LIBRARY=libfrodokem1344aes_opt.lib | |||
OBJECTS=kem.obj matrix_aes.obj noise.obj util.obj | |||
CFLAGS=/nologo /I ..\..\..\common /W4 /WX | |||
all: $(LIBRARY) | |||
# Make sure objects are recompiled if headers change. | |||
$(OBJECTS): *.h | |||
$(LIBRARY): $(OBJECTS) | |||
LIB.EXE /NOLOGO /WX /OUT:$@ $** | |||
clean: | |||
-DEL $(OBJECTS) | |||
-DEL $(LIBRARY) |
@@ -0,0 +1,20 @@ | |||
#ifndef PQCLEAN_FRODOKEM1344AES_OPT_API_H | |||
#define PQCLEAN_FRODOKEM1344AES_OPT_API_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#define PQCLEAN_FRODOKEM1344AES_OPT_CRYPTO_SECRETKEYBYTES 43088 // sizeof(s) + CRYPTO_PUBLICKEYBYTES + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH | |||
#define PQCLEAN_FRODOKEM1344AES_OPT_CRYPTO_PUBLICKEYBYTES 21520 // sizeof(seed_A) + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 | |||
#define PQCLEAN_FRODOKEM1344AES_OPT_CRYPTO_BYTES 32 | |||
#define PQCLEAN_FRODOKEM1344AES_OPT_CRYPTO_CIPHERTEXTBYTES 21632 // (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + (PARAMS_LOGQ*PARAMS_NBAR*PARAMS_NBAR)/8 | |||
#define PQCLEAN_FRODOKEM1344AES_OPT_CRYPTO_ALGNAME "FrodoKEM-1344-AES" | |||
int PQCLEAN_FRODOKEM1344AES_OPT_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); | |||
int PQCLEAN_FRODOKEM1344AES_OPT_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); | |||
int PQCLEAN_FRODOKEM1344AES_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); | |||
#endif |
@@ -0,0 +1,19 @@ | |||
#ifndef COMMON_H | |||
#define COMMON_H | |||
int PQCLEAN_FRODOKEM1344AES_OPT_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); | |||
int PQCLEAN_FRODOKEM1344AES_OPT_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); | |||
void PQCLEAN_FRODOKEM1344AES_OPT_sample_n(uint16_t *s, size_t n); | |||
void PQCLEAN_FRODOKEM1344AES_OPT_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s); | |||
void PQCLEAN_FRODOKEM1344AES_OPT_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e); | |||
void PQCLEAN_FRODOKEM1344AES_OPT_add(uint16_t *out, const uint16_t *a, const uint16_t *b); | |||
void PQCLEAN_FRODOKEM1344AES_OPT_sub(uint16_t *out, const uint16_t *a, const uint16_t *b); | |||
void PQCLEAN_FRODOKEM1344AES_OPT_key_encode(uint16_t *out, const uint16_t *in); | |||
void PQCLEAN_FRODOKEM1344AES_OPT_key_decode(uint16_t *out, const uint16_t *in); | |||
void PQCLEAN_FRODOKEM1344AES_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); | |||
void PQCLEAN_FRODOKEM1344AES_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); | |||
void PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes(uint8_t *mem, size_t n); | |||
uint16_t PQCLEAN_FRODOKEM1344AES_OPT_LE_TO_UINT16(uint16_t n); | |||
uint16_t PQCLEAN_FRODOKEM1344AES_OPT_UINT16_TO_LE(uint16_t n); | |||
#endif |
@@ -0,0 +1,238 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: Key Encapsulation Mechanism (KEM) based on Frodo | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "fips202.h" | |||
#include "randombytes.h" | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
int PQCLEAN_FRODOKEM1344AES_OPT_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { | |||
// FrodoKEM's key generation | |||
// Outputs: public key pk ( BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 bytes) | |||
// secret key sk (CRYPTO_BYTES + BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH bytes) | |||
uint8_t *pk_seedA = &pk[0]; | |||
uint8_t *pk_b = &pk[BYTES_SEED_A]; | |||
uint8_t *sk_s = &sk[0]; | |||
uint8_t *sk_pk = &sk[CRYPTO_BYTES]; | |||
uint8_t *sk_S = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; | |||
uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t S[2 * PARAMS_N * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *E = &S[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint8_t randomness[2 * CRYPTO_BYTES + BYTES_SEED_A]; // contains secret data via randomness_s and randomness_seedSE | |||
uint8_t *randomness_s = &randomness[0]; // contains secret data | |||
uint8_t *randomness_seedSE = &randomness[CRYPTO_BYTES]; // contains secret data | |||
uint8_t *randomness_z = &randomness[2 * CRYPTO_BYTES]; | |||
uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data | |||
// Generate the secret value s, the seed for S and E, and the seed for the seed for A. Add seed_A to the public key | |||
randombytes(randomness, CRYPTO_BYTES + CRYPTO_BYTES + BYTES_SEED_A); | |||
shake(pk_seedA, BYTES_SEED_A, randomness_z, BYTES_SEED_A); | |||
// Generate S and E, and compute B = A*S + E. Generate A on-the-fly | |||
shake_input_seedSE[0] = 0x5F; | |||
memcpy(&shake_input_seedSE[1], randomness_seedSE, CRYPTO_BYTES); | |||
shake((uint8_t *)S, 2 * PARAMS_N * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < 2 * PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM1344AES_OPT_LE_TO_UINT16(S[i]); | |||
} | |||
PQCLEAN_FRODOKEM1344AES_OPT_sample_n(S, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM1344AES_OPT_sample_n(E, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM1344AES_OPT_mul_add_as_plus_e(B, S, E, pk); | |||
// Encode the second part of the public key | |||
PQCLEAN_FRODOKEM1344AES_OPT_pack(pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, B, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Add s, pk and S to the secret key | |||
memcpy(sk_s, randomness_s, CRYPTO_BYTES); | |||
memcpy(sk_pk, pk, CRYPTO_PUBLICKEYBYTES); | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM1344AES_OPT_UINT16_TO_LE(S[i]); | |||
} | |||
memcpy(sk_S, S, 2 * PARAMS_N * PARAMS_NBAR); | |||
// Add H(pk) to the secret key | |||
shake(sk_pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes((uint8_t *)E, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes(randomness, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} | |||
int PQCLEAN_FRODOKEM1344AES_OPT_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { | |||
// FrodoKEM's key encapsulation | |||
const uint8_t *pk_seedA = &pk[0]; | |||
const uint8_t *pk_b = &pk[BYTES_SEED_A]; | |||
uint8_t *ct_c1 = &ct[0]; | |||
uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t V[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via mu | |||
uint8_t *pkh = &G2in[0]; | |||
uint8_t *mu = &G2in[BYTES_PKHASH]; // contains secret data | |||
uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data | |||
uint8_t *seedSE = &G2out[0]; // contains secret data | |||
uint8_t *k = &G2out[CRYPTO_BYTES]; // contains secret data | |||
uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k | |||
uint8_t *Fin_ct = &Fin[0]; | |||
uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data | |||
uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data | |||
// pkh <- G_1(pk), generate random mu, compute (seedSE || k) = G_2(pkh || mu) | |||
shake(pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); | |||
randombytes(mu, BYTES_MU); | |||
shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); | |||
// Generate Sp and Ep, and compute Bp = Sp*A + Ep. Generate A on-the-fly | |||
shake_input_seedSE[0] = 0x96; | |||
memcpy(&shake_input_seedSE[1], seedSE, CRYPTO_BYTES); | |||
shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { | |||
Sp[i] = PQCLEAN_FRODOKEM1344AES_OPT_LE_TO_UINT16(Sp[i]); | |||
} | |||
PQCLEAN_FRODOKEM1344AES_OPT_sample_n(Sp, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM1344AES_OPT_sample_n(Ep, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM1344AES_OPT_mul_add_sa_plus_e(Bp, Sp, Ep, pk_seedA); | |||
PQCLEAN_FRODOKEM1344AES_OPT_pack(ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, Bp, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Generate Epp, and compute V = Sp*B + Epp | |||
PQCLEAN_FRODOKEM1344AES_OPT_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM1344AES_OPT_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM1344AES_OPT_mul_add_sb_plus_e(V, B, Sp, Epp); | |||
// Encode mu, and compute C = V + enc(mu) (mod q) | |||
PQCLEAN_FRODOKEM1344AES_OPT_key_encode(C, (uint16_t *)mu); | |||
PQCLEAN_FRODOKEM1344AES_OPT_add(C, V, C); | |||
PQCLEAN_FRODOKEM1344AES_OPT_pack(ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, C, PARAMS_NBAR * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Compute ss = F(ct||KK) | |||
memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); | |||
memcpy(Fin_k, k, CRYPTO_BYTES); | |||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes((uint8_t *)V, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes(mu, BYTES_MU); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes(G2out, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes(Fin_k, CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} | |||
int PQCLEAN_FRODOKEM1344AES_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { | |||
// FrodoKEM's key decapsulation | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t W[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t CC[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t BBp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
const uint8_t *ct_c1 = &ct[0]; | |||
const uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; | |||
const uint8_t *sk_s = &sk[0]; | |||
const uint8_t *sk_pk = &sk[CRYPTO_BYTES]; | |||
const uint16_t *sk_S = (uint16_t *) &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; | |||
uint16_t S[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
const uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; | |||
const uint8_t *pk_seedA = &sk_pk[0]; | |||
const uint8_t *pk_b = &sk_pk[BYTES_SEED_A]; | |||
uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via muprime | |||
uint8_t *pkh = &G2in[0]; | |||
uint8_t *muprime = &G2in[BYTES_PKHASH]; // contains secret data | |||
uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data | |||
uint8_t *seedSEprime = &G2out[0]; // contains secret data | |||
uint8_t *kprime = &G2out[CRYPTO_BYTES]; // contains secret data | |||
uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k | |||
uint8_t *Fin_ct = &Fin[0]; | |||
uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data | |||
uint8_t shake_input_seedSEprime[1 + CRYPTO_BYTES]; // contains secret data | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM1344AES_OPT_LE_TO_UINT16(sk_S[i]); | |||
} | |||
// Compute W = C - Bp*S (mod q), and decode the randomness mu | |||
PQCLEAN_FRODOKEM1344AES_OPT_unpack(Bp, PARAMS_N * PARAMS_NBAR, ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM1344AES_OPT_unpack(C, PARAMS_NBAR * PARAMS_NBAR, ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM1344AES_OPT_mul_bs(W, Bp, S); | |||
PQCLEAN_FRODOKEM1344AES_OPT_sub(W, C, W); | |||
PQCLEAN_FRODOKEM1344AES_OPT_key_decode((uint16_t *)muprime, W); | |||
// Generate (seedSE' || k') = G_2(pkh || mu') | |||
memcpy(pkh, sk_pkh, BYTES_PKHASH); | |||
shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); | |||
// Generate Sp and Ep, and compute BBp = Sp*A + Ep. Generate A on-the-fly | |||
shake_input_seedSEprime[0] = 0x96; | |||
memcpy(&shake_input_seedSEprime[1], seedSEprime, CRYPTO_BYTES); | |||
shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSEprime, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { | |||
Sp[i] = PQCLEAN_FRODOKEM1344AES_OPT_LE_TO_UINT16(Sp[i]); | |||
} | |||
PQCLEAN_FRODOKEM1344AES_OPT_sample_n(Sp, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM1344AES_OPT_sample_n(Ep, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM1344AES_OPT_mul_add_sa_plus_e(BBp, Sp, Ep, pk_seedA); | |||
// Generate Epp, and compute W = Sp*B + Epp | |||
PQCLEAN_FRODOKEM1344AES_OPT_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM1344AES_OPT_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM1344AES_OPT_mul_add_sb_plus_e(W, B, Sp, Epp); | |||
// Encode mu, and compute CC = W + enc(mu') (mod q) | |||
PQCLEAN_FRODOKEM1344AES_OPT_key_encode(CC, (uint16_t *)muprime); | |||
PQCLEAN_FRODOKEM1344AES_OPT_add(CC, W, CC); | |||
// Prepare input to F | |||
memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); | |||
// Reducing BBp modulo q | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
BBp[i] = BBp[i] & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
// Is (Bp == BBp & C == CC) = true | |||
if (memcmp(Bp, BBp, 2 * PARAMS_N * PARAMS_NBAR) == 0 && memcmp(C, CC, 2 * PARAMS_NBAR * PARAMS_NBAR) == 0) { | |||
// Load k' to do ss = F(ct || k') | |||
memcpy(Fin_k, kprime, CRYPTO_BYTES); | |||
} else { | |||
// Load s to do ss = F(ct || s) | |||
memcpy(Fin_k, sk_s, CRYPTO_BYTES); | |||
} | |||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes((uint8_t *)W, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes(muprime, BYTES_MU); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes(G2out, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes(Fin_k, CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes(shake_input_seedSEprime, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} |
@@ -0,0 +1,125 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: matrix arithmetic functions used by the KEM | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "aes.h" | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
int PQCLEAN_FRODOKEM1344AES_OPT_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { | |||
// Generate-and-multiply: generate matrix A (N x N) row-wise, multiply by s on the right. | |||
// Inputs: s, e (N x N_BAR) | |||
// Output: out = A*s + e (N x N_BAR) | |||
int k; | |||
uint16_t i, j; | |||
int16_t a_row[4 * PARAMS_N]; | |||
for (i = 0; i < (PARAMS_N * PARAMS_NBAR); i += 2) { | |||
*((uint32_t *)&out[i]) = *((uint32_t *)&e[i]); | |||
} | |||
int16_t a_row_temp[4 * PARAMS_N] = {0}; // Take four lines of A at once | |||
aes128ctx ctx128; | |||
aes128_keyexp(&ctx128, seed_A); | |||
for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { | |||
a_row_temp[j + 1 + 0 * PARAMS_N] = PQCLEAN_FRODOKEM1344AES_OPT_UINT16_TO_LE(j); // Loading values in the little-endian order | |||
a_row_temp[j + 1 + 1 * PARAMS_N] = PQCLEAN_FRODOKEM1344AES_OPT_UINT16_TO_LE(j); | |||
a_row_temp[j + 1 + 2 * PARAMS_N] = PQCLEAN_FRODOKEM1344AES_OPT_UINT16_TO_LE(j); | |||
a_row_temp[j + 1 + 3 * PARAMS_N] = PQCLEAN_FRODOKEM1344AES_OPT_UINT16_TO_LE(j); | |||
} | |||
for (i = 0; i < PARAMS_N; i += 4) { | |||
for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { // Go through A, four rows at a time | |||
a_row_temp[j + 0 * PARAMS_N] = PQCLEAN_FRODOKEM1344AES_OPT_UINT16_TO_LE(i + 0); // Loading values in the little-endian order | |||
a_row_temp[j + 1 * PARAMS_N] = PQCLEAN_FRODOKEM1344AES_OPT_UINT16_TO_LE(i + 1); | |||
a_row_temp[j + 2 * PARAMS_N] = PQCLEAN_FRODOKEM1344AES_OPT_UINT16_TO_LE(i + 2); | |||
a_row_temp[j + 3 * PARAMS_N] = PQCLEAN_FRODOKEM1344AES_OPT_UINT16_TO_LE(i + 3); | |||
} | |||
aes128_ecb((uint8_t *)a_row, (uint8_t *)a_row_temp, 4 * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); | |||
for (k = 0; k < 4 * PARAMS_N; k++) { | |||
a_row[k] = PQCLEAN_FRODOKEM1344AES_OPT_LE_TO_UINT16(a_row[k]); | |||
} | |||
for (k = 0; k < PARAMS_NBAR; k++) { | |||
uint16_t sum[4] = {0}; | |||
for (j = 0; j < PARAMS_N; j++) { // Matrix-vector multiplication | |||
uint16_t sp = s[k * PARAMS_N + j]; | |||
sum[0] += a_row[0 * PARAMS_N + j] * sp; // Go through four lines with same s | |||
sum[1] += a_row[1 * PARAMS_N + j] * sp; | |||
sum[2] += a_row[2 * PARAMS_N + j] * sp; | |||
sum[3] += a_row[3 * PARAMS_N + j] * sp; | |||
} | |||
out[(i + 0)*PARAMS_NBAR + k] += sum[0]; | |||
out[(i + 2)*PARAMS_NBAR + k] += sum[2]; | |||
out[(i + 1)*PARAMS_NBAR + k] += sum[1]; | |||
out[(i + 3)*PARAMS_NBAR + k] += sum[3]; | |||
} | |||
} | |||
return 1; | |||
} | |||
int PQCLEAN_FRODOKEM1344AES_OPT_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { | |||
// Generate-and-multiply: generate matrix A (N x N) column-wise, multiply by s' on the left. | |||
// Inputs: s', e' (N_BAR x N) | |||
// Output: out = s'*A + e' (N_BAR x N) | |||
int j; | |||
uint16_t i, kk; | |||
for (i = 0; i < (PARAMS_N * PARAMS_NBAR); i += 2) { | |||
*((uint32_t *)&out[i]) = *((uint32_t *)&e[i]); | |||
} | |||
int k; | |||
uint16_t a_cols[PARAMS_N * PARAMS_STRIPE_STEP] = {0}; | |||
uint16_t a_cols_t[PARAMS_N * PARAMS_STRIPE_STEP]; | |||
uint16_t a_cols_temp[PARAMS_N * PARAMS_STRIPE_STEP] = {0}; | |||
aes128ctx ctx128; | |||
aes128_keyexp(&ctx128, seed_A); | |||
for (i = 0, j = 0; i < PARAMS_N; i++, j += PARAMS_STRIPE_STEP) { | |||
a_cols_temp[j] = PQCLEAN_FRODOKEM1344AES_OPT_UINT16_TO_LE(i); // Loading values in the little-endian order | |||
} | |||
for (kk = 0; kk < PARAMS_N; kk += PARAMS_STRIPE_STEP) { // Go through A's columns, 8 (== PARAMS_STRIPE_STEP) columns at a time. | |||
for (i = 0; i < (PARAMS_N * PARAMS_STRIPE_STEP); i += PARAMS_STRIPE_STEP) { | |||
a_cols_temp[i + 1] = PQCLEAN_FRODOKEM1344AES_OPT_UINT16_TO_LE(kk); // Loading values in the little-endian order | |||
} | |||
aes128_ecb((uint8_t *)a_cols, (uint8_t *)a_cols_temp, PARAMS_N * PARAMS_STRIPE_STEP * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); | |||
for (i = 0; i < PARAMS_N; i++) { // Transpose a_cols to have access to it in the column-major order. | |||
for (k = 0; k < PARAMS_STRIPE_STEP; k++) { | |||
a_cols_t[k * PARAMS_N + i] = PQCLEAN_FRODOKEM1344AES_OPT_LE_TO_UINT16(a_cols[i * PARAMS_STRIPE_STEP + k]); | |||
} | |||
} | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
for (k = 0; k < PARAMS_STRIPE_STEP; k += PARAMS_PARALLEL) { | |||
uint16_t sum[PARAMS_PARALLEL] = {0}; | |||
for (j = 0; j < PARAMS_N; j++) { // Matrix-vector multiplication | |||
uint16_t sp = s[i * PARAMS_N + j]; | |||
sum[0] += sp * a_cols_t[(k + 0) * PARAMS_N + j]; | |||
sum[1] += sp * a_cols_t[(k + 1) * PARAMS_N + j]; | |||
sum[2] += sp * a_cols_t[(k + 2) * PARAMS_N + j]; | |||
sum[3] += sp * a_cols_t[(k + 3) * PARAMS_N + j]; | |||
} | |||
out[i * PARAMS_N + kk + k + 0] += sum[0]; | |||
out[i * PARAMS_N + kk + k + 2] += sum[2]; | |||
out[i * PARAMS_N + kk + k + 1] += sum[1]; | |||
out[i * PARAMS_N + kk + k + 3] += sum[3]; | |||
} | |||
} | |||
} | |||
return 1; | |||
} |
@@ -0,0 +1,35 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: noise sampling functions | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
static uint16_t CDF_TABLE[CDF_TABLE_LEN] = CDF_TABLE_DATA; | |||
void PQCLEAN_FRODOKEM1344AES_OPT_sample_n(uint16_t *s, size_t n) { | |||
// Fills vector s with n samples from the noise distribution which requires 16 bits to sample. | |||
// The distribution is specified by its CDF. | |||
// Input: pseudo-random values (2*n bytes) passed in s. The input is overwritten by the output. | |||
size_t i; | |||
unsigned int j; | |||
for (i = 0; i < n; ++i) { | |||
uint16_t sample = 0; | |||
uint16_t prnd = s[i] >> 1; // Drop the least significant bit | |||
uint16_t sign = s[i] & 0x1; // Pick the least significant bit | |||
// No need to compare with the last value. | |||
for (j = 0; j < (unsigned int)(CDF_TABLE_LEN - 1); j++) { | |||
// Constant time comparison: 1 if CDF_TABLE[j] < s, 0 otherwise. Uses the fact that CDF_TABLE[j] and s fit in 15 bits. | |||
sample += (uint16_t)(CDF_TABLE[j] - prnd) >> 15; | |||
} | |||
// Assuming that sign is either 0 or 1, flips sample iff sign = 1 | |||
s[i] = ((-sign) ^ sample) + sign; | |||
} | |||
} |
@@ -0,0 +1,27 @@ | |||
#ifndef PARAMS_H | |||
#define PARAMS_H | |||
#define CRYPTO_SECRETKEYBYTES PQCLEAN_FRODOKEM1344AES_OPT_CRYPTO_SECRETKEYBYTES | |||
#define CRYPTO_PUBLICKEYBYTES PQCLEAN_FRODOKEM1344AES_OPT_CRYPTO_PUBLICKEYBYTES | |||
#define CRYPTO_BYTES PQCLEAN_FRODOKEM1344AES_OPT_CRYPTO_BYTES | |||
#define CRYPTO_CIPHERTEXTBYTES PQCLEAN_FRODOKEM1344AES_OPT_CRYPTO_CIPHERTEXTBYTES | |||
#define PARAMS_N 1344 | |||
#define PARAMS_NBAR 8 | |||
#define PARAMS_LOGQ 16 | |||
#define PARAMS_Q (1 << PARAMS_LOGQ) | |||
#define PARAMS_EXTRACTED_BITS 4 | |||
#define PARAMS_STRIPE_STEP 8 | |||
#define PARAMS_PARALLEL 4 | |||
#define BYTES_SEED_A 16 | |||
#define BYTES_MU ((PARAMS_EXTRACTED_BITS * PARAMS_NBAR * PARAMS_NBAR) / 8) | |||
#define BYTES_PKHASH CRYPTO_BYTES | |||
// Selecting SHAKE XOF function for the KEM and noise sampling | |||
#define shake shake256 | |||
// CDF table | |||
#define CDF_TABLE_DATA {9142, 23462, 30338, 32361, 32725, 32765, 32767} | |||
#define CDF_TABLE_LEN 7 | |||
#endif |
@@ -0,0 +1,235 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: additional functions for FrodoKEM | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
#define min(x, y) (((x) < (y)) ? (x) : (y)) | |||
uint16_t PQCLEAN_FRODOKEM1344AES_OPT_LE_TO_UINT16(uint16_t n) { | |||
return (((uint8_t *) &n)[0] | (((uint8_t *) &n)[1] << 8)); | |||
} | |||
uint16_t PQCLEAN_FRODOKEM1344AES_OPT_UINT16_TO_LE(uint16_t n) { | |||
uint16_t y; | |||
uint8_t *z = (uint8_t *) &y; | |||
z[0] = n & 0xFF; | |||
z[1] = (n & 0xFF00) >> 8; | |||
return y; | |||
} | |||
void PQCLEAN_FRODOKEM1344AES_OPT_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s) { | |||
// Multiply by s on the right | |||
// Inputs: b (N_BAR x N), s (N x N_BAR) | |||
// Output: out = b*s (N_BAR x N_BAR) | |||
int i, j, k; | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
for (j = 0; j < PARAMS_NBAR; j++) { | |||
out[i * PARAMS_NBAR + j] = 0; | |||
for (k = 0; k < PARAMS_N; k++) { | |||
out[i * PARAMS_NBAR + j] += b[i * PARAMS_N + k] * s[j * PARAMS_N + k]; | |||
} | |||
out[i * PARAMS_NBAR + j] = (uint32_t)(out[i * PARAMS_NBAR + j]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM1344AES_OPT_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e) { | |||
// Multiply by s on the left | |||
// Inputs: b (N x N_BAR), s (N_BAR x N), e (N_BAR x N_BAR) | |||
// Output: out = s*b + e (N_BAR x N_BAR) | |||
int i, j, k; | |||
for (k = 0; k < PARAMS_NBAR; k++) { | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
out[k * PARAMS_NBAR + i] = e[k * PARAMS_NBAR + i]; | |||
for (j = 0; j < PARAMS_N; j++) { | |||
out[k * PARAMS_NBAR + i] += s[k * PARAMS_N + j] * b[j * PARAMS_NBAR + i]; | |||
} | |||
out[k * PARAMS_NBAR + i] = (uint32_t)(out[k * PARAMS_NBAR + i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM1344AES_OPT_add(uint16_t *out, const uint16_t *a, const uint16_t *b) { | |||
// Add a and b | |||
// Inputs: a, b (N_BAR x N_BAR) | |||
// Output: c = a + b | |||
for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { | |||
out[i] = (a[i] + b[i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM1344AES_OPT_sub(uint16_t *out, const uint16_t *a, const uint16_t *b) { | |||
// Subtract a and b | |||
// Inputs: a, b (N_BAR x N_BAR) | |||
// Output: c = a - b | |||
for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { | |||
out[i] = (a[i] - b[i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM1344AES_OPT_key_encode(uint16_t *out, const uint16_t *in) { | |||
// Encoding | |||
unsigned int i, j, npieces_word = 8; | |||
unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; | |||
uint64_t temp, mask = ((uint64_t)1 << PARAMS_EXTRACTED_BITS) - 1; | |||
uint16_t *pos = out; | |||
for (i = 0; i < nwords; i++) { | |||
temp = 0; | |||
for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { | |||
temp |= ((uint64_t)((uint8_t *)in)[i * PARAMS_EXTRACTED_BITS + j]) << (8 * j); | |||
} | |||
for (j = 0; j < npieces_word; j++) { | |||
*pos = (uint16_t)((temp & mask) << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS)); | |||
temp >>= PARAMS_EXTRACTED_BITS; | |||
pos++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM1344AES_OPT_key_decode(uint16_t *out, const uint16_t *in) { | |||
// Decoding | |||
unsigned int i, j, index = 0, npieces_word = 8; | |||
unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; | |||
uint16_t temp, maskex = ((uint16_t)1 << PARAMS_EXTRACTED_BITS) - 1, maskq = ((uint16_t)1 << PARAMS_LOGQ) - 1; | |||
uint8_t *pos = (uint8_t *)out; | |||
uint64_t templong; | |||
for (i = 0; i < nwords; i++) { | |||
templong = 0; | |||
for (j = 0; j < npieces_word; j++) { // temp = floor(in*2^{-11}+0.5) | |||
temp = ((in[index] & maskq) + (1 << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS - 1))) >> (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS); | |||
templong |= ((uint64_t)(temp & maskex)) << (PARAMS_EXTRACTED_BITS * j); | |||
index++; | |||
} | |||
for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { | |||
pos[i * PARAMS_EXTRACTED_BITS + j] = (templong >> (8 * j)) & 0xFF; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM1344AES_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb) { | |||
// Pack the input uint16 vector into a char output vector, copying lsb bits from each input element. | |||
// If inlen * lsb / 8 > outlen, only outlen * 8 bits are copied. | |||
memset(out, 0, outlen); | |||
size_t i = 0; // whole bytes already filled in | |||
size_t j = 0; // whole uint16_t already copied | |||
uint16_t w = 0; // the leftover, not yet copied | |||
uint8_t bits = 0; // the number of lsb in w | |||
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { | |||
/* | |||
in: | | |********|********| | |||
^ | |||
j | |||
w : | ****| | |||
^ | |||
bits | |||
out:|**|**|**|**|**|**|**|**|* | | |||
^^ | |||
ib | |||
*/ | |||
uint8_t b = 0; // bits in out[i] already filled in | |||
while (b < 8) { | |||
int nbits = min(8 - b, bits); | |||
uint16_t mask = (1 << nbits) - 1; | |||
uint8_t t = (uint8_t) ((w >> (bits - nbits)) & mask); // the bits to copy from w to out | |||
out[i] = out[i] + (t << (8 - b - nbits)); | |||
b += (uint8_t) nbits; | |||
bits -= (uint8_t) nbits; | |||
w &= ~(mask << bits); // not strictly necessary; mostly for debugging | |||
if (bits == 0) { | |||
if (j < inlen) { | |||
w = in[j]; | |||
bits = lsb; | |||
j++; | |||
} else { | |||
break; // the input vector is exhausted | |||
} | |||
} | |||
} | |||
if (b == 8) { // out[i] is filled in | |||
i++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM1344AES_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb) { | |||
// Unpack the input char vector into a uint16_t output vector, copying lsb bits | |||
// for each output element from input. outlen must be at least ceil(inlen * 8 / lsb). | |||
memset(out, 0, outlen * sizeof(uint16_t)); | |||
size_t i = 0; // whole uint16_t already filled in | |||
size_t j = 0; // whole bytes already copied | |||
uint8_t w = 0; // the leftover, not yet copied | |||
uint8_t bits = 0; // the number of lsb bits of w | |||
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { | |||
/* | |||
in: | | | | | | |**|**|... | |||
^ | |||
j | |||
w : | *| | |||
^ | |||
bits | |||
out:| *****| *****| *** | |... | |||
^ ^ | |||
i b | |||
*/ | |||
uint8_t b = 0; // bits in out[i] already filled in | |||
while (b < lsb) { | |||
int nbits = min(lsb - b, bits); | |||
uint16_t mask = (1 << nbits) - 1; | |||
uint8_t t = (w >> (bits - nbits)) & mask; // the bits to copy from w to out | |||
out[i] = out[i] + (t << (lsb - b - nbits)); | |||
b += (uint8_t) nbits; | |||
bits -= (uint8_t) nbits; | |||
w &= ~(mask << bits); // not strictly necessary; mostly for debugging | |||
if (bits == 0) { | |||
if (j < inlen) { | |||
w = in[j]; | |||
bits = 8; | |||
j++; | |||
} else { | |||
break; // the input vector is exhausted | |||
} | |||
} | |||
} | |||
if (b == lsb) { // out[i] is filled in | |||
i++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM1344AES_OPT_clear_bytes(uint8_t *mem, size_t n) { | |||
// Clear 8-bit bytes from memory. "n" indicates the number of bytes to be zeroed. | |||
// This function uses the volatile type qualifier to inform the compiler not to optimize out the memory clearing. | |||
volatile uint8_t *v = mem; | |||
for (size_t i = 0; i < n; i++) { | |||
v[i] = 0; | |||
} | |||
} |
@@ -23,3 +23,5 @@ auxiliary-submitters: | |||
implementations: | |||
- name: clean | |||
version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/d5bbd0417ba111b08a959c0042a1dcc65fb14a89 | |||
- name: opt | |||
version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/d5bbd0417ba111b08a959c0042a1dcc65fb14a89 |
@@ -4,7 +4,7 @@ LIB=libfrodokem1344shake_clean.a | |||
HEADERS=api.h params.h common.h | |||
OBJECTS=kem.o matrix_shake.o noise.o util.o | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
@@ -0,0 +1,21 @@ | |||
MIT License | |||
Copyright (c) Microsoft Corporation. All rights reserved. | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE |
@@ -0,0 +1,19 @@ | |||
# This Makefile can be used with GNU Make or BSD Make | |||
LIB=libfrodokem1344shake_opt.a | |||
HEADERS=api.h params.h common.h | |||
OBJECTS=kem.o matrix_shake.o noise.o util.o | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
%.o: %.c $(HEADERS) | |||
$(CC) $(CFLAGS) -c -o $@ $< | |||
$(LIB): $(OBJECTS) | |||
$(AR) -r $@ $(OBJECTS) | |||
clean: | |||
$(RM) $(OBJECTS) | |||
$(RM) $(LIB) |
@@ -0,0 +1,19 @@ | |||
# This Makefile can be used with Microsoft Visual Studio's nmake using the command: | |||
# nmake /f Makefile.Microsoft_nmake | |||
LIBRARY=libfrodokem1344shake_opt.lib | |||
OBJECTS=kem.obj matrix_shake.obj noise.obj util.obj | |||
CFLAGS=/nologo /I ..\..\..\common /W4 /WX | |||
all: $(LIBRARY) | |||
# Make sure objects are recompiled if headers change. | |||
$(OBJECTS): *.h | |||
$(LIBRARY): $(OBJECTS) | |||
LIB.EXE /NOLOGO /WX /OUT:$@ $** | |||
clean: | |||
-DEL $(OBJECTS) | |||
-DEL $(LIBRARY) |
@@ -0,0 +1,20 @@ | |||
#ifndef PQCLEAN_FRODOKEM1344SHAKE_OPT_API_H | |||
#define PQCLEAN_FRODOKEM1344SHAKE_OPT_API_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#define PQCLEAN_FRODOKEM1344SHAKE_OPT_CRYPTO_SECRETKEYBYTES 43088 // sizeof(s) + CRYPTO_PUBLICKEYBYTES + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH | |||
#define PQCLEAN_FRODOKEM1344SHAKE_OPT_CRYPTO_PUBLICKEYBYTES 21520 // sizeof(seed_A) + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 | |||
#define PQCLEAN_FRODOKEM1344SHAKE_OPT_CRYPTO_BYTES 32 | |||
#define PQCLEAN_FRODOKEM1344SHAKE_OPT_CRYPTO_CIPHERTEXTBYTES 21632 // (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + (PARAMS_LOGQ*PARAMS_NBAR*PARAMS_NBAR)/8 | |||
#define PQCLEAN_FRODOKEM1344SHAKE_OPT_CRYPTO_ALGNAME "FrodoKEM-1344-SHAKE" | |||
int PQCLEAN_FRODOKEM1344SHAKE_OPT_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); | |||
int PQCLEAN_FRODOKEM1344SHAKE_OPT_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); | |||
int PQCLEAN_FRODOKEM1344SHAKE_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); | |||
#endif |
@@ -0,0 +1,19 @@ | |||
#ifndef COMMON_H | |||
#define COMMON_H | |||
int PQCLEAN_FRODOKEM1344SHAKE_OPT_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); | |||
int PQCLEAN_FRODOKEM1344SHAKE_OPT_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_sample_n(uint16_t *s, size_t n); | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s); | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e); | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_add(uint16_t *out, const uint16_t *a, const uint16_t *b); | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_sub(uint16_t *out, const uint16_t *a, const uint16_t *b); | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_key_encode(uint16_t *out, const uint16_t *in); | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_key_decode(uint16_t *out, const uint16_t *in); | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes(uint8_t *mem, size_t n); | |||
uint16_t PQCLEAN_FRODOKEM1344SHAKE_OPT_LE_TO_UINT16(uint16_t n); | |||
uint16_t PQCLEAN_FRODOKEM1344SHAKE_OPT_UINT16_TO_LE(uint16_t n); | |||
#endif |
@@ -0,0 +1,238 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: Key Encapsulation Mechanism (KEM) based on Frodo | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "fips202.h" | |||
#include "randombytes.h" | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
int PQCLEAN_FRODOKEM1344SHAKE_OPT_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { | |||
// FrodoKEM's key generation | |||
// Outputs: public key pk ( BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 bytes) | |||
// secret key sk (CRYPTO_BYTES + BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH bytes) | |||
uint8_t *pk_seedA = &pk[0]; | |||
uint8_t *pk_b = &pk[BYTES_SEED_A]; | |||
uint8_t *sk_s = &sk[0]; | |||
uint8_t *sk_pk = &sk[CRYPTO_BYTES]; | |||
uint8_t *sk_S = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; | |||
uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t S[2 * PARAMS_N * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *E = &S[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint8_t randomness[2 * CRYPTO_BYTES + BYTES_SEED_A]; // contains secret data via randomness_s and randomness_seedSE | |||
uint8_t *randomness_s = &randomness[0]; // contains secret data | |||
uint8_t *randomness_seedSE = &randomness[CRYPTO_BYTES]; // contains secret data | |||
uint8_t *randomness_z = &randomness[2 * CRYPTO_BYTES]; | |||
uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data | |||
// Generate the secret value s, the seed for S and E, and the seed for the seed for A. Add seed_A to the public key | |||
randombytes(randomness, CRYPTO_BYTES + CRYPTO_BYTES + BYTES_SEED_A); | |||
shake(pk_seedA, BYTES_SEED_A, randomness_z, BYTES_SEED_A); | |||
// Generate S and E, and compute B = A*S + E. Generate A on-the-fly | |||
shake_input_seedSE[0] = 0x5F; | |||
memcpy(&shake_input_seedSE[1], randomness_seedSE, CRYPTO_BYTES); | |||
shake((uint8_t *)S, 2 * PARAMS_N * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < 2 * PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM1344SHAKE_OPT_LE_TO_UINT16(S[i]); | |||
} | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_sample_n(S, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_sample_n(E, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_mul_add_as_plus_e(B, S, E, pk); | |||
// Encode the second part of the public key | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_pack(pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, B, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Add s, pk and S to the secret key | |||
memcpy(sk_s, randomness_s, CRYPTO_BYTES); | |||
memcpy(sk_pk, pk, CRYPTO_PUBLICKEYBYTES); | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM1344SHAKE_OPT_UINT16_TO_LE(S[i]); | |||
} | |||
memcpy(sk_S, S, 2 * PARAMS_N * PARAMS_NBAR); | |||
// Add H(pk) to the secret key | |||
shake(sk_pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes((uint8_t *)E, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes(randomness, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} | |||
int PQCLEAN_FRODOKEM1344SHAKE_OPT_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { | |||
// FrodoKEM's key encapsulation | |||
const uint8_t *pk_seedA = &pk[0]; | |||
const uint8_t *pk_b = &pk[BYTES_SEED_A]; | |||
uint8_t *ct_c1 = &ct[0]; | |||
uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t V[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via mu | |||
uint8_t *pkh = &G2in[0]; | |||
uint8_t *mu = &G2in[BYTES_PKHASH]; // contains secret data | |||
uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data | |||
uint8_t *seedSE = &G2out[0]; // contains secret data | |||
uint8_t *k = &G2out[CRYPTO_BYTES]; // contains secret data | |||
uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k | |||
uint8_t *Fin_ct = &Fin[0]; | |||
uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data | |||
uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data | |||
// pkh <- G_1(pk), generate random mu, compute (seedSE || k) = G_2(pkh || mu) | |||
shake(pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); | |||
randombytes(mu, BYTES_MU); | |||
shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); | |||
// Generate Sp and Ep, and compute Bp = Sp*A + Ep. Generate A on-the-fly | |||
shake_input_seedSE[0] = 0x96; | |||
memcpy(&shake_input_seedSE[1], seedSE, CRYPTO_BYTES); | |||
shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { | |||
Sp[i] = PQCLEAN_FRODOKEM1344SHAKE_OPT_LE_TO_UINT16(Sp[i]); | |||
} | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_sample_n(Sp, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_sample_n(Ep, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_mul_add_sa_plus_e(Bp, Sp, Ep, pk_seedA); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_pack(ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, Bp, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Generate Epp, and compute V = Sp*B + Epp | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_mul_add_sb_plus_e(V, B, Sp, Epp); | |||
// Encode mu, and compute C = V + enc(mu) (mod q) | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_key_encode(C, (uint16_t *)mu); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_add(C, V, C); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_pack(ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, C, PARAMS_NBAR * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Compute ss = F(ct||KK) | |||
memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); | |||
memcpy(Fin_k, k, CRYPTO_BYTES); | |||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes((uint8_t *)V, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes(mu, BYTES_MU); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes(G2out, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes(Fin_k, CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} | |||
int PQCLEAN_FRODOKEM1344SHAKE_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { | |||
// FrodoKEM's key decapsulation | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t W[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t CC[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t BBp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
const uint8_t *ct_c1 = &ct[0]; | |||
const uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; | |||
const uint8_t *sk_s = &sk[0]; | |||
const uint8_t *sk_pk = &sk[CRYPTO_BYTES]; | |||
const uint16_t *sk_S = (uint16_t *) &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; | |||
uint16_t S[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
const uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; | |||
const uint8_t *pk_seedA = &sk_pk[0]; | |||
const uint8_t *pk_b = &sk_pk[BYTES_SEED_A]; | |||
uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via muprime | |||
uint8_t *pkh = &G2in[0]; | |||
uint8_t *muprime = &G2in[BYTES_PKHASH]; // contains secret data | |||
uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data | |||
uint8_t *seedSEprime = &G2out[0]; // contains secret data | |||
uint8_t *kprime = &G2out[CRYPTO_BYTES]; // contains secret data | |||
uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k | |||
uint8_t *Fin_ct = &Fin[0]; | |||
uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data | |||
uint8_t shake_input_seedSEprime[1 + CRYPTO_BYTES]; // contains secret data | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM1344SHAKE_OPT_LE_TO_UINT16(sk_S[i]); | |||
} | |||
// Compute W = C - Bp*S (mod q), and decode the randomness mu | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_unpack(Bp, PARAMS_N * PARAMS_NBAR, ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_unpack(C, PARAMS_NBAR * PARAMS_NBAR, ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_mul_bs(W, Bp, S); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_sub(W, C, W); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_key_decode((uint16_t *)muprime, W); | |||
// Generate (seedSE' || k') = G_2(pkh || mu') | |||
memcpy(pkh, sk_pkh, BYTES_PKHASH); | |||
shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); | |||
// Generate Sp and Ep, and compute BBp = Sp*A + Ep. Generate A on-the-fly | |||
shake_input_seedSEprime[0] = 0x96; | |||
memcpy(&shake_input_seedSEprime[1], seedSEprime, CRYPTO_BYTES); | |||
shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSEprime, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { | |||
Sp[i] = PQCLEAN_FRODOKEM1344SHAKE_OPT_LE_TO_UINT16(Sp[i]); | |||
} | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_sample_n(Sp, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_sample_n(Ep, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_mul_add_sa_plus_e(BBp, Sp, Ep, pk_seedA); | |||
// Generate Epp, and compute W = Sp*B + Epp | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_mul_add_sb_plus_e(W, B, Sp, Epp); | |||
// Encode mu, and compute CC = W + enc(mu') (mod q) | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_key_encode(CC, (uint16_t *)muprime); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_add(CC, W, CC); | |||
// Prepare input to F | |||
memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); | |||
// Reducing BBp modulo q | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
BBp[i] = BBp[i] & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
// Is (Bp == BBp & C == CC) = true | |||
if (memcmp(Bp, BBp, 2 * PARAMS_N * PARAMS_NBAR) == 0 && memcmp(C, CC, 2 * PARAMS_NBAR * PARAMS_NBAR) == 0) { | |||
// Load k' to do ss = F(ct || k') | |||
memcpy(Fin_k, kprime, CRYPTO_BYTES); | |||
} else { | |||
// Load s to do ss = F(ct || s) | |||
memcpy(Fin_k, sk_s, CRYPTO_BYTES); | |||
} | |||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes((uint8_t *)W, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes(muprime, BYTES_MU); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes(G2out, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes(Fin_k, CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes(shake_input_seedSEprime, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} |
@@ -0,0 +1,108 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: matrix arithmetic functions used by the KEM | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "fips202.h" | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
int PQCLEAN_FRODOKEM1344SHAKE_OPT_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { | |||
// Generate-and-multiply: generate matrix A (N x N) row-wise, multiply by s on the right. | |||
// Inputs: s, e (N x N_BAR) | |||
// Output: out = A*s + e (N x N_BAR) | |||
int j, k; | |||
uint16_t i; | |||
int16_t a_row[4 * PARAMS_N]; | |||
for (i = 0; i < (PARAMS_N * PARAMS_NBAR); i += 2) { | |||
*((uint32_t *)&out[i]) = *((uint32_t *)&e[i]); | |||
} | |||
uint8_t seed_A_separated[2 + BYTES_SEED_A]; | |||
uint16_t *seed_A_origin = (uint16_t *)&seed_A_separated; | |||
memcpy(&seed_A_separated[2], seed_A, BYTES_SEED_A); | |||
for (i = 0; i < PARAMS_N; i += 4) { | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM1344SHAKE_OPT_UINT16_TO_LE(i + 0); | |||
shake128((unsigned char *)(a_row + 0 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM1344SHAKE_OPT_UINT16_TO_LE(i + 1); | |||
shake128((unsigned char *)(a_row + 1 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM1344SHAKE_OPT_UINT16_TO_LE(i + 2); | |||
shake128((unsigned char *)(a_row + 2 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM1344SHAKE_OPT_UINT16_TO_LE(i + 3); | |||
shake128((unsigned char *)(a_row + 3 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
for (k = 0; k < 4 * PARAMS_N; k++) { | |||
a_row[k] = PQCLEAN_FRODOKEM1344SHAKE_OPT_LE_TO_UINT16(a_row[k]); | |||
} | |||
for (k = 0; k < PARAMS_NBAR; k++) { | |||
uint16_t sum[4] = {0}; | |||
for (j = 0; j < PARAMS_N; j++) { // Matrix-vector multiplication | |||
uint16_t sp = s[k * PARAMS_N + j]; | |||
sum[0] += a_row[0 * PARAMS_N + j] * sp; // Go through four lines with same s | |||
sum[1] += a_row[1 * PARAMS_N + j] * sp; | |||
sum[2] += a_row[2 * PARAMS_N + j] * sp; | |||
sum[3] += a_row[3 * PARAMS_N + j] * sp; | |||
} | |||
out[(i + 0)*PARAMS_NBAR + k] += sum[0]; | |||
out[(i + 2)*PARAMS_NBAR + k] += sum[2]; | |||
out[(i + 1)*PARAMS_NBAR + k] += sum[1]; | |||
out[(i + 3)*PARAMS_NBAR + k] += sum[3]; | |||
} | |||
} | |||
return 1; | |||
} | |||
int PQCLEAN_FRODOKEM1344SHAKE_OPT_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { | |||
// Generate-and-multiply: generate matrix A (N x N) column-wise, multiply by s' on the left. | |||
// Inputs: s', e' (N_BAR x N) | |||
// Output: out = s'*A + e' (N_BAR x N) | |||
int i, j; | |||
uint16_t kk; | |||
for (i = 0; i < (PARAMS_N * PARAMS_NBAR); i += 2) { | |||
*((uint32_t *)&out[i]) = *((uint32_t *)&e[i]); | |||
} | |||
int t = 0; | |||
uint16_t a_cols[4 * PARAMS_N]; | |||
int k; | |||
uint8_t seed_A_separated[2 + BYTES_SEED_A]; | |||
uint16_t *seed_A_origin = (uint16_t *)&seed_A_separated; | |||
memcpy(&seed_A_separated[2], seed_A, BYTES_SEED_A); | |||
for (kk = 0; kk < PARAMS_N; kk += 4) { | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM1344SHAKE_OPT_UINT16_TO_LE(kk + 0); | |||
shake128((unsigned char *)(a_cols + 0 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM1344SHAKE_OPT_UINT16_TO_LE(kk + 1); | |||
shake128((unsigned char *)(a_cols + 1 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM1344SHAKE_OPT_UINT16_TO_LE(kk + 2); | |||
shake128((unsigned char *)(a_cols + 2 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM1344SHAKE_OPT_UINT16_TO_LE(kk + 3); | |||
shake128((unsigned char *)(a_cols + 3 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
for (i = 0; i < 4 * PARAMS_N; i++) { | |||
a_cols[i] = PQCLEAN_FRODOKEM1344SHAKE_OPT_LE_TO_UINT16(a_cols[i]); | |||
} | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
uint16_t sum[PARAMS_N] = {0}; | |||
for (j = 0; j < 4; j++) { | |||
uint16_t sp = s[i * PARAMS_N + kk + j]; | |||
for (k = 0; k < PARAMS_N; k++) { // Matrix-vector multiplication | |||
sum[k] += sp * a_cols[(t + j) * PARAMS_N + k]; | |||
} | |||
} | |||
for (k = 0; k < PARAMS_N; k++) { | |||
out[i * PARAMS_N + k] += sum[k]; | |||
} | |||
} | |||
} | |||
return 1; | |||
} |
@@ -0,0 +1,35 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: noise sampling functions | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
static uint16_t CDF_TABLE[CDF_TABLE_LEN] = CDF_TABLE_DATA; | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_sample_n(uint16_t *s, size_t n) { | |||
// Fills vector s with n samples from the noise distribution which requires 16 bits to sample. | |||
// The distribution is specified by its CDF. | |||
// Input: pseudo-random values (2*n bytes) passed in s. The input is overwritten by the output. | |||
size_t i; | |||
unsigned int j; | |||
for (i = 0; i < n; ++i) { | |||
uint16_t sample = 0; | |||
uint16_t prnd = s[i] >> 1; // Drop the least significant bit | |||
uint16_t sign = s[i] & 0x1; // Pick the least significant bit | |||
// No need to compare with the last value. | |||
for (j = 0; j < (unsigned int)(CDF_TABLE_LEN - 1); j++) { | |||
// Constant time comparison: 1 if CDF_TABLE[j] < s, 0 otherwise. Uses the fact that CDF_TABLE[j] and s fit in 15 bits. | |||
sample += (uint16_t)(CDF_TABLE[j] - prnd) >> 15; | |||
} | |||
// Assuming that sign is either 0 or 1, flips sample iff sign = 1 | |||
s[i] = ((-sign) ^ sample) + sign; | |||
} | |||
} |
@@ -0,0 +1,27 @@ | |||
#ifndef PARAMS_H | |||
#define PARAMS_H | |||
#define CRYPTO_SECRETKEYBYTES PQCLEAN_FRODOKEM1344SHAKE_OPT_CRYPTO_SECRETKEYBYTES | |||
#define CRYPTO_PUBLICKEYBYTES PQCLEAN_FRODOKEM1344SHAKE_OPT_CRYPTO_PUBLICKEYBYTES | |||
#define CRYPTO_BYTES PQCLEAN_FRODOKEM1344SHAKE_OPT_CRYPTO_BYTES | |||
#define CRYPTO_CIPHERTEXTBYTES PQCLEAN_FRODOKEM1344SHAKE_OPT_CRYPTO_CIPHERTEXTBYTES | |||
#define PARAMS_N 1344 | |||
#define PARAMS_NBAR 8 | |||
#define PARAMS_LOGQ 16 | |||
#define PARAMS_Q (1 << PARAMS_LOGQ) | |||
#define PARAMS_EXTRACTED_BITS 4 | |||
#define PARAMS_STRIPE_STEP 8 | |||
#define PARAMS_PARALLEL 4 | |||
#define BYTES_SEED_A 16 | |||
#define BYTES_MU ((PARAMS_EXTRACTED_BITS * PARAMS_NBAR * PARAMS_NBAR) / 8) | |||
#define BYTES_PKHASH CRYPTO_BYTES | |||
// Selecting SHAKE XOF function for the KEM and noise sampling | |||
#define shake shake256 | |||
// CDF table | |||
#define CDF_TABLE_DATA {9142, 23462, 30338, 32361, 32725, 32765, 32767} | |||
#define CDF_TABLE_LEN 7 | |||
#endif |
@@ -0,0 +1,235 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: additional functions for FrodoKEM | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
#define min(x, y) (((x) < (y)) ? (x) : (y)) | |||
uint16_t PQCLEAN_FRODOKEM1344SHAKE_OPT_LE_TO_UINT16(uint16_t n) { | |||
return (((uint8_t *) &n)[0] | (((uint8_t *) &n)[1] << 8)); | |||
} | |||
uint16_t PQCLEAN_FRODOKEM1344SHAKE_OPT_UINT16_TO_LE(uint16_t n) { | |||
uint16_t y; | |||
uint8_t *z = (uint8_t *) &y; | |||
z[0] = n & 0xFF; | |||
z[1] = (n & 0xFF00) >> 8; | |||
return y; | |||
} | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s) { | |||
// Multiply by s on the right | |||
// Inputs: b (N_BAR x N), s (N x N_BAR) | |||
// Output: out = b*s (N_BAR x N_BAR) | |||
int i, j, k; | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
for (j = 0; j < PARAMS_NBAR; j++) { | |||
out[i * PARAMS_NBAR + j] = 0; | |||
for (k = 0; k < PARAMS_N; k++) { | |||
out[i * PARAMS_NBAR + j] += b[i * PARAMS_N + k] * s[j * PARAMS_N + k]; | |||
} | |||
out[i * PARAMS_NBAR + j] = (uint32_t)(out[i * PARAMS_NBAR + j]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e) { | |||
// Multiply by s on the left | |||
// Inputs: b (N x N_BAR), s (N_BAR x N), e (N_BAR x N_BAR) | |||
// Output: out = s*b + e (N_BAR x N_BAR) | |||
int i, j, k; | |||
for (k = 0; k < PARAMS_NBAR; k++) { | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
out[k * PARAMS_NBAR + i] = e[k * PARAMS_NBAR + i]; | |||
for (j = 0; j < PARAMS_N; j++) { | |||
out[k * PARAMS_NBAR + i] += s[k * PARAMS_N + j] * b[j * PARAMS_NBAR + i]; | |||
} | |||
out[k * PARAMS_NBAR + i] = (uint32_t)(out[k * PARAMS_NBAR + i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_add(uint16_t *out, const uint16_t *a, const uint16_t *b) { | |||
// Add a and b | |||
// Inputs: a, b (N_BAR x N_BAR) | |||
// Output: c = a + b | |||
for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { | |||
out[i] = (a[i] + b[i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_sub(uint16_t *out, const uint16_t *a, const uint16_t *b) { | |||
// Subtract a and b | |||
// Inputs: a, b (N_BAR x N_BAR) | |||
// Output: c = a - b | |||
for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { | |||
out[i] = (a[i] - b[i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_key_encode(uint16_t *out, const uint16_t *in) { | |||
// Encoding | |||
unsigned int i, j, npieces_word = 8; | |||
unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; | |||
uint64_t temp, mask = ((uint64_t)1 << PARAMS_EXTRACTED_BITS) - 1; | |||
uint16_t *pos = out; | |||
for (i = 0; i < nwords; i++) { | |||
temp = 0; | |||
for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { | |||
temp |= ((uint64_t)((uint8_t *)in)[i * PARAMS_EXTRACTED_BITS + j]) << (8 * j); | |||
} | |||
for (j = 0; j < npieces_word; j++) { | |||
*pos = (uint16_t)((temp & mask) << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS)); | |||
temp >>= PARAMS_EXTRACTED_BITS; | |||
pos++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_key_decode(uint16_t *out, const uint16_t *in) { | |||
// Decoding | |||
unsigned int i, j, index = 0, npieces_word = 8; | |||
unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; | |||
uint16_t temp, maskex = ((uint16_t)1 << PARAMS_EXTRACTED_BITS) - 1, maskq = ((uint16_t)1 << PARAMS_LOGQ) - 1; | |||
uint8_t *pos = (uint8_t *)out; | |||
uint64_t templong; | |||
for (i = 0; i < nwords; i++) { | |||
templong = 0; | |||
for (j = 0; j < npieces_word; j++) { // temp = floor(in*2^{-11}+0.5) | |||
temp = ((in[index] & maskq) + (1 << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS - 1))) >> (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS); | |||
templong |= ((uint64_t)(temp & maskex)) << (PARAMS_EXTRACTED_BITS * j); | |||
index++; | |||
} | |||
for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { | |||
pos[i * PARAMS_EXTRACTED_BITS + j] = (templong >> (8 * j)) & 0xFF; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb) { | |||
// Pack the input uint16 vector into a char output vector, copying lsb bits from each input element. | |||
// If inlen * lsb / 8 > outlen, only outlen * 8 bits are copied. | |||
memset(out, 0, outlen); | |||
size_t i = 0; // whole bytes already filled in | |||
size_t j = 0; // whole uint16_t already copied | |||
uint16_t w = 0; // the leftover, not yet copied | |||
uint8_t bits = 0; // the number of lsb in w | |||
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { | |||
/* | |||
in: | | |********|********| | |||
^ | |||
j | |||
w : | ****| | |||
^ | |||
bits | |||
out:|**|**|**|**|**|**|**|**|* | | |||
^^ | |||
ib | |||
*/ | |||
uint8_t b = 0; // bits in out[i] already filled in | |||
while (b < 8) { | |||
int nbits = min(8 - b, bits); | |||
uint16_t mask = (1 << nbits) - 1; | |||
uint8_t t = (uint8_t) ((w >> (bits - nbits)) & mask); // the bits to copy from w to out | |||
out[i] = out[i] + (t << (8 - b - nbits)); | |||
b += (uint8_t) nbits; | |||
bits -= (uint8_t) nbits; | |||
w &= ~(mask << bits); // not strictly necessary; mostly for debugging | |||
if (bits == 0) { | |||
if (j < inlen) { | |||
w = in[j]; | |||
bits = lsb; | |||
j++; | |||
} else { | |||
break; // the input vector is exhausted | |||
} | |||
} | |||
} | |||
if (b == 8) { // out[i] is filled in | |||
i++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb) { | |||
// Unpack the input char vector into a uint16_t output vector, copying lsb bits | |||
// for each output element from input. outlen must be at least ceil(inlen * 8 / lsb). | |||
memset(out, 0, outlen * sizeof(uint16_t)); | |||
size_t i = 0; // whole uint16_t already filled in | |||
size_t j = 0; // whole bytes already copied | |||
uint8_t w = 0; // the leftover, not yet copied | |||
uint8_t bits = 0; // the number of lsb bits of w | |||
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { | |||
/* | |||
in: | | | | | | |**|**|... | |||
^ | |||
j | |||
w : | *| | |||
^ | |||
bits | |||
out:| *****| *****| *** | |... | |||
^ ^ | |||
i b | |||
*/ | |||
uint8_t b = 0; // bits in out[i] already filled in | |||
while (b < lsb) { | |||
int nbits = min(lsb - b, bits); | |||
uint16_t mask = (1 << nbits) - 1; | |||
uint8_t t = (w >> (bits - nbits)) & mask; // the bits to copy from w to out | |||
out[i] = out[i] + (t << (lsb - b - nbits)); | |||
b += (uint8_t) nbits; | |||
bits -= (uint8_t) nbits; | |||
w &= ~(mask << bits); // not strictly necessary; mostly for debugging | |||
if (bits == 0) { | |||
if (j < inlen) { | |||
w = in[j]; | |||
bits = 8; | |||
j++; | |||
} else { | |||
break; // the input vector is exhausted | |||
} | |||
} | |||
} | |||
if (b == lsb) { // out[i] is filled in | |||
i++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_clear_bytes(uint8_t *mem, size_t n) { | |||
// Clear 8-bit bytes from memory. "n" indicates the number of bytes to be zeroed. | |||
// This function uses the volatile type qualifier to inform the compiler not to optimize out the memory clearing. | |||
volatile uint8_t *v = mem; | |||
for (size_t i = 0; i < n; i++) { | |||
v[i] = 0; | |||
} | |||
} |
@@ -23,3 +23,5 @@ auxiliary-submitters: | |||
implementations: | |||
- name: clean | |||
version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/d5bbd0417ba111b08a959c0042a1dcc65fb14a89 | |||
- name: opt | |||
version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/d5bbd0417ba111b08a959c0042a1dcc65fb14a89 |
@@ -4,7 +4,7 @@ LIB=libfrodokem640aes_clean.a | |||
HEADERS=api.h params.h common.h | |||
OBJECTS=kem.o matrix_aes.o noise.o util.o | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
@@ -0,0 +1,21 @@ | |||
MIT License | |||
Copyright (c) Microsoft Corporation. All rights reserved. | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE |
@@ -0,0 +1,19 @@ | |||
# This Makefile can be used with GNU Make or BSD Make | |||
LIB=libfrodokem640aes_opt.a | |||
HEADERS=api.h params.h common.h | |||
OBJECTS=kem.o matrix_aes.o noise.o util.o | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
%.o: %.c $(HEADERS) | |||
$(CC) $(CFLAGS) -c -o $@ $< | |||
$(LIB): $(OBJECTS) | |||
$(AR) -r $@ $(OBJECTS) | |||
clean: | |||
$(RM) $(OBJECTS) | |||
$(RM) $(LIB) |
@@ -0,0 +1,19 @@ | |||
# This Makefile can be used with Microsoft Visual Studio's nmake using the command: | |||
# nmake /f Makefile.Microsoft_nmake | |||
LIBRARY=libfrodokem640aes_opt.lib | |||
OBJECTS=kem.obj matrix_aes.obj noise.obj util.obj | |||
CFLAGS=/nologo /I ..\..\..\common /W4 /WX | |||
all: $(LIBRARY) | |||
# Make sure objects are recompiled if headers change. | |||
$(OBJECTS): *.h | |||
$(LIBRARY): $(OBJECTS) | |||
LIB.EXE /NOLOGO /WX /OUT:$@ $** | |||
clean: | |||
-DEL $(OBJECTS) | |||
-DEL $(LIBRARY) |
@@ -0,0 +1,20 @@ | |||
#ifndef PQCLEAN_FRODOKEM640AES_OPT_API_H | |||
#define PQCLEAN_FRODOKEM640AES_OPT_API_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#define PQCLEAN_FRODOKEM640AES_OPT_CRYPTO_SECRETKEYBYTES 19888 // sizeof(s) + CRYPTO_PUBLICKEYBYTES + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH | |||
#define PQCLEAN_FRODOKEM640AES_OPT_CRYPTO_PUBLICKEYBYTES 9616 // sizeof(seed_A) + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 | |||
#define PQCLEAN_FRODOKEM640AES_OPT_CRYPTO_BYTES 16 | |||
#define PQCLEAN_FRODOKEM640AES_OPT_CRYPTO_CIPHERTEXTBYTES 9720 // (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + (PARAMS_LOGQ*PARAMS_NBAR*PARAMS_NBAR)/8 | |||
#define PQCLEAN_FRODOKEM640AES_OPT_CRYPTO_ALGNAME "FrodoKEM-640-AES" | |||
int PQCLEAN_FRODOKEM640AES_OPT_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); | |||
int PQCLEAN_FRODOKEM640AES_OPT_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); | |||
int PQCLEAN_FRODOKEM640AES_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); | |||
#endif |
@@ -0,0 +1,19 @@ | |||
#ifndef COMMON_H | |||
#define COMMON_H | |||
int PQCLEAN_FRODOKEM640AES_OPT_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); | |||
int PQCLEAN_FRODOKEM640AES_OPT_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); | |||
void PQCLEAN_FRODOKEM640AES_OPT_sample_n(uint16_t *s, size_t n); | |||
void PQCLEAN_FRODOKEM640AES_OPT_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s); | |||
void PQCLEAN_FRODOKEM640AES_OPT_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e); | |||
void PQCLEAN_FRODOKEM640AES_OPT_add(uint16_t *out, const uint16_t *a, const uint16_t *b); | |||
void PQCLEAN_FRODOKEM640AES_OPT_sub(uint16_t *out, const uint16_t *a, const uint16_t *b); | |||
void PQCLEAN_FRODOKEM640AES_OPT_key_encode(uint16_t *out, const uint16_t *in); | |||
void PQCLEAN_FRODOKEM640AES_OPT_key_decode(uint16_t *out, const uint16_t *in); | |||
void PQCLEAN_FRODOKEM640AES_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); | |||
void PQCLEAN_FRODOKEM640AES_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); | |||
void PQCLEAN_FRODOKEM640AES_OPT_clear_bytes(uint8_t *mem, size_t n); | |||
uint16_t PQCLEAN_FRODOKEM640AES_OPT_LE_TO_UINT16(uint16_t n); | |||
uint16_t PQCLEAN_FRODOKEM640AES_OPT_UINT16_TO_LE(uint16_t n); | |||
#endif |
@@ -0,0 +1,238 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: Key Encapsulation Mechanism (KEM) based on Frodo | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "fips202.h" | |||
#include "randombytes.h" | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
int PQCLEAN_FRODOKEM640AES_OPT_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { | |||
// FrodoKEM's key generation | |||
// Outputs: public key pk ( BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 bytes) | |||
// secret key sk (CRYPTO_BYTES + BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH bytes) | |||
uint8_t *pk_seedA = &pk[0]; | |||
uint8_t *pk_b = &pk[BYTES_SEED_A]; | |||
uint8_t *sk_s = &sk[0]; | |||
uint8_t *sk_pk = &sk[CRYPTO_BYTES]; | |||
uint8_t *sk_S = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; | |||
uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t S[2 * PARAMS_N * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *E = &S[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint8_t randomness[2 * CRYPTO_BYTES + BYTES_SEED_A]; // contains secret data via randomness_s and randomness_seedSE | |||
uint8_t *randomness_s = &randomness[0]; // contains secret data | |||
uint8_t *randomness_seedSE = &randomness[CRYPTO_BYTES]; // contains secret data | |||
uint8_t *randomness_z = &randomness[2 * CRYPTO_BYTES]; | |||
uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data | |||
// Generate the secret value s, the seed for S and E, and the seed for the seed for A. Add seed_A to the public key | |||
randombytes(randomness, CRYPTO_BYTES + CRYPTO_BYTES + BYTES_SEED_A); | |||
shake(pk_seedA, BYTES_SEED_A, randomness_z, BYTES_SEED_A); | |||
// Generate S and E, and compute B = A*S + E. Generate A on-the-fly | |||
shake_input_seedSE[0] = 0x5F; | |||
memcpy(&shake_input_seedSE[1], randomness_seedSE, CRYPTO_BYTES); | |||
shake((uint8_t *)S, 2 * PARAMS_N * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < 2 * PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM640AES_OPT_LE_TO_UINT16(S[i]); | |||
} | |||
PQCLEAN_FRODOKEM640AES_OPT_sample_n(S, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM640AES_OPT_sample_n(E, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM640AES_OPT_mul_add_as_plus_e(B, S, E, pk); | |||
// Encode the second part of the public key | |||
PQCLEAN_FRODOKEM640AES_OPT_pack(pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, B, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Add s, pk and S to the secret key | |||
memcpy(sk_s, randomness_s, CRYPTO_BYTES); | |||
memcpy(sk_pk, pk, CRYPTO_PUBLICKEYBYTES); | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM640AES_OPT_UINT16_TO_LE(S[i]); | |||
} | |||
memcpy(sk_S, S, 2 * PARAMS_N * PARAMS_NBAR); | |||
// Add H(pk) to the secret key | |||
shake(sk_pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes((uint8_t *)E, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes(randomness, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} | |||
int PQCLEAN_FRODOKEM640AES_OPT_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { | |||
// FrodoKEM's key encapsulation | |||
const uint8_t *pk_seedA = &pk[0]; | |||
const uint8_t *pk_b = &pk[BYTES_SEED_A]; | |||
uint8_t *ct_c1 = &ct[0]; | |||
uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t V[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via mu | |||
uint8_t *pkh = &G2in[0]; | |||
uint8_t *mu = &G2in[BYTES_PKHASH]; // contains secret data | |||
uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data | |||
uint8_t *seedSE = &G2out[0]; // contains secret data | |||
uint8_t *k = &G2out[CRYPTO_BYTES]; // contains secret data | |||
uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k | |||
uint8_t *Fin_ct = &Fin[0]; | |||
uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data | |||
uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data | |||
// pkh <- G_1(pk), generate random mu, compute (seedSE || k) = G_2(pkh || mu) | |||
shake(pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); | |||
randombytes(mu, BYTES_MU); | |||
shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); | |||
// Generate Sp and Ep, and compute Bp = Sp*A + Ep. Generate A on-the-fly | |||
shake_input_seedSE[0] = 0x96; | |||
memcpy(&shake_input_seedSE[1], seedSE, CRYPTO_BYTES); | |||
shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { | |||
Sp[i] = PQCLEAN_FRODOKEM640AES_OPT_LE_TO_UINT16(Sp[i]); | |||
} | |||
PQCLEAN_FRODOKEM640AES_OPT_sample_n(Sp, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM640AES_OPT_sample_n(Ep, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM640AES_OPT_mul_add_sa_plus_e(Bp, Sp, Ep, pk_seedA); | |||
PQCLEAN_FRODOKEM640AES_OPT_pack(ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, Bp, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Generate Epp, and compute V = Sp*B + Epp | |||
PQCLEAN_FRODOKEM640AES_OPT_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM640AES_OPT_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM640AES_OPT_mul_add_sb_plus_e(V, B, Sp, Epp); | |||
// Encode mu, and compute C = V + enc(mu) (mod q) | |||
PQCLEAN_FRODOKEM640AES_OPT_key_encode(C, (uint16_t *)mu); | |||
PQCLEAN_FRODOKEM640AES_OPT_add(C, V, C); | |||
PQCLEAN_FRODOKEM640AES_OPT_pack(ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, C, PARAMS_NBAR * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Compute ss = F(ct||KK) | |||
memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); | |||
memcpy(Fin_k, k, CRYPTO_BYTES); | |||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes((uint8_t *)V, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes(mu, BYTES_MU); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes(G2out, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes(Fin_k, CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} | |||
int PQCLEAN_FRODOKEM640AES_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { | |||
// FrodoKEM's key decapsulation | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t W[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t CC[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t BBp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
const uint8_t *ct_c1 = &ct[0]; | |||
const uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; | |||
const uint8_t *sk_s = &sk[0]; | |||
const uint8_t *sk_pk = &sk[CRYPTO_BYTES]; | |||
const uint16_t *sk_S = (uint16_t *) &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; | |||
uint16_t S[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
const uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; | |||
const uint8_t *pk_seedA = &sk_pk[0]; | |||
const uint8_t *pk_b = &sk_pk[BYTES_SEED_A]; | |||
uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via muprime | |||
uint8_t *pkh = &G2in[0]; | |||
uint8_t *muprime = &G2in[BYTES_PKHASH]; // contains secret data | |||
uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data | |||
uint8_t *seedSEprime = &G2out[0]; // contains secret data | |||
uint8_t *kprime = &G2out[CRYPTO_BYTES]; // contains secret data | |||
uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k | |||
uint8_t *Fin_ct = &Fin[0]; | |||
uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data | |||
uint8_t shake_input_seedSEprime[1 + CRYPTO_BYTES]; // contains secret data | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM640AES_OPT_LE_TO_UINT16(sk_S[i]); | |||
} | |||
// Compute W = C - Bp*S (mod q), and decode the randomness mu | |||
PQCLEAN_FRODOKEM640AES_OPT_unpack(Bp, PARAMS_N * PARAMS_NBAR, ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM640AES_OPT_unpack(C, PARAMS_NBAR * PARAMS_NBAR, ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM640AES_OPT_mul_bs(W, Bp, S); | |||
PQCLEAN_FRODOKEM640AES_OPT_sub(W, C, W); | |||
PQCLEAN_FRODOKEM640AES_OPT_key_decode((uint16_t *)muprime, W); | |||
// Generate (seedSE' || k') = G_2(pkh || mu') | |||
memcpy(pkh, sk_pkh, BYTES_PKHASH); | |||
shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); | |||
// Generate Sp and Ep, and compute BBp = Sp*A + Ep. Generate A on-the-fly | |||
shake_input_seedSEprime[0] = 0x96; | |||
memcpy(&shake_input_seedSEprime[1], seedSEprime, CRYPTO_BYTES); | |||
shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSEprime, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { | |||
Sp[i] = PQCLEAN_FRODOKEM640AES_OPT_LE_TO_UINT16(Sp[i]); | |||
} | |||
PQCLEAN_FRODOKEM640AES_OPT_sample_n(Sp, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM640AES_OPT_sample_n(Ep, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM640AES_OPT_mul_add_sa_plus_e(BBp, Sp, Ep, pk_seedA); | |||
// Generate Epp, and compute W = Sp*B + Epp | |||
PQCLEAN_FRODOKEM640AES_OPT_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM640AES_OPT_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM640AES_OPT_mul_add_sb_plus_e(W, B, Sp, Epp); | |||
// Encode mu, and compute CC = W + enc(mu') (mod q) | |||
PQCLEAN_FRODOKEM640AES_OPT_key_encode(CC, (uint16_t *)muprime); | |||
PQCLEAN_FRODOKEM640AES_OPT_add(CC, W, CC); | |||
// Prepare input to F | |||
memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); | |||
// Reducing BBp modulo q | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
BBp[i] = BBp[i] & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
// Is (Bp == BBp & C == CC) = true | |||
if (memcmp(Bp, BBp, 2 * PARAMS_N * PARAMS_NBAR) == 0 && memcmp(C, CC, 2 * PARAMS_NBAR * PARAMS_NBAR) == 0) { | |||
// Load k' to do ss = F(ct || k') | |||
memcpy(Fin_k, kprime, CRYPTO_BYTES); | |||
} else { | |||
// Load s to do ss = F(ct || s) | |||
memcpy(Fin_k, sk_s, CRYPTO_BYTES); | |||
} | |||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes((uint8_t *)W, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes(muprime, BYTES_MU); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes(G2out, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes(Fin_k, CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM640AES_OPT_clear_bytes(shake_input_seedSEprime, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} |
@@ -0,0 +1,125 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: matrix arithmetic functions used by the KEM | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "aes.h" | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
int PQCLEAN_FRODOKEM640AES_OPT_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { | |||
// Generate-and-multiply: generate matrix A (N x N) row-wise, multiply by s on the right. | |||
// Inputs: s, e (N x N_BAR) | |||
// Output: out = A*s + e (N x N_BAR) | |||
int k; | |||
uint16_t i, j; | |||
int16_t a_row[4 * PARAMS_N]; | |||
for (i = 0; i < (PARAMS_N * PARAMS_NBAR); i += 2) { | |||
*((uint32_t *)&out[i]) = *((uint32_t *)&e[i]); | |||
} | |||
int16_t a_row_temp[4 * PARAMS_N] = {0}; // Take four lines of A at once | |||
aes128ctx ctx128; | |||
aes128_keyexp(&ctx128, seed_A); | |||
for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { | |||
a_row_temp[j + 1 + 0 * PARAMS_N] = PQCLEAN_FRODOKEM640AES_OPT_UINT16_TO_LE(j); // Loading values in the little-endian order | |||
a_row_temp[j + 1 + 1 * PARAMS_N] = PQCLEAN_FRODOKEM640AES_OPT_UINT16_TO_LE(j); | |||
a_row_temp[j + 1 + 2 * PARAMS_N] = PQCLEAN_FRODOKEM640AES_OPT_UINT16_TO_LE(j); | |||
a_row_temp[j + 1 + 3 * PARAMS_N] = PQCLEAN_FRODOKEM640AES_OPT_UINT16_TO_LE(j); | |||
} | |||
for (i = 0; i < PARAMS_N; i += 4) { | |||
for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { // Go through A, four rows at a time | |||
a_row_temp[j + 0 * PARAMS_N] = PQCLEAN_FRODOKEM640AES_OPT_UINT16_TO_LE(i + 0); // Loading values in the little-endian order | |||
a_row_temp[j + 1 * PARAMS_N] = PQCLEAN_FRODOKEM640AES_OPT_UINT16_TO_LE(i + 1); | |||
a_row_temp[j + 2 * PARAMS_N] = PQCLEAN_FRODOKEM640AES_OPT_UINT16_TO_LE(i + 2); | |||
a_row_temp[j + 3 * PARAMS_N] = PQCLEAN_FRODOKEM640AES_OPT_UINT16_TO_LE(i + 3); | |||
} | |||
aes128_ecb((uint8_t *)a_row, (uint8_t *)a_row_temp, 4 * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); | |||
for (k = 0; k < 4 * PARAMS_N; k++) { | |||
a_row[k] = PQCLEAN_FRODOKEM640AES_OPT_LE_TO_UINT16(a_row[k]); | |||
} | |||
for (k = 0; k < PARAMS_NBAR; k++) { | |||
uint16_t sum[4] = {0}; | |||
for (j = 0; j < PARAMS_N; j++) { // Matrix-vector multiplication | |||
uint16_t sp = s[k * PARAMS_N + j]; | |||
sum[0] += a_row[0 * PARAMS_N + j] * sp; // Go through four lines with same s | |||
sum[1] += a_row[1 * PARAMS_N + j] * sp; | |||
sum[2] += a_row[2 * PARAMS_N + j] * sp; | |||
sum[3] += a_row[3 * PARAMS_N + j] * sp; | |||
} | |||
out[(i + 0)*PARAMS_NBAR + k] += sum[0]; | |||
out[(i + 2)*PARAMS_NBAR + k] += sum[2]; | |||
out[(i + 1)*PARAMS_NBAR + k] += sum[1]; | |||
out[(i + 3)*PARAMS_NBAR + k] += sum[3]; | |||
} | |||
} | |||
return 1; | |||
} | |||
int PQCLEAN_FRODOKEM640AES_OPT_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { | |||
// Generate-and-multiply: generate matrix A (N x N) column-wise, multiply by s' on the left. | |||
// Inputs: s', e' (N_BAR x N) | |||
// Output: out = s'*A + e' (N_BAR x N) | |||
int j; | |||
uint16_t i, kk; | |||
for (i = 0; i < (PARAMS_N * PARAMS_NBAR); i += 2) { | |||
*((uint32_t *)&out[i]) = *((uint32_t *)&e[i]); | |||
} | |||
int k; | |||
uint16_t a_cols[PARAMS_N * PARAMS_STRIPE_STEP] = {0}; | |||
uint16_t a_cols_t[PARAMS_N * PARAMS_STRIPE_STEP]; | |||
uint16_t a_cols_temp[PARAMS_N * PARAMS_STRIPE_STEP] = {0}; | |||
aes128ctx ctx128; | |||
aes128_keyexp(&ctx128, seed_A); | |||
for (i = 0, j = 0; i < PARAMS_N; i++, j += PARAMS_STRIPE_STEP) { | |||
a_cols_temp[j] = PQCLEAN_FRODOKEM640AES_OPT_UINT16_TO_LE(i); // Loading values in the little-endian order | |||
} | |||
for (kk = 0; kk < PARAMS_N; kk += PARAMS_STRIPE_STEP) { // Go through A's columns, 8 (== PARAMS_STRIPE_STEP) columns at a time. | |||
for (i = 0; i < (PARAMS_N * PARAMS_STRIPE_STEP); i += PARAMS_STRIPE_STEP) { | |||
a_cols_temp[i + 1] = PQCLEAN_FRODOKEM640AES_OPT_UINT16_TO_LE(kk); // Loading values in the little-endian order | |||
} | |||
aes128_ecb((uint8_t *)a_cols, (uint8_t *)a_cols_temp, PARAMS_N * PARAMS_STRIPE_STEP * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); | |||
for (i = 0; i < PARAMS_N; i++) { // Transpose a_cols to have access to it in the column-major order. | |||
for (k = 0; k < PARAMS_STRIPE_STEP; k++) { | |||
a_cols_t[k * PARAMS_N + i] = PQCLEAN_FRODOKEM640AES_OPT_LE_TO_UINT16(a_cols[i * PARAMS_STRIPE_STEP + k]); | |||
} | |||
} | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
for (k = 0; k < PARAMS_STRIPE_STEP; k += PARAMS_PARALLEL) { | |||
uint16_t sum[PARAMS_PARALLEL] = {0}; | |||
for (j = 0; j < PARAMS_N; j++) { // Matrix-vector multiplication | |||
uint16_t sp = s[i * PARAMS_N + j]; | |||
sum[0] += sp * a_cols_t[(k + 0) * PARAMS_N + j]; | |||
sum[1] += sp * a_cols_t[(k + 1) * PARAMS_N + j]; | |||
sum[2] += sp * a_cols_t[(k + 2) * PARAMS_N + j]; | |||
sum[3] += sp * a_cols_t[(k + 3) * PARAMS_N + j]; | |||
} | |||
out[i * PARAMS_N + kk + k + 0] += sum[0]; | |||
out[i * PARAMS_N + kk + k + 2] += sum[2]; | |||
out[i * PARAMS_N + kk + k + 1] += sum[1]; | |||
out[i * PARAMS_N + kk + k + 3] += sum[3]; | |||
} | |||
} | |||
} | |||
return 1; | |||
} |
@@ -0,0 +1,35 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: noise sampling functions | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
static uint16_t CDF_TABLE[CDF_TABLE_LEN] = CDF_TABLE_DATA; | |||
void PQCLEAN_FRODOKEM640AES_OPT_sample_n(uint16_t *s, size_t n) { | |||
// Fills vector s with n samples from the noise distribution which requires 16 bits to sample. | |||
// The distribution is specified by its CDF. | |||
// Input: pseudo-random values (2*n bytes) passed in s. The input is overwritten by the output. | |||
size_t i; | |||
unsigned int j; | |||
for (i = 0; i < n; ++i) { | |||
uint16_t sample = 0; | |||
uint16_t prnd = s[i] >> 1; // Drop the least significant bit | |||
uint16_t sign = s[i] & 0x1; // Pick the least significant bit | |||
// No need to compare with the last value. | |||
for (j = 0; j < (unsigned int)(CDF_TABLE_LEN - 1); j++) { | |||
// Constant time comparison: 1 if CDF_TABLE[j] < s, 0 otherwise. Uses the fact that CDF_TABLE[j] and s fit in 15 bits. | |||
sample += (uint16_t)(CDF_TABLE[j] - prnd) >> 15; | |||
} | |||
// Assuming that sign is either 0 or 1, flips sample iff sign = 1 | |||
s[i] = ((-sign) ^ sample) + sign; | |||
} | |||
} |
@@ -0,0 +1,27 @@ | |||
#ifndef PARAMS_H | |||
#define PARAMS_H | |||
#define CRYPTO_SECRETKEYBYTES PQCLEAN_FRODOKEM640AES_OPT_CRYPTO_SECRETKEYBYTES | |||
#define CRYPTO_PUBLICKEYBYTES PQCLEAN_FRODOKEM640AES_OPT_CRYPTO_PUBLICKEYBYTES | |||
#define CRYPTO_BYTES PQCLEAN_FRODOKEM640AES_OPT_CRYPTO_BYTES | |||
#define CRYPTO_CIPHERTEXTBYTES PQCLEAN_FRODOKEM640AES_OPT_CRYPTO_CIPHERTEXTBYTES | |||
#define PARAMS_N 640 | |||
#define PARAMS_NBAR 8 | |||
#define PARAMS_LOGQ 15 | |||
#define PARAMS_Q (1 << PARAMS_LOGQ) | |||
#define PARAMS_EXTRACTED_BITS 2 | |||
#define PARAMS_STRIPE_STEP 8 | |||
#define PARAMS_PARALLEL 4 | |||
#define BYTES_SEED_A 16 | |||
#define BYTES_MU ((PARAMS_EXTRACTED_BITS * PARAMS_NBAR * PARAMS_NBAR) / 8) | |||
#define BYTES_PKHASH CRYPTO_BYTES | |||
// Selecting SHAKE XOF function for the KEM and noise sampling | |||
#define shake shake128 | |||
// CDF table | |||
#define CDF_TABLE_DATA {4643, 13363, 20579, 25843, 29227, 31145, 32103, 32525, 32689, 32745, 32762, 32766, 32767} | |||
#define CDF_TABLE_LEN 13 | |||
#endif |
@@ -0,0 +1,235 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: additional functions for FrodoKEM | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
#define min(x, y) (((x) < (y)) ? (x) : (y)) | |||
uint16_t PQCLEAN_FRODOKEM640AES_OPT_LE_TO_UINT16(uint16_t n) { | |||
return (((uint8_t *) &n)[0] | (((uint8_t *) &n)[1] << 8)); | |||
} | |||
uint16_t PQCLEAN_FRODOKEM640AES_OPT_UINT16_TO_LE(uint16_t n) { | |||
uint16_t y; | |||
uint8_t *z = (uint8_t *) &y; | |||
z[0] = n & 0xFF; | |||
z[1] = (n & 0xFF00) >> 8; | |||
return y; | |||
} | |||
void PQCLEAN_FRODOKEM640AES_OPT_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s) { | |||
// Multiply by s on the right | |||
// Inputs: b (N_BAR x N), s (N x N_BAR) | |||
// Output: out = b*s (N_BAR x N_BAR) | |||
int i, j, k; | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
for (j = 0; j < PARAMS_NBAR; j++) { | |||
out[i * PARAMS_NBAR + j] = 0; | |||
for (k = 0; k < PARAMS_N; k++) { | |||
out[i * PARAMS_NBAR + j] += b[i * PARAMS_N + k] * s[j * PARAMS_N + k]; | |||
} | |||
out[i * PARAMS_NBAR + j] = (uint32_t)(out[i * PARAMS_NBAR + j]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM640AES_OPT_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e) { | |||
// Multiply by s on the left | |||
// Inputs: b (N x N_BAR), s (N_BAR x N), e (N_BAR x N_BAR) | |||
// Output: out = s*b + e (N_BAR x N_BAR) | |||
int i, j, k; | |||
for (k = 0; k < PARAMS_NBAR; k++) { | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
out[k * PARAMS_NBAR + i] = e[k * PARAMS_NBAR + i]; | |||
for (j = 0; j < PARAMS_N; j++) { | |||
out[k * PARAMS_NBAR + i] += s[k * PARAMS_N + j] * b[j * PARAMS_NBAR + i]; | |||
} | |||
out[k * PARAMS_NBAR + i] = (uint32_t)(out[k * PARAMS_NBAR + i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM640AES_OPT_add(uint16_t *out, const uint16_t *a, const uint16_t *b) { | |||
// Add a and b | |||
// Inputs: a, b (N_BAR x N_BAR) | |||
// Output: c = a + b | |||
for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { | |||
out[i] = (a[i] + b[i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM640AES_OPT_sub(uint16_t *out, const uint16_t *a, const uint16_t *b) { | |||
// Subtract a and b | |||
// Inputs: a, b (N_BAR x N_BAR) | |||
// Output: c = a - b | |||
for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { | |||
out[i] = (a[i] - b[i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM640AES_OPT_key_encode(uint16_t *out, const uint16_t *in) { | |||
// Encoding | |||
unsigned int i, j, npieces_word = 8; | |||
unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; | |||
uint64_t temp, mask = ((uint64_t)1 << PARAMS_EXTRACTED_BITS) - 1; | |||
uint16_t *pos = out; | |||
for (i = 0; i < nwords; i++) { | |||
temp = 0; | |||
for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { | |||
temp |= ((uint64_t)((uint8_t *)in)[i * PARAMS_EXTRACTED_BITS + j]) << (8 * j); | |||
} | |||
for (j = 0; j < npieces_word; j++) { | |||
*pos = (uint16_t)((temp & mask) << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS)); | |||
temp >>= PARAMS_EXTRACTED_BITS; | |||
pos++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM640AES_OPT_key_decode(uint16_t *out, const uint16_t *in) { | |||
// Decoding | |||
unsigned int i, j, index = 0, npieces_word = 8; | |||
unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; | |||
uint16_t temp, maskex = ((uint16_t)1 << PARAMS_EXTRACTED_BITS) - 1, maskq = ((uint16_t)1 << PARAMS_LOGQ) - 1; | |||
uint8_t *pos = (uint8_t *)out; | |||
uint64_t templong; | |||
for (i = 0; i < nwords; i++) { | |||
templong = 0; | |||
for (j = 0; j < npieces_word; j++) { // temp = floor(in*2^{-11}+0.5) | |||
temp = ((in[index] & maskq) + (1 << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS - 1))) >> (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS); | |||
templong |= ((uint64_t)(temp & maskex)) << (PARAMS_EXTRACTED_BITS * j); | |||
index++; | |||
} | |||
for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { | |||
pos[i * PARAMS_EXTRACTED_BITS + j] = (templong >> (8 * j)) & 0xFF; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM640AES_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb) { | |||
// Pack the input uint16 vector into a char output vector, copying lsb bits from each input element. | |||
// If inlen * lsb / 8 > outlen, only outlen * 8 bits are copied. | |||
memset(out, 0, outlen); | |||
size_t i = 0; // whole bytes already filled in | |||
size_t j = 0; // whole uint16_t already copied | |||
uint16_t w = 0; // the leftover, not yet copied | |||
uint8_t bits = 0; // the number of lsb in w | |||
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { | |||
/* | |||
in: | | |********|********| | |||
^ | |||
j | |||
w : | ****| | |||
^ | |||
bits | |||
out:|**|**|**|**|**|**|**|**|* | | |||
^^ | |||
ib | |||
*/ | |||
uint8_t b = 0; // bits in out[i] already filled in | |||
while (b < 8) { | |||
int nbits = min(8 - b, bits); | |||
uint16_t mask = (1 << nbits) - 1; | |||
uint8_t t = (uint8_t) ((w >> (bits - nbits)) & mask); // the bits to copy from w to out | |||
out[i] = out[i] + (t << (8 - b - nbits)); | |||
b += (uint8_t) nbits; | |||
bits -= (uint8_t) nbits; | |||
w &= ~(mask << bits); // not strictly necessary; mostly for debugging | |||
if (bits == 0) { | |||
if (j < inlen) { | |||
w = in[j]; | |||
bits = lsb; | |||
j++; | |||
} else { | |||
break; // the input vector is exhausted | |||
} | |||
} | |||
} | |||
if (b == 8) { // out[i] is filled in | |||
i++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM640AES_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb) { | |||
// Unpack the input char vector into a uint16_t output vector, copying lsb bits | |||
// for each output element from input. outlen must be at least ceil(inlen * 8 / lsb). | |||
memset(out, 0, outlen * sizeof(uint16_t)); | |||
size_t i = 0; // whole uint16_t already filled in | |||
size_t j = 0; // whole bytes already copied | |||
uint8_t w = 0; // the leftover, not yet copied | |||
uint8_t bits = 0; // the number of lsb bits of w | |||
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { | |||
/* | |||
in: | | | | | | |**|**|... | |||
^ | |||
j | |||
w : | *| | |||
^ | |||
bits | |||
out:| *****| *****| *** | |... | |||
^ ^ | |||
i b | |||
*/ | |||
uint8_t b = 0; // bits in out[i] already filled in | |||
while (b < lsb) { | |||
int nbits = min(lsb - b, bits); | |||
uint16_t mask = (1 << nbits) - 1; | |||
uint8_t t = (w >> (bits - nbits)) & mask; // the bits to copy from w to out | |||
out[i] = out[i] + (t << (lsb - b - nbits)); | |||
b += (uint8_t) nbits; | |||
bits -= (uint8_t) nbits; | |||
w &= ~(mask << bits); // not strictly necessary; mostly for debugging | |||
if (bits == 0) { | |||
if (j < inlen) { | |||
w = in[j]; | |||
bits = 8; | |||
j++; | |||
} else { | |||
break; // the input vector is exhausted | |||
} | |||
} | |||
} | |||
if (b == lsb) { // out[i] is filled in | |||
i++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM640AES_OPT_clear_bytes(uint8_t *mem, size_t n) { | |||
// Clear 8-bit bytes from memory. "n" indicates the number of bytes to be zeroed. | |||
// This function uses the volatile type qualifier to inform the compiler not to optimize out the memory clearing. | |||
volatile uint8_t *v = mem; | |||
for (size_t i = 0; i < n; i++) { | |||
v[i] = 0; | |||
} | |||
} |
@@ -23,3 +23,5 @@ auxiliary-submitters: | |||
implementations: | |||
- name: clean | |||
version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/d5bbd0417ba111b08a959c0042a1dcc65fb14a89 | |||
- name: opt | |||
version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/d5bbd0417ba111b08a959c0042a1dcc65fb14a89 |
@@ -4,7 +4,7 @@ LIB=libfrodokem640shake_clean.a | |||
HEADERS=api.h params.h common.h | |||
OBJECTS=kem.o matrix_shake.o noise.o util.o | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
@@ -0,0 +1,21 @@ | |||
MIT License | |||
Copyright (c) Microsoft Corporation. All rights reserved. | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE |
@@ -0,0 +1,19 @@ | |||
# This Makefile can be used with GNU Make or BSD Make | |||
LIB=libfrodokem640shake_opt.a | |||
HEADERS=api.h params.h common.h | |||
OBJECTS=kem.o matrix_shake.o noise.o util.o | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
%.o: %.c $(HEADERS) | |||
$(CC) $(CFLAGS) -c -o $@ $< | |||
$(LIB): $(OBJECTS) | |||
$(AR) -r $@ $(OBJECTS) | |||
clean: | |||
$(RM) $(OBJECTS) | |||
$(RM) $(LIB) |
@@ -0,0 +1,19 @@ | |||
# This Makefile can be used with Microsoft Visual Studio's nmake using the command: | |||
# nmake /f Makefile.Microsoft_nmake | |||
LIBRARY=libfrodokem640shake_opt.lib | |||
OBJECTS=kem.obj matrix_shake.obj noise.obj util.obj | |||
CFLAGS=/nologo /I ..\..\..\common /W4 /WX | |||
all: $(LIBRARY) | |||
# Make sure objects are recompiled if headers change. | |||
$(OBJECTS): *.h | |||
$(LIBRARY): $(OBJECTS) | |||
LIB.EXE /NOLOGO /WX /OUT:$@ $** | |||
clean: | |||
-DEL $(OBJECTS) | |||
-DEL $(LIBRARY) |
@@ -0,0 +1,20 @@ | |||
#ifndef PQCLEAN_FRODOKEM640SHAKE_OPT_API_H | |||
#define PQCLEAN_FRODOKEM640SHAKE_OPT_API_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#define PQCLEAN_FRODOKEM640SHAKE_OPT_CRYPTO_SECRETKEYBYTES 19888 // sizeof(s) + CRYPTO_PUBLICKEYBYTES + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH | |||
#define PQCLEAN_FRODOKEM640SHAKE_OPT_CRYPTO_PUBLICKEYBYTES 9616 // sizeof(seed_A) + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 | |||
#define PQCLEAN_FRODOKEM640SHAKE_OPT_CRYPTO_BYTES 16 | |||
#define PQCLEAN_FRODOKEM640SHAKE_OPT_CRYPTO_CIPHERTEXTBYTES 9720 // (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + (PARAMS_LOGQ*PARAMS_NBAR*PARAMS_NBAR)/8 | |||
#define PQCLEAN_FRODOKEM640SHAKE_OPT_CRYPTO_ALGNAME "FrodoKEM-640-SHAKE" | |||
int PQCLEAN_FRODOKEM640SHAKE_OPT_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); | |||
int PQCLEAN_FRODOKEM640SHAKE_OPT_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); | |||
int PQCLEAN_FRODOKEM640SHAKE_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); | |||
#endif |
@@ -0,0 +1,19 @@ | |||
#ifndef COMMON_H | |||
#define COMMON_H | |||
int PQCLEAN_FRODOKEM640SHAKE_OPT_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); | |||
int PQCLEAN_FRODOKEM640SHAKE_OPT_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_sample_n(uint16_t *s, size_t n); | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s); | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e); | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_add(uint16_t *out, const uint16_t *a, const uint16_t *b); | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_sub(uint16_t *out, const uint16_t *a, const uint16_t *b); | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_key_encode(uint16_t *out, const uint16_t *in); | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_key_decode(uint16_t *out, const uint16_t *in); | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes(uint8_t *mem, size_t n); | |||
uint16_t PQCLEAN_FRODOKEM640SHAKE_OPT_LE_TO_UINT16(uint16_t n); | |||
uint16_t PQCLEAN_FRODOKEM640SHAKE_OPT_UINT16_TO_LE(uint16_t n); | |||
#endif |
@@ -0,0 +1,238 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: Key Encapsulation Mechanism (KEM) based on Frodo | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "fips202.h" | |||
#include "randombytes.h" | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
int PQCLEAN_FRODOKEM640SHAKE_OPT_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { | |||
// FrodoKEM's key generation | |||
// Outputs: public key pk ( BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 bytes) | |||
// secret key sk (CRYPTO_BYTES + BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH bytes) | |||
uint8_t *pk_seedA = &pk[0]; | |||
uint8_t *pk_b = &pk[BYTES_SEED_A]; | |||
uint8_t *sk_s = &sk[0]; | |||
uint8_t *sk_pk = &sk[CRYPTO_BYTES]; | |||
uint8_t *sk_S = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; | |||
uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t S[2 * PARAMS_N * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *E = &S[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint8_t randomness[2 * CRYPTO_BYTES + BYTES_SEED_A]; // contains secret data via randomness_s and randomness_seedSE | |||
uint8_t *randomness_s = &randomness[0]; // contains secret data | |||
uint8_t *randomness_seedSE = &randomness[CRYPTO_BYTES]; // contains secret data | |||
uint8_t *randomness_z = &randomness[2 * CRYPTO_BYTES]; | |||
uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data | |||
// Generate the secret value s, the seed for S and E, and the seed for the seed for A. Add seed_A to the public key | |||
randombytes(randomness, CRYPTO_BYTES + CRYPTO_BYTES + BYTES_SEED_A); | |||
shake(pk_seedA, BYTES_SEED_A, randomness_z, BYTES_SEED_A); | |||
// Generate S and E, and compute B = A*S + E. Generate A on-the-fly | |||
shake_input_seedSE[0] = 0x5F; | |||
memcpy(&shake_input_seedSE[1], randomness_seedSE, CRYPTO_BYTES); | |||
shake((uint8_t *)S, 2 * PARAMS_N * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < 2 * PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM640SHAKE_OPT_LE_TO_UINT16(S[i]); | |||
} | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_sample_n(S, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_sample_n(E, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_mul_add_as_plus_e(B, S, E, pk); | |||
// Encode the second part of the public key | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_pack(pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, B, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Add s, pk and S to the secret key | |||
memcpy(sk_s, randomness_s, CRYPTO_BYTES); | |||
memcpy(sk_pk, pk, CRYPTO_PUBLICKEYBYTES); | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM640SHAKE_OPT_UINT16_TO_LE(S[i]); | |||
} | |||
memcpy(sk_S, S, 2 * PARAMS_N * PARAMS_NBAR); | |||
// Add H(pk) to the secret key | |||
shake(sk_pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes((uint8_t *)E, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes(randomness, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} | |||
int PQCLEAN_FRODOKEM640SHAKE_OPT_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { | |||
// FrodoKEM's key encapsulation | |||
const uint8_t *pk_seedA = &pk[0]; | |||
const uint8_t *pk_b = &pk[BYTES_SEED_A]; | |||
uint8_t *ct_c1 = &ct[0]; | |||
uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t V[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via mu | |||
uint8_t *pkh = &G2in[0]; | |||
uint8_t *mu = &G2in[BYTES_PKHASH]; // contains secret data | |||
uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data | |||
uint8_t *seedSE = &G2out[0]; // contains secret data | |||
uint8_t *k = &G2out[CRYPTO_BYTES]; // contains secret data | |||
uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k | |||
uint8_t *Fin_ct = &Fin[0]; | |||
uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data | |||
uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data | |||
// pkh <- G_1(pk), generate random mu, compute (seedSE || k) = G_2(pkh || mu) | |||
shake(pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); | |||
randombytes(mu, BYTES_MU); | |||
shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); | |||
// Generate Sp and Ep, and compute Bp = Sp*A + Ep. Generate A on-the-fly | |||
shake_input_seedSE[0] = 0x96; | |||
memcpy(&shake_input_seedSE[1], seedSE, CRYPTO_BYTES); | |||
shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { | |||
Sp[i] = PQCLEAN_FRODOKEM640SHAKE_OPT_LE_TO_UINT16(Sp[i]); | |||
} | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_sample_n(Sp, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_sample_n(Ep, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_mul_add_sa_plus_e(Bp, Sp, Ep, pk_seedA); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_pack(ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, Bp, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Generate Epp, and compute V = Sp*B + Epp | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_mul_add_sb_plus_e(V, B, Sp, Epp); | |||
// Encode mu, and compute C = V + enc(mu) (mod q) | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_key_encode(C, (uint16_t *)mu); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_add(C, V, C); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_pack(ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, C, PARAMS_NBAR * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Compute ss = F(ct||KK) | |||
memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); | |||
memcpy(Fin_k, k, CRYPTO_BYTES); | |||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes((uint8_t *)V, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes(mu, BYTES_MU); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes(G2out, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes(Fin_k, CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} | |||
int PQCLEAN_FRODOKEM640SHAKE_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { | |||
// FrodoKEM's key decapsulation | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t W[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t CC[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t BBp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
const uint8_t *ct_c1 = &ct[0]; | |||
const uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; | |||
const uint8_t *sk_s = &sk[0]; | |||
const uint8_t *sk_pk = &sk[CRYPTO_BYTES]; | |||
const uint16_t *sk_S = (uint16_t *) &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; | |||
uint16_t S[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
const uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; | |||
const uint8_t *pk_seedA = &sk_pk[0]; | |||
const uint8_t *pk_b = &sk_pk[BYTES_SEED_A]; | |||
uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via muprime | |||
uint8_t *pkh = &G2in[0]; | |||
uint8_t *muprime = &G2in[BYTES_PKHASH]; // contains secret data | |||
uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data | |||
uint8_t *seedSEprime = &G2out[0]; // contains secret data | |||
uint8_t *kprime = &G2out[CRYPTO_BYTES]; // contains secret data | |||
uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k | |||
uint8_t *Fin_ct = &Fin[0]; | |||
uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data | |||
uint8_t shake_input_seedSEprime[1 + CRYPTO_BYTES]; // contains secret data | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM640SHAKE_OPT_LE_TO_UINT16(sk_S[i]); | |||
} | |||
// Compute W = C - Bp*S (mod q), and decode the randomness mu | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_unpack(Bp, PARAMS_N * PARAMS_NBAR, ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_unpack(C, PARAMS_NBAR * PARAMS_NBAR, ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_mul_bs(W, Bp, S); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_sub(W, C, W); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_key_decode((uint16_t *)muprime, W); | |||
// Generate (seedSE' || k') = G_2(pkh || mu') | |||
memcpy(pkh, sk_pkh, BYTES_PKHASH); | |||
shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); | |||
// Generate Sp and Ep, and compute BBp = Sp*A + Ep. Generate A on-the-fly | |||
shake_input_seedSEprime[0] = 0x96; | |||
memcpy(&shake_input_seedSEprime[1], seedSEprime, CRYPTO_BYTES); | |||
shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSEprime, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { | |||
Sp[i] = PQCLEAN_FRODOKEM640SHAKE_OPT_LE_TO_UINT16(Sp[i]); | |||
} | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_sample_n(Sp, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_sample_n(Ep, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_mul_add_sa_plus_e(BBp, Sp, Ep, pk_seedA); | |||
// Generate Epp, and compute W = Sp*B + Epp | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_mul_add_sb_plus_e(W, B, Sp, Epp); | |||
// Encode mu, and compute CC = W + enc(mu') (mod q) | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_key_encode(CC, (uint16_t *)muprime); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_add(CC, W, CC); | |||
// Prepare input to F | |||
memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); | |||
// Reducing BBp modulo q | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
BBp[i] = BBp[i] & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
// Is (Bp == BBp & C == CC) = true | |||
if (memcmp(Bp, BBp, 2 * PARAMS_N * PARAMS_NBAR) == 0 && memcmp(C, CC, 2 * PARAMS_NBAR * PARAMS_NBAR) == 0) { | |||
// Load k' to do ss = F(ct || k') | |||
memcpy(Fin_k, kprime, CRYPTO_BYTES); | |||
} else { | |||
// Load s to do ss = F(ct || s) | |||
memcpy(Fin_k, sk_s, CRYPTO_BYTES); | |||
} | |||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes((uint8_t *)W, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes(muprime, BYTES_MU); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes(G2out, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes(Fin_k, CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes(shake_input_seedSEprime, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} |
@@ -0,0 +1,108 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: matrix arithmetic functions used by the KEM | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "fips202.h" | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
int PQCLEAN_FRODOKEM640SHAKE_OPT_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { | |||
// Generate-and-multiply: generate matrix A (N x N) row-wise, multiply by s on the right. | |||
// Inputs: s, e (N x N_BAR) | |||
// Output: out = A*s + e (N x N_BAR) | |||
int j, k; | |||
uint16_t i; | |||
int16_t a_row[4 * PARAMS_N]; | |||
for (i = 0; i < (PARAMS_N * PARAMS_NBAR); i += 2) { | |||
*((uint32_t *)&out[i]) = *((uint32_t *)&e[i]); | |||
} | |||
uint8_t seed_A_separated[2 + BYTES_SEED_A]; | |||
uint16_t *seed_A_origin = (uint16_t *)&seed_A_separated; | |||
memcpy(&seed_A_separated[2], seed_A, BYTES_SEED_A); | |||
for (i = 0; i < PARAMS_N; i += 4) { | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM640SHAKE_OPT_UINT16_TO_LE(i + 0); | |||
shake128((unsigned char *)(a_row + 0 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM640SHAKE_OPT_UINT16_TO_LE(i + 1); | |||
shake128((unsigned char *)(a_row + 1 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM640SHAKE_OPT_UINT16_TO_LE(i + 2); | |||
shake128((unsigned char *)(a_row + 2 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM640SHAKE_OPT_UINT16_TO_LE(i + 3); | |||
shake128((unsigned char *)(a_row + 3 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
for (k = 0; k < 4 * PARAMS_N; k++) { | |||
a_row[k] = PQCLEAN_FRODOKEM640SHAKE_OPT_LE_TO_UINT16(a_row[k]); | |||
} | |||
for (k = 0; k < PARAMS_NBAR; k++) { | |||
uint16_t sum[4] = {0}; | |||
for (j = 0; j < PARAMS_N; j++) { // Matrix-vector multiplication | |||
uint16_t sp = s[k * PARAMS_N + j]; | |||
sum[0] += a_row[0 * PARAMS_N + j] * sp; // Go through four lines with same s | |||
sum[1] += a_row[1 * PARAMS_N + j] * sp; | |||
sum[2] += a_row[2 * PARAMS_N + j] * sp; | |||
sum[3] += a_row[3 * PARAMS_N + j] * sp; | |||
} | |||
out[(i + 0)*PARAMS_NBAR + k] += sum[0]; | |||
out[(i + 2)*PARAMS_NBAR + k] += sum[2]; | |||
out[(i + 1)*PARAMS_NBAR + k] += sum[1]; | |||
out[(i + 3)*PARAMS_NBAR + k] += sum[3]; | |||
} | |||
} | |||
return 1; | |||
} | |||
int PQCLEAN_FRODOKEM640SHAKE_OPT_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { | |||
// Generate-and-multiply: generate matrix A (N x N) column-wise, multiply by s' on the left. | |||
// Inputs: s', e' (N_BAR x N) | |||
// Output: out = s'*A + e' (N_BAR x N) | |||
int i, j; | |||
uint16_t kk; | |||
for (i = 0; i < (PARAMS_N * PARAMS_NBAR); i += 2) { | |||
*((uint32_t *)&out[i]) = *((uint32_t *)&e[i]); | |||
} | |||
int t = 0; | |||
uint16_t a_cols[4 * PARAMS_N]; | |||
int k; | |||
uint8_t seed_A_separated[2 + BYTES_SEED_A]; | |||
uint16_t *seed_A_origin = (uint16_t *)&seed_A_separated; | |||
memcpy(&seed_A_separated[2], seed_A, BYTES_SEED_A); | |||
for (kk = 0; kk < PARAMS_N; kk += 4) { | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM640SHAKE_OPT_UINT16_TO_LE(kk + 0); | |||
shake128((unsigned char *)(a_cols + 0 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM640SHAKE_OPT_UINT16_TO_LE(kk + 1); | |||
shake128((unsigned char *)(a_cols + 1 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM640SHAKE_OPT_UINT16_TO_LE(kk + 2); | |||
shake128((unsigned char *)(a_cols + 2 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM640SHAKE_OPT_UINT16_TO_LE(kk + 3); | |||
shake128((unsigned char *)(a_cols + 3 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
for (i = 0; i < 4 * PARAMS_N; i++) { | |||
a_cols[i] = PQCLEAN_FRODOKEM640SHAKE_OPT_LE_TO_UINT16(a_cols[i]); | |||
} | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
uint16_t sum[PARAMS_N] = {0}; | |||
for (j = 0; j < 4; j++) { | |||
uint16_t sp = s[i * PARAMS_N + kk + j]; | |||
for (k = 0; k < PARAMS_N; k++) { // Matrix-vector multiplication | |||
sum[k] += sp * a_cols[(t + j) * PARAMS_N + k]; | |||
} | |||
} | |||
for (k = 0; k < PARAMS_N; k++) { | |||
out[i * PARAMS_N + k] += sum[k]; | |||
} | |||
} | |||
} | |||
return 1; | |||
} |
@@ -0,0 +1,35 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: noise sampling functions | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
static uint16_t CDF_TABLE[CDF_TABLE_LEN] = CDF_TABLE_DATA; | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_sample_n(uint16_t *s, size_t n) { | |||
// Fills vector s with n samples from the noise distribution which requires 16 bits to sample. | |||
// The distribution is specified by its CDF. | |||
// Input: pseudo-random values (2*n bytes) passed in s. The input is overwritten by the output. | |||
size_t i; | |||
unsigned int j; | |||
for (i = 0; i < n; ++i) { | |||
uint16_t sample = 0; | |||
uint16_t prnd = s[i] >> 1; // Drop the least significant bit | |||
uint16_t sign = s[i] & 0x1; // Pick the least significant bit | |||
// No need to compare with the last value. | |||
for (j = 0; j < (unsigned int)(CDF_TABLE_LEN - 1); j++) { | |||
// Constant time comparison: 1 if CDF_TABLE[j] < s, 0 otherwise. Uses the fact that CDF_TABLE[j] and s fit in 15 bits. | |||
sample += (uint16_t)(CDF_TABLE[j] - prnd) >> 15; | |||
} | |||
// Assuming that sign is either 0 or 1, flips sample iff sign = 1 | |||
s[i] = ((-sign) ^ sample) + sign; | |||
} | |||
} |
@@ -0,0 +1,27 @@ | |||
#ifndef PARAMS_H | |||
#define PARAMS_H | |||
#define CRYPTO_SECRETKEYBYTES PQCLEAN_FRODOKEM640SHAKE_OPT_CRYPTO_SECRETKEYBYTES | |||
#define CRYPTO_PUBLICKEYBYTES PQCLEAN_FRODOKEM640SHAKE_OPT_CRYPTO_PUBLICKEYBYTES | |||
#define CRYPTO_BYTES PQCLEAN_FRODOKEM640SHAKE_OPT_CRYPTO_BYTES | |||
#define CRYPTO_CIPHERTEXTBYTES PQCLEAN_FRODOKEM640SHAKE_OPT_CRYPTO_CIPHERTEXTBYTES | |||
#define PARAMS_N 640 | |||
#define PARAMS_NBAR 8 | |||
#define PARAMS_LOGQ 15 | |||
#define PARAMS_Q (1 << PARAMS_LOGQ) | |||
#define PARAMS_EXTRACTED_BITS 2 | |||
#define PARAMS_STRIPE_STEP 8 | |||
#define PARAMS_PARALLEL 4 | |||
#define BYTES_SEED_A 16 | |||
#define BYTES_MU ((PARAMS_EXTRACTED_BITS * PARAMS_NBAR * PARAMS_NBAR) / 8) | |||
#define BYTES_PKHASH CRYPTO_BYTES | |||
// Selecting SHAKE XOF function for the KEM and noise sampling | |||
#define shake shake128 | |||
// CDF table | |||
#define CDF_TABLE_DATA {4643, 13363, 20579, 25843, 29227, 31145, 32103, 32525, 32689, 32745, 32762, 32766, 32767} | |||
#define CDF_TABLE_LEN 13 | |||
#endif |
@@ -0,0 +1,235 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: additional functions for FrodoKEM | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
#define min(x, y) (((x) < (y)) ? (x) : (y)) | |||
uint16_t PQCLEAN_FRODOKEM640SHAKE_OPT_LE_TO_UINT16(uint16_t n) { | |||
return (((uint8_t *) &n)[0] | (((uint8_t *) &n)[1] << 8)); | |||
} | |||
uint16_t PQCLEAN_FRODOKEM640SHAKE_OPT_UINT16_TO_LE(uint16_t n) { | |||
uint16_t y; | |||
uint8_t *z = (uint8_t *) &y; | |||
z[0] = n & 0xFF; | |||
z[1] = (n & 0xFF00) >> 8; | |||
return y; | |||
} | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s) { | |||
// Multiply by s on the right | |||
// Inputs: b (N_BAR x N), s (N x N_BAR) | |||
// Output: out = b*s (N_BAR x N_BAR) | |||
int i, j, k; | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
for (j = 0; j < PARAMS_NBAR; j++) { | |||
out[i * PARAMS_NBAR + j] = 0; | |||
for (k = 0; k < PARAMS_N; k++) { | |||
out[i * PARAMS_NBAR + j] += b[i * PARAMS_N + k] * s[j * PARAMS_N + k]; | |||
} | |||
out[i * PARAMS_NBAR + j] = (uint32_t)(out[i * PARAMS_NBAR + j]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e) { | |||
// Multiply by s on the left | |||
// Inputs: b (N x N_BAR), s (N_BAR x N), e (N_BAR x N_BAR) | |||
// Output: out = s*b + e (N_BAR x N_BAR) | |||
int i, j, k; | |||
for (k = 0; k < PARAMS_NBAR; k++) { | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
out[k * PARAMS_NBAR + i] = e[k * PARAMS_NBAR + i]; | |||
for (j = 0; j < PARAMS_N; j++) { | |||
out[k * PARAMS_NBAR + i] += s[k * PARAMS_N + j] * b[j * PARAMS_NBAR + i]; | |||
} | |||
out[k * PARAMS_NBAR + i] = (uint32_t)(out[k * PARAMS_NBAR + i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_add(uint16_t *out, const uint16_t *a, const uint16_t *b) { | |||
// Add a and b | |||
// Inputs: a, b (N_BAR x N_BAR) | |||
// Output: c = a + b | |||
for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { | |||
out[i] = (a[i] + b[i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_sub(uint16_t *out, const uint16_t *a, const uint16_t *b) { | |||
// Subtract a and b | |||
// Inputs: a, b (N_BAR x N_BAR) | |||
// Output: c = a - b | |||
for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { | |||
out[i] = (a[i] - b[i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_key_encode(uint16_t *out, const uint16_t *in) { | |||
// Encoding | |||
unsigned int i, j, npieces_word = 8; | |||
unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; | |||
uint64_t temp, mask = ((uint64_t)1 << PARAMS_EXTRACTED_BITS) - 1; | |||
uint16_t *pos = out; | |||
for (i = 0; i < nwords; i++) { | |||
temp = 0; | |||
for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { | |||
temp |= ((uint64_t)((uint8_t *)in)[i * PARAMS_EXTRACTED_BITS + j]) << (8 * j); | |||
} | |||
for (j = 0; j < npieces_word; j++) { | |||
*pos = (uint16_t)((temp & mask) << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS)); | |||
temp >>= PARAMS_EXTRACTED_BITS; | |||
pos++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_key_decode(uint16_t *out, const uint16_t *in) { | |||
// Decoding | |||
unsigned int i, j, index = 0, npieces_word = 8; | |||
unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; | |||
uint16_t temp, maskex = ((uint16_t)1 << PARAMS_EXTRACTED_BITS) - 1, maskq = ((uint16_t)1 << PARAMS_LOGQ) - 1; | |||
uint8_t *pos = (uint8_t *)out; | |||
uint64_t templong; | |||
for (i = 0; i < nwords; i++) { | |||
templong = 0; | |||
for (j = 0; j < npieces_word; j++) { // temp = floor(in*2^{-11}+0.5) | |||
temp = ((in[index] & maskq) + (1 << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS - 1))) >> (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS); | |||
templong |= ((uint64_t)(temp & maskex)) << (PARAMS_EXTRACTED_BITS * j); | |||
index++; | |||
} | |||
for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { | |||
pos[i * PARAMS_EXTRACTED_BITS + j] = (templong >> (8 * j)) & 0xFF; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb) { | |||
// Pack the input uint16 vector into a char output vector, copying lsb bits from each input element. | |||
// If inlen * lsb / 8 > outlen, only outlen * 8 bits are copied. | |||
memset(out, 0, outlen); | |||
size_t i = 0; // whole bytes already filled in | |||
size_t j = 0; // whole uint16_t already copied | |||
uint16_t w = 0; // the leftover, not yet copied | |||
uint8_t bits = 0; // the number of lsb in w | |||
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { | |||
/* | |||
in: | | |********|********| | |||
^ | |||
j | |||
w : | ****| | |||
^ | |||
bits | |||
out:|**|**|**|**|**|**|**|**|* | | |||
^^ | |||
ib | |||
*/ | |||
uint8_t b = 0; // bits in out[i] already filled in | |||
while (b < 8) { | |||
int nbits = min(8 - b, bits); | |||
uint16_t mask = (1 << nbits) - 1; | |||
uint8_t t = (uint8_t) ((w >> (bits - nbits)) & mask); // the bits to copy from w to out | |||
out[i] = out[i] + (t << (8 - b - nbits)); | |||
b += (uint8_t) nbits; | |||
bits -= (uint8_t) nbits; | |||
w &= ~(mask << bits); // not strictly necessary; mostly for debugging | |||
if (bits == 0) { | |||
if (j < inlen) { | |||
w = in[j]; | |||
bits = lsb; | |||
j++; | |||
} else { | |||
break; // the input vector is exhausted | |||
} | |||
} | |||
} | |||
if (b == 8) { // out[i] is filled in | |||
i++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb) { | |||
// Unpack the input char vector into a uint16_t output vector, copying lsb bits | |||
// for each output element from input. outlen must be at least ceil(inlen * 8 / lsb). | |||
memset(out, 0, outlen * sizeof(uint16_t)); | |||
size_t i = 0; // whole uint16_t already filled in | |||
size_t j = 0; // whole bytes already copied | |||
uint8_t w = 0; // the leftover, not yet copied | |||
uint8_t bits = 0; // the number of lsb bits of w | |||
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { | |||
/* | |||
in: | | | | | | |**|**|... | |||
^ | |||
j | |||
w : | *| | |||
^ | |||
bits | |||
out:| *****| *****| *** | |... | |||
^ ^ | |||
i b | |||
*/ | |||
uint8_t b = 0; // bits in out[i] already filled in | |||
while (b < lsb) { | |||
int nbits = min(lsb - b, bits); | |||
uint16_t mask = (1 << nbits) - 1; | |||
uint8_t t = (w >> (bits - nbits)) & mask; // the bits to copy from w to out | |||
out[i] = out[i] + (t << (lsb - b - nbits)); | |||
b += (uint8_t) nbits; | |||
bits -= (uint8_t) nbits; | |||
w &= ~(mask << bits); // not strictly necessary; mostly for debugging | |||
if (bits == 0) { | |||
if (j < inlen) { | |||
w = in[j]; | |||
bits = 8; | |||
j++; | |||
} else { | |||
break; // the input vector is exhausted | |||
} | |||
} | |||
} | |||
if (b == lsb) { // out[i] is filled in | |||
i++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM640SHAKE_OPT_clear_bytes(uint8_t *mem, size_t n) { | |||
// Clear 8-bit bytes from memory. "n" indicates the number of bytes to be zeroed. | |||
// This function uses the volatile type qualifier to inform the compiler not to optimize out the memory clearing. | |||
volatile uint8_t *v = mem; | |||
for (size_t i = 0; i < n; i++) { | |||
v[i] = 0; | |||
} | |||
} |
@@ -23,3 +23,5 @@ auxiliary-submitters: | |||
implementations: | |||
- name: clean | |||
version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/d5bbd0417ba111b08a959c0042a1dcc65fb14a89 | |||
- name: opt | |||
version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/d5bbd0417ba111b08a959c0042a1dcc65fb14a89 |
@@ -4,7 +4,7 @@ LIB=libfrodokem976aes_clean.a | |||
HEADERS=api.h params.h common.h | |||
OBJECTS=kem.o matrix_aes.o noise.o util.o | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
@@ -0,0 +1,21 @@ | |||
MIT License | |||
Copyright (c) Microsoft Corporation. All rights reserved. | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE |
@@ -0,0 +1,19 @@ | |||
# This Makefile can be used with GNU Make or BSD Make | |||
LIB=libfrodokem976aes_opt.a | |||
HEADERS=api.h params.h common.h | |||
OBJECTS=kem.o matrix_aes.o noise.o util.o | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
%.o: %.c $(HEADERS) | |||
$(CC) $(CFLAGS) -c -o $@ $< | |||
$(LIB): $(OBJECTS) | |||
$(AR) -r $@ $(OBJECTS) | |||
clean: | |||
$(RM) $(OBJECTS) | |||
$(RM) $(LIB) |
@@ -0,0 +1,19 @@ | |||
# This Makefile can be used with Microsoft Visual Studio's nmake using the command: | |||
# nmake /f Makefile.Microsoft_nmake | |||
LIBRARY=libfrodokem976aes_opt.lib | |||
OBJECTS=kem.obj matrix_aes.obj noise.obj util.obj | |||
CFLAGS=/nologo /I ..\..\..\common /W4 /WX | |||
all: $(LIBRARY) | |||
# Make sure objects are recompiled if headers change. | |||
$(OBJECTS): *.h | |||
$(LIBRARY): $(OBJECTS) | |||
LIB.EXE /NOLOGO /WX /OUT:$@ $** | |||
clean: | |||
-DEL $(OBJECTS) | |||
-DEL $(LIBRARY) |
@@ -0,0 +1,20 @@ | |||
#ifndef PQCLEAN_FRODOKEM976AES_OPT_API_H | |||
#define PQCLEAN_FRODOKEM976AES_OPT_API_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#define PQCLEAN_FRODOKEM976AES_OPT_CRYPTO_SECRETKEYBYTES 31296 // sizeof(s) + CRYPTO_PUBLICKEYBYTES + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH | |||
#define PQCLEAN_FRODOKEM976AES_OPT_CRYPTO_PUBLICKEYBYTES 15632 // sizeof(seed_A) + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 | |||
#define PQCLEAN_FRODOKEM976AES_OPT_CRYPTO_BYTES 24 | |||
#define PQCLEAN_FRODOKEM976AES_OPT_CRYPTO_CIPHERTEXTBYTES 15744 // (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + (PARAMS_LOGQ*PARAMS_NBAR*PARAMS_NBAR)/8 | |||
#define PQCLEAN_FRODOKEM976AES_OPT_CRYPTO_ALGNAME "FrodoKEM-976-AES" | |||
int PQCLEAN_FRODOKEM976AES_OPT_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); | |||
int PQCLEAN_FRODOKEM976AES_OPT_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); | |||
int PQCLEAN_FRODOKEM976AES_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); | |||
#endif |
@@ -0,0 +1,19 @@ | |||
#ifndef COMMON_H | |||
#define COMMON_H | |||
int PQCLEAN_FRODOKEM976AES_OPT_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); | |||
int PQCLEAN_FRODOKEM976AES_OPT_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); | |||
void PQCLEAN_FRODOKEM976AES_OPT_sample_n(uint16_t *s, size_t n); | |||
void PQCLEAN_FRODOKEM976AES_OPT_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s); | |||
void PQCLEAN_FRODOKEM976AES_OPT_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e); | |||
void PQCLEAN_FRODOKEM976AES_OPT_add(uint16_t *out, const uint16_t *a, const uint16_t *b); | |||
void PQCLEAN_FRODOKEM976AES_OPT_sub(uint16_t *out, const uint16_t *a, const uint16_t *b); | |||
void PQCLEAN_FRODOKEM976AES_OPT_key_encode(uint16_t *out, const uint16_t *in); | |||
void PQCLEAN_FRODOKEM976AES_OPT_key_decode(uint16_t *out, const uint16_t *in); | |||
void PQCLEAN_FRODOKEM976AES_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); | |||
void PQCLEAN_FRODOKEM976AES_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); | |||
void PQCLEAN_FRODOKEM976AES_OPT_clear_bytes(uint8_t *mem, size_t n); | |||
uint16_t PQCLEAN_FRODOKEM976AES_OPT_LE_TO_UINT16(uint16_t n); | |||
uint16_t PQCLEAN_FRODOKEM976AES_OPT_UINT16_TO_LE(uint16_t n); | |||
#endif |
@@ -0,0 +1,238 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: Key Encapsulation Mechanism (KEM) based on Frodo | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "fips202.h" | |||
#include "randombytes.h" | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
int PQCLEAN_FRODOKEM976AES_OPT_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { | |||
// FrodoKEM's key generation | |||
// Outputs: public key pk ( BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 bytes) | |||
// secret key sk (CRYPTO_BYTES + BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH bytes) | |||
uint8_t *pk_seedA = &pk[0]; | |||
uint8_t *pk_b = &pk[BYTES_SEED_A]; | |||
uint8_t *sk_s = &sk[0]; | |||
uint8_t *sk_pk = &sk[CRYPTO_BYTES]; | |||
uint8_t *sk_S = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; | |||
uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t S[2 * PARAMS_N * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *E = &S[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint8_t randomness[2 * CRYPTO_BYTES + BYTES_SEED_A]; // contains secret data via randomness_s and randomness_seedSE | |||
uint8_t *randomness_s = &randomness[0]; // contains secret data | |||
uint8_t *randomness_seedSE = &randomness[CRYPTO_BYTES]; // contains secret data | |||
uint8_t *randomness_z = &randomness[2 * CRYPTO_BYTES]; | |||
uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data | |||
// Generate the secret value s, the seed for S and E, and the seed for the seed for A. Add seed_A to the public key | |||
randombytes(randomness, CRYPTO_BYTES + CRYPTO_BYTES + BYTES_SEED_A); | |||
shake(pk_seedA, BYTES_SEED_A, randomness_z, BYTES_SEED_A); | |||
// Generate S and E, and compute B = A*S + E. Generate A on-the-fly | |||
shake_input_seedSE[0] = 0x5F; | |||
memcpy(&shake_input_seedSE[1], randomness_seedSE, CRYPTO_BYTES); | |||
shake((uint8_t *)S, 2 * PARAMS_N * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < 2 * PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM976AES_OPT_LE_TO_UINT16(S[i]); | |||
} | |||
PQCLEAN_FRODOKEM976AES_OPT_sample_n(S, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM976AES_OPT_sample_n(E, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM976AES_OPT_mul_add_as_plus_e(B, S, E, pk); | |||
// Encode the second part of the public key | |||
PQCLEAN_FRODOKEM976AES_OPT_pack(pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, B, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Add s, pk and S to the secret key | |||
memcpy(sk_s, randomness_s, CRYPTO_BYTES); | |||
memcpy(sk_pk, pk, CRYPTO_PUBLICKEYBYTES); | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM976AES_OPT_UINT16_TO_LE(S[i]); | |||
} | |||
memcpy(sk_S, S, 2 * PARAMS_N * PARAMS_NBAR); | |||
// Add H(pk) to the secret key | |||
shake(sk_pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes((uint8_t *)E, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes(randomness, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} | |||
int PQCLEAN_FRODOKEM976AES_OPT_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { | |||
// FrodoKEM's key encapsulation | |||
const uint8_t *pk_seedA = &pk[0]; | |||
const uint8_t *pk_b = &pk[BYTES_SEED_A]; | |||
uint8_t *ct_c1 = &ct[0]; | |||
uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t V[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via mu | |||
uint8_t *pkh = &G2in[0]; | |||
uint8_t *mu = &G2in[BYTES_PKHASH]; // contains secret data | |||
uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data | |||
uint8_t *seedSE = &G2out[0]; // contains secret data | |||
uint8_t *k = &G2out[CRYPTO_BYTES]; // contains secret data | |||
uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k | |||
uint8_t *Fin_ct = &Fin[0]; | |||
uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data | |||
uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data | |||
// pkh <- G_1(pk), generate random mu, compute (seedSE || k) = G_2(pkh || mu) | |||
shake(pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); | |||
randombytes(mu, BYTES_MU); | |||
shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); | |||
// Generate Sp and Ep, and compute Bp = Sp*A + Ep. Generate A on-the-fly | |||
shake_input_seedSE[0] = 0x96; | |||
memcpy(&shake_input_seedSE[1], seedSE, CRYPTO_BYTES); | |||
shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { | |||
Sp[i] = PQCLEAN_FRODOKEM976AES_OPT_LE_TO_UINT16(Sp[i]); | |||
} | |||
PQCLEAN_FRODOKEM976AES_OPT_sample_n(Sp, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM976AES_OPT_sample_n(Ep, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM976AES_OPT_mul_add_sa_plus_e(Bp, Sp, Ep, pk_seedA); | |||
PQCLEAN_FRODOKEM976AES_OPT_pack(ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, Bp, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Generate Epp, and compute V = Sp*B + Epp | |||
PQCLEAN_FRODOKEM976AES_OPT_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM976AES_OPT_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM976AES_OPT_mul_add_sb_plus_e(V, B, Sp, Epp); | |||
// Encode mu, and compute C = V + enc(mu) (mod q) | |||
PQCLEAN_FRODOKEM976AES_OPT_key_encode(C, (uint16_t *)mu); | |||
PQCLEAN_FRODOKEM976AES_OPT_add(C, V, C); | |||
PQCLEAN_FRODOKEM976AES_OPT_pack(ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, C, PARAMS_NBAR * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Compute ss = F(ct||KK) | |||
memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); | |||
memcpy(Fin_k, k, CRYPTO_BYTES); | |||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes((uint8_t *)V, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes(mu, BYTES_MU); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes(G2out, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes(Fin_k, CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} | |||
int PQCLEAN_FRODOKEM976AES_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { | |||
// FrodoKEM's key decapsulation | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t W[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t CC[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t BBp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
const uint8_t *ct_c1 = &ct[0]; | |||
const uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; | |||
const uint8_t *sk_s = &sk[0]; | |||
const uint8_t *sk_pk = &sk[CRYPTO_BYTES]; | |||
const uint16_t *sk_S = (uint16_t *) &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; | |||
uint16_t S[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
const uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; | |||
const uint8_t *pk_seedA = &sk_pk[0]; | |||
const uint8_t *pk_b = &sk_pk[BYTES_SEED_A]; | |||
uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via muprime | |||
uint8_t *pkh = &G2in[0]; | |||
uint8_t *muprime = &G2in[BYTES_PKHASH]; // contains secret data | |||
uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data | |||
uint8_t *seedSEprime = &G2out[0]; // contains secret data | |||
uint8_t *kprime = &G2out[CRYPTO_BYTES]; // contains secret data | |||
uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k | |||
uint8_t *Fin_ct = &Fin[0]; | |||
uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data | |||
uint8_t shake_input_seedSEprime[1 + CRYPTO_BYTES]; // contains secret data | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM976AES_OPT_LE_TO_UINT16(sk_S[i]); | |||
} | |||
// Compute W = C - Bp*S (mod q), and decode the randomness mu | |||
PQCLEAN_FRODOKEM976AES_OPT_unpack(Bp, PARAMS_N * PARAMS_NBAR, ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM976AES_OPT_unpack(C, PARAMS_NBAR * PARAMS_NBAR, ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM976AES_OPT_mul_bs(W, Bp, S); | |||
PQCLEAN_FRODOKEM976AES_OPT_sub(W, C, W); | |||
PQCLEAN_FRODOKEM976AES_OPT_key_decode((uint16_t *)muprime, W); | |||
// Generate (seedSE' || k') = G_2(pkh || mu') | |||
memcpy(pkh, sk_pkh, BYTES_PKHASH); | |||
shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); | |||
// Generate Sp and Ep, and compute BBp = Sp*A + Ep. Generate A on-the-fly | |||
shake_input_seedSEprime[0] = 0x96; | |||
memcpy(&shake_input_seedSEprime[1], seedSEprime, CRYPTO_BYTES); | |||
shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSEprime, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { | |||
Sp[i] = PQCLEAN_FRODOKEM976AES_OPT_LE_TO_UINT16(Sp[i]); | |||
} | |||
PQCLEAN_FRODOKEM976AES_OPT_sample_n(Sp, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM976AES_OPT_sample_n(Ep, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM976AES_OPT_mul_add_sa_plus_e(BBp, Sp, Ep, pk_seedA); | |||
// Generate Epp, and compute W = Sp*B + Epp | |||
PQCLEAN_FRODOKEM976AES_OPT_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM976AES_OPT_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM976AES_OPT_mul_add_sb_plus_e(W, B, Sp, Epp); | |||
// Encode mu, and compute CC = W + enc(mu') (mod q) | |||
PQCLEAN_FRODOKEM976AES_OPT_key_encode(CC, (uint16_t *)muprime); | |||
PQCLEAN_FRODOKEM976AES_OPT_add(CC, W, CC); | |||
// Prepare input to F | |||
memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); | |||
// Reducing BBp modulo q | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
BBp[i] = BBp[i] & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
// Is (Bp == BBp & C == CC) = true | |||
if (memcmp(Bp, BBp, 2 * PARAMS_N * PARAMS_NBAR) == 0 && memcmp(C, CC, 2 * PARAMS_NBAR * PARAMS_NBAR) == 0) { | |||
// Load k' to do ss = F(ct || k') | |||
memcpy(Fin_k, kprime, CRYPTO_BYTES); | |||
} else { | |||
// Load s to do ss = F(ct || s) | |||
memcpy(Fin_k, sk_s, CRYPTO_BYTES); | |||
} | |||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes((uint8_t *)W, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes(muprime, BYTES_MU); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes(G2out, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes(Fin_k, CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM976AES_OPT_clear_bytes(shake_input_seedSEprime, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} |
@@ -0,0 +1,125 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: matrix arithmetic functions used by the KEM | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "aes.h" | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
int PQCLEAN_FRODOKEM976AES_OPT_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { | |||
// Generate-and-multiply: generate matrix A (N x N) row-wise, multiply by s on the right. | |||
// Inputs: s, e (N x N_BAR) | |||
// Output: out = A*s + e (N x N_BAR) | |||
int k; | |||
uint16_t i, j; | |||
int16_t a_row[4 * PARAMS_N]; | |||
for (i = 0; i < (PARAMS_N * PARAMS_NBAR); i += 2) { | |||
*((uint32_t *)&out[i]) = *((uint32_t *)&e[i]); | |||
} | |||
int16_t a_row_temp[4 * PARAMS_N] = {0}; // Take four lines of A at once | |||
aes128ctx ctx128; | |||
aes128_keyexp(&ctx128, seed_A); | |||
for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { | |||
a_row_temp[j + 1 + 0 * PARAMS_N] = PQCLEAN_FRODOKEM976AES_OPT_UINT16_TO_LE(j); // Loading values in the little-endian order | |||
a_row_temp[j + 1 + 1 * PARAMS_N] = PQCLEAN_FRODOKEM976AES_OPT_UINT16_TO_LE(j); | |||
a_row_temp[j + 1 + 2 * PARAMS_N] = PQCLEAN_FRODOKEM976AES_OPT_UINT16_TO_LE(j); | |||
a_row_temp[j + 1 + 3 * PARAMS_N] = PQCLEAN_FRODOKEM976AES_OPT_UINT16_TO_LE(j); | |||
} | |||
for (i = 0; i < PARAMS_N; i += 4) { | |||
for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) { // Go through A, four rows at a time | |||
a_row_temp[j + 0 * PARAMS_N] = PQCLEAN_FRODOKEM976AES_OPT_UINT16_TO_LE(i + 0); // Loading values in the little-endian order | |||
a_row_temp[j + 1 * PARAMS_N] = PQCLEAN_FRODOKEM976AES_OPT_UINT16_TO_LE(i + 1); | |||
a_row_temp[j + 2 * PARAMS_N] = PQCLEAN_FRODOKEM976AES_OPT_UINT16_TO_LE(i + 2); | |||
a_row_temp[j + 3 * PARAMS_N] = PQCLEAN_FRODOKEM976AES_OPT_UINT16_TO_LE(i + 3); | |||
} | |||
aes128_ecb((uint8_t *)a_row, (uint8_t *)a_row_temp, 4 * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); | |||
for (k = 0; k < 4 * PARAMS_N; k++) { | |||
a_row[k] = PQCLEAN_FRODOKEM976AES_OPT_LE_TO_UINT16(a_row[k]); | |||
} | |||
for (k = 0; k < PARAMS_NBAR; k++) { | |||
uint16_t sum[4] = {0}; | |||
for (j = 0; j < PARAMS_N; j++) { // Matrix-vector multiplication | |||
uint16_t sp = s[k * PARAMS_N + j]; | |||
sum[0] += a_row[0 * PARAMS_N + j] * sp; // Go through four lines with same s | |||
sum[1] += a_row[1 * PARAMS_N + j] * sp; | |||
sum[2] += a_row[2 * PARAMS_N + j] * sp; | |||
sum[3] += a_row[3 * PARAMS_N + j] * sp; | |||
} | |||
out[(i + 0)*PARAMS_NBAR + k] += sum[0]; | |||
out[(i + 2)*PARAMS_NBAR + k] += sum[2]; | |||
out[(i + 1)*PARAMS_NBAR + k] += sum[1]; | |||
out[(i + 3)*PARAMS_NBAR + k] += sum[3]; | |||
} | |||
} | |||
return 1; | |||
} | |||
int PQCLEAN_FRODOKEM976AES_OPT_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { | |||
// Generate-and-multiply: generate matrix A (N x N) column-wise, multiply by s' on the left. | |||
// Inputs: s', e' (N_BAR x N) | |||
// Output: out = s'*A + e' (N_BAR x N) | |||
int j; | |||
uint16_t i, kk; | |||
for (i = 0; i < (PARAMS_N * PARAMS_NBAR); i += 2) { | |||
*((uint32_t *)&out[i]) = *((uint32_t *)&e[i]); | |||
} | |||
int k; | |||
uint16_t a_cols[PARAMS_N * PARAMS_STRIPE_STEP] = {0}; | |||
uint16_t a_cols_t[PARAMS_N * PARAMS_STRIPE_STEP]; | |||
uint16_t a_cols_temp[PARAMS_N * PARAMS_STRIPE_STEP] = {0}; | |||
aes128ctx ctx128; | |||
aes128_keyexp(&ctx128, seed_A); | |||
for (i = 0, j = 0; i < PARAMS_N; i++, j += PARAMS_STRIPE_STEP) { | |||
a_cols_temp[j] = PQCLEAN_FRODOKEM976AES_OPT_UINT16_TO_LE(i); // Loading values in the little-endian order | |||
} | |||
for (kk = 0; kk < PARAMS_N; kk += PARAMS_STRIPE_STEP) { // Go through A's columns, 8 (== PARAMS_STRIPE_STEP) columns at a time. | |||
for (i = 0; i < (PARAMS_N * PARAMS_STRIPE_STEP); i += PARAMS_STRIPE_STEP) { | |||
a_cols_temp[i + 1] = PQCLEAN_FRODOKEM976AES_OPT_UINT16_TO_LE(kk); // Loading values in the little-endian order | |||
} | |||
aes128_ecb((uint8_t *)a_cols, (uint8_t *)a_cols_temp, PARAMS_N * PARAMS_STRIPE_STEP * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128); | |||
for (i = 0; i < PARAMS_N; i++) { // Transpose a_cols to have access to it in the column-major order. | |||
for (k = 0; k < PARAMS_STRIPE_STEP; k++) { | |||
a_cols_t[k * PARAMS_N + i] = PQCLEAN_FRODOKEM976AES_OPT_LE_TO_UINT16(a_cols[i * PARAMS_STRIPE_STEP + k]); | |||
} | |||
} | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
for (k = 0; k < PARAMS_STRIPE_STEP; k += PARAMS_PARALLEL) { | |||
uint16_t sum[PARAMS_PARALLEL] = {0}; | |||
for (j = 0; j < PARAMS_N; j++) { // Matrix-vector multiplication | |||
uint16_t sp = s[i * PARAMS_N + j]; | |||
sum[0] += sp * a_cols_t[(k + 0) * PARAMS_N + j]; | |||
sum[1] += sp * a_cols_t[(k + 1) * PARAMS_N + j]; | |||
sum[2] += sp * a_cols_t[(k + 2) * PARAMS_N + j]; | |||
sum[3] += sp * a_cols_t[(k + 3) * PARAMS_N + j]; | |||
} | |||
out[i * PARAMS_N + kk + k + 0] += sum[0]; | |||
out[i * PARAMS_N + kk + k + 2] += sum[2]; | |||
out[i * PARAMS_N + kk + k + 1] += sum[1]; | |||
out[i * PARAMS_N + kk + k + 3] += sum[3]; | |||
} | |||
} | |||
} | |||
return 1; | |||
} |
@@ -0,0 +1,35 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: noise sampling functions | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
static uint16_t CDF_TABLE[CDF_TABLE_LEN] = CDF_TABLE_DATA; | |||
void PQCLEAN_FRODOKEM976AES_OPT_sample_n(uint16_t *s, size_t n) { | |||
// Fills vector s with n samples from the noise distribution which requires 16 bits to sample. | |||
// The distribution is specified by its CDF. | |||
// Input: pseudo-random values (2*n bytes) passed in s. The input is overwritten by the output. | |||
size_t i; | |||
unsigned int j; | |||
for (i = 0; i < n; ++i) { | |||
uint16_t sample = 0; | |||
uint16_t prnd = s[i] >> 1; // Drop the least significant bit | |||
uint16_t sign = s[i] & 0x1; // Pick the least significant bit | |||
// No need to compare with the last value. | |||
for (j = 0; j < (unsigned int)(CDF_TABLE_LEN - 1); j++) { | |||
// Constant time comparison: 1 if CDF_TABLE[j] < s, 0 otherwise. Uses the fact that CDF_TABLE[j] and s fit in 15 bits. | |||
sample += (uint16_t)(CDF_TABLE[j] - prnd) >> 15; | |||
} | |||
// Assuming that sign is either 0 or 1, flips sample iff sign = 1 | |||
s[i] = ((-sign) ^ sample) + sign; | |||
} | |||
} |
@@ -0,0 +1,27 @@ | |||
#ifndef PARAMS_H | |||
#define PARAMS_H | |||
#define CRYPTO_SECRETKEYBYTES PQCLEAN_FRODOKEM976AES_OPT_CRYPTO_SECRETKEYBYTES | |||
#define CRYPTO_PUBLICKEYBYTES PQCLEAN_FRODOKEM976AES_OPT_CRYPTO_PUBLICKEYBYTES | |||
#define CRYPTO_BYTES PQCLEAN_FRODOKEM976AES_OPT_CRYPTO_BYTES | |||
#define CRYPTO_CIPHERTEXTBYTES PQCLEAN_FRODOKEM976AES_OPT_CRYPTO_CIPHERTEXTBYTES | |||
#define PARAMS_N 976 | |||
#define PARAMS_NBAR 8 | |||
#define PARAMS_LOGQ 16 | |||
#define PARAMS_Q (1 << PARAMS_LOGQ) | |||
#define PARAMS_EXTRACTED_BITS 3 | |||
#define PARAMS_STRIPE_STEP 8 | |||
#define PARAMS_PARALLEL 4 | |||
#define BYTES_SEED_A 16 | |||
#define BYTES_MU ((PARAMS_EXTRACTED_BITS * PARAMS_NBAR * PARAMS_NBAR) / 8) | |||
#define BYTES_PKHASH CRYPTO_BYTES | |||
// Selecting SHAKE XOF function for the KEM and noise sampling | |||
#define shake shake256 | |||
// CDF table | |||
#define CDF_TABLE_DATA {5638, 15915, 23689, 28571, 31116, 32217, 32613, 32731, 32760, 32766, 32767} | |||
#define CDF_TABLE_LEN 11 | |||
#endif |
@@ -0,0 +1,235 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: additional functions for FrodoKEM | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
#define min(x, y) (((x) < (y)) ? (x) : (y)) | |||
uint16_t PQCLEAN_FRODOKEM976AES_OPT_LE_TO_UINT16(uint16_t n) { | |||
return (((uint8_t *) &n)[0] | (((uint8_t *) &n)[1] << 8)); | |||
} | |||
uint16_t PQCLEAN_FRODOKEM976AES_OPT_UINT16_TO_LE(uint16_t n) { | |||
uint16_t y; | |||
uint8_t *z = (uint8_t *) &y; | |||
z[0] = n & 0xFF; | |||
z[1] = (n & 0xFF00) >> 8; | |||
return y; | |||
} | |||
void PQCLEAN_FRODOKEM976AES_OPT_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s) { | |||
// Multiply by s on the right | |||
// Inputs: b (N_BAR x N), s (N x N_BAR) | |||
// Output: out = b*s (N_BAR x N_BAR) | |||
int i, j, k; | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
for (j = 0; j < PARAMS_NBAR; j++) { | |||
out[i * PARAMS_NBAR + j] = 0; | |||
for (k = 0; k < PARAMS_N; k++) { | |||
out[i * PARAMS_NBAR + j] += b[i * PARAMS_N + k] * s[j * PARAMS_N + k]; | |||
} | |||
out[i * PARAMS_NBAR + j] = (uint32_t)(out[i * PARAMS_NBAR + j]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM976AES_OPT_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e) { | |||
// Multiply by s on the left | |||
// Inputs: b (N x N_BAR), s (N_BAR x N), e (N_BAR x N_BAR) | |||
// Output: out = s*b + e (N_BAR x N_BAR) | |||
int i, j, k; | |||
for (k = 0; k < PARAMS_NBAR; k++) { | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
out[k * PARAMS_NBAR + i] = e[k * PARAMS_NBAR + i]; | |||
for (j = 0; j < PARAMS_N; j++) { | |||
out[k * PARAMS_NBAR + i] += s[k * PARAMS_N + j] * b[j * PARAMS_NBAR + i]; | |||
} | |||
out[k * PARAMS_NBAR + i] = (uint32_t)(out[k * PARAMS_NBAR + i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM976AES_OPT_add(uint16_t *out, const uint16_t *a, const uint16_t *b) { | |||
// Add a and b | |||
// Inputs: a, b (N_BAR x N_BAR) | |||
// Output: c = a + b | |||
for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { | |||
out[i] = (a[i] + b[i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM976AES_OPT_sub(uint16_t *out, const uint16_t *a, const uint16_t *b) { | |||
// Subtract a and b | |||
// Inputs: a, b (N_BAR x N_BAR) | |||
// Output: c = a - b | |||
for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { | |||
out[i] = (a[i] - b[i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM976AES_OPT_key_encode(uint16_t *out, const uint16_t *in) { | |||
// Encoding | |||
unsigned int i, j, npieces_word = 8; | |||
unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; | |||
uint64_t temp, mask = ((uint64_t)1 << PARAMS_EXTRACTED_BITS) - 1; | |||
uint16_t *pos = out; | |||
for (i = 0; i < nwords; i++) { | |||
temp = 0; | |||
for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { | |||
temp |= ((uint64_t)((uint8_t *)in)[i * PARAMS_EXTRACTED_BITS + j]) << (8 * j); | |||
} | |||
for (j = 0; j < npieces_word; j++) { | |||
*pos = (uint16_t)((temp & mask) << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS)); | |||
temp >>= PARAMS_EXTRACTED_BITS; | |||
pos++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM976AES_OPT_key_decode(uint16_t *out, const uint16_t *in) { | |||
// Decoding | |||
unsigned int i, j, index = 0, npieces_word = 8; | |||
unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; | |||
uint16_t temp, maskex = ((uint16_t)1 << PARAMS_EXTRACTED_BITS) - 1, maskq = ((uint16_t)1 << PARAMS_LOGQ) - 1; | |||
uint8_t *pos = (uint8_t *)out; | |||
uint64_t templong; | |||
for (i = 0; i < nwords; i++) { | |||
templong = 0; | |||
for (j = 0; j < npieces_word; j++) { // temp = floor(in*2^{-11}+0.5) | |||
temp = ((in[index] & maskq) + (1 << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS - 1))) >> (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS); | |||
templong |= ((uint64_t)(temp & maskex)) << (PARAMS_EXTRACTED_BITS * j); | |||
index++; | |||
} | |||
for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { | |||
pos[i * PARAMS_EXTRACTED_BITS + j] = (templong >> (8 * j)) & 0xFF; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM976AES_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb) { | |||
// Pack the input uint16 vector into a char output vector, copying lsb bits from each input element. | |||
// If inlen * lsb / 8 > outlen, only outlen * 8 bits are copied. | |||
memset(out, 0, outlen); | |||
size_t i = 0; // whole bytes already filled in | |||
size_t j = 0; // whole uint16_t already copied | |||
uint16_t w = 0; // the leftover, not yet copied | |||
uint8_t bits = 0; // the number of lsb in w | |||
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { | |||
/* | |||
in: | | |********|********| | |||
^ | |||
j | |||
w : | ****| | |||
^ | |||
bits | |||
out:|**|**|**|**|**|**|**|**|* | | |||
^^ | |||
ib | |||
*/ | |||
uint8_t b = 0; // bits in out[i] already filled in | |||
while (b < 8) { | |||
int nbits = min(8 - b, bits); | |||
uint16_t mask = (1 << nbits) - 1; | |||
uint8_t t = (uint8_t) ((w >> (bits - nbits)) & mask); // the bits to copy from w to out | |||
out[i] = out[i] + (t << (8 - b - nbits)); | |||
b += (uint8_t) nbits; | |||
bits -= (uint8_t) nbits; | |||
w &= ~(mask << bits); // not strictly necessary; mostly for debugging | |||
if (bits == 0) { | |||
if (j < inlen) { | |||
w = in[j]; | |||
bits = lsb; | |||
j++; | |||
} else { | |||
break; // the input vector is exhausted | |||
} | |||
} | |||
} | |||
if (b == 8) { // out[i] is filled in | |||
i++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM976AES_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb) { | |||
// Unpack the input char vector into a uint16_t output vector, copying lsb bits | |||
// for each output element from input. outlen must be at least ceil(inlen * 8 / lsb). | |||
memset(out, 0, outlen * sizeof(uint16_t)); | |||
size_t i = 0; // whole uint16_t already filled in | |||
size_t j = 0; // whole bytes already copied | |||
uint8_t w = 0; // the leftover, not yet copied | |||
uint8_t bits = 0; // the number of lsb bits of w | |||
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { | |||
/* | |||
in: | | | | | | |**|**|... | |||
^ | |||
j | |||
w : | *| | |||
^ | |||
bits | |||
out:| *****| *****| *** | |... | |||
^ ^ | |||
i b | |||
*/ | |||
uint8_t b = 0; // bits in out[i] already filled in | |||
while (b < lsb) { | |||
int nbits = min(lsb - b, bits); | |||
uint16_t mask = (1 << nbits) - 1; | |||
uint8_t t = (w >> (bits - nbits)) & mask; // the bits to copy from w to out | |||
out[i] = out[i] + (t << (lsb - b - nbits)); | |||
b += (uint8_t) nbits; | |||
bits -= (uint8_t) nbits; | |||
w &= ~(mask << bits); // not strictly necessary; mostly for debugging | |||
if (bits == 0) { | |||
if (j < inlen) { | |||
w = in[j]; | |||
bits = 8; | |||
j++; | |||
} else { | |||
break; // the input vector is exhausted | |||
} | |||
} | |||
} | |||
if (b == lsb) { // out[i] is filled in | |||
i++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM976AES_OPT_clear_bytes(uint8_t *mem, size_t n) { | |||
// Clear 8-bit bytes from memory. "n" indicates the number of bytes to be zeroed. | |||
// This function uses the volatile type qualifier to inform the compiler not to optimize out the memory clearing. | |||
volatile uint8_t *v = mem; | |||
for (size_t i = 0; i < n; i++) { | |||
v[i] = 0; | |||
} | |||
} |
@@ -23,3 +23,5 @@ auxiliary-submitters: | |||
implementations: | |||
- name: clean | |||
version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/d5bbd0417ba111b08a959c0042a1dcc65fb14a89 | |||
- name: opt | |||
version: https://github.com/Microsoft/PQCrypto-LWEKE/commit/d5bbd0417ba111b08a959c0042a1dcc65fb14a89 |
@@ -4,7 +4,7 @@ LIB=libfrodokem976shake_clean.a | |||
HEADERS=api.h params.h common.h | |||
OBJECTS=kem.o matrix_shake.o noise.o util.o | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
@@ -0,0 +1,21 @@ | |||
MIT License | |||
Copyright (c) Microsoft Corporation. All rights reserved. | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE |
@@ -0,0 +1,19 @@ | |||
# This Makefile can be used with GNU Make or BSD Make | |||
LIB=libfrodokem976shake_opt.a | |||
HEADERS=api.h params.h common.h | |||
OBJECTS=kem.o matrix_shake.o noise.o util.o | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
%.o: %.c $(HEADERS) | |||
$(CC) $(CFLAGS) -c -o $@ $< | |||
$(LIB): $(OBJECTS) | |||
$(AR) -r $@ $(OBJECTS) | |||
clean: | |||
$(RM) $(OBJECTS) | |||
$(RM) $(LIB) |
@@ -0,0 +1,19 @@ | |||
# This Makefile can be used with Microsoft Visual Studio's nmake using the command: | |||
# nmake /f Makefile.Microsoft_nmake | |||
LIBRARY=libfrodokem976shake_opt.lib | |||
OBJECTS=kem.obj matrix_shake.obj noise.obj util.obj | |||
CFLAGS=/nologo /I ..\..\..\common /W4 /WX | |||
all: $(LIBRARY) | |||
# Make sure objects are recompiled if headers change. | |||
$(OBJECTS): *.h | |||
$(LIBRARY): $(OBJECTS) | |||
LIB.EXE /NOLOGO /WX /OUT:$@ $** | |||
clean: | |||
-DEL $(OBJECTS) | |||
-DEL $(LIBRARY) |
@@ -0,0 +1,20 @@ | |||
#ifndef PQCLEAN_FRODOKEM976SHAKE_OPT_API_H | |||
#define PQCLEAN_FRODOKEM976SHAKE_OPT_API_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#define PQCLEAN_FRODOKEM976SHAKE_OPT_CRYPTO_SECRETKEYBYTES 31296 // sizeof(s) + CRYPTO_PUBLICKEYBYTES + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH | |||
#define PQCLEAN_FRODOKEM976SHAKE_OPT_CRYPTO_PUBLICKEYBYTES 15632 // sizeof(seed_A) + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 | |||
#define PQCLEAN_FRODOKEM976SHAKE_OPT_CRYPTO_BYTES 24 | |||
#define PQCLEAN_FRODOKEM976SHAKE_OPT_CRYPTO_CIPHERTEXTBYTES 15744 // (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + (PARAMS_LOGQ*PARAMS_NBAR*PARAMS_NBAR)/8 | |||
#define PQCLEAN_FRODOKEM976SHAKE_OPT_CRYPTO_ALGNAME "FrodoKEM-976-SHAKE" | |||
int PQCLEAN_FRODOKEM976SHAKE_OPT_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); | |||
int PQCLEAN_FRODOKEM976SHAKE_OPT_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); | |||
int PQCLEAN_FRODOKEM976SHAKE_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); | |||
#endif |
@@ -0,0 +1,19 @@ | |||
#ifndef COMMON_H | |||
#define COMMON_H | |||
int PQCLEAN_FRODOKEM976SHAKE_OPT_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); | |||
int PQCLEAN_FRODOKEM976SHAKE_OPT_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A); | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_sample_n(uint16_t *s, size_t n); | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s); | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e); | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_add(uint16_t *out, const uint16_t *a, const uint16_t *b); | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_sub(uint16_t *out, const uint16_t *a, const uint16_t *b); | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_key_encode(uint16_t *out, const uint16_t *in); | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_key_decode(uint16_t *out, const uint16_t *in); | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb); | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb); | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes(uint8_t *mem, size_t n); | |||
uint16_t PQCLEAN_FRODOKEM976SHAKE_OPT_LE_TO_UINT16(uint16_t n); | |||
uint16_t PQCLEAN_FRODOKEM976SHAKE_OPT_UINT16_TO_LE(uint16_t n); | |||
#endif |
@@ -0,0 +1,238 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: Key Encapsulation Mechanism (KEM) based on Frodo | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "fips202.h" | |||
#include "randombytes.h" | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
int PQCLEAN_FRODOKEM976SHAKE_OPT_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { | |||
// FrodoKEM's key generation | |||
// Outputs: public key pk ( BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 bytes) | |||
// secret key sk (CRYPTO_BYTES + BYTES_SEED_A + (PARAMS_LOGQ*PARAMS_N*PARAMS_NBAR)/8 + 2*PARAMS_N*PARAMS_NBAR + BYTES_PKHASH bytes) | |||
uint8_t *pk_seedA = &pk[0]; | |||
uint8_t *pk_b = &pk[BYTES_SEED_A]; | |||
uint8_t *sk_s = &sk[0]; | |||
uint8_t *sk_pk = &sk[CRYPTO_BYTES]; | |||
uint8_t *sk_S = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; | |||
uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t S[2 * PARAMS_N * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *E = &S[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint8_t randomness[2 * CRYPTO_BYTES + BYTES_SEED_A]; // contains secret data via randomness_s and randomness_seedSE | |||
uint8_t *randomness_s = &randomness[0]; // contains secret data | |||
uint8_t *randomness_seedSE = &randomness[CRYPTO_BYTES]; // contains secret data | |||
uint8_t *randomness_z = &randomness[2 * CRYPTO_BYTES]; | |||
uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data | |||
// Generate the secret value s, the seed for S and E, and the seed for the seed for A. Add seed_A to the public key | |||
randombytes(randomness, CRYPTO_BYTES + CRYPTO_BYTES + BYTES_SEED_A); | |||
shake(pk_seedA, BYTES_SEED_A, randomness_z, BYTES_SEED_A); | |||
// Generate S and E, and compute B = A*S + E. Generate A on-the-fly | |||
shake_input_seedSE[0] = 0x5F; | |||
memcpy(&shake_input_seedSE[1], randomness_seedSE, CRYPTO_BYTES); | |||
shake((uint8_t *)S, 2 * PARAMS_N * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < 2 * PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM976SHAKE_OPT_LE_TO_UINT16(S[i]); | |||
} | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_sample_n(S, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_sample_n(E, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_mul_add_as_plus_e(B, S, E, pk); | |||
// Encode the second part of the public key | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_pack(pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, B, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Add s, pk and S to the secret key | |||
memcpy(sk_s, randomness_s, CRYPTO_BYTES); | |||
memcpy(sk_pk, pk, CRYPTO_PUBLICKEYBYTES); | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM976SHAKE_OPT_UINT16_TO_LE(S[i]); | |||
} | |||
memcpy(sk_S, S, 2 * PARAMS_N * PARAMS_NBAR); | |||
// Add H(pk) to the secret key | |||
shake(sk_pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes((uint8_t *)E, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes(randomness, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} | |||
int PQCLEAN_FRODOKEM976SHAKE_OPT_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { | |||
// FrodoKEM's key encapsulation | |||
const uint8_t *pk_seedA = &pk[0]; | |||
const uint8_t *pk_b = &pk[BYTES_SEED_A]; | |||
uint8_t *ct_c1 = &ct[0]; | |||
uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t V[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via mu | |||
uint8_t *pkh = &G2in[0]; | |||
uint8_t *mu = &G2in[BYTES_PKHASH]; // contains secret data | |||
uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data | |||
uint8_t *seedSE = &G2out[0]; // contains secret data | |||
uint8_t *k = &G2out[CRYPTO_BYTES]; // contains secret data | |||
uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k | |||
uint8_t *Fin_ct = &Fin[0]; | |||
uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data | |||
uint8_t shake_input_seedSE[1 + CRYPTO_BYTES]; // contains secret data | |||
// pkh <- G_1(pk), generate random mu, compute (seedSE || k) = G_2(pkh || mu) | |||
shake(pkh, BYTES_PKHASH, pk, CRYPTO_PUBLICKEYBYTES); | |||
randombytes(mu, BYTES_MU); | |||
shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); | |||
// Generate Sp and Ep, and compute Bp = Sp*A + Ep. Generate A on-the-fly | |||
shake_input_seedSE[0] = 0x96; | |||
memcpy(&shake_input_seedSE[1], seedSE, CRYPTO_BYTES); | |||
shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { | |||
Sp[i] = PQCLEAN_FRODOKEM976SHAKE_OPT_LE_TO_UINT16(Sp[i]); | |||
} | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_sample_n(Sp, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_sample_n(Ep, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_mul_add_sa_plus_e(Bp, Sp, Ep, pk_seedA); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_pack(ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, Bp, PARAMS_N * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Generate Epp, and compute V = Sp*B + Epp | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_mul_add_sb_plus_e(V, B, Sp, Epp); | |||
// Encode mu, and compute C = V + enc(mu) (mod q) | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_key_encode(C, (uint16_t *)mu); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_add(C, V, C); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_pack(ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, C, PARAMS_NBAR * PARAMS_NBAR, PARAMS_LOGQ); | |||
// Compute ss = F(ct||KK) | |||
memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); | |||
memcpy(Fin_k, k, CRYPTO_BYTES); | |||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes((uint8_t *)V, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes(mu, BYTES_MU); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes(G2out, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes(Fin_k, CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes(shake_input_seedSE, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} | |||
int PQCLEAN_FRODOKEM976SHAKE_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { | |||
// FrodoKEM's key decapsulation | |||
uint16_t B[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Bp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t W[PARAMS_NBAR * PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t C[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t CC[PARAMS_NBAR * PARAMS_NBAR] = {0}; | |||
uint16_t BBp[PARAMS_N * PARAMS_NBAR] = {0}; | |||
uint16_t Sp[(2 * PARAMS_N + PARAMS_NBAR)*PARAMS_NBAR] = {0}; // contains secret data | |||
uint16_t *Ep = &Sp[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
uint16_t *Epp = &Sp[2 * PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
const uint8_t *ct_c1 = &ct[0]; | |||
const uint8_t *ct_c2 = &ct[(PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8]; | |||
const uint8_t *sk_s = &sk[0]; | |||
const uint8_t *sk_pk = &sk[CRYPTO_BYTES]; | |||
const uint16_t *sk_S = (uint16_t *) &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES]; | |||
uint16_t S[PARAMS_N * PARAMS_NBAR]; // contains secret data | |||
const uint8_t *sk_pkh = &sk[CRYPTO_BYTES + CRYPTO_PUBLICKEYBYTES + 2 * PARAMS_N * PARAMS_NBAR]; | |||
const uint8_t *pk_seedA = &sk_pk[0]; | |||
const uint8_t *pk_b = &sk_pk[BYTES_SEED_A]; | |||
uint8_t G2in[BYTES_PKHASH + BYTES_MU]; // contains secret data via muprime | |||
uint8_t *pkh = &G2in[0]; | |||
uint8_t *muprime = &G2in[BYTES_PKHASH]; // contains secret data | |||
uint8_t G2out[2 * CRYPTO_BYTES]; // contains secret data | |||
uint8_t *seedSEprime = &G2out[0]; // contains secret data | |||
uint8_t *kprime = &G2out[CRYPTO_BYTES]; // contains secret data | |||
uint8_t Fin[CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES]; // contains secret data via Fin_k | |||
uint8_t *Fin_ct = &Fin[0]; | |||
uint8_t *Fin_k = &Fin[CRYPTO_CIPHERTEXTBYTES]; // contains secret data | |||
uint8_t shake_input_seedSEprime[1 + CRYPTO_BYTES]; // contains secret data | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
S[i] = PQCLEAN_FRODOKEM976SHAKE_OPT_LE_TO_UINT16(sk_S[i]); | |||
} | |||
// Compute W = C - Bp*S (mod q), and decode the randomness mu | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_unpack(Bp, PARAMS_N * PARAMS_NBAR, ct_c1, (PARAMS_LOGQ * PARAMS_N * PARAMS_NBAR) / 8, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_unpack(C, PARAMS_NBAR * PARAMS_NBAR, ct_c2, (PARAMS_LOGQ * PARAMS_NBAR * PARAMS_NBAR) / 8, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_mul_bs(W, Bp, S); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_sub(W, C, W); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_key_decode((uint16_t *)muprime, W); | |||
// Generate (seedSE' || k') = G_2(pkh || mu') | |||
memcpy(pkh, sk_pkh, BYTES_PKHASH); | |||
shake(G2out, CRYPTO_BYTES + CRYPTO_BYTES, G2in, BYTES_PKHASH + BYTES_MU); | |||
// Generate Sp and Ep, and compute BBp = Sp*A + Ep. Generate A on-the-fly | |||
shake_input_seedSEprime[0] = 0x96; | |||
memcpy(&shake_input_seedSEprime[1], seedSEprime, CRYPTO_BYTES); | |||
shake((uint8_t *)Sp, (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR * sizeof(uint16_t), shake_input_seedSEprime, 1 + CRYPTO_BYTES); | |||
for (size_t i = 0; i < (2 * PARAMS_N + PARAMS_NBAR) * PARAMS_NBAR; i++) { | |||
Sp[i] = PQCLEAN_FRODOKEM976SHAKE_OPT_LE_TO_UINT16(Sp[i]); | |||
} | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_sample_n(Sp, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_sample_n(Ep, PARAMS_N * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_mul_add_sa_plus_e(BBp, Sp, Ep, pk_seedA); | |||
// Generate Epp, and compute W = Sp*B + Epp | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_sample_n(Epp, PARAMS_NBAR * PARAMS_NBAR); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_unpack(B, PARAMS_N * PARAMS_NBAR, pk_b, CRYPTO_PUBLICKEYBYTES - BYTES_SEED_A, PARAMS_LOGQ); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_mul_add_sb_plus_e(W, B, Sp, Epp); | |||
// Encode mu, and compute CC = W + enc(mu') (mod q) | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_key_encode(CC, (uint16_t *)muprime); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_add(CC, W, CC); | |||
// Prepare input to F | |||
memcpy(Fin_ct, ct, CRYPTO_CIPHERTEXTBYTES); | |||
// Reducing BBp modulo q | |||
for (size_t i = 0; i < PARAMS_N * PARAMS_NBAR; i++) { | |||
BBp[i] = BBp[i] & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
// Is (Bp == BBp & C == CC) = true | |||
if (memcmp(Bp, BBp, 2 * PARAMS_N * PARAMS_NBAR) == 0 && memcmp(C, CC, 2 * PARAMS_NBAR * PARAMS_NBAR) == 0) { | |||
// Load k' to do ss = F(ct || k') | |||
memcpy(Fin_k, kprime, CRYPTO_BYTES); | |||
} else { | |||
// Load s to do ss = F(ct || s) | |||
memcpy(Fin_k, sk_s, CRYPTO_BYTES); | |||
} | |||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES); | |||
// Cleanup: | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes((uint8_t *)W, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes((uint8_t *)Sp, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes((uint8_t *)S, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes((uint8_t *)Ep, PARAMS_N * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes((uint8_t *)Epp, PARAMS_NBAR * PARAMS_NBAR * sizeof(uint16_t)); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes(muprime, BYTES_MU); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes(G2out, 2 * CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes(Fin_k, CRYPTO_BYTES); | |||
PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes(shake_input_seedSEprime, 1 + CRYPTO_BYTES); | |||
return 0; | |||
} |
@@ -0,0 +1,108 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: matrix arithmetic functions used by the KEM | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "fips202.h" | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
int PQCLEAN_FRODOKEM976SHAKE_OPT_mul_add_as_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { | |||
// Generate-and-multiply: generate matrix A (N x N) row-wise, multiply by s on the right. | |||
// Inputs: s, e (N x N_BAR) | |||
// Output: out = A*s + e (N x N_BAR) | |||
int j, k; | |||
uint16_t i; | |||
int16_t a_row[4 * PARAMS_N]; | |||
for (i = 0; i < (PARAMS_N * PARAMS_NBAR); i += 2) { | |||
*((uint32_t *)&out[i]) = *((uint32_t *)&e[i]); | |||
} | |||
uint8_t seed_A_separated[2 + BYTES_SEED_A]; | |||
uint16_t *seed_A_origin = (uint16_t *)&seed_A_separated; | |||
memcpy(&seed_A_separated[2], seed_A, BYTES_SEED_A); | |||
for (i = 0; i < PARAMS_N; i += 4) { | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM976SHAKE_OPT_UINT16_TO_LE(i + 0); | |||
shake128((unsigned char *)(a_row + 0 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM976SHAKE_OPT_UINT16_TO_LE(i + 1); | |||
shake128((unsigned char *)(a_row + 1 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM976SHAKE_OPT_UINT16_TO_LE(i + 2); | |||
shake128((unsigned char *)(a_row + 2 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM976SHAKE_OPT_UINT16_TO_LE(i + 3); | |||
shake128((unsigned char *)(a_row + 3 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
for (k = 0; k < 4 * PARAMS_N; k++) { | |||
a_row[k] = PQCLEAN_FRODOKEM976SHAKE_OPT_LE_TO_UINT16(a_row[k]); | |||
} | |||
for (k = 0; k < PARAMS_NBAR; k++) { | |||
uint16_t sum[4] = {0}; | |||
for (j = 0; j < PARAMS_N; j++) { // Matrix-vector multiplication | |||
uint16_t sp = s[k * PARAMS_N + j]; | |||
sum[0] += a_row[0 * PARAMS_N + j] * sp; // Go through four lines with same s | |||
sum[1] += a_row[1 * PARAMS_N + j] * sp; | |||
sum[2] += a_row[2 * PARAMS_N + j] * sp; | |||
sum[3] += a_row[3 * PARAMS_N + j] * sp; | |||
} | |||
out[(i + 0)*PARAMS_NBAR + k] += sum[0]; | |||
out[(i + 2)*PARAMS_NBAR + k] += sum[2]; | |||
out[(i + 1)*PARAMS_NBAR + k] += sum[1]; | |||
out[(i + 3)*PARAMS_NBAR + k] += sum[3]; | |||
} | |||
} | |||
return 1; | |||
} | |||
int PQCLEAN_FRODOKEM976SHAKE_OPT_mul_add_sa_plus_e(uint16_t *out, const uint16_t *s, const uint16_t *e, const uint8_t *seed_A) { | |||
// Generate-and-multiply: generate matrix A (N x N) column-wise, multiply by s' on the left. | |||
// Inputs: s', e' (N_BAR x N) | |||
// Output: out = s'*A + e' (N_BAR x N) | |||
int i, j; | |||
uint16_t kk; | |||
for (i = 0; i < (PARAMS_N * PARAMS_NBAR); i += 2) { | |||
*((uint32_t *)&out[i]) = *((uint32_t *)&e[i]); | |||
} | |||
int t = 0; | |||
uint16_t a_cols[4 * PARAMS_N]; | |||
int k; | |||
uint8_t seed_A_separated[2 + BYTES_SEED_A]; | |||
uint16_t *seed_A_origin = (uint16_t *)&seed_A_separated; | |||
memcpy(&seed_A_separated[2], seed_A, BYTES_SEED_A); | |||
for (kk = 0; kk < PARAMS_N; kk += 4) { | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM976SHAKE_OPT_UINT16_TO_LE(kk + 0); | |||
shake128((unsigned char *)(a_cols + 0 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM976SHAKE_OPT_UINT16_TO_LE(kk + 1); | |||
shake128((unsigned char *)(a_cols + 1 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM976SHAKE_OPT_UINT16_TO_LE(kk + 2); | |||
shake128((unsigned char *)(a_cols + 2 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
seed_A_origin[0] = PQCLEAN_FRODOKEM976SHAKE_OPT_UINT16_TO_LE(kk + 3); | |||
shake128((unsigned char *)(a_cols + 3 * PARAMS_N), (unsigned long long)(2 * PARAMS_N), seed_A_separated, 2 + BYTES_SEED_A); | |||
for (i = 0; i < 4 * PARAMS_N; i++) { | |||
a_cols[i] = PQCLEAN_FRODOKEM976SHAKE_OPT_LE_TO_UINT16(a_cols[i]); | |||
} | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
uint16_t sum[PARAMS_N] = {0}; | |||
for (j = 0; j < 4; j++) { | |||
uint16_t sp = s[i * PARAMS_N + kk + j]; | |||
for (k = 0; k < PARAMS_N; k++) { // Matrix-vector multiplication | |||
sum[k] += sp * a_cols[(t + j) * PARAMS_N + k]; | |||
} | |||
} | |||
for (k = 0; k < PARAMS_N; k++) { | |||
out[i * PARAMS_N + k] += sum[k]; | |||
} | |||
} | |||
} | |||
return 1; | |||
} |
@@ -0,0 +1,35 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: noise sampling functions | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
static uint16_t CDF_TABLE[CDF_TABLE_LEN] = CDF_TABLE_DATA; | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_sample_n(uint16_t *s, size_t n) { | |||
// Fills vector s with n samples from the noise distribution which requires 16 bits to sample. | |||
// The distribution is specified by its CDF. | |||
// Input: pseudo-random values (2*n bytes) passed in s. The input is overwritten by the output. | |||
size_t i; | |||
unsigned int j; | |||
for (i = 0; i < n; ++i) { | |||
uint16_t sample = 0; | |||
uint16_t prnd = s[i] >> 1; // Drop the least significant bit | |||
uint16_t sign = s[i] & 0x1; // Pick the least significant bit | |||
// No need to compare with the last value. | |||
for (j = 0; j < (unsigned int)(CDF_TABLE_LEN - 1); j++) { | |||
// Constant time comparison: 1 if CDF_TABLE[j] < s, 0 otherwise. Uses the fact that CDF_TABLE[j] and s fit in 15 bits. | |||
sample += (uint16_t)(CDF_TABLE[j] - prnd) >> 15; | |||
} | |||
// Assuming that sign is either 0 or 1, flips sample iff sign = 1 | |||
s[i] = ((-sign) ^ sample) + sign; | |||
} | |||
} |
@@ -0,0 +1,27 @@ | |||
#ifndef PARAMS_H | |||
#define PARAMS_H | |||
#define CRYPTO_SECRETKEYBYTES PQCLEAN_FRODOKEM976SHAKE_OPT_CRYPTO_SECRETKEYBYTES | |||
#define CRYPTO_PUBLICKEYBYTES PQCLEAN_FRODOKEM976SHAKE_OPT_CRYPTO_PUBLICKEYBYTES | |||
#define CRYPTO_BYTES PQCLEAN_FRODOKEM976SHAKE_OPT_CRYPTO_BYTES | |||
#define CRYPTO_CIPHERTEXTBYTES PQCLEAN_FRODOKEM976SHAKE_OPT_CRYPTO_CIPHERTEXTBYTES | |||
#define PARAMS_N 976 | |||
#define PARAMS_NBAR 8 | |||
#define PARAMS_LOGQ 16 | |||
#define PARAMS_Q (1 << PARAMS_LOGQ) | |||
#define PARAMS_EXTRACTED_BITS 3 | |||
#define PARAMS_STRIPE_STEP 8 | |||
#define PARAMS_PARALLEL 4 | |||
#define BYTES_SEED_A 16 | |||
#define BYTES_MU ((PARAMS_EXTRACTED_BITS * PARAMS_NBAR * PARAMS_NBAR) / 8) | |||
#define BYTES_PKHASH CRYPTO_BYTES | |||
// Selecting SHAKE XOF function for the KEM and noise sampling | |||
#define shake shake256 | |||
// CDF table | |||
#define CDF_TABLE_DATA {5638, 15915, 23689, 28571, 31116, 32217, 32613, 32731, 32760, 32766, 32767} | |||
#define CDF_TABLE_LEN 11 | |||
#endif |
@@ -0,0 +1,235 @@ | |||
/******************************************************************************************** | |||
* FrodoKEM: Learning with Errors Key Encapsulation | |||
* | |||
* Abstract: additional functions for FrodoKEM | |||
*********************************************************************************************/ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include "api.h" | |||
#include "common.h" | |||
#include "params.h" | |||
#define min(x, y) (((x) < (y)) ? (x) : (y)) | |||
uint16_t PQCLEAN_FRODOKEM976SHAKE_OPT_LE_TO_UINT16(uint16_t n) { | |||
return (((uint8_t *) &n)[0] | (((uint8_t *) &n)[1] << 8)); | |||
} | |||
uint16_t PQCLEAN_FRODOKEM976SHAKE_OPT_UINT16_TO_LE(uint16_t n) { | |||
uint16_t y; | |||
uint8_t *z = (uint8_t *) &y; | |||
z[0] = n & 0xFF; | |||
z[1] = (n & 0xFF00) >> 8; | |||
return y; | |||
} | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_mul_bs(uint16_t *out, const uint16_t *b, const uint16_t *s) { | |||
// Multiply by s on the right | |||
// Inputs: b (N_BAR x N), s (N x N_BAR) | |||
// Output: out = b*s (N_BAR x N_BAR) | |||
int i, j, k; | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
for (j = 0; j < PARAMS_NBAR; j++) { | |||
out[i * PARAMS_NBAR + j] = 0; | |||
for (k = 0; k < PARAMS_N; k++) { | |||
out[i * PARAMS_NBAR + j] += b[i * PARAMS_N + k] * s[j * PARAMS_N + k]; | |||
} | |||
out[i * PARAMS_NBAR + j] = (uint32_t)(out[i * PARAMS_NBAR + j]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_mul_add_sb_plus_e(uint16_t *out, const uint16_t *b, const uint16_t *s, const uint16_t *e) { | |||
// Multiply by s on the left | |||
// Inputs: b (N x N_BAR), s (N_BAR x N), e (N_BAR x N_BAR) | |||
// Output: out = s*b + e (N_BAR x N_BAR) | |||
int i, j, k; | |||
for (k = 0; k < PARAMS_NBAR; k++) { | |||
for (i = 0; i < PARAMS_NBAR; i++) { | |||
out[k * PARAMS_NBAR + i] = e[k * PARAMS_NBAR + i]; | |||
for (j = 0; j < PARAMS_N; j++) { | |||
out[k * PARAMS_NBAR + i] += s[k * PARAMS_N + j] * b[j * PARAMS_NBAR + i]; | |||
} | |||
out[k * PARAMS_NBAR + i] = (uint32_t)(out[k * PARAMS_NBAR + i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_add(uint16_t *out, const uint16_t *a, const uint16_t *b) { | |||
// Add a and b | |||
// Inputs: a, b (N_BAR x N_BAR) | |||
// Output: c = a + b | |||
for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { | |||
out[i] = (a[i] + b[i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_sub(uint16_t *out, const uint16_t *a, const uint16_t *b) { | |||
// Subtract a and b | |||
// Inputs: a, b (N_BAR x N_BAR) | |||
// Output: c = a - b | |||
for (size_t i = 0; i < (PARAMS_NBAR * PARAMS_NBAR); i++) { | |||
out[i] = (a[i] - b[i]) & ((1 << PARAMS_LOGQ) - 1); | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_key_encode(uint16_t *out, const uint16_t *in) { | |||
// Encoding | |||
unsigned int i, j, npieces_word = 8; | |||
unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; | |||
uint64_t temp, mask = ((uint64_t)1 << PARAMS_EXTRACTED_BITS) - 1; | |||
uint16_t *pos = out; | |||
for (i = 0; i < nwords; i++) { | |||
temp = 0; | |||
for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { | |||
temp |= ((uint64_t)((uint8_t *)in)[i * PARAMS_EXTRACTED_BITS + j]) << (8 * j); | |||
} | |||
for (j = 0; j < npieces_word; j++) { | |||
*pos = (uint16_t)((temp & mask) << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS)); | |||
temp >>= PARAMS_EXTRACTED_BITS; | |||
pos++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_key_decode(uint16_t *out, const uint16_t *in) { | |||
// Decoding | |||
unsigned int i, j, index = 0, npieces_word = 8; | |||
unsigned int nwords = (PARAMS_NBAR * PARAMS_NBAR) / 8; | |||
uint16_t temp, maskex = ((uint16_t)1 << PARAMS_EXTRACTED_BITS) - 1, maskq = ((uint16_t)1 << PARAMS_LOGQ) - 1; | |||
uint8_t *pos = (uint8_t *)out; | |||
uint64_t templong; | |||
for (i = 0; i < nwords; i++) { | |||
templong = 0; | |||
for (j = 0; j < npieces_word; j++) { // temp = floor(in*2^{-11}+0.5) | |||
temp = ((in[index] & maskq) + (1 << (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS - 1))) >> (PARAMS_LOGQ - PARAMS_EXTRACTED_BITS); | |||
templong |= ((uint64_t)(temp & maskex)) << (PARAMS_EXTRACTED_BITS * j); | |||
index++; | |||
} | |||
for (j = 0; j < PARAMS_EXTRACTED_BITS; j++) { | |||
pos[i * PARAMS_EXTRACTED_BITS + j] = (templong >> (8 * j)) & 0xFF; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb) { | |||
// Pack the input uint16 vector into a char output vector, copying lsb bits from each input element. | |||
// If inlen * lsb / 8 > outlen, only outlen * 8 bits are copied. | |||
memset(out, 0, outlen); | |||
size_t i = 0; // whole bytes already filled in | |||
size_t j = 0; // whole uint16_t already copied | |||
uint16_t w = 0; // the leftover, not yet copied | |||
uint8_t bits = 0; // the number of lsb in w | |||
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { | |||
/* | |||
in: | | |********|********| | |||
^ | |||
j | |||
w : | ****| | |||
^ | |||
bits | |||
out:|**|**|**|**|**|**|**|**|* | | |||
^^ | |||
ib | |||
*/ | |||
uint8_t b = 0; // bits in out[i] already filled in | |||
while (b < 8) { | |||
int nbits = min(8 - b, bits); | |||
uint16_t mask = (1 << nbits) - 1; | |||
uint8_t t = (uint8_t) ((w >> (bits - nbits)) & mask); // the bits to copy from w to out | |||
out[i] = out[i] + (t << (8 - b - nbits)); | |||
b += (uint8_t) nbits; | |||
bits -= (uint8_t) nbits; | |||
w &= ~(mask << bits); // not strictly necessary; mostly for debugging | |||
if (bits == 0) { | |||
if (j < inlen) { | |||
w = in[j]; | |||
bits = lsb; | |||
j++; | |||
} else { | |||
break; // the input vector is exhausted | |||
} | |||
} | |||
} | |||
if (b == 8) { // out[i] is filled in | |||
i++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb) { | |||
// Unpack the input char vector into a uint16_t output vector, copying lsb bits | |||
// for each output element from input. outlen must be at least ceil(inlen * 8 / lsb). | |||
memset(out, 0, outlen * sizeof(uint16_t)); | |||
size_t i = 0; // whole uint16_t already filled in | |||
size_t j = 0; // whole bytes already copied | |||
uint8_t w = 0; // the leftover, not yet copied | |||
uint8_t bits = 0; // the number of lsb bits of w | |||
while (i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) { | |||
/* | |||
in: | | | | | | |**|**|... | |||
^ | |||
j | |||
w : | *| | |||
^ | |||
bits | |||
out:| *****| *****| *** | |... | |||
^ ^ | |||
i b | |||
*/ | |||
uint8_t b = 0; // bits in out[i] already filled in | |||
while (b < lsb) { | |||
int nbits = min(lsb - b, bits); | |||
uint16_t mask = (1 << nbits) - 1; | |||
uint8_t t = (w >> (bits - nbits)) & mask; // the bits to copy from w to out | |||
out[i] = out[i] + (t << (lsb - b - nbits)); | |||
b += (uint8_t) nbits; | |||
bits -= (uint8_t) nbits; | |||
w &= ~(mask << bits); // not strictly necessary; mostly for debugging | |||
if (bits == 0) { | |||
if (j < inlen) { | |||
w = in[j]; | |||
bits = 8; | |||
j++; | |||
} else { | |||
break; // the input vector is exhausted | |||
} | |||
} | |||
} | |||
if (b == lsb) { // out[i] is filled in | |||
i++; | |||
} | |||
} | |||
} | |||
void PQCLEAN_FRODOKEM976SHAKE_OPT_clear_bytes(uint8_t *mem, size_t n) { | |||
// Clear 8-bit bytes from memory. "n" indicates the number of bytes to be zeroed. | |||
// This function uses the volatile type qualifier to inform the compiler not to optimize out the memory clearing. | |||
volatile uint8_t *v = mem; | |||
for (size_t i = 0; i < n; i++) { | |||
v[i] = 0; | |||
} | |||
} |
@@ -4,7 +4,7 @@ LIB=libkyber1024_clean.a | |||
HEADERS=api.h cbd.h indcpa.h ntt.h params.h poly.h polyvec.h reduce.h verify.h symmetric.h | |||
OBJECTS=cbd.o indcpa.o kem.o ntt.o poly.o polyvec.o reduce.o verify.o symmetric-fips202.o | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Werror -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
@@ -13,7 +13,7 @@ | |||
* - unsigned char i additional byte of input | |||
* - unsigned char j additional byte of input | |||
**************************************************/ | |||
void PQCLEAN_KYBER1024_CLEAN_kyber_shake128_absorb(keccak_state *s, const unsigned char *input, unsigned char x, unsigned char y) { | |||
void PQCLEAN_KYBER1024_CLEAN_kyber_shake128_absorb(shake128ctx *s, const unsigned char *input, unsigned char x, unsigned char y) { | |||
unsigned char extseed[KYBER_SYMBYTES + 2]; | |||
int i; | |||
@@ -22,7 +22,7 @@ void PQCLEAN_KYBER1024_CLEAN_kyber_shake128_absorb(keccak_state *s, const unsign | |||
} | |||
extseed[i++] = x; | |||
extseed[i] = y; | |||
shake128_absorb(s->s, extseed, KYBER_SYMBYTES + 2); | |||
shake128_absorb(s, extseed, KYBER_SYMBYTES + 2); | |||
} | |||
/************************************************* | |||
@@ -34,10 +34,10 @@ void PQCLEAN_KYBER1024_CLEAN_kyber_shake128_absorb(keccak_state *s, const unsign | |||
* | |||
* Arguments: - unsigned char *output: pointer to output blocks | |||
* - size_t nblocks: number of blocks to be squeezed (written to output) | |||
* - keccak_state *s: pointer to in/output Keccak state | |||
* - shake128ctx *s: pointer to in/output Keccak state | |||
**************************************************/ | |||
void PQCLEAN_KYBER1024_CLEAN_kyber_shake128_squeezeblocks(unsigned char *output, size_t nblocks, keccak_state *s) { | |||
shake128_squeezeblocks(output, nblocks, s->s); | |||
void PQCLEAN_KYBER1024_CLEAN_kyber_shake128_squeezeblocks(unsigned char *output, size_t nblocks, shake128ctx *s) { | |||
shake128_squeezeblocks(output, nblocks, s); | |||
} | |||
/************************************************* | |||
@@ -6,12 +6,8 @@ | |||
#include <stdlib.h> | |||
typedef struct { | |||
uint64_t s[25]; | |||
} keccak_state; | |||
void PQCLEAN_KYBER1024_CLEAN_kyber_shake128_absorb(keccak_state *s, const unsigned char *input, unsigned char x, unsigned char y); | |||
void PQCLEAN_KYBER1024_CLEAN_kyber_shake128_squeezeblocks(unsigned char *output, size_t nblocks, keccak_state *s); | |||
void PQCLEAN_KYBER1024_CLEAN_kyber_shake128_absorb(shake128ctx *s, const unsigned char *input, unsigned char x, unsigned char y); | |||
void PQCLEAN_KYBER1024_CLEAN_kyber_shake128_squeezeblocks(unsigned char *output, size_t nblocks, shake128ctx *s); | |||
void PQCLEAN_KYBER1024_CLEAN_shake256_prf(unsigned char *output, size_t outlen, const unsigned char *key, unsigned char nonce); | |||
#define hash_h(OUT, IN, INBYTES) sha3_256(OUT, IN, INBYTES) | |||
@@ -23,6 +19,6 @@ void PQCLEAN_KYBER1024_CLEAN_shake256_prf(unsigned char *output, size_t outlen, | |||
#define XOF_BLOCKBYTES 168 | |||
typedef keccak_state xof_state; | |||
typedef shake128ctx xof_state; | |||
#endif /* SYMMETRIC_H */ |
@@ -4,7 +4,7 @@ LIB=libkyber512_clean.a | |||
HEADERS=api.h cbd.h indcpa.h ntt.h params.h poly.h polyvec.h reduce.h verify.h symmetric.h | |||
OBJECTS=cbd.o indcpa.o kem.o ntt.o poly.o polyvec.o reduce.o verify.o symmetric-fips202.o | |||
CFLAGS=-Wall -O3 -Wmissing-prototypes -Wextra -Wpedantic -Werror -std=c99 -I../../../common $(EXTRAFLAGS) | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Werror -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
@@ -8,12 +8,12 @@ | |||
* | |||
* Description: Absorb step of the SHAKE128 specialized for the Kyber context. | |||
* | |||
* Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state | |||
* Arguments: - shake128ctx *s: pointer to (uninitialized) output Keccak state | |||
* - const unsigned char *input: pointer to KYBER_SYMBYTES input to be absorbed into s | |||
* - unsigned char i additional byte of input | |||
* - unsigned char j additional byte of input | |||
**************************************************/ | |||
void PQCLEAN_KYBER512_CLEAN_kyber_shake128_absorb(keccak_state *s, const unsigned char *input, unsigned char x, unsigned char y) { | |||
void PQCLEAN_KYBER512_CLEAN_kyber_shake128_absorb(shake128ctx *s, const unsigned char *input, unsigned char x, unsigned char y) { | |||
unsigned char extseed[KYBER_SYMBYTES + 2]; | |||
int i; | |||
@@ -22,7 +22,7 @@ void PQCLEAN_KYBER512_CLEAN_kyber_shake128_absorb(keccak_state *s, const unsigne | |||
} | |||
extseed[i++] = x; | |||
extseed[i] = y; | |||
shake128_absorb(s->s, extseed, KYBER_SYMBYTES + 2); | |||
shake128_absorb(s, extseed, KYBER_SYMBYTES + 2); | |||
} | |||
/************************************************* | |||
@@ -34,10 +34,10 @@ void PQCLEAN_KYBER512_CLEAN_kyber_shake128_absorb(keccak_state *s, const unsigne | |||
* | |||
* Arguments: - unsigned char *output: pointer to output blocks | |||
* - size_t nblocks: number of blocks to be squeezed (written to output) | |||
* - keccak_state *s: pointer to in/output Keccak state | |||
* - shake128ctx *s: pointer to in/output Keccak state | |||
**************************************************/ | |||
void PQCLEAN_KYBER512_CLEAN_kyber_shake128_squeezeblocks(unsigned char *output, size_t nblocks, keccak_state *s) { | |||
shake128_squeezeblocks(output, nblocks, s->s); | |||
void PQCLEAN_KYBER512_CLEAN_kyber_shake128_squeezeblocks(unsigned char *output, size_t nblocks, shake128ctx *s) { | |||
shake128_squeezeblocks(output, nblocks, s); | |||
} | |||
/************************************************* | |||
@@ -4,12 +4,8 @@ | |||
#include "fips202.h" | |||
#include "params.h" | |||
typedef struct { | |||
uint64_t s[25]; | |||
} keccak_state; | |||
void PQCLEAN_KYBER512_CLEAN_kyber_shake128_absorb(keccak_state *s, const unsigned char *input, unsigned char x, unsigned char y); | |||
void PQCLEAN_KYBER512_CLEAN_kyber_shake128_squeezeblocks(unsigned char *output, size_t nblocks, keccak_state *s); | |||
void PQCLEAN_KYBER512_CLEAN_kyber_shake128_absorb(shake128ctx *s, const unsigned char *input, unsigned char x, unsigned char y); | |||
void PQCLEAN_KYBER512_CLEAN_kyber_shake128_squeezeblocks(unsigned char *output, size_t nblocks, shake128ctx *s); | |||
void PQCLEAN_KYBER512_CLEAN_shake256_prf(unsigned char *output, size_t outlen, const unsigned char *key, unsigned char nonce); | |||
#define hash_h(OUT, IN, INBYTES) sha3_256(OUT, IN, INBYTES) | |||
@@ -21,6 +17,6 @@ void PQCLEAN_KYBER512_CLEAN_shake256_prf(unsigned char *output, size_t outlen, c | |||
#define XOF_BLOCKBYTES 168 | |||
typedef keccak_state xof_state; | |||
typedef shake128ctx xof_state; | |||
#endif /* SYMMETRIC_H */ |
@@ -4,7 +4,7 @@ LIB=libkyber768_clean.a | |||
HEADERS=api.h cbd.h indcpa.h ntt.h params.h poly.h polyvec.h reduce.h verify.h symmetric.h | |||
OBJECTS=cbd.o indcpa.o kem.o ntt.o poly.o polyvec.o reduce.o verify.o symmetric-fips202.o | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
@@ -8,12 +8,12 @@ | |||
* | |||
* Description: Absorb step of the SHAKE128 specialized for the Kyber context. | |||
* | |||
* Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state | |||
* Arguments: - shake128ctx *s: pointer to (uninitialized) output Keccak state | |||
* - const unsigned char *input: pointer to KYBER_SYMBYTES input to be absorbed into s | |||
* - unsigned char i additional byte of input | |||
* - unsigned char j additional byte of input | |||
**************************************************/ | |||
void PQCLEAN_KYBER768_CLEAN_kyber_shake128_absorb(keccak_state *s, const unsigned char *input, unsigned char x, unsigned char y) { | |||
void PQCLEAN_KYBER768_CLEAN_kyber_shake128_absorb(shake128ctx *s, const unsigned char *input, unsigned char x, unsigned char y) { | |||
unsigned char extseed[KYBER_SYMBYTES + 2]; | |||
int i; | |||
@@ -22,7 +22,7 @@ void PQCLEAN_KYBER768_CLEAN_kyber_shake128_absorb(keccak_state *s, const unsigne | |||
} | |||
extseed[i++] = x; | |||
extseed[i] = y; | |||
shake128_absorb(s->s, extseed, KYBER_SYMBYTES + 2); | |||
shake128_absorb(s, extseed, KYBER_SYMBYTES + 2); | |||
} | |||
/************************************************* | |||
@@ -34,10 +34,10 @@ void PQCLEAN_KYBER768_CLEAN_kyber_shake128_absorb(keccak_state *s, const unsigne | |||
* | |||
* Arguments: - unsigned char *output: pointer to output blocks | |||
* - size_t nblocks: number of blocks to be squeezed (written to output) | |||
* - keccak_state *s: pointer to in/output Keccak state | |||
* - shake128ctx *s: pointer to in/output Keccak state | |||
**************************************************/ | |||
void PQCLEAN_KYBER768_CLEAN_kyber_shake128_squeezeblocks(unsigned char *output, size_t nblocks, keccak_state *s) { | |||
shake128_squeezeblocks(output, nblocks, s->s); | |||
void PQCLEAN_KYBER768_CLEAN_kyber_shake128_squeezeblocks(unsigned char *output, size_t nblocks, shake128ctx *s) { | |||
shake128_squeezeblocks(output, nblocks, s); | |||
} | |||
/************************************************* | |||
@@ -4,12 +4,8 @@ | |||
#include "fips202.h" | |||
#include "params.h" | |||
typedef struct { | |||
uint64_t s[25]; | |||
} keccak_state; | |||
void PQCLEAN_KYBER768_CLEAN_kyber_shake128_absorb(keccak_state *s, const unsigned char *input, unsigned char x, unsigned char y); | |||
void PQCLEAN_KYBER768_CLEAN_kyber_shake128_squeezeblocks(unsigned char *output, size_t nblocks, keccak_state *s); | |||
void PQCLEAN_KYBER768_CLEAN_kyber_shake128_absorb(shake128ctx *s, const unsigned char *input, unsigned char x, unsigned char y); | |||
void PQCLEAN_KYBER768_CLEAN_kyber_shake128_squeezeblocks(unsigned char *output, size_t nblocks, shake128ctx *s); | |||
void PQCLEAN_KYBER768_CLEAN_shake256_prf(unsigned char *output, size_t outlen, const unsigned char *key, unsigned char nonce); | |||
#define hash_h(OUT, IN, INBYTES) sha3_256(OUT, IN, INBYTES) | |||
@@ -21,6 +17,6 @@ void PQCLEAN_KYBER768_CLEAN_shake256_prf(unsigned char *output, size_t outlen, c | |||
#define XOF_BLOCKBYTES 168 | |||
typedef keccak_state xof_state; | |||
typedef shake128ctx xof_state; | |||
#endif /* SYMMETRIC_H */ |
@@ -0,0 +1,21 @@ | |||
name: NewHope1024CCA | |||
type: kem | |||
claimed-nist-level: 5 | |||
claimed-security: IND-CCA2 | |||
length-public-key: 1824 | |||
length-secret-key: 3680 | |||
length-ciphertext: 2208 | |||
length-shared-secret: 32 | |||
nistkat-sha256: 8500b88222b3a62e57a6ecaac57f79258f08af49211e0c3f2ca7eab8089c0ce0 | |||
principal-submitter: Thomas Pöppelmann | |||
auxiliary-submitters: | |||
- Erdem Alkim | |||
- Roberto Avanzi | |||
- Joppe Bos | |||
- Léo Ducas | |||
- Antonio de la Piedra | |||
- Peter Schwabe | |||
- Douglas Stebila | |||
implementations: | |||
- name: clean | |||
version: https://github.com/newhopecrypto/newhope/commit/3fc68c6090b23c56cc190a78af2f43ee8900e9d0 |
@@ -0,0 +1 @@ | |||
Public Domain |
@@ -0,0 +1,19 @@ | |||
# This Makefile can be used with GNU Make or BSD Make | |||
LIB=libnewhope1024cca_clean.a | |||
HEADERS=api.h cpapke.h ntt.h params.h poly.h reduce.h verify.h | |||
OBJECTS=cpapke.o kem.o ntt.o poly.o precomp.o reduce.o verify.o | |||
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
%.o: %.c $(HEADERS) | |||
$(CC) $(CFLAGS) -c -o $@ $< | |||
$(LIB): $(OBJECTS) | |||
$(AR) -r $@ $(OBJECTS) | |||
clean: | |||
$(RM) $(OBJECTS) | |||
$(RM) $(LIB) |
@@ -0,0 +1,19 @@ | |||
# This Makefile can be used with Microsoft Visual Studio's nmake using the command: | |||
# nmake /f Makefile.Microsoft_nmake | |||
LIBRARY=libnewhope1024cca_clean.lib | |||
OBJECTS=cpapke.obj kem.obj ntt.obj poly.obj precomp.obj reduce.obj verify.obj | |||
CFLAGS=/nologo /I ..\..\..\common /W4 /WX | |||
all: $(LIBRARY) | |||
# Make sure objects are recompiled if headers change. | |||
$(OBJECTS): *.h | |||
$(LIBRARY): $(OBJECTS) | |||
LIB.EXE /NOLOGO /WX /OUT:$@ $** | |||
clean: | |||
-DEL $(OBJECTS) | |||
-DEL $(LIBRARY) |
@@ -0,0 +1,15 @@ | |||
#ifndef PQCLEAN_NEWHOPE1024CCA_CLEAN_API_H | |||
#define PQCLEAN_NEWHOPE1024CCA_CLEAN_API_H | |||
#define PQCLEAN_NEWHOPE1024CCA_CLEAN_CRYPTO_SECRETKEYBYTES 3680 | |||
#define PQCLEAN_NEWHOPE1024CCA_CLEAN_CRYPTO_PUBLICKEYBYTES 1824 | |||
#define PQCLEAN_NEWHOPE1024CCA_CLEAN_CRYPTO_CIPHERTEXTBYTES 2208 | |||
#define PQCLEAN_NEWHOPE1024CCA_CLEAN_CRYPTO_BYTES 32 | |||
#define PQCLEAN_NEWHOPE1024CCA_CLEAN_CRYPTO_ALGNAME "NewHope1024-CCAKEM" | |||
int PQCLEAN_NEWHOPE1024CCA_CLEAN_crypto_kem_keypair(unsigned char *pk, unsigned char *sk); | |||
int PQCLEAN_NEWHOPE1024CCA_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk); | |||
int PQCLEAN_NEWHOPE1024CCA_CLEAN_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk); | |||
#endif |
@@ -0,0 +1,192 @@ | |||
#include "api.h" | |||
#include "cpapke.h" | |||
#include "fips202.h" | |||
#include "poly.h" | |||
#include "randombytes.h" | |||
#include <stdio.h> | |||
/************************************************* | |||
* Name: encode_pk | |||
* | |||
* Description: Serialize the public key as concatenation of the | |||
* serialization of the polynomial pk and the public seed | |||
* used to generete the polynomial a. | |||
* | |||
* Arguments: unsigned char *r: pointer to the output serialized public key | |||
* const poly *pk: pointer to the input public-key polynomial | |||
* const unsigned char *seed: pointer to the input public seed | |||
**************************************************/ | |||
static void encode_pk(unsigned char *r, const poly *pk, const unsigned char *seed) { | |||
int i; | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_tobytes(r, pk); | |||
for (i = 0; i < NEWHOPE_SYMBYTES; i++) { | |||
r[NEWHOPE_POLYBYTES + i] = seed[i]; | |||
} | |||
} | |||
/************************************************* | |||
* Name: decode_pk | |||
* | |||
* Description: De-serialize the public key; inverse of encode_pk | |||
* | |||
* Arguments: poly *pk: pointer to output public-key polynomial | |||
* unsigned char *seed: pointer to output public seed | |||
* const unsigned char *r: pointer to input byte array | |||
**************************************************/ | |||
static void decode_pk(poly *pk, unsigned char *seed, const unsigned char *r) { | |||
int i; | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_frombytes(pk, r); | |||
for (i = 0; i < NEWHOPE_SYMBYTES; i++) { | |||
seed[i] = r[NEWHOPE_POLYBYTES + i]; | |||
} | |||
} | |||
/************************************************* | |||
* Name: encode_c | |||
* | |||
* Description: Serialize the ciphertext as concatenation of the | |||
* serialization of the polynomial b and serialization | |||
* of the compressed polynomial v | |||
* | |||
* Arguments: - unsigned char *r: pointer to the output serialized ciphertext | |||
* - const poly *b: pointer to the input polynomial b | |||
* - const poly *v: pointer to the input polynomial v | |||
**************************************************/ | |||
static void encode_c(unsigned char *r, const poly *b, const poly *v) { | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_tobytes(r, b); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_compress(r + NEWHOPE_POLYBYTES, v); | |||
} | |||
/************************************************* | |||
* Name: decode_c | |||
* | |||
* Description: de-serialize the ciphertext; inverse of encode_c | |||
* | |||
* Arguments: - poly *b: pointer to output polynomial b | |||
* - poly *v: pointer to output polynomial v | |||
* - const unsigned char *r: pointer to input byte array | |||
**************************************************/ | |||
static void decode_c(poly *b, poly *v, const unsigned char *r) { | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_frombytes(b, r); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_decompress(v, r + NEWHOPE_POLYBYTES); | |||
} | |||
/************************************************* | |||
* Name: gen_a | |||
* | |||
* Description: Deterministically generate public polynomial a from seed | |||
* | |||
* Arguments: - poly *a: pointer to output polynomial a | |||
* - const unsigned char *seed: pointer to input seed | |||
**************************************************/ | |||
static void gen_a(poly *a, const unsigned char *seed) { | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_uniform(a, seed); | |||
} | |||
/************************************************* | |||
* Name: cpapke_keypair | |||
* | |||
* Description: Generates public and private key | |||
* for the CPA public-key encryption scheme underlying | |||
* the NewHope KEMs | |||
* | |||
* Arguments: - unsigned char *pk: pointer to output public key | |||
* - unsigned char *sk: pointer to output private key | |||
**************************************************/ | |||
void PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_keypair(unsigned char *pk, | |||
unsigned char *sk) { | |||
poly ahat, ehat, ahat_shat, bhat, shat; | |||
unsigned char z[2 * NEWHOPE_SYMBYTES]; | |||
unsigned char *publicseed = z; | |||
unsigned char *noiseseed = z + NEWHOPE_SYMBYTES; | |||
randombytes(z, NEWHOPE_SYMBYTES); | |||
shake256(z, 2 * NEWHOPE_SYMBYTES, z, NEWHOPE_SYMBYTES); | |||
gen_a(&ahat, publicseed); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_sample(&shat, noiseseed, 0); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_ntt(&shat); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_sample(&ehat, noiseseed, 1); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_ntt(&ehat); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_mul_pointwise(&ahat_shat, &shat, &ahat); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_add(&bhat, &ehat, &ahat_shat); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_tobytes(sk, &shat); | |||
encode_pk(pk, &bhat, publicseed); | |||
} | |||
/************************************************* | |||
* Name: cpapke_enc | |||
* | |||
* Description: Encryption function of | |||
* the CPA public-key encryption scheme underlying | |||
* the NewHope KEMs | |||
* | |||
* Arguments: - unsigned char *c: pointer to output ciphertext | |||
* - const unsigned char *m: pointer to input message (of length NEWHOPE_SYMBYTES bytes) | |||
* - const unsigned char *pk: pointer to input public key | |||
* - const unsigned char *coin: pointer to input random coins used as seed | |||
* to deterministically generate all randomness | |||
**************************************************/ | |||
void PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_enc(unsigned char *c, | |||
const unsigned char *m, | |||
const unsigned char *pk, | |||
const unsigned char *coin) { | |||
poly sprime, eprime, vprime, ahat, bhat, eprimeprime, uhat, v; | |||
unsigned char publicseed[NEWHOPE_SYMBYTES]; | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_frommsg(&v, m); | |||
decode_pk(&bhat, publicseed, pk); | |||
gen_a(&ahat, publicseed); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_sample(&sprime, coin, 0); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_sample(&eprime, coin, 1); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_sample(&eprimeprime, coin, 2); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_ntt(&sprime); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_ntt(&eprime); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_mul_pointwise(&uhat, &ahat, &sprime); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_add(&uhat, &uhat, &eprime); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_mul_pointwise(&vprime, &bhat, &sprime); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_invntt(&vprime); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_add(&vprime, &vprime, &eprimeprime); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_add(&vprime, &vprime, &v); // add message | |||
encode_c(c, &uhat, &vprime); | |||
} | |||
/************************************************* | |||
* Name: cpapke_dec | |||
* | |||
* Description: Decryption function of | |||
* the CPA public-key encryption scheme underlying | |||
* the NewHope KEMs | |||
* | |||
* Arguments: - unsigned char *m: pointer to output decrypted message | |||
* - const unsigned char *c: pointer to input ciphertext | |||
* - const unsigned char *sk: pointer to input secret key | |||
**************************************************/ | |||
void PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_dec(unsigned char *m, | |||
const unsigned char *c, | |||
const unsigned char *sk) { | |||
poly vprime, uhat, tmp, shat; | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_frombytes(&shat, sk); | |||
decode_c(&uhat, &vprime, c); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_mul_pointwise(&tmp, &shat, &uhat); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_invntt(&tmp); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_sub(&tmp, &tmp, &vprime); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_poly_tomsg(m, &tmp); | |||
} |
@@ -0,0 +1,16 @@ | |||
#ifndef INDCPA_H | |||
#define INDCPA_H | |||
void PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_keypair(unsigned char *pk, | |||
unsigned char *sk); | |||
void PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_enc(unsigned char *c, | |||
const unsigned char *m, | |||
const unsigned char *pk, | |||
const unsigned char *coins); | |||
void PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_dec(unsigned char *m, | |||
const unsigned char *c, | |||
const unsigned char *sk); | |||
#endif |
@@ -0,0 +1,116 @@ | |||
#include "api.h" | |||
#include "cpapke.h" | |||
#include "fips202.h" | |||
#include "params.h" | |||
#include "randombytes.h" | |||
#include "verify.h" | |||
#include <string.h> | |||
/************************************************* | |||
* Name: crypto_kem_keypair | |||
* | |||
* Description: Generates public and private key | |||
* for CCA secure NewHope key encapsulation | |||
* mechanism | |||
* | |||
* Arguments: - unsigned char *pk: pointer to output public key (an already allocated array of CRYPTO_PUBLICKEYBYTES bytes) | |||
* - unsigned char *sk: pointer to output private key (an already allocated array of CRYPTO_SECRETKEYBYTES bytes) | |||
* | |||
* Returns 0 (success) | |||
**************************************************/ | |||
int PQCLEAN_NEWHOPE1024CCA_CLEAN_crypto_kem_keypair(unsigned char *pk, unsigned char *sk) { | |||
size_t i; | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_keypair(pk, sk); /* First put the actual secret key into sk */ | |||
sk += NEWHOPE_CPAPKE_SECRETKEYBYTES; | |||
for (i = 0; i < NEWHOPE_CPAPKE_PUBLICKEYBYTES; i++) { /* Append the public key for re-encryption */ | |||
sk[i] = pk[i]; | |||
} | |||
sk += NEWHOPE_CPAPKE_PUBLICKEYBYTES; | |||
shake256(sk, NEWHOPE_SYMBYTES, pk, NEWHOPE_CPAPKE_PUBLICKEYBYTES); /* Append the hash of the public key */ | |||
sk += NEWHOPE_SYMBYTES; | |||
randombytes(sk, NEWHOPE_SYMBYTES); /* Append the value s for pseudo-random output on reject */ | |||
return 0; | |||
} | |||
/************************************************* | |||
* Name: crypto_kem_enc | |||
* | |||
* Description: Generates cipher text and shared | |||
* secret for given public key | |||
* | |||
* Arguments: - unsigned char *ct: pointer to output cipher text (an already allocated array of CRYPTO_CIPHERTEXTBYTES bytes) | |||
* - unsigned char *ss: pointer to output shared secret (an already allocated array of CRYPTO_BYTES bytes) | |||
* - const unsigned char *pk: pointer to input public key (an already allocated array of CRYPTO_PUBLICKEYBYTES bytes) | |||
* | |||
* Returns 0 (success) | |||
**************************************************/ | |||
int PQCLEAN_NEWHOPE1024CCA_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk) { | |||
unsigned char k_coins_d[3 * NEWHOPE_SYMBYTES]; /* Will contain key, coins, qrom-hash */ | |||
unsigned char buf[2 * NEWHOPE_SYMBYTES]; | |||
int i; | |||
randombytes(buf, NEWHOPE_SYMBYTES); | |||
shake256(buf, NEWHOPE_SYMBYTES, buf, NEWHOPE_SYMBYTES); /* Don't release system RNG output */ | |||
shake256(buf + NEWHOPE_SYMBYTES, NEWHOPE_SYMBYTES, pk, NEWHOPE_CCAKEM_PUBLICKEYBYTES); /* Multitarget countermeasure for coins + contributory KEM */ | |||
shake256(k_coins_d, 3 * NEWHOPE_SYMBYTES, buf, 2 * NEWHOPE_SYMBYTES); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_enc(ct, buf, pk, k_coins_d + NEWHOPE_SYMBYTES); /* coins are in k_coins_d+NEWHOPE_SYMBYTES */ | |||
for (i = 0; i < NEWHOPE_SYMBYTES; i++) { | |||
ct[i + NEWHOPE_CPAPKE_CIPHERTEXTBYTES] = k_coins_d[i + 2 * NEWHOPE_SYMBYTES]; /* copy Targhi-Unruh hash into ct */ | |||
} | |||
shake256(k_coins_d + NEWHOPE_SYMBYTES, NEWHOPE_SYMBYTES, ct, NEWHOPE_CCAKEM_CIPHERTEXTBYTES); /* overwrite coins in k_coins_d with h(c) */ | |||
shake256(ss, NEWHOPE_SYMBYTES, k_coins_d, 2 * NEWHOPE_SYMBYTES); /* hash concatenation of pre-k and h(c) to ss */ | |||
return 0; | |||
} | |||
/************************************************* | |||
* Name: crypto_kem_dec | |||
* | |||
* Description: Generates shared secret for given | |||
* cipher text and private key | |||
* | |||
* Arguments: - unsigned char *ss: pointer to output shared secret (an already allocated array of CRYPTO_BYTES bytes) | |||
* - const unsigned char *ct: pointer to input cipher text (an already allocated array of CRYPTO_CIPHERTEXTBYTES bytes) | |||
* - const unsigned char *sk: pointer to input private key (an already allocated array of CRYPTO_SECRETKEYBYTES bytes) | |||
* | |||
* Returns 0 for sucess or -1 for failure | |||
* | |||
* On failure, ss will contain a randomized value. | |||
**************************************************/ | |||
int PQCLEAN_NEWHOPE1024CCA_CLEAN_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk) { | |||
int i, fail; | |||
unsigned char ct_cmp[NEWHOPE_CCAKEM_CIPHERTEXTBYTES]; | |||
unsigned char buf[2 * NEWHOPE_SYMBYTES]; | |||
unsigned char k_coins_d[3 * NEWHOPE_SYMBYTES]; /* Will contain key, coins, qrom-hash */ | |||
const unsigned char *pk = sk + NEWHOPE_CPAPKE_SECRETKEYBYTES; | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_dec(buf, ct, sk); | |||
for (i = 0; i < NEWHOPE_SYMBYTES; i++) { /* Use hash of pk stored in sk */ | |||
buf[NEWHOPE_SYMBYTES + i] = sk[NEWHOPE_CCAKEM_SECRETKEYBYTES - 2 * NEWHOPE_SYMBYTES + i]; | |||
} | |||
shake256(k_coins_d, 3 * NEWHOPE_SYMBYTES, buf, 2 * NEWHOPE_SYMBYTES); | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_cpapke_enc(ct_cmp, buf, pk, k_coins_d + NEWHOPE_SYMBYTES); /* coins are in k_coins_d+NEWHOPE_SYMBYTES */ | |||
for (i = 0; i < NEWHOPE_SYMBYTES; i++) { | |||
ct_cmp[i + NEWHOPE_CPAPKE_CIPHERTEXTBYTES] = k_coins_d[i + 2 * NEWHOPE_SYMBYTES]; | |||
} | |||
fail = PQCLEAN_NEWHOPE1024CCA_CLEAN_verify(ct, ct_cmp, NEWHOPE_CCAKEM_CIPHERTEXTBYTES); | |||
shake256(k_coins_d + NEWHOPE_SYMBYTES, NEWHOPE_SYMBYTES, ct, NEWHOPE_CCAKEM_CIPHERTEXTBYTES); /* overwrite coins in k_coins_d with h(c) */ | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_cmov(k_coins_d, sk + NEWHOPE_CCAKEM_SECRETKEYBYTES - NEWHOPE_SYMBYTES, NEWHOPE_SYMBYTES, (unsigned char) fail); /* Overwrite pre-k with z on re-encryption failure */ | |||
shake256(ss, NEWHOPE_SYMBYTES, k_coins_d, 2 * NEWHOPE_SYMBYTES); /* hash concatenation of pre-k and h(c) to k */ | |||
return 0; | |||
} |
@@ -0,0 +1,127 @@ | |||
#include "inttypes.h" | |||
#include "ntt.h" | |||
#include "params.h" | |||
#include "reduce.h" | |||
/************************************************************ | |||
* Name: bitrev_table | |||
* | |||
* Description: Contains bit-reversed 10-bit indices to be used to re-order | |||
* polynomials before number theoratic transform | |||
************************************************************/ | |||
static uint16_t bitrev_table[NEWHOPE_N] = { | |||
0, 512, 256, 768, 128, 640, 384, 896, 64, 576, 320, 832, 192, 704, 448, 960, 32, 544, 288, 800, 160, 672, 416, 928, 96, 608, 352, 864, 224, 736, 480, 992, | |||
16, 528, 272, 784, 144, 656, 400, 912, 80, 592, 336, 848, 208, 720, 464, 976, 48, 560, 304, 816, 176, 688, 432, 944, 112, 624, 368, 880, 240, 752, 496, 1008, | |||
8, 520, 264, 776, 136, 648, 392, 904, 72, 584, 328, 840, 200, 712, 456, 968, 40, 552, 296, 808, 168, 680, 424, 936, 104, 616, 360, 872, 232, 744, 488, 1000, | |||
24, 536, 280, 792, 152, 664, 408, 920, 88, 600, 344, 856, 216, 728, 472, 984, 56, 568, 312, 824, 184, 696, 440, 952, 120, 632, 376, 888, 248, 760, 504, 1016, | |||
4, 516, 260, 772, 132, 644, 388, 900, 68, 580, 324, 836, 196, 708, 452, 964, 36, 548, 292, 804, 164, 676, 420, 932, 100, 612, 356, 868, 228, 740, 484, 996, | |||
20, 532, 276, 788, 148, 660, 404, 916, 84, 596, 340, 852, 212, 724, 468, 980, 52, 564, 308, 820, 180, 692, 436, 948, 116, 628, 372, 884, 244, 756, 500, 1012, | |||
12, 524, 268, 780, 140, 652, 396, 908, 76, 588, 332, 844, 204, 716, 460, 972, 44, 556, 300, 812, 172, 684, 428, 940, 108, 620, 364, 876, 236, 748, 492, 1004, | |||
28, 540, 284, 796, 156, 668, 412, 924, 92, 604, 348, 860, 220, 732, 476, 988, 60, 572, 316, 828, 188, 700, 444, 956, 124, 636, 380, 892, 252, 764, 508, 1020, | |||
2, 514, 258, 770, 130, 642, 386, 898, 66, 578, 322, 834, 194, 706, 450, 962, 34, 546, 290, 802, 162, 674, 418, 930, 98, 610, 354, 866, 226, 738, 482, 994, | |||
18, 530, 274, 786, 146, 658, 402, 914, 82, 594, 338, 850, 210, 722, 466, 978, 50, 562, 306, 818, 178, 690, 434, 946, 114, 626, 370, 882, 242, 754, 498, 1010, | |||
10, 522, 266, 778, 138, 650, 394, 906, 74, 586, 330, 842, 202, 714, 458, 970, 42, 554, 298, 810, 170, 682, 426, 938, 106, 618, 362, 874, 234, 746, 490, 1002, | |||
26, 538, 282, 794, 154, 666, 410, 922, 90, 602, 346, 858, 218, 730, 474, 986, 58, 570, 314, 826, 186, 698, 442, 954, 122, 634, 378, 890, 250, 762, 506, 1018, | |||
6, 518, 262, 774, 134, 646, 390, 902, 70, 582, 326, 838, 198, 710, 454, 966, 38, 550, 294, 806, 166, 678, 422, 934, 102, 614, 358, 870, 230, 742, 486, 998, | |||
22, 534, 278, 790, 150, 662, 406, 918, 86, 598, 342, 854, 214, 726, 470, 982, 54, 566, 310, 822, 182, 694, 438, 950, 118, 630, 374, 886, 246, 758, 502, 1014, | |||
14, 526, 270, 782, 142, 654, 398, 910, 78, 590, 334, 846, 206, 718, 462, 974, 46, 558, 302, 814, 174, 686, 430, 942, 110, 622, 366, 878, 238, 750, 494, 1006, | |||
30, 542, 286, 798, 158, 670, 414, 926, 94, 606, 350, 862, 222, 734, 478, 990, 62, 574, 318, 830, 190, 702, 446, 958, 126, 638, 382, 894, 254, 766, 510, 1022, | |||
1, 513, 257, 769, 129, 641, 385, 897, 65, 577, 321, 833, 193, 705, 449, 961, 33, 545, 289, 801, 161, 673, 417, 929, 97, 609, 353, 865, 225, 737, 481, 993, | |||
17, 529, 273, 785, 145, 657, 401, 913, 81, 593, 337, 849, 209, 721, 465, 977, 49, 561, 305, 817, 177, 689, 433, 945, 113, 625, 369, 881, 241, 753, 497, 1009, | |||
9, 521, 265, 777, 137, 649, 393, 905, 73, 585, 329, 841, 201, 713, 457, 969, 41, 553, 297, 809, 169, 681, 425, 937, 105, 617, 361, 873, 233, 745, 489, 1001, | |||
25, 537, 281, 793, 153, 665, 409, 921, 89, 601, 345, 857, 217, 729, 473, 985, 57, 569, 313, 825, 185, 697, 441, 953, 121, 633, 377, 889, 249, 761, 505, 1017, | |||
5, 517, 261, 773, 133, 645, 389, 901, 69, 581, 325, 837, 197, 709, 453, 965, 37, 549, 293, 805, 165, 677, 421, 933, 101, 613, 357, 869, 229, 741, 485, 997, | |||
21, 533, 277, 789, 149, 661, 405, 917, 85, 597, 341, 853, 213, 725, 469, 981, 53, 565, 309, 821, 181, 693, 437, 949, 117, 629, 373, 885, 245, 757, 501, 1013, | |||
13, 525, 269, 781, 141, 653, 397, 909, 77, 589, 333, 845, 205, 717, 461, 973, 45, 557, 301, 813, 173, 685, 429, 941, 109, 621, 365, 877, 237, 749, 493, 1005, | |||
29, 541, 285, 797, 157, 669, 413, 925, 93, 605, 349, 861, 221, 733, 477, 989, 61, 573, 317, 829, 189, 701, 445, 957, 125, 637, 381, 893, 253, 765, 509, 1021, | |||
3, 515, 259, 771, 131, 643, 387, 899, 67, 579, 323, 835, 195, 707, 451, 963, 35, 547, 291, 803, 163, 675, 419, 931, 99, 611, 355, 867, 227, 739, 483, 995, | |||
19, 531, 275, 787, 147, 659, 403, 915, 83, 595, 339, 851, 211, 723, 467, 979, 51, 563, 307, 819, 179, 691, 435, 947, 115, 627, 371, 883, 243, 755, 499, 1011, | |||
11, 523, 267, 779, 139, 651, 395, 907, 75, 587, 331, 843, 203, 715, 459, 971, 43, 555, 299, 811, 171, 683, 427, 939, 107, 619, 363, 875, 235, 747, 491, 1003, | |||
27, 539, 283, 795, 155, 667, 411, 923, 91, 603, 347, 859, 219, 731, 475, 987, 59, 571, 315, 827, 187, 699, 443, 955, 123, 635, 379, 891, 251, 763, 507, 1019, | |||
7, 519, 263, 775, 135, 647, 391, 903, 71, 583, 327, 839, 199, 711, 455, 967, 39, 551, 295, 807, 167, 679, 423, 935, 103, 615, 359, 871, 231, 743, 487, 999, | |||
23, 535, 279, 791, 151, 663, 407, 919, 87, 599, 343, 855, 215, 727, 471, 983, 55, 567, 311, 823, 183, 695, 439, 951, 119, 631, 375, 887, 247, 759, 503, 1015, | |||
15, 527, 271, 783, 143, 655, 399, 911, 79, 591, 335, 847, 207, 719, 463, 975, 47, 559, 303, 815, 175, 687, 431, 943, 111, 623, 367, 879, 239, 751, 495, 1007, | |||
31, 543, 287, 799, 159, 671, 415, 927, 95, 607, 351, 863, 223, 735, 479, 991, 63, 575, 319, 831, 191, 703, 447, 959, 127, 639, 383, 895, 255, 767, 511, 1023 | |||
}; | |||
/************************************************* | |||
* Name: bitrev_vector | |||
* | |||
* Description: Permutes coefficients of a polynomial into bitreversed order | |||
* | |||
* Arguments: - uint16_t* poly: pointer to in/output polynomial | |||
**************************************************/ | |||
void PQCLEAN_NEWHOPE1024CCA_CLEAN_bitrev_vector(uint16_t *poly) { | |||
unsigned int i, r; | |||
uint16_t tmp; | |||
for (i = 0; i < NEWHOPE_N; i++) { | |||
r = bitrev_table[i]; | |||
if (i < r) { | |||
tmp = poly[i]; | |||
poly[i] = poly[r]; | |||
poly[r] = tmp; | |||
} | |||
} | |||
} | |||
/************************************************* | |||
* Name: mul_coefficients | |||
* | |||
* Description: Performs pointwise (coefficient-wise) multiplication | |||
* of two polynomials | |||
* Arguments: - uint16_t* poly: pointer to in/output polynomial | |||
* - const uint16_t* factors: pointer to input polynomial, coefficients | |||
* are assumed to be in Montgomery representation | |||
**************************************************/ | |||
void PQCLEAN_NEWHOPE1024CCA_CLEAN_mul_coefficients(uint16_t *poly, const uint16_t *factors) { | |||
unsigned int i; | |||
for (i = 0; i < NEWHOPE_N; i++) { | |||
poly[i] = PQCLEAN_NEWHOPE1024CCA_CLEAN_montgomery_reduce((poly[i] * factors[i])); | |||
} | |||
} | |||
void /************************************************* | |||
* Name: ntt | |||
* | |||
* Description: Computes number-theoretic transform (NTT) of | |||
* a polynomial in place; inputs assumed to be in | |||
* bitreversed order, output in normal order | |||
* | |||
* Arguments: - uint16_t * a: pointer to in/output polynomial | |||
* - const uint16_t* omega: pointer to input powers of root of unity omega; | |||
* assumed to be in Montgomery domain | |||
**************************************************/ | |||
PQCLEAN_NEWHOPE1024CCA_CLEAN_ntt(uint16_t *a, const uint16_t *omega) { | |||
int i, start, j, jTwiddle, distance; | |||
uint16_t temp, W; | |||
for (i = 0; i < 10; i += 2) { | |||
// Even level | |||
distance = (1 << i); | |||
for (start = 0; start < distance; start++) { | |||
jTwiddle = 0; | |||
for (j = start; j < NEWHOPE_N - 1; j += 2 * distance) { | |||
W = omega[jTwiddle++]; | |||
temp = a[j]; | |||
a[j] = (temp + a[j + distance]); // Omit reduction (be lazy) | |||
a[j + distance] = PQCLEAN_NEWHOPE1024CCA_CLEAN_montgomery_reduce((W * ((uint32_t)temp + 3 * NEWHOPE_Q - a[j + distance]))); | |||
} | |||
} | |||
// Odd level | |||
distance <<= 1; | |||
for (start = 0; start < distance; start++) { | |||
jTwiddle = 0; | |||
for (j = start; j < NEWHOPE_N - 1; j += 2 * distance) { | |||
W = omega[jTwiddle++]; | |||
temp = a[j]; | |||
a[j] = (temp + a[j + distance]) % NEWHOPE_Q; | |||
a[j + distance] = PQCLEAN_NEWHOPE1024CCA_CLEAN_montgomery_reduce((W * ((uint32_t)temp + 3 * NEWHOPE_Q - a[j + distance]))); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
#ifndef NTT_H | |||
#define NTT_H | |||
#include "inttypes.h" | |||
extern const uint16_t PQCLEAN_NEWHOPE1024CCA_CLEAN_omegas_inv_bitrev_montgomery[]; | |||
extern const uint16_t PQCLEAN_NEWHOPE1024CCA_CLEAN_gammas_bitrev_montgomery[]; | |||
extern const uint16_t PQCLEAN_NEWHOPE1024CCA_CLEAN_gammas_inv_montgomery[]; | |||
void PQCLEAN_NEWHOPE1024CCA_CLEAN_bitrev_vector(uint16_t *poly); | |||
void PQCLEAN_NEWHOPE1024CCA_CLEAN_mul_coefficients(uint16_t *poly, const uint16_t *factors); | |||
void PQCLEAN_NEWHOPE1024CCA_CLEAN_ntt(uint16_t *a, const uint16_t *omegas); | |||
#endif |
@@ -0,0 +1,25 @@ | |||
#ifndef PQCLEAN_NEWHOPE1024CCA_CLEAN_PARAMS_H | |||
#define PQCLEAN_NEWHOPE1024CCA_CLEAN_PARAMS_H | |||
#define NEWHOPE_N 1024 | |||
#define NEWHOPE_Q 12289 | |||
#define NEWHOPE_K 8 /* used in noise sampling */ | |||
#define NEWHOPE_SYMBYTES 32 /* size of shared key, seeds/coins, and hashes */ | |||
#define NEWHOPE_POLYBYTES ((14*NEWHOPE_N)/8) | |||
#define NEWHOPE_POLYCOMPRESSEDBYTES (( 3*NEWHOPE_N)/8) | |||
#define NEWHOPE_CPAPKE_PUBLICKEYBYTES (NEWHOPE_POLYBYTES + NEWHOPE_SYMBYTES) | |||
#define NEWHOPE_CPAPKE_SECRETKEYBYTES (NEWHOPE_POLYBYTES) | |||
#define NEWHOPE_CPAPKE_CIPHERTEXTBYTES (NEWHOPE_POLYBYTES + NEWHOPE_POLYCOMPRESSEDBYTES) | |||
#define NEWHOPE_CPAKEM_PUBLICKEYBYTES NEWHOPE_CPAPKE_PUBLICKEYBYTES | |||
#define NEWHOPE_CPAKEM_SECRETKEYBYTES NEWHOPE_CPAPKE_SECRETKEYBYTES | |||
#define NEWHOPE_CPAKEM_CIPHERTEXTBYTES NEWHOPE_CPAPKE_CIPHERTEXTBYTES | |||
#define NEWHOPE_CCAKEM_PUBLICKEYBYTES NEWHOPE_CPAPKE_PUBLICKEYBYTES | |||
#define NEWHOPE_CCAKEM_SECRETKEYBYTES (NEWHOPE_CPAPKE_SECRETKEYBYTES + NEWHOPE_CPAPKE_PUBLICKEYBYTES + 2*NEWHOPE_SYMBYTES) | |||
#define NEWHOPE_CCAKEM_CIPHERTEXTBYTES (NEWHOPE_CPAPKE_CIPHERTEXTBYTES + NEWHOPE_SYMBYTES) /* Second part is for Targhi-Unruh */ | |||
#endif |