1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

Implement OpenSSH 9.x's NTRU Prime / Curve25519 kex.

This consists of DJB's 'Streamlined NTRU Prime' quantum-resistant
cryptosystem, currently in round 3 of the NIST post-quantum key
exchange competition; it's run in parallel with ordinary Curve25519,
and generates a shared secret combining the output of both systems.

(Hence, even if you don't trust this newfangled NTRU Prime thing at
all, it's at least no _less_ secure than the kex you were using
already.)

As the OpenSSH developers point out, key exchange is the most urgent
thing to make quantum-resistant, even before working quantum computers
big enough to break crypto become available, because a break of the
kex algorithm can be applied retroactively to recordings of your past
sessions. By contrast, authentication is a real-time protocol, and can
only be broken by a quantum computer if there's one available to
attack you _already_.

I've implemented both sides of the mechanism, so that PuTTY and Uppity
both support it. In my initial testing, the two sides can both
interoperate with the appropriate half of OpenSSH, and also (of
course, but it would be embarrassing to mess it up) with each other.
This commit is contained in:
Simon Tatham 2022-04-15 17:19:47 +01:00
parent e59ee96554
commit faf1601a55
14 changed files with 2358 additions and 0 deletions

View File

@ -567,6 +567,8 @@ static void kexlist_handler(union control *ctrl, dlgparam *dlg,
{ "Diffie-Hellman group exchange", KEX_DHGEX }, { "Diffie-Hellman group exchange", KEX_DHGEX },
{ "RSA-based key exchange", KEX_RSA }, { "RSA-based key exchange", KEX_RSA },
{ "ECDH key exchange", KEX_ECDH }, { "ECDH key exchange", KEX_ECDH },
{ "NTRU Prime / Curve25519 hybrid kex"
" (quantum-resistant)", KEX_NTRU_HYBRID },
{ "-- warn below here --", KEX_WARN } { "-- warn below here --", KEX_WARN }
}; };

View File

