Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@ bcrypt_pbkdf is a ruby gem implementing bcrypt_pbkdf from OpenBSD. This is curre
* The gut of the code is based on OpenBSD's bcrypt_pbkdf.c implementation
* Some ideas/code were taken adopted bcrypt-ruby: https://github.com/codahale/bcrypt-ruby

## Upstream sources

The C sources are synced from openssh-portable, which tracks OpenBSD CVS:

| File | OpenBSD version |
|------|----------------|
| `ext/mri/bcrypt_pbkdf.c` | [v1.17](https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libutil/bcrypt_pbkdf.c?rev=1.17) |
| `ext/mri/blowfish.c` | [v1.20](https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libcrypto/blowfish.c?rev=1.20) |

## Links

* http://www.tedunangst.com/flak/post/bcrypt-pbkdf
* http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libutil/bcrypt_pbkdf.c?rev=1.13&content-type=text/x-cvsweb-markup

## Building

Expand Down
66 changes: 32 additions & 34 deletions ext/mri/bcrypt_pbkdf.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* $OpenBSD: bcrypt_pbkdf.c,v 1.13 2015/01/12 03:20:04 tedu Exp $ */
/* $OpenBSD: bcrypt_pbkdf.c,v 1.17 2022/12/27 17:10:08 jmc Exp $ */
/*
* Copyright (c) 2013 Ted Unangst <tedu@openbsd.org>
*
Expand All @@ -15,14 +15,13 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include <sys/types.h>
#include "includes.h"
#include "crypto_api.h"

#include <stdint.h>
#include <stdlib.h>
#include "blf.h"
#include "sha2.h"
#include <string.h>
#include "util.h"
#ifdef SHA512_DIGEST_LENGTH
# undef SHA512_DIGEST_LENGTH
#endif
#define SHA512_DIGEST_LENGTH crypto_hash_sha512_BYTES

#define MINIMUM(a,b) (((a) < (b)) ? (a) : (b))

Expand All @@ -33,7 +32,7 @@
* function with the following modifications:
* 1. The input password and salt are preprocessed with SHA512.
* 2. The output length is expanded to 256 bits.
* 3. Subsequently the magic string to be encrypted is lengthened and modifed
* 3. Subsequently the magic string to be encrypted is lengthened and modified
* to "OxychromaticBlowfishSwatDynamite"
* 4. The hash function is defined to perform 64 rounds of initial state
* expansion. (More rounds are performed by iterating the hash.)
Expand All @@ -50,14 +49,15 @@
* wise caller could do; we just do it for you.
*/

#define BCRYPT_WORDS 8
#define BCRYPT_HASHSIZE (BCRYPT_WORDS * 4)

