diff --git a/native/android/app/src/cpp/AESCrypto.cpp b/native/android/app/src/cpp/AESCrypto.cpp --- a/native/android/app/src/cpp/AESCrypto.cpp +++ b/native/android/app/src/cpp/AESCrypto.cpp @@ -1,8 +1,68 @@ +#include "jniHelpers.h" #include +#include +#include + +using namespace facebook::jni; + +class AESCryptoJavaClass : public JavaClass { +public: + // app.comm.android.aescrypto.AESCryptoModuleCompat + static auto constexpr kJavaDescriptor = + "Lapp/comm/android/aescrypto/AESCryptoModuleCompat;"; + + static void generateKey(rust::Slice buffer) { + local_ref byteBuffer = + JByteBuffer::wrapBytes(buffer.data(), buffer.size()); + + static const auto cls = javaClassStatic(); + static auto method = + cls->getStaticMethod)>("generateKey"); + method(cls, byteBuffer); + } + + static void encrypt( + rust::Slice key, + rust::Slice plaintext, + rust::Slice sealedData) { + local_ref keyBuffer = + JByteBuffer::wrapBytes(key.data(), key.size()); + local_ref plaintextBuffer = + JByteBuffer::wrapBytes(plaintext.data(), plaintext.size()); + local_ref sealedDataBuffer = + JByteBuffer::wrapBytes(sealedData.data(), sealedData.size()); + static const auto cls = javaClassStatic(); + static auto method = cls->getStaticMethod, + local_ref, + local_ref)>("encrypt"); + method(cls, keyBuffer, plaintextBuffer, sealedDataBuffer); + } + + static void decrypt( + rust::Slice key, + rust::Slice sealedData, + rust::Slice plaintext) { + local_ref keyBuffer = + JByteBuffer::wrapBytes(key.data(), key.size()); + local_ref sealedDataBuffer = + JByteBuffer::wrapBytes(sealedData.data(), sealedData.size()); + local_ref plaintextBuffer = + JByteBuffer::wrapBytes(plaintext.data(), plaintext.size()); + static const auto cls = javaClassStatic(); + static auto method = cls->getStaticMethod, + local_ref, + local_ref)>("decrypt"); + method(cls, keyBuffer, sealedDataBuffer, plaintextBuffer); + } +}; namespace comm { std::string AESCrypto::generateKey(rust::Slice buffer) { + NativeAndroidAccessProvider::runTask( + [&]() { AESCryptoJavaClass::generateKey(buffer); }); return std::string(); } @@ -10,6 +70,8 @@ rust::Slice key, rust::Slice plaintext, rust::Slice sealedData) { + NativeAndroidAccessProvider::runTask( + [&]() { AESCryptoJavaClass::encrypt(key, plaintext, sealedData); }); return std::string(); } @@ -17,6 +79,8 @@ rust::Slice key, rust::Slice sealedData, rust::Slice plaintext) { + NativeAndroidAccessProvider::runTask( + [&]() { AESCryptoJavaClass::decrypt(key, sealedData, plaintext); }); return std::string(); } diff --git a/native/expo-modules/comm-expo-package/android/src/main/java/app/comm/android/aescrypto/AESCryptoModule.kt b/native/expo-modules/comm-expo-package/android/src/main/java/app/comm/android/aescrypto/AESCryptoModule.kt --- a/native/expo-modules/comm-expo-package/android/src/main/java/app/comm/android/aescrypto/AESCryptoModule.kt +++ b/native/expo-modules/comm-expo-package/android/src/main/java/app/comm/android/aescrypto/AESCryptoModule.kt @@ -98,10 +98,8 @@ // region RN-agnostic implementations -// Compatibility module to be called from native Java +// Compatibility module to be called from native Java and C++ class AESCryptoModuleCompat { - private val secureRandom by lazy { SecureRandom() } - public fun generateKey(): ByteArray { return KeyGenerator.getInstance(ALGORITHM_AES).apply { init(KEY_SIZE * 8, secureRandom) @@ -130,6 +128,40 @@ val plaintext = decryptAES(sealedDataBuffer, secretKey) return ByteArray(plaintext.remaining()).also(plaintext::get) } + + companion object { + private val secureRandom by lazy { SecureRandom() } + @JvmStatic + fun generateKey( + buffer: ByteBuffer, + ) { + val key = KeyGenerator.getInstance(ALGORITHM_AES).apply { + init(KEY_SIZE * 8, secureRandom) + }.generateKey().encoded + + buffer.put(key) + } + + @JvmStatic + fun encrypt( + rawKey: ByteBuffer, + plaintext: ByteBuffer, + sealedData: ByteBuffer, + ) { + val secretKey = rawKey.toAESSecretKey() + encryptAES(plaintext, secretKey, sealedData) + } + + @JvmStatic + fun decrypt( + rawKey: ByteBuffer, + sealedData: ByteBuffer, + plaintext: ByteBuffer, + ) { + val secretKey = rawKey.toAESSecretKey() + decryptAES(sealedData, secretKey, plaintext) + } + } } /** @@ -204,6 +236,13 @@ .toSecretKey() } +fun ByteBuffer.toAESSecretKey(): SecretKey{ + if(this.remaining() != KEY_SIZE) { + throw InvalidKeyLengthException() + } + return ByteArray(this.remaining()).also(this::get).toSecretKey(); +} + // endregion // region Exception definitions