Add experimental post-quantum hybrid key exchange method
authortobhe <tobhe@openbsd.org>
Fri, 28 May 2021 18:01:39 +0000 (18:01 +0000)
committertobhe <tobhe@openbsd.org>
Fri, 28 May 2021 18:01:39 +0000 (18:01 +0000)
based on Streamlined NTRU Prime (coupled with X25519).

The sntrup761 implementation is imported from OpenSSH.
It is public domain code originally distributed as part
of the SUPERCOP cryptography benchmark suite
(https://bench.cr.yp.to/supercop.html).

The method is not part of the default proposal, but can
be enabled with 'ikesa group sntrup761x25519'.

ok markus@ patrick@

sbin/iked/Makefile
sbin/iked/crypto_api.h [new file with mode: 0644]
sbin/iked/crypto_hash.c [new file with mode: 0644]
sbin/iked/dh.c
sbin/iked/dh.h
sbin/iked/ikev2.h
sbin/iked/parse.y
sbin/iked/sntrup761.c [new file with mode: 0644]
sbin/iked/sntrup761.sh [new file with mode: 0644]

index 9cef5b2..2d0f536 100644 (file)
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.21 2021/03/05 22:03:51 tobhe Exp $
+# $OpenBSD: Makefile,v 1.22 2021/05/28 18:01:39 tobhe Exp $
 
 PROG=          iked
 SRCS=          ca.c chap_ms.c config.c control.c crypto.c dh.c \
@@ -6,6 +6,7 @@ SRCS=           ca.c chap_ms.c config.c control.c crypto.c dh.c \
                log.c ocsp.c pfkey.c policy.c print.c proc.c timer.c util.c \
                imsg_util.c smult_curve25519_ref.c vroute.c
 SRCS+=         eap_map.c ikev2_map.c
+SRCS+=         crypto_hash.c sntrup761.c
 SRCS+=         parse.y
 MAN=           iked.conf.5 iked.8
 #NOMAN=                yes
diff --git a/sbin/iked/crypto_api.h b/sbin/iked/crypto_api.h
new file mode 100644 (file)
index 0000000..f05352c
--- /dev/null
@@ -0,0 +1,43 @@
+/* $OpenBSD: crypto_api.h,v 1.1 2021/05/28 18:01:39 tobhe Exp $ */
+
+/*
+ * Assembled from generated headers and source files by Markus Friedl.
+ * Placed in the public domain.
+ */
+
+#ifndef crypto_api_h
+#define crypto_api_h
+
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef int8_t crypto_int8;
+typedef uint8_t crypto_uint8;
+typedef int16_t crypto_int16;
+typedef uint16_t crypto_uint16;
+typedef int32_t crypto_int32;
+typedef uint32_t crypto_uint32;
+typedef uint64_t crypto_uint64;
+
+#define randombytes(buf, buf_len) arc4random_buf((buf), (buf_len))
+#define small_random32() arc4random()
+
+#define crypto_hash_sha512_BYTES 64U
+
+int    crypto_hash_sha512(unsigned char *, const unsigned char *,
+    unsigned long long);
+
+int    crypto_verify_32(const unsigned char *, const unsigned char *);
+
+#define crypto_kem_sntrup761_PUBLICKEYBYTES 1158
+#define crypto_kem_sntrup761_SECRETKEYBYTES 1763
+#define crypto_kem_sntrup761_CIPHERTEXTBYTES 1039
+#define crypto_kem_sntrup761_BYTES 32
+
+int    crypto_kem_sntrup761_enc(unsigned char *cstr, unsigned char *k,
+    const unsigned char *pk);
+int    crypto_kem_sntrup761_dec(unsigned char *k,
+    const unsigned char *cstr, const unsigned char *sk);
+int    crypto_kem_sntrup761_keypair(unsigned char *pk, unsigned char *sk);
+
+#endif /* crypto_api_h */
diff --git a/sbin/iked/crypto_hash.c b/sbin/iked/crypto_hash.c
new file mode 100644 (file)
index 0000000..6886753
--- /dev/null
@@ -0,0 +1,19 @@
+/* $OpenBSD: crypto_hash.c,v 1.1 2021/05/28 18:01:39 tobhe Exp $ */
+/*
+ * Public domain. Author: Christian Weisgerber <naddy@openbsd.org>
+ * API compatible reimplementation of function from nacl
+ */
+
+#include "crypto_api.h"
+#include <openssl/evp.h>
+
+int
+crypto_hash_sha512(unsigned char *out, const unsigned char *in,
+    unsigned long long inlen)
+{
+       u_int mdlen;
+
+       if (!EVP_Digest(in, inlen, out, &mdlen, EVP_sha512(), NULL))
+               return -1;
+       return 0;
+}
index 379e222..c29d730 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dh.c,v 1.28 2021/05/13 14:01:35 tb Exp $      */
+/*     $OpenBSD: dh.c,v 1.29 2021/05/28 18:01:39 tobhe Exp $   */
 
 /*
  * Copyright (c) 2010-2014 Reyk Floeter <reyk@openbsd.org>
@@ -35,6 +35,7 @@
 
 #include "dh.h"
 #include "iked.h"
+#include "crypto_api.h"
 
 int    dh_init(struct dh_group *);
 int    dh_getlen(struct dh_group *);
@@ -77,6 +78,19 @@ extern int crypto_scalarmult_curve25519(unsigned char a[CURVE25519_SIZE],
        __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE)))
        __attribute__((__bounded__(__minbytes__, 3, CURVE25519_SIZE)));
 
+/* SNTRUP761 with X25519 */
+int    kemsx_init(struct dh_group *);
+int    kemsx_getlen(struct dh_group *);
+int    kemsx_create_exchange2(struct dh_group *, struct ibuf **, struct ibuf *);
+int    kemsx_create_shared2(struct dh_group *, struct ibuf **, struct ibuf *);
+
+struct kemsx_key {
+       uint8_t         kemkey[crypto_kem_sntrup761_BYTES];
+       uint8_t         secret[crypto_kem_sntrup761_SECRETKEYBYTES];
+       uint8_t         public[crypto_kem_sntrup761_PUBLICKEYBYTES];
+       uint8_t         initiator;
+};
+
 const struct group_id ike_groups[] = {
        { GROUP_MODP, 1, 768,
            "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
@@ -253,7 +267,13 @@ const struct group_id ike_groups[] = {
        { GROUP_ECP, 28, 256, NULL, NULL, NID_brainpoolP256r1 },
        { GROUP_ECP, 29, 384, NULL, NULL, NID_brainpoolP384r1 },
        { GROUP_ECP, 30, 512, NULL, NULL, NID_brainpoolP512r1 },
-       { GROUP_CURVE25519, 31, CURVE25519_SIZE * 8 }
+       { GROUP_CURVE25519, 31, CURVE25519_SIZE * 8 },
+       /* "Private use" extensions */
+       /* PQC KEM */
+       { GROUP_SNTRUP761X25519, 1035,
+          (MAX(crypto_kem_sntrup761_PUBLICKEYBYTES,
+               crypto_kem_sntrup761_CIPHERTEXTBYTES) +
+           CURVE25519_SIZE) * 8 }
 };
 
 void
@@ -273,6 +293,7 @@ group_free(struct dh_group *group)
        if (group->ec != NULL)
                EC_KEY_free(group->ec);
        freezero(group->curve25519, sizeof(struct curve25519_key));
+       freezero(group->kemsx, sizeof(struct kemsx_key));
        group->spec = NULL;
        free(group);
 }
@@ -312,6 +333,12 @@ group_get(uint32_t id)
                group->exchange = ec25519_create_exchange;
                group->shared = ec25519_create_shared;
                break;
+       case GROUP_SNTRUP761X25519:
+               group->init = kemsx_init;
+               group->getlen = kemsx_getlen;
+               group->exchange2 = kemsx_create_exchange2;
+               group->shared2 = kemsx_create_shared2;
+               break;
        default:
                group_free(group);
                return (NULL);
@@ -368,6 +395,8 @@ dh_create_exchange(struct dh_group *group, struct ibuf **bufp, struct ibuf *iexc
        struct ibuf *buf;
 
        *bufp = NULL;
+       if (group->exchange2)
+               return (group->exchange2(group, bufp, iexchange));
        buf = ibuf_new(NULL, dh_getlen(group));
        if (buf == NULL)
                return -1;
@@ -381,6 +410,8 @@ dh_create_shared(struct dh_group *group, struct ibuf **secretp, struct ibuf *exc
        struct ibuf *buf;
 
        *secretp = NULL;
+       if (group->shared2)
+               return (group->shared2(group, secretp, exchange));
        if (exchange == NULL ||
            (ssize_t)ibuf_size(exchange) != dh_getlen(group))
                return -1;
@@ -729,3 +760,132 @@ ec25519_create_shared(struct dh_group *group, uint8_t *shared, uint8_t *public)
        crypto_scalarmult_curve25519(shared, curve25519->secret, public);
        return (0);
 }
+
+/* combine sntrup761 with curve25519 */
+
+int
+kemsx_init(struct dh_group *group)
+{
+       /* delayed until kemsx_create_exchange2 */
+       return (0);
+}
+
+int
+kemsx_getlen(struct dh_group *group)
+{
+       return (0);
+}
+
+int
+kemsx_create_exchange2(struct dh_group *group, struct ibuf **bufp,
+    struct ibuf *iexchange)
+{
+       struct kemsx_key        *kemsx;
+       struct curve25519_key   *curve25519;
+       struct ibuf             *buf = NULL;
+       u_char *cp, *pk;
+       size_t have, need;
+
+       if (ec25519_init(group) == -1)
+               return (-1);
+       if (group->curve25519 == NULL)
+               return (-1);
+       if ((kemsx = calloc(1, sizeof(*kemsx))) == NULL)
+               return (-1);
+       group->kemsx = kemsx;
+
+       if (iexchange == NULL) {
+               kemsx->initiator = 1;
+               crypto_kem_sntrup761_keypair(kemsx->public, kemsx->secret);
+               /* output */
+               need = crypto_kem_sntrup761_PUBLICKEYBYTES +
+                   CURVE25519_SIZE;
+               buf = ibuf_new(NULL, need);
+               if (buf == NULL)
+                       return -1;
+               cp = buf->buf;
+               memcpy(cp, kemsx->public,
+                   crypto_kem_sntrup761_PUBLICKEYBYTES);
+               cp += crypto_kem_sntrup761_PUBLICKEYBYTES;
+       } else {
+               kemsx->initiator = 0;
+               /* input */
+               have = ibuf_size(iexchange);
+               need = crypto_kem_sntrup761_PUBLICKEYBYTES +
+                   CURVE25519_SIZE;
+               if (have != need)
+                       return -1;
+               /* output */
+               need = crypto_kem_sntrup761_CIPHERTEXTBYTES +
+                   CURVE25519_SIZE;
+               buf = ibuf_new(NULL, need);
+               if (buf == NULL)
+                       return -1;
+               cp = buf->buf;
+               pk = iexchange->buf;
+               crypto_kem_sntrup761_enc(cp, kemsx->kemkey, pk);
+               cp += crypto_kem_sntrup761_CIPHERTEXTBYTES;
+       }
+       curve25519 = group->curve25519;
+       memcpy(cp, curve25519->public, CURVE25519_SIZE);
+       *bufp = buf;
+       return (0);
+}
+
+int
+kemsx_create_shared2(struct dh_group *group, struct ibuf **sharedp,
+    struct ibuf *exchange)
+{
+       struct curve25519_key   *curve25519 = group->curve25519;
+       struct kemsx_key        *kemsx = group->kemsx;
+       struct ibuf             *buf = NULL;
+       EVP_MD_CTX              *ctx = NULL;
+       u_int8_t                *cp;
+       u_int8_t                 shared[CURVE25519_SIZE];
+       size_t                   have, need;
+       u_int                    len;
+
+       *sharedp = NULL;
+       if (kemsx == NULL)
+               return (-1);
+       if (exchange == NULL)
+               return (-1);
+
+       have = ibuf_size(exchange);
+       cp = exchange->buf;
+       if (kemsx->initiator) {
+               /* input */
+               need = crypto_kem_sntrup761_CIPHERTEXTBYTES +
+                   CURVE25519_SIZE;
+               if (have != need)
+                       return (-1);
+               crypto_kem_sntrup761_dec(kemsx->kemkey, cp, kemsx->secret);
+               cp += crypto_kem_sntrup761_CIPHERTEXTBYTES;
+       } else {
+               /* input, should have been checked before */
+               need = crypto_kem_sntrup761_PUBLICKEYBYTES +
+                   CURVE25519_SIZE;
+               if (have != need)
+                       return (-1);
+               cp += crypto_kem_sntrup761_PUBLICKEYBYTES;
+       }
+       crypto_scalarmult_curve25519(shared, curve25519->secret, cp);
+
+       /* result is hash of concatenation of KEM key and DH shared secret */
+       len = SHA512_DIGEST_LENGTH;
+       buf = ibuf_new(NULL, len);
+       if (buf == NULL)
+               return (-1);
+       if ((ctx = EVP_MD_CTX_new()) == NULL ||
+           EVP_DigestInit_ex(ctx, EVP_sha512(), NULL) != 1 ||
+           EVP_DigestUpdate(ctx, kemsx->kemkey, sizeof(kemsx->kemkey)) != 1 ||
+           EVP_DigestUpdate(ctx, shared, sizeof(shared)) != 1 ||
+           EVP_DigestFinal_ex(ctx, buf->buf, &len) != 1) {
+               EVP_MD_CTX_free(ctx);
+               ibuf_free(buf);
+               return (-1);
+       }
+       EVP_MD_CTX_free(ctx);
+       *sharedp = buf;
+       return (0);
+}
index a97fd0f..0ea98a9 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dh.h,v 1.14 2021/02/04 20:38:26 tobhe Exp $   */
+/*     $OpenBSD: dh.h,v 1.15 2021/05/28 18:01:39 tobhe Exp $   */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -22,7 +22,8 @@
 enum group_type {
        GROUP_MODP              = 0,
        GROUP_ECP               = 1,
-       GROUP_CURVE25519        = 2
+       GROUP_CURVE25519        = 2,
+       GROUP_SNTRUP761X25519   = 3
 };
 
 struct group_id {
@@ -42,12 +43,15 @@ struct dh_group {
        void            *dh;
        void            *ec;
        void            *curve25519;
+       void            *kemsx;
 
        int             (*init)(struct dh_group *);
        int             (*getlen)(struct dh_group *);
        int             (*secretlen)(struct dh_group *);
        int             (*exchange)(struct dh_group *, uint8_t *);
+       int             (*exchange2)(struct dh_group *, struct ibuf **, struct ibuf *);
        int             (*shared)(struct dh_group *, uint8_t *, uint8_t *);
+       int             (*shared2)(struct dh_group *, struct ibuf **, struct ibuf *);
 };
 
 #define DH_MAXSZ       1024    /* 8192 bits */
index 074928f..bb8d178 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ikev2.h,v 1.33 2020/05/26 20:24:31 tobhe Exp $        */
+/*     $OpenBSD: ikev2.h,v 1.34 2021/05/28 18:01:39 tobhe Exp $        */
 
 /*
  * Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de>
@@ -251,6 +251,7 @@ extern struct iked_constmap ikev2_xformauth_map[];
 #define IKEV2_XFORMDH_BRAINPOOL_P384R1 29      /* RFC6954 */
 #define IKEV2_XFORMDH_BRAINPOOL_P512R1 30      /* RFC6954 */
 #define IKEV2_XFORMDH_CURVE25519       31      /* RFC8031 */
+#define IKEV2_XFORMDH_X_SNTRUP761X25519        1035    /* private */
 
 extern struct iked_constmap ikev2_xformdh_map[];
 
index 72a171a..533ea48 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.130 2021/03/16 22:50:52 tobhe Exp $       */
+/*     $OpenBSD: parse.y,v 1.131 2021/05/28 18:01:39 tobhe Exp $       */
 
 /*
  * Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de>
@@ -279,6 +279,7 @@ const struct ipsec_xf groupxfs[] = {
        { "grp30",              IKEV2_XFORMDH_BRAINPOOL_P512R1 },
        { "curve25519",         IKEV2_XFORMDH_CURVE25519 },
        { "grp31",              IKEV2_XFORMDH_CURVE25519 },
+       { "sntrup761x25519",    IKEV2_XFORMDH_X_SNTRUP761X25519 },
        { NULL }
 };
 
diff --git a/sbin/iked/sntrup761.c b/sbin/iked/sntrup761.c
new file mode 100644 (file)
index 0000000..5fc21ea
--- /dev/null
@@ -0,0 +1,1269 @@
+/*  $OpenBSD: sntrup761.c,v 1.1 2021/05/28 18:01:39 tobhe Exp $ */
+
+/*
+ * Public Domain, Authors:
+ * - Daniel J. Bernstein
+ * - Chitchanok Chuengsatiansup
+ * - Tanja Lange
+ * - Christine van Vredendaal
+ */
+
+#include <string.h>
+#include "crypto_api.h"
+
+#define int8 crypto_int8
+#define uint8 crypto_uint8
+#define int16 crypto_int16
+#define uint16 crypto_uint16
+#define int32 crypto_int32
+#define uint32 crypto_uint32
+#define int64 crypto_int64
+#define uint64 crypto_uint64
+
+/* from supercop-20201130/crypto_sort/int32/portable4/int32_minmax.inc */
+#define int32_MINMAX(a,b) \
+do { \
+  int64_t ab = (int64_t)b ^ (int64_t)a; \
+  int64_t c = (int64_t)b - (int64_t)a; \
+  c ^= ab & (c ^ b); \
+  c >>= 31; \
+  c &= ab; \
+  a ^= c; \
+  b ^= c; \
+} while(0)
+
+/* from supercop-20201130/crypto_sort/int32/portable4/sort.c */
+
+
+static void crypto_sort_int32(void *array,long long n)
+{
+  long long top,p,q,r,i,j;
+  int32 *x = array;
+
+  if (n < 2) return;
+  top = 1;
+  while (top < n - top) top += top;
+
+  for (p = top;p >= 1;p >>= 1) {
+    i = 0;
+    while (i + 2 * p <= n) {
+      for (j = i;j < i + p;++j)
+        int32_MINMAX(x[j],x[j+p]);
+      i += 2 * p;
+    }
+    for (j = i;j < n - p;++j)
+      int32_MINMAX(x[j],x[j+p]);
+
+    i = 0;
+    j = 0;
+    for (q = top;q > p;q >>= 1) {
+      if (j != i) for (;;) {
+        if (j == n - q) goto done;
+        int32 a = x[j + p];
+        for (r = q;r > p;r >>= 1)
+          int32_MINMAX(a,x[j + r]);
+        x[j + p] = a;
+        ++j;
+        if (j == i + p) {
+          i += 2 * p;
+          break;
+        }
+      }
+      while (i + p <= n - q) {
+        for (j = i;j < i + p;++j) {
+          int32 a = x[j + p];
+          for (r = q;r > p;r >>= 1)
+            int32_MINMAX(a,x[j+r]);
+          x[j + p] = a;
+        }
+        i += 2 * p;
+      }
+      /* now i + p > n - q */
+      j = i;
+      while (j < n - q) {
+        int32 a = x[j + p];
+        for (r = q;r > p;r >>= 1)
+          int32_MINMAX(a,x[j+r]);
+        x[j + p] = a;
+        ++j;
+      }
+
+      done: ;
+    }
+  }
+}
+
+/* from supercop-20201130/crypto_sort/uint32/useint32/sort.c */
+
+/* can save time by vectorizing xor loops */
+/* can save time by integrating xor loops with int32_sort */
+
+static void crypto_sort_uint32(void *array,long long n)
+{
+  crypto_uint32 *x = array;
+  long long j;
+  for (j = 0;j < n;++j) x[j] ^= 0x80000000;
+  crypto_sort_int32(array,n);
+  for (j = 0;j < n;++j) x[j] ^= 0x80000000;
+}
+
+/* from supercop-20201130/crypto_kem/sntrup761/ref/uint32.c */
+
+/*
+CPU division instruction typically takes time depending on x.
+This software is designed to take time independent of x.
+Time still varies depending on m; user must ensure that m is constant.
+Time also varies on CPUs where multiplication is variable-time.
+There could be more CPU issues.
+There could also be compiler issues. 
+*/
+
+static void uint32_divmod_uint14(uint32 *q,uint16 *r,uint32 x,uint16 m)
+{
+  uint32 v = 0x80000000;
+  uint32 qpart;
+  uint32 mask;
+
+  v /= m;
+
+  /* caller guarantees m > 0 */
+  /* caller guarantees m < 16384 */
+  /* vm <= 2^31 <= vm+m-1 */
+  /* xvm <= 2^31 x <= xvm+x(m-1) */
+
+  *q = 0;
+
+  qpart = (x*(uint64)v)>>31;
+  /* 2^31 qpart <= xv <= 2^31 qpart + 2^31-1 */
+  /* 2^31 qpart m <= xvm <= 2^31 qpart m + (2^31-1)m */
+  /* 2^31 qpart m <= 2^31 x <= 2^31 qpart m + (2^31-1)m + x(m-1) */
+  /* 0 <= 2^31 newx <= (2^31-1)m + x(m-1) */
+  /* 0 <= newx <= (1-1/2^31)m + x(m-1)/2^31 */
+  /* 0 <= newx <= (1-1/2^31)(2^14-1) + (2^32-1)((2^14-1)-1)/2^31 */
+
+  x -= qpart*m; *q += qpart;
+  /* x <= 49146 */
+
+  qpart = (x*(uint64)v)>>31;
+  /* 0 <= newx <= (1-1/2^31)m + x(m-1)/2^31 */
+  /* 0 <= newx <= m + 49146(2^14-1)/2^31 */
+  /* 0 <= newx <= m + 0.4 */
+  /* 0 <= newx <= m */
+
+  x -= qpart*m; *q += qpart;
+  /* x <= m */
+
+  x -= m; *q += 1;
+  mask = -(x>>31);
+  x += mask&(uint32)m; *q += mask;
+  /* x < m */
+
+  *r = x;
+}
+
+
+static uint16 uint32_mod_uint14(uint32 x,uint16 m)
+{
+  uint32 q;
+  uint16 r;
+  uint32_divmod_uint14(&q,&r,x,m);
+  return r;
+}
+
+/* from supercop-20201130/crypto_kem/sntrup761/ref/int32.c */
+
+static void int32_divmod_uint14(int32 *q,uint16 *r,int32 x,uint16 m)
+{
+  uint32 uq,uq2;
+  uint16 ur,ur2;
+  uint32 mask;
+
+  uint32_divmod_uint14(&uq,&ur,0x80000000+(uint32)x,m);
+  uint32_divmod_uint14(&uq2,&ur2,0x80000000,m);
+  ur -= ur2; uq -= uq2;
+  mask = -(uint32)(ur>>15);
+  ur += mask&m; uq += mask;
+  *r = ur; *q = uq;
+}
+
+
+static uint16 int32_mod_uint14(int32 x,uint16 m)
+{
+  int32 q;
+  uint16 r;
+  int32_divmod_uint14(&q,&r,x,m);
+  return r;
+}
+
+/* from supercop-20201130/crypto_kem/sntrup761/ref/paramsmenu.h */
+/* pick one of these three: */
+#define SIZE761
+#undef SIZE653
+#undef SIZE857
+
+/* pick one of these two: */
+#define SNTRUP /* Streamlined NTRU Prime */
+#undef LPR /* NTRU LPRime */
+
+/* from supercop-20201130/crypto_kem/sntrup761/ref/params.h */
+#ifndef params_H
+#define params_H
+
+/* menu of parameter choices: */
+
+
+/* what the menu means: */
+
+#if defined(SIZE761)
+#define p 761
+#define q 4591
+#define Rounded_bytes 1007
+#ifndef LPR
+#define Rq_bytes 1158
+#define w 286
+#else
+#define w 250
+#define tau0 2156
+#define tau1 114
+#define tau2 2007
+#define tau3 287
+#endif
+
+#elif defined(SIZE653)
+#define p 653
+#define q 4621
+#define Rounded_bytes 865
+#ifndef LPR
+#define Rq_bytes 994
+#define w 288
+#else
+#define w 252
+#define tau0 2175
+#define tau1 113
+#define tau2 2031
+#define tau3 290
+#endif
+
+#elif defined(SIZE857)
+#define p 857
+#define q 5167
+#define Rounded_bytes 1152
+#ifndef LPR
+#define Rq_bytes 1322
+#define w 322
+#else
+#define w 281
+#define tau0 2433
+#define tau1 101
+#define tau2 2265
+#define tau3 324
+#endif
+
+#else
+#error "no parameter set defined"
+#endif
+
+#ifdef LPR
+#define I 256
+#endif
+
+#endif
+
+/* from supercop-20201130/crypto_kem/sntrup761/ref/Decode.h */
+#ifndef Decode_H
+#define Decode_H
+
+
+/* Decode(R,s,M,len) */
+/* assumes 0 < M[i] < 16384 */
+/* produces 0 <= R[i] < M[i] */
+
+#endif
+
+/* from supercop-20201130/crypto_kem/sntrup761/ref/Decode.c */
+
+static void Decode(uint16 *out,const unsigned char *S,const uint16 *M,long long len)
+{
+  if (len == 1) {
+    if (M[0] == 1)
+      *out = 0;
+    else if (M[0] <= 256)
+      *out = uint32_mod_uint14(S[0],M[0]);
+    else
+      *out = uint32_mod_uint14(S[0]+(((uint16)S[1])<<8),M[0]);
+  }
+  if (len > 1) {
+    uint16 R2[(len+1)/2];
+    uint16 M2[(len+1)/2];
+    uint16 bottomr[len/2];
+    uint32 bottomt[len/2];
+    long long i;
+    for (i = 0;i < len-1;i += 2) {
+      uint32 m = M[i]*(uint32) M[i+1];
+      if (m > 256*16383) {
+        bottomt[i/2] = 256*256;
+        bottomr[i/2] = S[0]+256*S[1];
+        S += 2;
+        M2[i/2] = (((m+255)>>8)+255)>>8;
+      } else if (m >= 16384) {
+        bottomt[i/2] = 256;
+        bottomr[i/2] = S[0];
+        S += 1;
+        M2[i/2] = (m+255)>>8;
+      } else {
+        bottomt[i/2] = 1;
+        bottomr[i/2] = 0;
+        M2[i/2] = m;
+      }
+    }
+    if (i < len)
+      M2[i/2] = M[i];
+    Decode(R2,S,M2,(len+1)/2);
+    for (i = 0;i < len-1;i += 2) {
+      uint32 r = bottomr[i/2];
+      uint32 r1;
+      uint16 r0;
+      r += bottomt[i/2]*R2[i/2];
+      uint32_divmod_uint14(&r1,&r0,r,M[i]);
+      r1 = uint32_mod_uint14(r1,M[i+1]); /* only needed for invalid inputs */
+      *out++ = r0;
+      *out++ = r1;
+    }
+    if (i < len)
+      *out++ = R2[i/2];
+  }
+}
+
+/* from supercop-20201130/crypto_kem/sntrup761/ref/Encode.h */
+#ifndef Encode_H
+#define Encode_H
+
+
+/* Encode(s,R,M,len) */
+/* assumes 0 <= R[i] < M[i] < 16384 */
+
+#endif
+
+/* from supercop-20201130/crypto_kem/sntrup761/ref/Encode.c */
+
+/* 0 <= R[i] < M[i] < 16384 */
+static void Encode(unsigned char *out,const uint16 *R,const uint16 *M,long long len)
+{
+  if (len == 1) {
+    uint16 r = R[0];
+    uint16 m = M[0];
+    while (m > 1) {
+      *out++ = r;
+      r >>= 8;
+      m = (m+255)>>8;
+    }
+  }
+  if (len > 1) {
+    uint16 R2[(len+1)/2];
+    uint16 M2[(len+1)/2];
+    long long i;
+    for (i = 0;i < len-1;i += 2) {
+      uint32 m0 = M[i];
+      uint32 r = R[i]+R[i+1]*m0;
+      uint32 m = M[i+1]*m0;
+      while (m >= 16384) {
+        *out++ = r;
+        r >>= 8;
+        m = (m+255)>>8;
+      }
+      R2[i/2] = r;
+      M2[i/2] = m;
+    }
+    if (i < len) {
+      R2[i/2] = R[i];
+      M2[i/2] = M[i];
+    }
+    Encode(out,R2,M2,(len+1)/2);
+  }
+}
+
+/* from supercop-20201130/crypto_kem/sntrup761/ref/kem.c */
+
+#ifdef LPR
+#endif
+
+
+/* ----- masks */
+
+#ifndef LPR
+
+/* return -1 if x!=0; else return 0 */
+static int int16_nonzero_mask(int16 x)
+{
+  uint16 u = x; /* 0, else 1...65535 */
+  uint32 v = u; /* 0, else 1...65535 */
+  v = -v; /* 0, else 2^32-65535...2^32-1 */
+  v >>= 31; /* 0, else 1 */
+  return -v; /* 0, else -1 */
+}
+
+#endif
+
+/* return -1 if x<0; otherwise return 0 */
+static int int16_negative_mask(int16 x)
+{
+  uint16 u = x;
+  u >>= 15;
+  return -(int) u;
+  /* alternative with gcc -fwrapv: */
+  /* x>>15 compiles to CPU's arithmetic right shift */
+}
+
+/* ----- arithmetic mod 3 */
+
+typedef int8 small;
+
+/* F3 is always represented as -1,0,1 */
+/* so ZZ_fromF3 is a no-op */
+
+/* x must not be close to top int16 */
+static small F3_freeze(int16 x)
+{
+  return int32_mod_uint14(x+1,3)-1;
+}
+
+/* ----- arithmetic mod q */
+
+#define q12 ((q-1)/2)
+typedef int16 Fq;
+/* always represented as -q12...q12 */
+/* so ZZ_fromFq is a no-op */
+
+/* x must not be close to top int32 */
+static Fq Fq_freeze(int32 x)
+{
+  return int32_mod_uint14(x+q12,q)-q12;
+}
+
+#ifndef LPR
+
+static Fq Fq_recip(Fq a1)
+{ 
+  int i = 1;
+  Fq ai = a1;
+
+  while (i < q-2) {
+    ai = Fq_freeze(a1*(int32)ai);
+    i += 1;
+  }
+  return ai;
+} 
+
+#endif
+
+/* ----- Top and Right */
+
+#ifdef LPR
+#define tau 16
+
+static int8 Top(Fq C)
+{
+  return (tau1*(int32)(C+tau0)+16384)>>15;
+}
+
+static Fq Right(int8 T)
+{
+  return Fq_freeze(tau3*(int32)T-tau2);
+}
+#endif
+
+/* ----- small polynomials */
+
+#ifndef LPR
+
+/* 0 if Weightw_is(r), else -1 */
+static int Weightw_mask(small *r)
+{
+  int weight = 0;
+  int i;
+
+  for (i = 0;i < p;++i) weight += r[i]&1;
+  return int16_nonzero_mask(weight-w);
+}
+
+/* R3_fromR(R_fromRq(r)) */
+static void R3_fromRq(small *out,const Fq *r)
+{
+  int i;
+  for (i = 0;i < p;++i) out[i] = F3_freeze(r[i]);
+}
+
+/* h = f*g in the ring R3 */
+static void R3_mult(small *h,const small *f,const small *g)
+{
+  small fg[p+p-1];
+  small result;
+  int i,j;
+
+  for (i = 0;i < p;++i) {
+    result = 0;
+    for (j = 0;j <= i;++j) result = F3_freeze(result+f[j]*g[i-j]);
+    fg[i] = result;
+  }
+  for (i = p;i < p+p-1;++i) {
+    result = 0;
+    for (j = i-p+1;j < p;++j) result = F3_freeze(result+f[j]*g[i-j]);
+    fg[i] = result;
+  }
+
+  for (i = p+p-2;i >= p;--i) {
+    fg[i-p] = F3_freeze(fg[i-p]+fg[i]);
+    fg[i-p+1] = F3_freeze(fg[i-p+1]+fg[i]);
+  }
+
+  for (i = 0;i < p;++i) h[i] = fg[i];
+}
+
+/* returns 0 if recip succeeded; else -1 */
+static int R3_recip(small *out,const small *in)
+{ 
+  small f[p+1],g[p+1],v[p+1],r[p+1];
+  int i,loop,delta;
+  int sign,swap,t;
+  
+  for (i = 0;i < p+1;++i) v[i] = 0;
+  for (i = 0;i < p+1;++i) r[i] = 0;
+  r[0] = 1;
+  for (i = 0;i < p;++i) f[i] = 0;
+  f[0] = 1; f[p-1] = f[p] = -1;
+  for (i = 0;i < p;++i) g[p-1-i] = in[i];
+  g[p] = 0;
+    
+  delta = 1; 
+
+  for (loop = 0;loop < 2*p-1;++loop) {
+    for (i = p;i > 0;--i) v[i] = v[i-1];
+    v[0] = 0;
+    
+    sign = -g[0]*f[0];
+    swap = int16_negative_mask(-delta) & int16_nonzero_mask(g[0]);
+    delta ^= swap&(delta^-delta);
+    delta += 1;
+    
+    for (i = 0;i < p+1;++i) {
+      t = swap&(f[i]^g[i]); f[i] ^= t; g[i] ^= t;
+      t = swap&(v[i]^r[i]); v[i] ^= t; r[i] ^= t;
+    }
+  
+    for (i = 0;i < p+1;++i) g[i] = F3_freeze(g[i]+sign*f[i]);
+    for (i = 0;i < p+1;++i) r[i] = F3_freeze(r[i]+sign*v[i]);
+
+    for (i = 0;i < p;++i) g[i] = g[i+1];
+    g[p] = 0;
+  }
+  
+  sign = f[0];
+  for (i = 0;i < p;++i) out[i] = sign*v[p-1-i];
+  
+  return int16_nonzero_mask(delta);
+} 
+
+#endif
+
+/* ----- polynomials mod q */
+
+/* h = f*g in the ring Rq */
+static void Rq_mult_small(Fq *h,const Fq *f,const small *g)
+{
+  Fq fg[p+p-1];
+  Fq result;
+  int i,j;
+
+  for (i = 0;i < p;++i) {
+    result = 0;
+    for (j = 0;j <= i;++j) result = Fq_freeze(result+f[j]*(int32)g[i-j]);
+    fg[i] = result;
+  }
+  for (i = p;i < p+p-1;++i) {
+    result = 0;
+    for (j = i-p+1;j < p;++j) result = Fq_freeze(result+f[j]*(int32)g[i-j]);
+    fg[i] = result;
+  }
+
+  for (i = p+p-2;i >= p;--i) {
+    fg[i-p] = Fq_freeze(fg[i-p]+fg[i]);
+    fg[i-p+1] = Fq_freeze(fg[i-p+1]+fg[i]);
+  }
+
+  for (i = 0;i < p;++i) h[i] = fg[i];
+}
+
+#ifndef LPR
+
+/* h = 3f in Rq */
+static void Rq_mult3(Fq *h,const Fq *f)
+{
+  int i;
+  
+  for (i = 0;i < p;++i) h[i] = Fq_freeze(3*f[i]);
+}
+
+/* out = 1/(3*in) in Rq */
+/* returns 0 if recip succeeded; else -1 */
+static int Rq_recip3(Fq *out,const small *in)
+{ 
+  Fq f[p+1],g[p+1],v[p+1],r[p+1];
+  int i,loop,delta;
+  int swap,t;
+  int32 f0,g0;
+  Fq scale;
+
+  for (i = 0;i < p+1;++i) v[i] = 0;
+  for (i = 0;i < p+1;++i) r[i] = 0;
+  r[0] = Fq_recip(3);
+  for (i = 0;i < p;++i) f[i] = 0;
+  f[0] = 1; f[p-1] = f[p] = -1;
+  for (i = 0;i < p;++i) g[p-1-i] = in[i];
+  g[p] = 0;
+
+  delta = 1;
+
+  for (loop = 0;loop < 2*p-1;++loop) {
+    for (i = p;i > 0;--i) v[i] = v[i-1];
+    v[0] = 0;
+
+    swap = int16_negative_mask(-delta) & int16_nonzero_mask(g[0]);
+    delta ^= swap&(delta^-delta);
+    delta += 1;
+
+    for (i = 0;i < p+1;++i) {
+      t = swap&(f[i]^g[i]); f[i] ^= t; g[i] ^= t;
+      t = swap&(v[i]^r[i]); v[i] ^= t; r[i] ^= t;
+    }
+
+    f0 = f[0];
+    g0 = g[0];
+    for (i = 0;i < p+1;++i) g[i] = Fq_freeze(f0*g[i]-g0*f[i]);
+    for (i = 0;i < p+1;++i) r[i] = Fq_freeze(f0*r[i]-g0*v[i]);
+
+    for (i = 0;i < p;++i) g[i] = g[i+1];
+    g[p] = 0;
+  }
+
+  scale = Fq_recip(f[0]);
+  for (i = 0;i < p;++i) out[i] = Fq_freeze(scale*(int32)v[p-1-i]);
+
+  return int16_nonzero_mask(delta);
+}
+
+#endif
+
+/* ----- rounded polynomials mod q */
+
+static void Round(Fq *out,const Fq *a)
+{
+  int i;
+  for (i = 0;i < p;++i) out[i] = a[i]-F3_freeze(a[i]);
+}
+
+/* ----- sorting to generate short polynomial */
+
+static void Short_fromlist(small *out,const uint32 *in)
+{
+  uint32 L[p];
+  int i;
+
+  for (i = 0;i < w;++i) L[i] = in[i]&(uint32)-2;
+  for (i = w;i < p;++i) L[i] = (in[i]&(uint32)-3)|1;
+  crypto_sort_uint32(L,p);
+  for (i = 0;i < p;++i) out[i] = (L[i]&3)-1;
+}
+
+/* ----- underlying hash function */
+
+#define Hash_bytes 32
+
+/* e.g., b = 0 means out = Hash0(in) */
+static void Hash_prefix(unsigned char *out,int b,const unsigned char *in,int inlen)
+{
+  unsigned char x[inlen+1];
+  unsigned char h[64];
+  int i;
+
+  x[0] = b;
+  for (i = 0;i < inlen;++i) x[i+1] = in[i];
+  crypto_hash_sha512(h,x,inlen+1);
+  for (i = 0;i < 32;++i) out[i] = h[i];
+}
+
+/* ----- higher-level randomness */
+
+static uint32 urandom32(void)
+{
+  unsigned char c[4];
+  uint32 out[4];
+
+  randombytes(c,4);
+  out[0] = (uint32)c[0];
+  out[1] = ((uint32)c[1])<<8;
+  out[2] = ((uint32)c[2])<<16;
+  out[3] = ((uint32)c[3])<<24;
+  return out[0]+out[1]+out[2]+out[3];
+}
+
+static void Short_random(small *out)
+{
+  uint32 L[p];
+  int i;
+
+  for (i = 0;i < p;++i) L[i] = urandom32();
+  Short_fromlist(out,L);
+}
+
+#ifndef LPR
+
+static void Small_random(small *out)
+{
+  int i;
+
+  for (i = 0;i < p;++i) out[i] = (((urandom32()&0x3fffffff)*3)>>30)-1;
+}
+
+#endif
+
+/* ----- Streamlined NTRU Prime Core */
+
+#ifndef LPR
+
+/* h,(f,ginv) = KeyGen() */
+static void KeyGen(Fq *h,small *f,small *ginv)
+{
+  small g[p];
+  Fq finv[p];
+  
+  for (;;) {
+    Small_random(g);
+    if (R3_recip(ginv,g) == 0) break;
+  }
+  Short_random(f);
+  Rq_recip3(finv,f); /* always works */
+  Rq_mult_small(h,finv,g);
+}
+
+/* c = Encrypt(r,h) */
+static void Encrypt(Fq *c,const small *r,const Fq *h)
+{
+  Fq hr[p];
+
+  Rq_mult_small(hr,h,r);
+  Round(c,hr);
+}
+
+/* r = Decrypt(c,(f,ginv)) */
+static void Decrypt(small *r,const Fq *c,const small *f,const small *ginv)
+{
+  Fq cf[p];
+  Fq cf3[p];
+  small e[p];
+  small ev[p];
+  int mask;
+  int i;
+
+  Rq_mult_small(cf,c,f);
+  Rq_mult3(cf3,cf);
+  R3_fromRq(e,cf3);
+  R3_mult(ev,e,ginv);
+
+  mask = Weightw_mask(ev); /* 0 if weight w, else -1 */
+  for (i = 0;i < w;++i) r[i] = ((ev[i]^1)&~mask)^1;
+  for (i = w;i < p;++i) r[i] = ev[i]&~mask;
+}
+  
+#endif
+
+/* ----- NTRU LPRime Core */
+
+#ifdef LPR
+
+/* (G,A),a = KeyGen(G); leaves G unchanged */
+static void KeyGen(Fq *A,small *a,const Fq *G)
+{
+  Fq aG[p];
+
+  Short_random(a);
+  Rq_mult_small(aG,G,a);
+  Round(A,aG);
+}
+
+/* B,T = Encrypt(r,(G,A),b) */
+static void Encrypt(Fq *B,int8 *T,const int8 *r,const Fq *G,const Fq *A,const small *b)
+{
+  Fq bG[p];
+  Fq bA[p];
+  int i;
+
+  Rq_mult_small(bG,G,b);
+  Round(B,bG);
+  Rq_mult_small(bA,A,b);
+  for (i = 0;i < I;++i) T[i] = Top(Fq_freeze(bA[i]+r[i]*q12));
+}
+
+/* r = Decrypt((B,T),a) */
+static void Decrypt(int8 *r,const Fq *B,const int8 *T,const small *a)
+{
+  Fq aB[p];
+  int i;
+
+  Rq_mult_small(aB,B,a);
+  for (i = 0;i < I;++i)
+    r[i] = -int16_negative_mask(Fq_freeze(Right(T[i])-aB[i]+4*w+1));
+}
+    
+#endif
+
+/* ----- encoding I-bit inputs */
+
+#ifdef LPR
+
+#define Inputs_bytes (I/8)
+typedef int8 Inputs[I]; /* passed by reference */
+
+static void Inputs_encode(unsigned char *s,const Inputs r)
+{
+  int i;
+  for (i = 0;i < Inputs_bytes;++i) s[i] = 0;
+  for (i = 0;i < I;++i) s[i>>3] |= r[i]<<(i&7);
+}
+
+#endif
+
+/* ----- Expand */
+
+#ifdef LPR
+
+static const unsigned char aes_nonce[16] = {0};
+
+static void Expand(uint32 *L,const unsigned char *k)
+{
+  int i;
+  crypto_stream_aes256ctr((unsigned char *) L,4*p,aes_nonce,k);
+  for (i = 0;i < p;++i) {
+    uint32 L0 = ((unsigned char *) L)[4*i];
+    uint32 L1 = ((unsigned char *) L)[4*i+1];
+    uint32 L2 = ((unsigned char *) L)[4*i+2];
+    uint32 L3 = ((unsigned char *) L)[4*i+3];
+    L[i] = L0+(L1<<8)+(L2<<16)+(L3<<24);
+  }
+}
+
+#endif
+
+/* ----- Seeds */
+
+#ifdef LPR
+
+#define Seeds_bytes 32
+
+static void Seeds_random(unsigned char *s)
+{
+  randombytes(s,Seeds_bytes);
+}
+
+#endif
+
+/* ----- Generator, HashShort */
+
+#ifdef LPR
+
+/* G = Generator(k) */
+static void Generator(Fq *G,const unsigned char *k)
+{
+  uint32 L[p];
+  int i;
+
+  Expand(L,k);
+  for (i = 0;i < p;++i) G[i] = uint32_mod_uint14(L[i],q)-q12;
+}
+
+/* out = HashShort(r) */
+static void HashShort(small *out,const Inputs r)
+{
+  unsigned char s[Inputs_bytes];
+  unsigned char h[Hash_bytes];
+  uint32 L[p];
+
+  Inputs_encode(s,r);
+  Hash_prefix(h,5,s,sizeof s);
+  Expand(L,h);
+  Short_fromlist(out,L);
+}
+
+#endif
+  
+/* ----- NTRU LPRime Expand */
+
+#ifdef LPR
+
+/* (S,A),a = XKeyGen() */
+static void XKeyGen(unsigned char *S,Fq *A,small *a)
+{
+  Fq G[p];
+
+  Seeds_random(S);
+  Generator(G,S);
+  KeyGen(A,a,G);
+}
+
+/* B,T = XEncrypt(r,(S,A)) */
+static void XEncrypt(Fq *B,int8 *T,const int8 *r,const unsigned char *S,const Fq *A)
+{
+  Fq G[p];
+  small b[p];
+
+  Generator(G,S);
+  HashShort(b,r);
+  Encrypt(B,T,r,G,A,b);
+}
+
+#define XDecrypt Decrypt
+
+#endif
+
+/* ----- encoding small polynomials (including short polynomials) */
+
+#define Small_bytes ((p+3)/4)
+
+/* these are the only functions that rely on p mod 4 = 1 */
+
+static void Small_encode(unsigned char *s,const small *f)
+{
+  small x;
+  int i;
+
+  for (i = 0;i < p/4;++i) {
+    x = *f++ + 1;
+    x += (*f++ + 1)<<2;
+    x += (*f++ + 1)<<4;
+    x += (*f++ + 1)<<6;
+    *s++ = x;
+  }
+  x = *f++ + 1;
+  *s++ = x;
+}
+
+static void Small_decode(small *f,const unsigned char *s)
+{
+  unsigned char x;
+  int i;
+
+  for (i = 0;i < p/4;++i) {
+    x = *s++;
+    *f++ = ((small)(x&3))-1; x >>= 2;
+    *f++ = ((small)(x&3))-1; x >>= 2;
+    *f++ = ((small)(x&3))-1; x >>= 2;
+    *f++ = ((small)(x&3))-1;
+  }
+  x = *s++;
+  *f++ = ((small)(x&3))-1;
+}
+
+/* ----- encoding general polynomials */
+
+#ifndef LPR
+
+static void Rq_encode(unsigned char *s,const Fq *r)
+{
+  uint16 R[p],M[p];
+  int i;
+  
+  for (i = 0;i < p;++i) R[i] = r[i]+q12;
+  for (i = 0;i < p;++i) M[i] = q;
+  Encode(s,R,M,p);
+}
+
+static void Rq_decode(Fq *r,const unsigned char *s)
+{
+  uint16 R[p],M[p];
+  int i;
+
+  for (i = 0;i < p;++i) M[i] = q;
+  Decode(R,s,M,p);
+  for (i = 0;i < p;++i) r[i] = ((Fq)R[i])-q12;
+}
+  
+#endif
+
+/* ----- encoding rounded polynomials */
+
+static void Rounded_encode(unsigned char *s,const Fq *r)
+{
+  uint16 R[p],M[p];
+  int i;
+
+  for (i = 0;i < p;++i) R[i] = ((r[i]+q12)*10923)>>15;
+  for (i = 0;i < p;++i) M[i] = (q+2)/3;
+  Encode(s,R,M,p);
+}
+
+static void Rounded_decode(Fq *r,const unsigned char *s)
+{
+  uint16 R[p],M[p];
+  int i;
+
+  for (i = 0;i < p;++i) M[i] = (q+2)/3;
+  Decode(R,s,M,p);
+  for (i = 0;i < p;++i) r[i] = R[i]*3-q12;
+}
+
+/* ----- encoding top polynomials */
+
+#ifdef LPR
+
+#define Top_bytes (I/2)
+
+static void Top_encode(unsigned char *s,const int8 *T)
+{
+  int i;
+  for (i = 0;i < Top_bytes;++i)
+    s[i] = T[2*i]+(T[2*i+1]<<4);
+}
+
+static void Top_decode(int8 *T,const unsigned char *s)
+{
+  int i;
+  for (i = 0;i < Top_bytes;++i) {
+    T[2*i] = s[i]&15;
+    T[2*i+1] = s[i]>>4;
+  }
+}
+
+#endif
+
+/* ----- Streamlined NTRU Prime Core plus encoding */
+
+#ifndef LPR
+
+typedef small Inputs[p]; /* passed by reference */
+#define Inputs_random Short_random
+#define Inputs_encode Small_encode
+#define Inputs_bytes Small_bytes
+
+#define Ciphertexts_bytes Rounded_bytes
+#define SecretKeys_bytes (2*Small_bytes)
+#define PublicKeys_bytes Rq_bytes
+
+/* pk,sk = ZKeyGen() */
+static void ZKeyGen(unsigned char *pk,unsigned char *sk)
+{
+  Fq h[p];
+  small f[p],v[p];
+
+  KeyGen(h,f,v);
+  Rq_encode(pk,h);
+  Small_encode(sk,f); sk += Small_bytes;
+  Small_encode(sk,v);
+}
+
+/* C = ZEncrypt(r,pk) */
+static void ZEncrypt(unsigned char *C,const Inputs r,const unsigned char *pk)
+{
+  Fq h[p];
+  Fq c[p];
+  Rq_decode(h,pk);
+  Encrypt(c,r,h);
+  Rounded_encode(C,c);
+}
+
+/* r = ZDecrypt(C,sk) */
+static void ZDecrypt(Inputs r,const unsigned char *C,const unsigned char *sk)
+{
+  small f[p],v[p];
+  Fq c[p];
+
+  Small_decode(f,sk); sk += Small_bytes;
+  Small_decode(v,sk);
+  Rounded_decode(c,C);
+  Decrypt(r,c,f,v);
+}
+
+#endif
+
+/* ----- NTRU LPRime Expand plus encoding */
+
+#ifdef LPR
+
+#define Ciphertexts_bytes (Rounded_bytes+Top_bytes)
+#define SecretKeys_bytes Small_bytes
+#define PublicKeys_bytes (Seeds_bytes+Rounded_bytes)
+
+static void Inputs_random(Inputs r)
+{
+  unsigned char s[Inputs_bytes];
+  int i;
+
+  randombytes(s,sizeof s);
+  for (i = 0;i < I;++i) r[i] = 1&(s[i>>3]>>(i&7));
+}
+
+/* pk,sk = ZKeyGen() */
+static void ZKeyGen(unsigned char *pk,unsigned char *sk)
+{
+  Fq A[p];
+  small a[p];
+
+  XKeyGen(pk,A,a); pk += Seeds_bytes;
+  Rounded_encode(pk,A);
+  Small_encode(sk,a);
+}
+
+/* c = ZEncrypt(r,pk) */
+static void ZEncrypt(unsigned char *c,const Inputs r,const unsigned char *pk)
+{
+  Fq A[p];
+  Fq B[p];
+  int8 T[I];
+
+  Rounded_decode(A,pk+Seeds_bytes);
+  XEncrypt(B,T,r,pk,A);
+  Rounded_encode(c,B); c += Rounded_bytes;
+  Top_encode(c,T);
+}
+
+/* r = ZDecrypt(C,sk) */
+static void ZDecrypt(Inputs r,const unsigned char *c,const unsigned char *sk)
+{
+  small a[p];
+  Fq B[p];
+  int8 T[I];
+
+  Small_decode(a,sk);
+  Rounded_decode(B,c);
+  Top_decode(T,c+Rounded_bytes);
+  XDecrypt(r,B,T,a);
+}
+
+#endif
+
+/* ----- confirmation hash */
+
+#define Confirm_bytes 32
+
+/* h = HashConfirm(r,pk,cache); cache is Hash4(pk) */
+static void HashConfirm(unsigned char *h,const unsigned char *r,const unsigned char *pk,const unsigned char *cache)
+{
+#ifndef LPR
+  unsigned char x[Hash_bytes*2];
+  int i;
+
+  Hash_prefix(x,3,r,Inputs_bytes);
+  for (i = 0;i < Hash_bytes;++i) x[Hash_bytes+i] = cache[i];
+#else
+  unsigned char x[Inputs_bytes+Hash_bytes];
+  int i;
+
+  for (i = 0;i < Inputs_bytes;++i) x[i] = r[i];
+  for (i = 0;i < Hash_bytes;++i) x[Inputs_bytes+i] = cache[i];
+#endif
+  Hash_prefix(h,2,x,sizeof x);
+}
+
+/* ----- session-key hash */
+
+/* k = HashSession(b,y,z) */
+static void HashSession(unsigned char *k,int b,const unsigned char *y,const unsigned char *z)
+{
+#ifndef LPR
+  unsigned char x[Hash_bytes+Ciphertexts_bytes+Confirm_bytes];
+  int i;
+
+  Hash_prefix(x,3,y,Inputs_bytes);
+  for (i = 0;i < Ciphertexts_bytes+Confirm_bytes;++i) x[Hash_bytes+i] = z[i];
+#else
+  unsigned char x[Inputs_bytes+Ciphertexts_bytes+Confirm_bytes];
+  int i;
+
+  for (i = 0;i < Inputs_bytes;++i) x[i] = y[i];
+  for (i = 0;i < Ciphertexts_bytes+Confirm_bytes;++i) x[Inputs_bytes+i] = z[i];
+#endif
+  Hash_prefix(k,b,x,sizeof x);
+}
+
+/* ----- Streamlined NTRU Prime and NTRU LPRime */
+
+/* pk,sk = KEM_KeyGen() */
+static void KEM_KeyGen(unsigned char *pk,unsigned char *sk)
+{
+  int i;
+
+  ZKeyGen(pk,sk); sk += SecretKeys_bytes;
+  for (i = 0;i < PublicKeys_bytes;++i) *sk++ = pk[i];
+  randombytes(sk,Inputs_bytes); sk += Inputs_bytes;
+  Hash_prefix(sk,4,pk,PublicKeys_bytes);
+}
+
+/* c,r_enc = Hide(r,pk,cache); cache is Hash4(pk) */
+static void Hide(unsigned char *c,unsigned char *r_enc,const Inputs r,const unsigned char *pk,const unsigned char *cache)
+{
+  Inputs_encode(r_enc,r);
+  ZEncrypt(c,r,pk); c += Ciphertexts_bytes;
+  HashConfirm(c,r_enc,pk,cache);
+}
+
+/* c,k = Encap(pk) */
+static void Encap(unsigned char *c,unsigned char *k,const unsigned char *pk)
+{
+  Inputs r;
+  unsigned char r_enc[Inputs_bytes];
+  unsigned char cache[Hash_bytes];
+
+  Hash_prefix(cache,4,pk,PublicKeys_bytes);
+  Inputs_random(r);
+  Hide(c,r_enc,r,pk,cache);
+  HashSession(k,1,r_enc,c);
+}
+
+/* 0 if matching ciphertext+confirm, else -1 */
+static int Ciphertexts_diff_mask(const unsigned char *c,const unsigned char *c2)
+{
+  uint16 differentbits = 0;
+  int len = Ciphertexts_bytes+Confirm_bytes;
+
+  while (len-- > 0) differentbits |= (*c++)^(*c2++);
+  return (1&((differentbits-1)>>8))-1;
+}
+
+/* k = Decap(c,sk) */
+static void Decap(unsigned char *k,const unsigned char *c,const unsigned char *sk)
+{
+  const unsigned char *pk = sk + SecretKeys_bytes;
+  const unsigned char *rho = pk + PublicKeys_bytes;
+  const unsigned char *cache = rho + Inputs_bytes;
+  Inputs r;
+  unsigned char r_enc[Inputs_bytes];
+  unsigned char cnew[Ciphertexts_bytes+Confirm_bytes];
+  int mask;
+  int i;
+
+  ZDecrypt(r,c,sk);
+  Hide(cnew,r_enc,r,pk,cache);
+  mask = Ciphertexts_diff_mask(c,cnew);
+  for (i = 0;i < Inputs_bytes;++i) r_enc[i] ^= mask&(r_enc[i]^rho[i]);
+  HashSession(k,1+mask,r_enc,c);
+}
+
+/* ----- crypto_kem API */
+
+
+int crypto_kem_sntrup761_keypair(unsigned char *pk,unsigned char *sk)
+{
+  KEM_KeyGen(pk,sk);
+  return 0;
+}
+
+int crypto_kem_sntrup761_enc(unsigned char *c,unsigned char *k,const unsigned char *pk)
+{
+  Encap(c,k,pk);
+  return 0;
+}
+
+int crypto_kem_sntrup761_dec(unsigned char *k,const unsigned char *c,const unsigned char *sk)
+{
+  Decap(k,c,sk);
+  return 0;
+}
+
diff --git a/sbin/iked/sntrup761.sh b/sbin/iked/sntrup761.sh
new file mode 100644 (file)
index 0000000..e8ebfbd
--- /dev/null
@@ -0,0 +1,85 @@
+#!/bin/sh
+#       $OpenBSD: sntrup761.sh,v 1.1 2021/05/28 18:01:39 tobhe Exp $
+#       Placed in the Public Domain.
+#
+AUTHOR="supercop-20201130/crypto_kem/sntrup761/ref/implementors"
+FILES="
+       supercop-20201130/crypto_sort/int32/portable4/int32_minmax.inc
+       supercop-20201130/crypto_sort/int32/portable4/sort.c
+       supercop-20201130/crypto_sort/uint32/useint32/sort.c
+       supercop-20201130/crypto_kem/sntrup761/ref/uint32.c
+       supercop-20201130/crypto_kem/sntrup761/ref/int32.c
+       supercop-20201130/crypto_kem/sntrup761/ref/paramsmenu.h
+       supercop-20201130/crypto_kem/sntrup761/ref/params.h
+       supercop-20201130/crypto_kem/sntrup761/ref/Decode.h
+       supercop-20201130/crypto_kem/sntrup761/ref/Decode.c
+       supercop-20201130/crypto_kem/sntrup761/ref/Encode.h
+       supercop-20201130/crypto_kem/sntrup761/ref/Encode.c
+       supercop-20201130/crypto_kem/sntrup761/ref/kem.c
+"
+###
+
+set -e
+cd $1
+echo -n '/*  $'
+echo 'OpenBSD: $ */'
+echo
+echo '/*'
+echo ' * Public Domain, Authors:'
+sed -e '/Alphabetical order:/d' -e 's/^/ * - /' < $AUTHOR
+echo ' */'
+echo
+echo '#include <string.h>'
+echo '#include "crypto_api.h"'
+echo
+# Map the types used in this code to the ones in crypto_api.h.  We use #define
+# instead of typedef since some systems have existing intXX types and do not
+# permit multiple typedefs even if they do not conflict.
+for t in int8 uint8 int16 uint16 int32 uint32 int64 uint64; do
+       echo "#define $t crypto_${t}"
+done
+echo
+for i in $FILES; do
+       echo "/* from $i */"
+       # Changes to all files:
+       #  - remove all includes, we inline everything required.
+       #  - make functions not required elsewhere static.
+       #  - rename the functions we do use.
+       #  - remove unneccesary defines and externs.
+       sed -e "/#include/d" \
+           -e "s/crypto_kem_/crypto_kem_sntrup761_/g" \
+           -e "s/^void /static void /g" \
+           -e "s/^int16 /static int16 /g" \
+           -e "s/^uint16 /static uint16 /g" \
+           -e "/^extern /d" \
+           -e '/CRYPTO_NAMESPACE/d' \
+           -e "/^#define int32 crypto_int32/d" \
+           $i | \
+       case "$i" in
+       # Use int64_t for intermediate values in int32_MINMAX to prevent signed
+       # 32-bit integer overflow when called by crypto_sort_uint32.
+       */int32_minmax.inc)
+           sed -e "s/int32 ab = b ^ a/int64_t ab = (int64_t)b ^ (int64_t)a/" \
+               -e "s/int32 c = b - a/int64_t c = (int64_t)b - (int64_t)a/"
+           ;;
+       */int32/portable4/sort.c)
+           sed -e "s/void crypto_sort/void crypto_sort_int32/g"
+           ;;
+       */uint32/useint32/sort.c)
+           sed -e "s/void crypto_sort/void crypto_sort_uint32/g"
+           ;;
+       # Remove unused function to prevent warning.
+       */crypto_kem/sntrup761/ref/int32.c)
+           sed -e '/ int32_div_uint14/,/^}$/d'
+           ;;
+       # Remove unused function to prevent warning.
+       */crypto_kem/sntrup761/ref/uint32.c)
+           sed -e '/ uint32_div_uint14/,/^}$/d'
+           ;;
+       # Default: pass through.
+       *)
+           cat
+           ;;
+       esac
+       echo
+done