@ -20,6 +20,7 @@ add_sources_from_current_dir(crypto
mac_simple.c mac_simple.c
md5.c md5.c
mpint.c mpint.c
ntru.c
prng.c prng.c
pubkey-pem.c pubkey-pem.c
pubkey-ppk.c pubkey-ppk.c

1915
crypto/ntru.c Normal file

File diff suppressed because it is too large Load Diff

53
crypto/ntru.h Normal file
View File

@ -0,0 +1,53 @@
/*
* Internal functions for the NTRU cryptosystem, exposed in a header
* that is expected to be included only by ntru.c and test programs.
*/
#ifndef PUTTY_CRYPTO_NTRU_H
#define PUTTY_CRYPTO_NTRU_H
unsigned ntru_ring_invert(uint16_t *out, const uint16_t *in,
unsigned p, unsigned q);
void ntru_ring_multiply(uint16_t *out, const uint16_t *a, const uint16_t *b,
unsigned p, unsigned q);
void ntru_mod3(uint16_t *out, const uint16_t *in, unsigned p, unsigned q);
void ntru_round3(uint16_t *out, const uint16_t *in, unsigned p, unsigned q);
void ntru_bias(uint16_t *out, const uint16_t *in, unsigned bias,
unsigned p, unsigned q);
void ntru_scale(uint16_t *out, const uint16_t *in, uint16_t scale,
unsigned p, unsigned q);
NTRUEncodeSchedule *ntru_encode_schedule(const uint16_t *ms_in, size_t n);
void ntru_encode_schedule_free(NTRUEncodeSchedule *sched);
size_t ntru_encode_schedule_length(NTRUEncodeSchedule *sched);
size_t ntru_encode_schedule_nvals(NTRUEncodeSchedule *sched);
void ntru_encode(NTRUEncodeSchedule *sched, const uint16_t *rs_in,
BinarySink *bs);
void ntru_decode(NTRUEncodeSchedule *sched, uint16_t *rs_out, ptrlen data);
void ntru_gen_short(uint16_t *v, unsigned p, unsigned w);
NTRUKeyPair *ntru_keygen_attempt(unsigned p, unsigned q, unsigned w);
NTRUKeyPair *ntru_keygen(unsigned p, unsigned q, unsigned w);
void ntru_keypair_free(NTRUKeyPair *keypair);
void ntru_encrypt(uint16_t *ciphertext, const uint16_t *plaintext,
uint16_t *pubkey, unsigned p, unsigned q);
void ntru_decrypt(uint16_t *plaintext, const uint16_t *ciphertext,
NTRUKeyPair *keypair);
void ntru_encode_pubkey(const uint16_t *pubkey, unsigned p, unsigned q,
BinarySink *bs);
ptrlen ntru_decode_pubkey(uint16_t *pubkey, unsigned p, unsigned q,
BinarySource *src);
void ntru_encode_ciphertext(const uint16_t *ciphertext, unsigned p, unsigned q,
BinarySink *bs);
ptrlen ntru_decode_ciphertext(uint16_t *ct, NTRUKeyPair *keypair,
BinarySource *src);
void ntru_encode_plaintext(const uint16_t *plaintext, unsigned p,
BinarySink *bs);
unsigned ntru_keypair_p(NTRUKeyPair *keypair);
const uint16_t *ntru_pubkey(NTRUKeyPair *keypair);
#endif /* PUTTY_CRYPTO_NTRU_H */

2
defs.h
View File

@ -168,6 +168,8 @@ typedef struct ssh2_ciphers ssh2_ciphers;
typedef struct dh_ctx dh_ctx; typedef struct dh_ctx dh_ctx;
typedef struct ecdh_key ecdh_key; typedef struct ecdh_key ecdh_key;
typedef struct ecdh_keyalg ecdh_keyalg; typedef struct ecdh_keyalg ecdh_keyalg;
typedef struct NTRUKeyPair NTRUKeyPair;
typedef struct NTRUEncodeSchedule NTRUEncodeSchedule;
typedef struct dlgparam dlgparam; typedef struct dlgparam dlgparam;

View File

@ -426,6 +426,7 @@ enum {
KEX_DHGEX, KEX_DHGEX,
KEX_RSA, KEX_RSA,
KEX_ECDH, KEX_ECDH,
KEX_NTRU_HYBRID,
KEX_MAX KEX_MAX
}; };

View File

