Fix timing leak in decapsulation.
As identified in: Qian Guo, Thomas Johansson, Alexander Nilsson. A
key-recovery timing attack on post-quantum primitives using the
Fujisaki-Okamoto transformation and its application on FrodoKEM. In
CRYPTO 2020.
Based on
155c24c3df
This commit is contained in:
父節點
1f8b852e8f
當前提交
96e5f1d7ae
@ -12,6 +12,8 @@ void PQCLEAN_FRODOKEM1344AES_CLEAN_key_encode(uint16_t *out, const uint16_t *in)
|
||||
void PQCLEAN_FRODOKEM1344AES_CLEAN_key_decode(uint16_t *out, const uint16_t *in);
|
||||
void PQCLEAN_FRODOKEM1344AES_CLEAN_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb);
|
||||
void PQCLEAN_FRODOKEM1344AES_CLEAN_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb);
|
||||
int8_t PQCLEAN_FRODOKEM1344AES_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len);
|
||||
void PQCLEAN_FRODOKEM1344AES_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector);
|
||||
void PQCLEAN_FRODOKEM1344AES_CLEAN_clear_bytes(uint8_t *mem, size_t n);
|
||||
uint16_t PQCLEAN_FRODOKEM1344AES_CLEAN_LE_TO_UINT16(uint16_t n);
|
||||
uint16_t PQCLEAN_FRODOKEM1344AES_CLEAN_UINT16_TO_LE(uint16_t n);
|
||||
|
@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM1344AES_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct,
|
||||
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);
|
||||
}
|
||||
// If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s)
|
||||
// Needs to avoid branching on secret data as per:
|
||||
// Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum
|
||||
// primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020.
|
||||
int8_t selector = PQCLEAN_FRODOKEM1344AES_CLEAN_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM1344AES_CLEAN_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR);
|
||||
// If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s)
|
||||
PQCLEAN_FRODOKEM1344AES_CLEAN_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector);
|
||||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES);
|
||||
|
||||
// Cleanup:
|
||||
|
@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM1344AES_CLEAN_unpack(uint16_t *out, size_t outlen, const ui
|
||||
}
|
||||
|
||||
|
||||
int8_t PQCLEAN_FRODOKEM1344AES_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) {
|
||||
// Compare two arrays in constant time.
|
||||
// Returns 0 if the byte arrays are equal, -1 otherwise.
|
||||
uint16_t r = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r |= a[i] ^ b[i];
|
||||
}
|
||||
|
||||
r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1);
|
||||
return (int8_t)r;
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM1344AES_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) {
|
||||
// Select one of the two input arrays to be moved to r
|
||||
// If (selector == 0) then load r with a, else if (selector == -1) load r with b
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r[i] = (~selector & a[i]) | (selector & b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM1344AES_CLEAN_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.
|
||||
|
@ -12,6 +12,8 @@ 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);
|
||||
int8_t PQCLEAN_FRODOKEM1344AES_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len);
|
||||
void PQCLEAN_FRODOKEM1344AES_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector);
|
||||
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);
|
||||
|
@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM1344AES_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, c
|
||||
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);
|
||||
}
|
||||
// If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s)
|
||||
// Needs to avoid branching on secret data as per:
|
||||
// Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum
|
||||
// primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020.
|
||||
int8_t selector = PQCLEAN_FRODOKEM1344AES_OPT_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM1344AES_OPT_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR);
|
||||
// If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s)
|
||||
PQCLEAN_FRODOKEM1344AES_OPT_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector);
|
||||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES);
|
||||
|
||||
// Cleanup:
|
||||
|
@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM1344AES_OPT_unpack(uint16_t *out, size_t outlen, const uint
|
||||
}
|
||||
|
||||
|
||||
int8_t PQCLEAN_FRODOKEM1344AES_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) {
|
||||
// Compare two arrays in constant time.
|
||||
// Returns 0 if the byte arrays are equal, -1 otherwise.
|
||||
uint16_t r = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r |= a[i] ^ b[i];
|
||||
}
|
||||
|
||||
r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1);
|
||||
return (int8_t)r;
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM1344AES_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) {
|
||||
// Select one of the two input arrays to be moved to r
|
||||
// If (selector == 0) then load r with a, else if (selector == -1) load r with b
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r[i] = (~selector & a[i]) | (selector & b[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.
|
||||
|
@ -12,6 +12,8 @@ void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_key_encode(uint16_t *out, const uint16_t *i
|
||||
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_key_decode(uint16_t *out, const uint16_t *in);
|
||||
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb);
|
||||
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb);
|
||||
int8_t PQCLEAN_FRODOKEM1344SHAKE_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len);
|
||||
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector);
|
||||
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_clear_bytes(uint8_t *mem, size_t n);
|
||||
uint16_t PQCLEAN_FRODOKEM1344SHAKE_CLEAN_LE_TO_UINT16(uint16_t n);
|
||||
uint16_t PQCLEAN_FRODOKEM1344SHAKE_CLEAN_UINT16_TO_LE(uint16_t n);
|
||||
|
@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM1344SHAKE_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *c
|
||||
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);
|
||||
}
|
||||
// If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s)
|
||||
// Needs to avoid branching on secret data as per:
|
||||
// Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum
|
||||
// primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020.
|
||||
int8_t selector = PQCLEAN_FRODOKEM1344SHAKE_CLEAN_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM1344SHAKE_CLEAN_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR);
|
||||
// If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s)
|
||||
PQCLEAN_FRODOKEM1344SHAKE_CLEAN_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector);
|
||||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES);
|
||||
|
||||
// Cleanup:
|
||||
|
@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_unpack(uint16_t *out, size_t outlen, const
|
||||
}
|
||||
|
||||
|
||||
int8_t PQCLEAN_FRODOKEM1344SHAKE_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) {
|
||||
// Compare two arrays in constant time.
|
||||
// Returns 0 if the byte arrays are equal, -1 otherwise.
|
||||
uint16_t r = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r |= a[i] ^ b[i];
|
||||
}
|
||||
|
||||
r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1);
|
||||
return (int8_t)r;
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) {
|
||||
// Select one of the two input arrays to be moved to r
|
||||
// If (selector == 0) then load r with a, else if (selector == -1) load r with b
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r[i] = (~selector & a[i]) | (selector & b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM1344SHAKE_CLEAN_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.
|
||||
|
@ -12,6 +12,8 @@ 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);
|
||||
int8_t PQCLEAN_FRODOKEM1344SHAKE_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len);
|
||||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector);
|
||||
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);
|
||||
|
@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM1344SHAKE_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct,
|
||||
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);
|
||||
}
|
||||
// If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s)
|
||||
// Needs to avoid branching on secret data as per:
|
||||
// Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum
|
||||
// primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020.
|
||||
int8_t selector = PQCLEAN_FRODOKEM1344SHAKE_OPT_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM1344SHAKE_OPT_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR);
|
||||
// If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s)
|
||||
PQCLEAN_FRODOKEM1344SHAKE_OPT_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector);
|
||||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES);
|
||||
|
||||
// Cleanup:
|
||||
|
@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM1344SHAKE_OPT_unpack(uint16_t *out, size_t outlen, const ui
|
||||
}
|
||||
|
||||
|
||||
int8_t PQCLEAN_FRODOKEM1344SHAKE_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) {
|
||||
// Compare two arrays in constant time.
|
||||
// Returns 0 if the byte arrays are equal, -1 otherwise.
|
||||
uint16_t r = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r |= a[i] ^ b[i];
|
||||
}
|
||||
|
||||
r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1);
|
||||
return (int8_t)r;
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM1344SHAKE_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) {
|
||||
// Select one of the two input arrays to be moved to r
|
||||
// If (selector == 0) then load r with a, else if (selector == -1) load r with b
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r[i] = (~selector & a[i]) | (selector & b[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.
|
||||
|
@ -12,6 +12,8 @@ void PQCLEAN_FRODOKEM640AES_CLEAN_key_encode(uint16_t *out, const uint16_t *in);
|
||||
void PQCLEAN_FRODOKEM640AES_CLEAN_key_decode(uint16_t *out, const uint16_t *in);
|
||||
void PQCLEAN_FRODOKEM640AES_CLEAN_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb);
|
||||
void PQCLEAN_FRODOKEM640AES_CLEAN_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb);
|
||||
int8_t PQCLEAN_FRODOKEM640AES_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len);
|
||||
void PQCLEAN_FRODOKEM640AES_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector);
|
||||
void PQCLEAN_FRODOKEM640AES_CLEAN_clear_bytes(uint8_t *mem, size_t n);
|
||||
uint16_t PQCLEAN_FRODOKEM640AES_CLEAN_LE_TO_UINT16(uint16_t n);
|
||||
uint16_t PQCLEAN_FRODOKEM640AES_CLEAN_UINT16_TO_LE(uint16_t n);
|
||||
|
@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM640AES_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct,
|
||||
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);
|
||||
}
|
||||
// If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s)
|
||||
// Needs to avoid branching on secret data as per:
|
||||
// Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum
|
||||
// primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020.
|
||||
int8_t selector = PQCLEAN_FRODOKEM640AES_CLEAN_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM640AES_CLEAN_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR);
|
||||
// If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s)
|
||||
PQCLEAN_FRODOKEM640AES_CLEAN_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector);
|
||||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES);
|
||||
|
||||
// Cleanup:
|
||||
|
@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM640AES_CLEAN_unpack(uint16_t *out, size_t outlen, const uin
|
||||
}
|
||||
|
||||
|
||||
int8_t PQCLEAN_FRODOKEM640AES_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) {
|
||||
// Compare two arrays in constant time.
|
||||
// Returns 0 if the byte arrays are equal, -1 otherwise.
|
||||
uint16_t r = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r |= a[i] ^ b[i];
|
||||
}
|
||||
|
||||
r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1);
|
||||
return (int8_t)r;
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM640AES_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) {
|
||||
// Select one of the two input arrays to be moved to r
|
||||
// If (selector == 0) then load r with a, else if (selector == -1) load r with b
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r[i] = (~selector & a[i]) | (selector & b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM640AES_CLEAN_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.
|
||||
|
@ -12,6 +12,8 @@ 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);
|
||||
int8_t PQCLEAN_FRODOKEM640AES_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len);
|
||||
void PQCLEAN_FRODOKEM640AES_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector);
|
||||
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);
|
||||
|
@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM640AES_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, co
|
||||
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);
|
||||
}
|
||||
// If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s)
|
||||
// Needs to avoid branching on secret data as per:
|
||||
// Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum
|
||||
// primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020.
|
||||
int8_t selector = PQCLEAN_FRODOKEM640AES_OPT_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM640AES_OPT_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR);
|
||||
// If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s)
|
||||
PQCLEAN_FRODOKEM640AES_OPT_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector);
|
||||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES);
|
||||
|
||||
// Cleanup:
|
||||
|
@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM640AES_OPT_unpack(uint16_t *out, size_t outlen, const uint8
|
||||
}
|
||||
|
||||
|
||||
int8_t PQCLEAN_FRODOKEM640AES_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) {
|
||||
// Compare two arrays in constant time.
|
||||
// Returns 0 if the byte arrays are equal, -1 otherwise.
|
||||
uint16_t r = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r |= a[i] ^ b[i];
|
||||
}
|
||||
|
||||
r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1);
|
||||
return (int8_t)r;
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM640AES_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) {
|
||||
// Select one of the two input arrays to be moved to r
|
||||
// If (selector == 0) then load r with a, else if (selector == -1) load r with b
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r[i] = (~selector & a[i]) | (selector & b[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.
|
||||
|
@ -12,6 +12,8 @@ void PQCLEAN_FRODOKEM640SHAKE_CLEAN_key_encode(uint16_t *out, const uint16_t *in
|
||||
void PQCLEAN_FRODOKEM640SHAKE_CLEAN_key_decode(uint16_t *out, const uint16_t *in);
|
||||
void PQCLEAN_FRODOKEM640SHAKE_CLEAN_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb);
|
||||
void PQCLEAN_FRODOKEM640SHAKE_CLEAN_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb);
|
||||
int8_t PQCLEAN_FRODOKEM640SHAKE_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len);
|
||||
void PQCLEAN_FRODOKEM640SHAKE_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector);
|
||||
void PQCLEAN_FRODOKEM640SHAKE_CLEAN_clear_bytes(uint8_t *mem, size_t n);
|
||||
uint16_t PQCLEAN_FRODOKEM640SHAKE_CLEAN_LE_TO_UINT16(uint16_t n);
|
||||
uint16_t PQCLEAN_FRODOKEM640SHAKE_CLEAN_UINT16_TO_LE(uint16_t n);
|
||||
|
@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM640SHAKE_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct
|
||||
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);
|
||||
}
|
||||
// If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s)
|
||||
// Needs to avoid branching on secret data as per:
|
||||
// Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum
|
||||
// primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020.
|
||||
int8_t selector = PQCLEAN_FRODOKEM640SHAKE_CLEAN_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM640SHAKE_CLEAN_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR);
|
||||
// If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s)
|
||||
PQCLEAN_FRODOKEM640SHAKE_CLEAN_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector);
|
||||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES);
|
||||
|
||||
// Cleanup:
|
||||
|
@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM640SHAKE_CLEAN_unpack(uint16_t *out, size_t outlen, const u
|
||||
}
|
||||
|
||||
|
||||
int8_t PQCLEAN_FRODOKEM640SHAKE_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) {
|
||||
// Compare two arrays in constant time.
|
||||
// Returns 0 if the byte arrays are equal, -1 otherwise.
|
||||
uint16_t r = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r |= a[i] ^ b[i];
|
||||
}
|
||||
|
||||
r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1);
|
||||
return (int8_t)r;
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM640SHAKE_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) {
|
||||
// Select one of the two input arrays to be moved to r
|
||||
// If (selector == 0) then load r with a, else if (selector == -1) load r with b
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r[i] = (~selector & a[i]) | (selector & b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM640SHAKE_CLEAN_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.
|
||||
|
@ -12,6 +12,8 @@ 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);
|
||||
int8_t PQCLEAN_FRODOKEM640SHAKE_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len);
|
||||
void PQCLEAN_FRODOKEM640SHAKE_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector);
|
||||
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);
|
||||
|
@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM640SHAKE_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct,
|
||||
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);
|
||||
}
|
||||
// If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s)
|
||||
// Needs to avoid branching on secret data as per:
|
||||
// Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum
|
||||
// primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020.
|
||||
int8_t selector = PQCLEAN_FRODOKEM640SHAKE_OPT_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM640SHAKE_OPT_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR);
|
||||
// If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s)
|
||||
PQCLEAN_FRODOKEM640SHAKE_OPT_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector);
|
||||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES);
|
||||
|
||||
// Cleanup:
|
||||
|
@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM640SHAKE_OPT_unpack(uint16_t *out, size_t outlen, const uin
|
||||
}
|
||||
|
||||
|
||||
int8_t PQCLEAN_FRODOKEM640SHAKE_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) {
|
||||
// Compare two arrays in constant time.
|
||||
// Returns 0 if the byte arrays are equal, -1 otherwise.
|
||||
uint16_t r = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r |= a[i] ^ b[i];
|
||||
}
|
||||
|
||||
r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1);
|
||||
return (int8_t)r;
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM640SHAKE_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) {
|
||||
// Select one of the two input arrays to be moved to r
|
||||
// If (selector == 0) then load r with a, else if (selector == -1) load r with b
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r[i] = (~selector & a[i]) | (selector & b[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.
|
||||
|
@ -12,6 +12,8 @@ void PQCLEAN_FRODOKEM976AES_CLEAN_key_encode(uint16_t *out, const uint16_t *in);
|
||||
void PQCLEAN_FRODOKEM976AES_CLEAN_key_decode(uint16_t *out, const uint16_t *in);
|
||||
void PQCLEAN_FRODOKEM976AES_CLEAN_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb);
|
||||
void PQCLEAN_FRODOKEM976AES_CLEAN_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb);
|
||||
int8_t PQCLEAN_FRODOKEM976AES_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len);
|
||||
void PQCLEAN_FRODOKEM976AES_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector);
|
||||
void PQCLEAN_FRODOKEM976AES_CLEAN_clear_bytes(uint8_t *mem, size_t n);
|
||||
uint16_t PQCLEAN_FRODOKEM976AES_CLEAN_LE_TO_UINT16(uint16_t n);
|
||||
uint16_t PQCLEAN_FRODOKEM976AES_CLEAN_UINT16_TO_LE(uint16_t n);
|
||||
|
@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM976AES_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct,
|
||||
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);
|
||||
}
|
||||
// If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s)
|
||||
// Needs to avoid branching on secret data as per:
|
||||
// Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum
|
||||
// primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020.
|
||||
int8_t selector = PQCLEAN_FRODOKEM976AES_CLEAN_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM976AES_CLEAN_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR);
|
||||
// If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s)
|
||||
PQCLEAN_FRODOKEM976AES_CLEAN_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector);
|
||||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES);
|
||||
|
||||
// Cleanup:
|
||||
|
@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM976AES_CLEAN_unpack(uint16_t *out, size_t outlen, const uin
|
||||
}
|
||||
|
||||
|
||||
int8_t PQCLEAN_FRODOKEM976AES_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) {
|
||||
// Compare two arrays in constant time.
|
||||
// Returns 0 if the byte arrays are equal, -1 otherwise.
|
||||
uint16_t r = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r |= a[i] ^ b[i];
|
||||
}
|
||||
|
||||
r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1);
|
||||
return (int8_t)r;
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM976AES_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) {
|
||||
// Select one of the two input arrays to be moved to r
|
||||
// If (selector == 0) then load r with a, else if (selector == -1) load r with b
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r[i] = (~selector & a[i]) | (selector & b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM976AES_CLEAN_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.
|
||||
|
@ -12,6 +12,8 @@ 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);
|
||||
int8_t PQCLEAN_FRODOKEM976AES_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len);
|
||||
void PQCLEAN_FRODOKEM976AES_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector);
|
||||
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);
|
||||
|
@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM976AES_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, co
|
||||
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);
|
||||
}
|
||||
// If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s)
|
||||
// Needs to avoid branching on secret data as per:
|
||||
// Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum
|
||||
// primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020.
|
||||
int8_t selector = PQCLEAN_FRODOKEM976AES_OPT_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM976AES_OPT_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR);
|
||||
// If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s)
|
||||
PQCLEAN_FRODOKEM976AES_OPT_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector);
|
||||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES);
|
||||
|
||||
// Cleanup:
|
||||
|
@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM976AES_OPT_unpack(uint16_t *out, size_t outlen, const uint8
|
||||
}
|
||||
|
||||
|
||||
int8_t PQCLEAN_FRODOKEM976AES_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) {
|
||||
// Compare two arrays in constant time.
|
||||
// Returns 0 if the byte arrays are equal, -1 otherwise.
|
||||
uint16_t r = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r |= a[i] ^ b[i];
|
||||
}
|
||||
|
||||
r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1);
|
||||
return (int8_t)r;
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM976AES_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) {
|
||||
// Select one of the two input arrays to be moved to r
|
||||
// If (selector == 0) then load r with a, else if (selector == -1) load r with b
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r[i] = (~selector & a[i]) | (selector & b[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.
|
||||
|
@ -12,6 +12,8 @@ void PQCLEAN_FRODOKEM976SHAKE_CLEAN_key_encode(uint16_t *out, const uint16_t *in
|
||||
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_key_decode(uint16_t *out, const uint16_t *in);
|
||||
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_pack(uint8_t *out, size_t outlen, const uint16_t *in, size_t inlen, uint8_t lsb);
|
||||
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_unpack(uint16_t *out, size_t outlen, const uint8_t *in, size_t inlen, uint8_t lsb);
|
||||
int8_t PQCLEAN_FRODOKEM976SHAKE_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len);
|
||||
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector);
|
||||
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_clear_bytes(uint8_t *mem, size_t n);
|
||||
uint16_t PQCLEAN_FRODOKEM976SHAKE_CLEAN_LE_TO_UINT16(uint16_t n);
|
||||
uint16_t PQCLEAN_FRODOKEM976SHAKE_CLEAN_UINT16_TO_LE(uint16_t n);
|
||||
|
@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM976SHAKE_CLEAN_crypto_kem_dec(uint8_t *ss, const uint8_t *ct
|
||||
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);
|
||||
}
|
||||
// If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s)
|
||||
// Needs to avoid branching on secret data as per:
|
||||
// Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum
|
||||
// primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020.
|
||||
int8_t selector = PQCLEAN_FRODOKEM976SHAKE_CLEAN_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM976SHAKE_CLEAN_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR);
|
||||
// If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s)
|
||||
PQCLEAN_FRODOKEM976SHAKE_CLEAN_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector);
|
||||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES);
|
||||
|
||||
// Cleanup:
|
||||
|
@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM976SHAKE_CLEAN_unpack(uint16_t *out, size_t outlen, const u
|
||||
}
|
||||
|
||||
|
||||
int8_t PQCLEAN_FRODOKEM976SHAKE_CLEAN_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) {
|
||||
// Compare two arrays in constant time.
|
||||
// Returns 0 if the byte arrays are equal, -1 otherwise.
|
||||
uint16_t r = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r |= a[i] ^ b[i];
|
||||
}
|
||||
|
||||
r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1);
|
||||
return (int8_t)r;
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) {
|
||||
// Select one of the two input arrays to be moved to r
|
||||
// If (selector == 0) then load r with a, else if (selector == -1) load r with b
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r[i] = (~selector & a[i]) | (selector & b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM976SHAKE_CLEAN_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.
|
||||
|
@ -12,6 +12,8 @@ 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);
|
||||
int8_t PQCLEAN_FRODOKEM976SHAKE_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len);
|
||||
void PQCLEAN_FRODOKEM976SHAKE_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector);
|
||||
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);
|
||||
|
@ -214,14 +214,13 @@ int PQCLEAN_FRODOKEM976SHAKE_OPT_crypto_kem_dec(uint8_t *ss, const uint8_t *ct,
|
||||
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);
|
||||
}
|
||||
// If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s)
|
||||
// Needs to avoid branching on secret data as per:
|
||||
// Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum
|
||||
// primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020.
|
||||
int8_t selector = PQCLEAN_FRODOKEM976SHAKE_OPT_ct_verify(Bp, BBp, PARAMS_N * PARAMS_NBAR) | PQCLEAN_FRODOKEM976SHAKE_OPT_ct_verify(C, CC, PARAMS_NBAR * PARAMS_NBAR);
|
||||
// If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == -1) load s to do ss = F(ct || s)
|
||||
PQCLEAN_FRODOKEM976SHAKE_OPT_ct_select((uint8_t *)Fin_k, (uint8_t *)kprime, (uint8_t *)sk_s, CRYPTO_BYTES, selector);
|
||||
shake(ss, CRYPTO_BYTES, Fin, CRYPTO_CIPHERTEXTBYTES + CRYPTO_BYTES);
|
||||
|
||||
// Cleanup:
|
||||
|
@ -224,6 +224,30 @@ void PQCLEAN_FRODOKEM976SHAKE_OPT_unpack(uint16_t *out, size_t outlen, const uin
|
||||
}
|
||||
|
||||
|
||||
int8_t PQCLEAN_FRODOKEM976SHAKE_OPT_ct_verify(const uint16_t *a, const uint16_t *b, size_t len) {
|
||||
// Compare two arrays in constant time.
|
||||
// Returns 0 if the byte arrays are equal, -1 otherwise.
|
||||
uint16_t r = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r |= a[i] ^ b[i];
|
||||
}
|
||||
|
||||
r = (-(int16_t)r) >> (8 * sizeof(uint16_t) -1);
|
||||
return (int8_t)r;
|
||||
}
|
||||
|
||||
|
||||
void PQCLEAN_FRODOKEM976SHAKE_OPT_ct_select(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len, int8_t selector) {
|
||||
// Select one of the two input arrays to be moved to r
|
||||
// If (selector == 0) then load r with a, else if (selector == -1) load r with b
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
r[i] = (~selector & a[i]) | (selector & b[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.
|
||||
|
載入中…
新增問題並參考
Block a user