UNPKG

7.27 kBJavaScriptView Raw
1let generateSecureRandom;
2if (require.getModules) {
3 const NativeModules = require('react-native').NativeModules;
4 const RNSecureRandom = NativeModules.RNSecureRandom;
5 const NativeUnimoduleProxy = NativeModules.NativeUnimoduleProxy;
6 if (RNSecureRandom && RNSecureRandom.generateSecureRandomAsBase64) {
7 generateSecureRandom = require('react-native-securerandom').generateSecureRandom;
8 } else if (NativeUnimoduleProxy && NativeUnimoduleProxy.exportedMethods.ExpoRandom) {
9 generateSecureRandom = require('expo-random').getRandomBytesAsync;
10 }
11}
12
13if (!generateSecureRandom) {
14 console.log(`
15 isomorphic-webcrypto cannot ensure the security of some operations!
16 Install and configure react-native-securerandom or expo-random
17 If managed by Expo, run 'expo install expo-random'
18 `);
19 generateSecureRandom = function(length) {
20 const uint8Array = new Uint8Array(length);
21 while (length && length--) {
22 uint8Array[length] = Math.floor(Math.random() * 256);
23 }
24 return Promise.resolve(uint8Array);
25 }
26}
27
28const str2buf = require('str2buf');
29const b64u = require('b64u-lite');
30const b64 = require('b64-lite');
31
32if(global.window.navigator === undefined)
33 global.window.navigator = {};
34
35global.window.navigator.userAgent = '';
36global.atob = typeof atob === 'undefined' ? b64.atob : atob;
37global.btoa = typeof btoa === 'undefined' ? b64.btoa : btoa;
38global.msrCryptoPermanentForceSync = true;
39
40const crypto = require('msrcrypto');
41
42let isSecured = false;
43const secured = new Promise((resolve, reject) => {
44 if (!crypto.initPrng) return resolve(false);
45 return generateSecureRandom(48)
46 .then(byteArray => {
47 crypto.initPrng(Array.from(byteArray))
48 isSecured = true;
49 resolve(true);
50 })
51 .catch(err => reject(err));
52 })
53 .then(() => {
54 if (!global.window.crypto) {
55 global.window.crypto = crypto;
56 }
57
58 global.asmCrypto = require('asmcrypto.js');
59 const liner = require('./webcrypto-liner');
60
61 const originalImportKey = crypto.subtle.importKey;
62 crypto.subtle.importKey = function importKey() {
63 const importType = arguments[0];
64 const key = arguments[1];
65 const algorithm = arguments[2];
66 if (algorithm.name.toUpperCase() === 'PBKDF2') {
67 let importKey, ref;
68 if (liner.crypto.subtle.getProvider) {
69 ref = liner.crypto.subtle.getProvider('PBKDF2');
70 importKey = ref.onImportKey;
71 } else {
72 ref = liner.crypto.subtle;
73 importKey = ref.importKey;
74 }
75 if (importType === 'raw') arguments[1] = new ArrayBuffer(arguments[1]);
76 return importKey.apply(ref, arguments);
77 }
78
79 return originalImportKey.apply(this, arguments)
80 .then(res => {
81 res.algorithm.name = standardizeAlgoName(res.algorithm.name);
82 switch(res.type) {
83 case 'secret':
84 res.usages = res.algorithm.name === 'HMAC' ? ['sign', 'verify'] : ['encrypt', 'decrypt'];
85 break;
86 case 'private':
87 res.usages = ['sign'];
88 break;
89 case 'public':
90 res.usages = ['verify'];
91 break;
92 }
93 if (importType === 'jwk' && key.kty === 'RSA') {
94 res.algorithm.modulusLength = b64u.toBinaryString(key.n).length * 8;
95 res.algorithm.publicExponent = str2buf.toUint8Array(b64u.toBinaryString(key.e));
96 }
97 return res;
98 });
99 }
100
101 const originalDeriveBits = crypto.subtle.deriveBits;
102 crypto.subtle.deriveBits = function deriveBits() {
103 const algorithm = arguments[0];
104 if (algorithm.name.toUpperCase() === 'PBKDF2') {
105 let deriveBits, ref;
106 if (liner.crypto.subtle.getProvider) {
107 ref = liner.crypto.subtle.getProvider('PBKDF2');
108 deriveBits = ref.onDeriveBits;
109 } else {
110 ref = liner.crypto.subtle;
111 deriveBits = ref.deriveBits;
112 }
113 return deriveBits.apply(ref, arguments);
114 }
115
116 return originalDeriveBits.apply(this, arguments);
117 }
118 })
119 .catch(e => {
120 console.log('Unable to secure:', e.message);
121 throw e;
122 });
123
124crypto.ensureSecure = () => secured;
125
126function standardizeAlgoName(algo) {
127 const upper = algo.toUpperCase();
128 return upper === 'RSASSA-PKCS1-V1_5' ? 'RSASSA-PKCS1-v1_5' : upper;
129}
130
131function ensureUint8Array(buffer) {
132 if (typeof buffer === 'string' || buffer instanceof String)
133 return str2buf.toUint8Array(buffer);
134 if (!buffer) return;
135 if (buffer instanceof ArrayBuffer) return new Uint8Array(buffer);
136 if (buffer instanceof Uint8Array) return buffer;
137 return buffer;
138}
139
140const originalGetRandomValues = crypto.getRandomValues;
141crypto.getRandomValues = function getRandomValues() {
142 if (!isSecured) {
143 throw new Error(`
144 You must wait until the library is secure to call this method:
145
146 await crypto.ensureSecure();
147 const safeValues = crypto.getRandomValues();
148 `);
149 }
150 return originalGetRandomValues.apply(crypto, arguments);
151}
152
153// wrap all methods to ensure they're secure
154const methods = [
155 'decrypt',
156 'digest',
157 'deriveKey',
158 'encrypt',
159 'exportKey',
160 'generateKey',
161 'importKey',
162 'sign',
163 'unwrapKey',
164 'verify',
165 'wrapKey'
166]
167methods.map(key => {
168 const original = crypto.subtle[key]
169 const proxy = function() {
170 const args = Array.from(arguments)
171 const before = crypto.subtle[key];
172 return crypto.ensureSecure()
173 .then(() => {
174 const after = crypto.subtle[key];
175 if (before === after) {
176 return original.apply(crypto.subtle, args)
177 } else {
178 return crypto.subtle[key].apply(crypto.subtle, args)
179 }
180 });
181 }
182 crypto.subtle[key] = proxy;
183 crypto.subtle[key].name = key;
184})
185
186const originalGenerateKey = crypto.subtle.generateKey;
187crypto.subtle.generateKey = function generateKey() {
188 const algo = arguments[0];
189 if (algo) {
190 if (algo.name) algo.name = algo.name.toLowerCase();
191 if (algo.hash && algo.hash.name) algo.hash.name = algo.hash.name.toLowerCase();
192 }
193 return originalGenerateKey.apply(this, arguments)
194 .then(res => {
195 if (res.publicKey) {
196 res.publicKey.usages = ['verify'];
197 res.publicKey.algorithm.name = standardizeAlgoName(res.publicKey.algorithm.name);
198 res.privateKey.usages = ['sign'];
199 res.privateKey.algorithm.name = standardizeAlgoName(res.privateKey.algorithm.name);
200 } else {
201 res.algorithm.name = standardizeAlgoName(res.algorithm.name);
202 res.usages = res.algorithm.name === 'HMAC' ? ['sign', 'verify'] : ['encrypt', 'decrypt'];
203 }
204 return res;
205 });
206}
207
208const originalExportKey = crypto.subtle.exportKey;
209crypto.subtle.exportKey = function exportKey() {
210 const key = arguments[1];
211 return originalExportKey.apply(this, arguments)
212 .then(res => {
213 if (res.kty === 'RSA' || res.kty === 'EC') {
214 if (res.d) {
215 res.key_ops = ['sign'];
216 } else {
217 res.key_ops = ['verify'];
218 }
219 }
220 switch(res.alg) {
221 case 'EC-256':
222 case 'EC-384':
223 case 'EC-521':
224 delete res.alg;
225 }
226 return res;
227 });
228}
229
230const originalDigest = crypto.subtle.digest;
231crypto.subtle.digest = function digest() {
232 arguments[1] = ensureUint8Array(arguments[1]);
233 return originalDigest.apply(this, arguments);
234}
235
236module.exports = crypto