diff --git a/.gitignore b/.gitignore index 8bbce2051..744c2b854 100644 --- a/.gitignore +++ b/.gitignore @@ -174,6 +174,10 @@ cscope.* /src/tools/Makefile /src/tools/Makefile.in +# /src/trunnel/ +/src/trunnel/libor-trunnel-testing.a +/src/trunnel/libor-trunnel.a + # /src/tools/tor-fw-helper/ /src/tools/tor-fw-helper/tor-fw-helper /src/tools/tor-fw-helper/tor-fw-helper.exe diff --git a/Makefile.am b/Makefile.am index 910cb1200..7125c7701 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,7 +23,6 @@ include src/include.am include doc/include.am include contrib/include.am - EXTRA_DIST+= \ ChangeLog \ INSTALL \ diff --git a/configure.ac b/configure.ac index 5aeeeb402..3d3509963 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]) @@ -722,6 +725,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.c b/src/common/crypto.c index 014c83e85..fa91f6dd8 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -3001,50 +3001,6 @@ base32_decode(char *dest, size_t destlen, const char *src, size_t srclen) return 0; } -/** Implement RFC2440-style iterated-salted S2K conversion: convert the - * secret_len-byte secret into a key_out_len byte - * key_out. As in RFC2440, the first 8 bytes of s2k_specifier - * are a salt; the 9th byte describes how much iteration to do. - * Does not support key_out_len > DIGEST_LEN. - */ -void -secret_to_key(char *key_out, size_t key_out_len, const char *secret, - size_t secret_len, const char *s2k_specifier) -{ - crypto_digest_t *d; - uint8_t c; - size_t count, tmplen; - char *tmp; - tor_assert(key_out_len < SIZE_T_CEILING); - -#define EXPBIAS 6 - c = s2k_specifier[8]; - count = ((uint32_t)16 + (c & 15)) << ((c >> 4) + EXPBIAS); -#undef EXPBIAS - - tor_assert(key_out_len <= DIGEST_LEN); - - d = crypto_digest_new(); - tmplen = 8+secret_len; - tmp = tor_malloc(tmplen); - memcpy(tmp,s2k_specifier,8); - memcpy(tmp+8,secret,secret_len); - secret_len += 8; - while (count) { - if (count >= secret_len) { - crypto_digest_add_bytes(d, tmp, secret_len); - count -= secret_len; - } else { - crypto_digest_add_bytes(d, tmp, count); - count = 0; - } - } - crypto_digest_get_digest(d, key_out, key_out_len); - memwipe(tmp, 0, tmplen); - tor_free(tmp); - crypto_digest_free(d); -} - /** * Destroy the sz bytes of data stored at mem, setting them to * the value byte. diff --git a/src/common/crypto.h b/src/common/crypto.h index aa4271aa3..39bbdb571 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -280,12 +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_SPECIFIER_LEN 9 -void secret_to_key(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_pwbox.c b/src/common/crypto_pwbox.c new file mode 100644 index 000000000..91659db2b --- /dev/null +++ b/src/common/crypto_pwbox.c @@ -0,0 +1,187 @@ + +#include "crypto.h" +#include "crypto_s2k.h" +#include "crypto_pwbox.h" +#include "di_ops.h" +#include "util.h" +#include "pwbox.h" + +/* 8 bytes "TORBOX00" + 1 byte: header len (H) + H bytes: header, denoting secret key algorithm. + 16 bytes: IV + Round up to multiple of 128 bytes, then encrypt: + 4 bytes: data len + data + zeros + 32 bytes: HMAC-SHA256 of all previous bytes. +*/ + +#define MAX_OVERHEAD (S2K_MAXLEN + 8 + 1 + 32 + CIPHER_IV_LEN) + +/** + * Make an authenticated passphrase-encrypted blob to encode the + * input_len bytes in input using the passphrase + * secret of secret_len bytes. Allocate a new chunk of memory + * to hold the encrypted data, and store a pointer to that memory in + * *out, and its size in outlen_out. Use s2k_flags as an + * argument to the passphrase-hashing function. + */ +int +crypto_pwbox(uint8_t **out, size_t *outlen_out, + const uint8_t *input, size_t input_len, + const char *secret, size_t secret_len, + unsigned s2k_flags) +{ + uint8_t *result = NULL, *encrypted_portion; + size_t encrypted_len = 128 * CEIL_DIV(input_len+4, 128); + ssize_t result_len; + int spec_len; + uint8_t keys[CIPHER_KEY_LEN + DIGEST256_LEN]; + pwbox_encoded_t *enc = NULL; + ssize_t enc_len; + + crypto_cipher_t *cipher; + int rv; + + enc = pwbox_encoded_new(); + + pwbox_encoded_setlen_skey_header(enc, S2K_MAXLEN); + + spec_len = secret_to_key_make_specifier( + pwbox_encoded_getarray_skey_header(enc), + S2K_MAXLEN, + s2k_flags); + if (spec_len < 0 || spec_len > S2K_MAXLEN) + goto err; + pwbox_encoded_setlen_skey_header(enc, spec_len); + enc->header_len = spec_len; + + crypto_rand((char*)enc->iv, sizeof(enc->iv)); + + pwbox_encoded_setlen_data(enc, encrypted_len); + encrypted_portion = pwbox_encoded_getarray_data(enc); + + set_uint32(encrypted_portion, htonl(input_len)); + memcpy(encrypted_portion+4, input, input_len); + + /* Now that all the data is in position, derive some keys, encrypt, and + * digest */ + if (secret_to_key_derivekey(keys, sizeof(keys), + pwbox_encoded_getarray_skey_header(enc), + spec_len, + secret, secret_len) < 0) + goto err; + + cipher = crypto_cipher_new_with_iv((char*)keys, (char*)enc->iv); + crypto_cipher_crypt_inplace(cipher, (char*)encrypted_portion, encrypted_len); + crypto_cipher_free(cipher); + + result_len = pwbox_encoded_encoded_len(enc); + if (result_len < 0) + goto err; + result = tor_malloc(result_len); + enc_len = pwbox_encoded_encode(result, result_len, enc); + if (enc_len < 0) + goto err; + tor_assert(enc_len == result_len); + + crypto_hmac_sha256((char*) result + result_len - 32, + (const char*)keys + CIPHER_KEY_LEN, + DIGEST256_LEN, + (const char*)result, + result_len - 32); + + *out = result; + *outlen_out = result_len; + rv = 0; + goto out; + + err: + tor_free(result); + rv = -1; + + out: + pwbox_encoded_free(enc); + memwipe(keys, 0, sizeof(keys)); + return rv; +} + +/** + * Try to decrypt the passphrase-encrypted blob of input_len bytes in + * input using the passphrase secret of secret_len bytes. + * On success, return 0 and allocate a new chunk of memory to hold the + * decrypted data, and store a pointer to that memory in *out, and its + * size in outlen_out. On failure, return UNPWBOX_BAD_SECRET if + * the passphrase might have been wrong, and UNPWBOX_CORRUPT if the object is + * definitely corrupt. + */ +int +crypto_unpwbox(uint8_t **out, size_t *outlen_out, + const uint8_t *inp, size_t input_len, + const char *secret, size_t secret_len) +{ + uint8_t *result = NULL; + const uint8_t *encrypted; + uint8_t keys[CIPHER_KEY_LEN + DIGEST256_LEN]; + uint8_t hmac[DIGEST256_LEN]; + uint32_t result_len; + size_t encrypted_len; + crypto_cipher_t *cipher = NULL; + int rv = UNPWBOX_CORRUPTED; + ssize_t got_len; + + pwbox_encoded_t *enc = NULL; + + got_len = pwbox_encoded_parse(&enc, inp, input_len); + if (got_len < 0 || (size_t)got_len != input_len) + goto err; + + /* Now derive the keys and check the hmac. */ + if (secret_to_key_derivekey(keys, sizeof(keys), + pwbox_encoded_getarray_skey_header(enc), + pwbox_encoded_getlen_skey_header(enc), + secret, secret_len) < 0) + goto err; + + crypto_hmac_sha256((char *)hmac, + (const char*)keys + CIPHER_KEY_LEN, DIGEST256_LEN, + (const char*)inp, input_len - DIGEST256_LEN); + + if (tor_memneq(hmac, enc->hmac, DIGEST256_LEN)) { + rv = UNPWBOX_BAD_SECRET; + goto err; + } + + /* How long is the plaintext? */ + encrypted = pwbox_encoded_getarray_data(enc); + encrypted_len = pwbox_encoded_getlen_data(enc); + if (encrypted_len < 4) + goto err; + + cipher = crypto_cipher_new_with_iv((char*)keys, (char*)enc->iv); + crypto_cipher_decrypt(cipher, (char*)&result_len, (char*)encrypted, 4); + result_len = ntohl(result_len); + if (encrypted_len < result_len + 4) + goto err; + + /* Allocate a buffer and decrypt */ + result = tor_malloc_zero(result_len); + crypto_cipher_decrypt(cipher, (char*)result, (char*)encrypted+4, result_len); + + *out = result; + *outlen_out = result_len; + + rv = UNPWBOX_OKAY; + goto out; + + err: + tor_free(result); + + out: + crypto_cipher_free(cipher); + pwbox_encoded_free(enc); + memwipe(keys, 0, sizeof(keys)); + return rv; +} + diff --git a/src/common/crypto_pwbox.h b/src/common/crypto_pwbox.h new file mode 100644 index 000000000..aadd47707 --- /dev/null +++ b/src/common/crypto_pwbox.h @@ -0,0 +1,20 @@ +#ifndef CRYPTO_PWBOX_H_INCLUDED_ +#define CRYPTO_PWBOX_H_INCLUDED_ + +#include "torint.h" + +#define UNPWBOX_OKAY 0 +#define UNPWBOX_BAD_SECRET -1 +#define UNPWBOX_CORRUPTED -2 + +int crypto_pwbox(uint8_t **out, size_t *outlen_out, + const uint8_t *inp, size_t input_len, + const char *secret, size_t secret_len, + unsigned s2k_flags); + +int crypto_unpwbox(uint8_t **out, size_t *outlen_out, + const uint8_t *inp, size_t input_len, + const char *secret, size_t secret_len); + +#endif + diff --git a/src/common/crypto_s2k.c b/src/common/crypto_s2k.c new file mode 100644 index 000000000..93c96e74a --- /dev/null +++ b/src/common/crypto_s2k.c @@ -0,0 +1,457 @@ +/* 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 */ + +#define CRYPTO_S2K_PRIVATE + +#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 + * key_out. As in RFC2440, the first 8 bytes of s2k_specifier + * are a salt; the 9th byte describes how much iteration to do. + * If key_out_len > DIGEST_LEN, use HDKF to expand the result. + */ +void +secret_to_key_rfc2440(char *key_out, size_t key_out_len, const char *secret, + size_t secret_len, const char *s2k_specifier) +{ + crypto_digest_t *d; + uint8_t c; + size_t count, tmplen; + char *tmp; + uint8_t buf[DIGEST_LEN]; + tor_assert(key_out_len < SIZE_T_CEILING); + +#define EXPBIAS 6 + c = s2k_specifier[8]; + count = ((uint32_t)16 + (c & 15)) << ((c >> 4) + EXPBIAS); +#undef EXPBIAS + + d = crypto_digest_new(); + tmplen = 8+secret_len; + tmp = tor_malloc(tmplen); + memcpy(tmp,s2k_specifier,8); + memcpy(tmp+8,secret,secret_len); + secret_len += 8; + while (count) { + if (count >= secret_len) { + crypto_digest_add_bytes(d, tmp, secret_len); + count -= secret_len; + } else { + crypto_digest_add_bytes(d, tmp, count); + count = 0; + } + } + crypto_digest_get_digest(d, (char*)buf, sizeof(buf)); + + if (key_out_len <= sizeof(buf)) { + memcpy(key_out, buf, key_out_len); + } else { + crypto_expand_key_material_rfc5869_sha256(buf, DIGEST_LEN, + (const uint8_t*)s2k_specifier, 8, + (const uint8_t*)"EXPAND", 6, + (uint8_t*)key_out, key_out_len); + } + memwipe(tmp, 0, tmplen); + memwipe(buf, 0, sizeof(buf)); + 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, size_t key_out_len, + const uint8_t *spec, size_t spec_len, + const char *secret, size_t secret_len, + int type) +{ + int rv; + if (key_out_len > INT_MAX) + return S2K_BAD_LEN; + + switch (type) { + case S2K_TYPE_RFC2440: + secret_to_key_rfc2440((char*)key_out, key_out_len, secret, secret_len, + (const char*)spec); + return (int)key_out_len; + + case S2K_TYPE_PBKDF2: { + uint8_t log_iters; + if (spec_len < 1 || secret_len > INT_MAX || spec_len > INT_MAX) + return S2K_BAD_LEN; + log_iters = spec[spec_len-1]; + if (log_iters > 31) + return S2K_BAD_PARAMS; + rv = PKCS5_PBKDF2_HMAC_SHA1(secret, (int)secret_len, + spec, (int)spec_len-1, + (1<> 4; + log_p = (spec[spec_len-1]) & 15; + if (log_N > 63) + return S2K_BAD_PARAMS; + N = ((uint64_t)1) << log_N; + r = 1u << log_r; + p = 1u << log_p; + rv = libscrypt_scrypt((const uint8_t*)secret, secret_len, + spec, spec_len-2, N, r, p, key_out, key_out_len); + if (rv != 0) + return S2K_FAILED; + return (int)key_out_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 r; + + 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; + } + + r = secret_to_key_compute_key(key_out, key_out_len, spec, spec_len, + secret, secret_len, type); + if (r < 0) + return r; + else + return S2K_OKAY; +} + +/** + * 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, key_len, + buf + 1, spec_len-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, key_len, + spec_and_key, spec_len, + 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..3693a430e --- /dev/null +++ b/src/common/crypto_s2k.h @@ -0,0 +1,73 @@ +/* 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); + +#ifdef CRYPTO_S2K_PRIVATE +STATIC int secret_to_key_compute_key(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 type); +#endif + +#endif + diff --git a/src/common/include.am b/src/common/include.am index 68e0110c2..d669cf473 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -16,7 +16,7 @@ EXTRA_DIST+= \ src/common/Makefile.nmake #CFLAGS = -Wall -Wpointer-arith -O2 -AM_CPPFLAGS += -I$(srcdir)/src/common -Isrc/common +AM_CPPFLAGS += -I$(srcdir)/src/common -Isrc/common -I$(srcdir)/src/ext/trunnel -I$(srcdir)/src/trunnel if USE_OPENBSD_MALLOC libor_extra_source=src/ext/OpenBSD_malloc_Linux.c @@ -69,15 +69,19 @@ LIBOR_A_SOURCES = \ src/common/util_process.c \ src/common/sandbox.c \ src/ext/csiphash.c \ + src/ext/trunnel/trunnel.c \ $(libor_extra_source) \ $(libor_mempool_source) LIBOR_CRYPTO_A_SOURCES = \ src/common/aes.c \ src/common/crypto.c \ + src/common/crypto_pwbox.c \ + src/common/crypto_s2k.c \ src/common/crypto_format.c \ src/common/torgzip.c \ src/common/tortls.c \ + src/trunnel/pwbox.c \ $(libcrypto_extra_source) LIBOR_EVENT_A_SOURCES = \ @@ -110,6 +114,8 @@ COMMONHEADERS = \ src/common/container.h \ src/common/crypto.h \ src/common/crypto_curve25519.h \ + src/common/crypto_pwbox.h \ + src/common/crypto_s2k.h \ src/common/di_ops.h \ src/common/memarea.h \ src/common/linux_syscalls.inc \ diff --git a/src/ext/trunnel/trunnel-impl.h b/src/ext/trunnel/trunnel-impl.h new file mode 100644 index 000000000..4dd710f4a --- /dev/null +++ b/src/ext/trunnel/trunnel-impl.h @@ -0,0 +1,306 @@ +/* trunnel-impl.h -- Implementation helpers for trunnel, included by + * generated trunnel files + * + * Copyright 2014, The Tor Project, Inc. + * See license at the end of this file for copying information. + */ + +#ifndef TRUNNEL_IMPL_H_INCLUDED_ +#define TRUNNEL_IMPL_H_INCLUDED_ +#include "trunnel.h" +#include +#include +#ifdef TRUNNEL_LOCAL_H +#include "trunnel-local.h" +#endif + +#ifdef _MSC_VER +#define uint8_t unsigned char +#define uint16_t unsigned short +#define uint32_t unsigned int +#define uint64_t unsigned __int64 +#define inline __inline +#else +#include +#endif + +#ifdef _WIN32 +uint32_t trunnel_htonl(uint32_t a); +uint32_t trunnel_ntohl(uint32_t a); +uint16_t trunnel_htons(uint16_t a); +uint16_t trunnel_ntohs(uint16_t a); +#else +#include +#define trunnel_htonl(x) htonl(x) +#define trunnel_htons(x) htons(x) +#define trunnel_ntohl(x) ntohl(x) +#define trunnel_ntohs(x) ntohs(x) +#endif +uint64_t trunnel_htonll(uint64_t a); +uint64_t trunnel_ntohll(uint64_t a); + +#ifndef trunnel_assert +#define trunnel_assert(x) assert(x) +#endif + +static inline void +trunnel_set_uint64(void *p, uint64_t v) { + memcpy(p, &v, 8); +} +static inline void +trunnel_set_uint32(void *p, uint32_t v) { + memcpy(p, &v, 4); +} +static inline void +trunnel_set_uint16(void *p, uint16_t v) { + memcpy(p, &v, 2); +} +static inline void +trunnel_set_uint8(void *p, uint8_t v) { + memcpy(p, &v, 1); +} + +static inline uint64_t +trunnel_get_uint64(const void *p) { + uint64_t x; + memcpy(&x, p, 8); + return x; +} +static inline uint32_t +trunnel_get_uint32(const void *p) { + uint32_t x; + memcpy(&x, p, 4); + return x; +} +static inline uint16_t +trunnel_get_uint16(const void *p) { + uint16_t x; + memcpy(&x, p, 2); + return x; +} +static inline uint8_t +trunnel_get_uint8(const void *p) { + return *(const uint8_t*)p; +} + + +#ifdef TRUNNEL_DEBUG_FAILING_ALLOC +extern int trunnel_provoke_alloc_failure; + +static inline void * +trunnel_malloc(size_t n) +{ + if (trunnel_provoke_alloc_failure) { + if (--trunnel_provoke_alloc_failure == 0) + return NULL; + } + return malloc(n); +} +static inline void * +trunnel_calloc(size_t a, size_t b) +{ + if (trunnel_provoke_alloc_failure) { + if (--trunnel_provoke_alloc_failure == 0) + return NULL; + } + return calloc(a,b); +} +static inline char * +trunnel_strdup(const char *s) +{ + if (trunnel_provoke_alloc_failure) { + if (--trunnel_provoke_alloc_failure == 0) + return NULL; + } + return strdup(s); +} +#else +#ifndef trunnel_malloc +#define trunnel_malloc(x) (malloc((x))) +#endif +#ifndef trunnel_calloc +#define trunnel_calloc(a,b) (calloc((a),(b))) +#endif +#ifndef trunnel_strdup +#define trunnel_strdup(s) (strdup((s))) +#endif +#endif + +#ifndef trunnel_realloc +#define trunnel_realloc(a,b) realloc((a),(b)) +#endif + +#ifndef trunnel_free_ +#define trunnel_free_(x) (free(x)) +#endif +#define trunnel_free(x) ((x) ? (trunnel_free_(x),0) : (0)) + +#ifndef trunnel_abort +#define trunnel_abort() abort() +#endif + +#ifndef trunnel_memwipe +#define trunnel_memwipe(mem, len) ((void)0) +#define trunnel_wipestr(s) ((void)0) +#else +#define trunnel_wipestr(s) do { \ + if (s) \ + trunnel_memwipe(s, strlen(s)); \ + } while (0) +#endif + +/* ====== dynamic arrays ======== */ + +#ifdef NDEBUG +#define TRUNNEL_DYNARRAY_GET(da, n) \ + ((da)->elts_[(n)]) +#else +/** Return the 'n'th element of 'da'. */ +#define TRUNNEL_DYNARRAY_GET(da, n) \ + (((n) >= (da)->n_ ? (trunnel_abort(),0) : 0), (da)->elts_[(n)]) +#endif + +/** Change the 'n'th element of 'da' to 'v'. */ +#define TRUNNEL_DYNARRAY_SET(da, n, v) do { \ + trunnel_assert((n) < (da)->n_); \ + (da)->elts_[(n)] = (v); \ + } while (0) + +/** Expand the dynamic array 'da' of 'elttype' so that it can hold at least + * 'howmanymore' elements than its current capacity. Always tries to increase + * the length of the array. On failure, run the code in 'on_fail' and goto + * trunnel_alloc_failed. */ +#define TRUNNEL_DYNARRAY_EXPAND(elttype, da, howmanymore, on_fail) do { \ + elttype *newarray; \ + newarray = trunnel_dynarray_expand(&(da)->allocated_, \ + (da)->elts_, (howmanymore), \ + sizeof(elttype)); \ + if (newarray == NULL) { \ + on_fail; \ + goto trunnel_alloc_failed; \ + } \ + (da)->elts_ = newarray; \ + } while (0) + +/** Add 'v' to the end of the dynamic array 'da' of 'elttype', expanding it if + * necessary. code in 'on_fail' and goto trunnel_alloc_failed. */ +#define TRUNNEL_DYNARRAY_ADD(elttype, da, v, on_fail) do { \ + if ((da)->n_ == (da)->allocated_) { \ + TRUNNEL_DYNARRAY_EXPAND(elttype, da, 1, on_fail); \ + } \ + (da)->elts_[(da)->n_++] = (v); \ + } while (0) + +/** Return the number of elements in 'da'. */ +#define TRUNNEL_DYNARRAY_LEN(da) ((da)->n_) + +/** Remove all storage held by 'da' and set it to be empty. Does not free + * storage held by the elements themselves. */ +#define TRUNNEL_DYNARRAY_CLEAR(da) do { \ + trunnel_free((da)->elts_); \ + (da)->elts_ = NULL; \ + (da)->n_ = (da)->allocated_ = 0; \ + } while (0) + +/** Remove all storage held by 'da' and set it to be empty. Does not free + * storage held by the elements themselves. */ +#define TRUNNEL_DYNARRAY_WIPE(da) do { \ + trunnel_memwipe((da)->elts_, (da)->allocated_ * sizeof((da)->elts_[0])); \ + } while (0) + +/** Helper: wraps or implements an OpenBSD-style reallocarray. Behaves + * as realloc(a, x*y), but verifies that no overflow will occur in the + * multiplication. Returns NULL on failure. */ +#ifndef trunnel_reallocarray +void *trunnel_reallocarray(void *a, size_t x, size_t y); +#endif + +/** Helper to expand a dynamic array. Behaves as TRUNNEL_DYNARRAY_EXPAND(), + * taking the array of elements in 'ptr', a pointer to thethe current number + * of allocated elements in allocated_p, the minimum numbeer of elements to + * add in 'howmanymore', and the size of a single element in 'eltsize'. + * + * On success, adjust *allocated_p, and return the new value for the array of + * elements. On failure, adjust nothing and return NULL. + */ +void *trunnel_dynarray_expand(size_t *allocated_p, void *ptr, + size_t howmanymore, size_t eltsize); + +/** Type for a function to free members of a dynarray of pointers. */ +typedef void (*trunnel_free_fn_t)(void *); + +/** + * Helper to change the length of a dynamic array. Takes pointers to the + * current allocated and n fields of the array in 'allocated_p' and 'len_p', + * and the current array of elements in 'ptr'; takes the length of a single + * element in 'eltsize'. Changes the length to 'newlen'. If 'newlen' is + * greater than the current length, pads the new elements with 0. If newlen + * is less than the current length, and free_fn is non-NULL, treat the + * array as an array of void *, and invoke free_fn() on each removed element. + * + * On success, adjust *allocated_p and *len_p, and return the new value for + * the array of elements. On failure, adjust nothing, set *errcode_ptr to 1, + * and return NULL. + */ +void *trunnel_dynarray_setlen(size_t *allocated_p, size_t *len_p, + void *ptr, size_t newlen, + size_t eltsize, trunnel_free_fn_t free_fn, + uint8_t *errcode_ptr); + +/** + * Helper: return a pointer to the value of 'str' as a NUL-terminated string. + * Might have to reallocate the storage for 'str' in order to fit in the final + * NUL character. On allocation failure, return NULL. + */ +const char *trunnel_string_getstr(trunnel_string_t *str); + +/** + * Helper: change the contents of 'str' to hold the 'len'-byte string in + * 'inp'. Adjusts the storage to have a terminating NUL that doesn't count + * towards the length of the string. On success, return 0. On failure, set + * *errcode_ptr to 1 and return -1. + */ +int trunnel_string_setstr0(trunnel_string_t *str, const char *inp, size_t len, + uint8_t *errcode_ptr); + +/** + * As trunnel_dynarray_setlen, but adjusts a string rather than a dynamic + * array, and ensures that the new string is NUL-terminated. + */ +int trunnel_string_setlen(trunnel_string_t *str, size_t newlen, + uint8_t *errcode_ptr); + +#endif + + +/* +Copyright 2014 The Tor Project, Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + + * Neither the names of the copyright owners nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ diff --git a/src/ext/trunnel/trunnel.c b/src/ext/trunnel/trunnel.c new file mode 100644 index 000000000..4bc28e3f0 --- /dev/null +++ b/src/ext/trunnel/trunnel.c @@ -0,0 +1,242 @@ +/* trunnel.c -- Helper functions to implement trunnel. + * + * Copyright 2014, The Tor Project, Inc. + * See license at the end of this file for copying information. + * + * See trunnel-impl.h for documentation of these functions. + */ + +#include +#include +#include "trunnel-impl.h" + +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define IS_LITTLE_ENDIAN 1 +#elif defined(BYTE_ORDER) && defined(ORDER_LITTLE_ENDIAN) && \ + BYTE_ORDER == __ORDER_LITTLE_ENDIAN +# define IS_LITTLE_ENDIAN 1 +#elif defined(_WIN32) +# define IS_LITTLE_ENDIAN 1 +#elif defined(__APPLE__) +# include +# define BSWAP64(x) OSSwapLittleToHostInt64(x) +#elif defined(sun) || defined(__sun) +# include +# ifndef _BIG_ENDIAN +# define IS_LITTLE_ENDIAN +# endif +#else +# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +# include +# else +# include +# endif +# if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN +# define IS_LITTLE_ENDIAN +# endif +#endif + +#ifdef _WIN32 +uint16_t +trunnel_htons(uint16_t s) +{ + return (s << 8) | (s >> 8); +} +uint16_t +trunnel_ntohs(uint16_t s) +{ + return (s << 8) | (s >> 8); +} +uint32_t +trunnel_htonl(uint32_t s) +{ + return (s << 24) | + ((s << 8)&0xff0000) | + ((s >> 8)&0xff00) | + (s >> 24); +} +uint32_t +trunnel_ntohl(uint32_t s) +{ + return (s << 24) | + ((s << 8)&0xff0000) | + ((s >> 8)&0xff00) | + (s >> 24); +} +#endif + +uint64_t +trunnel_htonll(uint64_t a) +{ +#ifdef IS_LITTLE_ENDIAN + return trunnel_htonl(a>>32) | (((uint64_t)trunnel_htonl(a))<<32); +#else + return a; +#endif +} + +uint64_t +trunnel_ntohll(uint64_t a) +{ + return trunnel_htonll(a); +} + +#ifdef TRUNNEL_DEBUG_FAILING_ALLOC +/** Used for debugging and running tricky test cases: Makes the nth + * memoryation allocation call from now fail. + */ +int trunnel_provoke_alloc_failure = 0; +#endif + +void * +trunnel_dynarray_expand(size_t *allocated_p, void *ptr, + size_t howmanymore, size_t eltsize) +{ + size_t newsize = howmanymore + *allocated_p; + void *newarray = NULL; + if (newsize < 8) + newsize = 8; + if (newsize < *allocated_p * 2) + newsize = *allocated_p * 2; + if (newsize <= *allocated_p || newsize < howmanymore) + return NULL; + newarray = trunnel_reallocarray(ptr, newsize, eltsize); + if (newarray == NULL) + return NULL; + + *allocated_p = newsize; + return newarray; +} + +#ifndef trunnel_reallocarray +void * +trunnel_reallocarray(void *a, size_t x, size_t y) +{ +#ifdef TRUNNEL_DEBUG_FAILING_ALLOC + if (trunnel_provoke_alloc_failure) { + if (--trunnel_provoke_alloc_failure == 0) + return NULL; + } +#endif + if (x > SIZE_MAX / y) + return NULL; + return trunnel_realloc(a, x * y); +} +#endif + +const char * +trunnel_string_getstr(trunnel_string_t *str) +{ + trunnel_assert(str->allocated_ >= str->n_); + if (str->allocated_ == str->n_) { + TRUNNEL_DYNARRAY_EXPAND(char, str, 1, {}); + } + str->elts_[str->n_] = 0; + return str->elts_; +trunnel_alloc_failed: + return NULL; +} + +int +trunnel_string_setstr0(trunnel_string_t *str, const char *val, size_t len, + uint8_t *errcode_ptr) +{ + if (len == SIZE_MAX) + goto trunnel_alloc_failed; + if (str->allocated_ <= len) { + TRUNNEL_DYNARRAY_EXPAND(char, str, len + 1 - str->allocated_, {}); + } + memcpy(str->elts_, val, len); + str->n_ = len; + str->elts_[len] = 0; + return 0; +trunnel_alloc_failed: + *errcode_ptr = 1; + return -1; +} + +int +trunnel_string_setlen(trunnel_string_t *str, size_t newlen, + uint8_t *errcode_ptr) +{ + if (newlen == SIZE_MAX) + goto trunnel_alloc_failed; + if (str->allocated_ < newlen + 1) { + TRUNNEL_DYNARRAY_EXPAND(char, str, newlen + 1 - str->allocated_, {}); + } + if (str->n_ < newlen) { + memset(& (str->elts_[str->n_]), 0, (newlen - str->n_)); + } + str->n_ = newlen; + str->elts_[newlen] = 0; + return 0; + + trunnel_alloc_failed: + *errcode_ptr = 1; + return -1; +} + +void * +trunnel_dynarray_setlen(size_t *allocated_p, size_t *len_p, + void *ptr, size_t newlen, + size_t eltsize, trunnel_free_fn_t free_fn, + uint8_t *errcode_ptr) +{ + if (*allocated_p < newlen) { + void *newptr = trunnel_dynarray_expand(allocated_p, ptr, + newlen - *allocated_p, eltsize); + if (newptr == NULL) + goto trunnel_alloc_failed; + ptr = newptr; + } + if (free_fn && *len_p > newlen) { + size_t i; + void **elts = (void **) ptr; + for (i = newlen; i < *len_p; ++i) { + free_fn(elts[i]); + elts[i] = NULL; + } + } + if (*len_p < newlen) { + memset( ((char*)ptr) + (eltsize * *len_p), 0, (newlen - *len_p) * eltsize); + } + *len_p = newlen; + return ptr; + trunnel_alloc_failed: + *errcode_ptr = 1; + return NULL; +} + +/* +Copyright 2014 The Tor Project, Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + + * Neither the names of the copyright owners nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ diff --git a/src/ext/trunnel/trunnel.h b/src/ext/trunnel/trunnel.h new file mode 100644 index 000000000..0a78e6cfc --- /dev/null +++ b/src/ext/trunnel/trunnel.h @@ -0,0 +1,60 @@ +/* trunnel.h -- Public declarations for trunnel, to be included + * in trunnel header files. + + * Copyright 2014, The Tor Project, Inc. + * See license at the end of this file for copying information. + */ + +#ifndef TRUNNEL_H_INCLUDED_ +#define TRUNNEL_H_INCLUDED_ + +#include + +/** Macro to declare a variable-length dynamically allocated array. Trunnel + * uses these to store all variable-length arrays. */ +#define TRUNNEL_DYNARRAY_HEAD(name, elttype) \ + struct name { \ + size_t n_; \ + size_t allocated_; \ + elttype *elts_; \ + } + +/** Initializer for a dynamic array of a given element type. */ +#define TRUNNEL_DYNARRAY_INIT(elttype) { 0, 0, (elttype*)NULL } + +/** Typedef used for storing variable-length arrays of char. */ +typedef TRUNNEL_DYNARRAY_HEAD(trunnel_string_st, char) trunnel_string_t; + +#endif + +/* +Copyright 2014 The Tor Project, Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + + * Neither the names of the copyright owners nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ diff --git a/src/include.am b/src/include.am index d0693e25b..c468af364 100644 --- a/src/include.am +++ b/src/include.am @@ -1,7 +1,9 @@ include src/ext/include.am +include src/trunnel/include.am include src/common/include.am include src/or/include.am include src/test/include.am include src/tools/include.am include src/win32/include.am include src/config/include.am + diff --git a/src/or/control.c b/src/or/control.c index df694930b..b1709e0d2 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 @@ -995,7 +996,8 @@ handle_control_setevents(control_connection_t *conn, uint32_t len, /** Decode the hashed, base64'd passwords stored in passwords. * Return a smartlist of acceptable passwords (unterminated strings of - * length S2K_SPECIFIER_LEN+DIGEST_LEN) on success, or NULL on failure. + * length S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN) on success, or NULL on + * failure. */ smartlist_t * decode_hashed_passwords(config_line_t *passwords) @@ -1011,16 +1013,17 @@ decode_hashed_passwords(config_line_t *passwords) if (!strcmpstart(hashed, "16:")) { if (base16_decode(decoded, sizeof(decoded), hashed+3, strlen(hashed+3))<0 - || strlen(hashed+3) != (S2K_SPECIFIER_LEN+DIGEST_LEN)*2) { + || strlen(hashed+3) != (S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN)*2) { goto err; } } else { if (base64_decode(decoded, sizeof(decoded), hashed, strlen(hashed)) - != S2K_SPECIFIER_LEN+DIGEST_LEN) { + != S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN) { goto err; } } - smartlist_add(sl, tor_memdup(decoded, S2K_SPECIFIER_LEN+DIGEST_LEN)); + smartlist_add(sl, + tor_memdup(decoded, S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN)); } return sl; @@ -1173,8 +1176,10 @@ handle_control_authenticate(control_connection_t *conn, uint32_t len, } else { SMARTLIST_FOREACH(sl, char *, expected, { - secret_to_key(received,DIGEST_LEN,password,password_len,expected); - if (tor_memeq(expected+S2K_SPECIFIER_LEN, received, DIGEST_LEN)) + secret_to_key_rfc2440(received,DIGEST_LEN, + password,password_len,expected); + if (tor_memeq(expected + S2K_RFC2440_SPECIFIER_LEN, + received, DIGEST_LEN)) goto ok; }); SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); diff --git a/src/or/main.c b/src/or/main.c index 094120fec..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" @@ -2674,11 +2675,11 @@ do_hash_password(void) { char output[256]; - char key[S2K_SPECIFIER_LEN+DIGEST_LEN]; + char key[S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN]; - crypto_rand(key, S2K_SPECIFIER_LEN-1); - key[S2K_SPECIFIER_LEN-1] = (uint8_t)96; /* Hash 64 K of data. */ - secret_to_key(key+S2K_SPECIFIER_LEN, DIGEST_LEN, + crypto_rand(key, S2K_RFC2440_SPECIFIER_LEN-1); + key[S2K_RFC2440_SPECIFIER_LEN-1] = (uint8_t)96; /* Hash 64 K of data. */ + secret_to_key_rfc2440(key+S2K_RFC2440_SPECIFIER_LEN, DIGEST_LEN, get_options()->command_arg, strlen(get_options()->command_arg), key); base16_encode(output, sizeof(output), key, sizeof(key)); diff --git a/src/test/include.am b/src/test/include.am index 77c92f12f..db9f2d1f1 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -60,7 +60,7 @@ src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ src_test_test_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \ src/common/libor-crypto-testing.a $(LIBDONNA) \ - src/common/libor-event-testing.a \ + src/common/libor-event-testing.a src/trunnel/libor-trunnel-testing.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 86b77a662..36af725e7 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -5,6 +5,7 @@ #include "orconfig.h" #define CRYPTO_CURVE25519_PRIVATE +#define CRYPTO_S2K_PRIVATE #include "or.h" #include "test.h" #include "aes.h" @@ -13,6 +14,8 @@ #ifdef CURVE25519_ENABLED #include "crypto_curve25519.h" #endif +#include "crypto_s2k.h" +#include "crypto_pwbox.h" extern const char AUTHORITY_SIGNKEY_3[]; extern const char AUTHORITY_SIGNKEY_A_DIGEST[]; @@ -710,7 +713,7 @@ test_crypto_formats(void *arg) /** Run unit tests for our secret-to-key passphrase hashing functionality. */ static void -test_crypto_s2k(void *arg) +test_crypto_s2k_rfc2440(void *arg) { char buf[29]; char buf2[29]; @@ -723,7 +726,7 @@ test_crypto_s2k(void *arg) buf3 = tor_malloc(65536); memset(buf3, 0, 65536); - secret_to_key(buf+9, 20, "", 0, buf); + secret_to_key_rfc2440(buf+9, 20, "", 0, buf); crypto_digest(buf2+9, buf3, 1024); tt_mem_op(buf,==, buf2, 29); @@ -731,7 +734,7 @@ test_crypto_s2k(void *arg) memcpy(buf2,"vrbacrda",8); buf[8] = 96; buf2[8] = 96; - secret_to_key(buf+9, 20, "12345678", 8, buf); + secret_to_key_rfc2440(buf+9, 20, "12345678", 8, buf); for (i = 0; i < 65536; i += 16) { memcpy(buf3+i, "vrbacrda12345678", 16); } @@ -742,6 +745,342 @@ test_crypto_s2k(void *arg) 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, sizeof(buf2)); + + 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: + ; +} + +static void +test_crypto_scrypt_vectors(void *arg) +{ + char *mem_op_hex_tmp = NULL; + uint8_t spec[64], out[64]; + + (void)arg; +#ifndef HAVE_LIBSCRYPT_H + if (1) + tt_skip(); +#endif + + /* Test vectors from + http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00 section 11. + + Note that the names of 'r' and 'N' are switched in that section. Or + possibly in libscrypt. + */ + + base16_decode((char*)spec, sizeof(spec), + "0400", 4); + memset(out, 0x00, sizeof(out)); + tt_int_op(64, ==, + secret_to_key_compute_key(out, 64, spec, 2, "", 0, 2)); + test_memeq_hex(out, + "77d6576238657b203b19ca42c18a0497" + "f16b4844e3074ae8dfdffa3fede21442" + "fcd0069ded0948f8326a753a0fc81f17" + "e8d3e0fb2e0d3628cf35e20c38d18906"); + + base16_decode((char*)spec, sizeof(spec), + "4e61436c" "0A34", 12); + memset(out, 0x00, sizeof(out)); + tt_int_op(64, ==, + secret_to_key_compute_key(out, 64, spec, 6, "password", 8, 2)); + test_memeq_hex(out, + "fdbabe1c9d3472007856e7190d01e9fe" + "7c6ad7cbc8237830e77376634b373162" + "2eaf30d92e22a3886ff109279d9830da" + "c727afb94a83ee6d8360cbdfa2cc0640"); + + base16_decode((char*)spec, sizeof(spec), + "536f6469756d43686c6f72696465" "0e30", 32); + memset(out, 0x00, sizeof(out)); + tt_int_op(64, ==, + secret_to_key_compute_key(out, 64, spec, 16, + "pleaseletmein", 13, 2)); + test_memeq_hex(out, + "7023bdcb3afd7348461c06cd81fd38eb" + "fda8fbba904f8e3ea9b543f6545da1f2" + "d5432955613f0fcf62d49705242a9af9" + "e61e85dc0d651e40dfcf017b45575887"); + + base16_decode((char*)spec, sizeof(spec), + "536f6469756d43686c6f72696465" "1430", 32); + memset(out, 0x00, sizeof(out)); + tt_int_op(64, ==, + secret_to_key_compute_key(out, 64, spec, 16, + "pleaseletmein", 13, 2)); + test_memeq_hex(out, + "2101cb9b6a511aaeaddbbe09cf70f881" + "ec568d574a2ffd4dabe5ee9820adaa47" + "8e56fd8f4ba5d09ffa1c6d927c40f4c3" + "37304049e8a952fbcbf45c6fa77a41a4"); + + done: + tor_free(mem_op_hex_tmp); +} + +static void +test_crypto_pbkdf2_vectors(void *arg) +{ + char *mem_op_hex_tmp = NULL; + uint8_t spec[64], out[64]; + (void)arg; + + /* Test vectors from RFC6070, section 2 */ + base16_decode((char*)spec, sizeof(spec), + "73616c74" "00" , 10); + memset(out, 0x00, sizeof(out)); + tt_int_op(20, ==, + secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1)); + test_memeq_hex(out, "0c60c80f961f0e71f3a9b524af6012062fe037a6"); + + base16_decode((char*)spec, sizeof(spec), + "73616c74" "01" , 10); + memset(out, 0x00, sizeof(out)); + tt_int_op(20, ==, + secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1)); + test_memeq_hex(out, "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"); + + base16_decode((char*)spec, sizeof(spec), + "73616c74" "0C" , 10); + memset(out, 0x00, sizeof(out)); + tt_int_op(20, ==, + secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1)); + test_memeq_hex(out, "4b007901b765489abead49d926f721d065a429c1"); + + base16_decode((char*)spec, sizeof(spec), + "73616c74" "18" , 10); + memset(out, 0x00, sizeof(out)); + tt_int_op(20, ==, + secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1)); + test_memeq_hex(out, "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984"); + + base16_decode((char*)spec, sizeof(spec), + "73616c7453414c5473616c7453414c5473616c745" + "3414c5473616c7453414c5473616c74" "0C" , 74); + memset(out, 0x00, sizeof(out)); + tt_int_op(25, ==, + secret_to_key_compute_key(out, 25, spec, 37, + "passwordPASSWORDpassword", 24, 1)); + test_memeq_hex(out, "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"); + + base16_decode((char*)spec, sizeof(spec), + "7361006c74" "0c" , 12); + memset(out, 0x00, sizeof(out)); + tt_int_op(16, ==, + secret_to_key_compute_key(out, 16, spec, 6, "pass\0word", 9, 1)); + test_memeq_hex(out, "56fa6aa75548099dcc37d7f03425e0c3"); + + done: + tor_free(mem_op_hex_tmp); +} + +static void +test_crypto_pwbox(void *arg) +{ + uint8_t *boxed=NULL, *decoded=NULL; + size_t len, dlen; + unsigned i; + const char msg[] = "This bunny reminds you that you still have a " + "salamander in your sylladex. She is holding the bunny Dave got you. " + "It’s sort of uncanny how similar they are, aside from the knitted " + "enhancements. Seriously, what are the odds?? So weird."; + const char pw[] = "I'm a night owl and a wise bird too"; + + const unsigned flags[] = { 0, + S2K_FLAG_NO_SCRYPT, + S2K_FLAG_LOW_MEM, + S2K_FLAG_NO_SCRYPT|S2K_FLAG_LOW_MEM, + S2K_FLAG_USE_PBKDF2 }; + (void)arg; + + for (i = 0; i < ARRAY_LENGTH(flags); ++i) { + tt_int_op(0, ==, crypto_pwbox(&boxed, &len, (const uint8_t*)msg, strlen(msg), + pw, strlen(pw), flags[i])); + tt_assert(boxed); + tt_assert(len > 128+32); + + tt_int_op(0, ==, crypto_unpwbox(&decoded, &dlen, boxed, len, + pw, strlen(pw))); + + tt_assert(decoded); + tt_uint_op(dlen, ==, strlen(msg)); + tt_mem_op(decoded, ==, msg, dlen); + + tor_free(decoded); + + tt_int_op(UNPWBOX_BAD_SECRET, ==, crypto_unpwbox(&decoded, &dlen, boxed, len, + pw, strlen(pw)-1)); + boxed[len-1] ^= 1; + tt_int_op(UNPWBOX_BAD_SECRET, ==, crypto_unpwbox(&decoded, &dlen, boxed, len, + pw, strlen(pw))); + boxed[0] = 255; + tt_int_op(UNPWBOX_CORRUPTED, ==, crypto_unpwbox(&decoded, &dlen, boxed, len, + pw, strlen(pw))); + + tor_free(boxed); + } + + done: + tor_free(boxed); + tor_free(decoded); + +} + /** Test AES-CTR encryption and decryption with IV. */ static void test_crypto_aes_iv(void *arg) @@ -1304,7 +1643,23 @@ 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 }, + { "scrypt_vectors", test_crypto_scrypt_vectors, 0, NULL, NULL }, + { "pbkdf2_vectors", test_crypto_pbkdf2_vectors, 0, NULL, NULL }, + { "pwbox", test_crypto_pwbox, 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), diff --git a/src/trunnel/include.am b/src/trunnel/include.am new file mode 100644 index 000000000..c7ac1679d --- /dev/null +++ b/src/trunnel/include.am @@ -0,0 +1,29 @@ + +noinst_LIBRARIES += \ + src/trunnel/libor-trunnel.a + +if UNITTESTS_ENABLED +noinst_LIBRARIES += \ + src/trunnel/libor-trunnel-testing.a +endif + +AM_CPPFLAGS += -I$(srcdir)/src/ext/trunnel -I$(srcdir)/src/trunnel + +TRUNNELSOURCES = \ + src/ext/trunnel/trunnel.c \ + src/trunnel/pwbox.c + +TRUNNELHEADERS = \ + src/ext/trunnel/trunnel.h \ + src/ext/trunnel/trunnel-impl.h \ + src/trunnel/trunnel-local.h \ + src/trunnel/pwbox.h + +src_trunnel_libor_trunnel_a_SOURCES = $(TRUNNELSOURCES) +src_trunnel_libor_trunnel_a_CPPFLAGS = -DTRUNNEL_LOCAL_H $(AM_CPPFLAGS) + +src_trunnel_libor_trunnel_testing_a_SOURCES = $(TRUNNELSOURCES) +src_trunnel_libor_trunnel_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS -DTRUNNEL_LOCAL_H $(AM_CPPFLAGS) +src_trunnel_libor_trunnel_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) + +noinst_HEADERS+= $(TRUNNELHEADERS) diff --git a/src/trunnel/pwbox.c b/src/trunnel/pwbox.c new file mode 100644 index 000000000..b1246277f --- /dev/null +++ b/src/trunnel/pwbox.c @@ -0,0 +1,509 @@ + +/* pwbox.c -- generated by trunnel. */ +#include +#include "trunnel-impl.h" + +#include "pwbox.h" + +#define TRUNNEL_SET_ERROR_CODE(obj) \ + do { \ + (obj)->trunnel_error_code_ = 1; \ + } while (0) + +pwbox_encoded_t * +pwbox_encoded_new(void) +{ + pwbox_encoded_t *val = trunnel_calloc(1, sizeof(pwbox_encoded_t)); + if (NULL == val) + return NULL; + val->fixedbytes0 = PWBOX0_CONST0; + val->fixedbytes1 = PWBOX0_CONST1; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +pwbox_encoded_clear(pwbox_encoded_t *obj) +{ + (void) obj; + TRUNNEL_DYNARRAY_WIPE(&obj->skey_header); + TRUNNEL_DYNARRAY_CLEAR(&obj->skey_header); + TRUNNEL_DYNARRAY_WIPE(&obj->data); + TRUNNEL_DYNARRAY_CLEAR(&obj->data); +} + +void +pwbox_encoded_free(pwbox_encoded_t *obj) +{ + if (obj == NULL) + return; + pwbox_encoded_clear(obj); + trunnel_memwipe(obj, sizeof(pwbox_encoded_t)); + trunnel_free_(obj); +} + +uint32_t +pwbox_encoded_get_fixedbytes0(pwbox_encoded_t *inp) +{ + return inp->fixedbytes0; +} +int +pwbox_encoded_set_fixedbytes0(pwbox_encoded_t *inp, uint32_t val) +{ + if (! ((val == PWBOX0_CONST0))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + inp->fixedbytes0 = val; + return 0; +} +uint32_t +pwbox_encoded_get_fixedbytes1(pwbox_encoded_t *inp) +{ + return inp->fixedbytes1; +} +int +pwbox_encoded_set_fixedbytes1(pwbox_encoded_t *inp, uint32_t val) +{ + if (! ((val == PWBOX0_CONST1))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + inp->fixedbytes1 = val; + return 0; +} +uint8_t +pwbox_encoded_get_header_len(pwbox_encoded_t *inp) +{ + return inp->header_len; +} +int +pwbox_encoded_set_header_len(pwbox_encoded_t *inp, uint8_t val) +{ + inp->header_len = val; + return 0; +} +size_t +pwbox_encoded_getlen_skey_header(const pwbox_encoded_t *inp) +{ + return TRUNNEL_DYNARRAY_LEN(&inp->skey_header); +} + +uint8_t +pwbox_encoded_get_skey_header(pwbox_encoded_t *inp, size_t idx) +{ + return TRUNNEL_DYNARRAY_GET(&inp->skey_header, idx); +} + +int +pwbox_encoded_set_skey_header(pwbox_encoded_t *inp, size_t idx, uint8_t elt) +{ + TRUNNEL_DYNARRAY_SET(&inp->skey_header, idx, elt); + return 0; +} +int +pwbox_encoded_add_skey_header(pwbox_encoded_t *inp, uint8_t elt) +{ +#if SIZE_MAX >= UINT8_MAX + if (inp->skey_header.n_ == UINT8_MAX) + goto trunnel_alloc_failed; +#endif + TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->skey_header, elt, {}); + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} + +uint8_t * +pwbox_encoded_getarray_skey_header(pwbox_encoded_t *inp) +{ + return inp->skey_header.elts_; +} +int +pwbox_encoded_setlen_skey_header(pwbox_encoded_t *inp, size_t newlen) +{ + uint8_t *newptr; +#if UINT8_MAX < SIZE_MAX + if (newlen > UINT8_MAX) + goto trunnel_alloc_failed; +#endif + newptr = trunnel_dynarray_setlen(&inp->skey_header.allocated_, + &inp->skey_header.n_, inp->skey_header.elts_, newlen, + sizeof(inp->skey_header.elts_[0]), (trunnel_free_fn_t) NULL, + &inp->trunnel_error_code_); + if (newptr == NULL) + goto trunnel_alloc_failed; + inp->skey_header.elts_ = newptr; + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} +size_t +pwbox_encoded_getlen_iv(const pwbox_encoded_t *inp) +{ + (void)inp; return 16; +} + +uint8_t +pwbox_encoded_get_iv(const pwbox_encoded_t *inp, size_t idx) +{ + trunnel_assert(idx < 16); + return inp->iv[idx]; +} + +int +pwbox_encoded_set_iv(pwbox_encoded_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 16); + inp->iv[idx] = elt; + return 0; +} + +uint8_t * +pwbox_encoded_getarray_iv(pwbox_encoded_t *inp) +{ + return inp->iv; +} +size_t +pwbox_encoded_getlen_data(const pwbox_encoded_t *inp) +{ + return TRUNNEL_DYNARRAY_LEN(&inp->data); +} + +uint8_t +pwbox_encoded_get_data(pwbox_encoded_t *inp, size_t idx) +{ + return TRUNNEL_DYNARRAY_GET(&inp->data, idx); +} + +int +pwbox_encoded_set_data(pwbox_encoded_t *inp, size_t idx, uint8_t elt) +{ + TRUNNEL_DYNARRAY_SET(&inp->data, idx, elt); + return 0; +} +int +pwbox_encoded_add_data(pwbox_encoded_t *inp, uint8_t elt) +{ + TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->data, elt, {}); + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} + +uint8_t * +pwbox_encoded_getarray_data(pwbox_encoded_t *inp) +{ + return inp->data.elts_; +} +int +pwbox_encoded_setlen_data(pwbox_encoded_t *inp, size_t newlen) +{ + uint8_t *newptr; + newptr = trunnel_dynarray_setlen(&inp->data.allocated_, + &inp->data.n_, inp->data.elts_, newlen, + sizeof(inp->data.elts_[0]), (trunnel_free_fn_t) NULL, + &inp->trunnel_error_code_); + if (newptr == NULL) + goto trunnel_alloc_failed; + inp->data.elts_ = newptr; + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} +size_t +pwbox_encoded_getlen_hmac(const pwbox_encoded_t *inp) +{ + (void)inp; return 32; +} + +uint8_t +pwbox_encoded_get_hmac(const pwbox_encoded_t *inp, size_t idx) +{ + trunnel_assert(idx < 32); + return inp->hmac[idx]; +} + +int +pwbox_encoded_set_hmac(pwbox_encoded_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 32); + inp->hmac[idx] = elt; + return 0; +} + +uint8_t * +pwbox_encoded_getarray_hmac(pwbox_encoded_t *inp) +{ + return inp->hmac; +} +const char * +pwbox_encoded_check(const pwbox_encoded_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + if (! (obj->fixedbytes0 == PWBOX0_CONST0)) + return "Integer out of bounds"; + if (! (obj->fixedbytes1 == PWBOX0_CONST1)) + return "Integer out of bounds"; + if (TRUNNEL_DYNARRAY_LEN(&obj->skey_header) != obj->header_len) + return "Length mismatch for skey_header"; + return NULL; +} + +ssize_t +pwbox_encoded_encoded_len(const pwbox_encoded_t *obj) +{ + ssize_t result = 0; + + if (NULL != pwbox_encoded_check(obj)) + return -1; + + + /* Length of u32 fixedbytes0 IN [PWBOX0_CONST0] */ + result += 4; + + /* Length of u32 fixedbytes1 IN [PWBOX0_CONST1] */ + result += 4; + + /* Length of u8 header_len */ + result += 1; + + /* Length of u8 skey_header[header_len] */ + result += TRUNNEL_DYNARRAY_LEN(&obj->skey_header); + + /* Length of u8 iv[16] */ + result += 16; + + /* Length of u8 data[] */ + result += TRUNNEL_DYNARRAY_LEN(&obj->data); + + /* Length of u8 hmac[32] */ + result += 32; + return result; +} +int +pwbox_encoded_clear_errors(pwbox_encoded_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +pwbox_encoded_encode(uint8_t *output, size_t avail, const pwbox_encoded_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; + int enforce_avail = 0; + const size_t avail_orig = avail; + + if (NULL != (msg = pwbox_encoded_check(obj))) + goto check_failed; + + + /* Encode u32 fixedbytes0 IN [PWBOX0_CONST0] */ + trunnel_assert(written <= avail); + if (avail - written < 4) + goto truncated; + trunnel_set_uint32(ptr, trunnel_htonl(obj->fixedbytes0)); + written += 4; ptr += 4; + + /* Encode u32 fixedbytes1 IN [PWBOX0_CONST1] */ + trunnel_assert(written <= avail); + if (avail - written < 4) + goto truncated; + trunnel_set_uint32(ptr, trunnel_htonl(obj->fixedbytes1)); + written += 4; ptr += 4; + + /* Encode u8 header_len */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->header_len)); + written += 1; ptr += 1; + + /* Encode u8 skey_header[header_len] */ + { + size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->skey_header); + trunnel_assert(obj->header_len == elt_len); + trunnel_assert(written <= avail); + if (avail - written < elt_len) + goto truncated; + memcpy(ptr, obj->skey_header.elts_, elt_len); + written += elt_len; ptr += elt_len; + } + + /* Encode u8 iv[16] */ + trunnel_assert(written <= avail); + if (avail - written < 16) + goto truncated; + memcpy(ptr, obj->iv, 16); + written += 16; ptr += 16; + { + + /* Encode u8 data[] */ + { + size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->data); + trunnel_assert(written <= avail); + if (avail - written < elt_len) + goto truncated; + memcpy(ptr, obj->data.elts_, elt_len); + written += elt_len; ptr += elt_len; + } + trunnel_assert(written <= avail); + if (avail - written < 32) + goto truncated; + avail = written + 32; + enforce_avail = 1; + } + + /* Encode u8 hmac[32] */ + trunnel_assert(written <= avail); + if (avail - written < 32) { + if (avail_orig - written < 32) + goto truncated; + else + goto check_failed; + } + memcpy(ptr, obj->hmac, 32); + written += 32; ptr += 32; + + + trunnel_assert(ptr == output + written); + if (enforce_avail && avail != written) + goto check_failed; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + ssize_t encoded_len = pwbox_encoded_encoded_len(obj); + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As pwbox_encoded_parse(), but do not allocate the output object. + */ +static ssize_t +pwbox_encoded_parse_into(pwbox_encoded_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u32 fixedbytes0 IN [PWBOX0_CONST0] */ + if (remaining < 4) + goto truncated; + obj->fixedbytes0 = trunnel_ntohl(trunnel_get_uint32(ptr)); + remaining -= 4; ptr += 4; + if (! (obj->fixedbytes0 == PWBOX0_CONST0)) + goto fail; + + /* Parse u32 fixedbytes1 IN [PWBOX0_CONST1] */ + if (remaining < 4) + goto truncated; + obj->fixedbytes1 = trunnel_ntohl(trunnel_get_uint32(ptr)); + remaining -= 4; ptr += 4; + if (! (obj->fixedbytes1 == PWBOX0_CONST1)) + goto fail; + + /* Parse u8 header_len */ + if (remaining < 1) + goto truncated; + obj->header_len = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse u8 skey_header[header_len] */ + if (remaining < obj->header_len) + goto truncated; + TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->skey_header, obj->header_len, {}); + obj->skey_header.n_ = obj->header_len; + memcpy(obj->skey_header.elts_, ptr, obj->header_len); + ptr += obj->header_len; remaining -= obj->header_len; + + /* Parse u8 iv[16] */ + if (remaining < (16)) + goto truncated; + memcpy(obj->iv, ptr, 16); + { + unsigned idx; + for (idx = 0; idx < 16; ++idx) + obj->iv[idx] = (obj->iv[idx]); + } + remaining -= 16; ptr += 16; + { + size_t remaining_after; + if (remaining < 32) + goto truncated; + remaining_after = 32; + remaining = remaining - 32; + + /* Parse u8 data[] */ + TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->data, remaining, {}); + obj->data.n_ = remaining; + memcpy(obj->data.elts_, ptr, remaining); + ptr += remaining; remaining -= remaining; + if (remaining != 0) + goto fail; + remaining = remaining_after; + } + + /* Parse u8 hmac[32] */ + if (remaining < (32)) + goto truncated; + memcpy(obj->hmac, ptr, 32); + { + unsigned idx; + for (idx = 0; idx < 32; ++idx) + obj->hmac[idx] = (obj->hmac[idx]); + } + remaining -= 32; ptr += 32; + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + trunnel_alloc_failed: + return -1; + fail: + result = -1; + return result; +} + +ssize_t +pwbox_encoded_parse(pwbox_encoded_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = pwbox_encoded_new(); + if (NULL == *output) + return -1; + result = pwbox_encoded_parse_into(*output, input, len_in); + if (result < 0) { + pwbox_encoded_free(*output); + *output = NULL; + } + return result; +} diff --git a/src/trunnel/pwbox.h b/src/trunnel/pwbox.h new file mode 100644 index 000000000..d37ef5d7e --- /dev/null +++ b/src/trunnel/pwbox.h @@ -0,0 +1,171 @@ + +/* pwbox.h -- generated by trunnel. */ +#ifndef TRUNNEL_PWBOX_H +#define TRUNNEL_PWBOX_H + +#include +#include "trunnel.h" + +#define PWBOX0_CONST0 1414484546 +#define PWBOX0_CONST1 1331179568 +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_PWBOX_ENCODED) +struct pwbox_encoded_st { + uint32_t fixedbytes0; + uint32_t fixedbytes1; + uint8_t header_len; + TRUNNEL_DYNARRAY_HEAD(, uint8_t) skey_header; + uint8_t iv[16]; + TRUNNEL_DYNARRAY_HEAD(, uint8_t) data; + uint8_t hmac[32]; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct pwbox_encoded_st pwbox_encoded_t; +/** Return a newly allocated pwbox_encoded with all elements set to + * zero. + */ +pwbox_encoded_t *pwbox_encoded_new(void); +/** Release all storage held by the pwbox_encoded in 'victim'. (Do + * nothing if 'victim' is NULL.) + */ +void pwbox_encoded_free(pwbox_encoded_t *victim); +/** Try to parse a pwbox_encoded from the buffer in 'input', using up + * to 'len_in' bytes from the input buffer. On success, return the + * number of bytes consumed and set *output to the newly allocated + * pwbox_encoded_t. On failure, return -2 if the input appears + * truncated, and -1 if the input is otherwise invalid. + */ +ssize_t pwbox_encoded_parse(pwbox_encoded_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * pwbox_encoded in 'obj'. On failure, return a negative value. Note + * that this value may be an overestimate, and can even be an + * underestimate for certain unencodeable objects. + */ +ssize_t pwbox_encoded_encoded_len(const pwbox_encoded_t *obj); +/** Try to encode the pwbox_encoded from 'input' into the buffer at + * 'output', using up to 'avail' bytes of the output buffer. On + * success, return the number of bytes used. On failure, return -2 if + * the buffer was not long enough, and -1 if the input was invalid. + */ +ssize_t pwbox_encoded_encode(uint8_t *output, const size_t avail, const pwbox_encoded_t *input); +/** Check whether the internal state of the pwbox_encoded in 'obj' is + * consistent. Return NULL if it is, and a short message if it is not. + */ +const char *pwbox_encoded_check(const pwbox_encoded_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int pwbox_encoded_clear_errors(pwbox_encoded_t *obj); +/** Return the value of the fixedbytes0 field of the pwbox_encoded_t + * in 'inp' + */ +uint32_t pwbox_encoded_get_fixedbytes0(pwbox_encoded_t *inp); +/** Set the value of the fixedbytes0 field of the pwbox_encoded_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int pwbox_encoded_set_fixedbytes0(pwbox_encoded_t *inp, uint32_t val); +/** Return the value of the fixedbytes1 field of the pwbox_encoded_t + * in 'inp' + */ +uint32_t pwbox_encoded_get_fixedbytes1(pwbox_encoded_t *inp); +/** Set the value of the fixedbytes1 field of the pwbox_encoded_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int pwbox_encoded_set_fixedbytes1(pwbox_encoded_t *inp, uint32_t val); +/** Return the value of the header_len field of the pwbox_encoded_t in + * 'inp' + */ +uint8_t pwbox_encoded_get_header_len(pwbox_encoded_t *inp); +/** Set the value of the header_len field of the pwbox_encoded_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int pwbox_encoded_set_header_len(pwbox_encoded_t *inp, uint8_t val); +/** Return the length of the dynamic array holding the skey_header + * field of the pwbox_encoded_t in 'inp'. + */ +size_t pwbox_encoded_getlen_skey_header(const pwbox_encoded_t *inp); +/** Return the element at position 'idx' of the dynamic array field + * skey_header of the pwbox_encoded_t in 'inp'. + */ +uint8_t pwbox_encoded_get_skey_header(pwbox_encoded_t *inp, size_t idx); +/** Change the element at position 'idx' of the dynamic array field + * skey_header of the pwbox_encoded_t in 'inp', so that it will hold + * the value 'elt'. + */ +int pwbox_encoded_set_skey_header(pwbox_encoded_t *inp, size_t idx, uint8_t elt); +/** Append a new element 'elt' to the dynamic array field skey_header + * of the pwbox_encoded_t in 'inp'. + */ +int pwbox_encoded_add_skey_header(pwbox_encoded_t *inp, uint8_t elt); +/** Return a pointer to the variable-length array field skey_header of + * 'inp'. + */ +uint8_t * pwbox_encoded_getarray_skey_header(pwbox_encoded_t *inp); +/** Change the length of the variable-length array field skey_header + * of 'inp' to 'newlen'.Fill extra elements with 0. Return 0 on + * success; return -1 and set the error code on 'inp' on failure. + */ +int pwbox_encoded_setlen_skey_header(pwbox_encoded_t *inp, size_t newlen); +/** Return the (constant) length of the array holding the iv field of + * the pwbox_encoded_t in 'inp'. + */ +size_t pwbox_encoded_getlen_iv(const pwbox_encoded_t *inp); +/** Return the element at position 'idx' of the fixed array field iv + * of the pwbox_encoded_t in 'inp'. + */ +uint8_t pwbox_encoded_get_iv(const pwbox_encoded_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field iv + * of the pwbox_encoded_t in 'inp', so that it will hold the value + * 'elt'. + */ +int pwbox_encoded_set_iv(pwbox_encoded_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 16-element array field iv of 'inp'. + */ +uint8_t * pwbox_encoded_getarray_iv(pwbox_encoded_t *inp); +/** Return the length of the dynamic array holding the data field of + * the pwbox_encoded_t in 'inp'. + */ +size_t pwbox_encoded_getlen_data(const pwbox_encoded_t *inp); +/** Return the element at position 'idx' of the dynamic array field + * data of the pwbox_encoded_t in 'inp'. + */ +uint8_t pwbox_encoded_get_data(pwbox_encoded_t *inp, size_t idx); +/** Change the element at position 'idx' of the dynamic array field + * data of the pwbox_encoded_t in 'inp', so that it will hold the + * value 'elt'. + */ +int pwbox_encoded_set_data(pwbox_encoded_t *inp, size_t idx, uint8_t elt); +/** Append a new element 'elt' to the dynamic array field data of the + * pwbox_encoded_t in 'inp'. + */ +int pwbox_encoded_add_data(pwbox_encoded_t *inp, uint8_t elt); +/** Return a pointer to the variable-length array field data of 'inp'. + */ +uint8_t * pwbox_encoded_getarray_data(pwbox_encoded_t *inp); +/** Change the length of the variable-length array field data of 'inp' + * to 'newlen'.Fill extra elements with 0. Return 0 on success; return + * -1 and set the error code on 'inp' on failure. + */ +int pwbox_encoded_setlen_data(pwbox_encoded_t *inp, size_t newlen); +/** Return the (constant) length of the array holding the hmac field + * of the pwbox_encoded_t in 'inp'. + */ +size_t pwbox_encoded_getlen_hmac(const pwbox_encoded_t *inp); +/** Return the element at position 'idx' of the fixed array field hmac + * of the pwbox_encoded_t in 'inp'. + */ +uint8_t pwbox_encoded_get_hmac(const pwbox_encoded_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field hmac + * of the pwbox_encoded_t in 'inp', so that it will hold the value + * 'elt'. + */ +int pwbox_encoded_set_hmac(pwbox_encoded_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 32-element array field hmac of 'inp'. + */ +uint8_t * pwbox_encoded_getarray_hmac(pwbox_encoded_t *inp); + + +#endif diff --git a/src/trunnel/pwbox.trunnel b/src/trunnel/pwbox.trunnel new file mode 100644 index 000000000..10db74b4e --- /dev/null +++ b/src/trunnel/pwbox.trunnel @@ -0,0 +1,14 @@ + +const PWBOX0_CONST0 = 0x544f5242; // TORB +const PWBOX0_CONST1 = 0x4f583030; // OX00 + +struct pwbox_encoded { + u32 fixedbytes0 IN [PWBOX0_CONST0]; + u32 fixedbytes1 IN [PWBOX0_CONST1]; + u8 header_len; + u8 skey_header[header_len]; + u8 iv[16]; + u8 data[..-32]; + u8 hmac[32]; +}; + diff --git a/src/trunnel/trunnel-local.h b/src/trunnel/trunnel-local.h new file mode 100644 index 000000000..b7c2ab98e --- /dev/null +++ b/src/trunnel/trunnel-local.h @@ -0,0 +1,18 @@ + +#ifndef TRUNNEL_LOCAL_H_INCLUDED +#define TRUNNEL_LOCAL_H_INCLUDED + +#include "util.h" +#include "compat.h" +#include "crypto.h" + +#define trunnel_malloc tor_malloc +#define trunnel_calloc tor_calloc +#define trunnel_strdup tor_strdup +#define trunnel_free_ tor_free_ +#define trunnel_realloc tor_realloc +#define trunnel_reallocarray tor_reallocarray +#define trunnel_assert tor_assert +#define trunnel_memwipe(mem, len) memwipe((mem), 0, (len)) + +#endif