mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-05 15:00:06 +00:00
232 lines
5.1 KiB
C
232 lines
5.1 KiB
C
/*
|
|
* bcrypt wrapper library
|
|
*
|
|
* Written in 2011, 2013, 2014, 2015 by Ricardo Garcia <r@rg3.name>
|
|
*
|
|
* To the extent possible under law, the author(s) have dedicated all copyright
|
|
* and related and neighboring rights to this software to the public domain
|
|
* worldwide. This software is distributed without any warranty.
|
|
*
|
|
* You should have received a copy of the CC0 Public Domain Dedication along
|
|
* with this software. If not, see
|
|
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
*/
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#ifdef _WIN32
|
|
#elif _WIN64
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
#include <errno.h>
|
|
|
|
#ifdef _WIN32 || _WIN64
|
|
// On windows we need to generate random bytes differently.
|
|
#define BCRYPT_HASHSIZE 60
|
|
|
|
#include "bcrypt.h"
|
|
|
|
#include <windows.h>
|
|
#include <wincrypt.h> /* CryptAcquireContext, CryptGenRandom */
|
|
#else
|
|
#include "bcrypt.h"
|
|
#include "ow-crypt.h"
|
|
#endif
|
|
|
|
#define RANDBYTES (16)
|
|
|
|
static int try_close(int fd)
|
|
{
|
|
int ret;
|
|
for (;;) {
|
|
errno = 0;
|
|
ret = close(fd);
|
|
if (ret == -1 && errno == EINTR)
|
|
continue;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
static int try_read(int fd, char *out, size_t count)
|
|
{
|
|
size_t total;
|
|
ssize_t partial;
|
|
|
|
total = 0;
|
|
while (total < count)
|
|
{
|
|
for (;;) {
|
|
errno = 0;
|
|
partial = read(fd, out + total, count - total);
|
|
if (partial == -1 && errno == EINTR)
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
if (partial < 1)
|
|
return -1;
|
|
|
|
total += partial;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* This is a best effort implementation. Nothing prevents a compiler from
|
|
* optimizing this function and making it vulnerable to timing attacks, but
|
|
* this method is commonly used in crypto libraries like NaCl.
|
|
*
|
|
* Return value is zero if both strings are equal and nonzero otherwise.
|
|
*/
|
|
static int timing_safe_strcmp(const char *str1, const char *str2)
|
|
{
|
|
const unsigned char *u1;
|
|
const unsigned char *u2;
|
|
int ret;
|
|
int i;
|
|
|
|
int len1 = strlen(str1);
|
|
int len2 = strlen(str2);
|
|
|
|
/* In our context both strings should always have the same length
|
|
* because they will be hashed passwords. */
|
|
if (len1 != len2)
|
|
return 1;
|
|
|
|
/* Force unsigned for bitwise operations. */
|
|
u1 = (const unsigned char *)str1;
|
|
u2 = (const unsigned char *)str2;
|
|
|
|
ret = 0;
|
|
for (i = 0; i < len1; ++i)
|
|
ret |= (u1[i] ^ u2[i]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int bcrypt_gensalt(int factor, char salt[BCRYPT_HASHSIZE])
|
|
{
|
|
int fd;
|
|
char input[RANDBYTES];
|
|
int workf;
|
|
char *aux;
|
|
|
|
// Note: Windows does not have /dev/urandom sadly.
|
|
#ifdef _WIN32 || _WIN64
|
|
HCRYPTPROV p;
|
|
ULONG i;
|
|
|
|
// Acquire a crypt context for generating random bytes.
|
|
if (CryptAcquireContext(&p, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) == FALSE) {
|
|
return 1;
|
|
}
|
|
|
|
if (CryptGenRandom(p, RANDBYTES, (BYTE*)input) == FALSE) {
|
|
return 2;
|
|
}
|
|
|
|
if (CryptReleaseContext(p, 0) == FALSE) {
|
|
return 3;
|
|
}
|
|
#else
|
|
// Get random bytes on Unix/Linux.
|
|
fd = open("/dev/urandom", O_RDONLY);
|
|
if (fd == -1)
|
|
return 1;
|
|
|
|
if (try_read(fd, input, RANDBYTES) != 0) {
|
|
if (try_close(fd) != 0)
|
|
return 4;
|
|
return 2;
|
|
}
|
|
|
|
if (try_close(fd) != 0)
|
|
return 3;
|
|
#endif
|
|
|
|
/* Generate salt. */
|
|
workf = (factor < 4 || factor > 31)?12:factor;
|
|
aux = crypt_gensalt_rn("$2a$", workf, input, RANDBYTES,
|
|
salt, BCRYPT_HASHSIZE);
|
|
return (aux == NULL)?5:0;
|
|
}
|
|
|
|
int bcrypt_hashpw(const char *passwd, const char salt[BCRYPT_HASHSIZE], char hash[BCRYPT_HASHSIZE])
|
|
{
|
|
char *aux;
|
|
aux = crypt_rn(passwd, salt, hash, BCRYPT_HASHSIZE);
|
|
return (aux == NULL)?1:0;
|
|
}
|
|
|
|
int bcrypt_checkpw(const char *passwd, const char hash[BCRYPT_HASHSIZE])
|
|
{
|
|
int ret;
|
|
char outhash[BCRYPT_HASHSIZE];
|
|
|
|
ret = bcrypt_hashpw(passwd, hash, outhash);
|
|
if (ret != 0)
|
|
return -1;
|
|
|
|
return timing_safe_strcmp(hash, outhash);
|
|
}
|
|
|
|
#ifdef TEST_BCRYPT
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
int main(void)
|
|
{
|
|
clock_t before;
|
|
clock_t after;
|
|
char salt[BCRYPT_HASHSIZE];
|
|
char hash[BCRYPT_HASHSIZE];
|
|
int ret;
|
|
|
|
const char pass[] = "hi,mom";
|
|
const char hash1[] = "$2a$10$VEVmGHy4F4XQMJ3eOZJAUeb.MedU0W10pTPCuf53eHdKJPiSE8sMK";
|
|
const char hash2[] = "$2a$10$3F0BVk5t8/aoS.3ddaB3l.fxg5qvafQ9NybxcpXLzMeAt.nVWn.NO";
|
|
|
|
ret = bcrypt_gensalt(12, salt);
|
|
assert(ret == 0);
|
|
printf("Generated salt: %s\n", salt);
|
|
before = clock();
|
|
ret = bcrypt_hashpw("testtesttest", salt, hash);
|
|
assert(ret == 0);
|
|
after = clock();
|
|
printf("Hashed password: %s\n", hash);
|
|
printf("Time taken: %f seconds\n",
|
|
(double)(after - before) / CLOCKS_PER_SEC);
|
|
|
|
ret = bcrypt_hashpw(pass, hash1, hash);
|
|
assert(ret == 0);
|
|
printf("First hash check: %s\n", (strcmp(hash1, hash) == 0)?"OK":"FAIL");
|
|
ret = bcrypt_hashpw(pass, hash2, hash);
|
|
assert(ret == 0);
|
|
printf("Second hash check: %s\n", (strcmp(hash2, hash) == 0)?"OK":"FAIL");
|
|
|
|
before = clock();
|
|
ret = (bcrypt_checkpw(pass, hash1) == 0);
|
|
after = clock();
|
|
printf("First hash check with bcrypt_checkpw: %s\n", ret?"OK":"FAIL");
|
|
printf("Time taken: %f seconds\n",
|
|
(double)(after - before) / CLOCKS_PER_SEC);
|
|
|
|
before = clock();
|
|
ret = (bcrypt_checkpw(pass, hash2) == 0);
|
|
after = clock();
|
|
printf("Second hash check with bcrypt_checkpw: %s\n", ret?"OK":"FAIL");
|
|
printf("Time taken: %f seconds\n",
|
|
(double)(after - before) / CLOCKS_PER_SEC);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|