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-2024 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 2.4.0
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 "encoding/radix64.h"
38 
39 //Check crypto library configuration
40 #if (BCRYPT_SUPPORT == ENABLED)
41 
42 
43 /**
44  * @brief Password hashing function
45  * @param[in] prngAlgo PRNG algorithm
46  * @param[in] prngContext Pointer to the PRNG context
47  * @param[in] cost Key expansion iteration count as a power of two
48  * @param[in] password NULL-terminated password to be encoded
49  * @param[out] hash NULL-terminated hash string
50  * @param[out] hashLen Length of the hash string (optional parameter)
51  * @return Error code
52  **/
53 
54 error_t bcryptHashPassword(const PrngAlgo *prngAlgo, void *prngContext,
55  uint_t cost, const char_t *password, char_t *hash, size_t *hashLen)
56 {
57  error_t error;
58  uint8_t salt[16];
59 
60  //Check parameters
61  if(prngAlgo == NULL || prngContext == NULL || password == NULL ||
62  hash == NULL)
63  {
65  }
66 
67  //Generate a 16-byte random salt
68  error = prngAlgo->read(prngContext, salt, sizeof(salt));
69 
70  //Check status code
71  if(!error)
72  {
73  //Hash the password using bcrypt algorithm
74  error = bcrypt(cost, salt, password, hash, hashLen);
75  }
76 
77  //Return status code
78  return error;
79 }
80 
81 
82 /**
83  * @brief Password verification function
84  * @param[in] password NULL-terminated password to be checked
85  * @param[in] hash NULL-terminated hash string
86  * @return Error code
87  **/
88 
89 error_t bcryptVerifyPassword(const char_t *password, const char_t *hash)
90 {
91  error_t error;
92  size_t i;
93  size_t n;
94  uint_t cost;
95  uint8_t mask;
96  char_t *p;
97  uint8_t salt[16];
99 
100  //Check parameters
101  if(password == NULL || hash == NULL)
103 
104  //Check the length of the hash string
105  if(osStrlen(hash) != BCRYPT_HASH_STRING_LEN)
106  return ERROR_INVALID_PASSWORD;
107 
108  //bcrypt uses the $2a$ prefix in the hash string
109  if(osMemcmp(hash, "$2a$", 4))
110  return ERROR_INVALID_PASSWORD;
111 
112  //Parse cost parameter
113  cost = osStrtoul(hash + 4, &p, 10);
114 
115  //Malformed hash string?
116  if(p != (hash + 6) || *p != '$')
117  return ERROR_INVALID_PASSWORD;
118 
119  //Check the value of the cost parameter
120  if(cost < BCRYPT_MIN_COST || cost > BCRYPT_MAX_COST)
121  return ERROR_INVALID_PASSWORD;
122 
123  //Parse salt parameter
124  error = radix64Decode(hash + 7, 22, salt, &n);
125  //Any error to report?
126  if(error)
127  return error;
128 
129  //Hash the password using bcrypt algorithm
130  error = bcrypt(cost, salt, password, temp, &n);
131  //Any error to report?
132  if(error)
133  return error;
134 
135  //The calculated string is bitwise compared to the hash string. The
136  //password is correct if and only if the strings match
137  for(mask = 0, i = 0; i < BCRYPT_HASH_STRING_LEN; i++)
138  {
139  mask |= temp[i] ^ hash[i];
140  }
141 
142  //Return status code
143  return (mask == 0) ? NO_ERROR : ERROR_INVALID_PASSWORD;
144 }
145 
146 
147 /**
148  * @brief bcrypt algorithm
149  * @param[in] cost Key expansion iteration count as a power of two
150  * @param[in] salt Random salt (16 bytes)
151  * @param[in] password NULL-terminated password to be encoded
152  * @param[out] hash NULL-terminated hash string
153  * @param[out] hashLen Length of the hash string (optional parameter)
154  * @return Error code
155  **/
156 
157 error_t bcrypt(uint_t cost, const uint8_t *salt, const char_t *password,
158  char_t *hash, size_t *hashLen)
159 {
160  error_t error;
161  uint_t i;
162  size_t n;
163  size_t length;
164  uint8_t buffer[24];
165 #if (CRYPTO_STATIC_MEM_SUPPORT == DISABLED)
166  BlowfishContext *context;
167 #else
168  BlowfishContext context[1];
169 #endif
170 
171  //Check parameters
172  if(salt == NULL || password == NULL || hash == NULL)
174 
175 #if (CRYPTO_STATIC_MEM_SUPPORT == DISABLED)
176  //Allocate a memory buffer to hold the Blowfish context
177  context = cryptoAllocMem(sizeof(BlowfishContext));
178  //Failed to allocate memory?
179  if(context == NULL)
180  return ERROR_OUT_OF_MEMORY;
181 #endif
182 
183  //Calculate the length of the password (including the NULL-terminator byte)
184  length = osStrlen(password) + 1;
185 
186  //The key setup begins with a modified form of the standard Blowfish key
187  //setup, in which both the salt and password are used to set all subkeys
188  error = eksBlowfishSetup(context, cost, salt, 16, password, length);
189 
190  //Check status code
191  if(!error)
192  {
193  //Initialize plaintext
194  osMemcpy(buffer, "OrpheanBeholderScryDoubt", 24);
195 
196  //Repeatedly encrypt the text "OrpheanBeholderScryDoubt" 64 times
197  for(i = 0; i < 64; i++)
198  {
199  //Perform encryption using Blowfish in ECB mode
200  blowfishEncryptBlock(context, buffer, buffer);
201  blowfishEncryptBlock(context, buffer + 8, buffer + 8);
202  blowfishEncryptBlock(context, buffer + 16, buffer + 16);
203  }
204  }
205 
206  //Check status code
207  if(!error)
208  {
209  //bcrypt uses the $2a$ prefix in the hash string
210  length = osSprintf(hash, "$2a$%02u$", cost);
211 
212  //Concatenate the salt and the ciphertext
213  radix64Encode(salt, 16, hash + length, &n);
214  length += n;
215  radix64Encode(buffer, 23, hash + length, &n);
216  length += n;
217 
218  //Return the length of the resulting hash string
219  if(hashLen != NULL)
220  {
221  *hashLen = length;
222  }
223  }
224 
225 #if (CRYPTO_STATIC_MEM_SUPPORT == DISABLED)
226  //Release Blowfish context
227  cryptoFreeMem(context);
228 #endif
229 
230  //Return status code
231  return error;
232 }
233 
234 
235 /**
236  * @brief Expensive key setup
237  * @param[in] context Pointer to the Blowfish context
238  * @param[in] cost Key expansion iteration count as a power of 2
239  * @param[in] salt Random salt
240  * @param[in] saltLen Length of the random salt, in bytes
241  * @param[in] password NULL-terminated password to be encoded
242  * @param[in] passwordLen Length of the password, in bytes
243  * @return Error code
244  **/
245 
247  const uint8_t *salt, size_t saltLen, const char_t *password,
248  size_t passwordLen)
249 {
250  error_t error;
251  uint32_t i;
252  uint32_t n;
253 
254  //Check the value of the cost parameter
255  if(cost < BCRYPT_MIN_COST || cost > BCRYPT_MAX_COST)
257 
258  //The cost parameter specifies a key expansion iteration count as a power
259  //of two
260  n = 1U << cost;
261 
262  //Initialize Blowfish state
263  error = blowfishInitState(context);
264 
265  //Check status code
266  if(!error)
267  {
268  //Perform the first key expansion
269  error = blowfishExpandKey(context, salt, saltLen, (uint8_t *) password,
270  passwordLen);
271  }
272 
273  //Check status code
274  if(!error)
275  {
276  //Iterate as many times as desired
277  for(i = 0; i < n; i++)
278  {
279  //Perform key expansion with password
280  error = blowfishExpandKey(context, NULL, 0, (uint8_t *) password,
281  passwordLen);
282  //Any error to report?
283  if(error)
284  break;
285 
286  //Perform key expansion with salt
287  error = blowfishExpandKey(context, NULL, 0, salt, saltLen);
288  //Any error to report?
289  if(error)
290  break;
291  }
292  }
293 
294  //Return status code
295  return error;
296 }
297 
298 #endif
error_t bcryptVerifyPassword(const char_t *password, const char_t *hash)
Password verification function.
Definition: bcrypt.c:89
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:54
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:157
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:246
bcrypt password hashing function
#define BCRYPT_HASH_STRING_LEN
Definition: bcrypt.h:53
#define BCRYPT_MAX_COST
Definition: bcrypt.h:47
error_t blowfishInitState(BlowfishContext *context)
Blowfish state initialization.
Definition: blowfish.c:257
void blowfishEncryptBlock(BlowfishContext *context, const uint8_t *input, uint8_t *output)
Encrypt a 8-byte block using Blowfish algorithm.
Definition: blowfish.c:411
error_t blowfishExpandKey(BlowfishContext *context, const uint8_t *salt, size_t saltLen, const uint8_t *key, size_t keyLen)
Key expansion.
Definition: blowfish.c:291
unsigned int uint_t
Definition: compiler_port.h:50
char char_t
Definition: compiler_port.h:48
General definitions for cryptographic algorithms.
#define cryptoAllocMem(size)
Definition: crypto.h:765
#define cryptoFreeMem(p)
Definition: crypto.h:770
#define PrngAlgo
Definition: crypto.h:917
uint8_t n
error_t
Error codes.
Definition: error.h:43
@ ERROR_INVALID_PASSWORD
Definition: error.h:279
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
uint8_t p
Definition: ndp.h:300
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
#define osMemcmp(p1, p2, length)
Definition: os_port.h:153
#define osStrlen(s)
Definition: os_port.h:165
#define osSprintf(dest,...)
Definition: os_port.h:231
#define osStrtoul(s, endptr, base)
Definition: os_port.h:249
void radix64Encode(const void *input, size_t inputLen, char_t *output, size_t *outputLen)
Radix64 encoding algorithm.
Definition: radix64.c:72
error_t radix64Decode(const char_t *input, size_t inputLen, void *output, size_t *outputLen)
Radix64 decoding algorithm.
Definition: radix64.c:184
Radix64 encoding scheme.
Blowfish algorithm context.
Definition: blowfish.h:53
uint8_t length
Definition: tcp.h:368
uint8_t mask
Definition: web_socket.h:319