Skip to content

Commit 867417c

Browse files
committed
Reland "Use crypo RNG for auto ID generation to reduce conflicts (#2872)"
1 parent 9f7b7df commit 867417c

File tree

5 files changed

+54
-2
lines changed

5 files changed

+54
-2
lines changed

packages/firestore/src/platform/platform.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ export interface Platform {
4343
/** Converts a binary string to a Base64 encoded string. */
4444
btoa(raw: string): string;
4545

46+
/**
47+
* Generates `nBytes` of random bytes. If `nBytes` is negative, an empty array
48+
* will be returned.
49+
*/
50+
randomBytes(nBytes: number): Uint8Array;
51+
4652
/** The Platform's 'window' implementation or null if not available. */
4753
readonly window: Window | null;
4854

packages/firestore/src/platform_browser/browser_platform.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,19 @@ import { Platform } from '../platform/platform';
2020
import { Connection } from '../remote/connection';
2121
import { JsonProtoSerializer } from '../remote/serializer';
2222
import { ConnectivityMonitor } from './../remote/connectivity_monitor';
23+
2324
import { NoopConnectivityMonitor } from '../remote/connectivity_monitor_noop';
2425
import { BrowserConnectivityMonitor } from './browser_connectivity_monitor';
2526
import { WebChannelConnection } from './webchannel_connection';
2627

28+
// Polyfill for IE and WebWorker
29+
// eslint-disable-next-line no-restricted-globals
30+
const crypto = window['crypto'] || window['msCrypto'] || self['crypto'];
31+
2732
// Implements the Platform API for browsers and some browser-like environments
2833
// (including ReactNative).
2934
export class BrowserPlatform implements Platform {
35+
readonly useProto3Json = true;
3036
readonly base64Available: boolean;
3137

3238
constructor() {
@@ -72,4 +78,21 @@ export class BrowserPlatform implements Platform {
7278
btoa(raw: string): string {
7379
return btoa(raw);
7480
}
81+
82+
randomBytes(nBytes: number): Uint8Array {
83+
if (nBytes <= 0) {
84+
return new Uint8Array();
85+
}
86+
87+
const v = new Uint8Array(nBytes);
88+
if(!!crypto) {
89+
crypto.getRandomValues(v);
90+
} else {
91+
// Falls back to Math.random
92+
for(let i = 0; i < nBytes; i++) {
93+
v[i] = Math.floor(Math.random() * 256);
94+
}
95+
}
96+
return v;
97+
}
7598
}

packages/firestore/src/platform_node/node_platform.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18+
import { randomBytes } from 'crypto';
1819
import { inspect } from 'util';
1920

2021
import { DatabaseId, DatabaseInfo } from '../core/database_info';
@@ -75,4 +76,12 @@ export class NodePlatform implements Platform {
7576
btoa(raw: string): string {
7677
return new Buffer(raw, 'binary').toString('base64');
7778
}
79+
80+
randomBytes(nBytes: number): Uint8Array {
81+
if (nBytes <= 0) {
82+
return new Uint8Array();
83+
}
84+
85+
return randomBytes(nBytes);
86+
}
7887
}

packages/firestore/src/util/misc.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717

1818
import { debugAssert } from './assert';
19+
import { PlatformSupport } from '../platform/platform';
1920

2021
export type EventHandler<E> = (value: E) => void;
2122
export interface Indexable {
@@ -28,8 +29,17 @@ export class AutoId {
2829
const chars =
2930
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
3031
let autoId = '';
31-
for (let i = 0; i < 20; i++) {
32-
autoId += chars.charAt(Math.floor(Math.random() * chars.length));
32+
while (autoId.length < 20) {
33+
const bytes = PlatformSupport.getPlatform().randomBytes(40);
34+
for (const b of Array.from(bytes)) {
35+
// Length of `chars` is 62. We only take bytes between 0 and 62*4-1
36+
// (both inclusive). The value is then evenly mapped to indices of `char`
37+
// via a modulo operation.
38+
const maxValue = 62 * 4 - 1;
39+
if (autoId.length < 20 && b <= maxValue) {
40+
autoId += chars.charAt(b % 62);
41+
}
42+
}
3343
}
3444
debugAssert(autoId.length === 20, 'Invalid auto ID: ' + autoId);
3545
return autoId;

packages/firestore/test/util/test_platform.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,10 @@ export class TestPlatform implements Platform {
266266
btoa(raw: string): string {
267267
return this.basePlatform.btoa(raw);
268268
}
269+
270+
randomBytes(nBytes: number): Uint8Array {
271+
return this.basePlatform.randomBytes(nBytes);
272+
}
269273
}
270274

271275
/** Returns true if we are running under Node. */

0 commit comments

Comments
 (0)