PPS uses Ed25519 keys.
The server stores only public keys.
The client stores private key material.
seed: 32 bytes
public key: 32 bytes
secret key: 64 bytes
A master seed can derive normal and duress seeds.
normal_seed = HKDF(master_seed, "PulseProofSentinel/seed/v1|normal")
duress_seed = HKDF(master_seed, "PulseProofSentinel/seed/v1|duress")
For stronger duress isolation, use independent seeds.
Each device key has a key identifier:
kid = 16 random bytes
Use pairwise kid values per Relying Party to reduce correlation.
Each single-device Pulse rotates the signing key.
K_t signs Pulse containing pk_t and next_pk_{t+1}
After success:
server current_pk = next_pk
client current_seed = next_seed
The old private key should be deleted.
Rotation certificates allow offline catch-up.
cert_t = Sign(K_t, pk_t -> pk_{t+1})
The client stores pending certificates until the server acknowledges them.
Recommended storage:
Secure Enclave
StrongBox
TPM
hardware security key
encrypted key store
Avoid storing seeds in plain text.
Recovery options:
secondary registered device
encrypted cloud backup
recovery codes
social recovery
threshold recovery
admin-assisted recovery
Recovery flows must resist social engineering.