]> git.ipfire.org Git - thirdparty/tor.git/commitdiff
crypto: Add support for raw (ECB) AES
authorNick Mathewson <nickm@torproject.org>
Sat, 19 Apr 2025 14:56:25 +0000 (10:56 -0400)
committerNick Mathewson <nickm@torproject.org>
Wed, 21 May 2025 13:43:51 +0000 (09:43 -0400)
We'll need this to define the LRW2 tweakable block cipher used in CGO.

src/lib/crypt_ops/aes.h
src/lib/crypt_ops/aes_nss.c
src/lib/crypt_ops/aes_openssl.c
src/test/test_crypto.c

index d712b7713659bf53a9a0f7e5cb1e918b6f123686..e8a8f2cdd98c745f808cae9446012222b23f3bc8 100644 (file)
@@ -28,4 +28,15 @@ void aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len);
 int evaluate_evp_for_aes(int force_value);
 int evaluate_ctr_for_aes(void);
 
+#ifdef USE_AES_RAW
+typedef struct aes_raw_t aes_raw_t;
+
+aes_raw_t *aes_raw_new(const uint8_t *key, int key_bits, bool encrypt);
+void aes_raw_free_(aes_raw_t *cipher);
+#define aes_raw_free(cipher) \
+  FREE_AND_NULL(aes_raw_t, aes_raw_free_, (cipher))
+void aes_raw_encrypt(const aes_raw_t *cipher, uint8_t *block);
+void aes_raw_decrypt(const aes_raw_t *cipher, uint8_t *block);
+#endif
+
 #endif /* !defined(TOR_AES_H) */
index 7e4fe5ac2684385f473f5ee1cd00798452dc2a2e..a8e99fcaf6edfeb11d2ef8e9592ced17f5892ce0 100644 (file)
@@ -9,6 +9,8 @@
  * \brief Use NSS to implement AES_CTR.
  **/
 
+#define USE_AES_RAW
+
 #include "orconfig.h"
 #include "lib/crypt_ops/aes.h"
 #include "lib/crypt_ops/crypto_nss_mgt.h"
@@ -104,3 +106,74 @@ evaluate_ctr_for_aes(void)
 {
   return 0;
 }
+
+aes_raw_t *
+aes_raw_new(const uint8_t *key, int key_bits, bool encrypt)
+{
+  const CK_MECHANISM_TYPE ckm = CKM_AES_ECB;
+  SECItem keyItem = { .type = siBuffer, // ????
+                      .data = (unsigned char *)key,
+                      .len = (key_bits / 8) };
+  SECItem ivItem = { .type = siBuffer,
+                     .data = NULL,
+                     .len = 0 };
+  PK11SlotInfo *slot = NULL;
+  PK11SymKey *keyObj = NULL;
+  SECItem *ivObj = NULL;
+  PK11Context *result = NULL;
+
+  slot = PK11_GetBestSlot(ckm, NULL);
+  if (!slot)
+    goto err;
+
+  CK_ATTRIBUTE_TYPE mode = encrypt ? CKA_ENCRYPT : CKA_DECRYPT;
+
+  keyObj = PK11_ImportSymKey(slot, ckm, PK11_OriginUnwrap,
+                             mode, &keyItem, NULL);
+  if (!keyObj)
+    goto err;
+
+  ivObj = PK11_ParamFromIV(ckm, &ivItem);
+  if (!ivObj)
+    goto err;
+
+  PORT_SetError(SEC_ERROR_IO);
+  result = PK11_CreateContextBySymKey(ckm, mode, keyObj, ivObj);
+
+ err:
+
+  if (ivObj)
+    SECITEM_FreeItem(ivObj, PR_TRUE);
+  if (keyObj)
+    PK11_FreeSymKey(keyObj);
+  if (slot)
+    PK11_FreeSlot(slot);
+
+  tor_assert(result);
+  return (aes_raw_t *)result;
+}
+
+void
+aes_raw_free_(aes_raw_t *cipher_)
+{
+  if (!cipher_)
+    return;
+  PK11Context *ctx = (PK11Context*)cipher_;
+  PK11_DestroyContext(ctx, PR_TRUE);
+}
+void
+aes_raw_encrypt(const aes_raw_t *cipher, uint8_t *block)
+{
+  SECStatus s;
+  PK11Context *ctx = (PK11Context*)cipher;
+  int result_len = 0;
+  s = PK11_CipherOp(ctx, block, &result_len, 16, block, 16);
+  tor_assert(s == SECSuccess);
+  tor_assert(result_len == 16);
+}
+void
+aes_raw_decrypt(const aes_raw_t *cipher, uint8_t *block)
+{
+  /* This is the same function call for NSS. */
+  aes_raw_encrypt(cipher, block);
+}
index add18bccdd30abd04264216954874d4cfce88bda..f68d5e69b9f36b1aa1c0253a43c429fb3e49a947 100644 (file)
@@ -9,6 +9,8 @@
  * \brief Use OpenSSL to implement AES_CTR.
  **/
 
