-
Notifications
You must be signed in to change notification settings - Fork 929
Reland "Use crypo RNG for auto ID generation to reduce conflicts (#2764)" #3012
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
867417c
a2b89c2
02592ec
17b6bc5
ea4967a
434e650
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,9 +20,11 @@ import { Platform } from '../platform/platform'; | |
import { Connection } from '../remote/connection'; | ||
import { JsonProtoSerializer } from '../remote/serializer'; | ||
import { ConnectivityMonitor } from './../remote/connectivity_monitor'; | ||
|
||
import { NoopConnectivityMonitor } from '../remote/connectivity_monitor_noop'; | ||
import { BrowserConnectivityMonitor } from './browser_connectivity_monitor'; | ||
import { WebChannelConnection } from './webchannel_connection'; | ||
import { debugAssert } from '../util/assert'; | ||
|
||
// Implements the Platform API for browsers and some browser-like environments | ||
// (including ReactNative). | ||
|
@@ -72,4 +74,23 @@ export class BrowserPlatform implements Platform { | |
btoa(raw: string): string { | ||
return btoa(raw); | ||
} | ||
|
||
randomBytes(nBytes: number): Uint8Array { | ||
debugAssert(nBytes >= 0, 'Number of random bytes must not be negative'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider including the invalid value in the failure message; this could assist in debugging should the assert ever get triggered and all we have to use for debugging is the stack track and error message. e.g. debugAssert(nBytes >= 0, `invalid nBytes: ${nBytes}`); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
|
||
// Polyfill for IE and WebWorker | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you improve this comment to explain how it affects the reader? Is the polyfill being done by the code block that follows? Or is it instructing the reader to perform some polyfilling? Consider re-writing this comment as a sentence that would be either clearly informative or clearly actionable by the reader. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
const crypto = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might also consider adding support for this shim on ReactNative: https://stackoverflow.com/questions/45301900/howto-patch-shim-crypto-getrandomvalues-for-react-native While the fallback will make sure these environments don't break, we can possibly extend our support for Edit: I missed that you added There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So we don't need it, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am ok with not adding support for ReactNative until the ReactNative Firebase wrapper bundles "crypto", but note that without getting "crypto" from globals, we will always fall back to Math.random() for RN. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, I am filing a ticket to get crypto added for react native firestore. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. react-native-firebase participant here, just cross-linking invertase/react-native-firebase#3618 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just thought I'd quickly summerize; React Native Firebase doesn't use the JS SDK, we're a thin JS wrapper around the native Android & iOS SDKs so this change has no direct affect on us. However our JS wrapper does use an identical random id generator - so I think we may need to port this change across in some form - we'll pick this up separately, thanks! |
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
typeof self !== 'undefined' && (self.crypto || (self as any)['msCrypto']); | ||
const v = new Uint8Array(nBytes); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Consider a more meaningful name than There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
if (crypto) { | ||
crypto.getRandomValues(v); | ||
} else { | ||
// Falls back to Math.random | ||
for (let i = 0; i < nBytes; i++) { | ||
v[i] = Math.floor(Math.random() * 256); | ||
} | ||
} | ||
return v; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ | |
* limitations under the License. | ||
*/ | ||
|
||
import { randomBytes } from 'crypto'; | ||
import { inspect } from 'util'; | ||
|
||
import { DatabaseId, DatabaseInfo } from '../core/database_info'; | ||
|
@@ -27,6 +28,7 @@ import { NoopConnectivityMonitor } from './../remote/connectivity_monitor_noop'; | |
|
||
import { GrpcConnection } from './grpc_connection'; | ||
import { loadProtos } from './load_protos'; | ||
import { validatePositiveNumber } from '../util/input_validation'; | ||
|
||
export class NodePlatform implements Platform { | ||
readonly base64Available = true; | ||
|
@@ -75,4 +77,10 @@ export class NodePlatform implements Platform { | |
btoa(raw: string): string { | ||
return new Buffer(raw, 'binary').toString('base64'); | ||
} | ||
|
||
randomBytes(nBytes: number): Uint8Array { | ||
validatePositiveNumber('randomBytes', 1, nBytes); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DebugAssert here as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, make sure that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
|
||
return randomBytes(nBytes); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ | |
*/ | ||
|
||
import { debugAssert } from './assert'; | ||
import { PlatformSupport } from '../platform/platform'; | ||
|
||
export type EventHandler<E> = (value: E) => void; | ||
export interface Indexable { | ||
|
@@ -28,8 +29,17 @@ export class AutoId { | |
const chars = | ||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; | ||
let autoId = ''; | ||
for (let i = 0; i < 20; i++) { | ||
autoId += chars.charAt(Math.floor(Math.random() * chars.length)); | ||
while (autoId.length < 20) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider re-writing this loop as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rewrote the section to reduce hardcoding numbers. |
||
const bytes = PlatformSupport.getPlatform().randomBytes(40); | ||
for (let i = 0; i < bytes.length; ++i) { | ||
// Length of `chars` is 62. We only take bytes between 0 and 62*4-1 | ||
// (both inclusive). The value is then evenly mapped to indices of `char` | ||
// via a modulo operation. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm assuming this logic is used to ensure an even distribution between all of the characters in |
||
const maxValue = 62 * 4 - 1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider moving the calculation of maxValue to immediately follow the declaration of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hardcoding const chars = '...';
const maxValue = chars.length * 4 - 1;
debugAssert(maxValue < 256); Then the hardcoded value of |
||
if (autoId.length < 20 && bytes[i] <= maxValue) { | ||
autoId += chars.charAt(bytes[i] % 62); | ||
} | ||
} | ||
} | ||
debugAssert(autoId.length === 20, 'Invalid auto ID: ' + autoId); | ||
return autoId; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: nBytes==0 is valid; please update this comment to state that it will only reject values that are strictly less than zero.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.