mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
Implementation of OpenSSH's bcrypt.
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.
This commit is contained in:
parent
a8658edb17
commit
d637528181
4
ssh.h
4
ssh.h
@ -663,6 +663,10 @@ void des_encrypt_xdmauth(const unsigned char *key,
|
||||
void des_decrypt_xdmauth(const unsigned char *key,
|
||||
unsigned char *blk, int len);
|
||||
|
||||
void openssh_bcrypt(const char *passphrase,
|
||||
const unsigned char *salt, int saltbytes,
|
||||
int rounds, unsigned char *out, int outbytes);
|
||||
|
||||
/*
|
||||
* For progress updates in the key generation utility.
|
||||
*/
|
||||
|
124
sshbcrypt.c
Normal file
124
sshbcrypt.c
Normal file
@ -0,0 +1,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));
|
||||
}
|
85
sshblowf.c
85
sshblowf.c
@ -7,11 +7,12 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include "ssh.h"
|
||||
#include "sshblowf.h"
|
||||
|
||||
typedef struct {
|
||||
struct BlowfishContext {
|
||||
word32 S0[256], S1[256], S2[256], S3[256], P[18];
|
||||
word32 iv0, iv1; /* for CBC mode */
|
||||
} BlowfishContext;
|
||||
};
|
||||
|
||||
/*
|
||||
* The Blowfish init data: hex digits of the fractional part of pi.
|
||||
@ -305,6 +306,24 @@ static void blowfish_lsb_encrypt_cbc(unsigned char *blk, int len,
|
||||
ctx->iv1 = iv1;
|
||||
}
|
||||
|
||||
void blowfish_lsb_encrypt_ecb(unsigned char *blk, int len,
|
||||
BlowfishContext * ctx)
|
||||
{
|
||||
word32 xL, xR, out[2];
|
||||
|
||||
assert((len & 7) == 0);
|
||||
|
||||
while (len > 0) {
|
||||
xL = GET_32BIT_LSB_FIRST(blk);
|
||||
xR = GET_32BIT_LSB_FIRST(blk + 4);
|
||||
blowfish_encrypt(xL, xR, out, ctx);
|
||||
PUT_32BIT_LSB_FIRST(blk, out[0]);
|
||||
PUT_32BIT_LSB_FIRST(blk + 4, out[1]);
|
||||
blk += 8;
|
||||
len -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
static void blowfish_lsb_decrypt_cbc(unsigned char *blk, int len,
|
||||
BlowfishContext * ctx)
|
||||
{
|
||||
@ -415,8 +434,25 @@ static void blowfish_msb_sdctr(unsigned char *blk, int len,
|
||||
ctx->iv1 = iv1;
|
||||
}
|
||||
|
||||
static void blowfish_setkey(BlowfishContext * ctx,
|
||||
const unsigned char *key, short keybytes)
|
||||
void blowfish_initkey(BlowfishContext *ctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 18; i++) {
|
||||
ctx->P[i] = parray[i];
|
||||
}
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
ctx->S0[i] = sbox0[i];
|
||||
ctx->S1[i] = sbox1[i];
|
||||
ctx->S2[i] = sbox2[i];
|
||||
ctx->S3[i] = sbox3[i];
|
||||
}
|
||||
}
|
||||
|
||||
void blowfish_expandkey(BlowfishContext * ctx,
|
||||
const unsigned char *key, short keybytes,
|
||||
const unsigned char *salt, short saltbytes)
|
||||
{
|
||||
word32 *S0 = ctx->S0;
|
||||
word32 *S1 = ctx->S1;
|
||||
@ -424,10 +460,18 @@ static void blowfish_setkey(BlowfishContext * ctx,
|
||||
word32 *S3 = ctx->S3;
|
||||
word32 *P = ctx->P;
|
||||
word32 str[2];
|
||||
int i;
|
||||
int i, j;
|
||||
int saltpos;
|
||||
unsigned char dummysalt[1];
|
||||
|
||||
saltpos = 0;
|
||||
if (!salt) {
|
||||
saltbytes = 1;
|
||||
salt = dummysalt;
|
||||
dummysalt[0] = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < 18; i++) {
|
||||
P[i] = parray[i];
|
||||
P[i] ^=
|
||||
((word32) (unsigned char) (key[(i * 4 + 0) % keybytes])) << 24;
|
||||
P[i] ^=
|
||||
@ -437,48 +481,59 @@ static void blowfish_setkey(BlowfishContext * ctx,
|
||||
P[i] ^= ((word32) (unsigned char) (key[(i * 4 + 3) % keybytes]));
|
||||
}
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
S0[i] = sbox0[i];
|
||||
S1[i] = sbox1[i];
|
||||
S2[i] = sbox2[i];
|
||||
S3[i] = sbox3[i];
|
||||
}
|
||||
|
||||
str[0] = str[1] = 0;
|
||||
|
||||
for (i = 0; i < 18; i += 2) {
|
||||
for (j = 0; j < 8; j++)
|
||||
str[j/4] ^= ((word32)salt[saltpos++ % saltbytes]) << (24-8*(j%4));
|
||||
|
||||
blowfish_encrypt(str[0], str[1], str, ctx);
|
||||
P[i] = str[0];
|
||||
P[i + 1] = str[1];
|
||||
}
|
||||
|
||||
for (i = 0; i < 256; i += 2) {
|
||||
for (j = 0; j < 8; j++)
|
||||
str[j/4] ^= ((word32)salt[saltpos++ % saltbytes]) << (24-8*(j%4));
|
||||
blowfish_encrypt(str[0], str[1], str, ctx);
|
||||
S0[i] = str[0];
|
||||
S0[i + 1] = str[1];
|
||||
}
|
||||
for (i = 0; i < 256; i += 2) {
|
||||
for (j = 0; j < 8; j++)
|
||||
str[j/4] ^= ((word32)salt[saltpos++ % saltbytes]) << (24-8*(j%4));
|
||||
blowfish_encrypt(str[0], str[1], str, ctx);
|
||||
S1[i] = str[0];
|
||||
S1[i + 1] = str[1];
|
||||
}
|
||||
for (i = 0; i < 256; i += 2) {
|
||||
for (j = 0; j < 8; j++)
|
||||
str[j/4] ^= ((word32)salt[saltpos++ % saltbytes]) << (24-8*(j%4));
|
||||
blowfish_encrypt(str[0], str[1], str, ctx);
|
||||
S2[i] = str[0];
|
||||
S2[i + 1] = str[1];
|
||||
}
|
||||
for (i = 0; i < 256; i += 2) {
|
||||
for (j = 0; j < 8; j++)
|
||||
str[j/4] ^= ((word32)salt[saltpos++ % saltbytes]) << (24-8*(j%4));
|
||||
blowfish_encrypt(str[0], str[1], str, ctx);
|
||||
S3[i] = str[0];
|
||||
S3[i + 1] = str[1];
|
||||
}
|
||||
}
|
||||
|
||||
static void blowfish_setkey(BlowfishContext *ctx,
|
||||
const unsigned char *key, short keybytes)
|
||||
{
|
||||
blowfish_initkey(ctx);
|
||||
blowfish_expandkey(ctx, key, keybytes, NULL, 0);
|
||||
}
|
||||
|
||||
/* -- Interface with PuTTY -- */
|
||||
|
||||
#define SSH_SESSION_KEY_LENGTH 32
|
||||
|
||||
static void *blowfish_make_context(void)
|
||||
void *blowfish_make_context(void)
|
||||
{
|
||||
return snew(BlowfishContext);
|
||||
}
|
||||
@ -489,7 +544,7 @@ static void *blowfish_ssh1_make_context(void)
|
||||
return snewn(2, BlowfishContext);
|
||||
}
|
||||
|
||||
static void blowfish_free_context(void *handle)
|
||||
void blowfish_free_context(void *handle)
|
||||
{
|
||||
sfree(handle);
|
||||
}
|
||||
|
15
sshblowf.h
Normal file
15
sshblowf.h
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Header file shared between sshblowf.c and sshbcrypt.c. Exposes the
|
||||
* internal Blowfish routines needed by bcrypt.
|
||||
*/
|
||||
|
||||
typedef struct BlowfishContext BlowfishContext;
|
||||
|
||||
void *blowfish_make_context(void);
|
||||
void blowfish_free_context(void *handle);
|
||||
void blowfish_initkey(BlowfishContext *ctx);
|
||||
void blowfish_expandkey(BlowfishContext *ctx,
|
||||
const unsigned char *key, short keybytes,
|
||||
const unsigned char *salt, short saltbytes);
|
||||
void blowfish_lsb_encrypt_ecb(unsigned char *blk, int len,
|
||||
BlowfishContext *ctx);
|
Loading…
Reference in New Issue
Block a user