1import * as Crypto from 'expo-crypto'; 2import { Platform } from 'react-native'; 3 4function areArrayBuffersEqual(a, b) { 5 if (a.byteLength !== b.byteLength) { 6 return false; 7 } 8 const dv1 = new Int8Array(a); 9 const dv2 = new Int8Array(b); 10 return dv1.every((item, index) => item === dv2[index]); 11} 12 13function getArrayBufferFromHex(hex) { 14 const bytes = new Uint8Array(Math.ceil(hex.length / 2)); 15 return bytes.map((_, index) => parseInt(hex.substr(index * 2, 2), 16)).buffer; 16} 17 18const { CryptoEncoding, CryptoDigestAlgorithm } = Crypto; 19 20const testValue = 'Expo'; 21const testTypedArray = new Uint8Array([69, 120, 112, 111]); 22 23const valueMapping = { 24 [CryptoEncoding.HEX]: { 25 [CryptoDigestAlgorithm.SHA1]: `c275355dc46ac171633935033d113e3872d595e5`, 26 [CryptoDigestAlgorithm.SHA256]: `f5e5cae536b49d394e1e72d4368d64b00a23298ec5ae11f3a9102a540e2532dc`, 27 [CryptoDigestAlgorithm.SHA384]: `aa356c88afdbd8a7a5a9c3133541af0035e4b04e82438a223aa1939240ccb6b3ca28294b5ac0f42703b15183f4c016fc`, 28 [CryptoDigestAlgorithm.SHA512]: `f1924f3e61aac4e4caa7fd566591b8abb541b0e642cd7bf0c71573267cfacf1b6dafe905bd6e42633bfba67c59774e070095e19a7c2078ac18ccd23245d76f1c`, 29 [CryptoDigestAlgorithm.MD2]: `fb85323c3b15b016e0351006e2f47bf7`, 30 [CryptoDigestAlgorithm.MD4]: `2d36099794ec182cbb36d02e1188fc1e`, 31 [CryptoDigestAlgorithm.MD5]: `c29f23f279126757ba18ec74d0d27cfa`, 32 }, 33 [CryptoEncoding.BASE64]: { 34 [CryptoDigestAlgorithm.SHA1]: `wnU1XcRqwXFjOTUDPRE+OHLVleU=`, 35 [CryptoDigestAlgorithm.SHA256]: `9eXK5Ta0nTlOHnLUNo1ksAojKY7FrhHzqRAqVA4lMtw=`, 36 [CryptoDigestAlgorithm.SHA384]: `qjVsiK/b2KelqcMTNUGvADXksE6CQ4oiOqGTkkDMtrPKKClLWsD0JwOxUYP0wBb8`, 37 [CryptoDigestAlgorithm.SHA512]: `8ZJPPmGqxOTKp/1WZZG4q7VBsOZCzXvwxxVzJnz6zxttr+kFvW5CYzv7pnxZd04HAJXhmnwgeKwYzNIyRddvHA==`, 38 [CryptoDigestAlgorithm.MD2]: `+4UyPDsVsBbgNRAG4vR79w==`, 39 [CryptoDigestAlgorithm.MD4]: `LTYJl5TsGCy7NtAuEYj8Hg==`, 40 [CryptoDigestAlgorithm.MD5]: `wp8j8nkSZ1e6GOx00NJ8+g==`, 41 }, 42}; 43 44export const name = 'Crypto'; 45 46const UNSUPPORTED = Platform.select({ 47 web: ['MD2', 'MD4', 'MD5'], 48 android: ['MD2', 'MD4'], 49 default: [], 50}); 51function supportedAlgorithm(algorithm) { 52 return !UNSUPPORTED.includes(algorithm); 53} 54 55export async function test({ describe, it, expect }) { 56 describe('Crypto', () => { 57 describe('digestStringAsync()', async () => { 58 it(`Invalid CryptoEncoding throws an error`, async () => { 59 let error = null; 60 try { 61 await Crypto.digestStringAsync(CryptoDigestAlgorithm.SHA1, testValue, { 62 encoding: 'INVALID', 63 }); 64 } catch (e) { 65 error = e; 66 } 67 expect(error).not.toBeNull(); 68 }); 69 70 for (const encodingEntry of Object.entries(CryptoEncoding)) { 71 const [encodingKey, encoding] = encodingEntry; 72 describe(`Encoded with CryptoEncoding.${encodingKey}`, () => { 73 for (const entry of Object.entries(CryptoDigestAlgorithm)) { 74 const [key, algorithm] = entry; 75 it(`CryptoDigestAlgorithm.${key}`, async () => { 76 const targetValue = valueMapping[encoding][algorithm]; 77 if (supportedAlgorithm(algorithm)) { 78 const value = await Crypto.digestStringAsync(algorithm, testValue, { encoding }); 79 expect(value).toBe(targetValue); 80 } else { 81 let error = null; 82 try { 83 await Crypto.digestStringAsync(algorithm, testValue, { encoding }); 84 } catch (e) { 85 error = e; 86 } 87 expect(error).not.toBeNull(); 88 } 89 }); 90 } 91 }); 92 } 93 }); 94 95 describe('digest()', async () => { 96 for (const entry of Object.entries(CryptoDigestAlgorithm)) { 97 const [key, algorithm] = entry; 98 it(`CryptoDigestAlgorithm.${key}`, async () => { 99 const hex = valueMapping[CryptoEncoding.HEX][algorithm]; 100 const targetValue = getArrayBufferFromHex(hex); 101 if (supportedAlgorithm(algorithm)) { 102 const value = await Crypto.digest(algorithm, testTypedArray); 103 const buffersAreEqual = areArrayBuffersEqual(value, targetValue); 104 expect(buffersAreEqual).toBe(true); 105 } 106 }); 107 } 108 }); 109 }); 110} 111