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;
 }