DEV Community

Hossein Hezami
Hossein Hezami

Posted on

TOTP Secrets Are a Liability: Meet PPS, an Asymmetric Authentication Protocol for PHP and Laravel

Most two-factor authentication systems still rely on a dangerous assumption:

If the server is breached, authentication secrets can be protected.

TOTP breaks that assumption.

With TOTP, the server stores a shared secret. If your database leaks, attackers can generate valid OTP codes.

That is exactly the problem PulseProof Sentinel Protocol, or PPS, tries to solve.

PPS is an experimental open authentication protocol that replaces shared-secret TOTP codes with signed, time-bound, asymmetric proofs called Pulses.

Instead of this:

TOTP:
  code = HMAC(shared_secret, time)
Enter fullscreen mode Exit fullscreen mode

PPS uses this:

PPS:
  Pulse = Sign(private_key, time + rp + nonce + counter + context + policy)
Enter fullscreen mode Exit fullscreen mode

The server never stores a shared OTP secret.

It stores only public keys.


What Is PPS?

PulseProof Sentinel Protocol is an asymmetric authentication protocol designed for scenarios where traditional OTP, push authentication, and even WebAuthn are not enough.

It is especially useful for:

  • offline authentication
  • legacy OTP input forms
  • QR-code and deep-link login
  • constrained hardware terminals
  • point-of-sale and IoT devices
  • transaction signing with human-visible confirmation
  • silent duress signaling
  • offline multi-device threshold approval

PPS is not trying to replace WebAuthn/FIDO2.

WebAuthn remains the preferred standard for browser-based passkey authentication.

PPS is designed to complement WebAuthn by covering operational gaps where WebAuthn is unavailable or impractical.

You can explore the protocol here:


Why Not Just Use WebAuthn?

WebAuthn is excellent.

If you are building browser-based passwordless authentication, use WebAuthn.

But many real systems still need:

- OTP-style input fields
- offline verification
- hardware terminals without browser support
- transaction amount confirmation
- silent duress codes
- multi-device approval
- QR/deep-link authentication flows
Enter fullscreen mode Exit fullscreen mode

That is where PPS fits.

Think of it like this:

Scenario Recommended
Browser passkey login WebAuthn/FIDO2
Legacy OTP form PPS
Offline terminal authentication PPS
High-value transaction signing PPS
Silent duress authentication PPS
Multi-device approval PPS
IoT or constrained device authentication PPS

Core Features

PPS includes a small core protocol plus optional advanced modules.

Core

  • Ed25519 signatures
  • deterministic CBOR encoding
  • time-bound Pulses
  • nonce binding
  • monotonic counter replay protection
  • relying-party binding
  • expiration enforcement

Optional Advanced Modules

  • forward-secure key ratcheting
  • silent duress / Honey-Pulse signaling
  • offline n-of-m threshold approval
  • transaction AmountMark verification
  • dynamic policy binding
  • environmental context binding

The protocol specification is available here:

https://pps-protocol.github.io/pulseproof-sentinel/SPEC.html


The PHP Implementation: pps-php

The core PHP implementation is available as a framework-agnostic package:

pps-protocol/pps-php
Enter fullscreen mode Exit fullscreen mode

Links:

Install it with Composer:

composer require pps-protocol/pps-php
Enter fullscreen mode Exit fullscreen mode

It supports:

  • Pulse creation
  • Pulse verification
  • registration flows
  • Trust Code generation
  • AmountMark transaction codes
  • duress keys
  • threshold signatures
  • offline mode
  • memory, file, PDO, and Redis storage backends

Quick Example: Creating a Pulse

Here is a simplified client-side example:

use Pps\Client\AuthenticatorClient;

$client = new AuthenticatorClient($clientState);

$pulse = $client
    ->rpId('example.com')
    ->nonce($nonceFromServer)
    ->createPulse();

echo $pulse['token'];
echo $pulse['trust_code'];
Enter fullscreen mode Exit fullscreen mode

The server verifies the Pulse using only the stored public key.

No shared secret is required.


Laravel Integration: laravel-pps

