nowMsTrusted method

int nowMsTrusted()

Trusted "now", in UTC epoch milliseconds. Never goes backward (rewind-proof, same guarantee nowMsClamped() gives), and never permanently locks onto a suspicious far-future jump (see class doc).

Migrates from the legacy StorageKeys.maxMsSeen watermark on first ever call (IDEA-40 slice 4) — an install already running the plain nowMsClamped-backed clock keeps its existing rewind-protection floor instead of restarting from a lower value, which would both look like a spurious backward jump and could hand back a window an old exploit already burned.

Implementation

int nowMsTrusted() {
  final storage = StorageService.to;
  final sample = _sampleNow();

  final storedBaseline = storage.getInt(
    StorageKeys.trustedClockBaselineMs,
    def: 0,
  );

  // A dedicated existence check — NOT `prevWallMs == 0` — is required
  // here: 0 is a perfectly legitimate wall-clock reading in tests (and,
  // in principle, epoch 0 in production), so treating it as the "never
  // initialized" sentinel would make any real sample that happens to
  // land back on the stored 0 re-run first-call migration instead of
  // being classified normally.
  final hasPrevSample = storage.allKeys().contains(
    StorageKeys.trustedClockPrevWallMs,
  );
  if (!hasPrevSample) {
    // First ever sample for this install — nothing to classify yet.
    // Migrate from the legacy watermark (see doc above) if higher than
    // this fresh wall-clock reading.
    final legacyWatermark = storage.getInt(StorageKeys.maxMsSeen, def: 0);
    final baseline = legacyWatermark > sample.wallMs
        ? legacyWatermark
        : sample.wallMs;
    _persist(sample, baseline);
    return baseline;
  }

  final prevWallMs = storage.getInt(
    StorageKeys.trustedClockPrevWallMs,
    def: 0,
  );
  final prevMonotonicMs = storage.getInt(
    StorageKeys.trustedClockPrevMonotonicMs,
    def: 0,
  );
  final judgement = classifyClockSample(
    previous: ClockSample(wallMs: prevWallMs, monotonicMs: prevMonotonicMs),
    current: sample,
    normalTolerance: normalTolerance,
    suspiciousJumpThreshold: suspiciousJumpThreshold,
  );
  _lastJudgement = judgement;

  switch (judgement) {
    case ClockJudgement.normal:
    case ClockJudgement.reboot:
      // Both a plain forward tick AND a reboot advance the baseline
      // when the new wall reading is ahead of it — a reboot can't be
      // cross-checked against monotonic elapsed time (the whole point
      // of "reboot"), so it degrades to the same rewind-safe-only
      // guarantee `nowMsClamped()` already gives (documented
      // limitation, not a new one: `clamped_clock.dart` is equally
      // unable to catch a kill-app-wind-clock-reopen cycle).
      //
      // The new sample becomes the trusted "previous" going forward —
      // ONLY for a trusted judgement. See the other 2 cases below for
      // why an untrusted sample must never become that reference.
      final newBaseline = sample.wallMs > storedBaseline
          ? sample.wallMs
          : storedBaseline;
      _persist(sample, newBaseline);
      return newBaseline;
    case ClockJudgement.rewind:
    case ClockJudgement.suspiciousForwardJump:
      // Neither the baseline NOR the "previous sample" reference
      // advances here — this is what makes recovery possible (IDEA-40's
      // actual fix). If a quarantined jump's bogus wall reading were
      // persisted as the new "previous", the very next honest sample
      // (wall clock corrected back near reality) would itself look
      // like a huge REWIND relative to that bogus reference and get
      // quarantined too — a self-inflicted permanent lock, exactly the
      // failure mode this class exists to avoid. Keeping the last
      // TRUSTED sample as the comparison point means a corrected clock
      // is compared against reality, not against the bad reading.
      return storedBaseline;
  }
}