# HG changeset patch # User Matthew Wild # Date 1673620450 0 # Node ID d3ae47d8a7a7ffd28243bf5e8f186f05efa1ac6a # Parent dbe9781fd278e50fdf97f77d4a09f83d7efa8f4f util.crypto: Add support for AES-256-CTR This is required by PASETO v3.local diff -r dbe9781fd278 -r d3ae47d8a7a7 spec/util_crypto_spec.lua --- a/spec/util_crypto_spec.lua Fri Jan 13 14:29:08 2023 +0000 +++ b/spec/util_crypto_spec.lua Fri Jan 13 14:34:10 2023 +0000 @@ -164,4 +164,21 @@ end end); end); + + describe("AES-CTR encryption", function () + it("works", function () + local message = "foo\0bar hello world"; + local key_256_bit = random.bytes(32); + local test_cases = { + { crypto.aes_256_ctr_decrypt, crypto.aes_256_ctr_decrypt, key = key_256_bit }; + }; + for _, params in pairs(test_cases) do + local iv = params.iv or random.bytes(16); + local encrypted = params[1](params.key, iv, message); + assert.not_equal(message, encrypted); + local decrypted = params[2](params.key, iv, encrypted); + assert.equal(message, decrypted); + end + end); + end); end); diff -r dbe9781fd278 -r d3ae47d8a7a7 util-src/crypto.c --- a/util-src/crypto.c Fri Jan 13 14:29:08 2023 +0000 +++ b/util-src/crypto.c Fri Jan 13 14:34:10 2023 +0000 @@ -285,8 +285,8 @@ return base_evp_verify(L, NID_ED25519, NULL); } -/* gcm_encrypt(key, iv, plaintext) */ -static int Laes_gcm_encrypt(lua_State *L, const EVP_CIPHER *cipher, const unsigned char expected_key_len) { +/* encrypt(key, iv, plaintext) */ +static int Levp_encrypt(lua_State *L, const EVP_CIPHER *cipher, const unsigned char expected_key_len, const unsigned char expected_iv_len, const size_t tag_len) { EVP_CIPHER_CTX *ctx; luaL_Buffer ciphertext_buffer; @@ -300,7 +300,9 @@ if(key_len != expected_key_len) { return luaL_error(L, "key must be %d bytes", expected_key_len); } - luaL_argcheck(L, iv_len == 12, 2, "iv must be 12 bytes"); + if(iv_len != expected_iv_len) { + return luaL_error(L, "iv must be %d bytes", expected_iv_len); + } if(lua_gettop(L) > 3) { return luaL_error(L, "Expected 3 arguments, got %d", lua_gettop(L)); } @@ -319,7 +321,7 @@ } luaL_buffinit(L, &ciphertext_buffer); - unsigned char *ciphertext = (unsigned char*)luaL_prepbuffsize(&ciphertext_buffer, plaintext_len+16); + unsigned char *ciphertext = (unsigned char*)luaL_prepbuffsize(&ciphertext_buffer, plaintext_len+tag_len); if(1 != EVP_EncryptUpdate(ctx, ciphertext, &ciphertext_len, plaintext, plaintext_len)) { return luaL_error(L, "Error while encrypting data"); @@ -336,27 +338,35 @@ return luaL_error(L, "Non-zero final data"); } - /* Get the tag */ - if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, ciphertext + ciphertext_len)) { - return luaL_error(L, "Unable to read AEAD tag of encrypted data"); + if(tag_len > 0) { + /* Get the tag */ + if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, ciphertext + ciphertext_len)) { + return luaL_error(L, "Unable to read AEAD tag of encrypted data"); + } + /* Append tag */ + luaL_addsize(&ciphertext_buffer, ciphertext_len + tag_len); + } else { + luaL_addsize(&ciphertext_buffer, ciphertext_len); } - - luaL_addsize(&ciphertext_buffer, ciphertext_len + 16); luaL_pushresult(&ciphertext_buffer); return 1; } static int Laes_128_gcm_encrypt(lua_State *L) { - return Laes_gcm_encrypt(L, EVP_aes_128_gcm(), 16); + return Levp_encrypt(L, EVP_aes_128_gcm(), 16, 12, 16); } static int Laes_256_gcm_encrypt(lua_State *L) { - return Laes_gcm_encrypt(L, EVP_aes_256_gcm(), 32); + return Levp_encrypt(L, EVP_aes_256_gcm(), 32, 12, 16); } -/* gcm_decrypt(key, iv, ciphertext) */ -static int Laes_gcm_decrypt(lua_State *L, const EVP_CIPHER *cipher, const unsigned char expected_key_len) { +static int Laes_256_ctr_encrypt(lua_State *L) { + return Levp_encrypt(L, EVP_aes_256_ctr(), 32, 16, 0); +} + +/* decrypt(key, iv, ciphertext) */ +static int Levp_decrypt(lua_State *L, const EVP_CIPHER *cipher, const unsigned char expected_key_len, const unsigned char expected_iv_len, const size_t tag_len) { EVP_CIPHER_CTX *ctx; luaL_Buffer plaintext_buffer; @@ -370,8 +380,12 @@ if(key_len != expected_key_len) { return luaL_error(L, "key must be %d bytes", expected_key_len); } - luaL_argcheck(L, iv_len == 12, 2, "iv must be 12 bytes"); - luaL_argcheck(L, ciphertext_len > 16, 3, "ciphertext must be at least 16 bytes (including tag)"); + if(iv_len != expected_iv_len) { + return luaL_error(L, "iv must be %d bytes", expected_iv_len); + } + if(ciphertext_len <= tag_len) { + return luaL_error(L, "ciphertext must be at least %d bytes (including tag)", tag_len); + } if(lua_gettop(L) > 3) { return luaL_error(L, "Expected 3 arguments, got %d", lua_gettop(L)); } @@ -396,13 +410,15 @@ * Provide the message to be decrypted, and obtain the plaintext output. * EVP_DecryptUpdate can be called multiple times if necessary */ - if(!EVP_DecryptUpdate(ctx, plaintext, &plaintext_len, ciphertext, ciphertext_len-16)) { + if(!EVP_DecryptUpdate(ctx, plaintext, &plaintext_len, ciphertext, ciphertext_len-tag_len)) { return luaL_error(L, "Error while decrypting data"); } - /* Set expected tag value. Works in OpenSSL 1.0.1d and later */ - if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, (unsigned char*)ciphertext + (ciphertext_len-16))) { - return luaL_error(L, "Error while processing authentication tag"); + if(tag_len > 0) { + /* Set expected tag value. Works in OpenSSL 1.0.1d and later */ + if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, (unsigned char*)ciphertext + (ciphertext_len-tag_len))) { + return luaL_error(L, "Error while processing authentication tag"); + } } /* @@ -424,11 +440,15 @@ } static int Laes_128_gcm_decrypt(lua_State *L) { - return Laes_gcm_decrypt(L, EVP_aes_128_gcm(), 16); + return Levp_decrypt(L, EVP_aes_128_gcm(), 16, 12, 16); } static int Laes_256_gcm_decrypt(lua_State *L) { - return Laes_gcm_decrypt(L, EVP_aes_256_gcm(), 32); + return Levp_decrypt(L, EVP_aes_256_gcm(), 32, 12, 16); +} + +static int Laes_256_ctr_decrypt(lua_State *L) { + return Levp_decrypt(L, EVP_aes_256_ctr(), 32, 16, 0); } /* r, s = parse_ecdsa_sig(sig_der) */ @@ -547,6 +567,9 @@ { "aes_256_gcm_encrypt", Laes_256_gcm_encrypt }, { "aes_256_gcm_decrypt", Laes_256_gcm_decrypt }, + { "aes_256_ctr_encrypt", Laes_256_ctr_encrypt }, + { "aes_256_ctr_decrypt", Laes_256_ctr_decrypt }, + { "generate_ed25519_keypair", Lgenerate_ed25519_keypair }, { "import_private_pem", Limport_private_pem },