[tor-commits] [tor/master] Rudimentary-but-sufficient passphrase-encrypted box code.

nickm at torproject.org nickm at torproject.org
Thu Sep 25 16:06:16 UTC 2014


commit 8184839a47ed1c7b42993d69d54e757a43bfda9c
Author: Nick Mathewson <nickm at torproject.org>
Date:   Thu Aug 28 17:59:06 2014 -0400

    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.
---
 src/common/crypto_pwbox.c |  167 +++++++++++++++++++++++++++++++++++++++++++++
 src/common/crypto_pwbox.h |   20 ++++++
 src/common/include.am     |    2 +
 src/test/test_crypto.c    |   33 +++++++++
 4 files changed, 222 insertions(+)

diff --git a/src/common/crypto_pwbox.c b/src/common/crypto_pwbox.c
new file mode 100644
index 0000000..61b6ebe
--- /dev/null
+++ b/src/common/crypto_pwbox.c
@@ -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;
+}
+
diff --git a/src/common/crypto_pwbox.h b/src/common/crypto_pwbox.h
new file mode 100644
index 0000000..aadd477
--- /dev/null
+++ b/src/common/crypto_pwbox.h
@@ -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
+
diff --git a/src/common/include.am b/src/common/include.am
index 0fb1962..83a3706 100644
--- a/src/common/include.am
+++ b/src/common/include.am
@@ -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				\
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index d36d657..3da440d 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -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),





More information about the tor-commits mailing list