diff --git a/binaries/puttycac-0.80.zip b/binaries/puttycac-0.80.zip deleted file mode 100644 index 677ce44..0000000 Binary files a/binaries/puttycac-0.80.zip and /dev/null differ diff --git a/binaries/puttycac-0.81.zip b/binaries/puttycac-0.81.zip new file mode 100644 index 0000000..c7b29e0 Binary files /dev/null and b/binaries/puttycac-0.81.zip differ diff --git a/binaries/puttycac-64bit-0.80.zip b/binaries/puttycac-64bit-0.80.zip deleted file mode 100644 index 859c9cd..0000000 Binary files a/binaries/puttycac-64bit-0.80.zip and /dev/null differ diff --git a/binaries/puttycac-64bit-0.81.zip b/binaries/puttycac-64bit-0.81.zip new file mode 100644 index 0000000..de7b1f7 Binary files /dev/null and b/binaries/puttycac-64bit-0.81.zip differ diff --git a/binaries/puttycac-hash.txt b/binaries/puttycac-hash.txt index 6cba7d9..5bf0d94 100644 --- a/binaries/puttycac-hash.txt +++ b/binaries/puttycac-hash.txt @@ -1,39 +1,39 @@ Algorithm Hash Path --------- ---- ---- -SHA256 A5FBAD48B9FF90747CC6F387978179ECC629A316CB50C9BD3CECE723D6A3D48F x64\pageant.exe -SHA256 209EA118E3CAB6FC1DB454CEFBFB016A6026AC845EF2C4C5C330F63ABA48E9DC x64\putty.exe -SHA256 4B09E9889B6C98A3F5CA874375466CBE4BE76451ED76C3BE4A8B313351ADCED1 x64\puttyimp.exe -SHA256 25F83DCC692D0F63496C6BEBEB1D5FB033B0D6F71A6A7E556B7A5DCBB2A57C7B x86\pageant.exe -SHA256 95D555525C5881735FF8D19778F19AFF5BFC45E26C2522AFEBC836AA050312CD x86\putty.exe -SHA256 D383A4BAE9848BCCA20DD8F971A9283C9874A54F6AD0F713084008C00695C434 x86\puttyimp.exe -SHA256 D2420A55B4CCB1397A84B8247A9DDCD7461FFAD537635A20A063142BF4F62447 puttycac-0.80.zip -SHA256 80620361FDAE858C28C037BDFA8B4D5709349C0144D2278784A7EAE49ED54B9D puttycac-64bit-0.80.zip +SHA256 ED0A5A28BA717A21D85A5965A77697CE8D8110BD020DAEE118D35E92C97A1933 x64\pageant.exe +SHA256 F7DFD9BDC0A9E43415BA1D8DCC355DC612DF43A01E158E91880F79D316B6F795 x64\putty.exe +SHA256 42A430C5B9D8463BC9454F6A1B276509EB6C5BA9A1832CC83B04C62BCC61694D x64\puttyimp.exe +SHA256 94FE41845319858751AB9DDD7A686CF5BDDE0D0D6C087A488B1848635A8E531A x86\pageant.exe +SHA256 C2D1E040B7F1092D62BD3D40BE72A38DE906ACAD88E9F3186872496E02E1A3B4 x86\putty.exe +SHA256 D7060A95ACEE8EB8D9C8D1A1718DC40463D8FACC8DF06EFE1440610B5F06E0B9 x86\puttyimp.exe +SHA256 0281C8FE19FE463EE3E66251C483E29C409650D91ED0533D76670E4D0ED2A70E puttycac-0.81.zip +SHA256 F14EB1946A01490A6FC7D20D023911B5971A680A47485D7F67E754E3801E43B3 puttycac-64bit-0.81.zip Algorithm Hash Path --------- ---- ---- -SHA1 7794CCEE7D3FC2B0D576BFBE8C5CF28CE5ACF372 x64\pageant.exe -SHA1 4B5565FA6F0CC0BE2BE4898FA95A836F6212B01E x64\putty.exe -SHA1 8532F03364F527D631109134153CB181E9D20554 x64\puttyimp.exe -SHA1 525C7A3E355186C52730F444C77F9416D5635D4D x86\pageant.exe -SHA1 1F3D5A63B54105E1972C281F379958326E274D33 x86\putty.exe -SHA1 880B3646BC1B5B6561D4B491FF066D8306AD28BE x86\puttyimp.exe -SHA1 746CACD101CFB33E5ECFA02899669F48B033E842 puttycac-0.80.zip -SHA1 2F8597DE278F103E3F5610A78F55A65D8475550E puttycac-64bit-0.80.zip +SHA1 4BE7FC935C047157B368E42F79268A0462C52B29 x64\pageant.exe +SHA1 2DF75CCE7402A8207BE68AB51C935CE39968FD63 x64\putty.exe +SHA1 FD8D0DC6A300112EEED215B434E1EB833181B5C0 x64\puttyimp.exe +SHA1 A475B1B92A52B56CDDB123C9A22EE7132D778846 x86\pageant.exe +SHA1 0ED25CD5143F99295A3628AA5C6251FEED0EE636 x86\putty.exe +SHA1 9E451AE9BF5FA0C0B098DD9E1FE96F622256799C x86\puttyimp.exe +SHA1 EA807CE551E765AD6BB716E258C335CFD6BB66A9 puttycac-0.81.zip +SHA1 D02E5347EE4C1BDCCFF1E1098C202DBD8A24D6AC puttycac-64bit-0.81.zip Algorithm Hash Path --------- ---- ---- -MD5 B836929008B30C467371EA895F3049D5 x64\pageant.exe -MD5 DE268F510E1FE7149F2FB3ADA18E6F3E x64\putty.exe -MD5 1A49C1D17083D6AA700B88B7A2B4F6E3 x64\puttyimp.exe -MD5 3F5FD4D66545E99298A385E1B1E6EF19 x86\pageant.exe -MD5 8DEFFEFF258536386BEBF81A5326AA10 x86\putty.exe -MD5 740D25FEA0831806C58D49EA90A3CD44 x86\puttyimp.exe -MD5 F756A80ECF718E3EBF7F604F40FEDADE puttycac-0.80.zip -MD5 3829581D12A227A6DBC868A3133251A8 puttycac-64bit-0.80.zip +MD5 3921518A1324D965CA654E64658FAFBC x64\pageant.exe +MD5 067166F560A45C6661CC9E1B6BD4BC4A x64\putty.exe +MD5 B3D9B88B47C4AF29AECCF2682A760F9B x64\puttyimp.exe +MD5 4540651EEF42A34597E8E2C3E8F51712 x86\pageant.exe +MD5 AF519A08CD09FEC8C3AAFBCE4BB59256 x86\putty.exe +MD5 AB420A22DBE73D0A9DCA50E30DAFB619 x86\puttyimp.exe +MD5 35330A737054E008310CA06259493F88 puttycac-0.81.zip +MD5 EE51607DCC83D8C6B55BC0B849EFD44C puttycac-64bit-0.81.zip diff --git a/binaries/x64/pageant.exe b/binaries/x64/pageant.exe index 6fd9417..897ee96 100644 Binary files a/binaries/x64/pageant.exe and b/binaries/x64/pageant.exe differ diff --git a/binaries/x64/putty.exe b/binaries/x64/putty.exe index 1fe3a04..a8ed7f0 100644 Binary files a/binaries/x64/putty.exe and b/binaries/x64/putty.exe differ diff --git a/binaries/x64/puttyimp.exe b/binaries/x64/puttyimp.exe index 0bb62b8..d0d411e 100644 Binary files a/binaries/x64/puttyimp.exe and b/binaries/x64/puttyimp.exe differ diff --git a/binaries/x86/pageant.exe b/binaries/x86/pageant.exe index 16ae1b0..bd91d56 100644 Binary files a/binaries/x86/pageant.exe and b/binaries/x86/pageant.exe differ diff --git a/binaries/x86/putty.exe b/binaries/x86/putty.exe index 37d55a1..cc814a1 100644 Binary files a/binaries/x86/putty.exe and b/binaries/x86/putty.exe differ diff --git a/binaries/x86/puttyimp.exe b/binaries/x86/puttyimp.exe index d349dcd..5c9c45f 100644 Binary files a/binaries/x86/puttyimp.exe and b/binaries/x86/puttyimp.exe differ diff --git a/code/Buildscr b/code/Buildscr index 8c74551..5049e76 100644 --- a/code/Buildscr +++ b/code/Buildscr @@ -35,7 +35,7 @@ module putty ifeq "$(RELEASE)" "" set Ndate $(!builddate) ifneq "$(Ndate)" "" in . do echo $(Ndate) | perl -pe 's/(....)(..)(..)/$$1-$$2-$$3/' > date ifneq "$(Ndate)" "" read Date date -set Epoch 18707 # update this at every release +set Epoch 18819 # update this at every release ifneq "$(Ndate)" "" in . do echo $(Ndate) | perl -ne 'use Time::Local; /(....)(..)(..)/ and print timegm(0,0,0,$$3,$$2-1,$$1) / 86400 - $(Epoch)' > days ifneq "$(Ndate)" "" read Days days diff --git a/code/cmake/setup.cmake b/code/cmake/setup.cmake index d81d5a5..1be448d 100644 --- a/code/cmake/setup.cmake +++ b/code/cmake/setup.cmake @@ -108,6 +108,14 @@ include_directories( ${platform} ${extra_dirs}) +check_c_source_compiles(" +#define _ISOC11_SOURCE +#include +int main(int argc, char **argv) { + void *p = aligned_alloc(128, 12345); + free(p); +}" HAVE_ALIGNED_ALLOC) + if(PUTTY_DEBUG) add_compile_definitions(DEBUG) endif() diff --git a/code/crypto/CMakeLists.txt b/code/crypto/CMakeLists.txt index 4b0aa90..edb02ce 100644 --- a/code/crypto/CMakeLists.txt +++ b/code/crypto/CMakeLists.txt @@ -30,6 +30,7 @@ add_sources_from_current_dir(crypto pubkey-pem.c pubkey-ppk.c pubkey-ssh1.c + rfc6979.c rsa.c sha256-common.c sha256-select.c diff --git a/code/crypto/dsa.c b/code/crypto/dsa.c index 71fcd94..1999a1c 100644 --- a/code/crypto/dsa.c +++ b/code/crypto/dsa.c @@ -340,117 +340,6 @@ static int dsa_pubkey_bits(const ssh_keyalg *self, ptrlen pub) return ret; } -mp_int *dsa_gen_k(const char *id_string, mp_int *modulus, - mp_int *private_key, - unsigned char *digest, int digest_len) -{ - /* - * The basic DSA signing algorithm is: - * - * - invent a random k between 1 and q-1 (exclusive). - * - Compute r = (g^k mod p) mod q. - * - Compute s = k^-1 * (hash + x*r) mod q. - * - * This has the dangerous properties that: - * - * - if an attacker in possession of the public key _and_ the - * signature (for example, the host you just authenticated - * to) can guess your k, he can reverse the computation of s - * and work out x = r^-1 * (s*k - hash) mod q. That is, he - * can deduce the private half of your key, and masquerade - * as you for as long as the key is still valid. - * - * - since r is a function purely of k and the public key, if - * the attacker only has a _range of possibilities_ for k - * it's easy for him to work through them all and check each - * one against r; he'll never be unsure of whether he's got - * the right one. - * - * - if you ever sign two different hashes with the same k, it - * will be immediately obvious because the two signatures - * will have the same r, and moreover an attacker in - * possession of both signatures (and the public key of - * course) can compute k = (hash1-hash2) * (s1-s2)^-1 mod q, - * and from there deduce x as before. - * - * - the Bleichenbacher attack on DSA makes use of methods of - * generating k which are significantly non-uniformly - * distributed; in particular, generating a 160-bit random - * number and reducing it mod q is right out. - * - * For this reason we must be pretty careful about how we - * generate our k. Since this code runs on Windows, with no - * particularly good system entropy sources, we can't trust our - * RNG itself to produce properly unpredictable data. Hence, we - * use a totally different scheme instead. - * - * What we do is to take a SHA-512 (_big_) hash of the private - * key x, and then feed this into another SHA-512 hash that - * also includes the message hash being signed. That is: - * - * proto_k = SHA512 ( SHA512(x) || SHA160(message) ) - * - * This number is 512 bits long, so reducing it mod q won't be - * noticeably non-uniform. So - * - * k = proto_k mod q - * - * This has the interesting property that it's _deterministic_: - * signing the same hash twice with the same key yields the - * same signature. - * - * Despite this determinism, it's still not predictable to an - * attacker, because in order to repeat the SHA-512 - * construction that created it, the attacker would have to - * know the private key value x - and by assumption he doesn't, - * because if he knew that he wouldn't be attacking k! - * - * (This trick doesn't, _per se_, protect against reuse of k. - * Reuse of k is left to chance; all it does is prevent - * _excessively high_ chances of reuse of k due to entropy - * problems.) - * - * Thanks to Colin Plumb for the general idea of using x to - * ensure k is hard to guess, and to the Cambridge University - * Computer Security Group for helping to argue out all the - * fine details. - */ - ssh_hash *h; - unsigned char digest512[64]; - - /* - * Hash some identifying text plus x. - */ - h = ssh_hash_new(&ssh_sha512); - put_asciz(h, id_string); - put_mp_ssh2(h, private_key); - ssh_hash_digest(h, digest512); - - /* - * Now hash that digest plus the message hash. - */ - ssh_hash_reset(h); - put_data(h, digest512, sizeof(digest512)); - put_data(h, digest, digest_len); - ssh_hash_final(h, digest512); - - /* - * Now convert the result into a bignum, and coerce it to the - * range [2,q), which we do by reducing it mod q-2 and adding 2. - */ - mp_int *modminus2 = mp_copy(modulus); - mp_sub_integer_into(modminus2, modminus2, 2); - mp_int *proto_k = mp_from_bytes_be(make_ptrlen(digest512, 64)); - mp_int *k = mp_mod(proto_k, modminus2); - mp_free(proto_k); - mp_free(modminus2); - mp_add_integer_into(k, k, 2); - - smemclr(digest512, sizeof(digest512)); - - return k; -} - static void dsa_sign(ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs) { struct dsa_key *dsa = container_of(key, struct dsa_key, sshk); @@ -459,8 +348,9 @@ static void dsa_sign(ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs) hash_simple(&ssh_sha1, data, digest); - mp_int *k = dsa_gen_k("DSA deterministic k generator", dsa->q, dsa->x, - digest, sizeof(digest)); + /* Generate any valid exponent k, using the RFC 6979 deterministic + * procedure. */ + mp_int *k = rfc6979(&ssh_sha1, dsa->q, dsa->x, data); mp_int *kinv = mp_invert(k, dsa->q); /* k^-1 mod q */ /* diff --git a/code/crypto/ecc-ssh.c b/code/crypto/ecc-ssh.c index 35b9630..30dccf2 100644 --- a/code/crypto/ecc-ssh.c +++ b/code/crypto/ecc-ssh.c @@ -1126,16 +1126,10 @@ static void ecdsa_sign(ssh_key *key, ptrlen data, mp_int *z = ecdsa_signing_exponent_from_data(ek->curve, extra, data); - /* Generate k between 1 and curve->n, using the same deterministic - * k generation system we use for conventional DSA. */ - mp_int *k; - { - unsigned char digest[20]; - hash_simple(&ssh_sha1, data, digest); - k = dsa_gen_k( - "ECDSA deterministic k generator", ek->curve->w.G_order, - ek->privateKey, digest, sizeof(digest)); - } + /* Generate any valid exponent k, using the RFC 6979 deterministic + * procedure. */ + mp_int *k = rfc6979( + extra->hash, ek->curve->w.G_order, ek->privateKey, data); WeierstrassPoint *kG = ecc_weierstrass_multiply(ek->curve->w.G, k); mp_int *x; diff --git a/code/crypto/hmac.c b/code/crypto/hmac.c index cc25582..afec9ef 100644 --- a/code/crypto/hmac.c +++ b/code/crypto/hmac.c @@ -18,9 +18,10 @@ struct hmac_extra { const char *suffix, *annotation; }; -static ssh2_mac *hmac_new(const ssh2_macalg *alg, ssh_cipher *cipher) +/* Most of hmac_new(). Takes the actual 'struct hmac' as a parameter, + * because sometimes it will have been allocated in a special way. */ +static ssh2_mac *hmac_new_inner(struct hmac *ctx, const ssh2_macalg *alg) { - struct hmac *ctx = snew(struct hmac); const struct hmac_extra *extra = (const struct hmac_extra *)alg->extra; ctx->h_outer = ssh_hash_new(extra->hashalg_base); @@ -64,6 +65,11 @@ static ssh2_mac *hmac_new(const ssh2_macalg *alg, ssh_cipher *cipher) return &ctx->mac; } +static ssh2_mac *hmac_new(const ssh2_macalg *alg, ssh_cipher *cipher) +{ + return hmac_new_inner(snew(struct hmac), alg); /* cipher isn't needed */ +} + static void hmac_free(ssh2_mac *mac) { struct hmac *ctx = container_of(mac, struct hmac, mac); @@ -277,3 +283,38 @@ const ssh2_macalg ssh_hmac_sha1_96_buggy = { .keylen = 16, .extra = &ssh_hmac_sha1_96_buggy_extra, }; + +ssh2_mac *hmac_new_from_hash(const ssh_hashalg *hash) +{ + /* + * Construct a custom ssh2_macalg, derived directly from the + * provided hash vtable. It's included in the same memory + * allocation as the struct hmac, so that it all gets freed + * together. + */ + + struct alloc { + struct hmac hmac; + ssh2_macalg alg; + struct hmac_extra extra; + }; + + struct alloc *alloc = snew(struct alloc); + alloc->alg.new = hmac_new; + alloc->alg.free = hmac_free; + alloc->alg.setkey = hmac_key; + alloc->alg.start = hmac_start; + alloc->alg.genresult = hmac_genresult; + alloc->alg.next_message = nullmac_next_message; + alloc->alg.text_name = hmac_text_name; + alloc->alg.name = NULL; + alloc->alg.etm_name = NULL; + alloc->alg.len = hash->hlen; + alloc->alg.keylen = hash->hlen; + alloc->alg.extra = &alloc->extra; + alloc->extra.hashalg_base = hash; + alloc->extra.suffix = ""; + alloc->extra.annotation = NULL; + + return hmac_new_inner(&alloc->hmac, &alloc->alg); +} diff --git a/code/crypto/rfc6979.c b/code/crypto/rfc6979.c new file mode 100644 index 0000000..73e5c92 --- /dev/null +++ b/code/crypto/rfc6979.c @@ -0,0 +1,359 @@ +/* + * Code to generate 'nonce' values for DSA signature algorithms, in a + * deterministic way. + */ + +#include "ssh.h" +#include "mpint.h" +#include "misc.h" + +/* + * All DSA-type signature systems depend on a nonce - a random number + * generated during the signing operation. + * + * This nonce is a weak point of DSA and needs careful protection, + * for multiple reasons: + * + * 1. If an attacker in possession of your public key and a single + * signature can find out or guess the nonce you used in that + * signature, they can immediately recover your _private key_. + * + * 2. If you reuse the same nonce in two different signatures, this + * will be instantly obvious to the attacker (one of the two + * values making up the signature will match), and again, they can + * immediately recover the private key as soon as they notice this. + * + * 3. In at least one system, information about your private key is + * leaked merely by generating nonces with a significant bias. + * + * Attacks #1 and #2 work across all of integer DSA, NIST-style ECDSA, + * and EdDSA. The details vary, but the headline effects are the same. + * + * So we must be very careful with our nonces. They must be generated + * with uniform distribution, but also, they must avoid depending on + * any random number generator that has the slightest doubt about its + * reliability. + * + * In particular, PuTTY's policy is that for this purpose we don't + * _even_ trust the PRNG we use for other cryptography. This is mostly + * a concern because of Windows, where system entropy sources are + * limited and we have doubts about their trustworthiness + * - even CryptGenRandom. PuTTY compensates as best it can with its + * own ongoing entropy collection, and we trust that for session keys, + * but revealing the private key that goes with a long-term public key + * is a far worse outcome than revealing one SSH session key, and for + * keeping your private key safe, we don't think the available Windows + * entropy gives us enough confidence. + * + * A common strategy these days (although PuTTY was doing it + * before it was cool) is to avoid using a PRNG based on + * system entropy at all. Instead, you use a deterministic PRNG that + * starts from a fixed input seed, and in that input seed you include + * the message to be signed and the _private key_. + * + * Including the private key in the seed is counterintuitive, but does + * actually make sense. A deterministic nonce generation strategy must + * use _some_ piece of input that the attacker doesn't have, or else + * they'd be able to repeat the entire computation and construct the + * same nonce you did. And the one thing they don't know is the + * private key! So we include that in the seed data (under enough + * layers of overcautious hashing to protect it against exposure), and + * then they _can't_ repeat the same construction. Moreover, if they + * _could_, they'd already know the private key, so they wouldn't need + * to perform an attack of this kind at all! + * + * (This trick doesn't, _per se_, protect against reuse of nonces. + * That is left to chance, which is enough, because the space of + * nonces is large enough to make it adequately unlikely. But it + * avoids escalating the reuse risk due to inadequate entropy.) + * + * For integer DSA and ECDSA, the system we use for deterministic + * generation of k is exactly the one specified in RFC 6979. We + * switched to this from the old system that PuTTY used to use before + * that RFC came out. The old system had a critical bug: it did not + * always generate _enough_ data to get uniform distribution, because + * its output was a single SHA-512 hash. We could have fixed that + * minimally, by concatenating multiple hashes, but it seemed more + * sensible to switch to a system that comes with test vectors. + * + * One downside of RFC 6979 is that it's based on rejection sampling + * (that is, you generate a random number and keep retrying until it's + * in range). This makes it play badly with our side-channel test + * system, which wants every execution trace of a supposedly + * constant-time operation to be the same. To work around this + * awkwardness, we break up the algorithm further, into a setup phase + * and an 'attempt to generate an output' phase, each of which is + * individually constant-time. + */ + +struct RFC6979 { + /* + * Size of the cyclic group over which we're doing DSA. + * Equivalently, the multiplicative order of g (for integer DSA) + * or the curve's base point (for ECDSA). For integer DSA this is + * also the same thing as the small prime q from the key + * parameters. + * + * This pointer is not owned. Freeing this structure will not free + * it, and freeing the pointed-to integer before freeing this + * structure will make this structure dangerous to use. + */ + mp_int *q; + + /* + * The private key integer, which is always the discrete log of + * the public key with respect to the group generator. + * + * This pointer is not owned. Freeing this structure will not free + * it, and freeing the pointed-to integer before freeing this + * structure will make this structure dangerous to use. + */ + mp_int *x; + + /* + * Cached values derived from q: its length in bits, and in bytes. + */ + size_t qbits, qbytes; + + /* + * Reusable hash and MAC objects. + */ + ssh_hash *hash; + ssh2_mac *mac; + + /* + * Cached value: the output length of the hash. + */ + size_t hlen; + + /* + * The byte string V used in the algorithm. + */ + unsigned char V[MAX_HASH_LEN]; + + /* + * The string T to use during each attempt, and how many + * hash-sized blocks to fill it with. + */ + size_t T_nblocks; + unsigned char *T; +}; + +static mp_int *bits2int(ptrlen b, RFC6979 *s) +{ + if (b.len > s->qbytes) + b.len = s->qbytes; + mp_int *x = mp_from_bytes_be(b); + + /* + * Rationale for using mp_rshift_fixed_into and not + * mp_rshift_safe_into: the shift count is derived from the + * difference between the length of the modulus q, and the length + * of the input bit string, i.e. between the _sizes_ of things + * involved in the protocol. But the sizes aren't secret. Only the + * actual values of integers and bit strings of those sizes are + * secret. So it's OK for the shift count to be known to an + * attacker - they'd know it anyway just from which DSA algorithm + * we were using. + */ + if (b.len * 8 > s->qbits) + mp_rshift_fixed_into(x, x, b.len * 8 - s->qbits); + + return x; +} + +static void BinarySink_put_int2octets(BinarySink *bs, mp_int *x, RFC6979 *s) +{ + mp_int *x_mod_q = mp_mod(x, s->q); + for (size_t i = s->qbytes; i-- > 0 ;) + put_byte(bs, mp_get_byte(x_mod_q, i)); + mp_free(x_mod_q); +} + +static void BinarySink_put_bits2octets(BinarySink *bs, ptrlen b, RFC6979 *s) +{ + mp_int *x = bits2int(b, s); + BinarySink_put_int2octets(bs, x, s); + mp_free(x); +} + +#define put_int2octets(bs, x, s) \ + BinarySink_put_int2octets(BinarySink_UPCAST(bs), x, s) +#define put_bits2octets(bs, b, s) \ + BinarySink_put_bits2octets(BinarySink_UPCAST(bs), b, s) + +RFC6979 *rfc6979_new(const ssh_hashalg *hashalg, mp_int *q, mp_int *x) +{ + /* Make the state structure. */ + RFC6979 *s = snew(RFC6979); + s->q = q; + s->x = x; + s->qbits = mp_get_nbits(q); + s->qbytes = (s->qbits + 7) >> 3; + s->hash = ssh_hash_new(hashalg); + s->mac = hmac_new_from_hash(hashalg); + s->hlen = hashalg->hlen; + + /* In each attempt, we concatenate enough hash blocks to be + * greater than qbits in size. */ + size_t hbits = 8 * s->hlen; + s->T_nblocks = (s->qbits + hbits - 1) / hbits; + s->T = snewn(s->T_nblocks * s->hlen, unsigned char); + + return s; +} + +void rfc6979_setup(RFC6979 *s, ptrlen message) +{ + unsigned char h1[MAX_HASH_LEN]; + unsigned char K[MAX_HASH_LEN]; + + /* 3.2 (a): hash the message to get h1. */ + ssh_hash_reset(s->hash); + put_datapl(s->hash, message); + ssh_hash_digest(s->hash, h1); + + /* 3.2 (b): set V to a sequence of 0x01 bytes the same size as the + * hash function's output. */ + memset(s->V, 1, s->hlen); + + /* 3.2 (c): set the initial HMAC key K to all zeroes, again the + * same size as the hash function's output. */ + memset(K, 0, s->hlen); + ssh2_mac_setkey(s->mac, make_ptrlen(K, s->hlen)); + + /* 3.2 (d): compute the MAC of V, the private key, and h1, with + * key K, making a new key to replace K. */ + ssh2_mac_start(s->mac); + put_data(s->mac, s->V, s->hlen); + put_byte(s->mac, 0); + put_int2octets(s->mac, s->x, s); + put_bits2octets(s->mac, make_ptrlen(h1, s->hlen), s); + ssh2_mac_genresult(s->mac, K); + ssh2_mac_setkey(s->mac, make_ptrlen(K, s->hlen)); + + /* 3.2 (e): replace V with its HMAC using the new K. */ + ssh2_mac_start(s->mac); + put_data(s->mac, s->V, s->hlen); + ssh2_mac_genresult(s->mac, s->V); + + /* 3.2 (f): repeat step (d), only using the new K in place of the + * initial all-zeroes one, and with the extra byte in the middle + * of the MAC preimage being 1 rather than 0. */ + ssh2_mac_start(s->mac); + put_data(s->mac, s->V, s->hlen); + put_byte(s->mac, 1); + put_int2octets(s->mac, s->x, s); + put_bits2octets(s->mac, make_ptrlen(h1, s->hlen), s); + ssh2_mac_genresult(s->mac, K); + ssh2_mac_setkey(s->mac, make_ptrlen(K, s->hlen)); + + /* 3.2 (g): repeat step (e), using the again-replaced K. */ + ssh2_mac_start(s->mac); + put_data(s->mac, s->V, s->hlen); + ssh2_mac_genresult(s->mac, s->V); + + smemclr(h1, sizeof(h1)); + smemclr(K, sizeof(K)); +} + +RFC6979Result rfc6979_attempt(RFC6979 *s) +{ + RFC6979Result result; + + /* 3.2 (h) 1: set T to the empty string */ + /* 3.2 (h) 2: make lots of output by concatenating MACs of V */ + for (size_t i = 0; i < s->T_nblocks; i++) { + ssh2_mac_start(s->mac); + put_data(s->mac, s->V, s->hlen); + ssh2_mac_genresult(s->mac, s->V); + memcpy(s->T + i * s->hlen, s->V, s->hlen); + } + + /* 3.2 (h) 3: if we have a number in [1, q-1], return it ... */ + result.k = bits2int(make_ptrlen(s->T, s->T_nblocks * s->hlen), s); + result.ok = mp_hs_integer(result.k, 1) & ~mp_cmp_hs(result.k, s->q); + + /* + * Perturb K and regenerate V ready for the next attempt. + * + * We do this unconditionally, whether or not the k we just + * generated is acceptable. The time cost isn't large compared to + * the public-key operation we're going to do next (not to mention + * the larger number of these same operations we've already done), + * and it makes side-channel testing easier if this function is + * constant-time from beginning to end. + * + * In other rejection-sampling situations, particularly prime + * generation, we're not this careful: it's enough to ensure that + * _successful_ attempts run in constant time, Failures can do + * whatever they like, on the theory that the only information + * they _have_ to potentially expose via side channels is + * information that was subsequently thrown away without being + * used for anything important. (Hence, for example, it's fine to + * have multiple different early-exit paths for failures you + * detect at different times.) + * + * But here, the situation is different. Prime generation attempts + * are independent of each other. These are not. All our + * iterations round this loop use the _same_ secret data set up by + * rfc6979_new(), and also, the perturbation step we're about to + * compute will be used by the next iteration if there is one. So + * it's absolutely _not_ true that a failed iteration deals + * exclusively with data that won't contribute to the eventual + * output. Hence, we have to be careful about the failures as well + * as the successes. + * + * (Even so, it would be OK to make successes and failures take + * different amounts of time, as long as each of those amounts was + * consistent. But it's easier for testing to make them the same.) + */ + ssh2_mac_start(s->mac); + put_data(s->mac, s->V, s->hlen); + put_byte(s->mac, 0); + unsigned char K[MAX_HASH_LEN]; + ssh2_mac_genresult(s->mac, K); + ssh2_mac_setkey(s->mac, make_ptrlen(K, s->hlen)); + smemclr(K, sizeof(K)); + + ssh2_mac_start(s->mac); + put_data(s->mac, s->V, s->hlen); + ssh2_mac_genresult(s->mac, s->V); + + return result; +} + +void rfc6979_free(RFC6979 *s) +{ + /* We don't free s->q or s->x: our caller still owns those. */ + + ssh_hash_free(s->hash); + ssh2_mac_free(s->mac); + smemclr(s->T, s->T_nblocks * s->hlen); + sfree(s->T); + + /* Clear the whole structure before freeing. Most fields aren't + * sensitive (pointers or well-known length values), but V is, and + * it's easier to clear the whole lot than fiddle about + * identifying the sensitive fields. */ + smemclr(s, sizeof(*s)); + + sfree(s); +} + +mp_int *rfc6979( + const ssh_hashalg *hashalg, mp_int *q, mp_int *x, ptrlen message) +{ + RFC6979 *s = rfc6979_new(hashalg, q, x); + rfc6979_setup(s, message); + RFC6979Result result; + while (true) { + result = rfc6979_attempt(s); + if (result.ok) + break; + else + mp_free(result.k); + } + rfc6979_free(s); + return result.k; +} diff --git a/code/defs.h b/code/defs.h index 286e0c9..8b1f271 100644 --- a/code/defs.h +++ b/code/defs.h @@ -177,6 +177,8 @@ typedef struct ecdh_key ecdh_key; typedef struct ecdh_keyalg ecdh_keyalg; typedef struct NTRUKeyPair NTRUKeyPair; typedef struct NTRUEncodeSchedule NTRUEncodeSchedule; +typedef struct RFC6979 RFC6979; +typedef struct RFC6979Result RFC6979Result; typedef struct dlgparam dlgparam; typedef struct dlgcontrol dlgcontrol; diff --git a/code/doc/html/AppendixA.html b/code/doc/html/AppendixA.html index e6cb572..3cc8589 100644 --- a/code/doc/html/AppendixA.html +++ b/code/doc/html/AppendixA.html @@ -1023,5 +1023,5 @@

