Skip to content

Add opt-in crypto.getRandomValues, crypto.randomUUID and performance.now - #3100

Merged
lahma merged 1 commit into
sebastienros:mainfrom
lahma:webapi/crypto-performance
Aug 20, 2026
Merged

lahma merged 1 commit into
sebastienros:mainfrom
lahma:webapi/crypto-performance

Conversation

@lahma

@lahma lahma commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Part of the opt-in web API series (#3079): crypto.getRandomValues/crypto.randomUUID behind WebApiFeatures.Crypto and performance.now()/performance.timeOrigin behind WebApiFeatures.Performance (both in Default). 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 WebIDL TypeError, while a Float*Array/DataView (which are views) reaches step 1 and gets TypeMismatchError; byteLength over 65536 is QuotaExceededError; the view's own slice is filled via RandomNumberGenerator.Fill and 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.randomUUID implements the spec's byte-level algorithm (16 cryptographically random bytes, version nibble forced to 4, variant to 10xx, hyphenated lowercase hex) rather than trusting a Guid — with tests pinning the version nibble, variant bits and lowercase.
  • performance.now() is a monotonic sub-millisecond double measured from performance.timeOrigin — the wall-clock instant the engine's web-API state was created — reading the same TimeProvider the timers use, so a fake clock drives timers, Event.timeStamp and performance coherently. Coarsening (hr-time's 100µs anti-fingerprinting) is deliberately not applied — a host wanting a coarse clock supplies a coarse provider; documented.
  • This PR also completes the shared-state unification the events PR set up: one monotonic origin timestamp on the per-engine state serving Event.timeStamp and performance.now through a single CurrentHighResolutionTime, with the wall-clock TimeOrigin alongside — per engine, deliberately not reset by a global-snapshot restore so now() can never go backwards across evaluation cycles for a pooled engine.

crypto.subtle intentionally 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

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>
@lahma
lahma merged commit 71194a0 into sebastienros:main Aug 20, 2026
5 checks passed
@lahma
lahma deleted the webapi/crypto-performance branch August 20, 2026 19:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant