forked from rdebath/PuTTY
-
Notifications
You must be signed in to change notification settings - Fork 2
/
sshbcrypt.c
124 lines (106 loc) · 4.2 KB
/
sshbcrypt.c
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
119
120
121
122
123
124
/*
* 'bcrypt' password hash function, for PuTTY's import/export of
* OpenSSH encrypted private key files.
*
* This is not really the same as the original bcrypt; OpenSSH has
* modified it in various ways, and of course we have to do the same.
*/
#include <stddef.h>
#include <string.h>
#include "ssh.h"
#include "sshblowf.h"
BlowfishContext *bcrypt_setup(const unsigned char *key, int keybytes,
const unsigned char *salt, int saltbytes)
{
int i;
BlowfishContext *ctx;
ctx = blowfish_make_context();
blowfish_initkey(ctx);
blowfish_expandkey(ctx, key, keybytes, salt, saltbytes);
/* Original bcrypt replaces this fixed loop count with the
* variable cost. OpenSSH instead iterates the whole thing more
* than once if it wants extra rounds. */
for (i = 0; i < 64; i++) {
blowfish_expandkey(ctx, salt, saltbytes, NULL, 0);
blowfish_expandkey(ctx, key, keybytes, NULL, 0);
}
return ctx;
}
void bcrypt_hash(const unsigned char *key, int keybytes,
const unsigned char *salt, int saltbytes,
unsigned char output[32])
{
BlowfishContext *ctx;
int i;
ctx = bcrypt_setup(key, keybytes, salt, saltbytes);
/* This was quite a nice starting string until it ran into
* little-endian Blowfish :-/ */
memcpy(output, "cyxOmorhcitawolBhsiftawSanyDetim", 32);
for (i = 0; i < 64; i++) {
blowfish_lsb_encrypt_ecb(output, 32, ctx);
}
blowfish_free_context(ctx);
}
void bcrypt_genblock(int counter,
const unsigned char hashed_passphrase[64],
const unsigned char *salt, int saltbytes,
unsigned char output[32])
{
SHA512_State shastate;
unsigned char hashed_salt[64];
unsigned char countbuf[4];
/* Hash the input salt with the counter value optionally suffixed
* to get our real 32-byte salt */
SHA512_Init(&shastate);
SHA512_Bytes(&shastate, salt, saltbytes);
if (counter) {
PUT_32BIT_MSB_FIRST(countbuf, counter);
SHA512_Bytes(&shastate, countbuf, 4);
}
SHA512_Final(&shastate, hashed_salt);
bcrypt_hash(hashed_passphrase, 64, hashed_salt, 64, output);
smemclr(&shastate, sizeof(shastate));
smemclr(&hashed_salt, sizeof(hashed_salt));
}
void openssh_bcrypt(const char *passphrase,
const unsigned char *salt, int saltbytes,
int rounds, unsigned char *out, int outbytes)
{
unsigned char hashed_passphrase[64];
unsigned char block[32], outblock[32];
const unsigned char *thissalt;
int thissaltbytes;
int modulus, residue, i, j, round;
/* Hash the passphrase to get the bcrypt key material */
SHA512_Simple(passphrase, strlen(passphrase), hashed_passphrase);
/* We output key bytes in a scattered fashion to meld all output
* key blocks into all parts of the output. To do this, we pick a
* modulus, and we output the key bytes to indices of out[] in the
* following order: first the indices that are multiples of the
* modulus, then the ones congruent to 1 mod modulus, etc. Each of
* those passes consumes exactly one block output from
* bcrypt_genblock, so we must pick a modulus large enough that at
* most 32 bytes are used in the pass. */
modulus = (outbytes + 31) / 32;
for (residue = 0; residue < modulus; residue++) {
/* Our output block of data is the XOR of all blocks generated
* by bcrypt in the following loop */
memset(outblock, 0, sizeof(outblock));
thissalt = salt;
thissaltbytes = saltbytes;
for (round = 0; round < rounds; round++) {
bcrypt_genblock(round == 0 ? residue+1 : 0,
hashed_passphrase,
thissalt, thissaltbytes, block);
/* Each subsequent bcrypt call reuses the previous one's
* output as its salt */
thissalt = block;
thissaltbytes = 32;
for (i = 0; i < 32; i++)
outblock[i] ^= block[i];
}
for (i = residue, j = 0; i < outbytes; i += modulus, j++)
out[i] = outblock[j];
}
smemclr(&hashed_passphrase, sizeof(hashed_passphrase));
}