packages feed

KpqC-0.1.0: cbits/third_party/NTRUplus/NTRU+768/kem.c

#include "util.h"
#include <stddef.h>
#include <stdint.h>
#include "api.h"
#include "params.h"
#include "symmetric.h"
#include "poly.h"
#include "fips202/fips202.h"
#include "randombytes.h"

#ifdef NTRUPLUS_SUPERCOP
#include "crypto_declassify.h"
#define ntruplus_declassify crypto_declassify
#else
#define ntruplus_declassify(x, xlen) ((void)(x), (void)(xlen))
#endif

/*************************************************
* Name:        verify
*
* Description: Compares two byte arrays for equality in constant time.
*
* Arguments:   - const uint8_t *a: first byte array
*              - const uint8_t *b: second byte array
*              - size_t len:      length of the arrays
*
* Returns 0 if the arrays are equal, 1 otherwise.
**************************************************/
static inline int verify(const uint8_t *a, const uint8_t *b, size_t len)
{
    uint8_t acc = 0;

    for (size_t i = 0; i < len; i++)
        acc |= (uint8_t)(a[i] ^ b[i]);

    return (-(uint64_t)acc) >> 63;
}

/*************************************************
* Name:        genf_derand
*
* Description: Deterministically generates a secret polynomial f and its
*              multiplicative inverse finv in the NTT domain.
*
* Arguments:   - poly *f:     output polynomial f (NTT domain)
*              - poly *finv:  output multiplicative inverse of f
*                              in the NTT domain
*              - uint8_t *buf: workspace of NTRUPLUS_N/4 bytes
*              - const uint8_t *coins: 32-byte deterministic seed
*
* Returns 0 on success; non-zero if f is not invertible in the NTT domain.
**************************************************/
static inline int genf_derand(poly *f, poly *finv,
                              uint8_t buf[NTRUPLUS_N / 4],
                              const uint8_t *coins)
{
    shake256(buf, NTRUPLUS_N / 4, coins, 32);

    poly_cbd1(f, buf);
    poly_triple(f);
    f->coeffs[0] += 1;

    poly_ntt(f);

    return poly_baseinv(finv, f);
}

/*************************************************
* Name:        geng_derand
*
* Description: Deterministically generates a secret polynomial g and its
*              multiplicative inverse ginv in the NTT domain.
*
* Arguments:   - poly *g:      output polynomial g (NTT domain)
*              - poly *ginv:   output multiplicative inverse of g
*                               in the NTT domain
*              - uint8_t *buf: workspace of NTRUPLUS_N/4 bytes
*              - const uint8_t *coins: 32-byte deterministic seed
*
* Returns 0 on success; non-zero if g is not invertible in the NTT domain.
**************************************************/
static inline int geng_derand(poly *g, poly *ginv,
                              uint8_t buf[NTRUPLUS_N / 4],
                              const uint8_t *coins)
{
    shake256(buf, NTRUPLUS_N / 4, coins, 32);

    poly_cbd1(g, buf);
    poly_triple(g);

    poly_ntt(g);

    return poly_baseinv(ginv, g);
}

/*************************************************
* Name:        crypto_kem_keypair_derand
*
* Description: Computes the deterministic public and secret key pair from
*              the secret polynomials f and g and their multiplicative
*              inverses finv and ginv in the NTT domain.
*
* Arguments:   - uint8_t *pk:   output public key
*              - uint8_t *sk:   output secret key
*              - const poly *f:     secret polynomial f (NTT domain)
*              - const poly *finv:  multiplicative inverse of f (NTT domain)
*              - const poly *g:     secret polynomial g (NTT domain)
*              - const poly *ginv:  multiplicative inverse of g (NTT domain)
*
* Returns:     void
**************************************************/
static inline void crypto_kem_keypair_derand(uint8_t *pk, uint8_t *sk,
                                             const poly *f,  const poly *finv,
                                             const poly *g,  const poly *ginv)
{
    poly h, hinv;

    poly_basemul(&h, g, finv);
    poly_basemul(&hinv, f, ginv);

    poly_tobytes(pk, &h);
    poly_tobytes(sk, f);
    poly_tobytes(sk + NTRUPLUS_POLYBYTES, &hinv);
    hash_f(sk + 2 * NTRUPLUS_POLYBYTES, pk);

    secure_clear(&h, sizeof h);
    secure_clear(&hinv, sizeof hinv);
}