void
bcrypt_hash(const uint8_t *sha2pass, const uint8_t *sha2salt, uint8_t *out)
bcrypt_hash(uint8_t *sha2pass, uint8_t *sha2salt, uint8_t *out)
{
blf_ctx state;
#if defined(__has_attribute) && __has_attribute(__nonstring__)
uint8_t __attribute__ ((__nonstring__)) ciphertext[BCRYPT_HASHSIZE] =
#else
uint8_t ciphertext[BCRYPT_HASHSIZE] =
#endif
"OxychromaticBlowfishSwatDynamite";
uint32_t cdata[BCRYPT_WORDS];
int i;
Expand All @@ -78,7 +78,7 @@ bcrypt_hash(const uint8_t *sha2pass, const uint8_t *sha2salt, uint8_t *out)
cdata[i] = Blowfish_stream2word(ciphertext, sizeof(ciphertext),
&j);
for (i = 0; i < 64; i++)
blf_enc(&state, cdata, sizeof(cdata) / sizeof(uint64_t));
blf_enc(&state, cdata, BCRYPT_WORDS / 2);

/* copy out */
for (i = 0; i < BCRYPT_WORDS; i++) {
Expand All @@ -98,12 +98,11 @@ int
bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt, size_t saltlen,
uint8_t *key, size_t keylen, unsigned int rounds)
{
SHA2_CTX ctx;
uint8_t sha2pass[SHA512_DIGEST_LENGTH];
uint8_t sha2salt[SHA512_DIGEST_LENGTH];
uint8_t out[BCRYPT_HASHSIZE];
uint8_t tmpout[BCRYPT_HASHSIZE];
uint8_t countsalt[4];
uint8_t *countsalt;
size_t i, j, amt, stride;
uint32_t count;
size_t origkeylen = keylen;
Expand All @@ -112,37 +111,34 @@ bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt, size_t saltl
if (rounds < 1)
return -1;
if (passlen == 0 || saltlen == 0 || keylen == 0 ||
keylen > sizeof(out) * sizeof(out))
keylen > sizeof(out) * sizeof(out) || saltlen > 1<<20)
return -1;
if ((countsalt = calloc(1, saltlen + 4)) == NULL)
return -1;
stride = (keylen + sizeof(out) - 1) / sizeof(out);
amt = (keylen + stride - 1) / stride;

/* collapse password */
SHA512Init(&ctx);
SHA512Update(&ctx, pass, passlen);
SHA512Final(sha2pass, &ctx);
memcpy(countsalt, salt, saltlen);

/* collapse password */
crypto_hash_sha512(sha2pass, (const uint8_t *)pass, passlen);

/* generate key, sizeof(out) at a time */
for (count = 1; keylen > 0; count++) {
countsalt[0] = (count >> 24) & 0xff;
countsalt[1] = (count >> 16) & 0xff;
countsalt[2] = (count >> 8) & 0xff;
countsalt[3] = count & 0xff;
countsalt[saltlen + 0] = (count >> 24) & 0xff;
countsalt[saltlen + 1] = (count >> 16) & 0xff;
countsalt[saltlen + 2] = (count >> 8) & 0xff;
countsalt[saltlen + 3] = count & 0xff;

/* first round, salt is salt */
SHA512Init(&ctx);
SHA512Update(&ctx, salt, saltlen);
SHA512Update(&ctx, countsalt, sizeof(countsalt));
SHA512Final(sha2salt, &ctx);
crypto_hash_sha512(sha2salt, countsalt, saltlen + 4);

bcrypt_hash(sha2pass, sha2salt, tmpout);
memcpy(out, tmpout, sizeof(out));

for (i = 1; i < rounds; i++) {
/* subsequent rounds, salt is previous output */
SHA512Init(&ctx);
SHA512Update(&ctx, tmpout, sizeof(tmpout));
SHA512Final(sha2salt, &ctx);
crypto_hash_sha512(sha2salt, tmpout, sizeof(tmpout));
bcrypt_hash(sha2pass, sha2salt, tmpout);
for (j = 0; j < sizeof(out); j++)
out[j] ^= tmpout[j];
Expand All @@ -162,8 +158,10 @@ bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt, size_t saltl
}

/* zap */
explicit_bzero(&ctx, sizeof(ctx));
explicit_bzero(countsalt, saltlen + 4);
free(countsalt);
explicit_bzero(out, sizeof(out));
explicit_bzero(tmpout, sizeof(tmpout));

return 0;
}
}
2 changes: 1 addition & 1 deletion ext/mri/bcrypt_pbkdf_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ static VALUE bc_crypt_hash(VALUE self, VALUE pass, VALUE salt) {
return Qnil;
if (RSTRING_LEN(salt) != 64U)
return Qnil;
bcrypt_hash((const u_int8_t*)StringValuePtr(pass), (const u_int8_t*)StringValuePtr(salt), hash);
bcrypt_hash((u_int8_t*)StringValuePtr(pass), (u_int8_t*)StringValuePtr(salt), hash);
return rb_str_new((const char*)hash, sizeof(hash));
}

Expand Down
7 changes: 2 additions & 5 deletions ext/mri/blowfish.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* $OpenBSD: blowfish.c,v 1.19 2015/09/11 09:18:27 guenther Exp $ */
/* $OpenBSD: blowfish.c,v 1.20 2021/11/29 01:04:45 djm Exp $ */
/*
* Blowfish block cipher for OpenBSD
* Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
Expand All @@ -14,10 +14,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Niels Provos.
* 4. The name of the author may not be used to endorse or promote products
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
Expand Down
2 changes: 1 addition & 1 deletion ext/mri/includes.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ typedef uint32_t u_int32_t;
void explicit_bzero(void *p, size_t n);
int bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt, size_t saltlen,
uint8_t *key, size_t keylen, unsigned int rounds);
void bcrypt_hash(const uint8_t *sha2pass, const uint8_t *sha2salt, uint8_t *out);
void bcrypt_hash(uint8_t *sha2pass, uint8_t *sha2salt, uint8_t *out);

#define BCRYPT_WORDS 8
#define BCRYPT_HASHSIZE (BCRYPT_WORDS * 4)
Expand Down
Loading