|
1 |
| -let AES: any; |
2 |
| -let ENC: any; |
3 |
| - |
4 |
| -if (process.env.PARSE_BUILD === 'react-native') { |
5 |
| - const CryptoJS = require('react-native-crypto-js'); |
6 |
| - AES = CryptoJS.AES; |
7 |
| - ENC = CryptoJS.enc.Utf8; |
| 1 | +let webcrypto; |
| 2 | +let encoder; |
| 3 | +let decoder; |
| 4 | +if (typeof window !== 'undefined' && window.crypto && process.env.PARSE_BUILD !== 'node') { |
| 5 | + webcrypto = window.crypto; |
| 6 | + encoder = new TextEncoder(); |
| 7 | + decoder = new TextDecoder(); |
8 | 8 | } else {
|
9 |
| - AES = require('crypto-js/aes'); |
10 |
| - ENC = require('crypto-js/enc-utf8'); |
| 9 | + const { TextEncoder, TextDecoder } = require('util'); |
| 10 | + webcrypto = require('crypto').webcrypto; |
| 11 | + encoder = new TextEncoder(); |
| 12 | + decoder = new TextDecoder(); |
11 | 13 | }
|
12 | 14 |
|
| 15 | +const bufferToBase64 = buff => |
| 16 | + btoa(new Uint8Array(buff).reduce((data, byte) => data + String.fromCharCode(byte), '')); |
| 17 | + |
| 18 | +const base64ToBuffer = b64 => Uint8Array.from(atob(b64), c => c.charCodeAt(null)); |
| 19 | + |
| 20 | +const importKey = async key => |
| 21 | + webcrypto.subtle.importKey('raw', encoder.encode(key), 'PBKDF2', false, ['deriveKey']); |
| 22 | + |
| 23 | +const deriveKey = (key, salt, keyUsage) => |
| 24 | + webcrypto.subtle.deriveKey( |
| 25 | + { |
| 26 | + salt, |
| 27 | + name: 'PBKDF2', |
| 28 | + iterations: 250000, |
| 29 | + hash: 'SHA-256', |
| 30 | + }, |
| 31 | + key, |
| 32 | + { name: 'AES-GCM', length: 256 }, |
| 33 | + false, |
| 34 | + keyUsage |
| 35 | + ); |
| 36 | + |
13 | 37 | const CryptoController = {
|
14 |
| - encrypt(obj: any, secretKey: string): string { |
15 |
| - const encrypted = AES.encrypt(JSON.stringify(obj), secretKey); |
16 |
| - return encrypted.toString(); |
| 38 | + async: 1, |
| 39 | + async encrypt(json: any, parseSecret: any): Promise<string> { |
| 40 | + const salt = webcrypto.getRandomValues(new Uint8Array(16)); |
| 41 | + const iv = webcrypto.getRandomValues(new Uint8Array(12)); |
| 42 | + const key = await importKey(parseSecret); |
| 43 | + const aesKey = await deriveKey(key, salt, ['encrypt']); |
| 44 | + const encodedData = encoder.encode(JSON.stringify(json)); |
| 45 | + const encrypted = await webcrypto.subtle.encrypt({ name: 'AES-GCM', iv }, aesKey, encodedData); |
| 46 | + const encryptedArray = new Uint8Array(encrypted); |
| 47 | + const buffer = new Uint8Array(salt.byteLength + iv.byteLength + encryptedArray.byteLength); |
| 48 | + buffer.set(salt, 0); |
| 49 | + buffer.set(iv, salt.byteLength); |
| 50 | + buffer.set(encryptedArray, salt.byteLength + iv.byteLength); |
| 51 | + const base64Buffer = bufferToBase64(buffer); |
| 52 | + return base64Buffer; |
17 | 53 | },
|
18 | 54 |
|
19 |
| - decrypt(encryptedText: string, secretKey: string): string { |
20 |
| - const decryptedStr = AES.decrypt(encryptedText, secretKey).toString(ENC); |
21 |
| - return decryptedStr; |
| 55 | + async decrypt(encryptedJSON: string, parseSecret: any): Promise<string> { |
| 56 | + const buffer = base64ToBuffer(encryptedJSON); |
| 57 | + const salt = buffer.slice(0, 16); |
| 58 | + const iv = buffer.slice(16, 16 + 12); |
| 59 | + const data = buffer.slice(16 + 12); |
| 60 | + const key = await importKey(parseSecret); |
| 61 | + const aesKey = await deriveKey(key, salt, ['decrypt']); |
| 62 | + const decrypted = await webcrypto.subtle.decrypt({ name: 'AES-GCM', iv }, aesKey, data); |
| 63 | + return decoder.decode(decrypted); |
22 | 64 | },
|
23 | 65 | };
|
24 | 66 |
|
|
0 commit comments