/*************************************************
* Name:        crypto_kem_keypair
*
* Description: Generates an NTRU+ public/secret key pair for the
*              CCA-secure key encapsulation mechanism. Secret
*              polynomials f and g and their NTT-domain inverses
*              are sampled internally using fresh randomness.
*
* Arguments:   - uint8_t *pk: output public key
*                (array of CRYPTO_PUBLICKEYBYTES bytes)
*              - uint8_t *sk: output secret key
*                (array of CRYPTO_SECRETKEYBYTES bytes)
*
* Returns 0 on success.
**************************************************/
int crypto_kem_keypair(unsigned char *pk, unsigned char *sk)
{
    uint8_t coins[NTRUPLUS_SYMBYTES];
    uint8_t buf[NTRUPLUS_N / 4];
    int r;
    int status = -1;

    poly f, finv;
    poly g, ginv;

    do {
        if (randombytes(coins, sizeof coins) != 0) goto cleanup;
        r = genf_derand(&f, &finv, buf, coins);
        ntruplus_declassify(&r, sizeof r);
    } while (r);

    do {
        if (randombytes(coins, sizeof coins) != 0) goto cleanup;
        r = geng_derand(&g, &ginv, buf, coins);
        ntruplus_declassify(&r, sizeof r);
    } while (r);

    crypto_kem_keypair_derand(pk, sk, &f, &finv, &g, &ginv);
    status = 0;

cleanup:
    secure_clear(coins, sizeof coins);
    secure_clear(buf, sizeof buf);
    secure_clear(&f, sizeof f);
    secure_clear(&finv, sizeof finv);
    secure_clear(&g, sizeof g);
    secure_clear(&ginv, sizeof ginv);
    return status;
}

/*************************************************
* Name:        crypto_kem_enc_derand
*
* Description: Deterministically performs NTRU+ KEM encapsulation using
*              the public key pk and the supplied randomness coins,
*              producing a ciphertext ct and shared secret ss.
*
* Arguments:   - uint8_t *ct:      output ciphertext
*              - uint8_t *ss:      output shared secret
*              - const uint8_t *pk:    input public key
*              - const uint8_t *coins: input randomness for
*                                      deterministic encapsulation
*
* Returns 0 on success. If pk contains a non-canonical coefficient, ct and ss
* are set to all zero bytes and 1 is returned.
**************************************************/
static inline int crypto_kem_enc_derand(uint8_t *ct, uint8_t *ss,
                                        const uint8_t *pk,
                                        const uint8_t *coins)
{
	uint8_t msg[HASH_H_INBYTES];
	uint8_t buf1[HASH_H_OUTBYTES];
	uint8_t buf2[HASH_G_INBYTES];
    
    poly c, h, r, m;

    if(poly_frombytes(&h, pk))
    {
        for (size_t i = 0; i < NTRUPLUS_CIPHERTEXTBYTES; i++)
            ct[i] = 0;
        secure_clear(ss, NTRUPLUS_SSBYTES);

        return 1;
    }
    
    for (size_t i = 0; i < NTRUPLUS_N / 8; i++)
        msg[i] = coins[i];

    hash_f(msg + NTRUPLUS_N / 8, pk);
    hash_h(buf1, msg);
    
    poly_cbd1(&r, buf1 + NTRUPLUS_SYMBYTES);
    poly_ntt(&r);
    
    poly_tobytes(buf2, &r);
    hash_g(buf2, buf2);
    poly_sotp_encode(&m, msg, buf2);
    poly_ntt(&m);
    
    poly_basemul_add(&c, &h, &r, &m);
    poly_tobytes(ct, &c);
    
    for (size_t i = 0; i < NTRUPLUS_SSBYTES; i++)
        ss[i] = buf1[i];

    secure_clear(msg, sizeof msg);
    secure_clear(buf1, sizeof buf1);
    secure_clear(buf2, sizeof buf2);
    secure_clear(&r, sizeof r);
    secure_clear(&m, sizeof m);
    
    return 0;
}