@ -28,6 +28,7 @@ static const struct keyvalwhere ciphernames[] = {
* compatibility warts in load_open_settings(), and should be kept * compatibility warts in load_open_settings(), and should be kept
* in sync with those. */ * in sync with those. */
static const struct keyvalwhere kexnames[] = { static const struct keyvalwhere kexnames[] = {
{ "ntru-curve25519", KEX_NTRU_HYBRID, -1, +1 },
{ "ecdh", KEX_ECDH, -1, +1 }, { "ecdh", KEX_ECDH, -1, +1 },
/* This name is misleading: it covers both SHA-256 and SHA-1 variants */ /* This name is misleading: it covers both SHA-256 and SHA-1 variants */
{ "dh-gex-sha1", KEX_DHGEX, -1, -1 }, { "dh-gex-sha1", KEX_DHGEX, -1, -1 },

1
ssh.h
View File

@ -1058,6 +1058,7 @@ extern const ssh_kex ssh_ec_kex_nistp256;
extern const ssh_kex ssh_ec_kex_nistp384; extern const ssh_kex ssh_ec_kex_nistp384;
extern const ssh_kex ssh_ec_kex_nistp521; extern const ssh_kex ssh_ec_kex_nistp521;
extern const ssh_kexes ssh_ecdh_kex; extern const ssh_kexes ssh_ecdh_kex;
extern const ssh_kexes ssh_ntru_hybrid_kex;
extern const ssh_keyalg ssh_dsa; extern const ssh_keyalg ssh_dsa;
extern const ssh_keyalg ssh_rsa; extern const ssh_keyalg ssh_rsa;
extern const ssh_keyalg ssh_rsa_sha256; extern const ssh_keyalg ssh_rsa_sha256;

View File

@ -533,6 +533,10 @@ static void ssh2_write_kexinit_lists(
preferred_kex[n_preferred_kex++] = preferred_kex[n_preferred_kex++] =
&ssh_ecdh_kex; &ssh_ecdh_kex;
break; break;
case KEX_NTRU_HYBRID:
preferred_kex[n_preferred_kex++] =
&ssh_ntru_hybrid_kex;
break;
case KEX_WARN: case KEX_WARN:
/* Flag for later. Don't bother if it's the last in /* Flag for later. Don't bother if it's the last in
* the list. */ * the list. */

View File

@ -1274,6 +1274,107 @@ class keygen(MyTestBase):
mr = miller_rabin_new(n) mr = miller_rabin_new(n)
self.assertEqual(miller_rabin_test(mr, 0x251), "failed") self.assertEqual(miller_rabin_test(mr, 0x251), "failed")
class ntru(MyTestBase):
def testMultiply(self):
self.assertEqual(
ntru_ring_multiply([1,1,1,1,1,1], [1,1,1,1,1,1], 11, 59),
[1,2,3,4,5,6,5,4,3,2,1])
self.assertEqual(ntru_ring_multiply(
[1,0,1,2,0,0,1,2,0,1,2], [2,0,0,1,0,1,2,2,2,0,2], 11, 3),
[1,0,0,0,0,0,0,0,0,0,0])
def testInvert(self):
# Over GF(3), x^11-x-1 factorises as
# (x^3+x^2+2) * (x^8+2*x^7+x^6+2*x^4+2*x^3+x^2+x+1)
# so we expect that 2,0,1,1 has no inverse, being one of those factors.
self.assertEqual(ntru_ring_invert([0], 11, 3), None)
self.assertEqual(ntru_ring_invert([1], 11, 3),
[1,0,0,0,0,0,0,0,0,0,0])
self.assertEqual(ntru_ring_invert([2,0,1,1], 11, 3), None)
self.assertEqual(ntru_ring_invert([1,0,1,2,0,0,1,2,0,1,2], 11, 3),
[2,0,0,1,0,1,2,2,2,0,2])
self.assertEqual(ntru_ring_invert([1,0,1,2,0,0,1,2,0,1,2], 11, 59),
[1,26,10,1,38,48,34,37,53,3,53])
def testMod3Round3(self):
# Try a prime congruent to 1 mod 3
self.assertEqual(ntru_mod3([4,5,6,0,1,2,3], 7, 7),
[0,1,-1,0,1,-1,0])
self.assertEqual(ntru_round3([4,5,6,0,1,2,3], 7, 7),
[-3,-3,0,0,0,3,3])
# And one congruent to 2 mod 3
self.assertEqual(ntru_mod3([6,7,8,9,10,0,1,2,3,4,5], 11, 11),
[1,-1,0,1,-1,0,1,-1,0,1,-1])
self.assertEqual(ntru_round3([6,7,8,9,10,0,1,2,3,4,5], 11, 11),
[-6,-3,-3,-3,0,0,0,3,3,3,6])
def testBiasScale(self):
self.assertEqual(ntru_bias([0,1,2,3,4,5,6,7,8,9,10], 4, 11, 11),
[4,5,6,7,8,9,10,0,1,2,3])
self.assertEqual(ntru_scale([0,1,2,3,4,5,6,7,8,9,10], 4, 11, 11),
[0,4,8,1,5,9,2,6,10,3,7])
def testEncode(self):
# Test a small case. Worked through in detail:
#
# Pass 1:
# Input list is (89:123, 90:234, 344:345, 432:456, 222:567)
# (89:123, 90:234) -> (89+123*90 : 123*234) = (11159:28782)
# Emit low byte of 11159 = 0x97, and get (43:113)
# (344:345, 432:456) -> (344+345*432 : 345*456) = (149384:157320)
# Emit low byte of 149384 = 0x88, and get (583:615)
# Odd pair (222:567) is copied to end of new list
# Final list is (43:113, 583:615, 222:567)
# Pass 2:
# Input list is (43:113, 583:615, 222:567)
# (43:113, 583:615) -> (43+113*583, 113*615) = (65922:69495)
# Emit low byte of 65922 = 0x82, and get (257:272)
# Odd pair (222:567) is copied to end of new list
# Final list is (257:272, 222:567)
# Pass 3:
# Input list is (257:272, 222:567)
# (257:272, 222:567) -> (257+272*222, 272*567) = (60641:154224)
# Emit low byte of 60641 = 0xe1, and get (236:603)
# Final list is (236:603)
# Cleanup:
# Emit low byte of 236 = 0xec, and get (0:3)
# Emit low byte of 0 = 0x00, and get (0:1)
ms = [123,234,345,456,567]
rs = [89,90,344,432,222]
encoding = unhex('978882e1ec00')
sched = ntru_encode_schedule(ms)
self.assertEqual(sched.encode(rs), encoding)
self.assertEqual(sched.decode(encoding), rs)
# Encode schedules for sntrup761 public keys and ciphertexts
pubsched = ntru_encode_schedule([4591]*761)
self.assertEqual(pubsched.length(), 1158)
ciphersched = ntru_encode_schedule([1531]*761)
self.assertEqual(ciphersched.length(), 1007)
# Test round-trip encoding using those schedules
testlist = list(range(761))
pubtext = pubsched.encode(testlist)
self.assertEqual(pubsched.decode(pubtext), testlist)
ciphertext = ciphersched.encode(testlist)
self.assertEqual(ciphersched.decode(ciphertext), testlist)
def testCore(self):
# My own set of NTRU Prime parameters, satisfying all the
# requirements and tiny enough for convenient testing
p, q, w = 11, 59, 3
with random_prng('ntru keygen seed'):
keypair = ntru_keygen(p, q, w)
plaintext = ntru_gen_short(p, w)
ciphertext = ntru_encrypt(plaintext, ntru_pubkey(keypair), p, q)
recovered = ntru_decrypt(ciphertext, keypair)
self.assertEqual(plaintext, recovered)
class crypt(MyTestBase): class crypt(MyTestBase):
def testSSH1Fingerprint(self): def testSSH1Fingerprint(self):
# Example key and reference fingerprint value generated by # Example key and reference fingerprint value generated by

View File

@ -352,6 +352,35 @@ FUNC(void, ecdh_key_getpublic, ARG(val_ecdh, key),
FUNC_WRAPPED(opt_val_string, ecdh_key_getkey, ARG(val_ecdh, key), FUNC_WRAPPED(opt_val_string, ecdh_key_getkey, ARG(val_ecdh, key),
ARG(val_string_ptrlen, pub)) ARG(val_string_ptrlen, pub))
/*
* NTRU and its subroutines.
*/
FUNC_WRAPPED(int16_list, ntru_ring_multiply, ARG(int16_list, a),
ARG(int16_list, b), ARG(uint, p), ARG(uint, q))
FUNC_WRAPPED(opt_int16_list, ntru_ring_invert, ARG(int16_list, r),
ARG(uint, p), ARG(uint, q))
FUNC_WRAPPED(int16_list, ntru_mod3, ARG(int16_list, r),
ARG(uint, p), ARG(uint, q))
FUNC_WRAPPED(int16_list, ntru_round3, ARG(int16_list, r),
ARG(uint, p), ARG(uint, q))
FUNC_WRAPPED(int16_list, ntru_bias, ARG(int16_list, r),
ARG(uint, bias), ARG(uint, p), ARG(uint, q))
FUNC_WRAPPED(int16_list, ntru_scale, ARG(int16_list, r),
ARG(uint, scale), ARG(uint, p), ARG(uint, q))
FUNC_WRAPPED(val_ntruencodeschedule, ntru_encode_schedule, ARG(int16_list, ms))
FUNC(uint, ntru_encode_schedule_length, ARG(val_ntruencodeschedule, sched))
FUNC_WRAPPED(void, ntru_encode, ARG(val_ntruencodeschedule, sched),
ARG(int16_list, rs), ARG(out_val_string_binarysink, data))
FUNC_WRAPPED(opt_int16_list, ntru_decode, ARG(val_ntruencodeschedule, sched),
ARG(val_string_ptrlen, data))
FUNC_WRAPPED(int16_list, ntru_gen_short, ARG(uint, p), ARG(uint, w))
FUNC(val_ntrukeypair, ntru_keygen, ARG(uint, p), ARG(uint, q), ARG(uint, w))
FUNC_WRAPPED(int16_list, ntru_pubkey, ARG(val_ntrukeypair, keypair))
FUNC_WRAPPED(int16_list, ntru_encrypt, ARG(int16_list, plaintext),
ARG(int16_list, pubkey), ARG(uint, p), ARG(uint, q))
FUNC_WRAPPED(int16_list, ntru_decrypt, ARG(int16_list, ciphertext),
ARG(val_ntrukeypair, keypair))
/* /*
* RSA key exchange, and also the BinarySource get function * RSA key exchange, and also the BinarySource get function
* get_ssh1_rsa_priv_agent, which is a convenient way to make an * get_ssh1_rsa_priv_agent, which is a convenient way to make an

View File

@ -35,6 +35,7 @@
#include "misc.h" #include "misc.h"
#include "mpint.h" #include "mpint.h"
#include "crypto/ecc.h" #include "crypto/ecc.h"
#include "crypto/ntru.h"
#include "proxy/cproxy.h" #include "proxy/cproxy.h"
static NORETURN PRINTF_LIKE(1, 2) void fatal_error(const char *p, ...) static NORETURN PRINTF_LIKE(1, 2) void fatal_error(const char *p, ...)
@ -96,6 +97,8 @@ uint64_t prng_reseed_time_ms(void)
X(pgc, PrimeGenerationContext *, primegen_free_context(v)) \ X(pgc, PrimeGenerationContext *, primegen_free_context(v)) \
X(pockle, Pockle *, pockle_free(v)) \ X(pockle, Pockle *, pockle_free(v)) \
X(millerrabin, MillerRabin *, miller_rabin_free(v)) \ X(millerrabin, MillerRabin *, miller_rabin_free(v)) \
X(ntrukeypair, NTRUKeyPair *, ntru_keypair_free(v)) \
X(ntruencodeschedule, NTRUEncodeSchedule *, ntru_encode_schedule_free(v)) \
/* end of list */ /* end of list */
typedef struct Value Value; typedef struct Value Value;
@ -221,6 +224,7 @@ typedef RsaSsh1Order TD_rsaorder;
typedef key_components *TD_keycomponents; typedef key_components *TD_keycomponents;
typedef const PrimeGenerationPolicy *TD_primegenpolicy; typedef const PrimeGenerationPolicy *TD_primegenpolicy;
typedef struct mpint_list TD_mpint_list; typedef struct mpint_list TD_mpint_list;
typedef struct int16_list *TD_int16_list;
typedef PockleStatus TD_pocklestatus; typedef PockleStatus TD_pocklestatus;
typedef struct mr_result TD_mr_result; typedef struct mr_result TD_mr_result;
typedef Argon2Flavour TD_argon2flavour; typedef Argon2Flavour TD_argon2flavour;
@ -385,6 +389,46 @@ static struct mpint_list get_mpint_list(BinarySource *in)
return mpl; return mpl;
} }
typedef struct int16_list {
size_t n;
uint16_t *integers;
} int16_list;
static void finaliser_int16_list_free(strbuf *out, void *vlist)
{
int16_list *list = (int16_list *)vlist;
sfree(list->integers);
sfree(list);
}
static int16_list *make_int16_list(size_t n)
{
int16_list *list = snew(int16_list);
list->n = n;
list->integers = snewn(n, uint16_t);
add_finaliser(finaliser_int16_list_free, list);
return list;
}
static int16_list *get_int16_list(BinarySource *in)
{
size_t n = get_uint(in);
int16_list *list = make_int16_list(n);
for (size_t i = 0; i < n; i++)
list->integers[i] = get_uint(in);
return list;
}
static void return_int16_list(strbuf *out, int16_list *list)
{
for (size_t i = 0; i < list->n; i++) {
if (i > 0)
put_byte(out, ',');
put_fmt(out, "%d", (int)(int16_t)list->integers[i]);
}
put_byte(out, '\n');
}
static void finaliser_return_uint(strbuf *out, void *ctx) static void finaliser_return_uint(strbuf *out, void *ctx)
{ {
unsigned *uval = (unsigned *)ctx; unsigned *uval = (unsigned *)ctx;
@ -543,6 +587,7 @@ NULLABLE_RETURN_WRAPPER(val_cipher, ssh_cipher *)
NULLABLE_RETURN_WRAPPER(val_hash, ssh_hash *) NULLABLE_RETURN_WRAPPER(val_hash, ssh_hash *)
NULLABLE_RETURN_WRAPPER(val_key, ssh_key *) NULLABLE_RETURN_WRAPPER(val_key, ssh_key *)
NULLABLE_RETURN_WRAPPER(val_mpint, mp_int *) NULLABLE_RETURN_WRAPPER(val_mpint, mp_int *)
NULLABLE_RETURN_WRAPPER(int16_list, int16_list *)
static void handle_hello(BinarySource *in, strbuf *out) static void handle_hello(BinarySource *in, strbuf *out)
{ {
@ -799,6 +844,130 @@ strbuf *ecdh_key_getkey_wrapper(ecdh_key *ek, ptrlen remoteKey)
return sb; return sb;
} }
static void int16_list_resize(int16_list *list, unsigned p)
{
list->integers = sresize(list->integers, p, uint16_t);
for (size_t i = list->n; i < p; i++)
list->integers[i] = 0;
}
#if 0
static int16_list ntru_ring_to_list_and_free(uint16_t *out, unsigned p)
{
struct mpint_list mpl;
mpl.n = p;
mpl->integers = snewn(p, mp_int *);
for (unsigned i = 0; i < p; i++)
mpl->integers[i] = mp_from_integer((int16_t)out[i]);
sfree(out);
add_finaliser(finaliser_sfree, mpl->integers);
return mpl;
}
#endif
int16_list *ntru_ring_multiply_wrapper(
int16_list *a, int16_list *b, unsigned p, unsigned q)
{
int16_list_resize(a, p);
int16_list_resize(b, p);
int16_list *out = make_int16_list(p);
ntru_ring_multiply(out->integers, a->integers, b->integers, p, q);
return out;
}
int16_list *ntru_ring_invert_wrapper(int16_list *in, unsigned p, unsigned q)
{
int16_list_resize(in, p);
int16_list *out = make_int16_list(p);
unsigned success = ntru_ring_invert(out->integers, in->integers, p, q);
if (!success)
return NULL;
return out;
}
int16_list *ntru_mod3_wrapper(int16_list *in, unsigned p, unsigned q)
{
int16_list_resize(in, p);
int16_list *out = make_int16_list(p);
ntru_mod3(out->integers, in->integers, p, q);
return out;
}
int16_list *ntru_round3_wrapper(int16_list *in, unsigned p, unsigned q)
{
int16_list_resize(in, p);
int16_list *out = make_int16_list(p);
ntru_round3(out->integers, in->integers, p, q);
return out;
}
int16_list *ntru_bias_wrapper(int16_list *in, unsigned bias,
unsigned p, unsigned q)
{
int16_list_resize(in, p);
int16_list *out = make_int16_list(p);
ntru_bias(out->integers, in->integers, bias, p, q);
return out;
}
int16_list *ntru_scale_wrapper(int16_list *in, unsigned scale,
unsigned p, unsigned q)
{
int16_list_resize(in, p);
int16_list *out = make_int16_list(p);
ntru_scale(out->integers, in->integers, scale, p, q);
return out;
}
NTRUEncodeSchedule *ntru_encode_schedule_wrapper(int16_list *in)
{
return ntru_encode_schedule(in->integers, in->n);
}
void ntru_encode_wrapper(NTRUEncodeSchedule *sched, int16_list *rs,
BinarySink *bs)
{
ntru_encode(sched, rs->integers, bs);
}
int16_list *ntru_decode_wrapper(NTRUEncodeSchedule *sched, ptrlen data)
{
int16_list *out = make_int16_list(ntru_encode_schedule_nvals(sched));
ntru_decode(sched, out->integers, data);
return out;
}
int16_list *ntru_gen_short_wrapper(unsigned p, unsigned w)
{
int16_list *out = make_int16_list(p);
ntru_gen_short(out->integers, p, w);
return out;
}
int16_list *ntru_pubkey_wrapper(NTRUKeyPair *keypair)
{
unsigned p = ntru_keypair_p(keypair);
int16_list *out = make_int16_list(p);
memcpy(out->integers, ntru_pubkey(keypair), p*sizeof(uint16_t));
return out;
}
int16_list *ntru_encrypt_wrapper(int16_list *plaintext, int16_list *pubkey,
unsigned p, unsigned q)
{
int16_list *out = make_int16_list(p);
ntru_encrypt(out->integers, plaintext->integers, pubkey->integers, p, q);
return out;
}
int16_list *ntru_decrypt_wrapper(int16_list *ciphertext, NTRUKeyPair *keypair)
{
unsigned p = ntru_keypair_p(keypair);
int16_list *out = make_int16_list(p);
ntru_decrypt(out->integers, ciphertext->integers, keypair);
return out;
}
strbuf *rsa_ssh1_encrypt_wrapper(ptrlen input, RSAKey *key) strbuf *rsa_ssh1_encrypt_wrapper(ptrlen input, RSAKey *key)
{ {
/* Fold the boolean return value in C into the string return value /* Fold the boolean return value in C into the string return value

View File

@ -199,6 +199,13 @@ def make_argword(arg, argtype, fnname, argindex, argname, to_preserve):
sublist.append(make_argword(val, ("val_mpint", False), sublist.append(make_argword(val, ("val_mpint", False),
fnname, argindex, argname, to_preserve)) fnname, argindex, argname, to_preserve))
return b" ".join(coerce_to_bytes(sub) for sub in sublist) return b" ".join(coerce_to_bytes(sub) for sub in sublist)
if typename == "int16_list":
sublist = [make_argword(len(arg), ("uint", False),
fnname, argindex, argname, to_preserve)]
for val in arg:
sublist.append(make_argword(val & 0xFFFF, ("uint", False),
fnname, argindex, argname, to_preserve))
return b" ".join(coerce_to_bytes(sub) for sub in sublist)
raise TypeError( raise TypeError(
"Can't convert {}() argument #{:d} ({}) to {} (value was {!r})".format( "Can't convert {}() argument #{:d} ({}) to {} (value was {!r})".format(
fnname, argindex, argname, typename, arg)) fnname, argindex, argname, typename, arg))
@ -247,6 +254,8 @@ def make_retval(rettype, word, unpack_strings):
return word == b"true" return word == b"true"
elif rettype in {"pocklestatus", "mr_result"}: elif rettype in {"pocklestatus", "mr_result"}:
return word.decode("ASCII") return word.decode("ASCII")
elif rettype == "int16_list":
return list(map(int, word.split(b',')))
raise TypeError("Can't deal with return value {!r} of type {!r}" raise TypeError("Can't deal with return value {!r} of type {!r}"
.format(word, rettype)) .format(word, rettype))

View File

@ -81,6 +81,7 @@
#include "misc.h" #include "misc.h"
#include "mpint.h" #include "mpint.h"
#include "crypto/ecc.h" #include "crypto/ecc.h"
#include "crypto/ntru.h"
static NORETURN PRINTF_LIKE(1, 2) void fatal_error(const char *p, ...) static NORETURN PRINTF_LIKE(1, 2) void fatal_error(const char *p, ...)
{ {
@ -395,6 +396,7 @@ VOLATILE_WRAPPED_DEFN(static, size_t, looplimit, (size_t x))
HASHES(HASH_TESTLIST, X) \ HASHES(HASH_TESTLIST, X) \
X(argon2) \ X(argon2) \
X(primegen_probabilistic) \ X(primegen_probabilistic) \
X(ntru) \
/* end of list */ /* end of list */
static void test_mp_get_nbits(void) static void test_mp_get_nbits(void)
@ -1556,6 +1558,74 @@ static void test_primegen_probabilistic(void)
test_primegen(&primegen_probabilistic); test_primegen(&primegen_probabilistic);
} }
static void test_ntru(void)
{
unsigned p = 11, q = 59, w = 3;
uint16_t *pubkey_orig = snewn(p, uint16_t);
uint16_t *pubkey_check = snewn(p, uint16_t);
uint16_t *pubkey = snewn(p, uint16_t);
uint16_t *plaintext = snewn(p, uint16_t);
uint16_t *ciphertext = snewn(p, uint16_t);
strbuf *buffer = strbuf_new();
strbuf_append(buffer, 16384);
BinarySource src[1];
for (size_t i = 0; i < looplimit(32); i++) {
while (true) {
random_advance_counter();
struct random_state st = random_get_state();
NTRUKeyPair *keypair = ntru_keygen_attempt(p, q, w);
if (keypair) {
memcpy(pubkey_orig, ntru_pubkey(keypair),
p*sizeof(*pubkey_orig));
ntru_keypair_free(keypair);
random_set_state(st);
log_start();
NTRUKeyPair *keypair = ntru_keygen_attempt(p, q, w);
memcpy(pubkey_check, ntru_pubkey(keypair),
p*sizeof(*pubkey_check));
ntru_gen_short(plaintext, p, w);
ntru_encrypt(ciphertext, plaintext, pubkey, p, w);
ntru_decrypt(plaintext, ciphertext, keypair);
strbuf_clear(buffer);
ntru_encode_pubkey(ntru_pubkey(keypair), p, q,
BinarySink_UPCAST(buffer));
BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(buffer));
ntru_decode_pubkey(pubkey, p, q, src);
strbuf_clear(buffer);
ntru_encode_ciphertext(ciphertext, p, q,
BinarySink_UPCAST(buffer));
BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(buffer));
ntru_decode_ciphertext(ciphertext, keypair, src);
strbuf_clear(buffer);
ntru_encode_plaintext(plaintext, p, BinarySink_UPCAST(buffer));
log_end();
break;
}
assert(!memcmp(pubkey_orig, pubkey_check,
p*sizeof(*pubkey_check)));
}
}
sfree(pubkey_orig);
sfree(pubkey_check);
sfree(pubkey);
sfree(plaintext);
sfree(ciphertext);
strbuf_free(buffer);
}
static const struct test tests[] = { static const struct test tests[] = {
#define STRUCT_TEST(X) { #X, test_##X }, #define STRUCT_TEST(X) { #X, test_##X },
TESTLIST(STRUCT_TEST) TESTLIST(STRUCT_TEST)