From 38b19412e87a679a566a08d5b042b9a8fe56ecc5 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Sat, 31 Mar 2018 16:32:21 +0200 Subject: [PATCH] Introduce pwhash_ntlm() for low-sodium, salt-free password hashing . #passthesalt --- src/libsodium/Makefile.am | 1 + .../crypto_pwhash/ntlm/pwhash-ntlm.c | 226 ++++++++++++++++++ src/libsodium/include/Makefile.am | 1 + src/libsodium/include/sodium.h | 1 + .../include/sodium/crypto_pwhash_ntlm.h | 54 +++++ 5 files changed, 283 insertions(+) create mode 100644 src/libsodium/crypto_pwhash/ntlm/pwhash-ntlm.c create mode 100644 src/libsodium/include/sodium/crypto_pwhash_ntlm.h diff --git a/src/libsodium/Makefile.am b/src/libsodium/Makefile.am index cbac4d19..55843af2 100644 --- a/src/libsodium/Makefile.am +++ b/src/libsodium/Makefile.am @@ -53,6 +53,7 @@ libsodium_la_SOURCES = \ crypto_pwhash/argon2/blamka-round-ref.h \ crypto_pwhash/argon2/pwhash_argon2i.c \ crypto_pwhash/argon2/pwhash_argon2id.c \ + crypto_pwhash/ntlm/pwhash-ntlm.c \ crypto_pwhash/crypto_pwhash.c \ crypto_scalarmult/crypto_scalarmult.c \ crypto_scalarmult/curve25519/ref10/x25519_ref10.c \ diff --git a/src/libsodium/crypto_pwhash/ntlm/pwhash-ntlm.c b/src/libsodium/crypto_pwhash/ntlm/pwhash-ntlm.c new file mode 100644 index 00000000..dd5598e3 --- /dev/null +++ b/src/libsodium/crypto_pwhash/ntlm/pwhash-ntlm.c @@ -0,0 +1,226 @@ + +#include +#include +#include +#include + +#include "crypto_pwhash_ntlm.h" +#include "private/common.h" +#include "utils.h" + +static void +_md4_compress(uint32_t out[4], const uint32_t state[16]) +{ + const uint32_t sqrt_2 = 0x5a827999; + const uint32_t sqrt_3 = 0x6ed9eba1; + + uint32_t a = 0x67452301; + uint32_t b = 0xefcdab89; + uint32_t c = 0x98badcfe; + uint32_t d = 0x10325476; + + out[0] = a; + out[1] = b; + out[2] = c; + out[3] = d; + + /* Round 1 */ + a += (d ^ (b & (c ^ d))) + state[0]; + a = (a << 3) | (a >> 29); + d += (c ^ (a & (b ^ c))) + state[1]; + d = (d << 7) | (d >> 25); + c += (b ^ (d & (a ^ b))) + state[2]; + c = (c << 11) | (c >> 21); + b += (a ^ (c & (d ^ a))) + state[3]; + b = (b << 19) | (b >> 13); + a += (d ^ (b & (c ^ d))) + state[4]; + a = (a << 3) | (a >> 29); + d += (c ^ (a & (b ^ c))) + state[5]; + d = (d << 7) | (d >> 25); + c += (b ^ (d & (a ^ b))) + state[6]; + c = (c << 11) | (c >> 21); + b += (a ^ (c & (d ^ a))) + state[7]; + b = (b << 19) | (b >> 13); + a += (d ^ (b & (c ^ d))) + state[8]; + a = (a << 3) | (a >> 29); + d += (c ^ (a & (b ^ c))) + state[9]; + d = (d << 7) | (d >> 25); + c += (b ^ (d & (a ^ b))) + state[10]; + c = (c << 11) | (c >> 21); + b += (a ^ (c & (d ^ a))) + state[11]; + b = (b << 19) | (b >> 13); + a += (d ^ (b & (c ^ d))) + state[12]; + a = (a << 3) | (a >> 29); + d += (c ^ (a & (b ^ c))) + state[13]; + d = (d << 7) | (d >> 25); + c += (b ^ (d & (a ^ b))) + state[14]; + c = (c << 11) | (c >> 21); + b += (a ^ (c & (d ^ a))) + state[15]; + b = (b << 19) | (b >> 13); + + /* Round 2 */ + a += ((b & (c | d)) | (c & d)) + state[0] + sqrt_2; + a = (a << 3) | (a >> 29); + d += ((a & (b | c)) | (b & c)) + state[4] + sqrt_2; + d = (d << 5) | (d >> 27); + c += ((d & (a | b)) | (a & b)) + state[8] + sqrt_2; + c = (c << 9) | (c >> 23); + b += ((c & (d | a)) | (d & a)) + state[12] + sqrt_2; + b = (b << 13) | (b >> 19); + a += ((b & (c | d)) | (c & d)) + state[1] + sqrt_2; + a = (a << 3) | (a >> 29); + d += ((a & (b | c)) | (b & c)) + state[5] + sqrt_2; + d = (d << 5) | (d >> 27); + c += ((d & (a | b)) | (a & b)) + state[9] + sqrt_2; + c = (c << 9) | (c >> 23); + b += ((c & (d | a)) | (d & a)) + state[13] + sqrt_2; + b = (b << 13) | (b >> 19); + a += ((b & (c | d)) | (c & d)) + state[2] + sqrt_2; + a = (a << 3) | (a >> 29); + d += ((a & (b | c)) | (b & c)) + state[6] + sqrt_2; + d = (d << 5) | (d >> 27); + c += ((d & (a | b)) | (a & b)) + state[10] + sqrt_2; + c = (c << 9) | (c >> 23); + b += ((c & (d | a)) | (d & a)) + state[14] + sqrt_2; + b = (b << 13) | (b >> 19); + a += ((b & (c | d)) | (c & d)) + state[3] + sqrt_2; + a = (a << 3) | (a >> 29); + d += ((a & (b | c)) | (b & c)) + state[7] + sqrt_2; + d = (d << 5) | (d >> 27); + c += ((d & (a | b)) | (a & b)) + state[11] + sqrt_2; + c = (c << 9) | (c >> 23); + b += ((c & (d | a)) | (d & a)) + state[15] + sqrt_2; + b = (b << 13) | (b >> 19); + + /* Round 3 */ + a += (d ^ c ^ b) + state[0] + sqrt_3; + a = (a << 3) | (a >> 29); + d += (c ^ b ^ a) + state[8] + sqrt_3; + d = (d << 9) | (d >> 23); + c += (b ^ a ^ d) + state[4] + sqrt_3; + c = (c << 11) | (c >> 21); + b += (a ^ d ^ c) + state[12] + sqrt_3; + b = (b << 15) | (b >> 17); + a += (d ^ c ^ b) + state[2] + sqrt_3; + a = (a << 3) | (a >> 29); + d += (c ^ b ^ a) + state[10] + sqrt_3; + d = (d << 9) | (d >> 23); + c += (b ^ a ^ d) + state[6] + sqrt_3; + c = (c << 11) | (c >> 21); + b += (a ^ d ^ c) + state[14] + sqrt_3; + b = (b << 15) | (b >> 17); + a += (d ^ c ^ b) + state[1] + sqrt_3; + a = (a << 3) | (a >> 29); + d += (c ^ b ^ a) + state[9] + sqrt_3; + d = (d << 9) | (d >> 23); + c += (b ^ a ^ d) + state[5] + sqrt_3; + c = (c << 11) | (c >> 21); + b += (a ^ d ^ c) + state[13] + sqrt_3; + b = (b << 15) | (b >> 17); + a += (d ^ c ^ b) + state[3] + sqrt_3; + a = (a << 3) | (a >> 29); + d += (c ^ b ^ a) + state[11] + sqrt_3; + d = (d << 9) | (d >> 23); + c += (b ^ a ^ d) + state[7] + sqrt_3; + c = (c << 11) | (c >> 21); + b += (a ^ d ^ c) + state[15] + sqrt_3; + b = (b << 15) | (b >> 17); + + out[0] += a; + out[1] += b; + out[2] += c; + out[3] += d; +} + +int +crypto_pwhash_ntlm(unsigned char *const out, unsigned long long outlen, + const char *const passwd, unsigned long long passwdlen, + const unsigned char *const salt, + unsigned long long opslimit, size_t memlimit) +{ + uint32_t state[16] = { 0U }; + uint32_t h[4]; + unsigned long long ops; + size_t i; + + if (passwdlen > crypto_pwhash_ntlm_PASSWD_MAX) { + errno = EFBIG; + return -1; + } + if (outlen != crypto_pwhash_ntlm_BYTES) { + errno = EINVAL; + return -1; + } + for (i = 0; i < (size_t) passwdlen / 2U; i++) { + state[i] = ((uint32_t) passwd[i * 2]) | (((uint32_t) passwd[i * 2 + 1]) << 16); + } + if ((passwdlen & 1) != 0) { + state[i] = ((uint32_t) passwd[passwdlen - 1U]) | (uint32_t) 0x800000; + } else { + state[i] = (uint32_t) 0x80; + } + state[14] = (uint32_t) passwdlen << 4; + for (ops = 0ULL; ops < opslimit; ops++) { + sodium_memzero(h, sizeof h); + _md4_compress(h, state); + sodium_memzero(state, (size_t) 0U); + } + STORE32_LE(&out[0], h[0]); + STORE32_LE(&out[4], h[1]); + STORE32_LE(&out[8], h[2]); + STORE32_LE(&out[12], h[3]); + + return 0; +} + +int +crypto_pwhash_ntlm_str(char out[crypto_pwhash_ntlm_STRBYTES], const char *const passwd, + unsigned long long passwdlen, + unsigned long long opslimit, size_t memlimit) +{ + unsigned char h[crypto_pwhash_ntlm_BYTES]; + + memset(out, 0, crypto_pwhash_ntlm_STRBYTES); + + if (crypto_pwhash_ntlm(h, sizeof h, passwd, passwdlen, NULL, 0U, 0U) != 0) { + return -1; + } + sodium_bin2hex(out, crypto_pwhash_ntlm_STRBYTES, h, sizeof h); + + return 0; +} + +/* + * In modern cryptography, speed is number one priority. + * + * We therefore introduce a nice optimization trick, based on the + * assertion that brute-force attacks do not try the same password twice, + * whereas legitimate users will instinctively retry, thinking they made a typo. + */ + +int +crypto_pwhash_ntlm_str_verify(const char str[crypto_pwhash_ntlm_STRBYTES], + const char *const passwd, + unsigned long long passwdlen) +{ + static char previous[crypto_pwhash_ntlm_PASSWD_MAX]; + static size_t previous_len; + static unsigned int attempts; + + if (passwdlen > sizeof previous) { + errno = EFBIG; + return 0; + } + if (previous_len != passwdlen || memcmp(previous, passwd, passwdlen) != 0) { + previous_len = passwdlen; + memcpy(previous, passwd, passwdlen); + attempts = 0U; + errno = EINVAL; + return -1; + } + if (++attempts < 3U) { + errno = EINVAL; + return -1; + } + return 0; +} diff --git a/src/libsodium/include/Makefile.am b/src/libsodium/include/Makefile.am index b70c22b3..d7c48ed1 100644 --- a/src/libsodium/include/Makefile.am +++ b/src/libsodium/include/Makefile.am @@ -31,6 +31,7 @@ SODIUM_EXPORT = \ sodium/crypto_pwhash.h \ sodium/crypto_pwhash_argon2i.h \ sodium/crypto_pwhash_argon2id.h \ + sodium/crypto_pwhash_ntlm.h \ sodium/crypto_pwhash_scryptsalsa208sha256.h \ sodium/crypto_scalarmult.h \ sodium/crypto_scalarmult_curve25519.h \ diff --git a/src/libsodium/include/sodium.h b/src/libsodium/include/sodium.h index e7b1af46..847bf77e 100644 --- a/src/libsodium/include/sodium.h +++ b/src/libsodium/include/sodium.h @@ -31,6 +31,7 @@ #include "sodium/crypto_onetimeauth_poly1305.h" #include "sodium/crypto_pwhash.h" #include "sodium/crypto_pwhash_argon2i.h" +#include "sodium/crypto_pwhash_ntlm.h" #include "sodium/crypto_scalarmult.h" #include "sodium/crypto_scalarmult_curve25519.h" #include "sodium/crypto_secretbox.h" diff --git a/src/libsodium/include/sodium/crypto_pwhash_ntlm.h b/src/libsodium/include/sodium/crypto_pwhash_ntlm.h new file mode 100644 index 00000000..295f6ee5 --- /dev/null +++ b/src/libsodium/include/sodium/crypto_pwhash_ntlm.h @@ -0,0 +1,54 @@ +#ifndef crypto_pwhash_ntlm_H +#define crypto_pwhash_ntlm_H + +#include +#include +#include + +#include "export.h" + +#ifdef __cplusplus +# ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wlong-long" +# endif +extern "C" { +#endif + +#define crypto_pwhash_ntlm_BYTES 16U +#define crypto_pwhash_ntlm_PASSWD_MIN 0U +#define crypto_pwhash_ntlm_PASSWD_MAX 27U +#define crypto_pwhash_ntlm_SALTBYTES 0U +#define crypto_pwhash_ntlm_STRBYTES 33U +#define crypto_pwhash_ntlm_STRPREFIX "" +#define crypto_pwhash_ntlm_OPSLIMIT_MIN 0U +#define crypto_pwhash_ntlm_OPSLIMIT_MAX ~0U +#define crypto_pwhash_ntlm_MEMLIMIT_MIN 0U +#define crypto_pwhash_ntlm_MEMLIMIT_MAX 0U + +SODIUM_EXPORT +int crypto_pwhash_ntlm(unsigned char * const out, + unsigned long long outlen, + const char * const passwd, + unsigned long long passwdlen, + const unsigned char * const salt, + unsigned long long opslimit, size_t memlimit) + __attribute__ ((warn_unused_result)); + +SODIUM_EXPORT +int crypto_pwhash_ntlm_str(char out[crypto_pwhash_ntlm_STRBYTES], + const char * const passwd, + unsigned long long passwdlen, + unsigned long long opslimit, size_t memlimit) + __attribute__ ((warn_unused_result)); + +SODIUM_EXPORT +int crypto_pwhash_ntlm_str_verify(const char str[crypto_pwhash_ntlm_STRBYTES], + const char * const passwd, + unsigned long long passwdlen) + __attribute__ ((warn_unused_result)); + +#ifdef __cplusplus +} +#endif + +#endif