If you are using Laravel, there is a dedicated integration package:

pps-protocol/laravel-pps
Enter fullscreen mode Exit fullscreen mode

Links:

Install it:

composer require pps-protocol/laravel-pps
Enter fullscreen mode Exit fullscreen mode

Then run:

php artisan pps:install
Enter fullscreen mode Exit fullscreen mode

This publishes the PPS config and migrations.

The package provides:

  • service provider
  • publishable configuration
  • API routes
  • HTTP controller
  • facade
  • Artisan commands
  • storage backend integration
  • transaction challenge endpoints
  • silent duress handling support

Default routes:

Method Endpoint Description
GET /pps/health Health check
GET /pps/challenge Create authentication challenge
POST /pps/register Register a device
POST /pps/verify Verify a Pulse Token
POST /pps/transaction/challenge Create transaction challenge
POST /pps/transaction/verify Verify transaction-bound Pulse

Test the health endpoint:

curl http://localhost:8000/pps/health
Enter fullscreen mode Exit fullscreen mode

Example response:

{
    "status": "ok",
    "protocol": "PulseProof Sentinel Protocol",
    "version": 1,
    "algorithm": "PPS-ED25519-CBOR30",
    "epoch_seconds": 30,
    "rp_id": "localhost"
}
Enter fullscreen mode Exit fullscreen mode

Transaction Signing With AmountMark

One of the most useful PPS features is transaction signing.

Imagine a withdrawal:

Amount: 2,500,067 IRR
Trust Code: 49371867
Enter fullscreen mode Exit fullscreen mode

The last two digits, 67, are derived from the transaction amount.

This allows the user to visually confirm that the signed transaction matches the displayed amount.

The client binds the transaction hash and amount to the Pulse:

use Pps\Crypto\Base64Url;
use Pps\Payload\ContextObject;

$context = new ContextObject();
$context->sessionId = Base64Url::decode($sessionId);
$context->txHash = Base64Url::decode($txHash);

$pulse = $client
    ->rpId('example.com')
    ->nonce($nonce)
    ->context($context)
    ->amountMinor(2500067)
    ->createPulse();
Enter fullscreen mode Exit fullscreen mode

The server verifies:

  • signature
  • nonce
  • counter
  • transaction hash
  • amount mark
  • Trust Code

This is much stronger than sending a plain OTP for transaction confirmation.


Silent Duress Authentication

PPS also supports silent duress signaling.

If a user is forced to authenticate, the client can sign with a hidden duress key.

The server detects this internally but returns a normal-looking success response.

if ($result->honey) {
    // Return normal response.
    // Internally restrict account, alert security, delay settlement, etc.
}
Enter fullscreen mode Exit fullscreen mode

This is useful for fintech, banking, crypto exchanges, and high-risk accounts.

The outward response should remain indistinguishable from a normal login.


How PPS Compares to TOTP

Feature TOTP PPS
Shared secret on server Yes No
Asymmetric proof No Yes
Offline capable Yes Yes
Legacy OTP form support Yes Yes
Transaction binding Weak Strong
Amount confirmation No Yes
Silent duress No Optional
Threshold approval No Optional
Policy binding No Optional
Context binding No Optional

Important Links

Here are the most important links if you want to explore PPS:


Try It

If you are building:

  • fintech authentication
  • crypto withdrawal confirmation
  • offline device authentication
  • POS or IoT login
  • high-security admin approval
  • duress-aware accounts
  • multi-device authorization

PPS may be worth exploring.

Start with the website:

https://pps-protocol.github.io

Then read the specification:

https://pps-protocol.github.io/pulseproof-sentinel/SPEC.html

If you use PHP or Laravel, install the packages:

composer require pps-protocol/pps-php
composer require pps-protocol/laravel-pps
Enter fullscreen mode Exit fullscreen mode

PPS is experimental, so it is not recommended for production without security review. But if you are interested in authentication protocols, asymmetric OTP alternatives, or transaction signing, it is definitely worth a look.

If you find the idea useful, star the repositories, test the packages, and share feedback.

GitHub organization:

https://github.com/pps-protocol

Top comments (0)