A.10.4 How do I pronounce


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/AppendixB.html b/code/doc/html/AppendixB.html index bb965fd..472a004 100644 --- a/code/doc/html/AppendixB.html +++ b/code/doc/html/AppendixB.html @@ -289,5 +289,5 @@

B.12 E-mail address


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/AppendixC.html b/code/doc/html/AppendixC.html index e9d7a15..ca0cd7d 100644 --- a/code/doc/html/AppendixC.html +++ b/code/doc/html/AppendixC.html @@ -350,5 +350,5 @@

C.5.2 Version 1


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/AppendixD.html b/code/doc/html/AppendixD.html index e21325a..1ba5979 100644 --- a/code/doc/html/AppendixD.html +++ b/code/doc/html/AppendixD.html @@ -14,7 +14,7 @@

Previous | Contents | Index | Next

Appendix D: PuTTY Licence

-PuTTY is copyright 1997-2023 Simon Tatham. +PuTTY is copyright 1997-2024 Simon Tatham.

Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, Colin Watson, Christopher Staite, Lorenz Diener, Christian Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav Kuzmich, Nico Williams, Viktor Dukhovni, Josh Dersch, Lars Brinkhoff, and CORE SDI S.A. @@ -30,5 +30,5 @@

Appendix D: PuTTY Li


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/AppendixE.html b/code/doc/html/AppendixE.html index c4aeba6..c34222b 100644 --- a/code/doc/html/AppendixE.html +++ b/code/doc/html/AppendixE.html @@ -417,5 +417,5 @@

