-
Notifications
You must be signed in to change notification settings - Fork 109
Description
What feature or enhancement are you suggesting?
This repo looks great. Thanks for all the work!
I'm currently on a mission to implement our encryption functions that work in the react webapp and NodeJS in our react native app. These functions use the node:crypto and Web Crypto API.
We use the following functions from the crypto.subtle.
| Operation | Algorithm | Supported |
|---|---|---|
crypto.subtle.importKey("raw", "AES-GCM") |
AES-GCM | ✅ |
crypto.subtle.generateKey("AES-GCM") |
AES-GCM | ❌ |
crypto.subtle.exportKey("raw") |
AES-GCM | ✅ |
crypto.subtle.encrypt("AES-GCM") |
AES-GCM | ✅ |
crypto.subtle.decrypt("AES-GCM") |
AES-GCM | ✅ |
crypto.subtle.generateKey("RSA-OAEP") |
RSA-OAEP | ✅ |
crypto.subtle.importKey("spki", "RSA-OAEP") |
RSA-OAEP | ✅ |
crypto.subtle.importKey("pkcs8", "RSA-OAEP") |
RSA-OAEP | ❌ |
crypto.subtle.encrypt("RSA-OAEP") |
RSA-OAEP | ✅ |
crypto.subtle.decrypt("RSA-OAEP") |
RSA-OAEP | ✅ |
crypto.subtle.exportKey("spki", publicKey) |
RSA-OAEP | ✅ |
crypto.subtle.exportKey("pkcs8", privateKey) |
RSA-OAEP | ✅ |
crypto.subtle.importKey("raw", "PBKDF2") |
PBKDF2 | ✅ |
crypto.subtle.deriveBits("PBKDF2") |
PBKDF2 | ✅ |
The only two function that we are missing are:
cryptoKey = await crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 256,
},
true,
["encrypt", "decrypt"],
)await crypto.subtle.importKey(
"pkcs8",
arrayBuffer,
{ name: "RSA-OAEP", hash: "SHA-256" },
true,
["decrypt"],
)What Platforms whould this feature/enhancement affect?
iOS, Android
Alternatives/Workarounds
The current workaround is using the react-native-webview-crypto which works. This brings window.crypto.subtle to your React Native application. It does this by communicating with a hidden WebView, which performs the actual computation.
However, this does not seem great.
Additional information
- I agree to follow this project's Code of Conduct
- I searched for similar feature requests in this repository and found none.
I saw this comment. I can try to setup a unit test that uses the two missing functions.
Unit test: `importKey("RSA-OAEP")`
import { decode, encode } from "js-base64"
const arrayBufferToBase64 = (buffer: ArrayBuffer) => {
const bytes = new Uint8Array(buffer)
let binary = ""
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i])
}
return btoa(binary)
}
const base64ToUint8Array = (base64: string) => {
const binaryString = atob(base64)
const bytes = new Uint8Array(binaryString.length)
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i)
}
return bytes
}
export const testImportKeyRSAOAEP = async () => {
const publicKeyBase64 = `MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Yxs1i4Z5HzsewxTAAhQ8hMrmlzMvQ4EL1+grkjWvhGJAFjxK9OapVQJ7yOhovt4HB8x6EY0na2JF9X/z82HcNVaO5twL1y50783WIHHoO5Z5VgK6LLF/HdhlqXqSO9LuqpSmlobv+YSLM09phpOmZ2y4IBiD08AROIW0qAmOjXZjfyqxc9I2ZQRB89Ek5VBnaimnFNto06FMei2rPLplD0Ez05Xrib44LlS4ofmbAkQsjplnNqMZHj1kaErzHqxBAWZyna9J8V3evUOwlUvSJUsyFfR869UQtgSqDhW6m/IDBQIT1PLov9nLExVkF5CJzuty4gIbW9eqxHeO7fGiwIDAQAB`
const publicKeyUint8array = base64ToUint8Array(publicKeyBase64)
const publicKeyArrayBuffer = publicKeyUint8array.buffer
const encryptorKey = await crypto.subtle.importKey(
"spki",
publicKeyArrayBuffer,
{ name: "RSA-OAEP", hash: "SHA-256" },
true,
["encrypt"],
)
const data = "Hello World!"
const base64Data = encode(data) // utf8ToBase64(data)
const dataUint8array = base64ToUint8Array(base64Data)
const dataArrayBuffer = dataUint8array.buffer
const encryptedDataRSA = await crypto.subtle.encrypt(
{ name: "RSA-OAEP" },
encryptorKey,
dataArrayBuffer,
)
const privateKeyBase64 = `MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDRjGzWLhnkfOx7DFMACFDyEyuaXMy9DgQvX6CuSNa+EYkAWPEr05qlVAnvI6Gi+3gcHzHoRjSdrYkX1f/PzYdw1Vo7m3AvXLnTvzdYgceg7lnlWArossX8d2GWpepI70u6qlKaWhu/5hIszT2mGk6ZnbLggGIPTwBE4hbSoCY6NdmN/KrFz0jZlBEHz0STlUGdqKacU22jToUx6Las8umUPQTPTleuJvjguVLih+ZsCRCyOmWc2oxkePWRoSvMerEEBZnKdr0nxXd69Q7CVS9IlSzIV9Hzr1RC2BKoOFbqb8gMFAhPU8ui/2csTFWQXkInO63LiAhtb16rEd47t8aLAgMBAAECggEAITsMs3aCIqrw8Z6Ftxaah5kkrAkVatHDNiQLHjhs3Z14RXbVYCbhemB2ZtcWtfr9FDCaQISJqYuwlvgX5kNovCsJcTR4OPqSeZL0WvPRzaKe3PD2Yeqf3Satci+DlOdl8gc6rEGn7um0bihqI2I+nrvUdyfE5TqZB1N3XRWKmmZQS72ZJzpK1iZgzqyFBdp4UuhFaN/V+LZXCnIaglir4Td0VIOl71vDZkRPdtmy8jupYxbCk/B3DEKWDSadZgJvWBtTNJp38K8g6C2FbEmXwYqMP6fUp2C3Dec9+rSL/eFxV24lmnHjVyZtbr+JvjgZJWkq1GbVOszbms3Kl8uyEQKBgQDtLDS5R312BCVo7Sha2NN3vvsF89ErtdjDpASb9psKyVBkU32MBPOCZRXYa+WHvATNORHuYDU0V/nVrfZ/UsGlEf2c7osRGBTB32//XcszB6OqDBJlMV2zstDL6XatjiMJ8mUNR7LlzxfC/BpyvQ2Zotnm7c0ahVCiKrMbVvKsiQKBgQDiLthAl0kT4Dc8XNR9VvX0ZmK9FHb0i4pOIlgligzvLPnXPcSqYgv1iK0RG8mdFP4jupOyaQgYAzHYr06vOAiTFnIFSPMfSS35F27A3ukDsHHffpDwVn7j3dVf+DW8HpxSrvc//Baxb0OtOU816kBXyR89bi4qrsygxjEoYzLdcwKBgBysMnePyAAjgi5MNYu+GNqqMQjIMCp7oogMZS5Bwv6r1dc7LLtnwdSqydhPOwGM3nu9AYjzApugYyjNDjbYV2bQZPu67v8TDTde/tg9i5pQux2MthCbxjs6S/nK8LkMrPm/3y2a1Grp/XJqLfxfFKzVPkinyRsCsPvZ86tDeLUZAoGBAJoJ3zs2DQ3dQKD6c7ic9cqpxAsTmeP3+Iw39aIzP5XQIqMFLSAAwDZLC9q/+vHg7ye0FIyH3XxFCLiSw9qvJZ/OxH527STceNPQspvl8/mQPC1CjEEyFx7m4D+I0ke47Suef0LzUx0qMoQRqLGGRKXEkmMK26Q0AaZo8+eWj3ijAoGAcUm27e9/TzyUmegV/4L/oMxnbLKoaedvOVEFm2fa0VuwfzNPNXtol2vZoTejJqnpEDEaEmRxN3mabapmmltVrfZes4KfGYDvjK9phSPnT/0LJNpbgu6su+SX/AB42o5pC6ckoh4fpFr8RsnU0qWvFcHtzko2l3rcfs5j7il3djU=`
const uint8array = base64ToUint8Array(privateKeyBase64)
const arrayBuffer = uint8array.buffer
const key = await crypto.subtle.importKey(
"pkcs8",
arrayBuffer,
{ name: "RSA-OAEP", hash: "SHA-256" },
true,
["decrypt"],
)
// expect(key).toBeDefined()
// expect(key.type).toBe("private")
// expect(key.algorithm.name).toBe("RSA-OAEP")
// expect(key.usages).toContain("decrypt")
const decryptedDataRSA = await crypto.subtle.decrypt({ name: "RSA-OAEP" }, key, encryptedDataRSA)
// decode() is base64ToUtf8()
const decryptedDataUTF8 = decode(arrayBufferToBase64(decryptedDataRSA))
console.log(decryptedDataUTF8)
// expect(decryptedDataUTF8).toBe(data)
}Unit test: `generateKey("AES-GCM")`
import { decode, encode } from "js-base64"
const arrayBufferToBase64 = (buffer: ArrayBuffer) => {
const bytes = new Uint8Array(buffer)
let binary = ""
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i])
}
return btoa(binary)
}
export const testGenerateKeyAESGCM = async () => {
const cryptoKey = await crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 256,
},
true,
["encrypt", "decrypt"],
)
expect(cryptoKey).toBeDefined()
expect(cryptoKey.type).toBe("secret")
expect(cryptoKey.algorithm.name).toBe("AES-GCM")
const aesKeyBuffer = await crypto.subtle.exportKey("raw", cryptoKey)
expect(aesKeyBuffer).toBeInstanceOf(ArrayBuffer)
expect(aesKeyBuffer.byteLength).toBe(32) // 256 bits = 32 bytes
const aesKeyBase64 = arrayBufferToBase64(aesKeyBuffer)
expect(typeof aesKeyBase64).toBe("string")
expect(aesKeyBase64.length).toBeGreaterThan(0)
}However, regarding the actual implementation of the missing functions generateKey("AES-GCM") and importKey("pkcs8"+RSA-OAEP) I wouldn't know where to start.
Let me know how I could help getting these functions supported by react-native-quick-crypto.