diff --git a/configure.ac b/configure.ac index 414c72a42..b93683fa8 100644 --- a/configure.ac +++ b/configure.ac @@ -153,6 +153,9 @@ AC_ARG_ENABLE(tool-name-check, AC_ARG_ENABLE(seccomp, 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 AN_MAKEVAR([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]) 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 We need an implementation of curve25519. diff --git a/src/common/crypto.h b/src/common/crypto.h index ba6fe84f0..39bbdb571 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -280,13 +280,6 @@ int digest_from_base64(char *digest, const char *d64); int digest256_to_base64(char *d64, const char *digest); 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. */ void memwipe(void *mem, uint8_t byte, size_t sz); diff --git a/src/common/crypto_s2k.c b/src/common/crypto_s2k.c index 669ee5301..8f08a2282 100644 --- a/src/common/crypto_s2k.c +++ b/src/common/crypto_s2k.c @@ -7,6 +7,165 @@ #include "crypto.h" #include "util.h" #include "compat.h" +#include "crypto_s2k.h" + +#include + +#ifdef HAVE_LIBSCRYPT_H +#define HAVE_SCRYPT +#include +#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 spec_and_key of length + * spec_and_key_len, along with its prefix algorithm ID byte, and along + * with a key if key_included is true, check whether the whole + * specifier-and-key is of valid length, and return the algorithm type if it + * is. Set *legacy_out 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 type, without prefixing + * type byte, to spec_out, which must have enough room. May adjust + * parameter choice based on flags. + */ +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 * secret_len-byte secret into a key_out_len byte @@ -51,3 +210,249 @@ secret_to_key_rfc2440(char *key_out, size_t key_out_len, const char *secret, tor_free(tmp); crypto_digest_free(d); } + +/** + * Helper: given a valid specifier without prefix type byte in spec, + * whose length must be correct, and given a secret passphrase secret + * of length secret_len, compute the key and store it into + * key_out, 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< 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 spec of length spec_len, and a secret password in + * secret of length secret_len, generate key_out_len + * bytes of cryptographic material in key_out. 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 buf, according + * to the bitwise-or of some S2K_FLAG_* options in flags. Up to + * buf_len bytes of storage may be used in buf. 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 secret of length secret_len, according + * to the bitwise-or of some S2K_FLAG_* options in flags, and store the + * hash along with salt and hashing parameters into buf. Up to + * buf_len bytes of storage may be used in buf. Set + * *len_out 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 spec_and_key of length + * spec_and_key_len as generated by secret_to_key_new(), verify whether + * it is a hash of the passphrase secret of length secret_len. + * 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; +} + diff --git a/src/common/crypto_s2k.h b/src/common/crypto_s2k.h new file mode 100644 index 000000000..abd801431 --- /dev/null +++ b/src/common/crypto_s2k.h @@ -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 +#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 + diff --git a/src/common/include.am b/src/common/include.am index 7a8cc5ce8..0fb196272 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -111,6 +111,7 @@ COMMONHEADERS = \ src/common/container.h \ src/common/crypto.h \ src/common/crypto_curve25519.h \ + src/common/crypto_s2k.h \ src/common/di_ops.h \ src/common/memarea.h \ src/common/linux_syscalls.inc \ diff --git a/src/or/control.c b/src/or/control.c index 08cdad494..0062e8180 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -47,6 +47,7 @@ #include #endif +#include "crypto_s2k.h" #include "procmon.h" /** Yield true iff s is the state of a control_connection_t that has diff --git a/src/or/main.c b/src/or/main.c index 4ead8aa35..61efc1f6f 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -28,6 +28,7 @@ #include "connection_or.h" #include "control.h" #include "cpuworker.h" +#include "crypto_s2k.h" #include "directory.h" #include "dirserv.h" #include "dirvote.h" diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index d0024750c..d36d65745 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -13,6 +13,7 @@ #ifdef CURVE25519_ENABLED #include "crypto_curve25519.h" #endif +#include "crypto_s2k.h" extern const char AUTHORITY_SIGNKEY_3[]; 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. */ static void -test_crypto_s2k(void) +test_crypto_s2k_rfc2440(void) { char buf[29]; char buf2[29]; @@ -727,6 +728,165 @@ test_crypto_s2k(void) 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. */ static void 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 }, CRYPTO_LEGACY(digests), 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_EVP", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"evp" }, CRYPTO_LEGACY(base32_decode),