Package: release.debian.org Severity: normal Tags: bullseye X-Debbugs-Cc: pu...@packages.debian.org Control: affects -1 + src:putty User: release.debian....@packages.debian.org Usertags: pu
[ Reason ] Security fix CVE-2024-31497 [ Impact ] Vulnerable biased nonce generation is still here. [ Tests ] Full crypto test suite testing particularly CVE-2024-31497 is run [ Risks ] Low reviewed by maintainer Approved by Colin [ Checklist ] [X] *all* changes are documented in the d/changelog [X] I reviewed all changes and I approve them [X] attach debdiff against the package in (old)stable [X] the issue is verified as fixed in unstable [ Changes ] putty (0.74-1+deb11u2) bullseye; urgency=medium * Non-maintainer upload. * Cherry-pick from upstream: - Refactor the ssh_hash vtable. - Add an extra HMAC constructor function. - Fix CVE-2024-31497: biased ECDSA nonce generation allows an attacker to recover a user's NIST P-521 secret key via a quick attack in approximately 60 signatures. In other words, an adversary may already have enough signature information to compromise a victim's private key, even if there is no further use of vulnerable PuTTY versions.
diff -Nru putty-0.74/debian/changelog putty-0.74/debian/changelog --- putty-0.74/debian/changelog 2023-12-22 17:36:21.000000000 +0000 +++ putty-0.74/debian/changelog 2024-07-16 10:13:59.000000000 +0000 @@ -1,3 +1,18 @@ +putty (0.74-1+deb11u2) bullseye; urgency=medium + + * Non-maintainer upload. + * Cherry-pick from upstream: + - Refactor the ssh_hash vtable. + - Add an extra HMAC constructor function. + - Fix CVE-2024-31497: biased ECDSA nonce generation allows an attacker + to recover a user's NIST P-521 secret key via a quick attack in + approximately 60 signatures. In other words, an adversary + may already have enough signature information to compromise a victim's + private key, even if there is no further use of vulnerable PuTTY + versions. + + -- Bastien Roucari??s <ro...@debian.org> Tue, 16 Jul 2024 10:13:59 +0000 + putty (0.74-1+deb11u1) bullseye-security; urgency=medium * Cherry-pick from upstream: diff -Nru putty-0.74/debian/.git-dpm putty-0.74/debian/.git-dpm --- putty-0.74/debian/.git-dpm 2023-12-21 16:54:36.000000000 +0000 +++ putty-0.74/debian/.git-dpm 2024-07-16 10:13:59.000000000 +0000 @@ -1,6 +1,6 @@ # see git-dpm(1) from git-dpm package -a24da4ff8e3a0d9f2b4adf9d092358f41df18432 -a24da4ff8e3a0d9f2b4adf9d092358f41df18432 +3b973f00dd0076ae305a0b5e7ddab9b811a833dd +3b973f00dd0076ae305a0b5e7ddab9b811a833dd 4bd8df1aca313a0da36e559bd4a4d0cf0bc2eaa8 4bd8df1aca313a0da36e559bd4a4d0cf0bc2eaa8 putty_0.74.orig.tar.gz diff -Nru putty-0.74/debian/.gitignore putty-0.74/debian/.gitignore --- putty-0.74/debian/.gitignore 2023-12-21 16:54:36.000000000 +0000 +++ putty-0.74/debian/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -/*.debhelper* -/*.substvars -/files -/pterm -/putty -/putty-doc -/putty-tools diff -Nru putty-0.74/debian/patches/0006-Refactor-the-ssh_hash-vtable.-NFC.patch putty-0.74/debian/patches/0006-Refactor-the-ssh_hash-vtable.-NFC.patch --- putty-0.74/debian/patches/0006-Refactor-the-ssh_hash-vtable.-NFC.patch 1970-01-01 00:00:00.000000000 +0000 +++ putty-0.74/debian/patches/0006-Refactor-the-ssh_hash-vtable.-NFC.patch 2024-07-16 10:13:59.000000000 +0000 @@ -0,0 +1,691 @@ +From 9f15a5795bf67d90aad97a394c4b1a93a56d4cba Mon Sep 17 00:00:00 2001 +From: Simon Tatham <ana...@pobox.com> +Date: Sun, 15 Dec 2019 09:30:10 +0000 +Subject: Refactor the ssh_hash vtable. (NFC) + +Refactor the ssh_hash vtable. (NFC) + +The idea is to arrange that an ssh_hash object can be reused without +having to free it and allocate a new one. So the 'final' method has +been replaced with 'digest', which does everything except the trailing +free; and there's also a new pair of methods 'reset' and 'copyfrom' +which overwrite the state of a hash with either the starting state or +a copy of another state. Meanwhile, the 'new' allocator function has +stopped performing 'reset' as a side effect; now it _just_ does the +administrative stuff (allocation, setting up vtables), and returns an +object which isn't yet ready to receive any actual data, expecting +that the caller will either reset it or copy another hash state into +it. + +In particular, that means that the SHA-384 / SHA-512 pair no longer +need separate 'new' methods, because only the 'reset' part has to +change between them. + +This commit makes no change to the user-facing API of wrapper +functions in ssh.h, except to add new functions which nothing yet +calls. The user-facing ssh_hash_new() calls the new and reset methods +in succession, and the copy and final methods still exist to do +new+copy and digest+free. + +origin: backport, https://git.tartarus.org/?p=simon/putty.git;a=commitdiff_plain;h=156762fc0246c4ff587c72eed7010552f9c1e5bb +--- + ssh.h | 26 ++++++++++---- + sshmd5.c | 26 +++++++------- + sshsh256.c | 100 +++++++++++++++++++++++++++++------------------------ + sshsh512.c | 35 ++++++++----------- + sshsha.c | 97 +++++++++++++++++++++++++++------------------------ + 5 files changed, 154 insertions(+), 130 deletions(-) + +diff --git a/ssh.h b/ssh.h +index 2449c6a4..2f8df928 100644 +--- a/ssh.h ++++ b/ssh.h +@@ -717,8 +717,9 @@ struct ssh_hash { + + struct ssh_hashalg { + ssh_hash *(*new)(const ssh_hashalg *alg); +- ssh_hash *(*copy)(ssh_hash *); +- void (*final)(ssh_hash *, unsigned char *); /* ALSO FREES THE ssh_hash! */ ++ void (*reset)(ssh_hash *); ++ void (*copyfrom)(ssh_hash *dest, ssh_hash *src); ++ void (*digest)(ssh_hash *, unsigned char *); + void (*free)(ssh_hash *); + int hlen; /* output length in bytes */ + int blocklen; /* length of the hash's input block, or 0 for N/A */ +@@ -728,16 +729,27 @@ struct ssh_hashalg { + }; + + static inline ssh_hash *ssh_hash_new(const ssh_hashalg *alg) +-{ return alg->new(alg); } +-static inline ssh_hash *ssh_hash_copy(ssh_hash *h) +-{ return h->vt->copy(h); } +-static inline void ssh_hash_final(ssh_hash *h, unsigned char *out) +-{ h->vt->final(h, out); } ++{ ssh_hash *h = alg->new(alg); if (h) h->vt->reset(h); return h; } ++static inline ssh_hash *ssh_hash_copy(ssh_hash *orig) ++{ ssh_hash *h = orig->vt->new(orig->vt); h->vt->copyfrom(h, orig); return h; } ++static inline void ssh_hash_digest(ssh_hash *h, unsigned char *out) ++{ h->vt->digest(h, out); } + static inline void ssh_hash_free(ssh_hash *h) + { h->vt->free(h); } + static inline const ssh_hashalg *ssh_hash_alg(ssh_hash *h) + { return h->vt; } + ++/* The reset and copyfrom vtable methods return void. But for call-site ++ * convenience, these wrappers return their input pointer. */ ++static inline ssh_hash *ssh_hash_reset(ssh_hash *h) ++{ h->vt->reset(h); return h; } ++static inline ssh_hash *ssh_hash_copyfrom(ssh_hash *dest, ssh_hash *src) ++{ dest->vt->copyfrom(dest, src); return dest; } ++ ++/* ssh_hash_final emits the digest _and_ frees the ssh_hash */ ++static inline void ssh_hash_final(ssh_hash *h, unsigned char *out) ++{ h->vt->digest(h, out); h->vt->free(h); } ++ + /* Handy macros for defining all those text-name fields at once */ + #define HASHALG_NAMES_BARE(base) \ + base, NULL, base +diff --git a/sshmd5.c b/sshmd5.c +index 04de6816..dbcba3f7 100644 +--- a/sshmd5.c ++++ b/sshmd5.c +@@ -235,24 +235,24 @@ struct md5_hash { + static ssh_hash *md5_new(const ssh_hashalg *alg) + { + struct md5_hash *h = snew(struct md5_hash); +- MD5Init(&h->state); + h->hash.vt = alg; + BinarySink_DELEGATE_INIT(&h->hash, &h->state); + return &h->hash; + } + +-static ssh_hash *md5_copy(ssh_hash *hashold) ++static void md5_reset(ssh_hash *hash) + { +- struct md5_hash *hold, *hnew; +- ssh_hash *hashnew = md5_new(hashold->vt); +- +- hold = container_of(hashold, struct md5_hash, hash); +- hnew = container_of(hashnew, struct md5_hash, hash); ++ struct md5_hash *h = container_of(hash, struct md5_hash, hash); ++ MD5Init(&h->state); ++} + +- hnew->state = hold->state; +- BinarySink_COPIED(&hnew->state); ++static void md5_copyfrom(ssh_hash *hcopy, ssh_hash *horig) ++{ ++ struct md5_hash *copy = container_of(hcopy, struct md5_hash, hash); ++ struct md5_hash *orig = container_of(horig, struct md5_hash, hash); + +- return hashnew; ++ copy->state = orig->state; ++ BinarySink_COPIED(©->state); + } + + static void md5_free(ssh_hash *hash) +@@ -263,13 +263,13 @@ static void md5_free(ssh_hash *hash) + sfree(h); + } + +-static void md5_final(ssh_hash *hash, unsigned char *output) ++static void md5_digest(ssh_hash *hash, unsigned char *output) + { + struct md5_hash *h = container_of(hash, struct md5_hash, hash); + MD5Final(output, &h->state); +- md5_free(hash); + } + + const ssh_hashalg ssh_md5 = { +- md5_new, md5_copy, md5_final, md5_free, 16, 64, HASHALG_NAMES_BARE("MD5"), ++ md5_new, md5_reset, md5_copyfrom, md5_digest, md5_free, ++ 16, 64, HASHALG_NAMES_BARE("MD5"), + }; +diff --git a/sshsh256.c b/sshsh256.c +index 1e445171..363e50a4 100644 +--- a/sshsh256.c ++++ b/sshsh256.c +@@ -98,7 +98,7 @@ static ssh_hash *sha256_select(const ssh_hashalg *alg) + } + + const ssh_hashalg ssh_sha256 = { +- sha256_select, NULL, NULL, NULL, ++ sha256_select, NULL, NULL, NULL, NULL, + 32, 64, HASHALG_NAMES_ANNOTATED("SHA-256", "dummy selector vtable"), + }; + +@@ -276,26 +276,28 @@ static ssh_hash *sha256_sw_new(const ssh_hashalg *alg) + { + sha256_sw *s = snew(sha256_sw); + +- memcpy(s->core, sha256_initial_state, sizeof(s->core)); +- +- sha256_block_setup(&s->blk); +- + s->hash.vt = alg; + BinarySink_INIT(s, sha256_sw_write); + BinarySink_DELEGATE_INIT(&s->hash, s); + return &s->hash; + } + +-static ssh_hash *sha256_sw_copy(ssh_hash *hash) ++static void sha256_sw_reset(ssh_hash *hash) + { + sha256_sw *s = container_of(hash, sha256_sw, hash); +- sha256_sw *copy = snew(sha256_sw); + +- memcpy(copy, s, sizeof(*copy)); ++ memcpy(s->core, sha256_initial_state, sizeof(s->core)); ++ sha256_block_setup(&s->blk); ++} ++ ++static void sha256_sw_copyfrom(ssh_hash *hcopy, ssh_hash *horig) ++{ ++ sha256_sw *copy = container_of(hcopy, sha256_sw, hash); ++ sha256_sw *orig = container_of(horig, sha256_sw, hash); ++ ++ memcpy(copy, orig, sizeof(*copy)); + BinarySink_COPIED(copy); + BinarySink_DELEGATE_INIT(©->hash, copy); +- +- return ©->hash; + } + + static void sha256_sw_free(ssh_hash *hash) +@@ -315,18 +317,18 @@ static void sha256_sw_write(BinarySink *bs, const void *vp, size_t len) + sha256_sw_block(s->core, s->blk.block); + } + +-static void sha256_sw_final(ssh_hash *hash, uint8_t *digest) ++static void sha256_sw_digest(ssh_hash *hash, uint8_t *digest) + { + sha256_sw *s = container_of(hash, sha256_sw, hash); + + sha256_block_pad(&s->blk, BinarySink_UPCAST(s)); + for (size_t i = 0; i < 8; i++) + PUT_32BIT_MSB_FIRST(digest + 4*i, s->core[i]); +- sha256_sw_free(hash); + } + + const ssh_hashalg ssh_sha256_sw = { +- sha256_sw_new, sha256_sw_copy, sha256_sw_final, sha256_sw_free, ++ sha256_sw_new, sha256_sw_reset, sha256_sw_copyfrom, sha256_sw_digest, ++ sha256_sw_free, + 32, 64, HASHALG_NAMES_ANNOTATED("SHA-256", "unaccelerated"), + }; + +@@ -602,13 +604,24 @@ static sha256_ni *sha256_ni_alloc(void) + return s; + } + +-FUNC_ISA static ssh_hash *sha256_ni_new(const ssh_hashalg *alg) ++static ssh_hash *sha256_ni_new(const ssh_hashalg *alg) + { + if (!sha256_hw_available_cached()) + return NULL; + + sha256_ni *s = sha256_ni_alloc(); + ++ s->hash.vt = alg; ++ BinarySink_INIT(s, sha256_ni_write); ++ BinarySink_DELEGATE_INIT(&s->hash, s); ++ ++ return &s->hash; ++} ++ ++FUNC_ISA static void sha256_ni_reset(ssh_hash *hash) ++{ ++ sha256_ni *s = container_of(hash, sha256_ni, hash); ++ + /* Initialise the core vectors in their storage order */ + s->core[0] = _mm_set_epi64x( + 0x6a09e667bb67ae85ULL, 0x510e527f9b05688cULL); +@@ -616,26 +629,19 @@ FUNC_ISA static ssh_hash *sha256_ni_new(const ssh_hashalg *alg) + 0x3c6ef372a54ff53aULL, 0x1f83d9ab5be0cd19ULL); + + sha256_block_setup(&s->blk); +- +- s->hash.vt = alg; +- BinarySink_INIT(s, sha256_ni_write); +- BinarySink_DELEGATE_INIT(&s->hash, s); +- return &s->hash; + } + +-static ssh_hash *sha256_ni_copy(ssh_hash *hash) ++static void sha256_ni_copyfrom(ssh_hash *hcopy, ssh_hash *horig) + { +- sha256_ni *s = container_of(hash, sha256_ni, hash); +- sha256_ni *copy = sha256_ni_alloc(); ++ sha256_ni *copy = container_of(hcopy, sha256_ni, hash); ++ sha256_ni *orig = container_of(horig, sha256_ni, hash); + + void *ptf_save = copy->pointer_to_free; +- *copy = *s; /* structure copy */ ++ *copy = *orig; /* structure copy */ + copy->pointer_to_free = ptf_save; + + BinarySink_COPIED(copy); + BinarySink_DELEGATE_INIT(©->hash, copy); +- +- return ©->hash; + } + + static void sha256_ni_free(ssh_hash *hash) +@@ -656,7 +662,7 @@ static void sha256_ni_write(BinarySink *bs, const void *vp, size_t len) + sha256_ni_block(s->core, s->blk.block); + } + +-FUNC_ISA static void sha256_ni_final(ssh_hash *hash, uint8_t *digest) ++FUNC_ISA static void sha256_ni_digest(ssh_hash *hash, uint8_t *digest) + { + sha256_ni *s = container_of(hash, sha256_ni, hash); + +@@ -677,12 +683,11 @@ FUNC_ISA static void sha256_ni_final(ssh_hash *hash, uint8_t *digest) + __m128i *output = (__m128i *)digest; + _mm_storeu_si128(output, dcba); + _mm_storeu_si128(output+1, hgfe); +- +- sha256_ni_free(hash); + } + + const ssh_hashalg ssh_sha256_hw = { +- sha256_ni_new, sha256_ni_copy, sha256_ni_final, sha256_ni_free, ++ sha256_ni_new, sha256_ni_reset, sha256_ni_copyfrom, sha256_ni_digest, ++ sha256_ni_free, + 32, 64, HASHALG_NAMES_ANNOTATED("SHA-256", "SHA-NI accelerated"), + }; + +@@ -818,28 +823,31 @@ static ssh_hash *sha256_neon_new(const ssh_hashalg *alg) + + sha256_neon *s = snew(sha256_neon); + +- s->core.abcd = vld1q_u32(sha256_initial_state); +- s->core.efgh = vld1q_u32(sha256_initial_state + 4); +- +- sha256_block_setup(&s->blk); +- + s->hash.vt = alg; + BinarySink_INIT(s, sha256_neon_write); + BinarySink_DELEGATE_INIT(&s->hash, s); + return &s->hash; + } + +-static ssh_hash *sha256_neon_copy(ssh_hash *hash) ++static void sha256_neon_reset(ssh_hash *hash) + { + sha256_neon *s = container_of(hash, sha256_neon, hash); +- sha256_neon *copy = snew(sha256_neon); + +- *copy = *s; /* structure copy */ ++ s->core.abcd = vld1q_u32(sha256_initial_state); ++ s->core.efgh = vld1q_u32(sha256_initial_state + 4); ++ ++ sha256_block_setup(&s->blk); ++} ++ ++static void sha256_neon_copyfrom(ssh_hash *hcopy, ssh_hash *horig) ++{ ++ sha256_neon *copy = container_of(hcopy, sha256_neon, hash); ++ sha256_neon *orig = container_of(horig, sha256_neon, hash); ++ ++ *copy = *orig; /* structure copy */ + + BinarySink_COPIED(copy); + BinarySink_DELEGATE_INIT(©->hash, copy); +- +- return ©->hash; + } + + static void sha256_neon_free(ssh_hash *hash) +@@ -858,18 +866,18 @@ static void sha256_neon_write(BinarySink *bs, const void *vp, size_t len) + sha256_neon_block(&s->core, s->blk.block); + } + +-static void sha256_neon_final(ssh_hash *hash, uint8_t *digest) ++static void sha256_neon_digest(ssh_hash *hash, uint8_t *digest) + { + sha256_neon *s = container_of(hash, sha256_neon, hash); + + sha256_block_pad(&s->blk, BinarySink_UPCAST(s)); + vst1q_u8(digest, vrev32q_u8(vreinterpretq_u8_u32(s->core.abcd))); + vst1q_u8(digest + 16, vrev32q_u8(vreinterpretq_u8_u32(s->core.efgh))); +- sha256_neon_free(hash); + } + + const ssh_hashalg ssh_sha256_hw = { +- sha256_neon_new, sha256_neon_copy, sha256_neon_final, sha256_neon_free, ++ sha256_neon_new, sha256_neon_reset, sha256_neon_copyfrom, ++ sha256_neon_digest, sha256_neon_free, + 32, 64, HASHALG_NAMES_ANNOTATED("SHA-256", "NEON accelerated"), + }; + +@@ -895,12 +903,14 @@ static ssh_hash *sha256_stub_new(const ssh_hashalg *alg) + + #define STUB_BODY { unreachable("Should never be called"); } + +-static ssh_hash *sha256_stub_copy(ssh_hash *hash) STUB_BODY ++static void sha256_stub_reset(ssh_hash *hash) STUB_BODY ++static void sha256_stub_copyfrom(ssh_hash *hash, ssh_hash *orig) STUB_BODY + static void sha256_stub_free(ssh_hash *hash) STUB_BODY +-static void sha256_stub_final(ssh_hash *hash, uint8_t *digest) STUB_BODY ++static void sha256_stub_digest(ssh_hash *hash, uint8_t *digest) STUB_BODY + + const ssh_hashalg ssh_sha256_hw = { +- sha256_stub_new, sha256_stub_copy, sha256_stub_final, sha256_stub_free, ++ sha256_stub_new, sha256_stub_reset, sha256_stub_copyfrom, ++ sha256_stub_digest, sha256_stub_free, + 32, 64, HASHALG_NAMES_ANNOTATED( + "SHA-256", "!NONEXISTENT ACCELERATED VERSION!"), + }; +diff --git a/sshsh512.c b/sshsh512.c +index 6712047c..03201d74 100644 +--- a/sshsh512.c ++++ b/sshsh512.c +@@ -307,24 +307,24 @@ struct sha512_hash { + static ssh_hash *sha512_new(const ssh_hashalg *alg) + { + struct sha512_hash *h = snew(struct sha512_hash); +- SHA512_Init(&h->state); + h->hash.vt = alg; + BinarySink_DELEGATE_INIT(&h->hash, &h->state); +- return &h->hash; ++ return ssh_hash_reset(&h->hash); + } + +-static ssh_hash *sha512_copy(ssh_hash *hashold) ++static void sha512_reset(ssh_hash *hash) + { +- struct sha512_hash *hold, *hnew; +- ssh_hash *hashnew = sha512_new(hashold->vt); ++ struct sha512_hash *h = container_of(hash, struct sha512_hash, hash); ++ SHA512_Init(&h->state); ++} + +- hold = container_of(hashold, struct sha512_hash, hash); +- hnew = container_of(hashnew, struct sha512_hash, hash); ++static void sha512_copyfrom(ssh_hash *hashnew, ssh_hash *hashold) ++{ ++ struct sha512_hash *hold = container_of(hashold, struct sha512_hash, hash); ++ struct sha512_hash *hnew = container_of(hashnew, struct sha512_hash, hash); + + hnew->state = hold->state; + BinarySink_COPIED(&hnew->state); +- +- return hashnew; + } + + static void sha512_free(ssh_hash *hash) +@@ -335,35 +335,30 @@ static void sha512_free(ssh_hash *hash) + sfree(h); + } + +-static void sha512_final(ssh_hash *hash, unsigned char *output) ++static void sha512_digest(ssh_hash *hash, unsigned char *output) + { + struct sha512_hash *h = container_of(hash, struct sha512_hash, hash); + SHA512_Final(&h->state, output); +- sha512_free(hash); + } + + const ssh_hashalg ssh_sha512 = { +- sha512_new, sha512_copy, sha512_final, sha512_free, ++ sha512_new, sha512_reset, sha512_copyfrom, sha512_digest, sha512_free, + 64, BLKSIZE, HASHALG_NAMES_BARE("SHA-512"), + }; + +-static ssh_hash *sha384_new(const ssh_hashalg *alg) ++static void sha384_reset(ssh_hash *hash) + { +- struct sha512_hash *h = snew(struct sha512_hash); ++ struct sha512_hash *h = container_of(hash, struct sha512_hash, hash); + SHA384_Init(&h->state); +- h->hash.vt = alg; +- BinarySink_DELEGATE_INIT(&h->hash, &h->state); +- return &h->hash; + } + +-static void sha384_final(ssh_hash *hash, unsigned char *output) ++static void sha384_digest(ssh_hash *hash, unsigned char *output) + { + struct sha512_hash *h = container_of(hash, struct sha512_hash, hash); + SHA384_Final(&h->state, output); +- sha512_free(hash); + } + + const ssh_hashalg ssh_sha384 = { +- sha384_new, sha512_copy, sha384_final, sha512_free, ++ sha512_new, sha384_reset, sha512_copyfrom, sha384_digest, sha512_free, + 48, BLKSIZE, HASHALG_NAMES_BARE("SHA-384"), + }; +diff --git a/sshsha.c b/sshsha.c +index 0b8b58f5..dac393aa 100644 +--- a/sshsha.c ++++ b/sshsha.c +@@ -98,7 +98,7 @@ static ssh_hash *sha1_select(const ssh_hashalg *alg) + } + + const ssh_hashalg ssh_sha1 = { +- sha1_select, NULL, NULL, NULL, ++ sha1_select, NULL, NULL, NULL, NULL, + 20, 64, HASHALG_NAMES_ANNOTATED("SHA-1", "dummy selector vtable"), + }; + +@@ -259,26 +259,28 @@ static ssh_hash *sha1_sw_new(const ssh_hashalg *alg) + { + sha1_sw *s = snew(sha1_sw); + +- memcpy(s->core, sha1_initial_state, sizeof(s->core)); +- +- sha1_block_setup(&s->blk); +- + s->hash.vt = alg; + BinarySink_INIT(s, sha1_sw_write); + BinarySink_DELEGATE_INIT(&s->hash, s); + return &s->hash; + } + +-static ssh_hash *sha1_sw_copy(ssh_hash *hash) ++static void sha1_sw_reset(ssh_hash *hash) + { + sha1_sw *s = container_of(hash, sha1_sw, hash); +- sha1_sw *copy = snew(sha1_sw); + +- memcpy(copy, s, sizeof(*copy)); ++ memcpy(s->core, sha1_initial_state, sizeof(s->core)); ++ sha1_block_setup(&s->blk); ++} ++ ++static void sha1_sw_copyfrom(ssh_hash *hcopy, ssh_hash *horig) ++{ ++ sha1_sw *copy = container_of(hcopy, sha1_sw, hash); ++ sha1_sw *orig = container_of(horig, sha1_sw, hash); ++ ++ memcpy(copy, orig, sizeof(*copy)); + BinarySink_COPIED(copy); + BinarySink_DELEGATE_INIT(©->hash, copy); +- +- return ©->hash; + } + + static void sha1_sw_free(ssh_hash *hash) +@@ -298,18 +300,17 @@ static void sha1_sw_write(BinarySink *bs, const void *vp, size_t len) + sha1_sw_block(s->core, s->blk.block); + } + +-static void sha1_sw_final(ssh_hash *hash, uint8_t *digest) ++static void sha1_sw_digest(ssh_hash *hash, uint8_t *digest) + { + sha1_sw *s = container_of(hash, sha1_sw, hash); + + sha1_block_pad(&s->blk, BinarySink_UPCAST(s)); + for (size_t i = 0; i < 5; i++) + PUT_32BIT_MSB_FIRST(digest + 4*i, s->core[i]); +- sha1_sw_free(hash); + } + + const ssh_hashalg ssh_sha1_sw = { +- sha1_sw_new, sha1_sw_copy, sha1_sw_final, sha1_sw_free, ++ sha1_sw_new, sha1_sw_reset, sha1_sw_copyfrom, sha1_sw_digest, sha1_sw_free, + 20, 64, HASHALG_NAMES_ANNOTATED("SHA-1", "unaccelerated"), + }; + +@@ -573,39 +574,42 @@ static sha1_ni *sha1_ni_alloc(void) + return s; + } + +-FUNC_ISA static ssh_hash *sha1_ni_new(const ssh_hashalg *alg) ++static ssh_hash *sha1_ni_new(const ssh_hashalg *alg) + { + if (!sha1_hw_available_cached()) + return NULL; + + sha1_ni *s = sha1_ni_alloc(); + ++ s->hash.vt = alg; ++ BinarySink_INIT(s, sha1_ni_write); ++ BinarySink_DELEGATE_INIT(&s->hash, s); ++ return &s->hash; ++} ++ ++FUNC_ISA static void sha1_ni_reset(ssh_hash *hash) ++{ ++ sha1_ni *s = container_of(hash, sha1_ni, hash); ++ + /* Initialise the core vectors in their storage order */ + s->core[0] = _mm_set_epi64x( + 0x67452301efcdab89ULL, 0x98badcfe10325476ULL); + s->core[1] = _mm_set_epi32(0xc3d2e1f0, 0, 0, 0); + + sha1_block_setup(&s->blk); +- +- s->hash.vt = alg; +- BinarySink_INIT(s, sha1_ni_write); +- BinarySink_DELEGATE_INIT(&s->hash, s); +- return &s->hash; + } + +-static ssh_hash *sha1_ni_copy(ssh_hash *hash) ++static void sha1_ni_copyfrom(ssh_hash *hcopy, ssh_hash *horig) + { +- sha1_ni *s = container_of(hash, sha1_ni, hash); +- sha1_ni *copy = sha1_ni_alloc(); ++ sha1_ni *copy = container_of(hcopy, sha1_ni, hash); ++ sha1_ni *orig = container_of(horig, sha1_ni, hash); + + void *ptf_save = copy->pointer_to_free; +- *copy = *s; /* structure copy */ ++ *copy = *orig; /* structure copy */ + copy->pointer_to_free = ptf_save; + + BinarySink_COPIED(copy); + BinarySink_DELEGATE_INIT(©->hash, copy); +- +- return ©->hash; + } + + static void sha1_ni_free(ssh_hash *hash) +@@ -626,7 +630,7 @@ static void sha1_ni_write(BinarySink *bs, const void *vp, size_t len) + sha1_ni_block(s->core, s->blk.block); + } + +-FUNC_ISA static void sha1_ni_final(ssh_hash *hash, uint8_t *digest) ++FUNC_ISA static void sha1_ni_digest(ssh_hash *hash, uint8_t *digest) + { + sha1_ni *s = container_of(hash, sha1_ni, hash); + +@@ -645,12 +649,10 @@ FUNC_ISA static void sha1_ni_final(ssh_hash *hash, uint8_t *digest) + /* Finally, store the leftover word */ + uint32_t e = _mm_extract_epi32(s->core[1], 3); + PUT_32BIT_MSB_FIRST(digest + 16, e); +- +- sha1_ni_free(hash); + } + + const ssh_hashalg ssh_sha1_hw = { +- sha1_ni_new, sha1_ni_copy, sha1_ni_final, sha1_ni_free, ++ sha1_ni_new, sha1_ni_reset, sha1_ni_copyfrom, sha1_ni_digest, sha1_ni_free, + 20, 64, HASHALG_NAMES_ANNOTATED("SHA-1", "SHA-NI accelerated"), + }; + +@@ -813,28 +815,31 @@ static ssh_hash *sha1_neon_new(const ssh_hashalg *alg) + + sha1_neon *s = snew(sha1_neon); + +- s->core.abcd = vld1q_u32(sha1_initial_state); +- s->core.e = sha1_initial_state[4]; +- +- sha1_block_setup(&s->blk); +- + s->hash.vt = alg; + BinarySink_INIT(s, sha1_neon_write); + BinarySink_DELEGATE_INIT(&s->hash, s); + return &s->hash; + } + +-static ssh_hash *sha1_neon_copy(ssh_hash *hash) ++static void sha1_neon_reset(ssh_hash *hash) + { + sha1_neon *s = container_of(hash, sha1_neon, hash); +- sha1_neon *copy = snew(sha1_neon); + +- *copy = *s; /* structure copy */ ++ s->core.abcd = vld1q_u32(sha1_initial_state); ++ s->core.e = sha1_initial_state[4]; ++ ++ sha1_block_setup(&s->blk); ++} ++ ++static void sha1_neon_copyfrom(ssh_hash *hcopy, ssh_hash *horig) ++{ ++ sha1_neon *copy = container_of(hcopy, sha1_neon, hash); ++ sha1_neon *orig = container_of(horig, sha1_neon, hash); ++ ++ *copy = *orig; /* structure copy */ + + BinarySink_COPIED(copy); + BinarySink_DELEGATE_INIT(©->hash, copy); +- +- return ©->hash; + } + + static void sha1_neon_free(ssh_hash *hash) +@@ -853,18 +858,18 @@ static void sha1_neon_write(BinarySink *bs, const void *vp, size_t len) + sha1_neon_block(&s->core, s->blk.block); + } + +-static void sha1_neon_final(ssh_hash *hash, uint8_t *digest) ++static void sha1_neon_digest(ssh_hash *hash, uint8_t *digest) + { + sha1_neon *s = container_of(hash, sha1_neon, hash); + + sha1_block_pad(&s->blk, BinarySink_UPCAST(s)); + vst1q_u8(digest, vrev32q_u8(vreinterpretq_u8_u32(s->core.abcd))); + PUT_32BIT_MSB_FIRST(digest + 16, s->core.e); +- sha1_neon_free(hash); + } + + const ssh_hashalg ssh_sha1_hw = { +- sha1_neon_new, sha1_neon_copy, sha1_neon_final, sha1_neon_free, ++ sha1_neon_new, sha1_neon_reset, sha1_neon_copyfrom, sha1_neon_digest, ++ sha1_neon_free, + 20, 64, HASHALG_NAMES_ANNOTATED("SHA-1", "NEON accelerated"), + }; + +@@ -890,12 +895,14 @@ static ssh_hash *sha1_stub_new(const ssh_hashalg *alg) + + #define STUB_BODY { unreachable("Should never be called"); } + +-static ssh_hash *sha1_stub_copy(ssh_hash *hash) STUB_BODY ++static void sha1_stub_reset(ssh_hash *hash) STUB_BODY ++static void sha1_stub_copyfrom(ssh_hash *hash, ssh_hash *orig) STUB_BODY + static void sha1_stub_free(ssh_hash *hash) STUB_BODY +-static void sha1_stub_final(ssh_hash *hash, uint8_t *digest) STUB_BODY ++static void sha1_stub_digest(ssh_hash *hash, uint8_t *digest) STUB_BODY + + const ssh_hashalg ssh_sha1_hw = { +- sha1_stub_new, sha1_stub_copy, sha1_stub_final, sha1_stub_free, ++ sha1_stub_new, sha1_stub_reset, sha1_stub_copyfrom, sha1_stub_digest, ++ sha1_stub_free, + 20, 64, HASHALG_NAMES_ANNOTATED( + "SHA-1", "!NONEXISTENT ACCELERATED VERSION!"), + }; diff -Nru putty-0.74/debian/patches/0007-Add-an-extra-HMAC-constructor-function.patch putty-0.74/debian/patches/0007-Add-an-extra-HMAC-constructor-function.patch --- putty-0.74/debian/patches/0007-Add-an-extra-HMAC-constructor-function.patch 1970-01-01 00:00:00.000000000 +0000 +++ putty-0.74/debian/patches/0007-Add-an-extra-HMAC-constructor-function.patch 2024-07-16 10:13:59.000000000 +0000 @@ -0,0 +1,121 @@ +From dd4a85ecaadb2cfa038754efd3862072eb62b20f Mon Sep 17 00:00:00 2001 +From: Simon Tatham <ana...@pobox.com> +Date: Mon, 29 Apr 2024 18:50:46 +0000 +Subject: Add an extra HMAC constructor function. + +This takes a plain ssh_hashalg, and constructs the most natural kind +of HMAC wrapper around it, taking its key length and output length +to be the hash's output length. In other words, it converts SHA-foo +into exactly the thing usually called HMAC-SHA-foo. + +It does it by constructing a new ssh2_macalg vtable, and including it +in the same memory allocation as the actual hash object. That's the +first time in PuTTY I've done it this way. + +Nothing yet uses this, but a new piece of code is about to. + +[backport] +- Drop nullmac_next_message uses that is not used in this bullseye version + +origin: backport, https://git.tartarus.org/?p=simon/putty.git;a=commitdiff_plain;h=dea3ddca0537299ebfe907dd4c883fe65bfb4035 +--- + ssh.h | 5 +++++ + sshhmac.c | 48 +++++++++++++++++++++++++++++++++++++++++++++--- + 2 files changed, 50 insertions(+), 3 deletions(-) + +diff --git a/ssh.h b/ssh.h +index 2f8df928..aa375ab7 100644 +--- a/ssh.h ++++ b/ssh.h +@@ -710,6 +710,11 @@ bool ssh2_mac_verify(ssh2_mac *, const void *, int, unsigned long seq); + * 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; +diff --git a/sshhmac.c b/sshhmac.c +index 2f870f36..ff536b73 100644 +--- a/sshhmac.c ++++ b/sshhmac.c +@@ -19,9 +19,10 @@ struct hmac_extra { + const char *suffix, *annotation; + }; + +-static ssh2_mac *hmac_new(const ssh2_macalg *alg, ssh_cipher *cipher) +-{ +- struct hmac *ctx = snew(struct hmac); ++/* 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) ++ { + const struct hmac_extra *extra = (const struct hmac_extra *)alg->extra; + + ctx->h_outer = ssh_hash_new(extra->hashalg_base); +@@ -66,6 +67,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); +@@ -81,6 +87,8 @@ static void hmac_free(ssh2_mac *mac) + sfree(ctx); + } + ++ ++ + #define PAD_OUTER 0x5C + #define PAD_INNER 0x36 + +@@ -187,6 +195,40 @@ static const char *hmac_text_name(ssh2_mac *mac) + return ctx->text_name->s; + } + ++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.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); ++} ++ + const struct hmac_extra ssh_hmac_sha256_extra = { &ssh_sha256, "" }; + const ssh2_macalg ssh_hmac_sha256 = { + hmac_new, hmac_free, hmac_key, diff -Nru putty-0.74/debian/patches/0008-Switch-to-RFC-6979-for-DSA-nonce-generation.patch putty-0.74/debian/patches/0008-Switch-to-RFC-6979-for-DSA-nonce-generation.patch --- putty-0.74/debian/patches/0008-Switch-to-RFC-6979-for-DSA-nonce-generation.patch 1970-01-01 00:00:00.000000000 +0000 +++ putty-0.74/debian/patches/0008-Switch-to-RFC-6979-for-DSA-nonce-generation.patch 2024-07-16 10:13:59.000000000 +0000 @@ -0,0 +1,1206 @@ +From 3b973f00dd0076ae305a0b5e7ddab9b811a833dd Mon Sep 17 00:00:00 2001 +From: Simon Tatham <ana...@pobox.com> +Date: Mon, 29 Apr 2024 15:46:17 +0000 +Subject: Switch to RFC 6979 for DSA nonce generation. + +Switch to RFC 6979 for DSA nonce generation. + +This fixes a vulnerability that compromises NIST P521 ECDSA keys when +they are used with PuTTY's existing DSA nonce generation code. The +vulnerability has been assigned the identifier CVE-2024-31497. + +PuTTY has been doing its DSA signing deterministically for literally +as long as it's been doing it at all, because I didn't trust Windows's +entropy generation. Deterministic nonce generation was introduced in +commit d345ebc2a5a0b59, as part of the initial version of our DSA +signing routine. At the time, there was no standard for how to do it, +so we had to think up the details of our system ourselves, with some +help from the Cambridge University computer security group. + +More than ten years later, RFC 6979 was published, recommending a +similar system for general use, naturally with all the details +different. We didn't switch over to doing it that way, because we had +a scheme in place already, and as far as I could see, the differences +were not security-critical - just the normal sort of variation you +expect when any two people design a protocol component of this kind +independently. + +As far as I know, the _structure_ of our scheme is still perfectly +fine, in terms of what data gets hashed, how many times, and how the +hash output is converted into a nonce. But the weak spot is the choice +of hash function: inside our dsa_gen_k() function, we generate 512 +bits of random data using SHA-512, and then reduce that to the output +range by modular reduction, regardless of what signature algorithm +we're generating a nonce for. + +In the original use case, this introduced a theoretical bias (the +output size is an odd prime, which doesn't evenly divide the space of +2^512 possible inputs to the reduction), but the theory was that since +integer DSA uses a modulus prime only 160 bits long (being based on +SHA-1, at least in the form that SSH uses it), the bias would be too +small to be detectable, let alone exploitable. + +Then we reused the same function for NIST-style ECDSA, when it +arrived. This is fine for the P256 curve, and even P384. But in P521, +the order of the base point is _greater_ than 2^512, so when we +generate a 512-bit number and reduce it, the reduction never makes any +difference, and our output nonces are all in the first 2^512 elements +of the range of about 2^521. So this _does_ introduce a significant +bias in the nonces, compared to the ideal of uniformly random +distribution over the whole range. And it's been recently discovered +that a bias of this kind is sufficient to expose private keys, given a +manageably small number of signatures to work from. + +(Incidentally, none of this affects Ed25519. The spec for that system +includes its own idea of how you should do deterministic nonce +generation - completely different again, naturally - and we did it +that way rather than our way, so that we could use the existing test +vectors.) + +The simplest fix would be to patch our existing nonce generator to use +a longer hash, or concatenate a couple of SHA-512 hashes, or something +similar. But I think a more robust approach is to switch it out +completely for what is now the standard system. The main reason why I +prefer that is that the standard system comes with test vectors, which +adds a lot of confidence that I haven't made some other mistake in +following my own design. + +So here's a commit that adds an implementation of RFC 6979, and +removes the old dsa_gen_k() function. Tests are added based on the +RFC's appendix of test vectors (as many as are compatible with the +more limited API of PuTTY's crypto code, e.g. we lack support for the +NIST P192 curve, or for doing integer DSA with many different hash +functions). One existing test changes its expected outputs, namely the +one that has a sample key pair and signature for every key algorithm +we support. + +origin: backport, https://git.tartarus.org/?p=simon/putty.git;a=commitdiff_plain;h=c193fe9848f50a88a4089aac647fecc31ae96d27 +bug: https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/vuln-p521-bias.html +bug-debian-security: https://security-tracker.debian.org/tracker/CVE-2024-31497 +--- + Makefile.am | 24 +-- + Recipe | 9 +- + defs.h | 2 + + rfc6979.c | 359 +++++++++++++++++++++++++++++++++++++++++++++ + ssh.h | 16 +- + sshdss.c | 116 +-------------- + sshecc.c | 14 +- + test/cryptsuite.py | 253 +++++++++++++++++++++++++++++++- + testcrypt.h | 5 + + testsc.c | 60 ++++++++ + 10 files changed, 713 insertions(+), 145 deletions(-) + create mode 100644 rfc6979.c + +diff --git a/Makefile.am b/Makefile.am +index 3360c6b2..4ff7f76e 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -70,7 +70,7 @@ allsources = agentf.c aqsync.c be_all_s.c be_misc.c be_none.c be_nos_s.c \ + windows/winsecur.c windows/winsecur.h windows/winser.c \ + windows/winsftp.c windows/winshare.c windows/winstore.c \ + windows/winstuff.h windows/wintime.c windows/winucs.c \ +- windows/winutils.c windows/winx11.c x11fwd.c ++ windows/winutils.c windows/winx11.c x11fwd.c rfc6979.c + + if HAVE_GTK + bin_PROGRAMS = plink pscp psftp puttygen pageant pterm putty puttytel +@@ -104,7 +104,7 @@ cgtest_SOURCES = cgtest.c conf.c ecc.c import.c marshal.c memory.c misc.c \ + sshrsa.c sshrsag.c sshsh256.c sshsh512.c sshsha.c \ + stripctrl.c time.c tree234.c unix/uxcons.c unix/uxgen.c \ + unix/uxmisc.c unix/uxnogtk.c unix/uxnoise.c unix/uxpoll.c \ +- unix/uxstore.c unix/uxutils.c utils.c wcwidth.c ++ unix/uxstore.c unix/uxutils.c utils.c wcwidth.c rfc6979.c + cgtest_LDADD = libversion.a + + fuzzterm_SOURCES = be_none.c callback.c charset/fromucs.c charset/localenc.c \ +@@ -131,7 +131,7 @@ pageant_SOURCES = aqsync.c be_misc.c be_none.c callback.c conf.c ecc.c \ + unix/uxmisc.c unix/uxnet.c unix/uxnoise.c unix/uxpeer.c \ + unix/uxpgnt.c unix/uxpoll.c unix/uxproxy.c unix/uxsel.c \ + unix/uxsignal.c unix/uxstore.c unix/uxutils.c utils.c \ +- wcwidth.c x11fwd.c ++ wcwidth.c x11fwd.c rfc6979.c + pageant_LDADD = libversion.a $(GTK_LIBS) + endif + +@@ -154,7 +154,7 @@ plink_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c cmdline.c \ + unix/uxnet.c unix/uxnogtk.c unix/uxnoise.c unix/uxpeer.c \ + unix/uxplink.c unix/uxpoll.c unix/uxproxy.c unix/uxsel.c \ + unix/uxser.c unix/uxshare.c unix/uxsignal.c unix/uxstore.c \ +- unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c ++ unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c rfc6979.c + plink_LDADD = libversion.a + + pscp_SOURCES = agentf.c aqsync.c be_misc.c be_ssh.c callback.c cmdline.c \ +@@ -175,7 +175,7 @@ pscp_SOURCES = agentf.c aqsync.c be_misc.c be_ssh.c callback.c cmdline.c \ + unix/uxgss.c unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c \ + unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c unix/uxproxy.c \ + unix/uxsel.c unix/uxsftp.c unix/uxshare.c unix/uxstore.c \ +- unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c ++ unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c rfc6979.c + pscp_LDADD = libversion.a + + psftp_SOURCES = agentf.c aqsync.c be_misc.c be_ssh.c callback.c cmdline.c \ +@@ -196,7 +196,7 @@ psftp_SOURCES = agentf.c aqsync.c be_misc.c be_ssh.c callback.c cmdline.c \ + unix/uxgss.c unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c \ + unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c unix/uxproxy.c \ + unix/uxsel.c unix/uxsftp.c unix/uxshare.c unix/uxstore.c \ +- unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c ++ unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c rfc6979.c + psftp_LDADD = libversion.a + + if HAVE_GTK +@@ -261,7 +261,7 @@ putty_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c \ + unix/uxser.c unix/uxshare.c unix/uxsignal.c unix/uxstore.c \ + unix/uxucs.c unix/uxutils.c unix/x11misc.c unix/xkeysym.c \ + unix/xpmpucfg.c unix/xpmputty.c utils.c wcwidth.c wildcard.c \ +- x11fwd.c ++ x11fwd.c rfc6979.c + putty_LDADD = libversion.a $(GTK_LIBS) + endif + +@@ -293,7 +293,7 @@ puttyapp_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c \ + unix/uxser.c unix/uxshare.c unix/uxsignal.c unix/uxstore.c \ + unix/uxucs.c unix/uxutils.c unix/x11misc.c unix/xkeysym.c \ + unix/xpmpucfg.c unix/xpmputty.c utils.c wcwidth.c wildcard.c \ +- x11fwd.c ++ x11fwd.c rfc6979.c + puttyapp_LDADD = libversion.a $(GTK_LIBS) + endif + +@@ -304,7 +304,7 @@ puttygen_SOURCES = cmdgen.c conf.c ecc.c import.c marshal.c memory.c misc.c \ + sshrsa.c sshrsag.c sshsh256.c sshsh512.c sshsha.c \ + stripctrl.c time.c tree234.c unix/uxcons.c unix/uxgen.c \ + unix/uxmisc.c unix/uxnogtk.c unix/uxnoise.c unix/uxpoll.c \ +- unix/uxstore.c unix/uxutils.c utils.c wcwidth.c ++ unix/uxstore.c unix/uxutils.c utils.c wcwidth.c rfc6979.c + puttygen_LDADD = libversion.a + + if HAVE_GTK +@@ -331,13 +331,13 @@ testcrypt_SOURCES = ecc.c marshal.c memory.c mpint.c sshaes.c ssharcf.c \ + sshauxcrypt.c sshblowf.c sshccp.c sshcrc.c sshcrcda.c \ + sshdes.c sshdh.c sshdss.c sshecc.c sshhmac.c sshmd5.c \ + sshprime.c sshprng.c sshrsa.c sshsh256.c sshsh512.c sshsha.c \ +- testcrypt.c tree234.c unix/uxutils.c utils.c ++ testcrypt.c tree234.c unix/uxutils.c utils.c rfc6979.c + + testsc_SOURCES = ecc.c marshal.c memory.c mpint.c sshaes.c ssharcf.c \ + sshauxcrypt.c sshblowf.c sshccp.c sshcrc.c sshcrcda.c \ + sshdes.c sshdh.c sshdss.c sshecc.c sshhmac.c sshmac.c \ + sshmd5.c sshrsa.c sshsh256.c sshsh512.c sshsha.c testsc.c \ +- tree234.c unix/uxutils.c utils.c wildcard.c ++ tree234.c unix/uxutils.c utils.c wildcard.c rfc6979.c + + testzlib_SOURCES = marshal.c memory.c sshzlib.c testzlib.c utils.c + +@@ -360,7 +360,7 @@ uppity_SOURCES = be_misc.c be_none.c callback.c conf.c cproxy.c ecc.c \ + unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c unix/uxproxy.c \ + unix/uxpty.c unix/uxsel.c unix/uxserver.c \ + unix/uxsftpserver.c unix/uxsignal.c unix/uxstore.c \ +- unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c ++ unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c rfc6979.c + uppity_LDADD = libversion.a + + if AUTO_GIT_COMMIT +diff --git a/Recipe b/Recipe +index 4bad3f33..15d656b3 100644 +--- a/Recipe ++++ b/Recipe +@@ -262,6 +262,7 @@ SSHCRYPTO = ARITH sshmd5 sshsha sshsh256 sshsh512 + + sshdes sshblowf sshaes sshccp ssharcf + + sshdh sshcrc sshcrcda sshauxcrypt + + sshhmac ++ + rfc6979 + SSHCOMMON = sshcommon sshprng sshrand SSHCRYPTO + + sshverstring + + sshpubk sshzlib +@@ -338,7 +339,7 @@ psftp : [C] psftp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC + + psftp.res winnojmp winnohlp LIBS + + pageant : [G] winpgnt pageant sshrsa sshpubk sshdes ARITH sshmd5 version +- + tree234 MISC sshaes sshsha winsecur winpgntc aqsync sshdss sshsh256 ++ + tree234 MISC sshaes sshsha winsecur winpgntc aqsync sshdss sshsh256 rfc6979 + + sshsh512 winutils sshecc winmisc winmiscs winhelp conf pageant.res + + sshauxcrypt sshhmac LIBS + +@@ -346,7 +347,7 @@ puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes ARITH sshmd5 version + + sshrand winnoise sshsha winstore MISC winctrls sshrsa sshdss winmisc + + sshpubk sshaes sshsh256 sshsh512 IMPORT winutils puttygen.res + + tree234 notiming winhelp winnojmp CONF LIBS wintime sshecc sshprng +- + sshecdsag sshauxcrypt sshhmac winsecur winmiscs ++ + sshecdsag sshauxcrypt sshhmac winsecur winmiscs rfc6979 + + pterm : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore + + uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg +@@ -366,6 +367,7 @@ PUTTYGEN_UNIX = sshrsag sshdssg sshprime sshdes ARITH sshmd5 version sshprng + + sshpubk sshaes sshsh256 sshsh512 IMPORT puttygen.res time tree234 + + uxgen notiming CONF sshecc sshecdsag uxnogtk sshauxcrypt sshhmac + + uxpoll uxutils ++ + rfc6979 + puttygen : [U] cmdgen PUTTYGEN_UNIX + cgtest : [UT] cgtest PUTTYGEN_UNIX + +@@ -377,6 +379,7 @@ pageant : [X] uxpgnt uxagentc aqsync pageant sshrsa sshpubk sshdes ARITH + + sshecc CONF uxsignal nocproxy nogss be_none x11fwd ux_x11 uxcons + + gtkask gtkmisc nullplug logging UXMISC uxagentsock utils memory + + sshauxcrypt sshhmac sshprng uxnoise ++ + rfc6979 + + ptermapp : [XT] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore + + uxsignal CHARSET uxpterm version time xpmpterm xpmptcfg +@@ -390,10 +393,12 @@ fuzzterm : [UT] UXTERM CHARSET MISC version uxmisc uxucs fuzzterm time settings + + uxstore be_none uxnogtk memory + testcrypt : [UT] testcrypt SSHCRYPTO sshprng sshprime marshal utils + + memory tree234 uxutils ++ + rfc6979 + testcrypt : [C] testcrypt SSHCRYPTO sshprng sshprime marshal utils + + memory tree234 winmiscs + testsc : [UT] testsc SSHCRYPTO marshal utils memory tree234 wildcard + + sshmac uxutils ++ + rfc6979 + testzlib : [UT] testzlib sshzlib utils marshal memory + + uppity : [UT] uxserver SSHSERVER UXMISC uxsignal uxnoise uxgss uxnogtk +diff --git a/defs.h b/defs.h +index b9577036..4a4bf72e 100644 +--- a/defs.h ++++ b/defs.h +@@ -141,6 +141,8 @@ typedef struct ssh_cipher ssh_cipher; + typedef struct ssh2_ciphers ssh2_ciphers; + typedef struct dh_ctx dh_ctx; + typedef struct ecdh_key ecdh_key; ++typedef struct RFC6979 RFC6979; ++typedef struct RFC6979Result RFC6979Result; + + typedef struct dlgparam dlgparam; + +diff --git a/rfc6979.c b/rfc6979.c +new file mode 100644 +index 00000000..73e5c924 +--- /dev/null ++++ b/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 <hipster>PuTTY was doing it ++ * before it was cool</hipster>) 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/ssh.h b/ssh.h +index aa375ab7..4c1cad41 100644 +--- a/ssh.h ++++ b/ssh.h +@@ -589,11 +589,18 @@ void ssh_ecdhkex_getpublic(ecdh_key *key, BinarySink *bs); + mp_int *ssh_ecdhkex_getkey(ecdh_key *key, ptrlen remoteKey); + + /* +- * Helper function for k generation in DSA, reused in ECDSA ++ * System for generating k in DSA and ECDSA. + */ +-mp_int *dss_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; +@@ -971,6 +978,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 ssh2_poly1305; + extern const ssh_compression_alg ssh_zlib; + +diff --git a/sshdss.c b/sshdss.c +index 90dc075a..059d2b6d 100644 +--- a/sshdss.c ++++ b/sshdss.c +@@ -317,117 +317,6 @@ static int dss_pubkey_bits(const ssh_keyalg *self, ptrlen pub) + return ret; + } + +-mp_int *dss_gen_k(const char *id_string, mp_int *modulus, +- mp_int *private_key, +- unsigned char *digest, int digest_len) +-{ +- /* +- * The basic DSS 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_final(h, digest512); +- +- /* +- * Now hash that digest plus the message hash. +- */ +- h = ssh_hash_new(&ssh_sha512); +- 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 dss_sign(ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs) + { + struct dss_key *dss = container_of(key, struct dss_key, sshk); +@@ -436,8 +325,9 @@ static void dss_sign(ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs) + + hash_simple(&ssh_sha1, data, digest); + +- mp_int *k = dss_gen_k("DSA deterministic k generator", dss->q, dss->x, +- digest, sizeof(digest)); ++ /* Generate any valid exponent k, using the RFC 6979 deterministic ++ * procedure. */ ++ mp_int *k = rfc6979(&ssh_sha1, dss->q, dss->x, data); + mp_int *kinv = mp_invert(k, dss->q); /* k^-1 mod q */ + + /* +diff --git a/sshecc.c b/sshecc.c +index 3005e288..eec4ec21 100644 +--- a/sshecc.c ++++ b/sshecc.c +@@ -997,16 +997,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 = dss_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/test/cryptsuite.py b/test/cryptsuite.py +index 5564c11f..5e40f048 100755 +--- a/test/cryptsuite.py ++++ b/test/cryptsuite.py +@@ -86,6 +86,13 @@ 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)]) ++ ++def be_integer(x, nbits): ++ return bytes(reversed(le_integer(x, nbits))) ++ + @contextlib.contextmanager + def queued_random_data(nbytes, seed): + hashsize = 512 // 8 +@@ -1324,6 +1331,244 @@ culpa qui officia deserunt mollit anim id est laborum. + 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 testRSAVerify(self): + def blobs(n, e, d, p, q, iqmp): + pubblob = ssh_string(b"ssh-rsa") + ssh2_mpint(e) + ssh2_mpint(n) +@@ -1393,10 +1638,10 @@ culpa qui officia deserunt mollit anim id est laborum. + + test_keys = [ + ('ed25519', 'AAAAC3NzaC1lZDI1NTE5AAAAIM7jupzef6CD0ps2JYxJp9IlwY49oorOseV5z5JFDFKn', 'AAAAIAf4/WRtypofgdNF2vbZOUFE1h4hvjw4tkGJZyOzI7c3', 255, b'0xf4d6e7f6f4479c23f0764ef43cea1711dbfe02aa2b5a32ff925c7c1fbf0f0db,0x27520c4592cf79e5b1ce8aa23d8ec125d2a7498c25369bd283a07fde9cbae3ce', [(0, 'AAAAC3NzaC1lZDI1NTE5AAAAQN73EqfyA4WneqDhgZ98TlRj9V5Wg8zCrMxTLJN1UtyfAnPUJDtfG/U0vOsP8PrnQxd41DDDnxrAXuqJz8rOagc=')]), +- ('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/testcrypt.h b/testcrypt.h +index 690bfa2b..2cd6ddfb 100644 +--- a/testcrypt.h ++++ b/testcrypt.h +@@ -157,6 +157,11 @@ FUNC2(void, ssh_key_openssh_blob, val_key, out_val_string_binarysink) + FUNC1(val_string_asciz, ssh_key_cache_str, val_key) + FUNC2(uint, ssh_key_public_bits, keyalg, val_string_ptrlen) + ++/* ++ * DSA nonce generation. ++ */ ++FUNC4(opt_val_mpint, rfc6979, hashalg, val_mpint, val_mpint, val_string_ptrlen) ++ + /* + * The ssh_cipher abstraction. The in-place encrypt and decrypt + * functions are wrapped to replace them with versions that take one +diff --git a/testsc.c b/testsc.c +index 7127032e..04bd430a 100644 +--- a/testsc.c ++++ b/testsc.c +@@ -312,6 +312,8 @@ VOLATILE_WRAPPED_DEFN(static, size_t, looplimit, (size_t x)) + X(ecc_edwards_eq) \ + X(ecc_edwards_get_affine) \ + X(ecc_edwards_decompress) \ ++ X(rfc6979_setup) \ ++ X(rfc6979_attempt) \ + CIPHERS(CIPHER_TESTLIST, X) \ + MACS(MAC_TESTLIST, X) \ + HASHES(HASH_TESTLIST, X) \ +@@ -1377,6 +1379,64 @@ static void test_hash(const ssh_hashalg *halg) + ssh_hash_free(h); + } + ++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); ++} ++ ++ + #define HASH_TESTFN(Y_unused, hash) \ + static void test_hash_##hash(void) { test_hash(&hash); } + HASHES(HASH_TESTFN, Y_unused) diff -Nru putty-0.74/debian/patches/series putty-0.74/debian/patches/series --- putty-0.74/debian/patches/series 2023-12-21 16:54:36.000000000 +0000 +++ putty-0.74/debian/patches/series 2024-07-16 10:13:59.000000000 +0000 @@ -3,3 +3,6 @@ PTRLEN_DECL_LITERAL.patch add_to_commasep_pl.patch strict-kex.patch +0006-Refactor-the-ssh_hash-vtable.-NFC.patch +0007-Add-an-extra-HMAC-constructor-function.patch +0008-Switch-to-RFC-6979-for-DSA-nonce-generation.patch diff -Nru putty-0.74/debian/putty.NEWS putty-0.74/debian/putty.NEWS --- putty-0.74/debian/putty.NEWS 1970-01-01 00:00:00.000000000 +0000 +++ putty-0.74/debian/putty.NEWS 2024-07-16 09:42:13.000000000 +0000 @@ -0,0 +1,29 @@ +putty (0.74-1+deb11u2) bullseye; urgency=high + + Previous putty versions were affected by CVE-2024-31497, + a critical vulnerability that generates signatures + from ECDSA private keys that use the NIST P521 curve. + The effect of the vulnerability is to compromise the private key. + + An attacker in possession of a few dozen signed messages and the public + key has enough information to deduce the private key, and then forge + signatures as if they were made by the victim. This allows the attacker + to (for instance) log in to any servers the victim uses that key for. + To obtain these signatures, an attacker need only briefly compromise + any server the victim uses the key to authenticate to. + + Therefore, if you have any NIST-P521 ECDSA key, we strongly recommend + you to replace it with a freshly new created with a fixed version of + putty. Then, to revoke the old public key and remove it from any + machine where you use it to login into, so that a signature + from the compromised key has no value any more. + + The only affected key type is 521-bit ECDSA. That is, a key that appears + in Windows PuTTYgen with ecdsa-sha2-nistp521 at the start of the + 'Key fingerprint' box, or is described as 'NIST p521', or has an id + starting ecdsa-sha2-nistp521 in the SSH protocol or the key file. + Other sizes of ECDSA, and other key algorithms, are unaffected. + In particular, Ed25519 is not affected. + + -- Bastien Roucari??s <ro...@debian.org> Mon, 29 Apr 2024 16:55:15 +0000 + diff -Nru putty-0.74/debian/putty-tools.NEWS putty-0.74/debian/putty-tools.NEWS --- putty-0.74/debian/putty-tools.NEWS 1970-01-01 00:00:00.000000000 +0000 +++ putty-0.74/debian/putty-tools.NEWS 2024-07-16 09:42:13.000000000 +0000 @@ -0,0 +1,29 @@ +putty (0.74-1+deb11u2) bullseye; urgency=high + + Previous putty versions were affected by CVE-2024-31497, + a critical vulnerability that generates signatures + from ECDSA private keys that use the NIST P521 curve. + The effect of the vulnerability is to compromise the private key. + + An attacker in possession of a few dozen signed messages and the public + key has enough information to deduce the private key, and then forge + signatures as if they were made by the victim. This allows the attacker + to (for instance) log in to any servers the victim uses that key for. + To obtain these signatures, an attacker need only briefly compromise + any server the victim uses the key to authenticate to. + + Therefore, if you have any NIST-P521 ECDSA key, we strongly recommend + you to replace it with a freshly new created with a fixed version of + putty. Then, to revoke the old public key and remove it from any + machine where you use it to login into, so that a signature + from the compromised key has no value any more. + + The only affected key type is 521-bit ECDSA. That is, a key that appears + in Windows PuTTYgen with ecdsa-sha2-nistp521 at the start of the + 'Key fingerprint' box, or is described as 'NIST p521', or has an id + starting ecdsa-sha2-nistp521 in the SSH protocol or the key file. + Other sizes of ECDSA, and other key algorithms, are unaffected. + In particular, Ed25519 is not affected. + + -- Bastien Roucari??s <ro...@debian.org> Mon, 29 Apr 2024 16:55:15 +0000 +
signature.asc
Description: This is a digitally signed message part.