Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mRemoteNG/PuTTYNG.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/bcrypt.c')
-rw-r--r--crypto/bcrypt.c118
1 files changed, 118 insertions, 0 deletions
diff --git a/crypto/bcrypt.c b/crypto/bcrypt.c
new file mode 100644
index 00000000..1ccd4756
--- /dev/null
+++ b/crypto/bcrypt.c
@@ -0,0 +1,118 @@
+/*
+ * '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 "blowfish.h"
+
+static 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;
+}
+
+static 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);
+}
+
+static void bcrypt_genblock(int counter,
+ const unsigned char hashed_passphrase[64],
+ const unsigned char *salt, int saltbytes,
+ unsigned char output[32])
+{
+ unsigned char hashed_salt[64];
+
+ /* Hash the input salt with the counter value optionally suffixed
+ * to get our real 32-byte salt */
+ ssh_hash *h = ssh_hash_new(&ssh_sha512);
+ put_data(h, salt, saltbytes);
+ if (counter)
+ put_uint32(h, counter);
+ ssh_hash_final(h, hashed_salt);
+
+ bcrypt_hash(hashed_passphrase, 64, hashed_salt, 64, output);
+
+ smemclr(&hashed_salt, sizeof(hashed_salt));
+}
+
+void openssh_bcrypt(ptrlen passphrase, ptrlen salt,
+ 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 */
+ hash_simple(&ssh_sha512, 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.ptr;
+ thissaltbytes = salt.len;
+ 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));
+}