+#define USE_AES_RAW
+
 #include "orconfig.h"
 #include "lib/crypt_ops/aes.h"
 #include "lib/crypt_ops/crypto_util.h"
@@ -399,3 +401,79 @@ aes_set_iv(aes_cnt_cipher_t *cipher, const uint8_t *iv)
 }
 
 #endif /* defined(USE_EVP_AES_CTR) */
+
+/* ========
+ * Functions for "raw" (ECB) AES.
+ *
+ * I'm choosing the name "raw" here because ECB is not a mode;
+ * it's a disaster.  The only way to use this safely is
+ * within a real construction.
+ */
+
+/**
+ * Create a new instance of AES using a key of length 'key_bits'
+ * for raw block encryption.
+ *
+ * This is even more low-level than counter-mode, and you should
+ * only use it with extreme caution.
+ */
+aes_raw_t *
+aes_raw_new(const uint8_t *key, int key_bits, bool encrypt)
+{
+  EVP_CIPHER_CTX *cipher = EVP_CIPHER_CTX_new();
+  tor_assert(cipher);
+  const EVP_CIPHER *c = NULL;
+  switch (key_bits) {
+    case 128: c = EVP_aes_128_ecb(); break;
+    case 192: c = EVP_aes_192_ecb(); break;
+    case 256: c = EVP_aes_256_ecb(); break;
+    default: tor_assert_unreached();
+  }
+
+  int r = EVP_CipherInit(cipher, c, key, NULL, encrypt);
+  tor_assert(r == 1);
+  EVP_CIPHER_CTX_set_padding(cipher, 0);
+  return (aes_raw_t *)cipher;
+}
+/**
+ * Release storage held by 'cipher'.
+ */
+void
+aes_raw_free_(aes_raw_t *cipher_)
+{
+  if (!cipher_)
+    return;
+  EVP_CIPHER_CTX *cipher = (EVP_CIPHER_CTX *)cipher_;
+#ifdef OPENSSL_1_1_API
+  EVP_CIPHER_CTX_reset(cipher);
+#else
+  EVP_CIPHER_CTX_cleanup(cipher);
+#endif
+  EVP_CIPHER_CTX_free(cipher);
+}
+#define aes_raw_free(cipher) \
+  FREE_AND_NULL(aes_raw_t, aes_raw_free_, (cipher))
+/**
+ * Encrypt a single 16-byte block with 'cipher',
+ * which must have been initialized for encryption.
+ */
+void
+aes_raw_encrypt(const aes_raw_t *cipher, uint8_t *block)
+{
+  int outl = 16;
+  int r = EVP_EncryptUpdate((EVP_CIPHER_CTX *)cipher, block, &outl, block, 16);
+  tor_assert(r == 1);
+  tor_assert(outl == 16);
+}
+/**
+ * Decrypt a single 16-byte block with 'cipher',
+ * which must have been initialized for decryption.
+ */
+void
+aes_raw_decrypt(const aes_raw_t *cipher, uint8_t *block)
+{
+  int outl = 16;
+  int r = EVP_DecryptUpdate((EVP_CIPHER_CTX *)cipher, block, &outl, block, 16);
+  tor_assert(r == 1);
+  tor_assert(outl == 16);
+}
index 592ef7a77c30c4485c2953cf558d32916249443a..f20f1ae7476222dfd243bebbefc3ecd0647bc729 100644 (file)
@@ -6,6 +6,7 @@
 #include "orconfig.h"
 #define CRYPTO_CURVE25519_PRIVATE
 #define CRYPTO_RAND_PRIVATE
