Skip to content

Commit

Permalink
feat(core): Add BLF-CRYPT scheme.
Browse files Browse the repository at this point in the history
Dovecot promotes the use of BLF-CRYPT as a hashing scheme [1].
However, as not all libc-crypt() implementations support this scheme,
use the implementation added in a previous commit.

Also, update the references to the dovecot implementation and a small
test case for testing the blowfish implementation.

[1] https://doc.dovecot.org/configuration_manual/authentication/password_schemes/

Fixes #4958
  • Loading branch information
the-nic committed May 15, 2020
1 parent 562f81f commit 8c612fc
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 6 deletions.
10 changes: 5 additions & 5 deletions Documentation/SOGoInstallationGuide.asciidoc
Expand Up @@ -1069,7 +1069,7 @@ Possible values are: `none`, `plain`, `crypt`, `md5`, `md5-crypt`,
(plus setting of the encoding with `.b64` or `.hex`).
For a more detailed description see
http://wiki.dovecot.org/Authentication/PasswordSchemes.
https://doc.dovecot.org/configuration_manual/authentication/password_schemes/.
Note that `cram-md5` is not actually using cram-md5 (due to the lack of
challenge-response mechanism), its just saving the intermediate MD5
Expand Down Expand Up @@ -1659,13 +1659,13 @@ they have the same name as popular LDAP attributes (such as `givenName`,
|The default algorithm used for password encryption when changing
passwords. Possible values are: `none`, `plain`, `crypt`, `md5`,
`md5-crypt`, `smd5`, `cram-md5`, `ldap-md5`, and `sha`, `sha256`,
`sha256-crypt`, `sha512`, `sha512-crypt` and its ssha (e.g. `ssha` or
`ssha256`) variants and `sym-aes-128-cbc`. Passwords can have the
scheme prepended in the form `{scheme}encryptedPass`.
`sha256-crypt`, `sha512`, `sha512-crypt`, its ssha (e.g. `ssha` or
`ssha256`) variants, `blf-crypt`, and `sym-aes-128-cbc`. Passwords
can have the scheme prepended in the form `{scheme}encryptedPass`.
If no scheme is given, _userPasswordAlgorithm_ is used instead. The
schemes listed above follow the algorithms described in
http://wiki.dovecot.org/Authentication/PasswordSchemes.
https://doc.dovecot.org/configuration_manual/authentication/password_schemes/.
Note that `cram-md5` is not actually using cram-md5 (due to the lack of
challenge-response mechanism), its just saving the intermediate MD5
Expand Down
1 change: 1 addition & 0 deletions SoObjects/SOGo/NSData+Crypto.h
Expand Up @@ -53,6 +53,7 @@

- (NSData *) asCryptUsingSalt: (NSData *) theSalt;
- (NSData *) asMD5CryptUsingSalt: (NSData *) theSalt;
- (NSData *) asBlowfishCryptUsingSalt: (NSData *) theSalt;

- (NSData *) extractSalt: (NSString *) theScheme;

Expand Down
59 changes: 58 additions & 1 deletion SoObjects/SOGo/NSData+Crypto.m
@@ -1,6 +1,6 @@
/* NSData+Crypto.m - this file is part of SOGo
*
* Copyright (C) 2012 Nicolas Höft
* Copyright (C) 2012, 2020 Nicolas Höft
* Copyright (C) 2012-2020 Inverse inc.
* Copyright (C) 2012 Jeroen Dekkers
*
Expand Down Expand Up @@ -50,6 +50,7 @@
#endif

#include "aes.h"
#include "crypt_blowfish.h"
#include "lmhash.h"

#import <Foundation/NSArray.h>
Expand All @@ -62,6 +63,12 @@
static void _nettle_md5_compress(uint32_t *digest, const uint8_t *input);
#endif

#define BLF_CRYPT_DEFAULT_COMPLEXITY (5)
#define BLF_CRYPT_SALT_LEN (16)
#define BLF_CRYPT_BUFFER_LEN (128)
#define BLF_CRYPT_PREFIX_LEN (7+22+1) /* $2.$nn$ + salt */
#define BLF_CRYPT_PREFIX "$2y"


@implementation NSData (SOGoCryptoExtension)

Expand Down Expand Up @@ -245,6 +252,10 @@ - (NSData *) asCryptedPassUsingScheme: (NSString *) passwordScheme
{
return [self asSHA512CryptUsingSalt: theSalt];
}
else if ([passwordScheme caseInsensitiveCompare: @"blf-crypt"] == NSOrderedSame)
{
return [self asBlowfishCryptUsingSalt: theSalt];
}
else if ([[passwordScheme lowercaseString] hasPrefix: @"sym"])
{
// We first support one sym cipher, AES-128-CBC. If something else is provided
Expand Down Expand Up @@ -764,6 +775,52 @@ - (NSData *) asCryptUsingSalt: (NSData *) theSalt
return [NSData dataWithBytes: buf length: strlen(buf)];
}

/**
* Hash the data using blowfish-crypt
* @param theSalt The salt to be used must not be nil, if empty, one will be generated
*/
- (NSData *) asBlowfishCryptUsingSalt: (NSData *) theSalt
{
NSString *cleartext;
char hashed_password[BLF_CRYPT_BUFFER_LEN];
char magic_salt[BLF_CRYPT_PREFIX_LEN]; // contains $2.$nn$ + salt

if ([theSalt length] == 0)
{
// generate a salt with default complexity if none was provided
NSData* salt = [NSData generateSaltForLength: BLF_CRYPT_SALT_LEN];
if (_crypt_gensalt_blowfish_rn(BLF_CRYPT_PREFIX, BLF_CRYPT_DEFAULT_COMPLEXITY,
[salt bytes], BLF_CRYPT_SALT_LEN,
magic_salt, BLF_CRYPT_PREFIX_LEN) == NULL)
return nil;
}
else
{
const char* salt = [theSalt bytes];
if ([theSalt length] < BLF_CRYPT_PREFIX_LEN ||
salt[0] != '$' || salt[1] != '2' ||
salt[2] < 'a' || salt[2] > 'z' ||
salt[3] != '$')
{
return nil;
}
memcpy(magic_salt, salt, BLF_CRYPT_PREFIX_LEN);
}

cleartext = [[NSString alloc] initWithData: self encoding: NSUTF8StringEncoding];
const char* password = [cleartext UTF8String];

char* bf_res = _crypt_blowfish_rn(password, magic_salt,
hashed_password, BLF_CRYPT_BUFFER_LEN);
[cleartext autorelease];

if (bf_res == NULL)
return nil;

return [NSData dataWithBytes: hashed_password length: strlen(hashed_password)];
}


/**
* Get the salt from a password encrypted with a specied scheme
*
Expand Down
25 changes: 25 additions & 0 deletions Tests/Unit/TestNSString+Crypto.m
Expand Up @@ -60,4 +60,29 @@ - (void) test_dataCrypto
}
}

- (void) test_blowfish
{
NSString *error;
// well-known comparison
NSString *blf_key = @"123456";
NSString *blf_hash = @"{BLF-CRYPT}$2a$05$tLVuFQTgdwrZmixu.QMxoedUAUEeIFIBv89Ur5mQ6F1vBL8Vw1mXO";
error = [NSString stringWithFormat:
@"string '%@' wrong BLF-CRYPT: '%@'",
blf_key, blf_hash];
testWithMessage([blf_key isEqualToCrypted:blf_hash withDefaultScheme: @"CRYPT" keyPath: nil], error);

// generate a new blowfish-crypt key
NSString *blf_prefix = @"$2y$05$";

NSString *blf_result = [blf_key asCryptedPassUsingScheme: @"blf-crypt" keyPath: nil];

error = [NSString stringWithFormat:
@"returned hash '%@' has incorrect BLF-CRYPT prefix: '%@'",
blf_result, blf_prefix];

testWithMessage([blf_result hasPrefix: blf_prefix], error);

test([blf_key isEqualToCrypted:blf_result withDefaultScheme: @"BLF-CRYPT" keyPath: nil]);
}

@end

0 comments on commit 8c612fc

Please sign in to comment.