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:
parent
6f025c0b84
commit
5c8f3bf924
2
Recipe
2
Recipe
@ -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
5
ssh.h
@ -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
223
sshblake2.c
Normal 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(©->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"),
|
||||
};
|
@ -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):
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user