--- /dev/null
+/*
+ * '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));
+}
#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.
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)
{
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;
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] ^=
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);
}
return snewn(2, BlowfishContext);
}
-static void blowfish_free_context(void *handle)
+void blowfish_free_context(void *handle)
{
sfree(handle);
}