tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

crypto_pwbox.c (6900B)


      1 /* Copyright (c) 2014-2021, The Tor Project, Inc. */
      2 /* See LICENSE for licensing information */
      3 
      4 /**
      5 * \file crypto_pwbox.c
      6 *
      7 * \brief Code for encrypting secrets in a password-protected form and saving
      8 * them to disk.
      9 */
     10 
     11 #include <string.h>
     12 
     13 #include "lib/arch/bytes.h"
     14 #include "lib/crypt_ops/crypto_cipher.h"
     15 #include "lib/crypt_ops/crypto_digest.h"
     16 #include "lib/crypt_ops/crypto_pwbox.h"
     17 #include "lib/crypt_ops/crypto_rand.h"
     18 #include "lib/crypt_ops/crypto_s2k.h"
     19 #include "lib/crypt_ops/crypto_util.h"
     20 #include "lib/ctime/di_ops.h"
     21 #include "lib/intmath/muldiv.h"
     22 #include "trunnel/pwbox.h"
     23 #include "lib/log/util_bug.h"
     24 
     25 /* 8 bytes "TORBOX00"
     26   1 byte: header len (H)
     27   H bytes: header, denoting secret key algorithm.
     28   16 bytes: IV
     29   Round up to multiple of 128 bytes, then encrypt:
     30      4 bytes: data len
     31      data
     32      zeros
     33   32 bytes: HMAC-SHA256 of all previous bytes.
     34 */
     35 
     36 #define MAX_OVERHEAD (S2K_MAXLEN + 8 + 1 + 32 + CIPHER_IV_LEN)
     37 
     38 /**
     39 * Make an authenticated passphrase-encrypted blob to encode the
     40 * <b>input_len</b> bytes in <b>input</b> using the passphrase
     41 * <b>secret</b> of <b>secret_len</b> bytes.  Allocate a new chunk of memory
     42 * to hold the encrypted data, and store a pointer to that memory in
     43 * *<b>out</b>, and its size in <b>outlen_out</b>.  Use <b>s2k_flags</b> as an
     44 * argument to the passphrase-hashing function.
     45 */
     46 int
     47 crypto_pwbox(uint8_t **out, size_t *outlen_out,
     48             const uint8_t *input, size_t input_len,
     49             const char *secret, size_t secret_len,
     50             unsigned s2k_flags)
     51 {
     52  uint8_t *result = NULL, *encrypted_portion;
     53  size_t encrypted_len = 128 * CEIL_DIV(input_len+4, 128);
     54  ssize_t result_len;
     55  int spec_len;
     56  uint8_t keys[CIPHER_KEY_LEN + DIGEST256_LEN];
     57  pwbox_encoded_t *enc = NULL;
     58  ssize_t enc_len;
     59 
     60  crypto_cipher_t *cipher;
     61  int rv;
     62 
     63  enc = pwbox_encoded_new();
     64  tor_assert(enc);
     65 
     66  pwbox_encoded_setlen_skey_header(enc, S2K_MAXLEN);
     67 
     68  spec_len = secret_to_key_make_specifier(
     69                                      pwbox_encoded_getarray_skey_header(enc),
     70                                      S2K_MAXLEN,
     71                                      s2k_flags);
     72  if (BUG(spec_len < 0 || spec_len > S2K_MAXLEN))
     73    goto err;
     74  pwbox_encoded_setlen_skey_header(enc, spec_len);
     75  enc->header_len = spec_len;
     76 
     77  crypto_rand((char*)enc->iv, sizeof(enc->iv));
     78 
     79  pwbox_encoded_setlen_data(enc, encrypted_len);
     80  encrypted_portion = pwbox_encoded_getarray_data(enc);
     81 
     82  set_uint32(encrypted_portion, tor_htonl((uint32_t)input_len));
     83  memcpy(encrypted_portion+4, input, input_len);
     84 
     85  /* Now that all the data is in position, derive some keys, encrypt, and
     86   * digest */
     87  const int s2k_rv = secret_to_key_derivekey(keys, sizeof(keys),
     88                              pwbox_encoded_getarray_skey_header(enc),
     89                              spec_len,
     90                              secret, secret_len);
     91  if (BUG(s2k_rv < 0))
     92    goto err;
     93 
     94  cipher = crypto_cipher_new_with_iv((char*)keys, (char*)enc->iv);
     95  crypto_cipher_crypt_inplace(cipher, (char*)encrypted_portion, encrypted_len);
     96  crypto_cipher_free(cipher);
     97 
     98  result_len = pwbox_encoded_encoded_len(enc);
     99  if (BUG(result_len < 0))
    100    goto err;
    101  result = tor_malloc(result_len);
    102  enc_len = pwbox_encoded_encode(result, result_len, enc);
    103  if (BUG(enc_len < 0))
    104    goto err;
    105  tor_assert(enc_len == result_len);
    106 
    107  crypto_hmac_sha256((char*) result + result_len - 32,
    108                     (const char*)keys + CIPHER_KEY_LEN,
    109                     DIGEST256_LEN,
    110                     (const char*)result,
    111                     result_len - 32);
    112 
    113  *out = result;
    114  *outlen_out = result_len;
    115  rv = 0;
    116  goto out;
    117 
    118  /* LCOV_EXCL_START
    119 
    120     This error case is often unreachable if we're correctly coded, unless
    121     somebody adds a new error case somewhere, or unless you're building
    122     without scrypto support.
    123 
    124       - make_specifier can't fail, unless S2K_MAX_LEN is too short.
    125       - secret_to_key_derivekey can't really fail unless we're missing
    126         scrypt, or the underlying function fails, or we pass it a bogus
    127         algorithm or parameters.
    128       - pwbox_encoded_encoded_len can't fail unless we're using trunnel
    129         incorrectly.
    130       - pwbox_encoded_encode can't fail unless we're using trunnel wrong,
    131         or it's buggy.
    132   */
    133 err:
    134  tor_free(result);
    135  rv = -1;
    136  /* LCOV_EXCL_STOP */
    137 out:
    138  pwbox_encoded_free(enc);
    139  memwipe(keys, 0, sizeof(keys));
    140  return rv;
    141 }
    142 
    143 /**
    144 * Try to decrypt the passphrase-encrypted blob of <b>input_len</b> bytes in
    145 * <b>input</b> using the passphrase <b>secret</b> of <b>secret_len</b> bytes.
    146 * On success, return 0 and allocate a new chunk of memory to hold the
    147 * decrypted data, and store a pointer to that memory in *<b>out</b>, and its
    148 * size in <b>outlen_out</b>.  On failure, return UNPWBOX_BAD_SECRET if
    149 * the passphrase might have been wrong, and UNPWBOX_CORRUPT if the object is
    150 * definitely corrupt.
    151 */
    152 int
    153 crypto_unpwbox(uint8_t **out, size_t *outlen_out,
    154               const uint8_t *inp, size_t input_len,
    155               const char *secret, size_t secret_len)
    156 {
    157  uint8_t *result = NULL;
    158  const uint8_t *encrypted;
    159  uint8_t keys[CIPHER_KEY_LEN + DIGEST256_LEN];
    160  uint8_t hmac[DIGEST256_LEN];
    161  uint32_t result_len;
    162  size_t encrypted_len;
    163  crypto_cipher_t *cipher = NULL;
    164  int rv = UNPWBOX_CORRUPTED;
    165  ssize_t got_len;
    166 
    167  pwbox_encoded_t *enc = NULL;
    168 
    169  got_len = pwbox_encoded_parse(&enc, inp, input_len);
    170  if (got_len < 0 || (size_t)got_len != input_len)
    171    goto err;
    172 
    173  /* Now derive the keys and check the hmac. */
    174  if (secret_to_key_derivekey(keys, sizeof(keys),
    175                              pwbox_encoded_getarray_skey_header(enc),
    176                              pwbox_encoded_getlen_skey_header(enc),
    177                              secret, secret_len) < 0)
    178    goto err;
    179 
    180  crypto_hmac_sha256((char *)hmac,
    181                     (const char*)keys + CIPHER_KEY_LEN, DIGEST256_LEN,
    182                     (const char*)inp, input_len - DIGEST256_LEN);
    183 
    184  if (tor_memneq(hmac, enc->hmac, DIGEST256_LEN)) {
    185    rv = UNPWBOX_BAD_SECRET;
    186    goto err;
    187  }
    188 
    189  /* How long is the plaintext? */
    190  encrypted = pwbox_encoded_getarray_data(enc);
    191  encrypted_len = pwbox_encoded_getlen_data(enc);
    192  if (encrypted_len < 4)
    193    goto err;
    194 
    195  cipher = crypto_cipher_new_with_iv((char*)keys, (char*)enc->iv);
    196  crypto_cipher_decrypt(cipher, (char*)&result_len, (char*)encrypted, 4);
    197  result_len = tor_ntohl(result_len);
    198  if (encrypted_len < result_len + 4)
    199    goto err;
    200 
    201  /* Allocate a buffer and decrypt */
    202  result = tor_malloc_zero(result_len);
    203  crypto_cipher_decrypt(cipher, (char*)result, (char*)encrypted+4, result_len);
    204 
    205  *out = result;
    206  *outlen_out = result_len;
    207 
    208  rv = UNPWBOX_OKAY;
    209  goto out;
    210 
    211 err:
    212  tor_free(result);
    213 
    214 out:
    215  crypto_cipher_free(cipher);
    216  pwbox_encoded_free(enc);
    217  memwipe(keys, 0, sizeof(keys));
    218  return rv;
    219 }