This document explains the core concepts used by PPS-PHP.
A Pulse is a signed authentication statement generated by the client.
It proves possession of a private key bound to:
A Pulse Token is the serialized form of a Pulse.
It is encoded as:
base64url(CBOR envelope)
The envelope contains:
1: message
2: signatures
3: certificates, optional
The Relying Party identifier, abbreviated rp_id, identifies the service.
Examples:
example.com
login.example.com
example.com:8443
android:com.example.app
ios:com.example.app
The protocol stores:
rp_hash = SHA256(rp_id)
PPS divides time into epochs.
Default epoch length:
30 seconds
Epoch number:
epoch = floor(unix_time / 30)
The counter is a monotonic unsigned integer.
It prevents replay attacks.
The server rejects any Pulse where:
pulse_counter <= stored_counter
A nonce is a server-generated random value.
Recommended size:
16 bytes
Nonces should be:
Online mode requires a nonce.
Offline mode may omit the nonce.
PPS uses Ed25519 key pairs.
public key: 32 bytes
secret key: 64 bytes
seed: 32 bytes
The server stores only public keys.
A key identifier, abbreviated kid, identifies a registered device key.
Recommended size:
16 bytes
It should be opaque and should not contain personal data.
The rotation sequence tracks forward-secure key rotations.
Initial value:
key_seq = 0
Each rotation increments it:
new_key_seq = old_key_seq + 1
A rotation certificate authorizes transition from one public key to another.
It is signed by the old private key.
Rotation certificates allow offline catch-up when the server has not observed previous rotations.
A Trust Code is a short human-verifiable numeric value derived from the signature.
Example:
493718
The Trust Code is not the primary authentication secret.
The full Pulse Token is preferred.
An AmountMark is a two-digit transaction checksum.
It is derived from:
amount_minor mod 100
Example:
amount = 2,500,067
AmountMark = 67
Trust Code = 49371867
A Trust Phrase is an optional three-word human-friendly phrase.
Example:
river bronze lantern
It is derived from the same trust material as the Trust Code.
A context object binds the Pulse to environmental or session data.
Possible fields:
session_id
tx_hash
ble_hash
acoustic_hash
ip_hash
ua_hash
device_integrity_hash
custom_hash
The context hash is:
ctx = SHA256(CBOR(context_object))
A policy object binds the Pulse to server-defined security requirements.
Possible fields:
min_trust_digits
require_biometric
require_secure_enclave
threshold
max_amount_minor
require_proximity
max_clock_skew
allow_offline
require_amount_mark
require_duress_support
The policy hash is:
pol = SHA256(CBOR(policy_object))
A duress key is a hidden key used when the user is coerced.
A Pulse signed by the duress key is called a Honey-Pulse.
The server returns a normal-looking response but may internally restrict the account.
A threshold group allows multiple devices to approve the same Pulse.
PPS version 1 uses:
n-of-m Ed25519 multisignature
Future versions may define true threshold signatures such as FROST.