1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-08 08:58:00 +00:00

Stop using mp_int in sshprng.c.

We keep an internal 128-bit counter that's used as part of the hash
preimages. There's no real need to import all the mp_int machinery in
order to implement that: we can do it by hand using a small fixed-size
array and a trivial use of BignumADC. This is another inter-module
dependency that's easy to remove and useful to spinoff programs.

This changes the hash preimage calculation in the PRNG, because we're
now formatting our 128-bit integer in the fixed-length representation
of 16 little-endian bytes instead of as an SSH-2 mpint. This is
harmless (perhaps even mildly beneficial, due to the length now not
depending on how long the PRNG has been running), but means I have to
update the PRNG tests as well.
This commit is contained in:
Simon Tatham 2020-09-13 08:42:54 +01:00
parent a058054253
commit 7003b43963
2 changed files with 18 additions and 10 deletions

View File

@ -7,7 +7,7 @@
#include "putty.h"
#include "ssh.h"
#include "mpint.h"
#include "mpint_i.h"
#ifdef PRNG_DIAGNOSTICS
#define prngdebug debug
@ -57,7 +57,7 @@ struct prng_impl {
* calling ssh_hash_final.
*/
ssh_hash *generator;
mp_int *counter;
BignumInt counter[128 / BIGNUM_INT_BITS];
/*
* When re-seeding the generator, you call prng_seed_begin(),
@ -113,7 +113,7 @@ prng *prng_new(const ssh_hashalg *hashalg)
pi->hashalg = hashalg;
pi->keymaker = NULL;
pi->generator = NULL;
pi->counter = mp_new(128);
memset(pi->counter, 0, sizeof(pi->counter));
for (size_t i = 0; i < NCOLLECTORS; i++)
pi->collectors[i] = ssh_hash_new(pi->hashalg);
pi->until_reseed = 0;
@ -128,7 +128,7 @@ void prng_free(prng *pr)
{
prng_impl *pi = container_of(pr, prng_impl, Prng);
mp_free(pi->counter);
smemclr(pi->counter, sizeof(pi->counter));
for (size_t i = 0; i < NCOLLECTORS; i++)
ssh_hash_free(pi->collectors[i]);
if (pi->generator)
@ -204,10 +204,12 @@ static inline void prng_generate(prng_impl *pi, void *outbuf)
ssh_hash *h = ssh_hash_copy(pi->generator);
prngdebug("prng_generate\n");
put_byte(h, 'G');
put_mp_ssh2(h, pi->counter);
mp_add_integer_into(pi->counter, pi->counter, 1);
for (unsigned i = 0; i < 128; i += 8)
put_byte(h, pi->counter[i/BIGNUM_INT_BITS] >> (i%BIGNUM_INT_BITS));
BignumCarry c = 1;
for (unsigned i = 0; i < lenof(pi->counter); i++)
BignumADC(pi->counter[i], c, pi->counter[i], 0, c);
ssh_hash_final(h, outbuf);
}

View File

@ -89,6 +89,10 @@ def last(iterable):
pass
return toret
def le_integer(x, nbits):
assert nbits % 8 == 0
return bytes([0xFF & (x >> (8*n)) for n in range(nbits//8)])
@contextlib.contextmanager
def queued_random_data(nbytes, seed):
hashsize = 512 // 8
@ -1627,23 +1631,25 @@ class crypt(MyTestBase):
prng_add_entropy(pr, 0, entropy) # forces a reseed
data3 = prng_read(pr, 128)
le128 = lambda x: le_integer(x, 128)
key1 = hash_str(hashalg, b'R' + seed)
expected_data1 = b''.join(
hash_str(hashalg, key1 + b'G' + ssh2_mpint(counter))
hash_str(hashalg, key1 + b'G' + le128(counter))
for counter in range(4))
# After prng_read finishes, we expect the PRNG to have
# automatically reseeded itself, so that if its internal state
# is revealed then the previous output can't be reconstructed.
key2 = hash_str(hashalg, key1 + b'R')
expected_data2 = b''.join(
hash_str(hashalg, key2 + b'G' + ssh2_mpint(counter))
hash_str(hashalg, key2 + b'G' + le128(counter))
for counter in range(4,8))
# There will have been another reseed after the second
# prng_read, and then another due to the entropy.
key3 = hash_str(hashalg, key2 + b'R')
key4 = hash_str(hashalg, key3 + b'R' + hash_str(hashalg, entropy))
expected_data3 = b''.join(
hash_str(hashalg, key4 + b'G' + ssh2_mpint(counter))
hash_str(hashalg, key4 + b'G' + le128(counter))
for counter in range(8,12))
self.assertEqualBin(data1, expected_data1)