E.13 Do as we say, not as w


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/AppendixF.html b/code/doc/html/AppendixF.html index cacae85..fd1254e 100644 --- a/code/doc/html/AppendixF.html +++ b/code/doc/html/AppendixF.html @@ -307,5 +307,5 @@

F.3 Key rollover


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/AppendixG.html b/code/doc/html/AppendixG.html index 5377ddd..fd8201f 100644 --- a/code/doc/html/AppendixG.html +++ b/code/doc/html/AppendixG.html @@ -149,5 +149,5 @@

G.4 Agent extension request

If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/AppendixH.html b/code/doc/html/AppendixH.html index 4958985..9e970cd 100644 --- a/code/doc/html/AppendixH.html +++ b/code/doc/html/AppendixH.html @@ -455,5 +455,5 @@

H.7 References


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/Chapter1.html b/code/doc/html/Chapter1.html index 9096b5a..0b5d2b9 100644 --- a/code/doc/html/Chapter1.html +++ b/code/doc/html/Chapter1.html @@ -85,5 +85,5 @@

1.2 How do SSH, Telnet, Rlogin, a


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/Chapter10.html b/code/doc/html/Chapter10.html index 283e7d3..edb5d8e 100644 --- a/code/doc/html/Chapter10.html +++ b/code/doc/html/Chapter10.html @@ -268,5 +268,5 @@

10.20 ‘N


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/Chapter2.html b/code/doc/html/Chapter2.html index fd79863..897b0a2 100644 --- a/code/doc/html/Chapter2.html +++ b/code/doc/html/Chapter2.html @@ -120,5 +120,5 @@

2.5 Logging out


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/Chapter3.html b/code/doc/html/Chapter3.html index 270bc6c..af72186 100644 --- a/code/doc/html/Chapter3.html +++ b/code/doc/html/Chapter3.html @@ -809,5 +809,5 @@

3.11.3.29

If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/Chapter4.html b/code/doc/html/Chapter4.html index 65815b1..5d95fd8 100644 --- a/code/doc/html/Chapter4.html +++ b/code/doc/html/Chapter4.html @@ -2792,5 +2792,5 @@

4.33 Storin


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/Chapter5.html b/code/doc/html/Chapter5.html index 7667e49..986711b 100644 --- a/code/doc/html/Chapter5.html +++ b/code/doc/html/Chapter5.html @@ -51,7 +51,7 @@

5.2 PSCP Usage

C:\>pscp
 PuTTY Secure Copy client
-Release 0.80
+Release 0.81
 Usage: pscp [options] [user@]host:source target
        pscp [options] source [source...] [user@]host:target
        pscp [options] -ls [user@]host:filespec
@@ -271,5 +271,5 @@ 

5.2.4 Using

If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/Chapter6.html b/code/doc/html/Chapter6.html index 9161013..5e12620 100644 --- a/code/doc/html/Chapter6.html +++ b/code/doc/html/Chapter6.html @@ -535,5 +535,5 @@

6.3 Using pu


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/Chapter7.html b/code/doc/html/Chapter7.html index 593d919..47fc553 100644 --- a/code/doc/html/Chapter7.html +++ b/code/doc/html/Chapter7.html @@ -56,7 +56,7 @@

7.2 Using Plink

C:\>plink
 Plink: command-line connection utility
-Release 0.80
+Release 0.81
 Usage: plink [options] [user@]host [command]
        ("host" can also be a PuTTY saved session name)
 Options:
@@ -382,5 +382,5 @@ 

7.5 Using Plink with Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/Chapter8.html b/code/doc/html/Chapter8.html index 42e1b50..30235c6 100644 --- a/code/doc/html/Chapter8.html +++ b/code/doc/html/Chapter8.html @@ -475,5 +475,5 @@

8.3 Getting ready for p

If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/Chapter9.html b/code/doc/html/Chapter9.html index 1c8884d..7100f14 100644 --- a/code/doc/html/Chapter9.html +++ b/code/doc/html/Chapter9.html @@ -325,5 +325,5 @@

9.6 Security consideration


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/IndexPage.html b/code/doc/html/IndexPage.html index 5aae852..48d3c60 100644 --- a/code/doc/html/IndexPage.html +++ b/code/doc/html/IndexPage.html @@ -1001,5 +1001,5 @@

Index

xterm: Section 3.1.1, Section 4.4.2, Section 4.4.3, Section 4.4.3, Section 4.11.1, Section 4.15.3
xterm mouse reporting: Section 3.1.1, Section 4.6.2, Section 4.11.2


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/html/index.html b/code/doc/html/index.html index eea6833..8e4ff80 100644 --- a/code/doc/html/index.html +++ b/code/doc/html/index.html @@ -17,7 +17,7 @@

PuTTY User Manual

Note to Unix users: this manual currently primarily documents the Windows versions of the PuTTY utilities. Some options are therefore mentioned that are absent from the Unix version; the Unix version has features not described here; and the pterm and command-line puttygen and pageant utilities are not described at all. The only Unix-specific documentation that currently exists is the man pages.

-This manual is copyright 1997-2023 Simon Tatham. All rights reserved. You may distribute this documentation under the MIT licence. See appendix D for the licence text in full. +This manual is copyright 1997-2024 Simon Tatham. All rights reserved. You may distribute this documentation under the MIT licence. See appendix D for the licence text in full.

    @@ -221,5 +221,5 @@

    PuTTY User Manual


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

