pps-php

Threshold Mode

Threshold mode allows multiple devices to approve the same Pulse.

PPS version 1 uses n-of-m Ed25519 multisignature.


Use Cases


Group Registration

A threshold group contains:

gid
threshold
signers
last_counter
revoked

Example:

use Pps\Crypto\Base64Url;
use Pps\Key\KeyManager;

$gid = KeyManager::generateGid();
$gidB64u = Base64Url::encode($gid);

$phoneKey = KeyManager::generateKeyPair();
$watchKey = KeyManager::generateKeyPair();

$phoneKid = Base64Url::encode(KeyManager::generateKid());
$watchKid = Base64Url::encode(KeyManager::generateKid());

$storage->saveGroupState($gidB64u, [
    'gid' => $gidB64u,
    'threshold' => 2,
    'signers' => [
        $phoneKid => $phoneKey->publicKeyB64u(),
        $watchKid => $watchKey->publicKeyB64u(),
    ],
    'last_counter' => 0,
    'revoked' => false,
]);

Threshold Flow

1. Device A creates a partial Pulse payload.
2. Device A shares the payload with Device B.
3. Device B verifies the transaction details.
4. Device B signs the same payload.
5. Signatures are collected.
6. The final Pulse Token is assembled.
7. The server verifies the required number of signatures.

Example

use Pps\Client\AuthenticatorClient;

$phoneClient = new AuthenticatorClient($phoneState);
$partial = $phoneClient
    ->rpId('example.com')
    ->nonce($nonceB64u)
    ->amountMinor(5000000)
    ->thresholdGroup($gidB64u, 2)
    ->createPartialPulse();

$watchClient = new AuthenticatorClient($watchState);
$coSign = $watchClient->coSign($partial);

$token = AuthenticatorClient::assembleThresholdToken($partial, [
    $phoneKid => $phoneSignatureB64u,
    $watchKid => $coSign['signature'],
]);

Verification

The server verifies:

1. Group exists and is not revoked.
2. Threshold value matches stored threshold.
3. Each signer is registered.
4. Each signature is valid.
5. Number of valid signatures >= threshold.
6. Group counter increases.

Security Notes