Repository navigation
Add opt-in crypto.getRandomValues, crypto.randomUUID and performance.now - #3100
Merged
Merged
Conversation
Two independent web-API features, WebApiFeatures.Crypto (1 << 5) and WebApiFeatures.Performance (1 << 6), both in Default and both entirely crypto (https://w3c.github.io/webcrypto/#crypto-interface) - getRandomValues follows the algorithm step by step, and the two gates in front of it fail differently: WebIDL's own ArrayBufferView conversion raises a TypeError for anything that is not a view — and for a view onto a SharedArrayBuffer, which only an operation declaring [AllowShared] accepts — while step 1 raises a TypeMismatchError DOMException for a view that is one but holds floats, a Float16/32/64Array or a DataView. Step 3's 65536-byte quota is a QuotaExceededError DOMException, counted in bytes rather than elements, and 65536 itself is accepted. - The byte length comes from the typed array's buffer witness, so a detached view and a length-tracking view whose resizable buffer has shrunk past it both answer zero: step 4's byte sequence is empty, step 6 writes it into nothing, and the array comes back untouched rather than raising. Only the view's own slice is written, and an immutable ArrayBuffer refuses the write with the same TypeError an ordinary element assignment raises — asked only once there is something to write, exactly as IntegerIndexedElementSet orders it. - randomUUID is the algorithm verbatim: sixteen bytes from RandomNumberGenerator, the version nibble of byte 6 set to 0100 and the variant bits of byte 8 to 10, formatted through Guid's big-endian constructor. Guid.NewGuid() would be one line and does produce a v4 UUID, but which generator backs it is a platform implementation detail rather than a documented guarantee, and "cryptographically secure random bytes" is the one thing this operation promises. - crypto.subtle is deliberately absent rather than present-and-throwing, so `if (crypto.subtle)` gets the truthful answer and takes its fallback path. performance (https://w3c.github.io/hr-time/) - now() is the duration from the engine's time origin, and timeOrigin is that origin as Unix-epoch milliseconds, so the two sum to the current wall-clock time. Both read Options.WebApi.Timers.TimeProvider — the clock the timers are already scheduled against — so one fake provider drives setTimeout and the readings coherently instead of leaving one of them frozen. - The origin is per engine and deliberately survives RestoreGlobalSnapshot: a pooled engine keeps it, because rewinding it is precisely what would make now() go backwards, which the specification forbids outright. Readings are not coarsened; a host that wants a coarse clock supplies a coarse TimeProvider. - Marks, measures, the performance timeline, toJSON and the EventTarget the interface inherits from are all absent rather than throwing. WebApiRegistration now creates the engine's web-API state for Timers or Performance, with the timer queue null when only Performance asked — the extend-rather-than-overwrite rule the state's own comment states. Both objects carry the interface @@toStringTag and brand-check every member, so an extracted getRandomValues cannot be called on anything else. Like console, they are installed as ordinary enumerable data properties and their members are own properties with built-in attributes rather than WebIDL prototype members: two documented simplifications whose only observable difference, Object.keys, answers the empty array either way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of the opt-in web API series (#3079):
crypto.getRandomValues/crypto.randomUUIDbehindWebApiFeatures.Cryptoandperformance.now()/performance.timeOriginbehindWebApiFeatures.Performance(both inDefault). Usual rules: BCL only, net8+, opt-in, default engine unchanged.crypto.getRandomValues(https://w3c.github.io/webcrypto/#Crypto-method-getRandomValues): two gates that fail differently, both from the spec's own layering — a non-view is a WebIDLTypeError, while aFloat*Array/DataView(which are views) reaches step 1 and getsTypeMismatchError; byteLength over 65536 isQuotaExceededError; the view's own slice is filled viaRandomNumberGenerator.Filland the same array returned. Byte length comes from the typed-array-with-buffer-witness record, so detached and out-of-bounds length-tracking views answer zero bytes and return unfilled rather than throwing.crypto.randomUUIDimplements the spec's byte-level algorithm (16 cryptographically random bytes, version nibble forced to 4, variant to 10xx, hyphenated lowercase hex) rather than trusting aGuid— with tests pinning the version nibble, variant bits and lowercase.performance.now()is a monotonic sub-millisecond double measured fromperformance.timeOrigin— the wall-clock instant the engine's web-API state was created — reading the sameTimeProviderthe timers use, so a fake clock drives timers,Event.timeStampandperformancecoherently. Coarsening (hr-time's 100µs anti-fingerprinting) is deliberately not applied — a host wanting a coarse clock supplies a coarse provider; documented.Event.timeStampandperformance.nowthrough a singleCurrentHighResolutionTime, with the wall-clockTimeOriginalongside — per engine, deliberately not reset by a global-snapshot restore sonow()can never go backwards across evaluation cycles for a pooled engine.crypto.subtleintentionally arrives separately (digest-first, campaign #3083). Verified: all-TFM build, both suites both frameworks, both host-contract-verification configurations; mutation-verified tests for the quota-in-bytes rule and the view-slice fill.🤖 Generated with Claude Code