Skip to content

Commit

Permalink
Implement scrypt-based password hashing
Browse files Browse the repository at this point in the history
Signed-off-by: Axel Dörfler <axeld@pinc-software.de>
  • Loading branch information
i80and authored and axeld committed Jan 17, 2017
1 parent 108c68d commit f31b1a2
Show file tree
Hide file tree
Showing 20 changed files with 1,328 additions and 145 deletions.
9 changes: 9 additions & 0 deletions src/apps/aboutsystem/AboutSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1420,6 +1420,15 @@ AboutView::_CreateCreditsView()
_AddCopyrightsFromAttribute();
_AddPackageCreditEntries();

// scrypt
_AddPackageCredit(PackageCredit("scrypt")
.SetCopyright(B_TRANSLATE(COPYRIGHT_STRING "2009 Colin Percival"))
.SetLicense(kBSDTwoClause)
.SetURL("https://tarsnap.com/scrypt.html"));

_AddCopyrightsFromAttribute();
_AddPackageCreditEntries();

return new CropView(creditsScroller, 0, 1, 1, 1);
}

Expand Down
4 changes: 2 additions & 2 deletions src/bin/multiuser/passwd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ main(int argc, const char* const* argv)
memset(repeatedPassword, 0, sizeof(repeatedPassword));

// crypt it
encryptedPassword = crypt(password, user);
encryptedPassword = crypt(password, NULL);
memset(password, 0, sizeof(password));
}

Expand All @@ -182,7 +182,7 @@ main(int argc, const char* const* argv)
|| message.AddInt32("last changed", time(NULL)) != B_OK
|| message.AddString("password", "x") != B_OK
|| message.AddString("shadow password", encryptedPassword) != B_OK) {
fprintf(stderr, "Error: Out of memory!\n");
fprintf(stderr, "Error: Failed to construct message!\n");
exit(1);
}

Expand Down
39 changes: 1 addition & 38 deletions src/preferences/screensaver/PasswordWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,41 +145,6 @@ PasswordWindow::Update()
}


char*
PasswordWindow::_SanitizeSalt(const char* password)
{
char* salt;

uint8 length = strlen(password);

if (length < 2)
salt = new char[3];
else
salt = new char[length + 1];

uint8 i = 0;
uint8 j = 0;
for (; i < length; i++) {
if (isalnum(password[i]) || password[i] == '.' || password[i] == '/') {
salt[j] = password[i];
j++;
}
}

/*
* We need to pad the salt.
*/
while (j < 2) {
salt[j] = '.';
j++;
}

salt[j] = '\0';

return salt;
}


void
PasswordWindow::MessageReceived(BMessage* message)
{
Expand All @@ -196,9 +161,7 @@ PasswordWindow::MessageReceived(BMessage* message)
alert->Go();
break;
}
const char* salt = _SanitizeSalt(fPasswordControl->Text());
fSettings.SetPassword(crypt(fPasswordControl->Text(), salt));
delete[] salt;
fSettings.SetPassword(crypt(fPasswordControl->Text(), NULL));
} else
fSettings.SetPassword("");

