Skip to content

Commit a85f81d

Browse files
authored
Use crypo RNG for auto ID generation to reduce conflicts. (#2764)
* Use crypo RNG for auto ID generation to reduce conflicts. * Address comments. * Fix mistake. * Declare magic numbers.
1 parent 216eb63 commit a85f81d

File tree

5 files changed

+41
-2
lines changed

5 files changed

+41
-2
lines changed

packages/firestore/src/platform/platform.ts

+6
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

+10
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,14 @@ export class BrowserPlatform implements Platform {
6767
btoa(raw: string): string {
6868
return btoa(raw);
6969
}
70+
71+
randomBytes(nBytes: number): Uint8Array {
72+
if (nBytes <= 0) {
73+
return new Uint8Array();
74+
}
75+
76+
const v = new Uint8Array(nBytes);
77+
crypto.getRandomValues(v);
78+
return v;
79+
}
7080
}

packages/firestore/src/platform_node/node_platform.ts

+9
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 * as util from 'util';
1920

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

packages/firestore/src/util/misc.ts

+12-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717

1818
import { assert } 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+
bytes.forEach(b => {
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
assert(autoId.length === 20, 'Invalid auto ID: ' + autoId);
3545
return autoId;

packages/firestore/test/util/test_platform.ts

+4
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,10 @@ export class TestPlatform implements Platform {
262262
btoa(raw: string): string {
263263
return this.basePlatform.btoa(raw);
264264
}
265+
266+
randomBytes(nBytes: number): Uint8Array {
267+
return this.basePlatform.randomBytes(nBytes);
268+
}
265269
}
266270

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

0 commit comments

Comments
 (0)