Ed25519 leases
Offline lease verification
Section titled “Offline lease verification”The Swift SDK keeps a trusted keyset per app (KeylightConfiguration.trustedPublicKeys: [String: Curve25519.Signing.PublicKey]) and verifies every lease it receives locally using Ed25519 before trusting it. Leases are a v3 pipe-delimited payload.
The signed payload includes an entitlements_csv field (sorted, comma-joined,
empty string when no entitlements are granted). Sorting is required so the
client and server reconstruct an identical payload regardless of the order in
which entitlements were added on either side.
v3|<kid>|<keyHash>|<instanceId>|<issuedAt>|<expiresAt>|<status>|<entitlements_csv>signed by Keylight’s Ed25519 private key. The SDK:
- Parses the payload.
- Looks up
kidintrustedPublicKeys. - Rejects the lease if the kid is unknown (prevents downgrade attacks and defends against compromised-key scenarios once you ship a new build).
- Verifies the Ed25519 signature.
- Checks
expiresAtagainst the local clock (with clock-manipulation mitigation, below).
Because verification is local, entitlement survives the app being offline for the lease lifetime - typically days to weeks. Keylight is consulted only for activation, validation refreshes, and deactivation.
Clock manipulation
Section titled “Clock manipulation”A user could roll their system clock back to extend an expired lease. KeylightProvider.isClockManipulated() mitigates this:
- On every successful network round trip, the provider stores
lastSeen = nowin the keychain. - On every entitlement check, it compares
nowto the storedlastSeen. Ifnow < lastSeen - 1h, the clock has moved backward by more than the drift tolerance and the lease is treated as invalid. - The 1-hour tolerance absorbs legitimate NTP corrections and timezone shenanigans without false positives.
This doesn’t defeat an attacker who knows to clear the keychain entry, but it stops the common “set the clock back a year” attack cold.
Key rotation
Section titled “Key rotation”Keylight supports rotating your signing key when needed (suspected exposure, routine hygiene). Rotation is handled as a coordinated backend + app-side update:
- Both the old and new public keys ship in the SDK’s
trustedPublicKeysduring a grace window, so users holding an unexpired lease stay.licensedthrough the swap. - New leases are signed with the new
kidonce Keylight flips to the new key. - Once adoption reaches an acceptable threshold, a later app build drops the old key.
The SDK rejects any lease whose kid isn’t in its trusted keyset, so once users are on the post-grace build, a leaked old key is worthless. There is no remote revocation of already-issued leases by design - the 7-day TTL is the revocation window.