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

Implement "curve448-sha512" kex, from RFC 8731.

With all the preparation now in place, this is more or less trivial.
We add a new curve setup function in sshecc.c, and an ssh_kex linking
to it; we add the curve parameters to the reference / test code
eccref.py, and use them to generate the list of low-order input values
that should be rejected by the sanity check on the kex output; we add
the standard test vectors from RFC 7748 in cryptsuite.py, and the
low-order values we just generated.
This commit is contained in:
Simon Tatham 2020-02-29 06:00:39 +00:00
parent 75e7ddea02
commit 31e5b621b5
5 changed files with 98 additions and 10 deletions

1
ssh.h
View File

@ -981,6 +981,7 @@ extern const ssh_kexes ssh_diffiehellman_gex;
extern const ssh_kexes ssh_gssk5_sha1_kex;
extern const ssh_kexes ssh_rsa_kex;
extern const ssh_kex ssh_ec_kex_curve25519;
extern const ssh_kex ssh_ec_kex_curve448;
extern const ssh_kex ssh_ec_kex_nistp256;
extern const ssh_kex ssh_ec_kex_nistp384;
extern const ssh_kex ssh_ec_kex_nistp521;

View File

@ -213,6 +213,35 @@ static struct ec_curve *ec_curve25519(void)
return &curve;
}
static struct ec_curve *ec_curve448(void)
{
static struct ec_curve curve = { 0 };
static bool initialised = false;
if (!initialised)
{
mp_int *p = MP_LITERAL(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
mp_int *a = MP_LITERAL(0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000262a6);
mp_int *b = MP_LITERAL(0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001);
mp_int *G_x = MP_LITERAL(0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005);
initialise_mcurve(&curve, p, a, b, G_x, 2);
mp_free(p);
mp_free(a);
mp_free(b);
mp_free(G_x);
/* This curve doesn't need a name, because it's never used in
* any format that embeds the curve name */
curve.name = NULL;
curve.textname = "Curve448";
/* Now initialised, no need to do it again */
initialised = true;
}
return &curve;
}
static struct ec_curve *ec_ed25519(void)
{
static struct ec_curve curve = { 0 };
@ -1492,6 +1521,18 @@ const ssh_kex ssh_ec_kex_curve25519_libssh = {
&ssh_sha256, &kex_extra_curve25519,
};
static const struct eckex_extra kex_extra_curve448 = {
ec_curve448,
ssh_ecdhkex_m_setup,
ssh_ecdhkex_m_cleanup,
ssh_ecdhkex_m_getpublic,
ssh_ecdhkex_m_getkey,
};
const ssh_kex ssh_ec_kex_curve448 = {
"curve448-sha512", NULL, KEXTYPE_ECDH,
&ssh_sha512, &kex_extra_curve448,
};
static const struct eckex_extra kex_extra_nistp256 = {
ec_p256,
ssh_ecdhkex_w_setup,
@ -1529,6 +1570,7 @@ const ssh_kex ssh_ec_kex_nistp521 = {
};
static const ssh_kex *const ec_kex_list[] = {
&ssh_ec_kex_curve448,
&ssh_ec_kex_curve25519,
&ssh_ec_kex_curve25519_libssh,
&ssh_ec_kex_nistp256,

View File

@ -1565,11 +1565,35 @@ class crypt(MyTestBase):
"eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
]
# Same for Curve448, found by the analogous eccref function call
# find_montgomery_power2_order_x_values(curve448.p, curve448.a)
bad_keys_448 = [
# The first three are the bad values in canonical
# representationm. In Curve448 these are just 0, 1 and -1.
'0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
'0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
'fefffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff',
# As with Curve25519, we must also include values in
# non-canonical representation that reduce to one of the
# above mod p.
'fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff',
'00000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
# But that's all, because Curve448 fits neatly into a
# whole number of bytes, so there's no secondary reduction
# mod a power of 2.
]
with random_prng("doesn't matter"):
ecdh25519 = ssh_ecdhkex_newkey('curve25519')
ecdh448 = ssh_ecdhkex_newkey('curve448')
for pub in bad_keys_25519:
key = ssh_ecdhkex_getkey(ecdh25519, unhex(pub))
self.assertEqual(key, None)
for pub in bad_keys_448:
key = ssh_ecdhkex_getkey(ecdh448, unhex(pub))
self.assertEqual(key, None)
def testPRNG(self):
hashalg = 'sha256'
@ -2423,36 +2447,53 @@ class standard_test_vectors(MyTestBase):
# string and peer public value, giving the expected output
# shared key. Source: RFC 7748 section 5.2.
rfc7748s5_2 = [
('a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4',
('curve25519',
'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4',
'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c',
0xc3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552),
('4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d',
('curve25519',
'4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d',
'e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493',
0x95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957),
('curve448',
'3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3',
'06fce640fa3487bfda5f6cf2d5263f8aad88334cbd07437f020f08f9814dc031ddbdc38c19c6da2583fa5429db94ada18aa7a7fb4ef8a086',
0xce3e4ff95a60dc6697da1db1d85e6afbdf79b50a2412d7546d5f239fe14fbaadeb445fc66a01b0779d98223961111e21766282f73dd96b6f),
('curve448',
'203d494428b8399352665ddca42f9de8fef600908e0d461cb021f8c538345dd77c3e4806e25f46d3315c44e0a5b4371282dd2c8d5be3095f',
'0fbcc2f993cd56d3305b0b7d9e55d4c1a8fb5dbb52f8e9a1e9b6201b165d015894e56c4d3570bee52fe205e28a78b91cdfbde71ce8d157db',
0x884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d),
]
for priv, pub, expected in rfc7748s5_2:
for method, priv, pub, expected in rfc7748s5_2:
with queued_specific_random_data(unhex(priv)):
ecdh = ssh_ecdhkex_newkey('curve25519')
ecdh = ssh_ecdhkex_newkey(method)
key = ssh_ecdhkex_getkey(ecdh, unhex(pub))
self.assertEqual(int(key), expected)
# Bidirectional tests, consisting of the input random number
# strings for both parties, and the expected public values and
# shared key. Source: RFC 7748 section 6.1.
rfc7748s6_1 = [
('77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a',
# shared key. Source: RFC 7748 section 6.
rfc7748s6 = [
('curve25519', # section 6.1
'77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a',
'8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a',
'5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb',
'de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f',
0x4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742),
('curve448', # section 6.2
'9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b',
'9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0',
'1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d',
'3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609',
0x07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d),
]
for apriv, apub, bpriv, bpub, expected in rfc7748s6_1:
for method, apriv, apub, bpriv, bpub, expected in rfc7748s6:
with queued_specific_random_data(unhex(apriv)):
alice = ssh_ecdhkex_newkey('curve25519')
alice = ssh_ecdhkex_newkey(method)
with queued_specific_random_data(unhex(bpriv)):
bob = ssh_ecdhkex_newkey('curve25519')
bob = ssh_ecdhkex_newkey(method)
self.assertEqualBin(ssh_ecdhkex_getpublic(alice), unhex(apub))
self.assertEqualBin(ssh_ecdhkex_getpublic(bob), unhex(bpub))
akey = ssh_ecdhkex_getkey(alice, unhex(bpub))

View File

@ -316,6 +316,9 @@ p521.G_order = 0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
curve25519 = MontgomeryCurve(2**255-19, 0x76d06, 1)
curve25519.G = curve25519.cpoint(9)
curve448 = MontgomeryCurve(2**448-2**224-1, 0x262a6, 1)
curve448.G = curve448.cpoint(5)
ed25519 = TwistedEdwardsCurve(2**255-19, 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3, -1)
ed25519.G = ed25519.point(0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a,0x6666666666666666666666666666666666666666666666666666666666666658)
ed25519.G_order = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed

View File

@ -340,6 +340,7 @@ static const ssh_kex *get_ecdh_alg(BinarySource *in)
const ssh_kex *value;
} algs[] = {
{"curve25519", &ssh_ec_kex_curve25519},
{"curve448", &ssh_ec_kex_curve448},
{"nistp256", &ssh_ec_kex_nistp256},
{"nistp384", &ssh_ec_kex_nistp384},
{"nistp521", &ssh_ec_kex_nistp521},