Completed integration of ntru_crypto library into ntru plugin
This commit is contained in:
@@ -16,10 +16,12 @@
|
||||
*/
|
||||
|
||||
#include "ntru_public_key.h"
|
||||
|
||||
#include "ntru_crypto/ntru_crypto_ntru_convert.h"
|
||||
#include "ntru_trits.h"
|
||||
#include "ntru_poly.h"
|
||||
#include "ntru_convert.h"
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <utils/test.h>
|
||||
|
||||
typedef struct private_ntru_public_key_t private_ntru_public_key_t;
|
||||
|
||||
@@ -47,37 +49,268 @@ struct private_ntru_public_key_t {
|
||||
*/
|
||||
chunk_t encoding;
|
||||
|
||||
/**
|
||||
* Deterministic Random Bit Generator
|
||||
*/
|
||||
ntru_drbg_t *drbg;
|
||||
|
||||
};
|
||||
|
||||
METHOD(ntru_public_key_t, get_id, ntru_param_set_id_t,
|
||||
private_ntru_public_key_t *this)
|
||||
{
|
||||
return this->params->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate NTRU encryption public key encoding
|
||||
*/
|
||||
static void generate_encoding(private_ntru_public_key_t *this)
|
||||
{
|
||||
size_t pubkey_len;
|
||||
u_char *enc;
|
||||
|
||||
/* compute public key length encoded as packed coefficients */
|
||||
pubkey_len = (this->params->N * this->params->q_bits + 7) / 8;
|
||||
|
||||
/* allocate memory for public key encoding */
|
||||
this->encoding = chunk_alloc(2 + NTRU_OID_LEN + pubkey_len);
|
||||
enc = this->encoding.ptr;
|
||||
|
||||
/* format header and packed public key */
|
||||
*enc++ = NTRU_PUBKEY_TAG;
|
||||
*enc++ = NTRU_OID_LEN;
|
||||
memcpy(enc, this->params->oid, NTRU_OID_LEN);
|
||||
enc += NTRU_OID_LEN;
|
||||
ntru_elements_2_octets(this->params->N, this->pubkey,
|
||||
this->params->q_bits, enc);
|
||||
}
|
||||
|
||||
METHOD(ntru_public_key_t, get_encoding, chunk_t,
|
||||
private_ntru_public_key_t *this)
|
||||
{
|
||||
if (!this->encoding.len)
|
||||
{
|
||||
size_t pubkey_len;
|
||||
u_char *enc;
|
||||
|
||||
/* compute public key length encoded as packed coefficients */
|
||||
pubkey_len = (this->params->N * this->params->q_bits + 7) / 8;
|
||||
|
||||
/* allocate memory for public key encoding */
|
||||
this->encoding = chunk_alloc(2 + NTRU_OID_LEN + pubkey_len);
|
||||
enc = this->encoding.ptr;
|
||||
|
||||
/* format header and packed public key */
|
||||
*enc++ = NTRU_PUBKEY_TAG;
|
||||
*enc++ = NTRU_OID_LEN;
|
||||
memcpy(enc, this->params->oid, NTRU_OID_LEN);
|
||||
enc += NTRU_OID_LEN;
|
||||
ntru_elements_2_octets(this->params->N, this->pubkey,
|
||||
this->params->q_bits, enc);
|
||||
}
|
||||
return this->encoding;
|
||||
}
|
||||
|
||||
#define MAX_SEC_STRENGTH_LEN 32 /* bytes */
|
||||
|
||||
/**
|
||||
* Shared with ntru_private_key.c
|
||||
*/
|
||||
extern bool ntru_check_min_weight(uint16_t N, uint8_t *t, uint16_t min_wt);
|
||||
|
||||
METHOD(ntru_public_key_t, encrypt, bool,
|
||||
private_ntru_public_key_t *this, chunk_t plaintext, chunk_t *ciphertext)
|
||||
{
|
||||
hash_algorithm_t hash_algid;
|
||||
size_t t_len, seed1_len, seed2_len;
|
||||
uint16_t *t1, *t = NULL;
|
||||
uint8_t b[MAX_SEC_STRENGTH_LEN];
|
||||
uint8_t *t2, *Mtrin, *M, *mask_trits, *ptr;
|
||||
uint16_t mod_q_mask, mprime_len = 0;
|
||||
int16_t m1;
|
||||
chunk_t seed = chunk_empty;
|
||||
ntru_trits_t *mask;
|
||||
ntru_poly_t *r_poly;
|
||||
bool msg_rep_good, success = FALSE;
|
||||
int i;
|
||||
|
||||
*ciphertext = chunk_empty;
|
||||
|
||||
if (plaintext.len > this->params->m_len_max)
|
||||
{
|
||||
DBG1(DBG_LIB, "plaintext exceeds maximum size");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (this->params->sec_strength_len > MAX_SEC_STRENGTH_LEN)
|
||||
{
|
||||
DBG1(DBG_LIB, "required security strength exceeds %d bits",
|
||||
MAX_SEC_STRENGTH_LEN * BITS_PER_BYTE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* allocate temporary array t */
|
||||
t_len = (sizeof(uint16_t) + 3*sizeof(uint8_t)) * this->params->N;
|
||||
t = malloc(t_len);
|
||||
t1 = t;
|
||||
t2 = (uint8_t *)(t1 + this->params->N);
|
||||
Mtrin = t2 + this->params->N;
|
||||
M = Mtrin + this->params->N;
|
||||
|
||||
/* set hash algorithm based on security strength */
|
||||
hash_algid = (this->params->sec_strength_len <= 20) ? HASH_SHA1 :
|
||||
HASH_SHA256;
|
||||
/* set constants */
|
||||
mod_q_mask = this->params->q - 1;
|
||||
|
||||
/* allocate memory for the larger of the two seeds */
|
||||
seed1_len = (this->params->N + 3)/4;
|
||||
seed2_len = 3 + 2*this->params->sec_strength_len + plaintext.len;
|
||||
seed = chunk_alloc(max(seed1_len, seed2_len));
|
||||
|
||||
/* loop until a message representative with proper weight is achieved */
|
||||
do
|
||||
{
|
||||
if (!this->drbg->generate(this->drbg,
|
||||
this->params->sec_strength_len * BITS_PER_BYTE,
|
||||
this->params->sec_strength_len, b))
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* form sData (OID || m || b || hTrunc) */
|
||||
ptr = seed.ptr;
|
||||
memcpy(ptr, this->params->oid, NTRU_OID_LEN);
|
||||
ptr += NTRU_OID_LEN;
|
||||
memcpy(ptr, plaintext.ptr, plaintext.len);
|
||||
ptr += plaintext.len;
|
||||
memcpy(ptr, b, this->params->sec_strength_len);
|
||||
ptr += this->params->sec_strength_len;
|
||||
memcpy(ptr, this->encoding.ptr + 2 + NTRU_OID_LEN,
|
||||
this->params->sec_strength_len);
|
||||
ptr += this->params->sec_strength_len;
|
||||
seed.len = seed2_len;
|
||||
|
||||
DBG2(DBG_LIB, "generate polynomial r");
|
||||
r_poly = ntru_poly_create_from_seed(hash_algid, seed, this->params->c_bits,
|
||||
this->params->N, this->params->q,
|
||||
this->params->dF_r, this->params->dF_r,
|
||||
this->params->is_product_form);
|
||||
if (!r_poly)
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* form R = h * r */
|
||||
r_poly->ring_mult(r_poly, this->pubkey, t1);
|
||||
r_poly->destroy(r_poly);
|
||||
|
||||
/* form R mod 4 */
|
||||
ntru_coeffs_mod4_2_octets(this->params->N, t1, seed.ptr);
|
||||
seed.len = seed1_len;
|
||||
|
||||
/* form mask */
|
||||
mask = ntru_trits_create(this->params->N, hash_algid, seed);
|
||||
if (!mask)
|
||||
{
|
||||
DBG1(DBG_LIB, "mask creation failed");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* form the padded message M */
|
||||
ptr = M;
|
||||
memcpy(ptr, b, this->params->sec_strength_len);
|
||||
ptr += this->params->sec_strength_len;
|
||||
if (this->params->m_len_len == 2)
|
||||
{
|
||||
*ptr++ = (uint8_t)((plaintext.len >> 8) & 0xff);
|
||||
}
|
||||
*ptr++ = (uint8_t)(plaintext.len & 0xff);
|
||||
memcpy(ptr, plaintext.ptr, plaintext.len);
|
||||
ptr += plaintext.len;
|
||||
|
||||
/* add an extra zero byte in case without it the bit string
|
||||
* is not a multiple of 3 bits and therefore might not be
|
||||
* able to produce enough trits
|
||||
*/
|
||||
memset(ptr, 0, this->params->m_len_max - plaintext.len + 2);
|
||||
|
||||
/* convert M to trits (Mbin to Mtrin) */
|
||||
mprime_len = this->params->N;
|
||||
if (this->params->is_product_form)
|
||||
{
|
||||
--mprime_len;
|
||||
}
|
||||
ntru_bits_2_trits(M, mprime_len, Mtrin);
|
||||
mask_trits = mask->get_trits(mask);
|
||||
|
||||
|
||||
/* form the msg representative m' by adding Mtrin to mask, mod p */
|
||||
if (this->params->is_product_form)
|
||||
{
|
||||
m1 = 0;
|
||||
for (i = 0; i < mprime_len; i++)
|
||||
{
|
||||
t2[i] = mask_trits[i] + Mtrin[i];
|
||||
if (t2[i] >= 3)
|
||||
{
|
||||
t2[i] -= 3;
|
||||
}
|
||||
if (t2[i] == 1)
|
||||
{
|
||||
++m1;
|
||||
}
|
||||
else if (t2[i] == 2)
|
||||
{
|
||||
--m1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < mprime_len; i++)
|
||||
{
|
||||
t2[i] = mask_trits[i] + Mtrin[i];
|
||||
if (t2[i] >= 3)
|
||||
{
|
||||
t2[i] -= 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
mask->destroy(mask);
|
||||
|
||||
/* check that message representative meets minimum weight
|
||||
* requirements
|
||||
*/
|
||||
if (this->params->is_product_form)
|
||||
{
|
||||
msg_rep_good = (abs(m1) <= this->params->min_msg_rep_wt);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg_rep_good = ntru_check_min_weight(mprime_len, t2,
|
||||
this->params->min_msg_rep_wt);
|
||||
}
|
||||
}
|
||||
while (!msg_rep_good);
|
||||
|
||||
/* form ciphertext e by adding m' to R mod q */
|
||||
for (i = 0; i < mprime_len; i++)
|
||||
{
|
||||
if (t2[i] == 1)
|
||||
{
|
||||
t1[i] = (t1[i] + 1) & mod_q_mask;
|
||||
}
|
||||
else if (t2[i] == 2)
|
||||
{
|
||||
t1[i] = (t1[i] - 1) & mod_q_mask;
|
||||
}
|
||||
}
|
||||
if (this->params->is_product_form)
|
||||
{
|
||||
t1[i] = (t1[i] - m1) & mod_q_mask;
|
||||
}
|
||||
|
||||
/* pack ciphertext */
|
||||
*ciphertext = chunk_alloc((this->params->N * this->params->q_bits + 7) / 8);
|
||||
ntru_elements_2_octets(this->params->N, t1, this->params->q_bits,
|
||||
ciphertext->ptr);
|
||||
|
||||
memwipe(t, t_len);
|
||||
success = TRUE;
|
||||
|
||||
err:
|
||||
/* cleanup */
|
||||
chunk_clear(&seed);
|
||||
free(t);
|
||||
|
||||
return success;
|
||||
}
|
||||
METHOD(ntru_public_key_t, destroy, void,
|
||||
private_ntru_public_key_t *this)
|
||||
{
|
||||
this->drbg->destroy(this->drbg);
|
||||
chunk_clear(&this->encoding);
|
||||
free(this->pubkey);
|
||||
free(this);
|
||||
@@ -86,7 +319,8 @@ METHOD(ntru_public_key_t, destroy, void,
|
||||
/*
|
||||
* Described in header.
|
||||
*/
|
||||
ntru_public_key_t *ntru_public_key_create(ntru_param_set_t *params,
|
||||
ntru_public_key_t *ntru_public_key_create(ntru_drbg_t *drbg,
|
||||
ntru_param_set_t *params,
|
||||
uint16_t *pubkey)
|
||||
{
|
||||
private_ntru_public_key_t *this;
|
||||
@@ -94,11 +328,14 @@ ntru_public_key_t *ntru_public_key_create(ntru_param_set_t *params,
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.get_id = _get_id,
|
||||
.get_encoding = _get_encoding,
|
||||
.encrypt = _encrypt,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.params = params,
|
||||
.pubkey = malloc(params->N * sizeof(uint16_t)),
|
||||
.drbg = drbg->get_ref(drbg),
|
||||
);
|
||||
|
||||
for (i = 0; i < params->N; i++)
|
||||
@@ -106,5 +343,66 @@ ntru_public_key_t *ntru_public_key_create(ntru_param_set_t *params,
|
||||
this->pubkey[i] = pubkey[i];
|
||||
}
|
||||
|
||||
/* generate public key encoding */
|
||||
generate_encoding(this);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header.
|
||||
*/
|
||||
ntru_public_key_t *ntru_public_key_create_from_data(ntru_drbg_t *drbg,
|
||||
chunk_t data)
|
||||
{
|
||||
private_ntru_public_key_t *this;
|
||||
size_t header_len, pubkey_packed_len;
|
||||
ntru_param_set_t *params;
|
||||
|
||||
header_len = 2 + NTRU_OID_LEN;
|
||||
|
||||
/* check the NTRU public key header format */
|
||||
if (data.len < header_len ||
|
||||
data.ptr[0] != NTRU_PUBKEY_TAG ||
|
||||
data.ptr[1] != NTRU_OID_LEN)
|
||||
{
|
||||
DBG1(DBG_LIB, "received NTRU public key with invalid header");
|
||||
return NULL;
|
||||
}
|
||||
params = ntru_param_set_get_by_oid(data.ptr + 2);
|
||||
|
||||
if (!params)
|
||||
{
|
||||
DBG1(DBG_LIB, "received NTRU public key with unknown OID");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pubkey_packed_len = (params->N * params->q_bits + 7) / 8;
|
||||
|
||||
if (data.len < header_len + pubkey_packed_len)
|
||||
{
|
||||
DBG1(DBG_LIB, "received NTRU public key with wrong packed key size");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.get_id = _get_id,
|
||||
.get_encoding = _get_encoding,
|
||||
.encrypt = _encrypt,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.params = params,
|
||||
.pubkey = malloc(params->N * sizeof(uint16_t)),
|
||||
.encoding = chunk_clone(data),
|
||||
.drbg = drbg->get_ref(drbg),
|
||||
);
|
||||
|
||||
/* unpack the encoded public key */
|
||||
ntru_octets_2_elements(pubkey_packed_len, data.ptr + header_len,
|
||||
params->q_bits, this->pubkey);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
EXPORT_FUNCTION_FOR_TESTS(ntru, ntru_public_key_create_from_data);
|
||||
|
||||
Reference in New Issue
Block a user