Skip to content

Commit 9423f3b

Browse files
committed
add bcrypt
1 parent 5e30d35 commit 9423f3b

File tree

8 files changed

+414
-2
lines changed

8 files changed

+414
-2
lines changed

doc/crypt.tex

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7002,7 +7002,7 @@ \subsection{PKCS \#5}
70027002

70037003
The OpenSSL project implemented an extension to Algorithm One that allows for arbitrary keylengths; we have a compatible implementation described below.
70047004

7005-
\subsection{Algorithm One}
7005+
\subsubsection{Algorithm One}
70067006
Algorithm One accepts as input a password, an 8--byte salt, and an iteration counter. The iteration counter is meant to act as delay for
70077007
people trying to brute force guess the password. The higher the iteration counter the longer the delay. This algorithm also requires a hash
70087008
algorithm and produces an output no longer than the output of the hash.
@@ -7035,7 +7035,7 @@ \subsection{Algorithm One}
70357035
unsigned long *outlen)
70367036
\end{alltt}
70377037
As above, but we generate as many bytes as requested in outlen per the OpenSSL extension to Algorithm One. If you are trying to be compatible with OpenSSL's EVP\_BytesToKey() or the "openssl enc" command line (or variants such as perl's Crypt::CBC), then use this function with MD5 as your hash (ick!) and iteration\_count=1 (double-ick!!).
7038-
\subsection{Algorithm Two}
7038+
\subsubsection{Algorithm Two}
70397039

70407040
Algorithm Two is the recommended algorithm for this task. It allows variable length salts, and can produce outputs larger than the
70417041
hash functions output. As such, it can easily be used to derive session keys for ciphers and MACs as well initialization vectors as required
@@ -7091,6 +7091,35 @@ \subsection{Algorithm Two}
70917091
}
70927092
\end{verbatim}
70937093

