mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
d637528181
This isn't the same as the standard bcrypt; it's OpenSSH's modification that they use for their new-style key format. In order to implement this, I've broken up blowfish_setkey() into two subfunctions, and provided one of them with an extra optional salt parameter, which is NULL in ordinary Blowfish but used by bcrypt. Also, I've exposed some of sshblowf.c's internal machinery for the new sshbcrypt.c to use.
125 lines
4.2 KiB
C
125 lines
4.2 KiB
C
/*
|
|
* '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));
|
|
}
|