bcrypt.c
Go to the documentation of this file.
1 /**
2  * @file bcrypt.c
3  * @brief bcrypt password hashing function
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2019 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneCrypto Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 1.9.6
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL CRYPTO_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/crypto.h"
36 #include "kdf/bcrypt.h"
37 #include "cipher_mode/ecb.h"
38 #include "encoding/radix64.h"
39 
40 //Check crypto library configuration
41 #if (BCRYPT_SUPPORT == ENABLED)
42 
43 
44 /**
45  * @brief Password hashing function
46  * @param[in] prngAlgo PRNG algorithm
47  * @param[in] prngContext Pointer to the PRNG context
48  * @param[in] cost Key expansion iteration count as a power of two
49  * @param[in] password NULL-terminated password to be encoded
50  * @param[out] hash NULL-terminated hash string
51  * @param[out] hashLen Length of the hash string (optional parameter)
52  * @return Error code
53  **/
54 
55 error_t bcryptHashPassword(const PrngAlgo *prngAlgo, void *prngContext,
56  uint_t cost, const char_t *password, char_t *hash, size_t *hashLen)
57 {
58  error_t error;
59  uint8_t salt[16];
60 
61  //Check parameters
62  if(prngAlgo == NULL || prngContext == NULL || password == NULL ||
63  hash == NULL)
64  {
66  }
67 
68  //Generate a 16-byte random salt
69  error = prngAlgo->read(prngContext, salt, sizeof(salt));
70 
71  //Check status code
72  if(!error)
73  {
74  //Hash the password using bcrypt algorithm
75  error = bcrypt(cost, salt, password, hash, hashLen);
76  }
77 
78  //Return status code
79  return error;
80 }
81 
82 
83 /**
84  * @brief Password verification function
85  * @param[in] password NULL-terminated password to be checked
86  * @param[in] hash NULL-terminated hash string
87  * @return Error code
88  **/
89 
90 error_t bcryptVerifyPassword(const char_t *password, const char_t *hash)
91 {
92  error_t error;
93  size_t i;
94  size_t n;
95  uint_t cost;
96  uint8_t mask;
97  char_t *p;
98  uint8_t salt[16];
100 
101  //Check parameters
102  if(password == NULL || hash == NULL)
104 
105  //Check the length of the hash string
107  return ERROR_INVALID_PASSWORD;
108 
109  //bcrypt uses the $2a$ prefix in the hash string
110  if(cryptoMemcmp(hash, "$2a$", 4))
111  return ERROR_INVALID_PASSWORD;
112 
113  //Parse cost parameter
114  cost = cryptoStrtoul(hash + 4, &p, 10);
115 
116  //Malformed hash string?
117  if(p != (hash + 6) || *p != '$')
118  return ERROR_INVALID_PASSWORD;
119 
120  //Check the value of the cost parameter
121  if(cost < BCRYPT_MIN_COST || cost > BCRYPT_MAX_COST)
122  return ERROR_INVALID_PASSWORD;
123 
124  //Parse salt parameter
125  error = radix64Decode(hash + 7, 22, salt, &n);
126  //Any error to report?
127  if(error)
128  return error;
129 
130  //Hash the password using bcrypt algorithm
131  error = bcrypt(cost, salt, password, temp, &n);
132  //Any error to report?
133  if(error)
134  return error;
135 
136  //The calculated string is bitwise compared to the hash string. The
137  //password is correct if and only if the strings match
138  for(mask = 0, i = 0; i < BCRYPT_HASH_STRING_LEN; i++)
139  {
140  mask |= temp[i] ^ hash[i];
141  }
142 
143  //Return status code
144  return (mask == 0) ? NO_ERROR : ERROR_INVALID_PASSWORD;
145 }
146 
147 
148 /**
149  * @brief bcrypt algorithm
150  * @param[in] cost Key expansion iteration count as a power of two
151  * @param[in] salt Random salt (16 bytes)
152  * @param[in] password NULL-terminated password to be encoded
153  * @param[out] hash NULL-terminated hash string
154  * @param[out] hashLen Length of the hash string (optional parameter)
155  * @return Error code
156  **/
157 
158 error_t bcrypt(uint_t cost, const uint8_t *salt, const char_t *password,
159  char_t *hash, size_t *hashLen)
160 {
161  error_t error;
162  uint_t i;
163  size_t n;
164  size_t length;
165  BlowfishContext *context;
166  uint8_t buffer[24];
167 
168  //Check parameters
169  if(salt == NULL || password == NULL || hash == NULL)
171 
172  //Allocate a memory buffer to hold the Blowfish context
173  context = cryptoAllocMem(sizeof(BlowfishContext));
174 
175  //Successful memory allocation?
176  if(context != NULL)
177  {
178  //Calculate the length of the password (including the NULL-terminator byte)
179  length = cryptoStrlen(password) + 1;
180 
181  //The key setup begins with a modified form of the standard Blowfish key
182  //setup, in which both the salt and password are used to set all subkeys
183  error = eksBlowfishSetup(context, cost, salt, 16, password, length);
184 
185  //Check status code
186  if(!error)
187  {
188  //Initialize plaintext
189  cryptoMemcpy(buffer, "OrpheanBeholderScryDoubt", 24);
190 
191  //Repeatedly encrypt the text "OrpheanBeholderScryDoubt" 64 times
192  for(i = 0; i < 64 && !error; i++)
193  {
194  //Perform encryption using Blowfish in ECB mode
195  error = ecbEncrypt(BLOWFISH_CIPHER_ALGO, context, buffer, buffer, 24);
196  }
197  }
198 
199  //Release Blowfish context
200  cryptoFreeMem(context);
201  }
202  else
203  {
204  //Failed to allocate memory
205  error = ERROR_OUT_OF_MEMORY;
206  }
207 
208  //Check status code
209  if(!error)
210  {
211  //bcrypt uses the $2a$ prefix in the hash string
212  length = cryptoSprintf(hash, "$2a$%02u$", cost);
213 
214  //Concatenate the salt and the ciphertext
215  radix64Encode(salt, 16, hash + length, &n);
216  length += n;
217  radix64Encode(buffer, 23, hash + length, &n);
218  length += n;
219 
220  //Return the length of the resulting hash string
221  if(hashLen != NULL)
222  {
223  *hashLen = length;
224  }
225  }
226 
227  //Return status code
228  return error;
229 }
230 
231 
232 /**
233  * @brief Expensive key setup
234  * @param[in] context Pointer to the Blowfish context
235  * @param[in] cost Key expansion iteration count as a power of 2
236  * @param[in] salt Random salt
237  * @param[in] saltLen Length of the random salt, in bytes
238  * @param[in] password NULL-terminated password to be encoded
239  * @param[in] passwordLen Length of the password, in bytes
240  * @return Error code
241  **/
242 
244  const uint8_t *salt, size_t saltLen, const char_t *password,
245  size_t passwordLen)
246 {
247  error_t error;
248  uint32_t i;
249  uint32_t n;
250 
251  //Check the value of the cost parameter
252  if(cost < BCRYPT_MIN_COST || cost > BCRYPT_MAX_COST)
254 
255  //The cost parameter specifies a key expansion iteration count as a power
256  //of two
257  n = 1U << cost;
258 
259  //Initialize Blowfish state
260  error = blowfishInitState(context);
261 
262  //Check status code
263  if(!error)
264  {
265  //Perform the first key expansion
266  error = blowfishExpandKey(context, salt, saltLen, (uint8_t *) password,
267  passwordLen);
268  }
269 
270  //Check status code
271  if(!error)
272  {
273  //Iterate as many times as desired
274  for(i = 0; i < n; i++)
275  {
276  //Perform key expansion with password
277  error = blowfishExpandKey(context, NULL, 0, (uint8_t *) password,
278  passwordLen);
279  //Any error to report?
280  if(error)
281  break;
282 
283  //Perform key expansion with salt
284  error = blowfishExpandKey(context, NULL, 0, salt, saltLen);
285  //Any error to report?
286  if(error)
287  break;
288  }
289  }
290 
291  //Return status code
292  return error;
293 }
294 
295 #endif
uint8_t length
Definition: dtls_misc.h:149
Common interface for pseudo-random number generators.
Definition: crypto.h:1168
Radix64 encoding scheme.
#define BLOWFISH_CIPHER_ALGO
Definition: blowfish.h:40
PrngAlgoRead read
Definition: crypto.h:1176
bcrypt password hashing function
uint8_t p
Definition: ndp.h:298
#define cryptoSprintf(dest, format,...)
Definition: crypto.h:678
error_t bcrypt(uint_t cost, const uint8_t *salt, const char_t *password, char_t *hash, size_t *hashLen)
bcrypt algorithm
Definition: bcrypt.c:158
error_t eksBlowfishSetup(BlowfishContext *context, uint_t cost, const uint8_t *salt, size_t saltLen, const char_t *password, size_t passwordLen)
Expensive key setup.
Definition: bcrypt.c:243
error_t blowfishInitState(BlowfishContext *context)
Blowfish state initialization.
Definition: blowfish.c:256
error_t blowfishExpandKey(BlowfishContext *context, const uint8_t *salt, size_t saltLen, const uint8_t *key, size_t keyLen)
Key expansion.
Definition: blowfish.c:290
error_t radix64Decode(const char_t *input, size_t inputLen, void *output, size_t *outputLen)
Radix64 decoding algorithm.
Definition: radix64.c:184
Electronic Codebook (ECB) mode.
#define cryptoStrtoul(s, endptr, base)
Definition: crypto.h:684
#define BCRYPT_HASH_STRING_LEN
Definition: bcrypt.h:53
Invalid parameter.
Definition: error.h:47
#define cryptoStrlen(s)
Definition: crypto.h:660
error_t
Error codes.
Definition: error.h:42
General definitions for cryptographic algorithms.
uint8_t mask
Definition: web_socket.h:317
#define cryptoMemcmp(p1, p2, length)
Definition: crypto.h:654
error_t bcryptHashPassword(const PrngAlgo *prngAlgo, void *prngContext, uint_t cost, const char_t *password, char_t *hash, size_t *hashLen)
Password hashing function.
Definition: bcrypt.c:55
error_t ecbEncrypt(const CipherAlgo *cipher, void *context, const uint8_t *p, uint8_t *c, size_t length)
ECB encryption.
Definition: ecb.c:60
uint8_t hash
Definition: tls.h:1369
char char_t
Definition: compiler_port.h:43
error_t bcryptVerifyPassword(const char_t *password, const char_t *hash)
Password verification function.
Definition: bcrypt.c:90
uint8_t n
#define cryptoMemcpy(dest, src, length)
Definition: crypto.h:642
Blowfish algorithm context.
Definition: blowfish.h:52
#define cryptoFreeMem(p)
Definition: crypto.h:630
#define cryptoAllocMem(size)
Definition: crypto.h:625
void radix64Encode(const void *input, size_t inputLen, char_t *output, size_t *outputLen)
Radix64 encoding algorithm.
Definition: radix64.c:72
unsigned int uint_t
Definition: compiler_port.h:45
#define BCRYPT_MAX_COST
Definition: bcrypt.h:47
Success.
Definition: error.h:44