7094+
7095+
\subsection{bcrypt}
7096+
\index{bcrypt}
7097+
7098+
bcrypt is a password hashing function, similar to PKCS \#5, but it is based on the blowfish symmetric cipher.
7099+
It is widely used in e.g. OpenBSD as default password hash algorithm, or in encrypted OpenSSH key files.
7100+
7101+
This implementation provides the PBKDF version as used in OpenSSH key files.
7102+
7103+
The OpenBSD implementation is fixed to SHA512 as hashing algorithm, but this generalized implementation works with any hashing algorithm.
7104+
7105+
To hash a password with the bcrypt PBKDF algorithm, the following API function is provided.
7106+
7107+
\index{bcrypt()}
7108+
\begin{alltt}
7109+
int bcrypt_pbkdf_openbsd(const char *password, unsigned long password_len,
7110+
const unsigned char *salt, unsigned long salt_len,
7111+
unsigned int rounds, int hash_idx,
7112+
unsigned char *out, unsigned long *outlen);
7113+
\end{alltt}
7114+
7115+
The \textit{password} parameter is the utf-8 encoded user password of length \textit{password\_len}.
7116+
The \textit{salt} parameter is a pointer to the array of octets of length \textit{salt\_len} containing the salt.
7117+
The \textit{rounds} parameter defines the number of iterations of the expensive key setup that shall be executed.
7118+
The \textit{hash\_idx} parameter defines the hash algorithm that shall be used.
7119+
The \textit{out} parameter shall be a pointer to a buffer of at least 32 octets,
7120+
where \textit{outlen} contains the available buffer size on input and the written size after the invocation.
7121+
7122+
70947123
\mysection{PKCS \#8}
70957124
\index{PKCS \#8}
70967125

@@ -7127,6 +7156,7 @@ \subsection{Algorithm Two}
71277156
The PKCS \#8 import has no direct API endpoints, but it is available through Public Key Algorithm-specific
71287157
\textit{pkaX\_import\_pkcs8()} functions.
71297158

7159+
71307160
\mysection{Key Derviation Functions}
71317161
\subsection{HKDF}
71327162
\index{HKDF}

src/headers/tomcrypt_custom.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,12 @@
490490
/* Base16/hex encoding/decoding */
491491
#define LTC_BASE16
492492

493+
#define LTC_BCRYPT
494+
495+
#ifndef LTC_BCRYPT_DEFAULT_ROUNDS
496+
#define LTC_BCRYPT_DEFAULT_ROUNDS 10
497+
#endif
498+
493499
/* Keep LTC_NO_HKDF for compatibility reasons
494500
* superseeded by LTC_NO_MISC*/
495501
#ifndef LTC_NO_HKDF
@@ -601,6 +607,10 @@
601607
#error PK requires ASN.1 DER functionality, make sure LTC_DER is enabled
602608
#endif
603609

610+
#if defined(LTC_BCRYPT) && !defined(LTC_BLOWFISH)
611+
#error LTC_BCRYPT requires LTC_BLOWFISH
612+
#endif
613+
604614
#if defined(LTC_CHACHA20POLY1305_MODE) && (!defined(LTC_CHACHA) || !defined(LTC_POLY1305))
605615
#error LTC_CHACHA20POLY1305_MODE requires LTC_CHACHA + LTC_POLY1305
606616
#endif

src/headers/tomcrypt_misc.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ int base16_decode(const char *in, unsigned long inlen,
5959
unsigned char *out, unsigned long *outlen);
6060
#endif
6161

62+
#ifdef LTC_BCRYPT
63+
int bcrypt_pbkdf_openbsd(const char *password, unsigned long password_len,
64+
const unsigned char *salt, unsigned long salt_len,
65+
unsigned int rounds, int hash_idx,
66+
unsigned char *out, unsigned long *outlen);
67+
#endif
68+
6269
/* ===> LTC_HKDF -- RFC5869 HMAC-based Key Derivation Function <=== */
6370
#ifdef LTC_HKDF
6471

src/misc/bcrypt/bcrypt.c

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/* LibTomCrypt, modular cryptographic library -- Tom St Denis
2+
*
3+
* LibTomCrypt is a library that provides various cryptographic
4+
* algorithms in a highly modular and flexible manner.
5+
*
6+
* The library is free for all purposes without any express
7+
* guarantee it works.
8+
*/
9+
#include "tomcrypt_private.h"
10+
11+
/**
12+
@file bcrypt.c
13+
bcrypt pbkdf, Steffen Jaeckel
14+
*/
15+
#ifdef LTC_BCRYPT
16+
17+
#define BCRYPT_WORDS 8
18+
#define BCRYPT_HASHSIZE (BCRYPT_WORDS * 4)
19+
20+
static int _bcrypt_hash(const unsigned char *pt,
21+
const unsigned char *pass, unsigned long passlen,
22+
const unsigned char *salt, unsigned long saltlen,
23+
unsigned char *out, unsigned long *outlen)
24+
{
25+
symmetric_key key;
26+
int err, n;
27+
ulong32 ct[BCRYPT_WORDS];
28+
29+
if ((err = blowfish_setup_with_data(pass, passlen, salt, saltlen, &key)) != CRYPT_OK) {
30+
return err;
31+
}
32+
for (n = 0; n < 64; ++n) {
33+
if ((err = blowfish_expand(salt, saltlen, NULL, 0, &key)) != CRYPT_OK) {
34+
return err;
35+
}
36+
if ((err = blowfish_expand(pass, passlen, NULL, 0, &key)) != CRYPT_OK) {
37+
return err;
38+
}
39+
}
40+
41+
for (n = 0; n < BCRYPT_WORDS; ++n) {
42+
LOAD32H(ct[n], &pt[n*4]);
43+
}
44+
45+
for (n = 0; n < 64; ++n) {
46+
blowfish_enc(ct, BCRYPT_WORDS/2, &key);
47+
}
48+
49+
for (n = 0; n < BCRYPT_WORDS; ++n) {
50+
STORE32L(ct[n], &out[4 * n]);
51+
}
52+
*outlen = sizeof(ct);
53+
#ifdef LTC_CLEAN_STACK
54+
zeromem(&key, sizeof(key));
55+
zeromem(ct, sizeof(ct));
56+
#endif
57+
58+
return CRYPT_OK;
59+
}
60+
61+
static int _bcrypt_pbkdf_hash(const unsigned char *pass, unsigned long passlen,
62+
const unsigned char *salt, unsigned long saltlen,
63+
unsigned char *out, unsigned long *outlen)
64+
{
65+
const unsigned char pt[] = "OxychromaticBlowfishSwatDynamite";
66+
return _bcrypt_hash(pt, pass, passlen, salt, saltlen, out, outlen);
67+
}
68+
69+
/**
70+
Compatible to bcrypt_pbkdf() as provided in OpenBSD
71+
@param password The input password (or key)
72+
@param password_len The length of the password (octets)
73+
@param salt The salt (or nonce)
74+
@param salt_len The length of the salt (octets)
75+
@param rounds # of iterations desired [read specs for more]
76+
@param hash_idx The index of the hash desired
77+
@param out [out] The destination for this algorithm
78+
@param outlen [in/out] The desired size of the algorithm output
79+
@return CRYPT_OK if successful
80+
*/
81+
int bcrypt_pbkdf_openbsd(const char *password, unsigned long password_len,
82+
const unsigned char *salt, unsigned long salt_len,
83+
unsigned int rounds, int hash_idx,
84+
unsigned char *out, unsigned long *outlen)
85+
{
86+
int err;
87+
ulong32 blkno;
88+
unsigned long left, itts, x, y, hashed_pass_len, step_size, steps, dest, used_rounds;
89+
unsigned char *buf[3], blkbuf[4];
90+
unsigned char *hashed_pass;
91+
92+
LTC_ARGCHK(password != NULL);
93+
LTC_ARGCHK(salt != NULL);
94+
LTC_ARGCHK(out != NULL);
95+
LTC_ARGCHK(outlen != NULL);
96+
97+
if ((password_len == 0) || (salt_len == 0) || (*outlen == 0)) {
98+
return CRYPT_INVALID_ARG;
99+
}
100+
/* test hash IDX */
101+
if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
102+
return err;
103+
}
104+
/* set default value for rounds if not given */
105+
if (rounds == 0) {
106+
used_rounds = LTC_BCRYPT_DEFAULT_ROUNDS;
107+
} else {
108+
used_rounds = rounds;
109+
}
110+
111+
buf[0] = XMALLOC(MAXBLOCKSIZE * 3);
112+
hashed_pass = XMALLOC(MAXBLOCKSIZE);
113+
if (buf[0] == NULL || hashed_pass == NULL) {
114+
if (hashed_pass != NULL) {
115+
XFREE(hashed_pass);
116+
}
117+
if (buf[0] != NULL) {
118+
XFREE(buf[0]);
119+
}
120+
return CRYPT_MEM;
121+
}
122+
/* buf[1] points to the second block of MAXBLOCKSIZE bytes */
123+
buf[1] = buf[0] + MAXBLOCKSIZE;
124+
buf[2] = buf[1] + MAXBLOCKSIZE;
125+
126+
step_size = (*outlen + BCRYPT_HASHSIZE - 1) / BCRYPT_HASHSIZE;
127+
steps = (*outlen + step_size - 1) / step_size;
128+
129+
hashed_pass_len = MAXBLOCKSIZE;
130+
if ((err = hash_memory(hash_idx, (unsigned char*)password, password_len, hashed_pass, &hashed_pass_len)) != CRYPT_OK) {
131+
goto LBL_ERR;
132+
}
133+
134+
left = *outlen;
135+
blkno = 0;
136+
while (left != 0) {
137+
/* increment and store current block number */
138+
++blkno;
139+
STORE32H(blkno, blkbuf);
140+
141+
/* process block number blkno */
142+
zeromem(buf[0], MAXBLOCKSIZE*2);
143+
144+
x = MAXBLOCKSIZE;
145+
if ((err = hash_memory_multi(hash_idx, buf[0], &x,
146+
salt, salt_len,
147+
blkbuf, 4,
148+
NULL, 0)) != CRYPT_OK) {
149+
goto LBL_ERR;
150+
}
151+
y = MAXBLOCKSIZE;
152+
if ((err = _bcrypt_pbkdf_hash(hashed_pass, hashed_pass_len, buf[0], x, buf[1], &y)) != CRYPT_OK) {
153+
goto LBL_ERR;
154+
}
155+
XMEMCPY(buf[2], buf[1], y);
156+
157+
/* now compute repeated and XOR it in buf[2] */
158+
for (itts = 1; itts < used_rounds; ++itts) {
159+
x = MAXBLOCKSIZE;
160+
if ((err = hash_memory(hash_idx, buf[1], y, buf[0], &x)) != CRYPT_OK) {
161+
goto LBL_ERR;
162+
}
163+
y = MAXBLOCKSIZE;
164+
if ((err = _bcrypt_pbkdf_hash(hashed_pass, hashed_pass_len, buf[0], x, buf[1], &y)) != CRYPT_OK) {
165+
goto LBL_ERR;
166+
}
167+
for (x = 0; x < y; x++) {
168+
buf[2][x] ^= buf[1][x];
169+
}
170+
}
171+
172+
/* now emit upto `steps` bytes of buf[2] to output */
173+
steps = MIN(steps, left);
174+
for (y = 0; y < steps; ++y) {
175+
dest = y * step_size + (blkno - 1);
176+
if (dest >= *outlen)
177+
break;
178+
out[dest] = buf[2][y];
179+
}
180+
left -= y;
181+
}
182+
183+
err = CRYPT_OK;
184+
LBL_ERR:
185+
#ifdef LTC_CLEAN_STACK
186+
zeromem(buf[0], MAXBLOCKSIZE*3);
187+
zeromem(hashed_pass, MAXBLOCKSIZE);
188+
#endif
189+
190+
XFREE(hashed_pass);
191+
XFREE(buf[0]);
192+
193+
return err;
194+
}
195+
196+
#endif
197+
198+
199+
/* ref: $Format:%D$ */
200+
/* git commit: $Format:%H$ */
201+
/* commit time: $Format:%ai$ */

src/misc/crypt/crypt.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,10 @@ const char *crypt_build_settings =
434434
#if defined(LTC_BASE16)
435435
" BASE16 "
436436
#endif
437+
#if defined(LTC_BCRYPT)
438+
" BCRYPT "
439+
" " NAME_VALUE(LTC_BCRYPT_DEFAULT_ROUNDS) " "
440+
#endif
437441
#if defined(LTC_CRC32)
438442
" CRC32 "
439443
#endif

0 commit comments

Comments
 (0)