mirror of
https://github.com/henrydcase/pqc.git
synced 2024-11-23 16:08:59 +00:00
86 lines
3.3 KiB
C
86 lines
3.3 KiB
C
|
/********************************************************************************************
|
||
|
* 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_CLEAN_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 i, j, k;
|
||
|
int16_t A[PARAMS_N * PARAMS_N] = {0};
|
||
|
aes128ctx ctx128;
|
||
|
|
||
|
aes128_keyexp(&ctx128, seed_A);
|
||
|
for (i = 0; i < PARAMS_N; i++) {
|
||
|
for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) {
|
||
|
A[i*PARAMS_N + j] = i; // Loading values in the little-endian order
|
||
|
A[i*PARAMS_N + j + 1] = j;
|
||
|
}
|
||
|
}
|
||
|
aes128_ecb((uint8_t *) A, (uint8_t *) A, PARAMS_N * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128);
|
||
|
|
||
|
for (i = 0; i < PARAMS_N * PARAMS_N; i++) {
|
||
|
A[i] = PQCLEAN_FRODOKEM640AES_CLEAN_LE_TO_UINT16(A[i]);
|
||
|
}
|
||
|
memcpy(out, e, PARAMS_NBAR * PARAMS_N * sizeof(uint16_t));
|
||
|
|
||
|
for (i = 0; i < PARAMS_N; i++) { // Matrix multiplication-addition A*s + e
|
||
|
for (k = 0; k < PARAMS_NBAR; k++) {
|
||
|
uint16_t sum = 0;
|
||
|
for (j = 0; j < PARAMS_N; j++) {
|
||
|
sum += A[i * PARAMS_N + j] * s[k * PARAMS_N + j];
|
||
|
}
|
||
|
out[i * PARAMS_NBAR + k] += sum; // Adding e. No need to reduce modulo 2^15, extra bits are taken care of during packing later on.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
int PQCLEAN_FRODOKEM640AES_CLEAN_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, k;
|
||
|
int16_t A[PARAMS_N * PARAMS_N] = {0};
|
||
|
aes128ctx ctx128;
|
||
|
|
||
|
aes128_keyexp(&ctx128, seed_A);
|
||
|
for (i = 0; i < PARAMS_N; i++) {
|
||
|
for (j = 0; j < PARAMS_N; j += PARAMS_STRIPE_STEP) {
|
||
|
A[i*PARAMS_N + j] = i; // Loading values in the little-endian order
|
||
|
A[i*PARAMS_N + j + 1] = j;
|
||
|
}
|
||
|
}
|
||
|
aes128_ecb((uint8_t *) A, (uint8_t *) A, PARAMS_N * PARAMS_N * sizeof(int16_t) / AES_BLOCKBYTES, &ctx128);
|
||
|
|
||
|
for (i = 0; i < PARAMS_N * PARAMS_N; i++) {
|
||
|
A[i] = PQCLEAN_FRODOKEM640AES_CLEAN_LE_TO_UINT16(A[i]);
|
||
|
}
|
||
|
memcpy(out, e, PARAMS_NBAR * PARAMS_N * sizeof(uint16_t));
|
||
|
|
||
|
for (i = 0; i < PARAMS_N; i++) { // Matrix multiplication-addition A*s + e
|
||
|
for (k = 0; k < PARAMS_NBAR; k++) {
|
||
|
uint16_t sum = 0;
|
||
|
for (j = 0; j < PARAMS_N; j++) {
|
||
|
sum += A[j * PARAMS_N + i] * s[k * PARAMS_N + j];
|
||
|
}
|
||
|
out[k * PARAMS_N + i] += sum; // Adding e. No need to reduce modulo 2^15, extra bits are taken care of during packing later on.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|