+#define USE_AES_RAW
 #include "core/or/or.h"
 #include "test/test.h"
 #include "lib/crypt_ops/aes.h"
@@ -3238,6 +3239,75 @@ test_crypto_polyval(void *arg)
   tor_free(mem_op_hex_tmp);
 }
 
+static void
+test_aes_raw_one(int keybits,
+                 const char *key_hex,
+                 const char *plaintext_hex,
+                 const char *ciphertext_hex)
+{
+  aes_raw_t *enc = NULL;
+  aes_raw_t *dec = NULL;
+  uint8_t key[32]; // max key size.
+  uint8_t pt[16], ct[16], block[16];
+  tt_int_op(keybits, OP_EQ, strlen(key_hex) * 8 / 2);
+  base16_decode((char*)key, sizeof(key), key_hex, strlen(key_hex));
+  base16_decode((char*)pt, sizeof(pt), plaintext_hex, strlen(plaintext_hex));
+  base16_decode((char*)ct, sizeof(ct), ciphertext_hex, strlen(ciphertext_hex));
+
+  enc = aes_raw_new(key, keybits, true);
+  dec = aes_raw_new(key, keybits, false);
+  memcpy(block, pt, sizeof(pt));
+  aes_raw_encrypt(enc, block);
+  tt_mem_op(block, OP_EQ, ct, 16);
+  aes_raw_decrypt(dec, block);
+  tt_mem_op(block, OP_EQ, pt, 16);
+
+ done:
+  aes_raw_free(enc);
+  aes_raw_free(dec);
+}
+
+static void
+test_crypto_aes_raw(void *arg)
+{
+  (void)arg;
+
+#define T test_aes_raw_one
+
+  /* From https://csrc.nist.gov/CSRC/media/Projects/
+     Cryptographic-Algorithm-Validation-Program/documents/aes/AESAVS.pdf */
+  const char *z128 =
+    "00000000000000000000000000000000";
+  const char *z192 =
+    "00000000000000000000000000000000"
+    "0000000000000000";
+  const char *z256 =
+    "00000000000000000000000000000000"
+    "00000000000000000000000000000000";
+
+  T(128, z128,
+    "f34481ec3cc627bacd5dc3fb08f273e6",
+    "0336763e966d92595a567cc9ce537f5e");
+  T(192, z192,
+    "1b077a6af4b7f98229de786d7516b639",
+    "275cfc0413d8ccb70513c3859b1d0f72");
+  T(256, z256,
+    "014730f80ac625fe84f026c60bfd547d",
+    "5c9d844ed46f9885085e5d6a4f94c7d7");
+  T(128,
+    "10a58869d74be5a374cf867cfb473859", z128,
+    "6d251e6944b051e04eaa6fb4dbf78465");
+  T(192,
+    "e9f065d7c13573587f7875357dfbb16c53489f6a4bd0f7cd", z128,
+    "0956259c9cd5cfd0181cca53380cde06");
+  T(256,
+    "c47b0294dbbbee0fec4757f22ffeee35"
+    "87ca4730c3d33b691df38bab076bc558", z128,
+    "46f2fb342d6f0ab477476fc501242c5f");
+
+#undef T
+}
+
 #ifndef COCCI
 #define CRYPTO_LEGACY(name)                                            \
   { #name, test_crypto_ ## name , 0, NULL, NULL }
@@ -3306,5 +3376,6 @@ struct testcase_t crypto_tests[] = {
   { "hashx", test_crypto_hashx, 0, NULL, NULL },
   { "failure_modes", test_crypto_failure_modes, TT_FORK, NULL, NULL },
   { "polyval", test_crypto_polyval, 0, NULL, NULL },
+  { "aes_raw", test_crypto_aes_raw, 0, NULL, NULL },
   END_OF_TESTCASES
 };