-[PuTTY release 0.80]
+[PuTTY release 0.81] diff --git a/code/doc/plink.but b/code/doc/plink.but index 96d78c1..2b46040 100644 --- a/code/doc/plink.but +++ b/code/doc/plink.but @@ -41,7 +41,7 @@ use Plink: \c C:\>plink \c Plink: command-line connection utility -\c Release 0.80 +\c Release 0.81 \c Usage: plink [options] [user@]host [command] \c ("host" can also be a PuTTY saved session name) \c Options: diff --git a/code/doc/pscp.but b/code/doc/pscp.but index 2d7f022..1a27e7d 100644 --- a/code/doc/pscp.but +++ b/code/doc/pscp.but @@ -39,7 +39,7 @@ use PSCP: \c C:\>pscp \c PuTTY Secure Copy client -\c Release 0.80 +\c Release 0.81 \c Usage: pscp [options] [user@]host:source target \c pscp [options] source [source...] [user@]host:target \c pscp [options] -ls [user@]host:filespec diff --git a/code/doc/putty.chm b/code/doc/putty.chm index c5d423e..394e062 100644 Binary files a/code/doc/putty.chm and b/code/doc/putty.chm differ diff --git a/code/doc/puttydoc.txt b/code/doc/puttydoc.txt index f5ec925..25e74e2 100644 --- a/code/doc/puttydoc.txt +++ b/code/doc/puttydoc.txt @@ -12,7 +12,7 @@ not described here; and the pterm and command-line puttygen and pageant utilities are not described at all. The only Unix-specific documentation that currently exists is the man pages. -This manual is copyright 1997-2023 Simon Tatham. All rights reserved. You +This manual is copyright 1997-2024 Simon Tatham. All rights reserved. You may distribute this documentation under the MIT licence. See appendix D for the licence text in full. @@ -5530,7 +5530,7 @@ Chapter 5: Using PSCP to transfer files securely C:\>pscp PuTTY Secure Copy client - Release 0.80 + Release 0.81 Usage: pscp [options] [user@]host:source target pscp [options] source [source...] [user@]host:target pscp [options] -ls [user@]host:filespec @@ -6452,7 +6452,7 @@ Chapter 7: Using the command-line connection tool Plink C:\>plink Plink: command-line connection utility - Release 0.80 + Release 0.81 Usage: plink [options] [user@]host [command] ("host" can also be a PuTTY saved session name) Options: @@ -10789,7 +10789,7 @@ Appendix C: PPK file format Appendix D: PuTTY Licence ------------------------- - PuTTY is copyright 1997-2023 Simon Tatham. + PuTTY is copyright 1997-2024 Simon Tatham. Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, @@ -12479,4 +12479,4 @@ H.6.12 PLUGIN_AUTH_FAILURE Secure Shell Protocol (SSH)' (better known by its wire id `keyboard- interactive'). -[PuTTY release 0.80] +[PuTTY release 0.81] diff --git a/code/doc/version.but b/code/doc/version.but index 5b2096e..13ba0a2 100644 --- a/code/doc/version.but +++ b/code/doc/version.but @@ -1 +1 @@ -\versionid PuTTY release 0.80 +\versionid PuTTY release 0.81 diff --git a/code/ssh.h b/code/ssh.h index afd0ca1..4ba9afe 100644 --- a/code/ssh.h +++ b/code/ssh.h @@ -641,11 +641,18 @@ mp_int *ssh_rsakex_decrypt( RSAKey *key, const ssh_hashalg *h, ptrlen ciphertext); /* - * Helper function for k generation in DSA, reused in ECDSA + * System for generating k in DSA and ECDSA. */ -mp_int *dsa_gen_k(const char *id_string, - mp_int *modulus, mp_int *private_key, - unsigned char *digest, int digest_len); +struct RFC6979Result { + mp_int *k; + unsigned ok; +}; +RFC6979 *rfc6979_new(const ssh_hashalg *hashalg, mp_int *q, mp_int *x); +void rfc6979_setup(RFC6979 *s, ptrlen message); +RFC6979Result rfc6979_attempt(RFC6979 *s); +void rfc6979_free(RFC6979 *s); +mp_int *rfc6979(const ssh_hashalg *hashalg, mp_int *modulus, + mp_int *private_key, ptrlen message); struct ssh_cipher { const ssh_cipheralg *vt; @@ -774,6 +781,11 @@ void nullmac_next_message(ssh2_mac *m); * string with a given key in the most obvious way. */ void mac_simple(const ssh2_macalg *alg, ptrlen key, ptrlen data, void *output); +/* Constructor that makes an HMAC object given just a MAC. This object + * will have empty 'name' and 'etm_name' fields, so it's not suitable + * for use in SSH. It's used as a subroutine in RFC 6979. */ +ssh2_mac *hmac_new_from_hash(const ssh_hashalg *hash); + struct ssh_hash { const ssh_hashalg *vt; BinarySink_DELEGATE_IMPLEMENTATION; @@ -1223,6 +1235,7 @@ extern const ssh2_macalg ssh_hmac_sha1_buggy; extern const ssh2_macalg ssh_hmac_sha1_96; extern const ssh2_macalg ssh_hmac_sha1_96_buggy; extern const ssh2_macalg ssh_hmac_sha256; +extern const ssh2_macalg ssh_hmac_sha384; extern const ssh2_macalg ssh_hmac_sha512; extern const ssh2_macalg ssh2_poly1305; extern const ssh2_macalg ssh2_aesgcm_mac; diff --git a/code/test/cryptsuite.py b/code/test/cryptsuite.py index 9d2c215..3f3a0ca 100644 --- a/code/test/cryptsuite.py +++ b/code/test/cryptsuite.py @@ -90,6 +90,9 @@ def le_integer(x, nbits): assert nbits % 8 == 0 return bytes([0xFF & (x >> (8*n)) for n in range(nbits//8)]) +def be_integer(x, nbits): + return bytes(reversed(le_integer(x, nbits))) + @contextlib.contextmanager def queued_random_data(nbytes, seed): hashsize = 512 // 8 @@ -2075,6 +2078,244 @@ def testDSA(self): self.assertFalse(ssh_key_verify(pubkey, badsig0, "hello, again")) self.assertFalse(ssh_key_verify(pubkey, badsigq, "hello, again")) + def testRFC6979(self): + # The test case described in detail in RFC 6979 section A.1. + # We can't actually do the _signature_ for this, because it's + # based on ECDSA over a finite field of characteristic 2, and + # we only support prime-order fields. But we don't need to do + # full ECDSA, only generate the same deterministic nonce that + # the test case expects. + k = rfc6979('sha256', + 0x4000000000000000000020108A2E0CC0D99F8A5EF, + 0x09A4D6792295A7F730FC3F2B49CBC0F62E862272F, "sample") + self.assertEqual(int(k), 0x23AF4074C90A02B3FE61D286D5C87F425E6BDD81B) + + # Selected test cases from the rest of Appendix A. + # + # We can only use test cases for which we have the appropriate + # hash function, so I've left out the test cases based on + # SHA-224. (We could easily implement that, but I don't think + # it's worth it just for adding further tests of this one + # function.) Similarly, I've omitted test cases relating to + # ECDSA curves we don't implement: P192, P224, and all the + # curves over power-of-2 finite fields. + # + # Where possible, we also test the actual signature algorithm, + # to make sure it delivers the same entire signature as the + # test case. This demonstrates that the rfc6979() function is + # being called in the right way and the results are being used + # as they should be. Here I've had to cut down the test cases + # even further, because the RFC specifies test cases with a + # cross product of DSA group and hash function, whereas we + # have a fixed hash (specified by SSH) for each signature + # algorithm. And the RFC is clear that you use the same hash + # for nonce generation and actual signing. + + # A.2.1: 1024-bit DSA + q = 0x996F967F6C8E388D9E28D01E205FBA957A5698B1 + x = 0x411602CB19A6CCC34494D79D98EF1E7ED5AF25F7 + k = rfc6979('sha1', q, x, "sample") + self.assertEqual(int(k), 0x7BDB6B0FF756E1BB5D53583EF979082F9AD5BD5B) + k = rfc6979('sha256', q, x, "sample") + self.assertEqual(int(k), 0x519BA0546D0C39202A7D34D7DFA5E760B318BCFB) + k = rfc6979('sha384', q, x, "sample") + self.assertEqual(int(k), 0x95897CD7BBB944AA932DBC579C1C09EB6FCFC595) + k = rfc6979('sha512', q, x, "sample") + self.assertEqual(int(k), 0x09ECE7CA27D0F5A4DD4E556C9DF1D21D28104F8B) + k = rfc6979('sha1', q, x, "test") + self.assertEqual(int(k), 0x5C842DF4F9E344EE09F056838B42C7A17F4A6433) + k = rfc6979('sha256', q, x, "test") + self.assertEqual(int(k), 0x5A67592E8128E03A417B0484410FB72C0B630E1A) + k = rfc6979('sha384', q, x, "test") + self.assertEqual(int(k), 0x220156B761F6CA5E6C9F1B9CF9C24BE25F98CD89) + k = rfc6979('sha512', q, x, "test") + self.assertEqual(int(k), 0x65D2C2EEB175E370F28C75BFCDC028D22C7DBE9C) + # The rest of the public key, for signature testing + p = 0x86F5CA03DCFEB225063FF830A0C769B9DD9D6153AD91D7CE27F787C43278B447E6533B86B18BED6E8A48B784A14C252C5BE0DBF60B86D6385BD2F12FB763ED8873ABFD3F5BA2E0A8C0A59082EAC056935E529DAF7C610467899C77ADEDFC846C881870B7B19B2B58F9BE0521A17002E3BDD6B86685EE90B3D9A1B02B782B1779 + g = 0x07B0F92546150B62514BB771E2A0C0CE387F03BDA6C56B505209FF25FD3C133D89BBCD97E904E09114D9A7DEFDEADFC9078EA544D2E401AEECC40BB9FBBF78FD87995A10A1C27CB7789B594BA7EFB5C4326A9FE59A070E136DB77175464ADCA417BE5DCE2F40D10A46A3A3943F26AB7FD9C0398FF8C76EE0A56826A8A88F1DBD + y = 0x5DF5E01DED31D0297E274E1691C192FE5868FEF9E19A84776454B100CF16F65392195A38B90523E2542EE61871C0440CB87C322FC4B4D2EC5E1E7EC766E1BE8D4CE935437DC11C3C8FD426338933EBFE739CB3465F4D3668C5E473508253B1E682F65CBDC4FAE93C2EA212390E54905A86E2223170B44EAA7DA5DD9FFCFB7F3B + pubblob = ssh_string(b"ssh-dss") + b"".join(map(ssh2_mpint, [p,q,g,y])) + privblob = ssh2_mpint(x) + pubkey = ssh_key_new_pub('dsa', pubblob) + privkey = ssh_key_new_priv('dsa', pubblob, privblob) + sig = ssh_key_sign(privkey, b"sample", 0) + # Expected output using SHA-1 as the hash in nonce + # construction. + r = 0x2E1A0C2562B2912CAAF89186FB0F42001585DA55 + s = 0x29EFB6B0AFF2D7A68EB70CA313022253B9A88DF5 + ref_sig = ssh_string(b"ssh-dss") + ssh_string( + be_integer(r, 160) + be_integer(s, 160)) + self.assertEqual(sig, ref_sig) + # And the other test string. + sig = ssh_key_sign(privkey, b"test", 0) + r = 0x42AB2052FD43E123F0607F115052A67DCD9C5C77 + s = 0x183916B0230D45B9931491D4C6B0BD2FB4AAF088 + ref_sig = ssh_string(b"ssh-dss") + ssh_string( + be_integer(r, 160) + be_integer(s, 160)) + self.assertEqual(sig, ref_sig) + + # A.2.2: 2048-bit DSA + q = 0xF2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F + x = 0x69C7548C21D0DFEA6B9A51C9EAD4E27C33D3B3F180316E5BCAB92C933F0E4DBC + k = rfc6979('sha1', q, x, "sample") + self.assertEqual(int(k), 0x888FA6F7738A41BDC9846466ABDB8174C0338250AE50CE955CA16230F9CBD53E) + k = rfc6979('sha256', q, x, "sample") + self.assertEqual(int(k), 0x8926A27C40484216F052F4427CFD5647338B7B3939BC6573AF4333569D597C52) + k = rfc6979('sha384', q, x, "sample") + self.assertEqual(int(k), 0xC345D5AB3DA0A5BCB7EC8F8FB7A7E96069E03B206371EF7D83E39068EC564920) + k = rfc6979('sha512', q, x, "sample") + self.assertEqual(int(k), 0x5A12994431785485B3F5F067221517791B85A597B7A9436995C89ED0374668FC) + k = rfc6979('sha1', q, x, "test") + self.assertEqual(int(k), 0x6EEA486F9D41A037B2C640BC5645694FF8FF4B98D066A25F76BE641CCB24BA4F) + k = rfc6979('sha256', q, x, "test") + self.assertEqual(int(k), 0x1D6CE6DDA1C5D37307839CD03AB0A5CBB18E60D800937D67DFB4479AAC8DEAD7) + k = rfc6979('sha384', q, x, "test") + self.assertEqual(int(k), 0x206E61F73DBE1B2DC8BE736B22B079E9DACD974DB00EEBBC5B64CAD39CF9F91C) + k = rfc6979('sha512', q, x, "test") + self.assertEqual(int(k), 0xAFF1651E4CD6036D57AA8B2A05CCF1A9D5A40166340ECBBDC55BE10B568AA0AA) + # The rest of the public key, for signature testing + p = 0x9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44FFE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE235567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA153E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B + g = 0x5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C46A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7 + y = 0x667098C654426C78D7F8201EAC6C203EF030D43605032C2F1FA937E5237DBD949F34A0A2564FE126DC8B715C5141802CE0979C8246463C40E6B6BDAA2513FA611728716C2E4FD53BC95B89E69949D96512E873B9C8F8DFD499CC312882561ADECB31F658E934C0C197F2C4D96B05CBAD67381E7B768891E4DA3843D24D94CDFB5126E9B8BF21E8358EE0E0A30EF13FD6A664C0DCE3731F7FB49A4845A4FD8254687972A2D382599C9BAC4E0ED7998193078913032558134976410B89D2C171D123AC35FD977219597AA7D15C1A9A428E59194F75C721EBCBCFAE44696A499AFA74E04299F132026601638CB87AB79190D4A0986315DA8EEC6561C938996BEADF + pubblob = ssh_string(b"ssh-dss") + b"".join(map(ssh2_mpint, [p,q,g,y])) + privblob = ssh2_mpint(x) + pubkey = ssh_key_new_pub('dsa', pubblob) + privkey = ssh_key_new_priv('dsa', pubblob, privblob) + sig = ssh_key_sign(privkey, b"sample", 0) + # Expected output using SHA-1 as the hash in nonce + # construction, which is how SSH does things. RFC6979 lists + # the following 256-bit values for r and s, but we end up only + # using the low 160 bits of each. + r = 0x3A1B2DBD7489D6ED7E608FD036C83AF396E290DBD602408E8677DAABD6E7445A + s = 0xD26FCBA19FA3E3058FFC02CA1596CDBB6E0D20CB37B06054F7E36DED0CDBBCCF + ref_sig = ssh_string(b"ssh-dss") + ssh_string( + be_integer(r, 160) + be_integer(s, 160)) + self.assertEqual(sig, ref_sig) + # And the other test string. + sig = ssh_key_sign(privkey, b"test", 0) + r = 0xC18270A93CFC6063F57A4DFA86024F700D980E4CF4E2CB65A504397273D98EA0 + s = 0x414F22E5F31A8B6D33295C7539C1C1BA3A6160D7D68D50AC0D3A5BEAC2884FAA + ref_sig = ssh_string(b"ssh-dss") + ssh_string( + be_integer(r, 160) + be_integer(s, 160)) + self.assertEqual(sig, ref_sig) + + # A.2.5: ECDSA with NIST P256 + q = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551 + x = 0xC9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721 + k = rfc6979('sha1', q, x, "sample") + self.assertEqual(int(k), 0x882905F1227FD620FBF2ABF21244F0BA83D0DC3A9103DBBEE43A1FB858109DB4) + k = rfc6979('sha256', q, x, "sample") + self.assertEqual(int(k), 0xA6E3C57DD01ABE90086538398355DD4C3B17AA873382B0F24D6129493D8AAD60) + k = rfc6979('sha384', q, x, "sample") + self.assertEqual(int(k), 0x09F634B188CEFD98E7EC88B1AA9852D734D0BC272F7D2A47DECC6EBEB375AAD4) + k = rfc6979('sha512', q, x, "sample") + self.assertEqual(int(k), 0x5FA81C63109BADB88C1F367B47DA606DA28CAD69AA22C4FE6AD7DF73A7173AA5) + k = rfc6979('sha1', q, x, "test") + self.assertEqual(int(k), 0x8C9520267C55D6B980DF741E56B4ADEE114D84FBFA2E62137954164028632A2E) + k = rfc6979('sha256', q, x, "test") + self.assertEqual(int(k), 0xD16B6AE827F17175E040871A1C7EC3500192C4C92677336EC2537ACAEE0008E0) + k = rfc6979('sha384', q, x, "test") + self.assertEqual(int(k), 0x16AEFFA357260B04B1DD199693960740066C1A8F3E8EDD79070AA914D361B3B8) + k = rfc6979('sha512', q, x, "test") + self.assertEqual(int(k), 0x6915D11632ACA3C40D5D51C08DAF9C555933819548784480E93499000D9F0B7F) + # The public key, for signature testing + Ux = 0x60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6 + Uy = 0x7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299 + pubblob = ssh_string(b"ecdsa-sha2-nistp256") + ssh_string(b"nistp256") + ssh_string(b'\x04' + be_integer(Ux, 256) + be_integer(Uy, 256)) + privblob = ssh2_mpint(x) + pubkey = ssh_key_new_pub('p256', pubblob) + privkey = ssh_key_new_priv('p256', pubblob, privblob) + sig = ssh_key_sign(privkey, b"sample", 0) + # Expected output using SHA-256 + r = 0xEFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716 + s = 0xF7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8 + ref_sig = ssh_string(b"ecdsa-sha2-nistp256") + ssh_string(ssh2_mpint(r) + ssh2_mpint(s)) + self.assertEqual(sig, ref_sig) + # And the other test string + sig = ssh_key_sign(privkey, b"test", 0) + r = 0xF1ABB023518351CD71D881567B1EA663ED3EFCF6C5132B354F28D3B0B7D38367 + s = 0x019F4113742A2B14BD25926B49C649155F267E60D3814B4C0CC84250E46F0083 + ref_sig = ssh_string(b"ecdsa-sha2-nistp256") + ssh_string(ssh2_mpint(r) + ssh2_mpint(s)) + self.assertEqual(sig, ref_sig) + + # A.2.5: ECDSA with NIST P384 + q = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973 + x = 0x6B9D3DAD2E1B8C1C05B19875B6659F4DE23C3B667BF297BA9AA47740787137D896D5724E4C70A825F872C9EA60D2EDF5 + k = rfc6979('sha1', q, x, "sample") + self.assertEqual(int(k), 0x4471EF7518BB2C7C20F62EAE1C387AD0C5E8E470995DB4ACF694466E6AB096630F29E5938D25106C3C340045A2DB01A7) + k = rfc6979('sha256', q, x, "sample") + self.assertEqual(int(k), 0x180AE9F9AEC5438A44BC159A1FCB277C7BE54FA20E7CF404B490650A8ACC414E375572342863C899F9F2EDF9747A9B60) + k = rfc6979('sha384', q, x, "sample") + self.assertEqual(int(k), 0x94ED910D1A099DAD3254E9242AE85ABDE4BA15168EAF0CA87A555FD56D10FBCA2907E3E83BA95368623B8C4686915CF9) + k = rfc6979('sha512', q, x, "sample") + self.assertEqual(int(k), 0x92FC3C7183A883E24216D1141F1A8976C5B0DD797DFA597E3D7B32198BD35331A4E966532593A52980D0E3AAA5E10EC3) + k = rfc6979('sha1', q, x, "test") + self.assertEqual(int(k), 0x66CC2C8F4D303FC962E5FF6A27BD79F84EC812DDAE58CF5243B64A4AD8094D47EC3727F3A3C186C15054492E30698497) + k = rfc6979('sha256', q, x, "test") + self.assertEqual(int(k), 0x0CFAC37587532347DC3389FDC98286BBA8C73807285B184C83E62E26C401C0FAA48DD070BA79921A3457ABFF2D630AD7) + k = rfc6979('sha384', q, x, "test") + self.assertEqual(int(k), 0x015EE46A5BF88773ED9123A5AB0807962D193719503C527B031B4C2D225092ADA71F4A459BC0DA98ADB95837DB8312EA) + k = rfc6979('sha512', q, x, "test") + self.assertEqual(int(k), 0x3780C4F67CB15518B6ACAE34C9F83568D2E12E47DEAB6C50A4E4EE5319D1E8CE0E2CC8A136036DC4B9C00E6888F66B6C) + # The public key, for signature testing + Ux = 0xEC3A4E415B4E19A4568618029F427FA5DA9A8BC4AE92E02E06AAE5286B300C64DEF8F0EA9055866064A254515480BC13 + Uy = 0x8015D9B72D7D57244EA8EF9AC0C621896708A59367F9DFB9F54CA84B3F1C9DB1288B231C3AE0D4FE7344FD2533264720 + pubblob = ssh_string(b"ecdsa-sha2-nistp384") + ssh_string(b"nistp384") + ssh_string(b'\x04' + be_integer(Ux, 384) + be_integer(Uy, 384)) + privblob = ssh2_mpint(x) + pubkey = ssh_key_new_pub('p384', pubblob) + privkey = ssh_key_new_priv('p384', pubblob, privblob) + sig = ssh_key_sign(privkey, b"sample", 0) + # Expected output using SHA-384 + r = 0x94EDBB92A5ECB8AAD4736E56C691916B3F88140666CE9FA73D64C4EA95AD133C81A648152E44ACF96E36DD1E80FABE46 + s = 0x99EF4AEB15F178CEA1FE40DB2603138F130E740A19624526203B6351D0A3A94FA329C145786E679E7B82C71A38628AC8 + ref_sig = ssh_string(b"ecdsa-sha2-nistp384") + ssh_string(ssh2_mpint(r) + ssh2_mpint(s)) + self.assertEqual(sig, ref_sig) + # And the other test string + sig = ssh_key_sign(privkey, b"test", 0) + r = 0x8203B63D3C853E8D77227FB377BCF7B7B772E97892A80F36AB775D509D7A5FEB0542A7F0812998DA8F1DD3CA3CF023DB + s = 0xDDD0760448D42D8A43AF45AF836FCE4DE8BE06B485E9B61B827C2F13173923E06A739F040649A667BF3B828246BAA5A5 + ref_sig = ssh_string(b"ecdsa-sha2-nistp384") + ssh_string(ssh2_mpint(r) + ssh2_mpint(s)) + self.assertEqual(sig, ref_sig) + + # A.2.6: ECDSA with NIST P521 + q = 0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409 + x = 0x0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538 + k = rfc6979('sha1', q, x, "sample") + self.assertEqual(int(k), 0x089C071B419E1C2820962321787258469511958E80582E95D8378E0C2CCDB3CB42BEDE42F50E3FA3C71F5A76724281D31D9C89F0F91FC1BE4918DB1C03A5838D0F9) + k = rfc6979('sha256', q, x, "sample") + self.assertEqual(int(k), 0x0EDF38AFCAAECAB4383358B34D67C9F2216C8382AAEA44A3DAD5FDC9C32575761793FEF24EB0FC276DFC4F6E3EC476752F043CF01415387470BCBD8678ED2C7E1A0) + k = rfc6979('sha384', q, x, "sample") + self.assertEqual(int(k), 0x1546A108BC23A15D6F21872F7DED661FA8431DDBD922D0DCDB77CC878C8553FFAD064C95A920A750AC9137E527390D2D92F153E66196966EA554D9ADFCB109C4211) + k = rfc6979('sha512', q, x, "sample") + self.assertEqual(int(k), 0x1DAE2EA071F8110DC26882D4D5EAE0621A3256FC8847FB9022E2B7D28E6F10198B1574FDD03A9053C08A1854A168AA5A57470EC97DD5CE090124EF52A2F7ECBFFD3) + k = rfc6979('sha1', q, x, "test") + self.assertEqual(int(k), 0x0BB9F2BF4FE1038CCF4DABD7139A56F6FD8BB1386561BD3C6A4FC818B20DF5DDBA80795A947107A1AB9D12DAA615B1ADE4F7A9DC05E8E6311150F47F5C57CE8B222) + k = rfc6979('sha256', q, x, "test") + self.assertEqual(int(k), 0x01DE74955EFAABC4C4F17F8E84D881D1310B5392D7700275F82F145C61E843841AF09035BF7A6210F5A431A6A9E81C9323354A9E69135D44EBD2FCAA7731B909258) + k = rfc6979('sha384', q, x, "test") + self.assertEqual(int(k), 0x1F1FC4A349A7DA9A9E116BFDD055DC08E78252FF8E23AC276AC88B1770AE0B5DCEB1ED14A4916B769A523CE1E90BA22846AF11DF8B300C38818F713DADD85DE0C88) + k = rfc6979('sha512', q, x, "test") + self.assertEqual(int(k), 0x16200813020EC986863BEDFC1B121F605C1215645018AEA1A7B215A564DE9EB1B38A67AA1128B80CE391C4FB71187654AAA3431027BFC7F395766CA988C964DC56D) + # The public key, for signature testing + Ux = 0x1894550D0785932E00EAA23B694F213F8C3121F86DC97A04E5A7167DB4E5BCD371123D46E45DB6B5D5370A7F20FB633155D38FFA16D2BD761DCAC474B9A2F5023A4 + Uy = 0x0493101C962CD4D2FDDF782285E64584139C2F91B47F87FF82354D6630F746A28A0DB25741B5B34A828008B22ACC23F924FAAFBD4D33F81EA66956DFEAA2BFDFCF5 + pubblob = ssh_string(b"ecdsa-sha2-nistp521") + ssh_string(b"nistp521") + ssh_string(b'\x04' + be_integer(Ux, 528) + be_integer(Uy, 528)) + privblob = ssh2_mpint(x) + pubkey = ssh_key_new_pub('p521', pubblob) + privkey = ssh_key_new_priv('p521', pubblob, privblob) + sig = ssh_key_sign(privkey, b"sample", 0) + # Expected output using SHA-512 + r = 0x0C328FAFCBD79DD77850370C46325D987CB525569FB63C5D3BC53950E6D4C5F174E25A1EE9017B5D450606ADD152B534931D7D4E8455CC91F9B15BF05EC36E377FA + s = 0x0617CCE7CF5064806C467F678D3B4080D6F1CC50AF26CA209417308281B68AF282623EAA63E5B5C0723D8B8C37FF0777B1A20F8CCB1DCCC43997F1EE0E44DA4A67A + ref_sig = ssh_string(b"ecdsa-sha2-nistp521") + ssh_string(ssh2_mpint(r) + ssh2_mpint(s)) + self.assertEqual(sig, ref_sig) + # And the other test string + sig = ssh_key_sign(privkey, b"test", 0) + r = 0x13E99020ABF5CEE7525D16B69B229652AB6BDF2AFFCAEF38773B4B7D08725F10CDB93482FDCC54EDCEE91ECA4166B2A7C6265EF0CE2BD7051B7CEF945BABD47EE6D + s = 0x1FBD0013C674AA79CB39849527916CE301C66EA7CE8B80682786AD60F98F7E78A19CA69EFF5C57400E3B3A0AD66CE0978214D13BAF4E9AC60752F7B155E2DE4DCE3 + ref_sig = ssh_string(b"ecdsa-sha2-nistp521") + ssh_string(ssh2_mpint(r) + ssh2_mpint(s)) + self.assertEqual(sig, ref_sig) + def testBLAKE2b(self): # The standard test vectors for BLAKE2b (in the separate class # below) don't satisfy me because they only test one hash @@ -2381,10 +2622,10 @@ def testKeyMethods(self): test_keys = [ ('ed25519', 'AAAAC3NzaC1lZDI1NTE5AAAAIM7jupzef6CD0ps2JYxJp9IlwY49oorOseV5z5JFDFKn', 'AAAAIAf4/WRtypofgdNF2vbZOUFE1h4hvjw4tkGJZyOzI7c3', 255, b'0xf4d6e7f6f4479c23f0764ef43cea1711dbfe02aa2b5a32ff925c7c1fbf0f0db,0x27520c4592cf79e5b1ce8aa23d8ec125d2a7498c25369bd283a07fde9cbae3ce', [(0, 'AAAAC3NzaC1lZDI1NTE5AAAAQN73EqfyA4WneqDhgZ98TlRj9V5Wg8zCrMxTLJN1UtyfAnPUJDtfG/U0vOsP8PrnQxd41DDDnxrAXuqJz8rOagc=')]), ('ed448', 'AAAACXNzaC1lZDQ0OAAAADnRI0CQDym5IqUidLNDcSdHe54bYEwqjpjBlab8uKGoe6FRqqejha7+5U/VAHy7BmE23+ju26O9XgA=', 'AAAAObP9klqyiJSJsdFJf+xwZQdkbZGUqXE07K6e5plfRTGjYYkyWJFUNFH4jzIn9xH1TX9z9EGycPaXAA==', 448, b'0x4bf4a2b6586c60d8cdb52c2b45b897f6d2224bc37987489c0d70febb449e8c82964ed5785827be808e44d31dd31e6ff7c99f43e49f419928,0x5ebda3dbeee8df366106bb7c00d54fe5feae85a3a7aa51a17ba8a1b8fca695c1988e2a4c601b9e7b47277143b37422a522b9290f904023d1', [(0, 'AAAACXNzaC1lZDQ0OAAAAHLkSVioGMvLesZp3Tn+Z/sSK0Hl7RHsHP4q9flLzTpZG5h6JDH3VmZBEjTJ6iOLaa0v4FoNt0ng4wAB53WrlQC4h3iAusoGXnPMAKJLmqzplKOCi8HKXk8Xl8fsXbaoyhatv1OZpwJcffmh1x+x+LSgNQA=')]), - ('p256', 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHkYQ0sQoq5LbJI1VMWhw3bV43TSYi3WVpqIgKcBKK91TcFFlAMZgceOHQ0xAFYcSczIttLvFu+xkcLXrRd4N7Q=', 'AAAAIQCV/1VqiCsHZm/n+bq7lHEHlyy7KFgZBEbzqYaWtbx48Q==', 256, b'nistp256,0x7918434b10a2ae4b6c923554c5a1c376d5e374d2622dd6569a8880a70128af75,0x4dc14594031981c78e1d0d3100561c49ccc8b6d2ef16efb191c2d7ad177837b4', [(0, 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAABIAAAAIAryzHDGi/TcCnbdxZkIYR5EGR6SNYXr/HlQRF8le+/IAAAAIERfzn6eHuBbqWIop2qL8S7DWRB3lenN1iyL10xYQPKw')]), - ('p384', 'AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBMYK8PUtfAlJwKaBTIGEuCzH0vqOMa4UbcjrBbTbkGVSUnfo+nuC80NCdj9JJMs1jvfF8GzKLc5z8H3nZyM741/BUFjV7rEHsQFDek4KyWvKkEgKiTlZid19VukNo1q2Hg==', 'AAAAMGsfTmdB4zHdbiQ2euTSdzM6UKEOnrVjMAWwHEYvmG5qUOcBnn62fJDRJy67L+QGdg==', 384, b'nistp384,0xc60af0f52d7c0949c0a6814c8184b82cc7d2fa8e31ae146dc8eb05b4db9065525277e8fa7b82f34342763f4924cb358e,0xf7c5f06cca2dce73f07de767233be35fc15058d5eeb107b101437a4e0ac96bca90480a89395989dd7d56e90da35ab61e', [(0, 'AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAABpAAAAMDmHrtXCADzLvkkWG/duBAHlf6B1mVvdt6F0uzXfsf8Yub8WXNUNVnYq6ovrWPzLggAAADEA9izzwoUuFcXYRJeKcRLZEGMmSDDPzUZb7oZR0UgD1jsMQXs8UfpO31Qur/FDSCRK')]), - ('p521', 'AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAFrGthlKM152vu2Ghk+R7iO9/M6e+hTehNZ6+FBwof4HPkPB2/HHXj5+w5ynWyUrWiX5TI2riuJEIrJErcRH5LglADnJDX2w4yrKZ+wDHSz9lwh9p2F+B5R952es6gX3RJRkGA+qhKpKup8gKx78RMbleX8wgRtIu+4YMUnKb1edREiRg==', 'AAAAQgFh7VNJFUljWhhyAEiL0z+UPs/QggcMTd3Vv2aKDeBdCRl5di8r+BMm39L7bRzxRMEtW5NSKlDtE8MFEGdIE9khsw==', 521, b'nistp521,0x16b1ad86528cd79dafbb61a193e47b88ef7f33a7be8537a1359ebe141c287f81cf90f076fc71d78f9fb0e729d6c94ad6897e53236ae2b89108ac912b7111f92e094,0xe72435f6c38cab299fb00c74b3f65c21f69d85f81e51f79d9eb3a817dd125190603eaa12a92aea7c80ac7bf1131b95e5fcc2046d22efb860c52729bd5e75112246', [(0, 'AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAACMAAAAQgCLgvftvwM3CUaigrW0yzmCHoYjC6GLtO+6S91itqpgMEtWPNlaTZH6QQqkgscijWdXx98dDkQao/gcAKVmOZKPXgAAAEIB1PIrsDF1y6poJ/czqujB7NSUWt31v+c2t6UA8m2gTA1ARuVJ9XBGLMdceOTB00Hi9psC2RYFLpaWREOGCeDa6ow=')]), - ('dsa', 'AAAAB3NzaC1kc3MAAABhAJyWZzjVddGdyc5JPu/WPrC07vKRAmlqO6TUi49ah96iRcM7/D1aRMVAdYBepQ2mf1fsQTmvoC9KgQa79nN3kHhz0voQBKOuKI1ZAodfVOgpP4xmcXgjaA73Vjz22n4newAAABUA6l7/vIveaiA33YYv+SKcKLQaA8cAAABgbErc8QLw/WDz7mhVRZrU+9x3Tfs68j3eW+B/d7Rz1ZCqMYDk7r/F8dlBdQlYhpQvhuSBgzoFa0+qPvSSxPmutgb94wNqhHlVIUb9ZOJNloNr2lXiPP//Wu51TxXAEvAAAAAAYQCcQ9mufXtZa5RyfwT4NuLivdsidP4HRoLXdlnppfFAbNdbhxE0Us8WZt+a/443bwKnYxgif8dgxv5UROnWTngWu0jbJHpaDcTc9lRyTeSUiZZK312s/Sl7qDk3/Du7RUI=', 'AAAAFGx3ft7G8AQzFsjhle7PWardUXh3', 768, b'0x9c966738d575d19dc9ce493eefd63eb0b4eef29102696a3ba4d48b8f5a87dea245c33bfc3d5a44c54075805ea50da67f57ec4139afa02f4a8106bbf67377907873d2fa1004a3ae288d5902875f54e8293f8c66717823680ef7563cf6da7e277b,0xea5effbc8bde6a2037dd862ff9229c28b41a03c7,0x6c4adcf102f0fd60f3ee6855459ad4fbdc774dfb3af23dde5be07f77b473d590aa3180e4eebfc5f1d94175095886942f86e481833a056b4faa3ef492c4f9aeb606fde3036a8479552146fd64e24d96836bda55e23cffff5aee754f15c012f000,0x9c43d9ae7d7b596b94727f04f836e2e2bddb2274fe074682d77659e9a5f1406cd75b87113452cf1666df9aff8e376f02a76318227fc760c6fe5444e9d64e7816bb48db247a5a0dc4dcf654724de49489964adf5dacfd297ba83937fc3bbb4542', [(0, 'AAAAB3NzaC1kc3MAAAAo0T2t6dr8Qr5DK2B0ETwUa3BhxMLPjLY0ZtlOACmP/kUt3JgByLv+3g==')]), + ('p256', 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHkYQ0sQoq5LbJI1VMWhw3bV43TSYi3WVpqIgKcBKK91TcFFlAMZgceOHQ0xAFYcSczIttLvFu+xkcLXrRd4N7Q=', 'AAAAIQCV/1VqiCsHZm/n+bq7lHEHlyy7KFgZBEbzqYaWtbx48Q==', 256, b'nistp256,0x7918434b10a2ae4b6c923554c5a1c376d5e374d2622dd6569a8880a70128af75,0x4dc14594031981c78e1d0d3100561c49ccc8b6d2ef16efb191c2d7ad177837b4', [(0, 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAABIAAAAIFrd1bjr4GHfWsM9RNJ+y4Z0eVwpRRv3IvNE2moaA1x3AAAAIFWcwwCE69kS4oybMFEUP4r7qFAY8tSb1o8ItSFcSe2+')]), + ('p384', 'AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBMYK8PUtfAlJwKaBTIGEuCzH0vqOMa4UbcjrBbTbkGVSUnfo+nuC80NCdj9JJMs1jvfF8GzKLc5z8H3nZyM741/BUFjV7rEHsQFDek4KyWvKkEgKiTlZid19VukNo1q2Hg==', 'AAAAMGsfTmdB4zHdbiQ2euTSdzM6UKEOnrVjMAWwHEYvmG5qUOcBnn62fJDRJy67L+QGdg==', 384, b'nistp384,0xc60af0f52d7c0949c0a6814c8184b82cc7d2fa8e31ae146dc8eb05b4db9065525277e8fa7b82f34342763f4924cb358e,0xf7c5f06cca2dce73f07de767233be35fc15058d5eeb107b101437a4e0ac96bca90480a89395989dd7d56e90da35ab61e', [(0, 'AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAABoAAAAMFqCJ+gBP4GGc7yCy9F5e4EjkDlvYBYsYWMYFg3Md/ml7Md8pIrN7I0+8bFb99rZjQAAADAsM2kI+QOcgK+oVDaP0qkLRRbWDO1dSU5I2YfETyHVLYFNdRmgdWo6002XTO9jAsk=')]), + ('p521', 'AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAFrGthlKM152vu2Ghk+R7iO9/M6e+hTehNZ6+FBwof4HPkPB2/HHXj5+w5ynWyUrWiX5TI2riuJEIrJErcRH5LglADnJDX2w4yrKZ+wDHSz9lwh9p2F+B5R952es6gX3RJRkGA+qhKpKup8gKx78RMbleX8wgRtIu+4YMUnKb1edREiRg==', 'AAAAQgFh7VNJFUljWhhyAEiL0z+UPs/QggcMTd3Vv2aKDeBdCRl5di8r+BMm39L7bRzxRMEtW5NSKlDtE8MFEGdIE9khsw==', 521, b'nistp521,0x16b1ad86528cd79dafbb61a193e47b88ef7f33a7be8537a1359ebe141c287f81cf90f076fc71d78f9fb0e729d6c94ad6897e53236ae2b89108ac912b7111f92e094,0xe72435f6c38cab299fb00c74b3f65c21f69d85f81e51f79d9eb3a817dd125190603eaa12a92aea7c80ac7bf1131b95e5fcc2046d22efb860c52729bd5e75112246', [(0, 'AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAACLAAAAQVBkbaCKivgvc+68CULCdPayjzRUYZdj1G2pLyiPWTdmJKVKF/W1oDAtjMZlP53tqCpGxDdrLoJH2A39k6g5MgNjAAAAQgGrNcesPBw/HMopBQ1JqOG1cSlAzjiFT34FvM68ZhdIjbQ0eHFuYs97RekQ8dpxmkuM88e63ATbZy4yDX06pKgmuQ==')]), + ('dsa', 'AAAAB3NzaC1kc3MAAABhAJyWZzjVddGdyc5JPu/WPrC07vKRAmlqO6TUi49ah96iRcM7/D1aRMVAdYBepQ2mf1fsQTmvoC9KgQa79nN3kHhz0voQBKOuKI1ZAodfVOgpP4xmcXgjaA73Vjz22n4newAAABUA6l7/vIveaiA33YYv+SKcKLQaA8cAAABgbErc8QLw/WDz7mhVRZrU+9x3Tfs68j3eW+B/d7Rz1ZCqMYDk7r/F8dlBdQlYhpQvhuSBgzoFa0+qPvSSxPmutgb94wNqhHlVIUb9ZOJNloNr2lXiPP//Wu51TxXAEvAAAAAAYQCcQ9mufXtZa5RyfwT4NuLivdsidP4HRoLXdlnppfFAbNdbhxE0Us8WZt+a/443bwKnYxgif8dgxv5UROnWTngWu0jbJHpaDcTc9lRyTeSUiZZK312s/Sl7qDk3/Du7RUI=', 'AAAAFGx3ft7G8AQzFsjhle7PWardUXh3', 768, b'0x9c966738d575d19dc9ce493eefd63eb0b4eef29102696a3ba4d48b8f5a87dea245c33bfc3d5a44c54075805ea50da67f57ec4139afa02f4a8106bbf67377907873d2fa1004a3ae288d5902875f54e8293f8c66717823680ef7563cf6da7e277b,0xea5effbc8bde6a2037dd862ff9229c28b41a03c7,0x6c4adcf102f0fd60f3ee6855459ad4fbdc774dfb3af23dde5be07f77b473d590aa3180e4eebfc5f1d94175095886942f86e481833a056b4faa3ef492c4f9aeb606fde3036a8479552146fd64e24d96836bda55e23cffff5aee754f15c012f000,0x9c43d9ae7d7b596b94727f04f836e2e2bddb2274fe074682d77659e9a5f1406cd75b87113452cf1666df9aff8e376f02a76318227fc760c6fe5444e9d64e7816bb48db247a5a0dc4dcf654724de49489964adf5dacfd297ba83937fc3bbb4542', [(0, 'AAAAB3NzaC1kc3MAAAAoyCVHLG2QqdMx7NiCWaThx6tDA5mf7UGl+8By0IzmSldBujsGKNs20g==')]), ('rsa', 'AAAAB3NzaC1yc2EAAAABJQAAAGEA2ChX9+mQD/NULFkBrxLDI8d1PHgrInC2u11U4Grqu4oVzKvnFROo6DZeCu6sKhFJE5CnIL7evAthQ9hkXVHDhQ7xGVauzqyHGdIU4/pHRScAYWBv/PZOlNMrSoP/PP91', 'AAAAYCMNdgyGvWpez2EjMLSbQj0nQ3GW8jzvru3zdYwtA3hblNUU9QpWNxDmOMOApkwCzUgsdIPsBxctIeWT2h+v8sVOH+d66LCaNmNR0lp+dQ+iXM67hcGNuxJwRdMupD9ZbQAAADEA7XMrMAb4WuHaFafoTfGrf6Jhdy9Ozjqi1fStuld7Nj9JkoZluiL2dCwIrxqOjwU5AAAAMQDpC1gYiGVSPeDRILr2oxREtXWOsW+/ZZTfZNX7lvoufnp+qvwZPqvZnXQFHyZ8qB0AAAAwQE0wx8TPgcvRVEVv8Wt+o1NFlkJZayWD5hqpe/8AqUMZbqfg/aiso5mvecDLFgfV', 768, b'0x25,0xd82857f7e9900ff3542c5901af12c323c7753c782b2270b6bb5d54e06aeabb8a15ccabe71513a8e8365e0aeeac2a11491390a720bedebc0b6143d8645d51c3850ef11956aeceac8719d214e3fa4745270061606ffcf64e94d32b4a83ff3cff75', [(0, 'AAAAB3NzaC1yc2EAAABgrLSC4635RCsH1b3en58NqLsrH7PKRZyb3YmRasOyr8xIZMSlKZyxNg+kkn9OgBzbH9vChafzarfHyVwtJE2IMt3uwxTIWjwgwH19tc16k8YmNfDzujmB6OFOArmzKJgJ'), (2, 'AAAADHJzYS1zaGEyLTI1NgAAAGAJszr04BZlVBEdRLGOv1rTJwPiid/0I6/MycSH+noahvUH2wjrRhqDuv51F4nKYF5J9vBsEotTSrSF/cnLsliCdvVkEfmvhdcn/jx2LWF2OfjqETiYSc69Dde9UFmAPds='), (4, 'AAAADHJzYS1zaGEyLTUxMgAAAGBxfZ2m+WjvZ5YV5RFm0+w84CgHQ95EPndoAha0PCMc93AUHBmoHnezsJvEGuLovUm35w/0POmUNHI7HzM9PECwXrV0rO6N/HL/oFxJuDYmeqCpjMVmN8QXka+yxs2GEtA=')]), ] diff --git a/code/test/dsa_nonce_recover.py b/code/test/dsa_nonce_recover.py new file mode 100644 index 0000000..b6159b3 --- /dev/null +++ b/code/test/dsa_nonce_recover.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 + +# Recover the nonce value k used in integer DSA or NIST-style ECDSA, +# starting from the private key and the signature. +# +# _Without_ the private key, recovering the nonce is equivalent to +# recovering the private key itself. But with it, it's a trivial piece +# of modular arithmetic. +# +# This script generates a load of test signatures from various keys, +# recovers the nonces used, and prints them. This allows an eyeball +# check of whether they're evenly distributed. + +from base64 import b64decode as b64 + +from eccref import * +from testcrypt import * +from ssh import * + +def recover_nonce(order, hashalg, privint, transform_hash, r, s, message): + w = int(mp_invert(s, order)) + + h = ssh_hash_new(hashalg) + ssh_hash_update(h, message) + z = int(mp_from_bytes_be(ssh_hash_final(h))) + z = int(transform_hash(z)) + + return w * (z + r * privint) % order + +def dsa_decode_sig(signature): + _, signature = ssh_decode_string(signature, return_rest=True) + signature = ssh_decode_string(signature) + assert len(signature) == 40 + r = int(mp_from_bytes_be(signature[:20])) + s = int(mp_from_bytes_be(signature[20:])) + return r, s + +def ecdsa_decode_sig(signature): + _, signature = ssh_decode_string(signature, return_rest=True) + signature = ssh_decode_string(signature) + r, signature = ssh_decode_string(signature, return_rest=True) + s, signature = ssh_decode_string(signature, return_rest=True) + r = int(mp_from_bytes_be(r)) + s = int(mp_from_bytes_be(s)) + return r, s + +def test(privkey, decode_sig, transform_hash, order, hashalg, algid, obits): + print("----", algid) + print("k=0x{{:0{}b}}".format(obits).format(order)) + privblob = ssh_key_private_blob(privkey) + privint = int(mp_from_bytes_be(ssh_decode_string(privblob))) + for message in (f"msg{i}".encode('ASCII') for i in range(100)): + signature = ssh_key_sign(privkey, message, 0) + r, s = decode_sig(signature) + nonce = recover_nonce(order, hashalg, privint, transform_hash, + r, s, message) + print("k=0x{{:0{}b}}".format(obits).format(nonce)) + +def test_dsa(pubblob, privblob): + privkey = ssh_key_new_priv('dsa', pubblob, privblob) + _, buf = ssh_decode_string(pubblob, return_rest=True) + p, buf = ssh_decode_string(buf, return_rest=True) + q, buf = ssh_decode_string(buf, return_rest=True) + g, buf = ssh_decode_string(buf, return_rest=True) + p = int(mp_from_bytes_be(p)) + q = int(mp_from_bytes_be(q)) + g = int(mp_from_bytes_be(g)) + transform_hash = lambda h: h + test(privkey, dsa_decode_sig, transform_hash, q, 'sha1', 'dsa', 160) + +def test_ecdsa(algid, curve, hashalg, pubblob, privblob): + privkey = ssh_key_new_priv(algid, pubblob, privblob) + obits = int(mp_get_nbits(curve.G_order)) + def transform_hash(z): + shift = max(0, mp_get_nbits(z) - obits) + return mp_rshift_safe(z, shift) + test(privkey, ecdsa_decode_sig, transform_hash, curve.G_order, hashalg, + algid, obits) + +test_dsa(b64('AAAAB3NzaC1kc3MAAABhAJyWZzjVddGdyc5JPu/WPrC07vKRAmlqO6TUi49ah96iRcM7/D1aRMVAdYBepQ2mf1fsQTmvoC9KgQa79nN3kHhz0voQBKOuKI1ZAodfVOgpP4xmcXgjaA73Vjz22n4newAAABUA6l7/vIveaiA33YYv+SKcKLQaA8cAAABgbErc8QLw/WDz7mhVRZrU+9x3Tfs68j3eW+B/d7Rz1ZCqMYDk7r/F8dlBdQlYhpQvhuSBgzoFa0+qPvSSxPmutgb94wNqhHlVIUb9ZOJNloNr2lXiPP//Wu51TxXAEvAAAAAAYQCcQ9mufXtZa5RyfwT4NuLivdsidP4HRoLXdlnppfFAbNdbhxE0Us8WZt+a/443bwKnYxgif8dgxv5UROnWTngWu0jbJHpaDcTc9lRyTeSUiZZK312s/Sl7qDk3/Du7RUI='), b64('AAAAFGx3ft7G8AQzFsjhle7PWardUXh3')) +test_ecdsa('p256', p256, 'sha256', b64('AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHkYQ0sQoq5LbJI1VMWhw3bV43TSYi3WVpqIgKcBKK91TcFFlAMZgceOHQ0xAFYcSczIttLvFu+xkcLXrRd4N7Q='), b64('AAAAIQCV/1VqiCsHZm/n+bq7lHEHlyy7KFgZBEbzqYaWtbx48Q==')) +test_ecdsa('p384', p384, 'sha384', b64('AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBMYK8PUtfAlJwKaBTIGEuCzH0vqOMa4UbcjrBbTbkGVSUnfo+nuC80NCdj9JJMs1jvfF8GzKLc5z8H3nZyM741/BUFjV7rEHsQFDek4KyWvKkEgKiTlZid19VukNo1q2Hg=='), b64('AAAAMGsfTmdB4zHdbiQ2euTSdzM6UKEOnrVjMAWwHEYvmG5qUOcBnn62fJDRJy67L+QGdg==')) +test_ecdsa('p521', p521, 'sha512', b64('AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAFrGthlKM152vu2Ghk+R7iO9/M6e+hTehNZ6+FBwof4HPkPB2/HHXj5+w5ynWyUrWiX5TI2riuJEIrJErcRH5LglADnJDX2w4yrKZ+wDHSz9lwh9p2F+B5R952es6gX3RJRkGA+qhKpKup8gKx78RMbleX8wgRtIu+4YMUnKb1edREiRg=='), b64('AAAAQgFh7VNJFUljWhhyAEiL0z+UPs/QggcMTd3Vv2aKDeBdCRl5di8r+BMm39L7bRzxRMEtW5NSKlDtE8MFEGdIE9khsw==')) diff --git a/code/test/sclog/sclog.c b/code/test/sclog/sclog.c index d5304a0..74f4f86 100644 --- a/code/test/sclog/sclog.c +++ b/code/test/sclog/sclog.c @@ -214,6 +214,14 @@ static void wrap_malloc_pre(void *wrapctx, void **user_data) *user_data = drwrap_get_arg(wrapctx, 0); dr_fprintf(outfile, "malloc %"PRIuMAX"\n", (uintmax_t)*user_data); } +static void wrap_aligned_alloc_pre(void *wrapctx, void **user_data) +{ + logging_paused++; + size_t align = (size_t) drwrap_get_arg(wrapctx, 0); + *user_data = drwrap_get_arg(wrapctx, 1); + dr_fprintf(outfile, "aligned_alloc align=%zu size=%"PRIuMAX"\n", + align, (uintmax_t)*user_data); +} static void wrap_free_pre(void *wrapctx, void **user_data) { logging_paused++; @@ -239,71 +247,7 @@ static void wrap_alloc_post(void *wrapctx, void *user_data) } /* - * We wrap the C library function memset, because I've noticed that at - * least one optimised implementation of it diverges control flow - * internally based on what appears to be the _alignment_ of the input - * pointer - and that alignment check can vary depending on the - * addresses of allocated blocks. So I can't guarantee no divergence - * of control flow inside memset if malloc doesn't return the same - * values, and instead I just have to trust that memset isn't reading - * the contents of the block and basing control flow decisions on that. - */ -static void wrap_memset_pre(void *wrapctx, void **user_data) -{ - uint was_already_paused = logging_paused++; - - if (outfile == INVALID_FILE || was_already_paused) - return; - - const void *addr = drwrap_get_arg(wrapctx, 0); - size_t size = (size_t)drwrap_get_arg(wrapctx, 2); - - struct allocation *alloc = find_allocation(addr); - if (!alloc) { - dr_fprintf(outfile, "memset %"PRIuMAX" @ %"PRIxMAX"\n", - (uintmax_t)size, (uintmax_t)addr); - } else { - dr_fprintf(outfile, "memset %"PRIuMAX" @ allocations[%"PRIuPTR"]" - " + %"PRIxMAX"\n", (uintmax_t)size, alloc->index, - (uintmax_t)(addr - alloc->start)); - } -} - -/* - * Similarly to the above, wrap some versions of memmove. - */ -static void wrap_memmove_pre(void *wrapctx, void **user_data) -{ - uint was_already_paused = logging_paused++; - - if (outfile == INVALID_FILE || was_already_paused) - return; - - const void *daddr = drwrap_get_arg(wrapctx, 0); - const void *saddr = drwrap_get_arg(wrapctx, 1); - size_t size = (size_t)drwrap_get_arg(wrapctx, 2); - - - struct allocation *alloc; - - dr_fprintf(outfile, "memmove %"PRIuMAX" ", (uintmax_t)size); - if (!(alloc = find_allocation(daddr))) { - dr_fprintf(outfile, "to %"PRIxMAX" ", (uintmax_t)daddr); - } else { - dr_fprintf(outfile, "to allocations[%"PRIuPTR"] + %"PRIxMAX" ", - alloc->index, (uintmax_t)(daddr - alloc->start)); - } - if (!(alloc = find_allocation(saddr))) { - dr_fprintf(outfile, "from %"PRIxMAX"\n", (uintmax_t)saddr); - } else { - dr_fprintf(outfile, "from allocations[%"PRIuPTR"] + %"PRIxMAX"\n", - alloc->index, (uintmax_t)(saddr - alloc->start)); - } -} - -/* - * Common post-wrapper function for memset and free, whose entire - * function is to unpause the logging. + * Common post-wrapper function to unpause the logging. */ static void unpause_post(void *wrapctx, void *user_data) { @@ -594,10 +538,9 @@ static void load_module( TRY_WRAP("dry_run_real", NULL, wrap_dryrun); if (libc) { TRY_WRAP("malloc", wrap_malloc_pre, wrap_alloc_post); + TRY_WRAP("aligned_alloc", wrap_aligned_alloc_pre, wrap_alloc_post); TRY_WRAP("realloc", wrap_realloc_pre, wrap_alloc_post); TRY_WRAP("free", wrap_free_pre, unpause_post); - TRY_WRAP("memset", wrap_memset_pre, unpause_post); - TRY_WRAP("memmove", wrap_memmove_pre, unpause_post); /* * More strangely named versions of standard C library @@ -616,10 +559,6 @@ static void load_module( TRY_WRAP("__libc_malloc", wrap_malloc_pre, wrap_alloc_post); TRY_WRAP("__GI___libc_realloc", wrap_realloc_pre, wrap_alloc_post); TRY_WRAP("__GI___libc_free", wrap_free_pre, unpause_post); - TRY_WRAP("__memset_sse2_unaligned", wrap_memset_pre, unpause_post); - TRY_WRAP("__memset_sse2", wrap_memset_pre, unpause_post); - TRY_WRAP("__memmove_avx_unaligned_erms", wrap_memmove_pre, - unpause_post); TRY_WRAP("cfree", wrap_free_pre, unpause_post); } } diff --git a/code/test/testcrypt-func.h b/code/test/testcrypt-func.h index bd00729..cff2b86 100644 --- a/code/test/testcrypt-func.h +++ b/code/test/testcrypt-func.h @@ -327,6 +327,12 @@ FUNC(opt_val_string, key_components_nth_str, FUNC(opt_val_mpint, key_components_nth_mp, ARG(val_keycomponents, kc), ARG(uint, n)) +/* + * DSA nonce generation. + */ +FUNC(opt_val_mpint, rfc6979, ARG(hashalg, hash), ARG(val_mpint, modulus), + ARG(val_mpint, private_key), ARG(val_string_ptrlen, message)) + /* * The ssh_cipher abstraction. The in-place encrypt and decrypt * functions are wrapped to replace them with versions that take one diff --git a/code/test/testsc.c b/code/test/testsc.c index d6807c4..00421a6 100644 --- a/code/test/testsc.c +++ b/code/test/testsc.c @@ -431,6 +431,8 @@ VOLATILE_WRAPPED_DEFN(static, size_t, looplimit, (size_t x)) X(argon2) \ X(primegen_probabilistic) \ X(ntru) \ + X(rfc6979_setup) \ + X(rfc6979_attempt) \ /* end of list */ static void test_mp_get_nbits(void) @@ -1565,6 +1567,7 @@ static void test_hash(const ssh_hashalg *halg) test_skipped = true; return; } + ssh_hash_free(h); size_t datalen = 256; uint8_t *data = snewn(datalen, uint8_t); @@ -1574,16 +1577,14 @@ static void test_hash(const ssh_hashalg *halg) random_read(data, datalen); log_start(); + h = ssh_hash_new(halg); put_data(h, data, datalen); ssh_hash_final(h, hash); log_end(); - - h = ssh_hash_new(halg); } sfree(data); sfree(hash); - ssh_hash_free(h); } #define HASH_TESTFN(Y_unused, hash) \ @@ -1744,6 +1745,63 @@ static void test_ntru(void) strbuf_free(buffer); } +static void test_rfc6979_setup(void) +{ + mp_int *q = mp_new(512); + mp_int *x = mp_new(512); + + strbuf *message = strbuf_new(); + strbuf_append(message, 123); + + RFC6979 *s = rfc6979_new(&ssh_sha256, q, x); + + for (size_t i = 0; i < looplimit(20); i++) { + random_read(message->u, message->len); + mp_random_fill(q); + mp_random_fill(x); + + log_start(); + rfc6979_setup(s, ptrlen_from_strbuf(message)); + log_end(); + } + + rfc6979_free(s); + mp_free(q); + mp_free(x); + strbuf_free(message); +} + +static void test_rfc6979_attempt(void) +{ + mp_int *q = mp_new(512); + mp_int *x = mp_new(512); + + strbuf *message = strbuf_new(); + strbuf_append(message, 123); + + RFC6979 *s = rfc6979_new(&ssh_sha256, q, x); + + for (size_t i = 0; i < looplimit(5); i++) { + random_read(message->u, message->len); + mp_random_fill(q); + mp_random_fill(x); + + rfc6979_setup(s, ptrlen_from_strbuf(message)); + + for (size_t j = 0; j < looplimit(10); j++) { + log_start(); + RFC6979Result result = rfc6979_attempt(s); + mp_free(result.k); + log_end(); + } + } + + rfc6979_free(s); + mp_free(q); + mp_free(x); + strbuf_free(message); +} + static const struct test tests[] = { #define STRUCT_TEST(X) { #X, test_##X }, TESTLIST(STRUCT_TEST) diff --git a/code/unix/CMakeLists.txt b/code/unix/CMakeLists.txt index d4de28d..ce02098 100644 --- a/code/unix/CMakeLists.txt +++ b/code/unix/CMakeLists.txt @@ -100,9 +100,15 @@ add_executable(cgtest $) target_link_libraries(cgtest keygen console crypto utils) -add_executable(testsc - ${CMAKE_SOURCE_DIR}/test/testsc.c) -target_link_libraries(testsc keygen crypto utils) +if(HAVE_ALIGNED_ALLOC) + add_library(overaligned_alloc OBJECT + ${CMAKE_SOURCE_DIR}/utils/memory.c) + target_compile_definitions(overaligned_alloc PRIVATE ALLOCATION_ALIGNMENT=128) + add_executable(testsc + ${CMAKE_SOURCE_DIR}/test/testsc.c + $) + target_link_libraries(testsc keygen crypto utils) +endif() add_executable(testzlib ${CMAKE_SOURCE_DIR}/test/testzlib.c diff --git a/code/utils/memory.c b/code/utils/memory.c index 97ae940..0ba791a 100644 --- a/code/utils/memory.c +++ b/code/utils/memory.c @@ -2,6 +2,12 @@ * PuTTY's memory allocation wrappers. */ +#ifdef ALLOCATION_ALIGNMENT +/* Before we include standard headers, define _ISOC11_SOURCE so that + * we get the declaration of aligned_alloc(). */ +#define _ISOC11_SOURCE +#endif + #include #include #include @@ -28,6 +34,8 @@ void *safemalloc(size_t factor1, size_t factor2, size_t addend) void *p; #ifdef MINEFIELD p = minefield_c_malloc(size); +#elif defined ALLOCATION_ALIGNMENT + p = aligned_alloc(ALLOCATION_ALIGNMENT, size); #else p = malloc(size); #endif @@ -52,6 +60,8 @@ void *saferealloc(void *ptr, size_t n, size_t size) if (!ptr) { #ifdef MINEFIELD p = minefield_c_malloc(size); +#elif defined ALLOCATION_ALIGNMENT + p = aligned_alloc(ALLOCATION_ALIGNMENT, size); #else p = malloc(size); #endif diff --git a/code/version.h b/code/version.h index eb893e3..2f414d7 100644 --- a/code/version.h +++ b/code/version.h @@ -1,5 +1,5 @@ /* Generated by automated build script */ -#define RELEASE 0.80-1 -#define TEXTVER "Release 0.80-1" -#define SSHVER "-Release-0.80-1" -#define BINARY_VERSION 0,80,0,1 +#define RELEASE 0.81 +#define TEXTVER "Release 0.81" +#define SSHVER "-Release-0.81" +#define BINARY_VERSION 0,81,0,0 diff --git a/packager/build.cmd b/packager/build.cmd index 112332f..8ef116c 100644 --- a/packager/build.cmd +++ b/packager/build.cmd @@ -2,8 +2,8 @@ TITLE Building PuTTY-CAC :: version information -SET VER=0.80u1 -SET VERN=0.80.0.1 +SET VER=0.81 +SET VERN=0.81.0.0 :: setup environment variables based on location of this script SET INSTDIR=%~dp0 diff --git a/packager/build.vs.sln.cmd b/packager/build.vs.sln.cmd index 63e5bb9..6bfb1ce 100644 --- a/packager/build.vs.sln.cmd +++ b/packager/build.vs.sln.cmd @@ -2,8 +2,8 @@ TITLE Building PuTTY-CAC :: version information -SET VER=0.80 -SET VERN=0.80.0.0 +SET VER=0.81 +SET VERN=0.81.0.0 :: setup environment variables based on location of this script SET INSTDIR=%~dp0 diff --git a/packager/build.zip.hash.cmd b/packager/build.zip.hash.cmd index 33cdc23..5a10cef 100644 --- a/packager/build.zip.hash.cmd +++ b/packager/build.zip.hash.cmd @@ -2,8 +2,8 @@ TITLE Building PuTTY-CAC :: version information -SET VER=0.80 -SET VERN=0.80.0.0 +SET VER=0.81 +SET VERN=0.81.0.0 :: setup environment variables based on location of this script SET INSTDIR=%~dp0 diff --git a/packager/chocolatey/putty cac.nuspec b/packager/chocolatey/putty cac.nuspec index 8bf4d6d..2ba26fd 100644 --- a/packager/chocolatey/putty cac.nuspec +++ b/packager/chocolatey/putty cac.nuspec @@ -2,7 +2,7 @@ putty-cac - 0.80-u1 + 0.81 https://github.com/NoMoreFood/putty-cac NoMoreFood PuTTY-CAC diff --git a/packager/chocolatey/tools/chocolateyinstall.ps1 b/packager/chocolatey/tools/chocolateyinstall.ps1 index fe8a99f..73e0cc1 100644 --- a/packager/chocolatey/tools/chocolateyinstall.ps1 +++ b/packager/chocolatey/tools/chocolateyinstall.ps1 @@ -2,8 +2,8 @@ $packageName= $env:ChocolateyPackageName $toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" -$url = 'https://github.com/NoMoreFood/putty-cac/raw/0.80/binaries/puttycac-0.80u1-installer.msi' -$url64 = 'https://github.com/NoMoreFood/putty-cac/raw/0.80/binaries/puttycac-64bit-0.80u1-installer.msi' +$url = 'https://github.com/NoMoreFood/putty-cac/raw/0.81/binaries/puttycac-0.81-installer.msi' +$url64 = 'https://github.com/NoMoreFood/putty-cac/raw/0.81/binaries/puttycac-64bit-0.81-installer.msi' $packageArgs = @{ packageName = $packageName @@ -14,9 +14,9 @@ $packageArgs = @{ softwareName = 'PuTTY CAC*' - checksum = '26D216E8F78CECBFB2120F4B1888EE90E553B0CBF2F755D8F9D3A498ED11D468' + checksum = '0576FEA70A072D37805EDB2CAC5BC74254FABE75544FA9A0DD3B971DA7AEA254' checksumType = 'sha256' - checksum64 = '572F071DCCF03891A86312CE10D301AA71AAB0A42D79EAA5B6CA72D4F0258641' + checksum64 = '537C8670BBEC9C5FB6D120556231B753C226578D37D9383D2209CF2B78AE5FF5' checksumType64= 'sha256' silentArgs = "/qn /norestart /l*v `"$($env:TEMP)\$($packageName).$($env:chocolateyPackageVersion).MsiInstall.log`"" diff --git a/packager/winget/NoMoreFood.PuTTY-CAC.installer.yaml b/packager/winget/NoMoreFood.PuTTY-CAC.installer.yaml index 0469120..8c0cbbf 100644 --- a/packager/winget/NoMoreFood.PuTTY-CAC.installer.yaml +++ b/packager/winget/NoMoreFood.PuTTY-CAC.installer.yaml @@ -1,5 +1,5 @@ PackageIdentifier: NoMoreFood.PuTTY-CAC -PackageVersion: 0.80.0.1 +PackageVersion: 0.81.0.0 Platform: - Windows.Desktop MinimumOSVersion: 10.0.0.0 @@ -10,21 +10,21 @@ InstallModes: FileExtensions: - PPK Installers: -- InstallerUrl: https://github.com/NoMoreFood/putty-cac/raw/0.80/binaries/puttycac-64bit-0.80-installer.msi +- InstallerUrl: https://github.com/NoMoreFood/putty-cac/raw/0.81/binaries/puttycac-64bit-0.81-installer.msi InstallerLocale: en-US Architecture: x64 InstallerType: wix Scope: machine - InstallerSha256: 572F071DCCF03891A86312CE10D301AA71AAB0A42D79EAA5B6CA72D4F0258641 + InstallerSha256: 537C8670BBEC9C5FB6D120556231B753C226578D37D9383D2209CF2B78AE5FF5 UpgradeBehavior: install - ProductCode: '{ADC5D359-9B89-465E-A304-FCC6E52BF515}' -- InstallerUrl: https://github.com/NoMoreFood/putty-cac/raw/0.80/binaries/puttycac-0.80-installer.msi + ProductCode: '{D28B60ED-13E3-4EE9-9A08-EF69CB032152}' +- InstallerUrl: https://github.com/NoMoreFood/putty-cac/raw/0.81/binaries/puttycac-0.81-installer.msi InstallerLocale: en-US Architecture: x86 InstallerType: wix Scope: machine - InstallerSha256: 26D216E8F78CECBFB2120F4B1888EE90E553B0CBF2F755D8F9D3A498ED11D468 + InstallerSha256: 0576FEA70A072D37805EDB2CAC5BC74254FABE75544FA9A0DD3B971DA7AEA254 UpgradeBehavior: install - ProductCode: '{7FCD6E0D-AA3B-42E0-A475-E986C02B86A1}' + ProductCode: '{099DEBB9-E8CB-4345-9C14-DAF0398A349E}' ManifestType: installer -ManifestVersion: 1.5.0 \ No newline at end of file +ManifestVersion: 1.6.0 \ No newline at end of file diff --git a/packager/winget/NoMoreFood.PuTTY-CAC.locale.en-US.yaml b/packager/winget/NoMoreFood.PuTTY-CAC.locale.en-US.yaml index 3bc0d98..74e540a 100644 --- a/packager/winget/NoMoreFood.PuTTY-CAC.locale.en-US.yaml +++ b/packager/winget/NoMoreFood.PuTTY-CAC.locale.en-US.yaml @@ -1,5 +1,5 @@ PackageIdentifier: NoMoreFood.PuTTY-CAC -PackageVersion: 0.80.0.1 +PackageVersion: 0.81.0.0 PackageLocale: en-US Publisher: Simon Tatham PublisherUrl: https://github.com/NoMoreFood @@ -24,4 +24,4 @@ Tags: - capi - cac ManifestType: defaultLocale -ManifestVersion: 1.5.0 \ No newline at end of file +ManifestVersion: 1.6.0 \ No newline at end of file diff --git a/packager/winget/NoMoreFood.PuTTY-CAC.yaml b/packager/winget/NoMoreFood.PuTTY-CAC.yaml index e50677b..01016c3 100644 --- a/packager/winget/NoMoreFood.PuTTY-CAC.yaml +++ b/packager/winget/NoMoreFood.PuTTY-CAC.yaml @@ -1,5 +1,5 @@ PackageIdentifier: NoMoreFood.PuTTY-CAC -PackageVersion: 0.80.0.1 +PackageVersion: 0.81.0.0 DefaultLocale: en-US ManifestType: version -ManifestVersion: 1.5.0 \ No newline at end of file +ManifestVersion: 1.6.0 \ No newline at end of file