Annotation of embedaddon/strongswan/src/libstrongswan/plugins/newhope/newhope_ke.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2016 Andreas Steffen
! 3: * HSR Hochschule fuer Technik Rapperswil
! 4: *
! 5: * Based on public domain code by Erdem Alkim, Léo Ducas, Thomas Pöppelmann,
! 6: * and Peter Schwabe.
! 7: *
! 8: * This program is free software; you can redistribute it and/or modify it
! 9: * under the terms of the GNU General Public License as published by the
! 10: * Free Software Foundation; either version 2 of the License, or (at your
! 11: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 12: *
! 13: * This program is distributed in the hope that it will be useful, but
! 14: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 15: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 16: * for more details.
! 17: */
! 18:
! 19: #include "newhope_ke.h"
! 20: #include "newhope_noise.h"
! 21: #include "newhope_reconciliation.h"
! 22:
! 23: #include <ntt_fft.h>
! 24: #include <ntt_fft_reduce.h>
! 25: #include <crypto/diffie_hellman.h>
! 26: #include <utils/debug.h>
! 27:
! 28: static const int seed_len = 32; /* 256 bits */
! 29: static const int poly_len = 1792; /* size of 1024 packed 14-bit coefficients */
! 30: static const int rec_len = 256; /* size of 1024 packed 2-bit coefficients */
! 31:
! 32: typedef struct private_newhope_ke_t private_newhope_ke_t;
! 33:
! 34: /**
! 35: * Private data of an newhope_ke_t object.
! 36: */
! 37: struct private_newhope_ke_t {
! 38:
! 39: /**
! 40: * Public newhope_ke_t interface.
! 41: */
! 42: newhope_ke_t public;
! 43:
! 44: /**
! 45: * FFT parameter set
! 46: */
! 47: const ntt_fft_params_t *params;
! 48:
! 49: /**
! 50: * Secret noise polynomial s
! 51: */
! 52: uint32_t *s;
! 53:
! 54: /**
! 55: * Output polynomial u = a * NTT(s') + NTT(e')
! 56: */
! 57: uint32_t *u;
! 58:
! 59: /**
! 60: * Error reconciliation help bits
! 61: */
! 62: uint8_t *r;
! 63:
! 64: /**
! 65: * Shared secret
! 66: */
! 67: chunk_t shared_secret;
! 68:
! 69: };
! 70:
! 71: /**
! 72: * Derive 14-bit coefficients of polynomial a from 256 bit random seed
! 73: * using the SHAKE128 extended output function
! 74: */
! 75: static uint32_t* derive_a_poly(private_newhope_ke_t *this, chunk_t seed)
! 76: {
! 77: uint32_t *a;
! 78: uint8_t x[2];
! 79: int i = 0;
! 80: xof_t *xof;
! 81:
! 82: xof = lib->crypto->create_xof(lib->crypto, XOF_SHAKE_128);
! 83: if (!xof)
! 84: {
! 85: DBG1(DBG_LIB, "could not instantiate SHAKE128 XOF");
! 86: return NULL;
! 87: }
! 88:
! 89: if (!xof->set_seed(xof, seed))
! 90: {
! 91: DBG1(DBG_LIB, "could not set seed of SHAKE128 XOF");
! 92: xof->destroy(xof);
! 93: return NULL;
! 94: }
! 95:
! 96: /* allocate dynamic memory for polynomial a */
! 97: a = (uint32_t*)malloc(this->params->n * sizeof(uint32_t));
! 98:
! 99: while (i < this->params->n)
! 100: {
! 101: if (!xof->get_bytes(xof, sizeof(x), x))
! 102: {
! 103: DBG1(DBG_LIB, "could not get bytes from SHAKE128 XOF");
! 104: xof->destroy(xof);
! 105: free(a);
! 106: return NULL;
! 107: }
! 108:
! 109: /*
! 110: * Treat x as a 16 bit unsigned little endian integer
! 111: * and truncate to 14 bits
! 112: */
! 113: a[i] = uletoh16(x) & 0x3fff;
! 114:
! 115: if (a[i] < this->params->q)
! 116: {
! 117: i++;
! 118: }
! 119: }
! 120: xof->destroy(xof);
! 121:
! 122: return a;
! 123: }
! 124:
! 125: /**
! 126: * Pack four 14-bit coefficients into seven consecutive bytes
! 127: *
! 128: * 1 2 3
! 129: * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
! 130: * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! 131: * |L 0 0 0 0 0 0 0|L 1 H 0 0 0 0 0|M 1 1 1 1 1 1 1|L 2 2 2 H 1 1 1|
! 132: * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! 133: * |M 2 2 2 2 2 2 2|L 3 3 3 3 3 H 2|H 3 3 3 3 3 3 3|L 0 0 0 0 0 0 0|
! 134: * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! 135: */
! 136: static void pack_poly(private_newhope_ke_t *this, uint8_t *x, uint32_t *p)
! 137: {
! 138: int i;
! 139:
! 140: for (i = 0; i < this->params->n; i += 4)
! 141: {
! 142: *x++ = (p[i] & 0xff );
! 143: *x++ = (p[i] >> 8) | (p[i+1] << 6);
! 144: *x++ = (p[i+1] >> 2);
! 145: *x++ = (p[i+1] >> 10) | (p[i+2] << 4);
! 146: *x++ = (p[i+2] >> 4);
! 147: *x++ = (p[i+2] >> 12) | (p[i+3] << 2);
! 148: *x++ = (p[i+3] >> 6);
! 149: }
! 150: }
! 151:
! 152: /**
! 153: * Unpack seven consecutive bytes into four 14-bit coefficients
! 154: */
! 155: static uint32_t* unpack_poly(private_newhope_ke_t * this, uint8_t *x)
! 156: {
! 157: uint32_t *p;
! 158: int i;
! 159:
! 160: p = (uint32_t*)malloc(this->params->n * sizeof(uint32_t));
! 161:
! 162: for (i = 0; i < this->params->n; i += 4)
! 163: {
! 164: p[i] = x[0] | (((uint32_t)x[1] & 0x3f) << 8);
! 165: p[i+1] = (x[1] >> 6) | (((uint32_t)x[2]) << 2)
! 166: | (((uint32_t)x[3] & 0x0f) << 10);
! 167: p[i+2] = (x[3] >> 4) | (((uint32_t)x[4]) << 4)
! 168: | (((uint32_t)x[5] & 0x03) << 12);
! 169: p[i+3] = (x[5] >> 2) | (((uint32_t)x[6]) << 6);
! 170: x += 7;
! 171: }
! 172: for (i = 0; i < this->params->n; i++)
! 173: {
! 174: if (p[i] >= this->params->q)
! 175: {
! 176: DBG1(DBG_LIB, "polynomial coefficient must be smaller than %u",
! 177: this->params->q);
! 178: free(p);
! 179: return NULL;
! 180: }
! 181: }
! 182: return p;
! 183: }
! 184:
! 185: /**
! 186: * Multiply and add polynomials in the frequency domain
! 187: */
! 188: static uint32_t* multiply_add_poly(private_newhope_ke_t *this,
! 189: uint32_t *a, uint32_t *e)
! 190: {
! 191: ntt_fft_t *fft;
! 192: uint32_t *b, t;
! 193: int i;
! 194:
! 195: /* transform s and h to frequency domain */
! 196: fft = ntt_fft_create(this->params);
! 197: fft->transform(fft, this->s, this->s, FALSE);
! 198: fft->transform(fft, e, e, FALSE);
! 199: fft->destroy(fft);
! 200:
! 201: b = (uint32_t*)malloc(this->params->n * sizeof(uint32_t));
! 202:
! 203: /* compute b = a * s + e in the frequency domain */
! 204: for (i = 0; i < this->params->n; i++)
! 205: {
! 206: /* convert a[i] to Montgomery domain */
! 207: t = ntt_fft_mreduce(a[i] * this->params->r2, this->params);
! 208:
! 209: /* compute b[i] = a[i] * s[i] + e[i] in Montgomery domain */
! 210: t = ntt_fft_mreduce(t * this->s[i], this->params) + e[i];
! 211:
! 212: /* exit Montgomery domain before transmitting polynomial b */
! 213: b[i] = ntt_fft_mreduce(t, this->params);
! 214: }
! 215: memwipe(e, this->params->n * sizeof(uint32_t));
! 216:
! 217: return b;
! 218: }
! 219:
! 220: /**
! 221: * Multiply polynomials in the frequency domain and return to time domain
! 222: */
! 223: static uint32_t* multiply_ntt_inv_poly(private_newhope_ke_t *this, uint32_t *b)
! 224: {
! 225: ntt_fft_t *fft;
! 226: uint32_t *v, t;
! 227: int i;
! 228:
! 229: v = (uint32_t*)malloc(this->params->n * sizeof(uint32_t));
! 230:
! 231: for (i = 0; i < this->params->n; i++)
! 232: {
! 233: /* convert b[i] to Montgomery domain */
! 234: t = ntt_fft_mreduce(b[i] * this->params->r2, this->params);
! 235:
! 236: /* compute v[i] = b[i] * s[i] in Montgomery domain */
! 237: v[i] = ntt_fft_mreduce(t * this->s[i], this->params);
! 238: }
! 239:
! 240: /* transform v back to time domain */
! 241: fft = ntt_fft_create(this->params);
! 242: fft->transform(fft, v, v, TRUE);
! 243: fft->destroy(fft);
! 244:
! 245: return v;
! 246: }
! 247:
! 248: /**
! 249: * Pack four 2-bit coefficients into one byte
! 250: */
! 251: static void pack_rec(private_newhope_ke_t *this, uint8_t *x, uint8_t *r)
! 252: {
! 253: int i;
! 254:
! 255: for (i = 0; i < this->params->n; i += 4)
! 256: {
! 257: *x++ = r[i] | r[i+1] << 2 | r[i+2] << 4 | r[i+3] << 6;
! 258: }
! 259: }
! 260:
! 261: static uint8_t* unpack_rec(private_newhope_ke_t *this, uint8_t *x)
! 262: {
! 263: uint8_t *r;
! 264: int i;
! 265:
! 266: r = (uint8_t*)malloc(this->params->n);
! 267:
! 268: for (i = 0; i < this->params->n; i += 4)
! 269: {
! 270: r[i] = (*x) & 0x03;
! 271: r[i+1] = (*x >> 2) & 0x03;
! 272: r[i+2] = (*x >> 4) & 0x03;
! 273: r[i+3] = (*x >> 6) & 0x03;
! 274: x++;
! 275: }
! 276:
! 277: return r;
! 278: }
! 279:
! 280: METHOD(diffie_hellman_t, get_my_public_value, bool,
! 281: private_newhope_ke_t *this, chunk_t *value)
! 282: {
! 283: uint16_t n, q;
! 284: int i;
! 285:
! 286: /* Define some often-used constants */
! 287: n = this->params->n;
! 288: q = this->params->q;
! 289:
! 290: /* are we the initiator? */
! 291: if (this->u == NULL)
! 292: {
! 293: rng_t *rng;
! 294: uint32_t *a = NULL, *b = NULL, *e = NULL;
! 295: uint8_t noise_seed_buf[seed_len];
! 296: chunk_t noise_seed = { noise_seed_buf, seed_len};
! 297: chunk_t a_seed;
! 298: newhope_noise_t *noise = NULL;
! 299: bool success = FALSE;
! 300:
! 301: /* allocate space for public output value */
! 302: *value = chunk_alloc(poly_len + seed_len);
! 303: a_seed = chunk_create(value->ptr + poly_len, seed_len);
! 304:
! 305: /* create polynomial a from 256 bit random seed */
! 306: rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
! 307: if (!rng)
! 308: {
! 309: DBG1(DBG_LIB, "could not instantiate random source");
! 310: return FALSE;
! 311: }
! 312: if (!rng->get_bytes(rng, seed_len, a_seed.ptr))
! 313: {
! 314: DBG1(DBG_LIB, "could not generate seed for polynomial a");
! 315: goto end;
! 316: }
! 317:
! 318: a = derive_a_poly(this, a_seed);
! 319: if (a == NULL)
! 320: {
! 321: goto end;
! 322: }
! 323:
! 324: /* generate random seed for the derivation of noise polynomials */
! 325: if (!rng->get_bytes(rng, seed_len, noise_seed.ptr))
! 326: {
! 327: DBG1(DBG_LIB, "could not generate seed for noise polynomials");
! 328: goto end;
! 329: }
! 330:
! 331: /* create noise polynomial generator */
! 332: noise = newhope_noise_create(noise_seed);
! 333: if (!noise)
! 334: {
! 335: goto end;
! 336: }
! 337:
! 338: /* create noise polynomial s from seed with nonce = 0x00 */
! 339: this->s = noise->get_binomial_words(noise, 0x00, n, q);
! 340: if (this->s == NULL)
! 341: {
! 342: goto end;
! 343: }
! 344:
! 345: /* create noise polynomial e from seed with nonce = 0x01 */
! 346: e = noise->get_binomial_words(noise, 0x01, n, q);
! 347: if (e == NULL)
! 348: {
! 349: goto end;
! 350: }
! 351:
! 352: /* compute b = a * NTT(s) + NTT(e) */
! 353: b = multiply_add_poly(this, a, e);
! 354:
! 355: DBG3(DBG_LIB, " i a[i] b[i]");
! 356: for (i = 0; i < n; i++)
! 357: {
! 358: DBG3(DBG_LIB, "%4d %5u %5u", i, a[i], b[i]);
! 359: }
! 360:
! 361: /* pack coefficients of polynomial b */
! 362: pack_poly(this, value->ptr, b);
! 363: success = TRUE;
! 364:
! 365: end:
! 366: rng->destroy(rng);
! 367: DESTROY_IF(noise);
! 368: free(a);
! 369: free(b);
! 370: free(e);
! 371:
! 372: if (!success)
! 373: {
! 374: chunk_free(value);
! 375: }
! 376: return success;
! 377: }
! 378: else
! 379: {
! 380: DBG3(DBG_LIB, " i u[i] r[i]");
! 381: for (i = 0; i < n; i++)
! 382: {
! 383: DBG3(DBG_LIB, "%4d %5u %5u", i, this->u[i], this->r[i]);
! 384: }
! 385:
! 386: /* allocate space for public output value */
! 387: *value = chunk_alloc(poly_len + rec_len);
! 388:
! 389: /* pack coefficients of polynomial u */
! 390: pack_poly(this, value->ptr, this->u);
! 391:
! 392: /* pack coefficients of polynomial r */
! 393: pack_rec(this, value->ptr + poly_len, this->r);
! 394:
! 395: return TRUE;
! 396: }
! 397: }
! 398:
! 399: METHOD(diffie_hellman_t, get_shared_secret, bool,
! 400: private_newhope_ke_t *this, chunk_t *secret)
! 401: {
! 402: if (this->shared_secret.len == 0)
! 403: {
! 404: *secret = chunk_empty;
! 405: return FALSE;
! 406: }
! 407: *secret = chunk_clone(this->shared_secret);
! 408:
! 409: return TRUE;
! 410: }
! 411:
! 412: METHOD(diffie_hellman_t, set_other_public_value, bool,
! 413: private_newhope_ke_t *this, chunk_t value)
! 414: {
! 415: newhope_reconciliation_t * rec;
! 416: uint16_t n, q;
! 417: int i;
! 418:
! 419: /* Define some often-used constants */
! 420: n = this->params->n;
! 421: q = this->params->q;
! 422:
! 423: /* are we the responder? */
! 424: if (this->s == NULL)
! 425: {
! 426: uint32_t *a = NULL, *b = NULL, *e1 = NULL, *e2 = NULL, *v = NULL, t;
! 427: uint8_t *rbits = NULL;
! 428: uint8_t noise_seed_buf[seed_len];
! 429: chunk_t noise_seed = { noise_seed_buf, seed_len };
! 430: chunk_t a_seed;
! 431: newhope_noise_t *noise = NULL;
! 432: rng_t *rng = NULL;
! 433: bool success = FALSE;
! 434:
! 435: if (value.len != poly_len + seed_len)
! 436: {
! 437: DBG1(DBG_LIB, "received %N KE payload of incorrect size",
! 438: diffie_hellman_group_names, NH_128_BIT);
! 439: return FALSE;
! 440: }
! 441: a_seed = chunk_create(value.ptr + poly_len, seed_len);
! 442:
! 443: a = derive_a_poly(this, a_seed);
! 444: if (a == NULL)
! 445: {
! 446: return FALSE;
! 447: }
! 448:
! 449: b = unpack_poly(this, value.ptr);
! 450: if (b == NULL)
! 451: {
! 452: goto end;
! 453: }
! 454:
! 455: /* debug output of polynomials a and b */
! 456: DBG3(DBG_LIB, " i a[i] b[i]");
! 457: for (i = 0; i < n; i++)
! 458: {
! 459: DBG3(DBG_LIB, "%4d %5u %5u", i, a[i], b[i]);
! 460: }
! 461:
! 462: /* generate random seed for the derivation of noise polynomials */
! 463: rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
! 464: if (!rng)
! 465: {
! 466: DBG1(DBG_LIB, "could not instantiate random source");
! 467: goto end;
! 468: }
! 469: if (!rng->get_bytes(rng, seed_len, noise_seed.ptr))
! 470: {
! 471: DBG1(DBG_LIB, "could not generate seed for noise polynomials");
! 472: goto end;
! 473: }
! 474:
! 475: /* create noise polynomial generator */
! 476: noise = newhope_noise_create(noise_seed);
! 477: if (!noise)
! 478: {
! 479: goto end;
! 480: }
! 481:
! 482: /* create noise polynomial s' from seed with nonce = 0x00 */
! 483: this->s = noise->get_binomial_words(noise, 0x00, n, q);
! 484: if (this->s == NULL)
! 485: {
! 486: goto end;
! 487: }
! 488:
! 489: /* create noise polynomial e' from seed with nonce = 0x01 */
! 490: e1 = noise->get_binomial_words(noise, 0x01, n, q);
! 491: if (e1 == NULL)
! 492: {
! 493: goto end;
! 494: }
! 495:
! 496: /* create noise polynomial e'' from seed with nonce = 0x02 */
! 497: e2 = noise->get_binomial_words(noise, 0x02, n, q);
! 498: if (e2 == NULL)
! 499: {
! 500: goto end;
! 501: }
! 502:
! 503: /* compute u = a * NTT(s') + NTT(e') */
! 504: this->u = multiply_add_poly(this, a, e1);
! 505:
! 506: /* compute v = NTT_inv( b * NTT(s') ) */
! 507: v = multiply_ntt_inv_poly(this, b);
! 508:
! 509: /* compute v = v + e'' */
! 510: for (i = 0; i < n; i++)
! 511: {
! 512: t = v[i] + e2[i];
! 513: v[i] = (t < q) ? t : t - q;
! 514: }
! 515: memwipe(e2, n * sizeof(uint32_t));
! 516:
! 517: /* create uniform noise bytes from seed with nonce = 0x02 */
! 518: rbits = noise->get_uniform_bytes(noise, 0x03, n/(4*8));
! 519:
! 520: rec = newhope_reconciliation_create(n, q);
! 521: this->r = rec->help_reconcile(rec, v, rbits);
! 522: free(rbits);
! 523: this->shared_secret = rec->reconcile(rec, v, this->r);
! 524: rec->destroy(rec);
! 525:
! 526: DBG4(DBG_LIB, "key: %B", &this->shared_secret);
! 527: success = TRUE;
! 528:
! 529: end:
! 530: DESTROY_IF(rng);
! 531: DESTROY_IF(noise);
! 532: free(a);
! 533: free(b);
! 534: free(e1);
! 535: free(e2);
! 536: free(v);
! 537:
! 538: return success;
! 539: }
! 540: else
! 541: {
! 542: uint32_t *v;
! 543:
! 544: if (value.len != poly_len + rec_len)
! 545: {
! 546: DBG1(DBG_LIB, "received %N KE payload of incorrect size",
! 547: diffie_hellman_group_names, NH_128_BIT);
! 548: return FALSE;
! 549: }
! 550:
! 551: this->u = unpack_poly(this, value.ptr);
! 552: if (this->u == NULL)
! 553: {
! 554: return FALSE;
! 555: }
! 556:
! 557: this->r = unpack_rec(this, value.ptr + poly_len);
! 558: if (this->r == NULL)
! 559: {
! 560: return FALSE;
! 561: }
! 562:
! 563: DBG3(DBG_LIB, " i u[i] r[i]");
! 564: for (i = 0; i < n; i++)
! 565: {
! 566: DBG3(DBG_LIB, "%4d %5u %5u", i, this->u[i], this->r[i]);
! 567: }
! 568:
! 569: /* compute v' = NTT_inv( u * NTT(s) ) */
! 570: v = multiply_ntt_inv_poly(this, this->u);
! 571:
! 572: rec = newhope_reconciliation_create(n, q);
! 573: this->shared_secret = rec->reconcile(rec, v, this->r);
! 574: free(v);
! 575: rec->destroy(rec);
! 576:
! 577: DBG4(DBG_LIB, "key: %B", &this->shared_secret);
! 578:
! 579: return TRUE;
! 580: }
! 581: }
! 582:
! 583: METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t,
! 584: private_newhope_ke_t *this)
! 585: {
! 586: return NH_128_BIT;
! 587: }
! 588:
! 589: METHOD(diffie_hellman_t, destroy, void,
! 590: private_newhope_ke_t *this)
! 591: {
! 592: chunk_clear(&this->shared_secret);
! 593: memwipe(this->s, this->params->n * sizeof(uint32_t));
! 594: free(this->s);
! 595: free(this->u);
! 596: free(this->r);
! 597: free(this);
! 598: }
! 599:
! 600: /*
! 601: * Described in header.
! 602: */
! 603: newhope_ke_t *newhope_ke_create(diffie_hellman_group_t group, chunk_t g, chunk_t p)
! 604: {
! 605: private_newhope_ke_t *this;
! 606:
! 607: INIT(this,
! 608: .public = {
! 609: .dh = {
! 610: .get_shared_secret = _get_shared_secret,
! 611: .set_other_public_value = _set_other_public_value,
! 612: .get_my_public_value = _get_my_public_value,
! 613: .get_dh_group = _get_dh_group,
! 614: .destroy = _destroy,
! 615: },
! 616: },
! 617: .params = &ntt_fft_12289_1024,
! 618:
! 619: );
! 620:
! 621: return &this->public;
! 622: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>