Skip to content

Commit

Permalink
AES-CBC
Browse files Browse the repository at this point in the history
  • Loading branch information
amirhosv committed Apr 30, 2024
1 parent 6656ff9 commit 755c978
Show file tree
Hide file tree
Showing 32 changed files with 2,464 additions and 82 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/macos_ci_jobs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- name: Install lcov
- name: Install lcov golang
run: |
env HOMEBREW_NO_AUTO_UPDATE=1 brew install lcov
env HOMEBREW_NO_AUTO_UPDATE=1 brew install lcov golang
- name: Install clang-format
run: |
env HOMEBREW_NO_AUTO_UPDATE=1 brew install clang-format
Expand Down Expand Up @@ -70,9 +70,9 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- name: Install lcov
- name: Install lcov golang
run: |
env HOMEBREW_NO_AUTO_UPDATE=1 brew install lcov
env HOMEBREW_NO_AUTO_UPDATE=1 brew install lcov golang
- name: Install clang-format
run: |
env HOMEBREW_NO_AUTO_UPDATE=1 brew install clang-format
Expand Down Expand Up @@ -107,9 +107,9 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: true
- name: Install lcov
- name: Install lcov golang
run: |
env HOMEBREW_NO_AUTO_UPDATE=1 brew install lcov
env HOMEBREW_NO_AUTO_UPDATE=1 brew install lcov golang
- name: Install clang-format
run: |
env HOMEBREW_NO_AUTO_UPDATE=1 brew install clang-format
Expand Down
14 changes: 9 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ add_library(
amazonCorrettoCryptoProvider SHARED
csrc/aes_gcm.cpp
csrc/aes_xts.cpp
csrc/aes_cbc.cpp
csrc/aes_kwp.cpp
csrc/agreement.cpp
csrc/bn.cpp
Expand Down Expand Up @@ -682,16 +683,18 @@ add_custom_target(check-junit-SecurityManager

DEPENDS accp-jar tests-jar)

add_custom_target(check-junit-AesGcmLazy
add_custom_target(check-junit-AesLazy
COMMAND ${TEST_JAVA_EXECUTABLE}
-Dcom.amazon.corretto.crypto.provider.nativeContextReleaseStrategy=LAZY
${TEST_RUNNER_ARGUMENTS}
--select-class=com.amazon.corretto.crypto.provider.test.AesTest
--select-class=com.amazon.corretto.crypto.provider.test.AesGcmKatTest
--select-class=com.amazon.corretto.crypto.provider.test.AesCbcTest
--select-class=com.amazon.corretto.crypto.provider.test.AesCbcNistTest

DEPENDS accp-jar tests-jar)

add_custom_target(check-junit-AesGcmEager
add_custom_target(check-junit-AesEager
COMMAND ${TEST_JAVA_EXECUTABLE}
-Dcom.amazon.corretto.crypto.provider.nativeContextReleaseStrategy=EAGER
${TEST_RUNNER_ARGUMENTS}
Expand Down Expand Up @@ -807,9 +810,10 @@ add_custom_target(check
check-junit
check-junit-SecurityManager
check-external-lib
check-junit-AesGcmLazy
check-junit-AesGcmEager
check-junit-DifferentTempDir)
check-junit-AesLazy
check-junit-AesEager
check-junit-DifferentTempDir
)

if(ENABLE_NATIVE_TEST_HOOKS)
add_custom_target(check-keyutils
Expand Down
297 changes: 297 additions & 0 deletions csrc/aes_cbc.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
#include "buffer.h"
#include "env.h"
#include <openssl/err.h>
#include <openssl/evp.h>
#include <cstring>
#include <jni.h>

#define AWS_LC_BAD_PADDING_ERROR_CODE 0x1e000065

#define EX_BAD_PADDING "javax/crypto/BadPaddingException"

#define AES_CBC_BLOCK_SIZE_IN_BYTES 16
#define KEY_LEN_AES128 16
#define KEY_LEN_AES192 24
#define KEY_LEN_AES256 32

namespace AmazonCorrettoCryptoProvider {

class AesCbcCipher {
JNIEnv* jenv_;
EVP_CIPHER_CTX* ctx_;
bool own_ctx_;

static bool output_clobbers_input(uint8_t const* input, int input_len, uint8_t const* output, int unprocessed_input)
{
// Let's say we have 5 unprocessed bytes. The first 11 (16 - 5) bytes of input would produce 16 bytes of output.
// To avoid overwriting the input, output must be at least 11 bytes behind input.
int delta = (unprocessed_input == 0) || (unprocessed_input == AES_CBC_BLOCK_SIZE_IN_BYTES)
? unprocessed_input
: (AES_CBC_BLOCK_SIZE_IN_BYTES - unprocessed_input);
if ((output + delta) <= input) {
return false;
}

// If the output starts after the input ends, then we're not clobbering anything.
if ((input + input_len) <= output) {
return false;
}

return true;
}

static void check_unprocessed_input(int unprocessed_input)
{
if (unprocessed_input < 0 || unprocessed_input > 16) {
// This should not be reachable since we check this in Java.
throw java_ex(EX_ERROR, "unprocessed_input is not in [0, 16] range.");
}
}

public:
AesCbcCipher(JNIEnv* jenv, jlongArray ctx_container, jlong ctx_ptr, bool save_ctx)
: jenv_(jenv)
, ctx_(reinterpret_cast<EVP_CIPHER_CTX*>(ctx_ptr))
, own_ctx_(!save_ctx)
{
if (ctx_ == nullptr) {
// There is no context, so we need to create one.
ctx_ = EVP_CIPHER_CTX_new();
if (ctx_ == nullptr) {
throw_openssl(EX_RUNTIME_CRYPTO, "EVP_CIPHER_CTX_new failed.");
}

if (!own_ctx_) {
// We need to return the context.
jlong tmpPtr = reinterpret_cast<jlong>(ctx_);
jenv_->SetLongArrayRegion(ctx_container, 0, 1, &tmpPtr);
}
}
}

~AesCbcCipher()
{
if (own_ctx_) {
EVP_CIPHER_CTX_free(ctx_);
}
}

void init(int op_mode, int padding, uint8_t const* key, int key_len, uint8_t const* iv)
{
EVP_CIPHER const* cipher;
switch (key_len) {
case KEY_LEN_AES128:
cipher = EVP_aes_128_cbc();
break;
case KEY_LEN_AES192:
cipher = EVP_aes_192_cbc();
break;
case KEY_LEN_AES256:
cipher = EVP_aes_256_cbc();
break;
default:
// This should not happen since we check this in the Java layer.
throw java_ex(EX_ERROR, "THIS SHOULD NOT BE REACHABLE. Invalid AES key size.");
}

if (EVP_CipherInit_ex(ctx_, cipher, nullptr, key, iv, op_mode) != 1) {
throw_openssl(EX_RUNTIME_CRYPTO, "EVP_CipherInit_ex failed.");
}

// This method always returns 1 and succeeds.
if (EVP_CIPHER_CTX_set_padding(ctx_, padding) != 1) {
throw_openssl(EX_RUNTIME_CRYPTO, "EVP_CIPHER_CTX_set_padding failed.");
}
}

int update(uint8_t const* input, int input_len, uint8_t* output, int unprocessed_input)
{
check_unprocessed_input(unprocessed_input);
int result = 0;
if (output_clobbers_input(input, input_len, output, unprocessed_input)) {
SimpleBuffer temp(input_len + unprocessed_input);

if (EVP_CipherUpdate(ctx_, temp.get_buffer(), &result, input, input_len) != 1) {
throw_openssl(EX_RUNTIME_CRYPTO, "EVP_CipherUpdate failed.");
}

std::memcpy(output, temp.get_buffer(), result);
} else {
if (EVP_CipherUpdate(ctx_, output, &result, input, input_len) != 1) {
throw_openssl(EX_RUNTIME_CRYPTO, "EVP_CipherUpdate failed.");
}
}

return result;
}

int do_final(uint8_t* output)
{
int result = 0;
if (EVP_CipherFinal_ex(ctx_, output, &result) != 1) {
if (ERR_get_error() == AWS_LC_BAD_PADDING_ERROR_CODE) {
throw java_ex(EX_BAD_PADDING, "Bad padding");
} else {
throw_openssl(EX_RUNTIME_CRYPTO, "EVP_CipherFinal_ex failed.");
}
}
return result;
}
};

}

using namespace AmazonCorrettoCryptoProvider;

extern "C" JNIEXPORT jint JNICALL Java_com_amazon_corretto_crypto_provider_AesCbcSpi_nInitUpdateFinal(JNIEnv* env,
jclass,
jint opMod,
jint padding,
jbyteArray key,
jint keyLen,
jbyteArray iv,
jlongArray ctxContainer,
jlong ctxPtr,
jboolean saveCtx,
jobject inputDirect,
jbyteArray inputArray,
jint inputOffset,
jint inputLen,
jobject outputDirect,
jbyteArray outputArray,
jint outputOffset)
{
try {
AesCbcCipher aes_cbc_cipher(env, ctxContainer, ctxPtr, saveCtx);
// init
{
JBinaryBlob j_key(env, nullptr, key);
JBinaryBlob j_iv(env, nullptr, iv);
aes_cbc_cipher.init(opMod, padding, j_key.get(), keyLen, j_iv.get());
}

int result = 0;

// update
JBinaryBlob output(env, outputDirect, outputArray);

{
JBinaryBlob input(env, inputDirect, inputArray);
result = aes_cbc_cipher.update(input.get() + inputOffset, inputLen, output.get() + outputOffset, 0);
}

// final
result += aes_cbc_cipher.do_final(output.get() + outputOffset + result);

return result;
} catch (java_ex& ex) {
ex.throw_to_java(env);
return -1;
}
}

extern "C" JNIEXPORT jint JNICALL Java_com_amazon_corretto_crypto_provider_AesCbcSpi_nInitUpdate(JNIEnv* env,
jclass,
jint opMod,
jint padding,
jbyteArray key,
jint keyLen,
jbyteArray iv,
jlongArray ctxContainer,
jlong ctxPtr,
jobject inputDirect,
jbyteArray inputArray,
jint inputOffset,
jint inputLen,
jobject outputDirect,
jbyteArray outputArray,
jint outputOffset)
{
try {
AesCbcCipher aes_cbc_cipher(env, ctxContainer, ctxPtr, true);
// init
{
JBinaryBlob j_key(env, nullptr, key);
JBinaryBlob j_iv(env, nullptr, iv);
aes_cbc_cipher.init(opMod, padding, j_key.get(), keyLen, j_iv.get());
}

// update
JBinaryBlob output(env, outputDirect, outputArray);
JBinaryBlob input(env, inputDirect, inputArray);

return aes_cbc_cipher.update(input.get() + inputOffset, inputLen, output.get() + outputOffset, 0);

} catch (java_ex& ex) {
ex.throw_to_java(env);
return -1;
}
}

extern "C" JNIEXPORT jint JNICALL Java_com_amazon_corretto_crypto_provider_AesCbcSpi_nUpdate(JNIEnv* env,
jclass,
jlong ctxPtr,
jobject inputDirect,
jbyteArray inputArray,
jint inputOffset,
jint inputLen,
jint unprocessed_input,
jobject outputDirect,
jbyteArray outputArray,
jint outputOffset)
{
try {
AesCbcCipher aes_cbc_cipher(env, nullptr, ctxPtr, true);

// update
JBinaryBlob output(env, outputDirect, outputArray);
JBinaryBlob input(env, inputDirect, inputArray);

return aes_cbc_cipher.update(
input.get() + inputOffset, inputLen, output.get() + outputOffset, unprocessed_input);

} catch (java_ex& ex) {
ex.throw_to_java(env);
return -1;
}
}

extern "C" JNIEXPORT jint JNICALL Java_com_amazon_corretto_crypto_provider_AesCbcSpi_nUpdateFinal(JNIEnv* env,
jclass,
jlongArray ctxContainer,
jlong ctxPtr,
jboolean saveCtx,
jobject inputDirect,
jbyteArray inputArray,
jint inputOffset,
jint inputLen,
jint unprocessedInput,
jobject outputDirect,
jbyteArray outputArray,
jint outputOffset)
{
try {
AesCbcCipher aes_cbc_cipher(env, ctxContainer, ctxPtr, saveCtx);

int result = 0;

// update
JBinaryBlob output(env, outputDirect, outputArray);

{
JBinaryBlob input(env, inputDirect, inputArray);
result = aes_cbc_cipher.update(
input.get() + inputOffset, inputLen, output.get() + outputOffset, unprocessedInput);
}

// final
result += aes_cbc_cipher.do_final(output.get() + outputOffset + result);

return result;
} catch (java_ex& ex) {
ex.throw_to_java(env);
return -1;
}
}
7 changes: 0 additions & 7 deletions csrc/aes_gcm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,13 +241,6 @@ JNIEXPORT jlong JNICALL Java_com_amazon_corretto_crypto_provider_AesGcmSpi_encry
}
}

JNIEXPORT void JNICALL Java_com_amazon_corretto_crypto_provider_AesGcmSpi_releaseContext(JNIEnv*, jclass, jlong ctxPtr)
{
EVP_CIPHER_CTX* ctx = (EVP_CIPHER_CTX*)ctxPtr;

EVP_CIPHER_CTX_free(ctx);
}

JNIEXPORT jint JNICALL Java_com_amazon_corretto_crypto_provider_AesGcmSpi_encryptUpdate(JNIEnv* pEnv,
jclass,
jlong ctxPtr,
Expand Down
Loading

0 comments on commit 755c978

Please sign in to comment.