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

Add an implementation of BLAKE2b.

I have no plans to use this directly, but it's a component of Argon2,
which I'm about to add in the next commit.
This commit is contained in:
Simon Tatham 2021-02-13 14:47:26 +00:00
parent 6f025c0b84
commit 5c8f3bf924
7 changed files with 300 additions and 1 deletions

2
Recipe
View File

@ -252,7 +252,7 @@ NONSSH = telnet raw rlogin supdup ldisc pinger
# SSH back end (putty, plink, pscp, psftp).
ARITH = mpint ecc
SSHCRYPTO = ARITH sshmd5 sshsha sshsh256 sshsh512 sshsha3
SSHCRYPTO = ARITH sshmd5 sshsha sshsh256 sshsh512 sshsha3 sshblake2
+ sshrsa sshdss sshecc
+ sshdes sshblowf sshaes sshccp ssharcf
+ sshdh sshcrc sshcrcda sshauxcrypt

5
ssh.h
View File

@ -986,6 +986,7 @@ extern const ssh_hashalg ssh_sha3_256;
extern const ssh_hashalg ssh_sha3_384;
extern const ssh_hashalg ssh_sha3_512;
extern const ssh_hashalg ssh_shake256_114bytes;
extern const ssh_hashalg ssh_blake2b;
extern const ssh_kexes ssh_diffiehellman_group1;
extern const ssh_kexes ssh_diffiehellman_group14;
extern const ssh_kexes ssh_diffiehellman_gex;
@ -1015,6 +1016,10 @@ extern const ssh2_macalg ssh_hmac_sha256;
extern const ssh2_macalg ssh2_poly1305;
extern const ssh_compression_alg ssh_zlib;
/* Special constructor: BLAKE2b can be instantiated with any hash
* length up to 128 bytes */
ssh_hash *blake2b_new_general(unsigned hashlen);
/*
* On some systems, you have to detect hardware crypto acceleration by
* asking the local OS API rather than OS-agnostically asking the CPU

223
sshblake2.c Normal file
View File

@ -0,0 +1,223 @@
/*
* BLAKE2 (RFC 7693) implementation for PuTTY.
*
* The BLAKE2 hash family includes BLAKE2s, in which the hash state is
* operated on as a collection of 32-bit integers, and BLAKE2b, based
* on 64-bit integers. At present this code implements BLAKE2b only.
*/
#include <assert.h>
#include "ssh.h"
static inline uint64_t ror(uint64_t x, unsigned rotation)
{
unsigned lshift = 63 & -rotation, rshift = 63 & rotation;
return (x << lshift) | (x >> rshift);
}
/* RFC 7963 section 2.1 */
enum { R1 = 32, R2 = 24, R3 = 16, R4 = 63 };
/* RFC 7693 section 2.6 */
static const uint64_t iv[] = {
0x6a09e667f3bcc908, /* floor(2^64 * frac(sqrt(2))) */
0xbb67ae8584caa73b, /* floor(2^64 * frac(sqrt(3))) */
0x3c6ef372fe94f82b, /* floor(2^64 * frac(sqrt(5))) */
0xa54ff53a5f1d36f1, /* floor(2^64 * frac(sqrt(7))) */
0x510e527fade682d1, /* floor(2^64 * frac(sqrt(11))) */
0x9b05688c2b3e6c1f, /* floor(2^64 * frac(sqrt(13))) */
0x1f83d9abfb41bd6b, /* floor(2^64 * frac(sqrt(17))) */
0x5be0cd19137e2179, /* floor(2^64 * frac(sqrt(19))) */
};
/* RFC 7693 section 2.7 */
static const unsigned char sigma[][16] = {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
{11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
{12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
{13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
{10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0},
/* This array recycles if you have more than 10 rounds. BLAKE2b
* has 12, so we repeat the first two rows again. */
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
};
static inline void g_half(uint64_t v[16], unsigned a, unsigned b, unsigned c,
unsigned d, uint64_t x, unsigned r1, unsigned r2)
{
v[a] += v[b] + x;
v[d] ^= v[a];
v[d] = ror(v[d], r1);
v[c] += v[d];
v[b] ^= v[c];
v[b] = ror(v[b], r2);
}
static inline void g(uint64_t v[16], unsigned a, unsigned b, unsigned c,
unsigned d, uint64_t x, uint64_t y)
{
g_half(v, a, b, c, d, x, R1, R2);
g_half(v, a, b, c, d, y, R3, R4);
}
static inline void f(uint64_t h[8], uint64_t m[16], uint64_t offset_hi,
uint64_t offset_lo, unsigned final)
{
uint64_t v[16];
memcpy(v, h, 8 * sizeof(*v));
memcpy(v + 8, iv, 8 * sizeof(*v));
v[12] ^= offset_lo;
v[13] ^= offset_hi;
v[14] ^= -(uint64_t)final;
for (unsigned round = 0; round < 12; round++) {
const unsigned char *s = sigma[round];
g(v, 0, 4, 8, 12, m[s[ 0]], m[s[ 1]]);
g(v, 1, 5, 9, 13, m[s[ 2]], m[s[ 3]]);
g(v, 2, 6, 10, 14, m[s[ 4]], m[s[ 5]]);
g(v, 3, 7, 11, 15, m[s[ 6]], m[s[ 7]]);
g(v, 0, 5, 10, 15, m[s[ 8]], m[s[ 9]]);
g(v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
g(v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
g(v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
}
for (unsigned i = 0; i < 8; i++)
h[i] ^= v[i] ^ v[i+8];
smemclr(v, sizeof(v));
}
static inline void f_outer(uint64_t h[8], uint8_t blk[128], uint64_t offset_hi,
uint64_t offset_lo, unsigned final)
{
uint64_t m[16];
for (unsigned i = 0; i < 16; i++)
m[i] = GET_64BIT_LSB_FIRST(blk + 8*i);
f(h, m, offset_hi, offset_lo, final);
smemclr(m, sizeof(m));
}
typedef struct blake2b {
uint64_t h[8];
unsigned hashlen;
uint8_t block[128];
size_t used;
uint64_t lenhi, lenlo;
BinarySink_IMPLEMENTATION;
ssh_hash hash;
} blake2b;
static void blake2b_write(BinarySink *bs, const void *vp, size_t len);
static ssh_hash *blake2b_new_inner(unsigned hashlen)
{
assert(hashlen <= ssh_blake2b.hlen);
blake2b *s = snew(blake2b);
s->hash.vt = &ssh_blake2b;
s->hashlen = hashlen;
BinarySink_INIT(s, blake2b_write);
BinarySink_DELEGATE_INIT(&s->hash, s);
return &s->hash;
}
static ssh_hash *blake2b_new(const ssh_hashalg *alg)
{
return blake2b_new_inner(alg->hlen);
}
ssh_hash *blake2b_new_general(unsigned hashlen)
{
ssh_hash *h = blake2b_new_inner(hashlen);
ssh_hash_reset(h);
return h;
}
static void blake2b_reset(ssh_hash *hash)
{
blake2b *s = container_of(hash, blake2b, hash);
/* Initialise the hash to the standard IV */
memcpy(s->h, iv, sizeof(s->h));
/* XOR in the parameters: secret key length (here always 0) in
* byte 1, and hash length in byte 0. */
s->h[0] ^= 0x01010000 ^ s->hashlen;
s->used = 0;
s->lenhi = s->lenlo = 0;
}
static void blake2b_copyfrom(ssh_hash *hcopy, ssh_hash *horig)
{
blake2b *copy = container_of(hcopy, blake2b, hash);
blake2b *orig = container_of(horig, blake2b, hash);
memcpy(copy, orig, sizeof(*copy));
BinarySink_COPIED(copy);
BinarySink_DELEGATE_INIT(&copy->hash, copy);
}
static void blake2b_free(ssh_hash *hash)
{
blake2b *s = container_of(hash, blake2b, hash);
smemclr(s, sizeof(*s));
sfree(s);
}
static void blake2b_write(BinarySink *bs, const void *vp, size_t len)
{
blake2b *s = BinarySink_DOWNCAST(bs, blake2b);
const uint8_t *p = vp;
while (len > 0) {
if (s->used == sizeof(s->block)) {
f_outer(s->h, s->block, s->lenhi, s->lenlo, 0);
s->used = 0;
}
size_t chunk = sizeof(s->block) - s->used;
if (chunk > len)
chunk = len;
memcpy(s->block + s->used, p, chunk);
s->used += chunk;
p += chunk;
len -= chunk;
s->lenlo += chunk;
s->lenhi += (s->lenlo < chunk);
}
}
static void blake2b_digest(ssh_hash *hash, uint8_t *digest)
{
blake2b *s = container_of(hash, blake2b, hash);
memset(s->block + s->used, 0, sizeof(s->block) - s->used);
f_outer(s->h, s->block, s->lenhi, s->lenlo, 1);
uint8_t hash_pre[128];
for (unsigned i = 0; i < 8; i++)
PUT_64BIT_LSB_FIRST(hash_pre + 8*i, s->h[i]);
memcpy(digest, hash_pre, s->hashlen);
smemclr(hash_pre, sizeof(hash_pre));
}
const ssh_hashalg ssh_blake2b = {
.new = blake2b_new,
.reset = blake2b_reset,
.copyfrom = blake2b_copyfrom,
.digest = blake2b_digest,
.free = blake2b_free,
.hlen = 64,
.blocklen = 128,
HASHALG_NAMES_BARE("BLAKE2b-64"),
};

View File

@ -9,6 +9,7 @@ import contextlib
import hashlib
import binascii
import base64
import json
try:
from math import gcd
except ImportError:
@ -1727,6 +1728,35 @@ 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 testBLAKE2b(self):
# The standard test vectors for BLAKE2b (in the separate class
# below) don't satisfy me because they only test one hash
# size. These additional tests exercise BLAKE2b's configurable
# output length. The expected results are derived from the
# BLAKE2 reference implementation.
def b2_with_len(data, length):
h = blake2b_new_general(length)
h.update(data)
return h.digest()[:length]
self.assertEqualBin(b2_with_len(b'hello', 1), unhex("29"))
self.assertEqualBin(b2_with_len(b'hello', 2), unhex("accd"))
self.assertEqualBin(b2_with_len(b'hello', 3), unhex("980032"))
self.assertEqualBin(b2_with_len(b'hello', 5), unhex("9baecc38f2"))
self.assertEqualBin(b2_with_len(b'hello', 8), unhex(
"a7b6eda801e5347d"))
self.assertEqualBin(b2_with_len(b'hello', 13), unhex(
"6eedb122c6707328a66aa34a07"))
self.assertEqualBin(b2_with_len(b'hello', 21), unhex(
"c7f0f74a227116547b3d2788e927ee2a76c87d8797"))
self.assertEqualBin(b2_with_len(b'hello', 34), unhex(
"2f5fcdf2b870fa254051dd448193a1fb6e92be122efca539ba2aeac0bc6c77d0"
"dadc"))
self.assertEqualBin(b2_with_len(b'hello', 55), unhex(
"daafcf2bd6fccf976cbc234b71cd9f4f7d56fe0eb33a40018707089a215c44a8"
"4b272d0329ae6d85a0f8acc7e964dc2facb715ba472bb6"))
def testRSAVerify(self):
def blobs(n, e, d, p, q, iqmp):
pubblob = ssh_string(b"ssh-rsa") + ssh2_mpint(e) + ssh2_mpint(n)
@ -2404,6 +2434,43 @@ class standard_test_vectors(MyTestBase):
self.assertEqualBin(hash_str('shake256_114bytes', ''), unhex("46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762fd75dc4ddd8c0f200cb05019d67b592f6fc821c49479ab48640292eacb3b7c4be141e96616fb13957692cc7edd0b45ae3dc07223c8e92937bef84bc0eab862853349ec75546f58fb7c2775c38462c5010d846"))
self.assertEqualBin(hash_str('shake256_114bytes', unhex('a3')*200), unhex("cd8a920ed141aa0407a22d59288652e9d9f1a7ee0c1e7c1ca699424da84a904d2d700caae7396ece96604440577da4f3aa22aeb8857f961c4cd8e06f0ae6610b1048a7f64e1074cd629e85ad7566048efc4fb500b486a3309a8f26724c0ed628001a1099422468de726f1061d99eb9e93604"))
def testBLAKE2b(self):
# Test case from RFC 7693 appendix A.
self.assertEqualBin(hash_str('blake2b', b'abc'), unhex(
"ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"
"7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"))
# A small number of test cases from the larger test vector
# set, testing multiple blocks and the empty input.
self.assertEqualBin(hash_str('blake2b', b''), unhex(
"786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419"
"d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce"))
self.assertEqualBin(hash_str('blake2b', unhex('00')), unhex(
"2fa3f686df876995167e7c2e5d74c4c7b6e48f8068fe0e44208344d480f7904c"
"36963e44115fe3eb2a3ac8694c28bcb4f5a0f3276f2e79487d8219057a506e4b"))
self.assertEqualBin(hash_str('blake2b', bytes(range(255))), unhex(
"5b21c5fd8868367612474fa2e70e9cfa2201ffeee8fafab5797ad58fefa17c9b"
"5b107da4a3db6320baaf2c8617d5a51df914ae88da3867c2d41f0cc14fa67928"))
# You can get this test program to run the full version of the
# test vectors by modifying the source temporarily to set this
# variable to a pathname where you downloaded the JSON file
# blake2-kat.json.
blake2_test_vectors_path = None
if blake2_test_vectors_path is not None:
with open(blake2_test_vectors_path) as fh:
vectors = json.load(fh)
for vector in vectors:
if vector['hash'] != 'blake2b':
continue
if len(vector['key']) != 0:
continue
h = blake2b_new_general(len(vector['out']) // 2)
ssh_hash_update(h, unhex(vector['in']))
digest = ssh_hash_digest(h)
self.assertEqualBin(digest, unhex(vector['out']))
def testHmacSHA(self):
# Test cases from RFC 6234 section 8.5.
def vector(key, message, s1=None, s256=None):

View File

@ -225,6 +225,7 @@ static const ssh_hashalg *get_hashalg(BinarySource *in)
{"sha3_384", &ssh_sha3_384},
{"sha3_512", &ssh_sha3_512},
{"shake256_114bytes", &ssh_shake256_114bytes},
{"blake2b", &ssh_blake2b},
};
ptrlen name = get_word(in);

View File

@ -136,6 +136,8 @@ FUNC1(val_string, ssh_hash_digest, val_hash)
FUNC1(val_string, ssh_hash_final, consumed_val_hash)
FUNC2(void, ssh_hash_update, val_hash, val_string_ptrlen)
FUNC1(opt_val_hash, blake2b_new_general, uint)
/*
* The ssh2_mac abstraction. Note the optional ssh_cipher parameter
* to ssh2_mac_new. Also, again, I've invented an ssh2_mac_update so

View File

@ -277,6 +277,7 @@ VOLATILE_WRAPPED_DEFN(static, size_t, looplimit, (size_t x))
X(Y, ssh_sha3_384) \
X(Y, ssh_sha3_512) \
X(Y, ssh_shake256_114bytes) \
X(Y, ssh_blake2b) \
/* end of list */
#define HASH_TESTLIST(X, name) X(hash_ ## name)