mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-24 12:23:32 +01:00
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:
parent
e84e1c9745
commit
8184839a47
167
src/common/crypto_pwbox.c
Normal file
167
src/common/crypto_pwbox.c
Normal 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
20
src/common/crypto_pwbox.h
Normal 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
|
||||
|
@ -75,6 +75,7 @@ LIBOR_A_SOURCES = \
|
||||
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 \
|
||||
@ -111,6 +112,7 @@ 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 \
|
||||
|
@ -14,6 +14,7 @@
|
||||
#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[];
|
||||
@ -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. "
|
||||
"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";
|
||||
|
||||
(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. */
|
||||
static void
|
||||
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,
|
||||
(void*)"rfc2440-legacy" },
|
||||
{ "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_EVP", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"evp" },
|
||||
CRYPTO_LEGACY(base32_decode),
|
||||
|
Loading…
Reference in New Issue
Block a user