LCOV - code coverage report
Current view: top level - lib/crypt_ops - crypto_pwbox.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 83 90 92.2 %
Date: 2021-11-24 03:28:48 Functions: 2 2 100.0 %

          Line data    Source code
       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           6 : 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           6 :   uint8_t *result = NULL, *encrypted_portion;
      53           6 :   size_t encrypted_len = 128 * CEIL_DIV(input_len+4, 128);
      54           6 :   ssize_t result_len;
      55           6 :   int spec_len;
      56           6 :   uint8_t keys[CIPHER_KEY_LEN + DIGEST256_LEN];
      57           6 :   pwbox_encoded_t *enc = NULL;
      58           6 :   ssize_t enc_len;
      59             : 
      60           6 :   crypto_cipher_t *cipher;
      61           6 :   int rv;
      62             : 
      63           6 :   enc = pwbox_encoded_new();
      64           6 :   tor_assert(enc);
      65             : 
      66           6 :   pwbox_encoded_setlen_skey_header(enc, S2K_MAXLEN);
      67             : 
      68           6 :   spec_len = secret_to_key_make_specifier(
      69             :                                       pwbox_encoded_getarray_skey_header(enc),
      70             :                                       S2K_MAXLEN,
      71             :                                       s2k_flags);
      72           6 :   if (BUG(spec_len < 0 || spec_len > S2K_MAXLEN))
      73           0 :     goto err;
      74           6 :   pwbox_encoded_setlen_skey_header(enc, spec_len);
      75           6 :   enc->header_len = spec_len;
      76             : 
      77           6 :   crypto_rand((char*)enc->iv, sizeof(enc->iv));
      78             : 
      79           6 :   pwbox_encoded_setlen_data(enc, encrypted_len);
      80           6 :   encrypted_portion = pwbox_encoded_getarray_data(enc);
      81             : 
      82           6 :   set_uint32(encrypted_portion, tor_htonl((uint32_t)input_len));
      83           6 :   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           6 :   const int s2k_rv = secret_to_key_derivekey(keys, sizeof(keys),
      88           6 :                               pwbox_encoded_getarray_skey_header(enc),
      89             :                               spec_len,
      90             :                               secret, secret_len);
      91           6 :   if (BUG(s2k_rv < 0))
      92           0 :     goto err;
      93             : 
      94           6 :   cipher = crypto_cipher_new_with_iv((char*)keys, (char*)enc->iv);
      95           6 :   crypto_cipher_crypt_inplace(cipher, (char*)encrypted_portion, encrypted_len);
      96           6 :   crypto_cipher_free(cipher);
      97             : 
      98           6 :   result_len = pwbox_encoded_encoded_len(enc);
      99           6 :   if (BUG(result_len < 0))
     100           0 :     goto err;
     101           6 :   result = tor_malloc(result_len);
     102           6 :   enc_len = pwbox_encoded_encode(result, result_len, enc);
     103           6 :   if (BUG(enc_len < 0))
     104           0 :     goto err;
     105           6 :   tor_assert(enc_len == result_len);
     106             : 
     107           6 :   crypto_hmac_sha256((char*) result + result_len - 32,
     108             :                      (const char*)keys + CIPHER_KEY_LEN,
     109             :                      DIGEST256_LEN,
     110             :                      (const char*)result,
     111           6 :                      result_len - 32);
     112             : 
     113           6 :   *out = result;
     114           6 :   *outlen_out = result_len;
     115           6 :   rv = 0;
     116           6 :   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           6 :  out:
     138           6 :   pwbox_encoded_free(enc);
     139           6 :   memwipe(keys, 0, sizeof(keys));
     140           6 :   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          21 : 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          21 :   uint8_t *result = NULL;
     158          21 :   const uint8_t *encrypted;
     159          21 :   uint8_t keys[CIPHER_KEY_LEN + DIGEST256_LEN];
     160          21 :   uint8_t hmac[DIGEST256_LEN];
     161          21 :   uint32_t result_len;
     162          21 :   size_t encrypted_len;
     163          21 :   crypto_cipher_t *cipher = NULL;
     164          21 :   int rv = UNPWBOX_CORRUPTED;
     165          21 :   ssize_t got_len;
     166             : 
     167          21 :   pwbox_encoded_t *enc = NULL;
     168             : 
     169          21 :   got_len = pwbox_encoded_parse(&enc, inp, input_len);
     170          21 :   if (got_len < 0 || (size_t)got_len != input_len)
     171           5 :     goto err;
     172             : 
     173             :   /* Now derive the keys and check the hmac. */
     174          32 :   if (secret_to_key_derivekey(keys, sizeof(keys),
     175          16 :                               pwbox_encoded_getarray_skey_header(enc),
     176             :                               pwbox_encoded_getlen_skey_header(enc),
     177             :                               secret, secret_len) < 0)
     178           0 :     goto err;
     179             : 
     180          16 :   crypto_hmac_sha256((char *)hmac,
     181             :                      (const char*)keys + CIPHER_KEY_LEN, DIGEST256_LEN,
     182             :                      (const char*)inp, input_len - DIGEST256_LEN);
     183             : 
     184          16 :   if (tor_memneq(hmac, enc->hmac, DIGEST256_LEN)) {
     185          10 :     rv = UNPWBOX_BAD_SECRET;
     186          10 :     goto err;
     187             :   }
     188             : 
     189             :   /* How long is the plaintext? */
     190           6 :   encrypted = pwbox_encoded_getarray_data(enc);
     191           6 :   encrypted_len = pwbox_encoded_getlen_data(enc);
     192           6 :   if (encrypted_len < 4)
     193           0 :     goto err;
     194             : 
     195           6 :   cipher = crypto_cipher_new_with_iv((char*)keys, (char*)enc->iv);
     196           6 :   crypto_cipher_decrypt(cipher, (char*)&result_len, (char*)encrypted, 4);
     197           6 :   result_len = tor_ntohl(result_len);
     198           6 :   if (encrypted_len < result_len + 4)
     199           0 :     goto err;
     200             : 
     201             :   /* Allocate a buffer and decrypt */
     202           6 :   result = tor_malloc_zero(result_len);
     203           6 :   crypto_cipher_decrypt(cipher, (char*)result, (char*)encrypted+4, result_len);
     204             : 
     205           6 :   *out = result;
     206           6 :   *outlen_out = result_len;
     207             : 
     208           6 :   rv = UNPWBOX_OKAY;
     209           6 :   goto out;
     210             : 
     211             :  err:
     212          21 :   tor_free(result);
     213             : 
     214          21 :  out:
     215          21 :   crypto_cipher_free(cipher);
     216          21 :   pwbox_encoded_free(enc);
     217          21 :   memwipe(keys, 0, sizeof(keys));
     218          21 :   return rv;
     219             : }

Generated by: LCOV version 1.14