From ea84e39743fa1c071cf36e845cb1d4e2949d0f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Sat, 13 Jun 2026 11:04:13 +0200 Subject: [PATCH] =?UTF-8?q?sync=20bcrypt=5Fpbkdf.c=20(v1.13=E2=86=92v1.17)?= =?UTF-8?q?=20and=20blowfish.c=20(v1.19=E2=86=92v1.20)=20from=20OpenBSD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Key changes in bcrypt_pbkdf.c: - switch SHA512 to crypto_hash_sha512() (simpler one-call API) - heap-allocate countsalt to safely handle any salt length - add saltlen > 1<<20 DoS guard - fix blf_enc call: sizeof(cdata)/sizeof(uint64_t) -> BCRYPT_WORDS/2 - zero tmpout in cleanup (was missing) blowfish.c: drop superseded advertising clause from license --- README.md | 10 +++++- ext/mri/bcrypt_pbkdf.c | 66 ++++++++++++++++++-------------------- ext/mri/bcrypt_pbkdf_ext.c | 2 +- ext/mri/blowfish.c | 7 ++-- ext/mri/includes.h | 2 +- 5 files changed, 45 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 6d03a86..1e771ca 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/ext/mri/bcrypt_pbkdf.c b/ext/mri/bcrypt_pbkdf.c index 55c83e2..b99a037 100644 --- a/ext/mri/bcrypt_pbkdf.c +++ b/ext/mri/bcrypt_pbkdf.c @@ -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 * @@ -15,14 +15,13 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include "includes.h" +#include "crypto_api.h" -#include -#include -#include "blf.h" -#include "sha2.h" -#include -#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)) @@ -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.) @@ -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; @@ -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++) { @@ -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; @@ -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]; @@ -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; -} \ No newline at end of file +} diff --git a/ext/mri/bcrypt_pbkdf_ext.c b/ext/mri/bcrypt_pbkdf_ext.c index 2b06f3a..afa06dd 100644 --- a/ext/mri/bcrypt_pbkdf_ext.c +++ b/ext/mri/bcrypt_pbkdf_ext.c @@ -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)); } diff --git a/ext/mri/blowfish.c b/ext/mri/blowfish.c index 5d2137d..03d0a06 100644 --- a/ext/mri/blowfish.c +++ b/ext/mri/blowfish.c @@ -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 @@ -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 diff --git a/ext/mri/includes.h b/ext/mri/includes.h index a2a6514..569adbb 100644 --- a/ext/mri/includes.h +++ b/ext/mri/includes.h @@ -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)