mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-11 13:43:47 +01:00
213 lines
6.4 KiB
C
213 lines
6.4 KiB
C
/* Copyright (c) 2014-2016, The Tor Project, Inc. */
|
|
/* See LICENSE for licensing information */
|
|
|
|
/**
|
|
* \file crypto_pwbox.c
|
|
*
|
|
* \brief Code for encrypting secrets in a password-protected form and saving
|
|
* them to disk.
|
|
*/
|
|
|
|
#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
|
|
* <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 = 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 (BUG(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((uint32_t)input_len));
|
|
memcpy(encrypted_portion+4, input, input_len);
|
|
|
|
/* Now that all the data is in position, derive some keys, encrypt, and
|
|
* digest */
|
|
const int s2k_rv = secret_to_key_derivekey(keys, sizeof(keys),
|
|
pwbox_encoded_getarray_skey_header(enc),
|
|
spec_len,
|
|
secret, secret_len);
|
|
if (BUG(s2k_rv < 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 (BUG(result_len < 0))
|
|
goto err;
|
|
result = tor_malloc(result_len);
|
|
enc_len = pwbox_encoded_encode(result, result_len, enc);
|
|
if (BUG(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:
|
|
/* LCOV_EXCL_START
|
|
|
|
This error case is often unreachable if we're correctly coded, unless
|
|
somebody adds a new error case somewhere, or unless you're building
|
|
without scrypto support.
|
|
|
|
- make_specifier can't fail, unless S2K_MAX_LEN is too short.
|
|
- secret_to_key_derivekey can't really fail unless we're missing
|
|
scrypt, or the underlying function fails, or we pass it a bogus
|
|
algorithm or parameters.
|
|
- pwbox_encoded_encoded_len can't fail unless we're using trunnel
|
|
incorrectly.
|
|
- pwbox_encoded_encode can't fail unless we're using trunnel wrong,
|
|
or it's buggy.
|
|
*/
|
|
tor_free(result);
|
|
rv = -1;
|
|
/* LCOV_EXCL_STOP */
|
|
out:
|
|
pwbox_encoded_free(enc);
|
|
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];
|
|
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;
|
|
}
|
|
|