mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-03-22 00:23:53 -04:00
Implement react-native AES-GCM-256 decryption (#771)
This commit is contained in:
@@ -53,11 +53,14 @@ export const DbProvider: React.FC<{ children: React.ReactNode }> = ({ children }
|
||||
|
||||
const initializeDatabase = useCallback(async (vaultResponse: VaultResponse, derivedKey: string) => {
|
||||
// Attempt to decrypt the blob.
|
||||
console.log('attempt to decrypt vault');
|
||||
const decryptedBlob = await EncryptionUtility.symmetricDecrypt(
|
||||
vaultResponse.vault.blob,
|
||||
derivedKey
|
||||
);
|
||||
|
||||
console.log('decrypted blob', decryptedBlob);
|
||||
|
||||
// Initialize the SQLite client.
|
||||
const client = new SqliteClient();
|
||||
await client.initializeFromBase64(decryptedBlob);
|
||||
|
||||
@@ -1553,6 +1553,8 @@ PODS:
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- react-native-aes-gcm-crypto (0.2.2):
|
||||
- React-Core
|
||||
- react-native-get-random-values (1.11.0):
|
||||
- React-Core
|
||||
- react-native-safe-area-context (4.12.0):
|
||||
@@ -2157,6 +2159,7 @@ DEPENDENCIES:
|
||||
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
|
||||
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
|
||||
- React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)
|
||||
- react-native-aes-gcm-crypto (from `../node_modules/react-native-aes-gcm-crypto`)
|
||||
- react-native-get-random-values (from `../node_modules/react-native-get-random-values`)
|
||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
||||
- react-native-webview (from `../node_modules/react-native-webview`)
|
||||
@@ -2320,6 +2323,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native/ReactCommon"
|
||||
React-microtasksnativemodule:
|
||||
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks"
|
||||
react-native-aes-gcm-crypto:
|
||||
:path: "../node_modules/react-native-aes-gcm-crypto"
|
||||
react-native-get-random-values:
|
||||
:path: "../node_modules/react-native-get-random-values"
|
||||
react-native-safe-area-context:
|
||||
@@ -2455,6 +2460,7 @@ SPEC CHECKSUMS:
|
||||
React-logger: c4052eb941cca9a097ef01b59543a656dc088559
|
||||
React-Mapbuffer: 33546a3ebefbccb8770c33a1f8a5554fa96a54de
|
||||
React-microtasksnativemodule: d80ff86c8902872d397d9622f1a97aadcc12cead
|
||||
react-native-aes-gcm-crypto: d572dd7a69f31c539bb8309b3a829bfa3bfad244
|
||||
react-native-get-random-values: d16467cf726c618e9c7a8c3c39c31faa2244bbba
|
||||
react-native-safe-area-context: cd916088cac5300c3266876218377518987b995e
|
||||
react-native-webview: 6b9fc65c1951203a3e958ff3cc0a858d4b6be901
|
||||
|
||||
11
mobile-app/package-lock.json
generated
11
mobile-app/package-lock.json
generated
@@ -29,6 +29,7 @@
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-native": "0.76.9",
|
||||
"react-native-aes-gcm-crypto": "^0.2.2",
|
||||
"react-native-argon2": "^2.0.1",
|
||||
"react-native-gesture-handler": "~2.20.2",
|
||||
"react-native-get-random-values": "^1.11.0",
|
||||
@@ -11883,6 +11884,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-aes-gcm-crypto": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-aes-gcm-crypto/-/react-native-aes-gcm-crypto-0.2.2.tgz",
|
||||
"integrity": "sha512-vUwkh2zBiIQMRY191IfZhDmhHVT+nV4sxet/A0V8J35lVShCA4kuFzBL+QVB06RM2EF8oZSnNMt/uvFkKAx6QQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-argon2": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-argon2/-/react-native-argon2-2.0.1.tgz",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-native": "0.76.9",
|
||||
"react-native-aes-gcm-crypto": "^0.2.2",
|
||||
"react-native-argon2": "^2.0.1",
|
||||
"react-native-gesture-handler": "~2.20.2",
|
||||
"react-native-get-random-values": "^1.11.0",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import argon2 from 'react-native-argon2';
|
||||
import AesGcmCrypto from 'react-native-aes-gcm-crypto';
|
||||
import { Email } from './types/webapi/Email';
|
||||
import { EncryptionKey } from './types/EncryptionKey';
|
||||
import { MailboxEmail } from './types/webapi/MailboxEmail';
|
||||
@@ -61,36 +62,18 @@ class EncryptionUtility {
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
const key = await crypto.subtle.importKey(
|
||||
"raw",
|
||||
Uint8Array.from(atob(base64Key), c => c.charCodeAt(0)),
|
||||
{
|
||||
name: "AES-GCM",
|
||||
length: 256,
|
||||
},
|
||||
false,
|
||||
["encrypt"]
|
||||
);
|
||||
|
||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
||||
const encoder = new TextEncoder();
|
||||
const encoded = encoder.encode(plaintext);
|
||||
|
||||
const ciphertext = await crypto.subtle.encrypt(
|
||||
{ name: "AES-GCM", iv: iv },
|
||||
key,
|
||||
encoded
|
||||
);
|
||||
|
||||
const combined = new Uint8Array(iv.length + ciphertext.byteLength);
|
||||
combined.set(iv, 0);
|
||||
combined.set(new Uint8Array(ciphertext), iv.length);
|
||||
|
||||
return btoa(
|
||||
Array.from(combined)
|
||||
.map(byte => String.fromCharCode(byte))
|
||||
.join('')
|
||||
);
|
||||
try {
|
||||
const result = await AesGcmCrypto.encrypt(plaintext, false, base64Key);
|
||||
// Combine IV, tag, and content into a single string for storage
|
||||
return JSON.stringify({
|
||||
iv: result.iv,
|
||||
tag: result.tag,
|
||||
content: result.content
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('AES-GCM encryption failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,29 +84,28 @@ class EncryptionUtility {
|
||||
return base64Ciphertext;
|
||||
}
|
||||
|
||||
const key = await crypto.subtle.importKey(
|
||||
"raw",
|
||||
Uint8Array.from(atob(base64Key), c => c.charCodeAt(0)),
|
||||
{
|
||||
name: "AES-GCM",
|
||||
length: 256,
|
||||
},
|
||||
false,
|
||||
["decrypt"]
|
||||
);
|
||||
try {
|
||||
const ciphertext = Uint8Array.from(atob(base64Ciphertext), c => c.charCodeAt(0));
|
||||
const iv = ciphertext.slice(0, 12);
|
||||
const tag = ciphertext.slice(-16);
|
||||
const content = ciphertext.slice(12, -16);
|
||||
|
||||
const ivAndCiphertext = Uint8Array.from(atob(base64Ciphertext), c => c.charCodeAt(0));
|
||||
const iv = ivAndCiphertext.slice(0, 12);
|
||||
const ciphertext = ivAndCiphertext.slice(12);
|
||||
const contentBase64 = Buffer.from(content).toString('base64');
|
||||
const ivHex = Buffer.from(iv).toString('hex');
|
||||
const tagHex = Buffer.from(tag).toString('hex');
|
||||
|
||||
const decrypted = await crypto.subtle.decrypt(
|
||||
{ name: "AES-GCM", iv: iv },
|
||||
key,
|
||||
ciphertext
|
||||
);
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
return decoder.decode(decrypted);
|
||||
const decryptedData = await AesGcmCrypto.decrypt(
|
||||
contentBase64,
|
||||
base64Key,
|
||||
ivHex,
|
||||
tagHex,
|
||||
false
|
||||
);
|
||||
return decryptedData;
|
||||
} catch (error) {
|
||||
console.error('AES-GCM decryption failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user