Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 7 additions & 11 deletions packages/react-native-quick-crypto/cpp/cipher/CCMCipher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ void CCMCipher::init(const std::shared_ptr<ArrayBuffer> cipher_key, const std::s
checkCtx();

// 2. Perform CCM-specific initialization
auto native_iv = ToNativeArrayBuffer(iv);
size_t iv_len = native_iv->size();
size_t iv_len = iv->size();

// Set the IV length using CCM-specific control
if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_CCM_SET_IVLEN, iv_len, nullptr) != 1) {
Expand All @@ -39,9 +38,8 @@ void CCMCipher::init(const std::shared_ptr<ArrayBuffer> cipher_key, const std::s
}

// Finally, initialize the key and IV using the parameters passed to this function.
auto native_key = ToNativeArrayBuffer(cipher_key); // Use 'cipher_key' parameter
const unsigned char* key_ptr = reinterpret_cast<const unsigned char*>(native_key->data());
const unsigned char* iv_ptr = reinterpret_cast<const unsigned char*>(native_iv->data());
const unsigned char* key_ptr = reinterpret_cast<const unsigned char*>(cipher_key->data());
const unsigned char* iv_ptr = reinterpret_cast<const unsigned char*>(iv->data());

// The last argument (is_cipher) should be consistent with the initial setup call.
if (EVP_CipherInit_ex(ctx.get(), nullptr, nullptr, key_ptr, iv_ptr, is_cipher) != 1) {
Expand All @@ -56,8 +54,7 @@ std::shared_ptr<ArrayBuffer> CCMCipher::update(const std::shared_ptr<ArrayBuffer
checkCtx();
checkNotFinalized();
has_update_called = true;
auto native_data = ToNativeArrayBuffer(data);
size_t in_len = native_data->size();
size_t in_len = data->size();
if (in_len < 0 || in_len > INT_MAX) {
throw std::runtime_error("Invalid message length");
}
Expand All @@ -77,7 +74,7 @@ std::shared_ptr<ArrayBuffer> CCMCipher::update(const std::shared_ptr<ArrayBuffer
}

auto out_buf = std::make_unique<unsigned char[]>(out_len);
const uint8_t* in = reinterpret_cast<const uint8_t*>(native_data->data());
const uint8_t* in = reinterpret_cast<const uint8_t*>(data->data());

int actual_out_len = 0;
int ret = EVP_CipherUpdate(ctx.get(), out_buf.get(), &actual_out_len, in, in_len);
Expand Down Expand Up @@ -185,8 +182,7 @@ bool CCMCipher::setAAD(const std::shared_ptr<ArrayBuffer>& data, std::optional<d
int out_len = 0;

// Get AAD data and length *before* deciding whether to set total length
auto native_aad = ToNativeArrayBuffer(data);
size_t aad_len = native_aad->size();
size_t aad_len = data->size();

// 1. Set the total *ciphertext* length. This seems necessary based on examples,
// BUT the wiki says "(only needed if AAD is passed)". Let's skip if decrypting and AAD length is 0.
Expand All @@ -203,7 +199,7 @@ bool CCMCipher::setAAD(const std::shared_ptr<ArrayBuffer>& data, std::optional<d
// 2. Process AAD Data
// Per OpenSSL CCM decryption examples, this MUST be called even if aad_len is 0.
// Pass nullptr as the output buffer, the AAD data pointer, and its length.
if (EVP_CipherUpdate(ctx.get(), nullptr, &out_len, native_aad->data(), aad_len) != 1) {
if (EVP_CipherUpdate(ctx.get(), nullptr, &out_len, data->data(), aad_len) != 1) {
unsigned long err = ERR_get_error();
char err_buf[256];
ERR_error_string_n(err, err_buf, sizeof(err_buf));
Expand Down
16 changes: 6 additions & 10 deletions packages/react-native-quick-crypto/cpp/cipher/ChaCha20Cipher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,18 @@ void ChaCha20Cipher::init(const std::shared_ptr<ArrayBuffer> cipher_key, const s
}

// Set key and IV
auto native_key = ToNativeArrayBuffer(cipher_key);
auto native_iv = ToNativeArrayBuffer(iv);

// Validate key size
if (native_key->size() != kKeySize) {
if (cipher_key->size() != kKeySize) {
throw std::runtime_error("ChaCha20 key must be 32 bytes");
}

// Validate IV size
if (native_iv->size() != kIVSize) {
if (iv->size() != kIVSize) {
throw std::runtime_error("ChaCha20 IV must be 16 bytes");
}

const unsigned char* key_ptr = reinterpret_cast<const unsigned char*>(native_key->data());
const unsigned char* iv_ptr = reinterpret_cast<const unsigned char*>(native_iv->data());
const unsigned char* key_ptr = reinterpret_cast<const unsigned char*>(cipher_key->data());
const unsigned char* iv_ptr = reinterpret_cast<const unsigned char*>(iv->data());

if (EVP_CipherInit_ex(ctx.get(), nullptr, nullptr, key_ptr, iv_ptr, is_cipher) != 1) {
unsigned long err = ERR_get_error();
Expand All @@ -60,8 +57,7 @@ void ChaCha20Cipher::init(const std::shared_ptr<ArrayBuffer> cipher_key, const s
std::shared_ptr<ArrayBuffer> ChaCha20Cipher::update(const std::shared_ptr<ArrayBuffer>& data) {
checkCtx();
checkNotFinalized();
auto native_data = ToNativeArrayBuffer(data);
size_t in_len = native_data->size();
size_t in_len = data->size();
if (in_len > INT_MAX) {
throw std::runtime_error("Message too long");
}
Expand All @@ -71,7 +67,7 @@ std::shared_ptr<ArrayBuffer> ChaCha20Cipher::update(const std::shared_ptr<ArrayB
auto out_buf = std::make_unique<uint8_t[]>(out_len);

// Perform the cipher update operation
if (EVP_CipherUpdate(ctx.get(), out_buf.get(), &out_len, native_data->data(), in_len) != 1) {
if (EVP_CipherUpdate(ctx.get(), out_buf.get(), &out_len, data->data(), in_len) != 1) {
unsigned long err = ERR_get_error();
char err_buf[256];
ERR_error_string_n(err, err_buf, sizeof(err_buf));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,18 @@ void ChaCha20Poly1305Cipher::init(const std::shared_ptr<ArrayBuffer> cipher_key,
}

// Set key and IV
auto native_key = ToNativeArrayBuffer(cipher_key);
auto native_iv = ToNativeArrayBuffer(iv);

// Validate key size
if (native_key->size() != kKeySize) {
if (cipher_key->size() != kKeySize) {
throw std::runtime_error("ChaCha20-Poly1305 key must be 32 bytes");
}

// Validate nonce size
if (native_iv->size() != kNonceSize) {
if (iv->size() != kNonceSize) {
throw std::runtime_error("ChaCha20-Poly1305 nonce must be 12 bytes");
}

const unsigned char* key_ptr = reinterpret_cast<const unsigned char*>(native_key->data());
const unsigned char* iv_ptr = reinterpret_cast<const unsigned char*>(native_iv->data());
const unsigned char* key_ptr = reinterpret_cast<const unsigned char*>(cipher_key->data());
const unsigned char* iv_ptr = reinterpret_cast<const unsigned char*>(iv->data());

if (EVP_CipherInit_ex(ctx.get(), nullptr, nullptr, key_ptr, iv_ptr, is_cipher) != 1) {
unsigned long err = ERR_get_error();
Expand All @@ -66,8 +63,7 @@ std::shared_ptr<ArrayBuffer> ChaCha20Poly1305Cipher::update(const std::shared_pt
checkCtx();
checkNotFinalized();
has_update_called = true;
auto native_data = ToNativeArrayBuffer(data);
size_t in_len = native_data->size();
size_t in_len = data->size();
if (in_len > INT_MAX) {
throw std::runtime_error("Message too long");
}
Expand All @@ -77,7 +73,7 @@ std::shared_ptr<ArrayBuffer> ChaCha20Poly1305Cipher::update(const std::shared_pt
auto out_buf = std::make_unique<uint8_t[]>(out_len);

// Perform the cipher update operation
if (EVP_CipherUpdate(ctx.get(), out_buf.get(), &out_len, native_data->data(), in_len) != 1) {
if (EVP_CipherUpdate(ctx.get(), out_buf.get(), &out_len, data->data(), in_len) != 1) {
unsigned long err = ERR_get_error();
char err_buf[256];
ERR_error_string_n(err, err_buf, sizeof(err_buf));
Expand Down Expand Up @@ -121,12 +117,11 @@ std::shared_ptr<ArrayBuffer> ChaCha20Poly1305Cipher::final() {
bool ChaCha20Poly1305Cipher::setAAD(const std::shared_ptr<ArrayBuffer>& data, std::optional<double> plaintextLength) {
checkCtx();
checkAADBeforeUpdate();
auto native_aad = ToNativeArrayBuffer(data);
size_t aad_len = native_aad->size();
size_t aad_len = data->size();

// Set AAD data
int out_len = 0;
if (EVP_CipherUpdate(ctx.get(), nullptr, &out_len, native_aad->data(), aad_len) != 1) {
if (EVP_CipherUpdate(ctx.get(), nullptr, &out_len, data->data(), aad_len) != 1) {
unsigned long err = ERR_get_error();
char err_buf[256];
ERR_error_string_n(err, err_buf, sizeof(err_buf));
Expand Down Expand Up @@ -163,12 +158,11 @@ bool ChaCha20Poly1305Cipher::setAuthTag(const std::shared_ptr<ArrayBuffer>& tag)
throw std::runtime_error("setAuthTag can only be called during decryption");
}

auto native_tag = ToNativeArrayBuffer(tag);
if (native_tag->size() != kTagSize) {
if (tag->size() != kTagSize) {
throw std::runtime_error("ChaCha20-Poly1305 tag must be 16 bytes");
}

if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_TAG, kTagSize, native_tag->data()) != 1) {
if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_TAG, kTagSize, tag->data()) != 1) {
unsigned long err = ERR_get_error();
char err_buf[256];
ERR_error_string_n(err, err_buf, sizeof(err_buf));
Expand Down
8 changes: 3 additions & 5 deletions packages/react-native-quick-crypto/cpp/cipher/GCMCipher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ void GCMCipher::init(const std::shared_ptr<ArrayBuffer> cipher_key, const std::s
}

// 4. Set IV length for non-standard IV sizes (GCM default is 96 bits/12 bytes)
auto native_iv = ToNativeArrayBuffer(iv);
size_t iv_len = native_iv->size();
size_t iv_len = iv->size();

if (iv_len != 12) { // Only set if not the default length
if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, static_cast<int>(iv_len), nullptr) != 1) {
Expand All @@ -51,9 +50,8 @@ void GCMCipher::init(const std::shared_ptr<ArrayBuffer> cipher_key, const std::s
}

// 5. Now set the key and IV
auto native_key = ToNativeArrayBuffer(cipher_key);
const unsigned char* key_ptr = reinterpret_cast<const unsigned char*>(native_key->data());
const unsigned char* iv_ptr = reinterpret_cast<const unsigned char*>(native_iv->data());
const unsigned char* key_ptr = reinterpret_cast<const unsigned char*>(cipher_key->data());
const unsigned char* iv_ptr = reinterpret_cast<const unsigned char*>(iv->data());

if (EVP_CipherInit_ex(ctx.get(), nullptr, nullptr, key_ptr, iv_ptr, is_cipher) != 1) {
unsigned long err = ERR_get_error();
Expand Down
20 changes: 7 additions & 13 deletions packages/react-native-quick-crypto/cpp/cipher/HybridCipher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,8 @@ void HybridCipher::init(const std::shared_ptr<ArrayBuffer> cipher_key, const std

// For base hybrid cipher, set key and IV immediately.
// Derived classes like CCM might override init and handle this differently.
auto native_key = ToNativeArrayBuffer(cipher_key);
auto native_iv = ToNativeArrayBuffer(iv);
const unsigned char* key_ptr = reinterpret_cast<const unsigned char*>(native_key->data());
const unsigned char* iv_ptr = reinterpret_cast<const unsigned char*>(native_iv->data());
const unsigned char* key_ptr = reinterpret_cast<const unsigned char*>(cipher_key->data());
const unsigned char* iv_ptr = reinterpret_cast<const unsigned char*>(iv->data());

if (EVP_CipherInit_ex(ctx.get(), nullptr, nullptr, key_ptr, iv_ptr, is_cipher) != 1) {
unsigned long err = ERR_get_error();
Expand All @@ -106,11 +104,10 @@ void HybridCipher::init(const std::shared_ptr<ArrayBuffer> cipher_key, const std
}

std::shared_ptr<ArrayBuffer> HybridCipher::update(const std::shared_ptr<ArrayBuffer>& data) {
auto native_data = ToNativeArrayBuffer(data);
checkCtx();
checkNotFinalized();
has_update_called = true;
size_t in_len = native_data->size();
size_t in_len = data->size();
if (in_len > INT_MAX) {
throw std::runtime_error("Message too long");
}
Expand All @@ -119,7 +116,7 @@ std::shared_ptr<ArrayBuffer> HybridCipher::update(const std::shared_ptr<ArrayBuf
auto out_buf = std::make_unique<uint8_t[]>(out_len);
// Perform the cipher update operation. The real size of the output is
// returned in out_len
int ret = EVP_CipherUpdate(ctx.get(), out_buf.get(), &out_len, native_data->data(), in_len);
int ret = EVP_CipherUpdate(ctx.get(), out_buf.get(), &out_len, data->data(), in_len);

if (!ret) {
unsigned long err = ERR_get_error();
Expand Down Expand Up @@ -168,11 +165,9 @@ std::shared_ptr<ArrayBuffer> HybridCipher::final() {
bool HybridCipher::setAAD(const std::shared_ptr<ArrayBuffer>& data, std::optional<double> plaintextLength) {
checkCtx();
checkAADBeforeUpdate();
auto native_data = ToNativeArrayBuffer(data);

// Set the AAD
int out_len;
if (!EVP_CipherUpdate(ctx.get(), nullptr, &out_len, native_data->data(), native_data->size())) {
if (!EVP_CipherUpdate(ctx.get(), nullptr, &out_len, data->data(), data->size())) {
return false;
}

Expand All @@ -192,9 +187,8 @@ bool HybridCipher::setAuthTag(const std::shared_ptr<ArrayBuffer>& tag) {
throw std::runtime_error("setAuthTag can only be called during decryption.");
}

auto native_tag = ToNativeArrayBuffer(tag);
size_t tag_len = native_tag->size();
uint8_t* tag_ptr = native_tag->data();
size_t tag_len = tag->size();
uint8_t* tag_ptr = tag->data();

int mode = EVP_CIPHER_CTX_mode(ctx.get());

Expand Down
46 changes: 19 additions & 27 deletions packages/react-native-quick-crypto/cpp/cipher/HybridRsaCipher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,25 +102,23 @@ std::shared_ptr<ArrayBuffer> HybridRsaCipher::encrypt(const std::shared_ptr<Hybr
}

if (label.has_value() && label.value()->size() > 0) {
auto native_label = ToNativeArrayBuffer(label.value());
unsigned char* label_copy = (unsigned char*)OPENSSL_malloc(native_label->size());
unsigned char* label_copy = (unsigned char*)OPENSSL_malloc(label.value()->size());
if (!label_copy) {
EVP_PKEY_CTX_free(ctx);
throw std::runtime_error("Failed to allocate memory for label");
}
std::memcpy(label_copy, native_label->data(), native_label->size());
std::memcpy(label_copy, label.value()->data(), label.value()->size());

if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, label_copy, native_label->size()) <= 0) {
if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, label_copy, label.value()->size()) <= 0) {
OPENSSL_free(label_copy);
EVP_PKEY_CTX_free(ctx);
throw std::runtime_error("Failed to set OAEP label");
}
}
}

auto native_data = ToNativeArrayBuffer(data);
const unsigned char* in = native_data->data();
size_t inlen = native_data->size();
const unsigned char* in = data->data();
size_t inlen = data->size();

size_t outlen;
if (EVP_PKEY_encrypt(ctx, nullptr, &outlen, in, inlen) <= 0) {
Expand Down Expand Up @@ -197,25 +195,23 @@ std::shared_ptr<ArrayBuffer> HybridRsaCipher::decrypt(const std::shared_ptr<Hybr
}

if (label.has_value() && label.value()->size() > 0) {
auto native_label = ToNativeArrayBuffer(label.value());
unsigned char* label_copy = (unsigned char*)OPENSSL_malloc(native_label->size());
unsigned char* label_copy = (unsigned char*)OPENSSL_malloc(label.value()->size());
if (!label_copy) {
EVP_PKEY_CTX_free(ctx);
throw std::runtime_error("Failed to allocate memory for label");
}
std::memcpy(label_copy, native_label->data(), native_label->size());
std::memcpy(label_copy, label.value()->data(), label.value()->size());

if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, label_copy, native_label->size()) <= 0) {
if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, label_copy, label.value()->size()) <= 0) {
OPENSSL_free(label_copy);
EVP_PKEY_CTX_free(ctx);
throw std::runtime_error("Failed to set OAEP label");
}
}
}

auto native_data = ToNativeArrayBuffer(data);
const unsigned char* in = native_data->data();
size_t inlen = native_data->size();
const unsigned char* in = data->data();
size_t inlen = data->size();

// Both decrypt calls below operate on attacker-controlled ciphertext, so
// any failure must be surfaced with an opaque, content-independent message.
Expand Down Expand Up @@ -269,9 +265,8 @@ std::shared_ptr<ArrayBuffer> HybridRsaCipher::publicDecrypt(const std::shared_pt
throw std::runtime_error("Failed to set RSA padding");
}

auto native_data = ToNativeArrayBuffer(data);
const unsigned char* in = native_data->data();
size_t inlen = native_data->size();
const unsigned char* in = data->data();
size_t inlen = data->size();

// verify_recover acts on attacker-controlled ciphertext too — surface only
// an opaque error so a remote caller cannot distinguish failure modes.
Expand Down Expand Up @@ -351,9 +346,8 @@ std::shared_ptr<ArrayBuffer> HybridRsaCipher::privateEncrypt(const std::shared_p
throw std::runtime_error("Failed to set RSA padding");
}

auto native_data = ToNativeArrayBuffer(data);
const unsigned char* in = native_data->data();
size_t inlen = native_data->size();
const unsigned char* in = data->data();
size_t inlen = data->size();

size_t outlen;
if (EVP_PKEY_sign(ctx, nullptr, &outlen, in, inlen) <= 0) {
Expand Down Expand Up @@ -430,25 +424,23 @@ std::shared_ptr<ArrayBuffer> HybridRsaCipher::privateDecrypt(const std::shared_p
}

if (label.has_value() && label.value()->size() > 0) {
auto native_label = ToNativeArrayBuffer(label.value());
unsigned char* label_copy = (unsigned char*)OPENSSL_malloc(native_label->size());
unsigned char* label_copy = (unsigned char*)OPENSSL_malloc(label.value()->size());
if (!label_copy) {
EVP_PKEY_CTX_free(ctx);
throw std::runtime_error("Failed to allocate memory for label");
}
std::memcpy(label_copy, native_label->data(), native_label->size());
std::memcpy(label_copy, label.value()->data(), label.value()->size());

if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, label_copy, native_label->size()) <= 0) {
if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, label_copy, label.value()->size()) <= 0) {
OPENSSL_free(label_copy);
EVP_PKEY_CTX_free(ctx);
throw std::runtime_error("Failed to set OAEP label");
}
}
}

auto native_data = ToNativeArrayBuffer(data);
const unsigned char* in = native_data->data();
size_t inlen = native_data->size();
const unsigned char* in = data->data();
size_t inlen = data->size();

// Both decrypt calls below operate on attacker-controlled ciphertext, so
// any failure must be surfaced with an opaque, content-independent message.
Expand Down
5 changes: 2 additions & 3 deletions packages/react-native-quick-crypto/cpp/cipher/OCBCipher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,11 @@ bool OCBCipher::setAuthTag(const std::shared_ptr<ArrayBuffer>& tag) {
if (is_cipher) {
throw std::runtime_error("setAuthTag can only be called during decryption.");
}
auto native_tag = ToNativeArrayBuffer(tag);
size_t tag_len = native_tag->size();
size_t tag_len = tag->size();
if (tag_len < 8 || tag_len > 16) {
throw std::runtime_error("Invalid OCB tag length");
}
if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_TAG, tag_len, native_tag->data()) != 1) {
if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_TAG, tag_len, tag->data()) != 1) {
throw std::runtime_error("Failed to set OCB auth tag");
}
auth_tag_len = tag_len;
Expand Down
Loading
Loading