~sircmpwn/hare-ssh

hare-ssh/format/ssh/bcrypt.ha -rw-r--r-- 3.4 KiB
c6a39e37Armin Preiml harden against "compromise via lattices" 29 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// This is basically a straight port from OpenSSH.
//
// Copyright (c) 2013 Ted Unangst <tedu@openbsd.org>
// Copyright (c) 2022 Drew DeVault <sir@cmpwn.com>
//
// Permission to use, copy, modify, and distribute this software for any purpose
// with or without fee is hereby granted, provided that the above copyright
// notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
use bytes;
use crypto::bcrypt;
use crypto::blowfish;
use crypto::cipher;
use crypto::sha512;
use hash;
use strings;

def BCRYPT_WORDS: size = 8;
def BCRYPT_HASHSZ: size = BCRYPT_WORDS * 4;

// Performs the silly OpenBSD-specific bcrypt key derivation function. See
// openbsd-compat/bcrypt_pbkdf.c in the OpenSSH portable tree.
fn bcrypt_pbkdf(key: []u8, pass: []u8, salt: []u8, rounds: uint) void = {
	// Same sanity checks as OpenBSD
	assert(rounds >= 1 && len(pass) != 0 && len(salt) != 0
		&& len(key) != 0 && len(key) <= BCRYPT_HASHSZ * BCRYPT_HASHSZ
		&& len(salt) <= 1 << 20);

	let out: [BCRYPT_HASHSZ]u8 = [0...];
	let tmpout: [BCRYPT_HASHSZ]u8 = [0...];
	defer bytes::zero(out);
	defer bytes::zero(tmpout);

	const stride = (len(key) + len(out) - 1) / len(out);
	let amt = (len(key) + stride - 1) / stride;

	let countsalt: []u8 = alloc([0...], len(salt) + 4);
	defer free(countsalt);
	countsalt[..len(salt)] = salt[..];

	let shapass: [sha512::SZ]u8 = [0...];
	const sha = sha512::sha512();
	hash::write(&sha, pass);
	hash::sum(&sha, shapass);

	let shasalt: [sha512::SZ]u8 = [0...];
	let keylen = len(key);
	for (let count = 1u32; keylen > 0; count += 1) {
		countsalt[len(salt) + 0] = (count >> 24): u8;
		countsalt[len(salt) + 1] = (count >> 16): u8;
		countsalt[len(salt) + 2] = (count >> 8): u8;
		countsalt[len(salt) + 3] = count: u8;

		const sha = sha512::sha512();
		hash::write(&sha, countsalt);
		hash::sum(&sha, shasalt);

		bcrypt_hash(tmpout, shapass, shasalt);
		out[..] = tmpout[..];

		for (let i = 1u; i < rounds; i += 1) {
			hash::reset(&sha);
			hash::write(&sha, tmpout);
			hash::sum(&sha, shasalt);

			bcrypt_hash(tmpout, shapass, shasalt);

			for (let j = 0z; j < len(out); j += 1) {
				out[j] ^= tmpout[j];
			};
		};

		amt = if (amt < keylen) amt else keylen;

		let i = 0z;
		for (i < amt; i += 1) {
			const dest = i * stride + (count - 1);
			if (dest >= len(key)) {
				break;
			};
			key[dest] = out[i];
		};
		keylen -= i;
	};
};

fn bcrypt_hash(out: []u8, pass: []u8, salt: []u8) void = {
	const magic = strings::toutf8("OxychromaticBlowfishSwatDynamite");
	out[..len(magic)] = magic[..];

	let bf = blowfish::new();
	blowfish::init_salt(&bf, pass, salt);

	for (let i = 0z; i < 64; i += 1) {
		blowfish::init(&bf, salt);
		blowfish::init(&bf, pass);
	};

	for (let i = 0z; i < 32; i += 8) {
		for (let j = 0z; j < 64; j += 1) {
			cipher::encrypt(&bf, out[i..i+8], out[i..i+8]);
		};
	};

	// Endianness
	for (let i = 0z; i < 32; i += 4) {
		let x = 0u8;
		x = out[i + 3]; out[i + 3] = out[i + 0]; out[i + 0] = x;
		x = out[i + 2]; out[i + 2] = out[i + 1]; out[i + 1] = x;
	};
};