1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00
putty-source/sshsha.c
Simon Tatham 183a9ee98b Support OpenSSH encrypt-then-MAC protocol extension.
This causes the initial length field of the SSH-2 binary packet to be
unencrypted (with the knock-on effect that now the packet length not
including MAC must be congruent to 4 rather than 0 mod the cipher
block size), and then the MAC is applied over the unencrypted length
field and encrypted ciphertext (prefixed by the sequence number as
usual). At the cost of exposing some information about the packet
lengths to an attacker (but rarely anything they couldn't have
inferred from the TCP headers anyway), this closes down any
possibility of a MITM using the client as a decryption oracle, unless
they can _first_ fake a correct MAC.

ETM mode is enabled by means of selecting a different MAC identifier,
all the current ones of which are constructed by appending
"-etm@openssh.com" to the name of a MAC that already existed.

We currently prefer the original SSH-2 binary packet protocol (i.e. we
list all the ETM-mode MACs last in our KEXINIT), on the grounds that
it's better tested and more analysed, so at the moment the new mode is
only activated if a server refuses to speak anything else.
2015-04-26 23:30:32 +01:00

436 lines
10 KiB
C

/*
* SHA1 hash algorithm. Used in SSH-2 as a MAC, and the transform is
* also used as a `stirring' function for the PuTTY random number
* pool. Implemented directly from the specification by Simon
* Tatham.
*/
#include "ssh.h"
/* ----------------------------------------------------------------------
* Core SHA algorithm: processes 16-word blocks into a message digest.
*/
#define rol(x,y) ( ((x) << (y)) | (((uint32)x) >> (32-y)) )
static void SHA_Core_Init(uint32 h[5])
{
h[0] = 0x67452301;
h[1] = 0xefcdab89;
h[2] = 0x98badcfe;
h[3] = 0x10325476;
h[4] = 0xc3d2e1f0;
}
void SHATransform(word32 * digest, word32 * block)
{
word32 w[80];
word32 a, b, c, d, e;
int t;
#ifdef RANDOM_DIAGNOSTICS
{
extern int random_diagnostics;
if (random_diagnostics) {
int i;
printf("SHATransform:");
for (i = 0; i < 5; i++)
printf(" %08x", digest[i]);
printf(" +");
for (i = 0; i < 16; i++)
printf(" %08x", block[i]);
}
}
#endif
for (t = 0; t < 16; t++)
w[t] = block[t];
for (t = 16; t < 80; t++) {
word32 tmp = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16];
w[t] = rol(tmp, 1);
}
a = digest[0];
b = digest[1];
c = digest[2];
d = digest[3];
e = digest[4];
for (t = 0; t < 20; t++) {
word32 tmp =
rol(a, 5) + ((b & c) | (d & ~b)) + e + w[t] + 0x5a827999;
e = d;
d = c;
c = rol(b, 30);
b = a;
a = tmp;
}
for (t = 20; t < 40; t++) {
word32 tmp = rol(a, 5) + (b ^ c ^ d) + e + w[t] + 0x6ed9eba1;
e = d;
d = c;
c = rol(b, 30);
b = a;
a = tmp;
}
for (t = 40; t < 60; t++) {
word32 tmp = rol(a,
5) + ((b & c) | (b & d) | (c & d)) + e + w[t] +
0x8f1bbcdc;
e = d;
d = c;
c = rol(b, 30);
b = a;
a = tmp;
}
for (t = 60; t < 80; t++) {
word32 tmp = rol(a, 5) + (b ^ c ^ d) + e + w[t] + 0xca62c1d6;
e = d;
d = c;
c = rol(b, 30);
b = a;
a = tmp;
}
digest[0] += a;
digest[1] += b;
digest[2] += c;
digest[3] += d;
digest[4] += e;
#ifdef RANDOM_DIAGNOSTICS
{
extern int random_diagnostics;
if (random_diagnostics) {
int i;
printf(" =");
for (i = 0; i < 5; i++)
printf(" %08x", digest[i]);
printf("\n");
}
}
#endif
}
/* ----------------------------------------------------------------------
* Outer SHA algorithm: take an arbitrary length byte string,
* convert it into 16-word blocks with the prescribed padding at
* the end, and pass those blocks to the core SHA algorithm.
*/
void SHA_Init(SHA_State * s)
{
SHA_Core_Init(s->h);
s->blkused = 0;
s->lenhi = s->lenlo = 0;
}
void SHA_Bytes(SHA_State * s, const void *p, int len)
{
const unsigned char *q = (const unsigned char *) p;
uint32 wordblock[16];
uint32 lenw = len;
int i;
/*
* Update the length field.
*/
s->lenlo += lenw;
s->lenhi += (s->lenlo < lenw);
if (s->blkused && s->blkused + len < 64) {
/*
* Trivial case: just add to the block.
*/
memcpy(s->block + s->blkused, q, len);
s->blkused += len;
} else {
/*
* We must complete and process at least one block.
*/
while (s->blkused + len >= 64) {
memcpy(s->block + s->blkused, q, 64 - s->blkused);
q += 64 - s->blkused;
len -= 64 - s->blkused;
/* Now process the block. Gather bytes big-endian into words */
for (i = 0; i < 16; i++) {
wordblock[i] =
(((uint32) s->block[i * 4 + 0]) << 24) |
(((uint32) s->block[i * 4 + 1]) << 16) |
(((uint32) s->block[i * 4 + 2]) << 8) |
(((uint32) s->block[i * 4 + 3]) << 0);
}
SHATransform(s->h, wordblock);
s->blkused = 0;
}
memcpy(s->block, q, len);
s->blkused = len;
}
}
void SHA_Final(SHA_State * s, unsigned char *output)
{
int i;
int pad;
unsigned char c[64];
uint32 lenhi, lenlo;
if (s->blkused >= 56)
pad = 56 + 64 - s->blkused;
else
pad = 56 - s->blkused;
lenhi = (s->lenhi << 3) | (s->lenlo >> (32 - 3));
lenlo = (s->lenlo << 3);
memset(c, 0, pad);
c[0] = 0x80;
SHA_Bytes(s, &c, pad);
c[0] = (lenhi >> 24) & 0xFF;
c[1] = (lenhi >> 16) & 0xFF;
c[2] = (lenhi >> 8) & 0xFF;
c[3] = (lenhi >> 0) & 0xFF;
c[4] = (lenlo >> 24) & 0xFF;
c[5] = (lenlo >> 16) & 0xFF;
c[6] = (lenlo >> 8) & 0xFF;
c[7] = (lenlo >> 0) & 0xFF;
SHA_Bytes(s, &c, 8);
for (i = 0; i < 5; i++) {
output[i * 4] = (s->h[i] >> 24) & 0xFF;
output[i * 4 + 1] = (s->h[i] >> 16) & 0xFF;
output[i * 4 + 2] = (s->h[i] >> 8) & 0xFF;
output[i * 4 + 3] = (s->h[i]) & 0xFF;
}
}
void SHA_Simple(const void *p, int len, unsigned char *output)
{
SHA_State s;
SHA_Init(&s);
SHA_Bytes(&s, p, len);
SHA_Final(&s, output);
}
/*
* Thin abstraction for things where hashes are pluggable.
*/
static void *sha1_init(void)
{
SHA_State *s;
s = snew(SHA_State);
SHA_Init(s);
return s;
}
static void sha1_bytes(void *handle, void *p, int len)
{
SHA_State *s = handle;
SHA_Bytes(s, p, len);
}
static void sha1_final(void *handle, unsigned char *output)
{
SHA_State *s = handle;
SHA_Final(s, output);
sfree(s);
}
const struct ssh_hash ssh_sha1 = {
sha1_init, sha1_bytes, sha1_final, 20, "SHA-1"
};
/* ----------------------------------------------------------------------
* The above is the SHA-1 algorithm itself. Now we implement the
* HMAC wrapper on it.
*/
static void *sha1_make_context(void)
{
return snewn(3, SHA_State);
}
static void sha1_free_context(void *handle)
{
sfree(handle);
}
static void sha1_key_internal(void *handle, unsigned char *key, int len)
{
SHA_State *keys = (SHA_State *)handle;
unsigned char foo[64];
int i;
memset(foo, 0x36, 64);
for (i = 0; i < len && i < 64; i++)
foo[i] ^= key[i];
SHA_Init(&keys[0]);
SHA_Bytes(&keys[0], foo, 64);
memset(foo, 0x5C, 64);
for (i = 0; i < len && i < 64; i++)
foo[i] ^= key[i];
SHA_Init(&keys[1]);
SHA_Bytes(&keys[1], foo, 64);
smemclr(foo, 64); /* burn the evidence */
}
static void sha1_key(void *handle, unsigned char *key)
{
sha1_key_internal(handle, key, 20);
}
static void sha1_key_buggy(void *handle, unsigned char *key)
{
sha1_key_internal(handle, key, 16);
}
static void hmacsha1_start(void *handle)
{
SHA_State *keys = (SHA_State *)handle;
keys[2] = keys[0]; /* structure copy */
}
static void hmacsha1_bytes(void *handle, unsigned char const *blk, int len)
{
SHA_State *keys = (SHA_State *)handle;
SHA_Bytes(&keys[2], (void *)blk, len);
}
static void hmacsha1_genresult(void *handle, unsigned char *hmac)
{
SHA_State *keys = (SHA_State *)handle;
SHA_State s;
unsigned char intermediate[20];
s = keys[2]; /* structure copy */
SHA_Final(&s, intermediate);
s = keys[1]; /* structure copy */
SHA_Bytes(&s, intermediate, 20);
SHA_Final(&s, hmac);
}
static void sha1_do_hmac(void *handle, unsigned char *blk, int len,
unsigned long seq, unsigned char *hmac)
{
unsigned char seqbuf[4];
PUT_32BIT_MSB_FIRST(seqbuf, seq);
hmacsha1_start(handle);
hmacsha1_bytes(handle, seqbuf, 4);
hmacsha1_bytes(handle, blk, len);
hmacsha1_genresult(handle, hmac);
}
static void sha1_generate(void *handle, unsigned char *blk, int len,
unsigned long seq)
{
sha1_do_hmac(handle, blk, len, seq, blk + len);
}
static int hmacsha1_verresult(void *handle, unsigned char const *hmac)
{
unsigned char correct[20];
hmacsha1_genresult(handle, correct);
return !memcmp(correct, hmac, 20);
}
static int sha1_verify(void *handle, unsigned char *blk, int len,
unsigned long seq)
{
unsigned char correct[20];
sha1_do_hmac(handle, blk, len, seq, correct);
return !memcmp(correct, blk + len, 20);
}
static void hmacsha1_96_genresult(void *handle, unsigned char *hmac)
{
unsigned char full[20];
hmacsha1_genresult(handle, full);
memcpy(hmac, full, 12);
}
static void sha1_96_generate(void *handle, unsigned char *blk, int len,
unsigned long seq)
{
unsigned char full[20];
sha1_do_hmac(handle, blk, len, seq, full);
memcpy(blk + len, full, 12);
}
static int hmacsha1_96_verresult(void *handle, unsigned char const *hmac)
{
unsigned char correct[20];
hmacsha1_genresult(handle, correct);
return !memcmp(correct, hmac, 12);
}
static int sha1_96_verify(void *handle, unsigned char *blk, int len,
unsigned long seq)
{
unsigned char correct[20];
sha1_do_hmac(handle, blk, len, seq, correct);
return !memcmp(correct, blk + len, 12);
}
void hmac_sha1_simple(void *key, int keylen, void *data, int datalen,
unsigned char *output) {
SHA_State states[2];
unsigned char intermediate[20];
sha1_key_internal(states, key, keylen);
SHA_Bytes(&states[0], data, datalen);
SHA_Final(&states[0], intermediate);
SHA_Bytes(&states[1], intermediate, 20);
SHA_Final(&states[1], output);
}
const struct ssh_mac ssh_hmac_sha1 = {
sha1_make_context, sha1_free_context, sha1_key,
sha1_generate, sha1_verify,
hmacsha1_start, hmacsha1_bytes, hmacsha1_genresult, hmacsha1_verresult,
"hmac-sha1", "hmac-sha1-etm@openssh.com",
20,
"HMAC-SHA1"
};
const struct ssh_mac ssh_hmac_sha1_96 = {
sha1_make_context, sha1_free_context, sha1_key,
sha1_96_generate, sha1_96_verify,
hmacsha1_start, hmacsha1_bytes,
hmacsha1_96_genresult, hmacsha1_96_verresult,
"hmac-sha1-96", "hmac-sha1-96-etm@openssh.com",
12,
"HMAC-SHA1-96"
};
const struct ssh_mac ssh_hmac_sha1_buggy = {
sha1_make_context, sha1_free_context, sha1_key_buggy,
sha1_generate, sha1_verify,
hmacsha1_start, hmacsha1_bytes, hmacsha1_genresult, hmacsha1_verresult,
"hmac-sha1", NULL,
20,
"bug-compatible HMAC-SHA1"
};
const struct ssh_mac ssh_hmac_sha1_96_buggy = {
sha1_make_context, sha1_free_context, sha1_key_buggy,
sha1_96_generate, sha1_96_verify,
hmacsha1_start, hmacsha1_bytes,
hmacsha1_96_genresult, hmacsha1_96_verresult,
"hmac-sha1-96", NULL,
12,
"bug-compatible HMAC-SHA1-96"
};