Expand Down
1 change: 0 additions & 1 deletion src/preferences/screensaver/PasswordWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ class PasswordWindow : public BWindow {

private:
void _Setup();
char* _SanitizeSalt(const char* password);

BRadioButton* fUseCustom;
BRadioButton* fUseNetwork;
Expand Down
2 changes: 2 additions & 0 deletions src/system/libroot/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
$(librootNoDebugObjects)
[ TargetStaticLibsupc++ ]
[ TargetLibgcc ]
shared
;

# Use the standard libroot.so soname, so when the debug version is
Expand All @@ -99,6 +100,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
$(librootDebugObjects)
[ TargetStaticLibsupc++ ]
[ TargetLibgcc ]
shared
;

StaticLibrary [ MultiArchDefaultGristFiles libm.a ] : empty.c ;
Expand Down
11 changes: 9 additions & 2 deletions src/system/libroot/posix/crypt/Jamfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
SubDir HAIKU_TOP src system libroot posix crypt ;

UsePrivateHeaders shared ;
UsePrivateSystemHeaders ;

local architectureObject ;
for architectureObject in [ MultiArchSubDirSetup ] {
on $(architectureObject) {
Expand All @@ -11,8 +14,12 @@ for architectureObject in [ MultiArchSubDirSetup ] {
: -Wall -Wmissing-prototypes -Wsign-compare ] ;

MergeObject <$(architecture)>posix_crypt.o :
crypt.c
crypt_util.c
crypt_legacy.c
crypt_legacy_util.c
crypto_scrypt_smix.cpp
crypto_scrypt.cpp
crypt.cpp
pbkdf2.cpp
;
}
}
198 changes: 198 additions & 0 deletions src/system/libroot/posix/crypt/crypt.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
/*
* Copyright 2017, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Andrew Aldridge, i80and@foxquill.com
*/


#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <math.h>
#include <stdio.h>
#include <string.h>

#include <SupportDefs.h>

#include "crypt_legacy.h"
#include "crypto_scrypt.h"

#define SALT_BYTES 32
#define SALT_STR_BYTES (SALT_BYTES * 2 + 1)
#define DEFAULT_N_LOG2 14

// $s$99$ salt $ hash \0
#define CRYPT_OUTPUT_BYTES (6 + 64 + 1 + 64 + 1)

static const char* kHexAlphabet = "0123456789abcdef";
static const char kHexLookup[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3,
4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15};


static int
toHex(const uint8* buffer, size_t bufferLength, char* outBuffer,
size_t outBufferLength)
{
size_t i;
size_t outIndex = 0;

if (outBufferLength <= bufferLength * 2) {
outBuffer[0] = '\0';
return 1;
}

for (i = 0; i < bufferLength; i += 1) {
const uint8 n = buffer[i];
const uint8 upper = n >> 4;
const uint8 lower = n & 0x0f;

assert(lower < 16 && upper < 16);
outBuffer[outIndex++] = kHexAlphabet[upper];
outBuffer[outIndex++] = kHexAlphabet[lower];
outBuffer[outIndex] = '\0';
}

outBuffer[outIndex] = '\0';

return 0;
}


static size_t
fromHex(const char* hex, uint8* outBuffer, size_t outBufferLength)
{
size_t i = 0;
size_t outIndex = 0;

if (hex[0] == '\0' || outBufferLength == 0)
return 0;

while (hex[i] != '\0' && hex[i + 1] != '\0') {
const uint8 char1 = hex[i];
const uint8 char2 = hex[i + 1];

if (char1 >= sizeof(kHexLookup) || char2 >= sizeof(kHexLookup))
return outIndex;

const char index1 = kHexLookup[char1];
const char index2 = kHexLookup[char2];

if (outIndex >= outBufferLength)
return 0;

outBuffer[outIndex++] = (index1 << 4) | index2;
i += 2;
}

return outIndex;
}


//! Generate a new salt appropriate for crypt().
static char*
crypt_gensalt()
{
static char result[CRYPT_OUTPUT_BYTES];
uint8 salt[SALT_BYTES];
char saltString[SALT_STR_BYTES];
size_t totalBytesRead = 0;

int fd = open("/dev/random", O_RDONLY, 0);
if (fd < 0)
return NULL;

while (totalBytesRead < sizeof(salt)) {
const ssize_t bytesRead = read(fd,
static_cast<void*>(salt + totalBytesRead),
sizeof(salt) - totalBytesRead);
if (bytesRead <= 0) {
close(fd);
return NULL;
}

totalBytesRead += bytesRead;
}
close(fd);

assert(toHex(salt, sizeof(salt), saltString, sizeof(saltString)) == 0);
snprintf(result, sizeof(result), "$s$%d$%s$", DEFAULT_N_LOG2, saltString);
return result;
}


char *
crypt(const char* key, const char* setting)
{
static char outBuffer[CRYPT_OUTPUT_BYTES];
uint8 saltBinary[SALT_BYTES];
char saltString[SALT_STR_BYTES];
uint8 resultBuffer[32];
char hexResultBuffer[64 + 1];
int nLog2 = DEFAULT_N_LOG2;

if (setting == NULL) {
setting = crypt_gensalt();
if (setting == NULL) {
// crypt_gensalt should set errno itself.
return NULL;
}
}

// Some idioms existed where the password was also used as the salt.
// As a crude heuristic, use the old crypt algorithm if the salt is
// shortish.
if (strlen(setting) < 16)
return crypt_legacy(key, setting);

// We don't want to fall into the old algorithm by accident somehow, so
// if our salt is kind of like our salt, but not exactly, return an
// error.
if (sscanf(setting, "$s$%2d$%64s$", &nLog2, saltString) != 2) {
errno = EINVAL;
return NULL;
}

// Set a lower bound on N_log2: below 12 scrypt is weaker than bcrypt.
if (nLog2 < 12) {
errno = EINVAL;
return NULL;
}

size_t saltBinaryLength = fromHex(saltString, saltBinary,
sizeof(saltBinary));
if (saltBinaryLength != sizeof(saltBinary)) {
errno = EINVAL;
return NULL;
}

long n = static_cast<long>(pow(2, nLog2));
if (crypto_scrypt(reinterpret_cast<const uint8*>(key), strlen(key),
saltBinary, saltBinaryLength, n, 8, 1, resultBuffer,
sizeof(resultBuffer)) != 0) {
// crypto_scrypt sets errno itself
return NULL;
}

assert(toHex(resultBuffer, sizeof(resultBuffer), hexResultBuffer,
sizeof(hexResultBuffer)) == 0);
snprintf(outBuffer, sizeof(outBuffer), "$s$%d$%s$%s", nLog2, saltString,
hexResultBuffer);

return outBuffer;
}


//! To make fcrypt users happy. They don't need to call init_des.
char*
fcrypt(const char* key, const char* salt)
{
return crypt(key, salt);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Expand Down Expand Up @@ -51,19 +51,19 @@ ufc_long *_ufc_doit(l1, l2, r1, r2, itr)
k = &_ufc_keytab[0][0];
for(i=8; i--; ) {
s = *k++ ^ r1;
l1 ^= SBA(sb1, s & 0xffff); l2 ^= SBA(sb1, (s & 0xffff)+4);
l1 ^= SBA(sb0, s >>= 16); l2 ^= SBA(sb0, (s) +4);
s = *k++ ^ r2;
l1 ^= SBA(sb1, s & 0xffff); l2 ^= SBA(sb1, (s & 0xffff)+4);
l1 ^= SBA(sb0, s >>= 16); l2 ^= SBA(sb0, (s) +4);
s = *k++ ^ r2;
l1 ^= SBA(sb3, s & 0xffff); l2 ^= SBA(sb3, (s & 0xffff)+4);
l1 ^= SBA(sb2, s >>= 16); l2 ^= SBA(sb2, (s) +4);

s = *k++ ^ l1;
r1 ^= SBA(sb1, s & 0xffff); r2 ^= SBA(sb1, (s & 0xffff)+4);
r1 ^= SBA(sb0, s >>= 16); r2 ^= SBA(sb0, (s) +4);
s = *k++ ^ l2;
r1 ^= SBA(sb3, s & 0xffff); r2 ^= SBA(sb3, (s & 0xffff)+4);
s = *k++ ^ l1;
r1 ^= SBA(sb1, s & 0xffff); r2 ^= SBA(sb1, (s & 0xffff)+4);
r1 ^= SBA(sb0, s >>= 16); r2 ^= SBA(sb0, (s) +4);
s = *k++ ^ l2;
r1 ^= SBA(sb3, s & 0xffff); r2 ^= SBA(sb3, (s & 0xffff)+4);
r1 ^= SBA(sb2, s >>= 16); r2 ^= SBA(sb2, (s) +4);
}
}
s=l1; l1=r1; r1=s; s=l2; l2=r2; r2=s;
}
ary[0] = l1; ary[1] = l2; ary[2] = r1; ary[3] = r2;
Expand Down Expand Up @@ -111,7 +111,7 @@ ufc_long *_ufc_doit(l1, l2, r1, r2, itr)
r ^= SBA(sb2, (s >> 16) & 0xffff);
r ^= SBA(sb1, (s >> 32) & 0xffff);
r ^= SBA(sb0, (s >> 48) & 0xffff);
}
}
s=l; l=r; r=s;
}

Expand Down
14 changes: 14 additions & 0 deletions src/system/libroot/posix/crypt/crypt_legacy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef CRYPT_LEGACY_H
#define CRYPT_LEGACY_H

#ifdef __cplusplus
extern "C" {
#endif

char *crypt_legacy(const char *key, const char *salt);

#ifdef __cplusplus
}
#endif

#endif // CRYPT_LEGACY_H
Loading

0 comments on commit f31b1a2

Please sign in to comment.