mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-27 22:03:31 +01:00
More generic passphrase hashing code, including scrypt support
Uses libscrypt when found; otherwise, we don't have scrypt and we only support openpgp rfc2440 s2k hashing, or pbkdf2. Includes documentation and unit tests; coverage around 95%. Remaining uncovered code is sanity-checks that shouldn't be reachable fwict.
This commit is contained in:
parent
e72a5b3c07
commit
e84e1c9745
11
configure.ac
11
configure.ac
@ -153,6 +153,9 @@ AC_ARG_ENABLE(tool-name-check,
|
|||||||
AC_ARG_ENABLE(seccomp,
|
AC_ARG_ENABLE(seccomp,
|
||||||
AS_HELP_STRING(--disable-seccomp, do not attempt to use libseccomp))
|
AS_HELP_STRING(--disable-seccomp, do not attempt to use libseccomp))
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(libscrypt,
|
||||||
|
AS_HELP_STRING(--disable-libscrypt, do not attempt to use libscrypt))
|
||||||
|
|
||||||
dnl check for the correct "ar" when cross-compiling
|
dnl check for the correct "ar" when cross-compiling
|
||||||
AN_MAKEVAR([AR], [AC_PROG_AR])
|
AN_MAKEVAR([AR], [AC_PROG_AR])
|
||||||
AN_PROGRAM([ar], [AC_PROG_AR])
|
AN_PROGRAM([ar], [AC_PROG_AR])
|
||||||
@ -721,6 +724,14 @@ if test "x$enable_seccomp" != "xno"; then
|
|||||||
AC_SEARCH_LIBS(seccomp_init, [seccomp])
|
AC_SEARCH_LIBS(seccomp_init, [seccomp])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
dnl ============================================================
|
||||||
|
dnl Check for libscrypt
|
||||||
|
|
||||||
|
if test "x$enable_libscrypt" != "xno"; then
|
||||||
|
AC_CHECK_HEADERS([libscrypt.h])
|
||||||
|
AC_SEARCH_LIBS(libscrypt_scrypt, [scrypt])
|
||||||
|
fi
|
||||||
|
|
||||||
dnl ============================================================
|
dnl ============================================================
|
||||||
dnl We need an implementation of curve25519.
|
dnl We need an implementation of curve25519.
|
||||||
|
|
||||||
|
@ -280,13 +280,6 @@ int digest_from_base64(char *digest, const char *d64);
|
|||||||
int digest256_to_base64(char *d64, const char *digest);
|
int digest256_to_base64(char *d64, const char *digest);
|
||||||
int digest256_from_base64(char *digest, const char *d64);
|
int digest256_from_base64(char *digest, const char *d64);
|
||||||
|
|
||||||
/** Length of RFC2440-style S2K specifier: the first 8 bytes are a salt, the
|
|
||||||
* 9th describes how much iteration to do. */
|
|
||||||
#define S2K_RFC2440_SPECIFIER_LEN 9
|
|
||||||
void secret_to_key_rfc2440(
|
|
||||||
char *key_out, size_t key_out_len, const char *secret,
|
|
||||||
size_t secret_len, const char *s2k_specifier);
|
|
||||||
|
|
||||||
/** OpenSSL-based utility functions. */
|
/** OpenSSL-based utility functions. */
|
||||||
void memwipe(void *mem, uint8_t byte, size_t sz);
|
void memwipe(void *mem, uint8_t byte, size_t sz);
|
||||||
|
|
||||||
|
@ -7,6 +7,165 @@
|
|||||||
#include "crypto.h"
|
#include "crypto.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
|
#include "crypto_s2k.h"
|
||||||
|
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBSCRYPT_H
|
||||||
|
#define HAVE_SCRYPT
|
||||||
|
#include <libscrypt.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Encoded secrets take the form:
|
||||||
|
|
||||||
|
u8 type;
|
||||||
|
u8 salt_and_parameters[depends on type];
|
||||||
|
u8 key[depends on type];
|
||||||
|
|
||||||
|
As a special case, if the encoded secret is exactly 29 bytes long,
|
||||||
|
type 0 is understood.
|
||||||
|
|
||||||
|
Recognized types are:
|
||||||
|
00 -- RFC2440. salt_and_parameters is 9 bytes. key is 20 bytes.
|
||||||
|
salt_and_parameters is 8 bytes random salt,
|
||||||
|
1 byte iteration info.
|
||||||
|
01 -- PKBDF2_SHA1. salt_and_parameters is 17 bytes. key is 20 bytes.
|
||||||
|
salt_and_parameters is 16 bytes random salt,
|
||||||
|
1 byte iteration info.
|
||||||
|
02 -- SCRYPT_SALSA208_SHA256. salt_and_parameters is 18 bytes. key is
|
||||||
|
32 bytes.
|
||||||
|
salt_and_parameters is 18 bytes random salt, 2 bytes iteration
|
||||||
|
info.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define S2K_TYPE_RFC2440 0
|
||||||
|
#define S2K_TYPE_PBKDF2 1
|
||||||
|
#define S2K_TYPE_SCRYPT 2
|
||||||
|
|
||||||
|
#define PBKDF2_SPEC_LEN 17
|
||||||
|
#define PBKDF2_KEY_LEN 20
|
||||||
|
|
||||||
|
#define SCRYPT_SPEC_LEN 18
|
||||||
|
#define SCRYPT_KEY_LEN 32
|
||||||
|
|
||||||
|
/** Given an algorithm ID (one of S2K_TYPE_*), return the length of the
|
||||||
|
* specifier part of it, without the prefix type byte. */
|
||||||
|
static int
|
||||||
|
secret_to_key_spec_len(uint8_t type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case S2K_TYPE_RFC2440:
|
||||||
|
return S2K_RFC2440_SPECIFIER_LEN;
|
||||||
|
case S2K_TYPE_PBKDF2:
|
||||||
|
return PBKDF2_SPEC_LEN;
|
||||||
|
case S2K_TYPE_SCRYPT:
|
||||||
|
return SCRYPT_SPEC_LEN;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Given an algorithm ID (one of S2K_TYPE_*), return the length of the
|
||||||
|
* its preferred output. */
|
||||||
|
static int
|
||||||
|
secret_to_key_key_len(uint8_t type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case S2K_TYPE_RFC2440:
|
||||||
|
return DIGEST_LEN;
|
||||||
|
case S2K_TYPE_PBKDF2:
|
||||||
|
return DIGEST_LEN;
|
||||||
|
case S2K_TYPE_SCRYPT:
|
||||||
|
return DIGEST256_LEN;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Given a specifier in <b>spec_and_key</b> of length
|
||||||
|
* <b>spec_and_key_len</b>, along with its prefix algorithm ID byte, and along
|
||||||
|
* with a key if <b>key_included</b> is true, check whether the whole
|
||||||
|
* specifier-and-key is of valid length, and return the algorithm type if it
|
||||||
|
* is. Set *<b>legacy_out</b> to 1 iff this is a legacy password hash or
|
||||||
|
* legacy specifier. Return an error code on failure.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
secret_to_key_get_type(const uint8_t *spec_and_key, size_t spec_and_key_len,
|
||||||
|
int key_included, int *legacy_out)
|
||||||
|
{
|
||||||
|
size_t legacy_len = S2K_RFC2440_SPECIFIER_LEN;
|
||||||
|
uint8_t type;
|
||||||
|
int total_len;
|
||||||
|
|
||||||
|
if (key_included)
|
||||||
|
legacy_len += DIGEST_LEN;
|
||||||
|
|
||||||
|
if (spec_and_key_len == legacy_len) {
|
||||||
|
*legacy_out = 1;
|
||||||
|
return S2K_TYPE_RFC2440;
|
||||||
|
}
|
||||||
|
|
||||||
|
*legacy_out = 0;
|
||||||
|
if (spec_and_key_len == 0)
|
||||||
|
return S2K_BAD_LEN;
|
||||||
|
|
||||||
|
type = spec_and_key[0];
|
||||||
|
total_len = secret_to_key_spec_len(type);
|
||||||
|
if (total_len < 0)
|
||||||
|
return S2K_BAD_ALGORITHM;
|
||||||
|
if (key_included) {
|
||||||
|
int keylen = secret_to_key_key_len(type);
|
||||||
|
if (keylen < 0)
|
||||||
|
return S2K_BAD_ALGORITHM;
|
||||||
|
total_len += keylen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((size_t)total_len + 1 == spec_and_key_len)
|
||||||
|
return type;
|
||||||
|
else
|
||||||
|
return S2K_BAD_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a new random s2k specifier of type <b>type</b>, without prefixing
|
||||||
|
* type byte, to <b>spec_out</b>, which must have enough room. May adjust
|
||||||
|
* parameter choice based on <b>flags</b>.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
make_specifier(uint8_t *spec_out, uint8_t type, unsigned flags)
|
||||||
|
{
|
||||||
|
int speclen = secret_to_key_spec_len(type);
|
||||||
|
if (speclen < 0)
|
||||||
|
return S2K_BAD_ALGORITHM;
|
||||||
|
|
||||||
|
crypto_rand((char*)spec_out, speclen);
|
||||||
|
switch (type) {
|
||||||
|
case S2K_TYPE_RFC2440:
|
||||||
|
/* Hash 64 k of data. */
|
||||||
|
spec_out[S2K_RFC2440_SPECIFIER_LEN-1] = 96;
|
||||||
|
break;
|
||||||
|
case S2K_TYPE_PBKDF2:
|
||||||
|
/* 131 K iterations */
|
||||||
|
spec_out[PBKDF2_SPEC_LEN-1] = 17;
|
||||||
|
break;
|
||||||
|
case S2K_TYPE_SCRYPT:
|
||||||
|
if (flags & S2K_FLAG_LOW_MEM) {
|
||||||
|
/* N = 1<<12 */
|
||||||
|
spec_out[SCRYPT_SPEC_LEN-2] = 12;
|
||||||
|
} else {
|
||||||
|
/* N = 1<<15 */
|
||||||
|
spec_out[SCRYPT_SPEC_LEN-2] = 15;
|
||||||
|
}
|
||||||
|
/* r = 8; p = 2. */
|
||||||
|
spec_out[SCRYPT_SPEC_LEN-1] = (3u << 4) | (1u << 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
tor_fragile_assert();
|
||||||
|
return S2K_BAD_ALGORITHM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return speclen;
|
||||||
|
}
|
||||||
|
|
||||||
/** Implement RFC2440-style iterated-salted S2K conversion: convert the
|
/** Implement RFC2440-style iterated-salted S2K conversion: convert the
|
||||||
* <b>secret_len</b>-byte <b>secret</b> into a <b>key_out_len</b> byte
|
* <b>secret_len</b>-byte <b>secret</b> into a <b>key_out_len</b> byte
|
||||||
@ -51,3 +210,249 @@ secret_to_key_rfc2440(char *key_out, size_t key_out_len, const char *secret,
|
|||||||
tor_free(tmp);
|
tor_free(tmp);
|
||||||
crypto_digest_free(d);
|
crypto_digest_free(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper: given a valid specifier without prefix type byte in <b>spec</b>,
|
||||||
|
* whose length must be correct, and given a secret passphrase <b>secret</b>
|
||||||
|
* of length <b>secret_len</b>, compute the key and store it into
|
||||||
|
* <b>key_out</b>, which must have enough room for secret_to_key_key_len(type)
|
||||||
|
* bytes. Return the number of bytes written on success and an error code
|
||||||
|
* on failure.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
secret_to_key_compute_key(uint8_t *key_out, const uint8_t *spec,
|
||||||
|
const char *secret, size_t secret_len,
|
||||||
|
int type)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case S2K_TYPE_RFC2440:
|
||||||
|
secret_to_key_rfc2440((char*)key_out, DIGEST_LEN, secret, secret_len,
|
||||||
|
(const char*)spec);
|
||||||
|
return DIGEST_LEN;
|
||||||
|
|
||||||
|
case S2K_TYPE_PBKDF2: {
|
||||||
|
int iters;
|
||||||
|
if (spec[16] > 31)
|
||||||
|
return S2K_BAD_PARAMS;
|
||||||
|
if (secret_len > INT_MAX)
|
||||||
|
return S2K_BAD_LEN;
|
||||||
|
iters = 1 << spec[16];
|
||||||
|
rv = PKCS5_PBKDF2_HMAC_SHA1(secret, (int)secret_len,
|
||||||
|
spec, 16,
|
||||||
|
1<<spec[16],
|
||||||
|
DIGEST_LEN, key_out);
|
||||||
|
|
||||||
|
if (rv < 0)
|
||||||
|
return S2K_FAILED;
|
||||||
|
return DIGEST_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
case S2K_TYPE_SCRYPT: {
|
||||||
|
#ifdef HAVE_SCRYPT
|
||||||
|
uint64_t N;
|
||||||
|
uint32_t r, p;
|
||||||
|
if (spec[16] > 63)
|
||||||
|
return S2K_BAD_PARAMS;
|
||||||
|
N = ((uint64_t)1) << spec[16];
|
||||||
|
r = 1u << (spec[17] >> 4);
|
||||||
|
p = 1u << (spec[17] & 15);
|
||||||
|
rv = libscrypt_scrypt((const uint8_t*)secret, secret_len,
|
||||||
|
spec, 16, N, r, p, key_out, 32);
|
||||||
|
if (rv < 0)
|
||||||
|
return S2K_FAILED;
|
||||||
|
return DIGEST256_LEN;
|
||||||
|
#else
|
||||||
|
return S2K_NO_SCRYPT_SUPPORT;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return S2K_BAD_ALGORITHM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a specifier previously constructed with secret_to_key_make_specifier
|
||||||
|
* in <b>spec</b> of length <b>spec_len</b>, and a secret password in
|
||||||
|
* <b>secret</b> of length <b>secret_len</b>, generate <b>key_out_len</b>
|
||||||
|
* bytes of cryptographic material in <b>key_out</b>. The native output of
|
||||||
|
* the secret-to-key function will be truncated if key_out_len is short, and
|
||||||
|
* expanded with HKDF if key_out_len is long. Returns S2K_OKAY on success,
|
||||||
|
* and an error code on failure.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
secret_to_key_derivekey(uint8_t *key_out, size_t key_out_len,
|
||||||
|
const uint8_t *spec, size_t spec_len,
|
||||||
|
const char *secret, size_t secret_len)
|
||||||
|
{
|
||||||
|
int legacy_format = 0;
|
||||||
|
int type = secret_to_key_get_type(spec, spec_len, 0, &legacy_format);
|
||||||
|
int keylen, r;
|
||||||
|
uint8_t buf[32];
|
||||||
|
|
||||||
|
if (type < 0)
|
||||||
|
return type;
|
||||||
|
#ifndef HAVE_SCRYPT
|
||||||
|
if (type == S2K_TYPE_SCRYPT)
|
||||||
|
return S2K_NO_SCRYPT_SUPPORT;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (! legacy_format) {
|
||||||
|
++spec;
|
||||||
|
--spec_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
keylen = secret_to_key_key_len(type);
|
||||||
|
tor_assert(keylen > 0);
|
||||||
|
tor_assert(keylen <= (int)sizeof(buf));
|
||||||
|
|
||||||
|
r = secret_to_key_compute_key(buf, spec, secret, secret_len, type);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
tor_assert(r == keylen);
|
||||||
|
if (key_out_len <= sizeof(buf)) {
|
||||||
|
memcpy(key_out, buf, key_out_len);
|
||||||
|
r = S2K_OKAY;
|
||||||
|
} else {
|
||||||
|
r = crypto_expand_key_material_rfc5869_sha256(buf, keylen,
|
||||||
|
spec, spec_len,
|
||||||
|
(const uint8_t*)"EXPAND", 6,
|
||||||
|
key_out, key_out_len);
|
||||||
|
if (r < 0)
|
||||||
|
r = S2K_FAILED;
|
||||||
|
else
|
||||||
|
r = S2K_OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
memwipe(buf, 0, sizeof(buf));
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new s2k algorithm specifier and salt in <b>buf</b>, according
|
||||||
|
* to the bitwise-or of some S2K_FLAG_* options in <b>flags</b>. Up to
|
||||||
|
* <b>buf_len</b> bytes of storage may be used in <b>buf</b>. Return the
|
||||||
|
* number of bytes used on success and an error code on failure.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
secret_to_key_make_specifier(uint8_t *buf, size_t buf_len, unsigned flags)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
int spec_len;
|
||||||
|
#ifdef HAVE_SCRYPT
|
||||||
|
uint8_t type = S2K_TYPE_SCRYPT;
|
||||||
|
#else
|
||||||
|
uint8_t type = S2K_TYPE_RFC2440;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (flags & S2K_FLAG_NO_SCRYPT)
|
||||||
|
type = S2K_TYPE_RFC2440;
|
||||||
|
if (flags & S2K_FLAG_USE_PBKDF2)
|
||||||
|
type = S2K_TYPE_PBKDF2;
|
||||||
|
|
||||||
|
spec_len = secret_to_key_spec_len(type);
|
||||||
|
|
||||||
|
if ((int)buf_len < spec_len + 1)
|
||||||
|
return S2K_TRUNCATED;
|
||||||
|
|
||||||
|
buf[0] = type;
|
||||||
|
rv = make_specifier(buf+1, type, flags);
|
||||||
|
if (rv < 0)
|
||||||
|
return rv;
|
||||||
|
else
|
||||||
|
return rv + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash a passphrase from <b>secret</b> of length <b>secret_len</b>, according
|
||||||
|
* to the bitwise-or of some S2K_FLAG_* options in <b>flags</b>, and store the
|
||||||
|
* hash along with salt and hashing parameters into <b>buf</b>. Up to
|
||||||
|
* <b>buf_len</b> bytes of storage may be used in <b>buf</b>. Set
|
||||||
|
* *<b>len_out</b> to the number of bytes used and return S2K_OKAY on success;
|
||||||
|
* and return an error code on failure.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
secret_to_key_new(uint8_t *buf,
|
||||||
|
size_t buf_len,
|
||||||
|
size_t *len_out,
|
||||||
|
const char *secret, size_t secret_len,
|
||||||
|
unsigned flags)
|
||||||
|
{
|
||||||
|
int key_len;
|
||||||
|
int spec_len;
|
||||||
|
int type;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
spec_len = secret_to_key_make_specifier(buf, buf_len, flags);
|
||||||
|
|
||||||
|
if (spec_len < 0)
|
||||||
|
return spec_len;
|
||||||
|
|
||||||
|
type = buf[0];
|
||||||
|
key_len = secret_to_key_key_len(type);
|
||||||
|
|
||||||
|
if ((int)buf_len < key_len + spec_len)
|
||||||
|
return S2K_TRUNCATED;
|
||||||
|
|
||||||
|
rv = secret_to_key_compute_key(buf + spec_len, buf + 1,
|
||||||
|
secret, secret_len, type);
|
||||||
|
if (rv < 0)
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
*len_out = spec_len + key_len;
|
||||||
|
|
||||||
|
return S2K_OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a hashed passphrase in <b>spec_and_key</b> of length
|
||||||
|
* <b>spec_and_key_len</b> as generated by secret_to_key_new(), verify whether
|
||||||
|
* it is a hash of the passphrase <b>secret</b> of length <b>secret_len</b>.
|
||||||
|
* Return S2K_OKAY on a match, S2K_BAD_SECRET on a well-formed hash that
|
||||||
|
* doesn't match this secret, and another error code on other errors.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
secret_to_key_check(const uint8_t *spec_and_key, size_t spec_and_key_len,
|
||||||
|
const char *secret, size_t secret_len)
|
||||||
|
{
|
||||||
|
int is_legacy = 0;
|
||||||
|
int type = secret_to_key_get_type(spec_and_key, spec_and_key_len,
|
||||||
|
1, &is_legacy);
|
||||||
|
uint8_t buf[32];
|
||||||
|
int spec_len;
|
||||||
|
int key_len;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
if (type < 0)
|
||||||
|
return type;
|
||||||
|
|
||||||
|
if (! is_legacy) {
|
||||||
|
spec_and_key++;
|
||||||
|
spec_and_key_len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
spec_len = secret_to_key_spec_len(type);
|
||||||
|
key_len = secret_to_key_key_len(type);
|
||||||
|
tor_assert(spec_len > 0);
|
||||||
|
tor_assert(key_len > 0);
|
||||||
|
tor_assert(key_len <= (int) sizeof(buf));
|
||||||
|
tor_assert((int)spec_and_key_len == spec_len + key_len);
|
||||||
|
rv = secret_to_key_compute_key(buf,
|
||||||
|
spec_and_key,
|
||||||
|
secret, secret_len, type);
|
||||||
|
if (rv < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (tor_memeq(buf, spec_and_key + spec_len, key_len))
|
||||||
|
rv = S2K_OKAY;
|
||||||
|
else
|
||||||
|
rv = S2K_BAD_SECRET;
|
||||||
|
|
||||||
|
done:
|
||||||
|
memwipe(buf, 0, sizeof(buf));
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
66
src/common/crypto_s2k.h
Normal file
66
src/common/crypto_s2k.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/* Copyright (c) 2001, Matej Pfajfar.
|
||||||
|
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||||
|
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||||
|
* Copyright (c) 2007-2013, The Tor Project, Inc. */
|
||||||
|
/* See LICENSE for licensing information */
|
||||||
|
|
||||||
|
#ifndef TOR_CRYPTO_S2K_H_INCLUDED
|
||||||
|
#define TOR_CRYPTO_S2K_H_INCLUDED
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "torint.h"
|
||||||
|
|
||||||
|
/** Length of RFC2440-style S2K specifier: the first 8 bytes are a salt, the
|
||||||
|
* 9th describes how much iteration to do. */
|
||||||
|
#define S2K_RFC2440_SPECIFIER_LEN 9
|
||||||
|
void secret_to_key_rfc2440(
|
||||||
|
char *key_out, size_t key_out_len, const char *secret,
|
||||||
|
size_t secret_len, const char *s2k_specifier);
|
||||||
|
|
||||||
|
/** Flag for secret-to-key function: do not use scrypt. */
|
||||||
|
#define S2K_FLAG_NO_SCRYPT (1u<<0)
|
||||||
|
/** Flag for secret-to-key functions: if using a memory-tuned s2k function,
|
||||||
|
* assume that we have limited memory. */
|
||||||
|
#define S2K_FLAG_LOW_MEM (1u<<1)
|
||||||
|
/** Flag for secret-to-key functions: force use of pbkdf2. Without this, we
|
||||||
|
* default to scrypt, then RFC2440. */
|
||||||
|
#define S2K_FLAG_USE_PBKDF2 (1u<<2)
|
||||||
|
|
||||||
|
/** Maximum possible output length from secret_to_key_new. */
|
||||||
|
#define S2K_MAXLEN 64
|
||||||
|
|
||||||
|
/** Error code from secret-to-key functions: all is well */
|
||||||
|
#define S2K_OKAY 0
|
||||||
|
/** Error code from secret-to-key functions: generic failure */
|
||||||
|
#define S2K_FAILED -1
|
||||||
|
/** Error code from secret-to-key functions: provided secret didn't match */
|
||||||
|
#define S2K_BAD_SECRET -2
|
||||||
|
/** Error code from secret-to-key functions: didn't recognize the algorithm */
|
||||||
|
#define S2K_BAD_ALGORITHM -3
|
||||||
|
/** Error code from secret-to-key functions: specifier wasn't valid */
|
||||||
|
#define S2K_BAD_PARAMS -4
|
||||||
|
/** Error code from secret-to-key functions: compiled without scrypt */
|
||||||
|
#define S2K_NO_SCRYPT_SUPPORT -5
|
||||||
|
/** Error code from secret-to-key functions: not enough space to write output.
|
||||||
|
*/
|
||||||
|
#define S2K_TRUNCATED -6
|
||||||
|
/** Error code from secret-to-key functions: Wrong length for specifier. */
|
||||||
|
#define S2K_BAD_LEN -7
|
||||||
|
|
||||||
|
int secret_to_key_new(uint8_t *buf,
|
||||||
|
size_t buf_len,
|
||||||
|
size_t *len_out,
|
||||||
|
const char *secret, size_t secret_len,
|
||||||
|
unsigned flags);
|
||||||
|
|
||||||
|
int secret_to_key_make_specifier(uint8_t *buf, size_t buf_len, unsigned flags);
|
||||||
|
|
||||||
|
int secret_to_key_check(const uint8_t *spec_and_key, size_t spec_and_key_len,
|
||||||
|
const char *secret, size_t secret_len);
|
||||||
|
|
||||||
|
int secret_to_key_derivekey(uint8_t *key_out, size_t key_out_len,
|
||||||
|
const uint8_t *spec, size_t spec_len,
|
||||||
|
const char *secret, size_t secret_len);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -111,6 +111,7 @@ COMMONHEADERS = \
|
|||||||
src/common/container.h \
|
src/common/container.h \
|
||||||
src/common/crypto.h \
|
src/common/crypto.h \
|
||||||
src/common/crypto_curve25519.h \
|
src/common/crypto_curve25519.h \
|
||||||
|
src/common/crypto_s2k.h \
|
||||||
src/common/di_ops.h \
|
src/common/di_ops.h \
|
||||||
src/common/memarea.h \
|
src/common/memarea.h \
|
||||||
src/common/linux_syscalls.inc \
|
src/common/linux_syscalls.inc \
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "crypto_s2k.h"
|
||||||
#include "procmon.h"
|
#include "procmon.h"
|
||||||
|
|
||||||
/** Yield true iff <b>s</b> is the state of a control_connection_t that has
|
/** Yield true iff <b>s</b> is the state of a control_connection_t that has
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "connection_or.h"
|
#include "connection_or.h"
|
||||||
#include "control.h"
|
#include "control.h"
|
||||||
#include "cpuworker.h"
|
#include "cpuworker.h"
|
||||||
|
#include "crypto_s2k.h"
|
||||||
#include "directory.h"
|
#include "directory.h"
|
||||||
#include "dirserv.h"
|
#include "dirserv.h"
|
||||||
#include "dirvote.h"
|
#include "dirvote.h"
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#ifdef CURVE25519_ENABLED
|
#ifdef CURVE25519_ENABLED
|
||||||
#include "crypto_curve25519.h"
|
#include "crypto_curve25519.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "crypto_s2k.h"
|
||||||
|
|
||||||
extern const char AUTHORITY_SIGNKEY_3[];
|
extern const char AUTHORITY_SIGNKEY_3[];
|
||||||
extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
|
extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
|
||||||
@ -696,7 +697,7 @@ test_crypto_formats(void)
|
|||||||
|
|
||||||
/** Run unit tests for our secret-to-key passphrase hashing functionality. */
|
/** Run unit tests for our secret-to-key passphrase hashing functionality. */
|
||||||
static void
|
static void
|
||||||
test_crypto_s2k(void)
|
test_crypto_s2k_rfc2440(void)
|
||||||
{
|
{
|
||||||
char buf[29];
|
char buf[29];
|
||||||
char buf2[29];
|
char buf2[29];
|
||||||
@ -727,6 +728,165 @@ test_crypto_s2k(void)
|
|||||||
tor_free(buf3);
|
tor_free(buf3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
run_s2k_tests(const unsigned flags, const unsigned type,
|
||||||
|
int speclen, const int keylen, int legacy)
|
||||||
|
{
|
||||||
|
uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN], buf3[S2K_MAXLEN];
|
||||||
|
int r;
|
||||||
|
size_t sz;
|
||||||
|
const char pw1[] = "You can't come in here unless you say swordfish!";
|
||||||
|
const char pw2[] = "Now, I give you one more guess.";
|
||||||
|
|
||||||
|
r = secret_to_key_new(buf, sizeof(buf), &sz,
|
||||||
|
pw1, strlen(pw1), flags);
|
||||||
|
tt_int_op(r, ==, S2K_OKAY);
|
||||||
|
tt_int_op(buf[0], ==, type);
|
||||||
|
|
||||||
|
tt_int_op(sz, ==, keylen + speclen);
|
||||||
|
|
||||||
|
if (legacy) {
|
||||||
|
memmove(buf, buf+1, sz-1);
|
||||||
|
--sz;
|
||||||
|
--speclen;
|
||||||
|
}
|
||||||
|
|
||||||
|
tt_int_op(S2K_OKAY, ==,
|
||||||
|
secret_to_key_check(buf, sz, pw1, strlen(pw1)));
|
||||||
|
|
||||||
|
tt_int_op(S2K_BAD_SECRET, ==,
|
||||||
|
secret_to_key_check(buf, sz, pw2, strlen(pw2)));
|
||||||
|
|
||||||
|
/* Move key to buf2, and clear it. */
|
||||||
|
memset(buf3, 0, sizeof(buf3));
|
||||||
|
memcpy(buf2, buf+speclen, keylen);
|
||||||
|
memset(buf+speclen, 0, sz - speclen);
|
||||||
|
|
||||||
|
/* Derivekey should produce the same results. */
|
||||||
|
tt_int_op(S2K_OKAY, ==,
|
||||||
|
secret_to_key_derivekey(buf3, keylen, buf, speclen, pw1, strlen(pw1)));
|
||||||
|
|
||||||
|
tt_mem_op(buf2, ==, buf3, keylen);
|
||||||
|
|
||||||
|
/* Derivekey with a longer output should fill the output. */
|
||||||
|
memset(buf2, 0, sizeof(buf2));
|
||||||
|
tt_int_op(S2K_OKAY, ==,
|
||||||
|
secret_to_key_derivekey(buf2, sizeof(buf2), buf, speclen,
|
||||||
|
pw1, strlen(pw1)));
|
||||||
|
|
||||||
|
tt_mem_op(buf2, !=, buf3, keylen);
|
||||||
|
|
||||||
|
memset(buf3, 0, sizeof(buf3));
|
||||||
|
tt_int_op(S2K_OKAY, ==,
|
||||||
|
secret_to_key_derivekey(buf3, sizeof(buf3), buf, speclen,
|
||||||
|
pw1, strlen(pw1)));
|
||||||
|
tt_mem_op(buf2, ==, buf3, sizeof(buf3));
|
||||||
|
tt_assert(!tor_mem_is_zero((char*)buf2+keylen, sizeof(buf2)-keylen));
|
||||||
|
|
||||||
|
done:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_crypto_s2k_general(void *arg)
|
||||||
|
{
|
||||||
|
const char *which = arg;
|
||||||
|
|
||||||
|
if (!strcmp(which, "scrypt")) {
|
||||||
|
run_s2k_tests(0, 2, 19, 32, 0);
|
||||||
|
} else if (!strcmp(which, "scrypt-low")) {
|
||||||
|
run_s2k_tests(S2K_FLAG_LOW_MEM, 2, 19, 32, 0);
|
||||||
|
} else if (!strcmp(which, "pbkdf2")) {
|
||||||
|
run_s2k_tests(S2K_FLAG_USE_PBKDF2, 1, 18, 20, 0);
|
||||||
|
} else if (!strcmp(which, "rfc2440")) {
|
||||||
|
run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 0);
|
||||||
|
} else if (!strcmp(which, "rfc2440-legacy")) {
|
||||||
|
run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 1);
|
||||||
|
} else {
|
||||||
|
tt_fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_crypto_s2k_errors(void *arg)
|
||||||
|
{
|
||||||
|
uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN];
|
||||||
|
size_t sz;
|
||||||
|
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
/* Bogus specifiers: simple */
|
||||||
|
tt_int_op(S2K_BAD_LEN, ==,
|
||||||
|
secret_to_key_derivekey(buf, sizeof(buf),
|
||||||
|
(const uint8_t*)"", 0, "ABC", 3));
|
||||||
|
tt_int_op(S2K_BAD_ALGORITHM, ==,
|
||||||
|
secret_to_key_derivekey(buf, sizeof(buf),
|
||||||
|
(const uint8_t*)"\x10", 1, "ABC", 3));
|
||||||
|
tt_int_op(S2K_BAD_LEN, ==,
|
||||||
|
secret_to_key_derivekey(buf, sizeof(buf),
|
||||||
|
(const uint8_t*)"\x01\x02", 2, "ABC", 3));
|
||||||
|
|
||||||
|
tt_int_op(S2K_BAD_LEN, ==,
|
||||||
|
secret_to_key_check((const uint8_t*)"", 0, "ABC", 3));
|
||||||
|
tt_int_op(S2K_BAD_ALGORITHM, ==,
|
||||||
|
secret_to_key_check((const uint8_t*)"\x10", 1, "ABC", 3));
|
||||||
|
tt_int_op(S2K_BAD_LEN, ==,
|
||||||
|
secret_to_key_check((const uint8_t*)"\x01\x02", 2, "ABC", 3));
|
||||||
|
|
||||||
|
/* too long gets "BAD_LEN" too */
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
buf[0] = 2;
|
||||||
|
tt_int_op(S2K_BAD_LEN, ==,
|
||||||
|
secret_to_key_derivekey(buf2, sizeof(buf2),
|
||||||
|
buf, sizeof(buf), "ABC", 3));
|
||||||
|
|
||||||
|
/* Truncated output */
|
||||||
|
#ifdef HAVE_LIBSCRYPT_H
|
||||||
|
tt_int_op(S2K_TRUNCATED, ==, secret_to_key_new(buf, 50, &sz,
|
||||||
|
"ABC", 3, 0));
|
||||||
|
tt_int_op(S2K_TRUNCATED, ==, secret_to_key_new(buf, 50, &sz,
|
||||||
|
"ABC", 3, S2K_FLAG_LOW_MEM));
|
||||||
|
#endif
|
||||||
|
tt_int_op(S2K_TRUNCATED, ==, secret_to_key_new(buf, 37, &sz,
|
||||||
|
"ABC", 3, S2K_FLAG_USE_PBKDF2));
|
||||||
|
tt_int_op(S2K_TRUNCATED, ==, secret_to_key_new(buf, 29, &sz,
|
||||||
|
"ABC", 3, S2K_FLAG_NO_SCRYPT));
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBSCRYPT_H
|
||||||
|
tt_int_op(S2K_TRUNCATED, ==, secret_to_key_make_specifier(buf, 18, 0));
|
||||||
|
tt_int_op(S2K_TRUNCATED, ==, secret_to_key_make_specifier(buf, 18,
|
||||||
|
S2K_FLAG_LOW_MEM));
|
||||||
|
#endif
|
||||||
|
tt_int_op(S2K_TRUNCATED, ==, secret_to_key_make_specifier(buf, 17,
|
||||||
|
S2K_FLAG_USE_PBKDF2));
|
||||||
|
tt_int_op(S2K_TRUNCATED, ==, secret_to_key_make_specifier(buf, 9,
|
||||||
|
S2K_FLAG_NO_SCRYPT));
|
||||||
|
|
||||||
|
/* Now try using type-specific bogus specifiers. */
|
||||||
|
|
||||||
|
/* It's a bad pbkdf2 buffer if it has an iteration count that would overflow
|
||||||
|
* int32_t. */
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
buf[0] = 1; /* pbkdf2 */
|
||||||
|
buf[17] = 100; /* 1<<100 is much bigger than INT32_MAX */
|
||||||
|
tt_int_op(S2K_BAD_PARAMS, ==,
|
||||||
|
secret_to_key_derivekey(buf2, sizeof(buf2),
|
||||||
|
buf, 18, "ABC", 3));
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBSCRYPT_H
|
||||||
|
/* It's a bad scrypt buffer if N would overflow uint64 */
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
buf[0] = 2; /* scrypt */
|
||||||
|
buf[17] = 100; /* 1<<100 is much bigger than UINT64_MAX */
|
||||||
|
tt_int_op(S2K_BAD_PARAMS, ==,
|
||||||
|
secret_to_key_derivekey(buf2, sizeof(buf2),
|
||||||
|
buf, 19, "ABC", 3));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
done:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
/** Test AES-CTR encryption and decryption with IV. */
|
/** Test AES-CTR encryption and decryption with IV. */
|
||||||
static void
|
static void
|
||||||
test_crypto_aes_iv(void *arg)
|
test_crypto_aes_iv(void *arg)
|
||||||
@ -1288,7 +1448,20 @@ struct testcase_t crypto_tests[] = {
|
|||||||
{ "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL },
|
{ "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL },
|
||||||
CRYPTO_LEGACY(digests),
|
CRYPTO_LEGACY(digests),
|
||||||
CRYPTO_LEGACY(dh),
|
CRYPTO_LEGACY(dh),
|
||||||
CRYPTO_LEGACY(s2k),
|
CRYPTO_LEGACY(s2k_rfc2440),
|
||||||
|
#ifdef HAVE_LIBSCRYPT_H
|
||||||
|
{ "s2k_scrypt", test_crypto_s2k_general, 0, &pass_data,
|
||||||
|
(void*)"scrypt" },
|
||||||
|
{ "s2k_scrypt_low", test_crypto_s2k_general, 0, &pass_data,
|
||||||
|
(void*)"scrypt-low" },
|
||||||
|
#endif
|
||||||
|
{ "s2k_pbkdf2", test_crypto_s2k_general, 0, &pass_data,
|
||||||
|
(void*)"pbkdf2" },
|
||||||
|
{ "s2k_rfc2440_general", test_crypto_s2k_general, 0, &pass_data,
|
||||||
|
(void*)"rfc2440" },
|
||||||
|
{ "s2k_rfc2440_legacy", test_crypto_s2k_general, 0, &pass_data,
|
||||||
|
(void*)"rfc2440-legacy" },
|
||||||
|
{ "s2k_errors", test_crypto_s2k_errors, 0, NULL, NULL },
|
||||||
{ "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"aes" },
|
{ "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"aes" },
|
||||||
{ "aes_iv_EVP", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"evp" },
|
{ "aes_iv_EVP", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"evp" },
|
||||||
CRYPTO_LEGACY(base32_decode),
|
CRYPTO_LEGACY(base32_decode),
|
||||||
|
Loading…
Reference in New Issue
Block a user