1
1
import { Buffer } from 'buffer' ;
2
2
3
+ type RandomBytesFunction = ( size : number ) => Uint8Array ;
4
+
3
5
/**
4
6
* Normalizes our expected stringified form of a function across versions of node
5
7
* @param fn - The function to stringify
@@ -8,11 +10,20 @@ export function normalizedFunctionString(fn: Function): string {
8
10
return fn . toString ( ) . replace ( 'function(' , 'function (' ) ;
9
11
}
10
12
11
- function insecureRandomBytes ( size : number ) : Uint8Array {
13
+ const isReactNative =
14
+ typeof global . navigator === 'object' && global . navigator . product === 'ReactNative' ;
15
+
16
+ const insecureWarning = isReactNative
17
+ ? 'BSON: For React Native please polyfill crypto.getRandomValues, e.g. using: https://www.npmjs.com/package/react-native-get-random-values.'
18
+ : 'BSON: No cryptographic implementation for random bytes present, falling back to a less secure implementation.' ;
19
+
20
+ const insecureRandomBytes : RandomBytesFunction = function insecureRandomBytes ( size : number ) {
21
+ console . warn ( insecureWarning ) ;
22
+
12
23
const result = Buffer . alloc ( size ) ;
13
24
for ( let i = 0 ; i < size ; ++ i ) result [ i ] = Math . floor ( Math . random ( ) * 256 ) ;
14
25
return result ;
15
- }
26
+ } ;
16
27
17
28
/* We do not want to have to include DOM types just for this check */
18
29
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -22,22 +33,34 @@ declare let require: Function;
22
33
declare let global : any ;
23
34
declare const self : unknown ;
24
35
25
- export let randomBytes = insecureRandomBytes ;
26
- if ( typeof window !== 'undefined' && window . crypto && window . crypto . getRandomValues ) {
27
- randomBytes = size => window . crypto . getRandomValues ( Buffer . alloc ( size ) ) ;
28
- } else {
36
+ const detectRandomBytes = ( ) : RandomBytesFunction => {
37
+ if ( typeof window !== 'undefined' ) {
38
+ // browser crypto implementation(s)
39
+ const target = window . crypto || window . msCrypto ; // allow for IE11
40
+ if ( target && target . getRandomValues ) {
41
+ return size => target . getRandomValues ( Buffer . alloc ( size ) ) ;
42
+ }
43
+ }
44
+
45
+ if ( typeof global !== 'undefined' && global . crypto && global . crypto . getRandomValues ) {
46
+ // allow for RN packages such as https://www.npmjs.com/package/react-native-get-random-values to populate global
47
+ return size => global . crypto . getRandomValues ( Buffer . alloc ( size ) ) ;
48
+ }
49
+
50
+ let requiredRandomBytes : RandomBytesFunction | null | undefined ;
29
51
try {
30
52
// eslint-disable-next-line @typescript-eslint/no-var-requires
31
- randomBytes = require ( 'crypto' ) . randomBytes ;
53
+ requiredRandomBytes = require ( 'crypto' ) . randomBytes ;
32
54
} catch ( e ) {
33
55
// keep the fallback
34
56
}
35
57
36
58
// NOTE: in transpiled cases the above require might return null/undefined
37
- if ( randomBytes == null ) {
38
- randomBytes = insecureRandomBytes ;
39
- }
40
- }
59
+
60
+ return requiredRandomBytes || insecureRandomBytes ;
61
+ } ;
62
+
63
+ export const randomBytes = detectRandomBytes ( ) ;
41
64
42
65
export function isUint8Array ( value : unknown ) : value is Uint8Array {
43
66
return Object . prototype . toString . call ( value ) === '[object Uint8Array]' ;
0 commit comments