static void
run_s2k_tests(const unsigned flags, const unsigned type,
              int speclen, const int keylen, int legacy)
{
  uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN], buf3[S2K_MAXLEN];
  int r;
  size_t sz;
  const char pw1[] = "You can't come in here unless you say swordfish!";
  const char pw2[] = "Now, I give you one more guess.";

  r = secret_to_key_new(buf, sizeof(buf), &sz,
                        pw1, strlen(pw1), flags);
  tt_int_op(r, OP_EQ, S2K_OKAY);
  tt_int_op(buf[0], OP_EQ, type);

  tt_int_op(sz, OP_EQ, keylen + speclen);

  if (legacy) {
    memmove(buf, buf+1, sz-1);
    --sz;
    --speclen;
  }

  tt_int_op(S2K_OKAY, OP_EQ,
            secret_to_key_check(buf, sz, pw1, strlen(pw1)));

  tt_int_op(S2K_BAD_SECRET, OP_EQ,
            secret_to_key_check(buf, sz, pw2, strlen(pw2)));

  /* Move key to buf2, and clear it. */
  memset(buf3, 0, sizeof(buf3));
  memcpy(buf2, buf+speclen, keylen);
  memset(buf+speclen, 0, sz - speclen);

  /* Derivekey should produce the same results. */
  tt_int_op(S2K_OKAY, OP_EQ,
      secret_to_key_derivekey(buf3, keylen, buf, speclen, pw1, strlen(pw1)));

  tt_mem_op(buf2, OP_EQ, buf3, keylen);

  /* Derivekey with a longer output should fill the output. */
  memset(buf2, 0, sizeof(buf2));
  tt_int_op(S2K_OKAY, OP_EQ,
   secret_to_key_derivekey(buf2, sizeof(buf2), buf, speclen,
                           pw1, strlen(pw1)));

  tt_mem_op(buf2, OP_NE, buf3, sizeof(buf2));

  memset(buf3, 0, sizeof(buf3));
  tt_int_op(S2K_OKAY, OP_EQ,
            secret_to_key_derivekey(buf3, sizeof(buf3), buf, speclen,
                                    pw1, strlen(pw1)));
  tt_mem_op(buf2, OP_EQ, buf3, sizeof(buf3));
  tt_assert(!tor_mem_is_zero((char*)buf2+keylen, sizeof(buf2)-keylen));

 done:
  ;
}
Exemple #2
0
/**
 * 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;
}
static void
test_crypto_s2k_errors(void *arg)
{
  uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN];
  size_t sz;

  (void)arg;

  /* Bogus specifiers: simple */
  tt_int_op(S2K_BAD_LEN, OP_EQ,
            secret_to_key_derivekey(buf, sizeof(buf),
                                    (const uint8_t*)"", 0, "ABC", 3));
  tt_int_op(S2K_BAD_ALGORITHM, OP_EQ,
            secret_to_key_derivekey(buf, sizeof(buf),
                                    (const uint8_t*)"\x10", 1, "ABC", 3));
  tt_int_op(S2K_BAD_LEN, OP_EQ,
            secret_to_key_derivekey(buf, sizeof(buf),
                                    (const uint8_t*)"\x01\x02", 2, "ABC", 3));

  tt_int_op(S2K_BAD_LEN, OP_EQ,
            secret_to_key_check((const uint8_t*)"", 0, "ABC", 3));
  tt_int_op(S2K_BAD_ALGORITHM, OP_EQ,
            secret_to_key_check((const uint8_t*)"\x10", 1, "ABC", 3));
  tt_int_op(S2K_BAD_LEN, OP_EQ,
            secret_to_key_check((const uint8_t*)"\x01\x02", 2, "ABC", 3));

  /* too long gets "BAD_LEN" too */
  memset(buf, 0, sizeof(buf));
  buf[0] = 2;
  tt_int_op(S2K_BAD_LEN, OP_EQ,
            secret_to_key_derivekey(buf2, sizeof(buf2),
                                    buf, sizeof(buf), "ABC", 3));

  /* Truncated output */
#ifdef HAVE_LIBSCRYPT_H
  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz,
                                                 "ABC", 3, 0));
  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz,
                                                 "ABC", 3, S2K_FLAG_LOW_MEM));
#endif
  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 37, &sz,
                                              "ABC", 3, S2K_FLAG_USE_PBKDF2));
  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 29, &sz,
                                              "ABC", 3, S2K_FLAG_NO_SCRYPT));

#ifdef HAVE_LIBSCRYPT_H
  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18, 0));
  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18,
                                                 S2K_FLAG_LOW_MEM));
#endif
  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 17,
                                                 S2K_FLAG_USE_PBKDF2));
  tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 9,
                                                 S2K_FLAG_NO_SCRYPT));

  /* Now try using type-specific bogus specifiers. */

  /* It's a bad pbkdf2 buffer if it has an iteration count that would overflow
   * int32_t. */
  memset(buf, 0, sizeof(buf));
  buf[0] = 1; /* pbkdf2 */
  buf[17] = 100; /* 1<<100 is much bigger than INT32_MAX */
  tt_int_op(S2K_BAD_PARAMS, OP_EQ,
            secret_to_key_derivekey(buf2, sizeof(buf2),
                                    buf, 18, "ABC", 3));

#ifdef HAVE_LIBSCRYPT_H
  /* It's a bad scrypt buffer if N would overflow uint64 */
  memset(buf, 0, sizeof(buf));
  buf[0] = 2; /* scrypt */
  buf[17] = 100; /* 1<<100 is much bigger than UINT64_MAX */
  tt_int_op(S2K_BAD_PARAMS, OP_EQ,
            secret_to_key_derivekey(buf2, sizeof(buf2),
                                    buf, 19, "ABC", 3));
#endif

 done:
  ;
}
Exemple #4
0
/**
 * 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;
}