diff --git a/native/expo-modules/aes-crypto/android/src/main/java/app/comm/android/aescrypto/AESCryptoModule.kt b/native/expo-modules/aes-crypto/android/src/main/java/app/comm/android/aescrypto/AESCryptoModule.kt --- a/native/expo-modules/aes-crypto/android/src/main/java/app/comm/android/aescrypto/AESCryptoModule.kt +++ b/native/expo-modules/aes-crypto/android/src/main/java/app/comm/android/aescrypto/AESCryptoModule.kt @@ -1,14 +1,49 @@ package app.comm.android.aescrypto +import expo.modules.core.errors.CodedException import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition +import expo.modules.kotlin.typedarray.Uint8Array +import java.security.SecureRandom +import javax.crypto.KeyGenerator + +private const val ALGORITHM_AES = "AES" +private const val KEY_SIZE = 32 // bytes class AESCryptoModule : Module() { + private val secureRandom by lazy { SecureRandom() } + override fun definition() = ModuleDefinition { Name("AESCrypto") - Function("hello") { - return@Function "Hello world! 👋" + Function("generateKey", this@AESCryptoModule::generateKey) + } + + // region Function implementations + + /** + * Generates AES-256 key and stores it in [destination] array + * + * @param destination JS typed array, must be [KEY_SIZE] bytes long + */ + private fun generateKey(destination: Uint8Array) { + if (destination.byteLength != KEY_SIZE) { + throw InvalidKeyLengthException() } + + val keygen = KeyGenerator.getInstance(ALGORITHM_AES).apply { + init(KEY_SIZE * 8, secureRandom) + } + val keyBytes = keygen.generateKey().encoded + destination.write(keyBytes, position = 0, size = keyBytes.size) } + + // endregion } + +// region Exception definitions + +private class InvalidKeyLengthException : + CodedException("The AES key has invalid length") + +// endregion diff --git a/native/expo-modules/aes-crypto/ios/AESCryptoModule.swift b/native/expo-modules/aes-crypto/ios/AESCryptoModule.swift --- a/native/expo-modules/aes-crypto/ios/AESCryptoModule.swift +++ b/native/expo-modules/aes-crypto/ios/AESCryptoModule.swift @@ -1,11 +1,41 @@ import ExpoModulesCore +import CryptoKit + +private let KEY_SIZE = 32 // bytes public class AESCryptoModule: Module { public func definition() -> ModuleDefinition { Name("AESCrypto") - Function("hello") { () -> String in - return "Hello world! 👋" - } + Function("generateKey", generateKey) + } +} + +// MARK: - Function implementations + +private func generateKey(destination: Uint8Array) throws { + guard destination.byteLength == KEY_SIZE else { + throw InvalidKeyLengthException() + } + let key = SymmetricKey(size: .bits256) + key.withUnsafeBytes { bytes in + let _ = bytes.copyBytes(to: destination.rawBufferPtr()) + } +} + +// MARK: - Utilities + +extension TypedArray { + func rawBufferPtr() -> UnsafeMutableRawBufferPointer { + UnsafeMutableRawBufferPointer(start: self.rawPointer, + count: self.byteLength) + } +} + +// MARK: - Exception definitions + +private class InvalidKeyLengthException: Exception { + override var reason: String { + "The AES key has invalid length" } } diff --git a/native/utils/aes-crypto-module.js b/native/utils/aes-crypto-module.js --- a/native/utils/aes-crypto-module.js +++ b/native/utils/aes-crypto-module.js @@ -2,10 +2,14 @@ import { requireNativeModule } from 'expo-modules-core'; +const KEY_SIZE = 32; // bytes + const AESCryptoModule: { - +hello: () => string, + +generateKey: (destination: Uint8Array) => void, } = requireNativeModule('AESCrypto'); -export function hello(): string { - return AESCryptoModule.hello(); +export function generateKey(): Uint8Array { + const keyBuffer = new Uint8Array(KEY_SIZE); + AESCryptoModule.generateKey(keyBuffer); + return keyBuffer; }