/*************************************************
* Name:        crypto_kem_enc
*
* Description: Generates an NTRU+ KEM ciphertext ct and shared secret ss
*              using the public key pk. Fresh randomness is internally
*              sampled and used for encapsulation.
*
* Arguments:   - uint8_t *ct: output ciphertext
*                (array of CRYPTO_CIPHERTEXTBYTES bytes)
*              - uint8_t *ss: output shared secret
*                (array of CRYPTO_BYTES bytes)
*              - const uint8_t *pk: input public key
*                (array of CRYPTO_PUBLICKEYBYTES bytes)
*
* Returns 0 on success. If pk contains a non-canonical coefficient, ct and ss
* are set to all zero bytes and 1 is returned.
**************************************************/
int crypto_kem_enc(unsigned char *ct, unsigned char *ss,
                   const unsigned char *pk)
{
    uint8_t coins[NTRUPLUS_N / 8];
    int ret;

    if (randombytes(coins, sizeof coins) != 0) {
        secure_clear(coins, sizeof coins);
        return -1;
    }
    ret = crypto_kem_enc_derand(ct, ss, pk, coins);
    secure_clear(coins, sizeof coins);
    return ret;
}

/*************************************************
* Name:        crypto_kem_dec
*
* Description: Performs NTRU+ KEM decapsulation. Given a ciphertext ct
*              and a secret key sk, computes the shared secret ss. If
*              decapsulation fails, ss is set to all zero bytes.
*
* Arguments:   - uint8_t *ss:  output shared secret
*                (array of CRYPTO_BYTES bytes)
*              - const uint8_t *ct: input ciphertext
*                (array of CRYPTO_CIPHERTEXTBYTES bytes)
*              - const uint8_t *sk: input secret key
*                (array of CRYPTO_SECRETKEYBYTES bytes)
*
* Returns 0 on success, 1 on failure.
**************************************************/
int crypto_kem_dec(unsigned char *ss, const unsigned char *ct,
                   const unsigned char *sk)
{
	uint8_t msg[HASH_H_INBYTES];
	uint8_t buf1[HASH_G_INBYTES];
	uint8_t buf2[HASH_G_INBYTES];
	uint8_t buf3[HASH_H_OUTBYTES];

    int8_t fail = 1;
    
    poly c, f, hinv;
    poly r1, r2;
    poly m1, m2;

    if(poly_frombytes(&c, ct) ||
       poly_frombytes(&f, sk) ||
       poly_frombytes(&hinv, sk + NTRUPLUS_POLYBYTES))
    {
        secure_clear(ss, NTRUPLUS_SSBYTES);

        goto cleanup;
    }
    
    poly_basemul(&m1, &c, &f);
    poly_invntt(&m1);
    poly_crepmod3(&m1);
    
    m2 = m1;
    poly_ntt(&m2);
    poly_sub(&c, &c, &m2);
    poly_basemul(&r2, &c, &hinv);

    poly_tobytes(buf1, &r2);
    hash_g(buf2, buf1);
    fail = poly_sotp_decode(msg, &m1, buf2);
    
    for (size_t i = 0; i < HASH_F_OUTBYTES; i++)
        msg[i + NTRUPLUS_N / 8] = sk[i + 2 * NTRUPLUS_POLYBYTES];
    
    hash_h(buf3, msg);
    
    poly_cbd1(&r1, buf3 + NTRUPLUS_SSBYTES);
    poly_ntt(&r1);
    poly_tobytes(buf2, &r1);
    
    fail |= verify(buf1, buf2, NTRUPLUS_POLYBYTES);
    
    for (size_t i = 0; i < NTRUPLUS_SSBYTES; i++)
        ss[i] = buf3[i] & ~(-fail);

cleanup:
    secure_clear(msg, sizeof msg);
    secure_clear(buf1, sizeof buf1);
    secure_clear(buf2, sizeof buf2);
    secure_clear(buf3, sizeof buf3);
    secure_clear(&c, sizeof c);
    secure_clear(&f, sizeof f);
    secure_clear(&hinv, sizeof hinv);
    secure_clear(&r1, sizeof r1);
    secure_clear(&r2, sizeof r2);
    secure_clear(&m1, sizeof m1);
    secure_clear(&m2, sizeof m2);

    return fail;
}