Rudimentary-but-sufficient passphrase-encrypted box code.

See crypto_pwbox.c for a description of the file format.

There are tests for successful operation, but it still needs
error-case tests.
This commit is contained in:
Nick Mathewson 2014-08-28 17:59:06 -04:00
parent e84e1c9745
commit 8184839a47
4 changed files with 222 additions and 0 deletions

167
src/common/crypto_pwbox.c Normal file
View File

@ -0,0 +1,167 @@
#include "crypto.h"
#include "crypto_s2k.h"
#include "crypto_pwbox.h"
#include "di_ops.h"
#include "util.h"
/* 7 bytes "TORBOX0"
1 byte: header len (H)
H bytes: header, denoting secret key algorithm.
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 + 32)
/**
* Make an authenticated passphrase-encrypted blob to encode the
* <b>input_len</b> bytes in <b>input</b> using the passphrase
* <b>secret</b> of <b>secret_len</b> bytes. Allocate a new chunk of memory
* to hold the encrypted data, and store a pointer to that memory in
* *<b>out</b>, and its size in <b>outlen_out</b>. Use <b>s2k_flags</b> 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, *encrypted_portion, *hmac;
size_t encrypted_len = 128 * CEIL_DIV(input_len+4, 128);
size_t result_len = encrypted_len + MAX_OVERHEAD;
int spec_len;
uint8_t keys[CIPHER_KEY_LEN + DIGEST256_LEN];
crypto_cipher_t *cipher;
int rv;
/* Allocate a buffer and put things in the right place. */
result = tor_malloc_zero(result_len);
memcpy(result, "TORBOX0", 7);
spec_len = secret_to_key_make_specifier(result + 8,
result_len - 8,
s2k_flags);
if (spec_len < 0 || spec_len > 255)
goto err;
result[7] = (uint8_t) spec_len;
tor_assert(8 + spec_len + encrypted_len <= result_len);
encrypted_portion = result + 8 + spec_len;
hmac = encrypted_portion + encrypted_len;
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),
result+8, spec_len,
secret, secret_len) < 0)
goto err;
cipher = crypto_cipher_new((char*)keys);
crypto_cipher_crypt_inplace(cipher, (char*)encrypted_portion, encrypted_len);
crypto_cipher_free(cipher);
crypto_hmac_sha256((char*)hmac,
(const char*)keys + CIPHER_KEY_LEN,
sizeof(keys)-CIPHER_KEY_LEN,
(const char*)result, 8 + spec_len + encrypted_len);
*out = result;
*outlen_out = 8 + spec_len + encrypted_len + DIGEST256_LEN;
rv = 0;
goto out;
err:
tor_free(result);
rv = -1;
out:
memwipe(keys, 0, sizeof(keys));
return rv;
}
/**
* Try to decrypt the passphrase-encrypted blob of <b>input_len</b> bytes in
* <b>input</b> using the passphrase <b>secret</b> of <b>secret_len</b> 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 *<b>out</b>, and its
* size in <b>outlen_out</b>. 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];
size_t spec_bytes;
uint8_t hmac[DIGEST256_LEN];
uint32_t result_len;
crypto_cipher_t *cipher = NULL;
int rv = UNPWBOX_CORRUPTED;
if (input_len < 32)
goto err;
if (tor_memneq(inp, "TORBOX0", 7))
goto err;
spec_bytes = inp[7];
if (input_len < 8 + spec_bytes)
goto err;
/* Now derive the keys and check the hmac. */
if (secret_to_key_derivekey(keys, sizeof(keys),
inp+8, spec_bytes,
secret, secret_len) < 0)
goto err;
crypto_hmac_sha256((char *)hmac,
(const char*)keys + CIPHER_KEY_LEN, sizeof(keys)-CIPHER_KEY_LEN,
(const char*)inp, input_len - DIGEST256_LEN);
if (tor_memneq(hmac, inp + input_len - DIGEST256_LEN, DIGEST256_LEN)) {
rv = UNPWBOX_BAD_SECRET;
goto err;
}
encrypted = inp + 8 + spec_bytes;
/* How long is the plaintext? */
cipher = crypto_cipher_new((char*)keys);
crypto_cipher_decrypt(cipher, (char*)&result_len, (char*)encrypted, 4);
result_len = ntohl(result_len);
if (input_len < 8 + spec_bytes + 4 + result_len + 32)
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);
memwipe(keys, 0, sizeof(keys));
return rv;
}

20
src/common/crypto_pwbox.h Normal file
View File

@ -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

View File

@ -75,6 +75,7 @@ LIBOR_A_SOURCES = \
LIBOR_CRYPTO_A_SOURCES = \ LIBOR_CRYPTO_A_SOURCES = \
src/common/aes.c \ src/common/aes.c \
src/common/crypto.c \ src/common/crypto.c \
src/common/crypto_pwbox.c \
src/common/crypto_s2k.c \ src/common/crypto_s2k.c \
src/common/crypto_format.c \ src/common/crypto_format.c \
src/common/torgzip.c \ src/common/torgzip.c \
@ -111,6 +112,7 @@ COMMONHEADERS = \
src/common/container.h \ src/common/container.h \
src/common/crypto.h \ src/common/crypto.h \
src/common/crypto_curve25519.h \ src/common/crypto_curve25519.h \
src/common/crypto_pwbox.h \
src/common/crypto_s2k.h \ src/common/crypto_s2k.h \
src/common/di_ops.h \ src/common/di_ops.h \
src/common/memarea.h \ src/common/memarea.h \

View File

@ -14,6 +14,7 @@
#include "crypto_curve25519.h" #include "crypto_curve25519.h"
#endif #endif
#include "crypto_s2k.h" #include "crypto_s2k.h"
#include "crypto_pwbox.h"
extern const char AUTHORITY_SIGNKEY_3[]; extern const char AUTHORITY_SIGNKEY_3[];
extern const char AUTHORITY_SIGNKEY_A_DIGEST[]; extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
@ -887,6 +888,37 @@ test_crypto_s2k_errors(void *arg)
; ;
} }
static void
test_crypto_pwbox(void *arg)
{
uint8_t *boxed=NULL, *decoded=NULL;
size_t len, dlen;
const char msg[] = "This bunny reminds you that you still have a "
"salamander in your sylladex. She is holding the bunny Dave got you. "
"Its 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";
(void)arg;
tt_int_op(0, ==, crypto_pwbox(&boxed, &len, (const uint8_t*)msg, strlen(msg),
pw, strlen(pw), 0));
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);
done:
tor_free(boxed);
tor_free(decoded);
}
/** Test AES-CTR encryption and decryption with IV. */ /** Test AES-CTR encryption and decryption with IV. */
static void static void
test_crypto_aes_iv(void *arg) test_crypto_aes_iv(void *arg)
@ -1462,6 +1494,7 @@ struct testcase_t crypto_tests[] = {
{ "s2k_rfc2440_legacy", test_crypto_s2k_general, 0, &pass_data, { "s2k_rfc2440_legacy", test_crypto_s2k_general, 0, &pass_data,
(void*)"rfc2440-legacy" }, (void*)"rfc2440-legacy" },
{ "s2k_errors", test_crypto_s2k_errors, 0, NULL, NULL }, { "s2k_errors", test_crypto_s2k_errors, 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_AES", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"aes" },
{ "aes_iv_EVP", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"evp" }, { "aes_iv_EVP", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"evp" },
CRYPTO_LEGACY(base32_decode), CRYPTO_LEGACY(base32_decode),