-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path04-duress-authentication.php
More file actions
148 lines (117 loc) · 4.36 KB
/
Copy path04-duress-authentication.php
File metadata and controls
148 lines (117 loc) · 4.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
<?php
/**
* PPS-PHP Example 04: Silent Duress Authentication
*
* Demonstrates the Honey-Pulse duress mechanism:
* 1. User authenticates under duress using hidden duress key
* 2. Server detects duress but returns normal-looking response
* 3. Server internally triggers silent alert
*
* Usage:
* php examples/04-duress-authentication.php
*/
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use Pps\Crypto\Base64Url;
use Pps\Crypto\Ed25519;
use Pps\Crypto\Random;
use Pps\Encoding\CborEncoder;
use Pps\Key\KeyManager;
use Pps\Payload\PulsePayload;
use Pps\Storage\InMemoryStorage;
use Pps\Util\SystemTimeProvider;
use Pps\Verification\PulseVerifier;
echo "=== PPS-PHP Example 04: Silent Duress Authentication ===\n\n";
$timeProvider = new SystemTimeProvider();
$storage = new InMemoryStorage();
$rpId = 'example.com';
// ---------------------------------------------------------------
// Setup: Register device with normal and duress keys
// ---------------------------------------------------------------
echo "Setup: Registering device with duress key...\n";
$clientState = KeyManager::createClientState($rpId);
$kid = $clientState['kid'];
$normalSeed = Base64Url::decode($clientState['current_seed']);
$duressSeed = Base64Url::decode($clientState['duress_seed']);
$normalKey = KeyManager::keyPairFromSeed($normalSeed);
$duressKey = KeyManager::keyPairFromSeed($duressSeed);
$storage->saveDeviceState($kid, [
'kid' => $kid,
'current_pk' => $normalKey->publicKeyB64u(),
'key_seq' => 0,
'last_counter' => 0,
'duress_pk' => $duressKey->publicKeyB64u(),
'duress_key_seq' => 0,
'duress_last_counter' => 0,
'revoked' => false,
]);
echo " Normal PK: " . $normalKey->publicKeyB64u() . "\n";
echo " Duress PK: " . $duressKey->publicKeyB64u() . "\n\n";
// ---------------------------------------------------------------
// Step 1: User authenticates under duress
// ---------------------------------------------------------------
echo "Step 1: User authenticates under duress\n";
echo " (Using hidden duress key instead of normal key)\n\n";
$epoch = $timeProvider->epoch();
$counter = 1;
$exp = $epoch + 2;
$nonce = Random::nonce();
$nonceB64u = Base64Url::encode($nonce);
// Generate next duress key for rotation
$nextDuressKey = KeyManager::rotateKeyPair($duressSeed, $epoch, $counter, duress: true);
// Build Pulse payload signed with duress key
$payload = new PulsePayload($rpId, $epoch, $counter, $exp);
$payload->singleDevice(
kid: Base64Url::decode($kid),
pk: $duressKey->publicKey,
nextPk: $nextDuressKey->publicKey,
rotSeq: 1
);
$payload->nonce($nonce);
$message = $payload->encode();
$signature = Ed25519::sign($message, $duressKey->secretKey);
$envelope = CborEncoder::encode([
1 => $message,
2 => [
$kid => Base64Url::encode($signature),
],
]);
$pulseToken = Base64Url::encode($envelope);
echo " Pulse created with duress key.\n\n";
// ---------------------------------------------------------------
// Step 2: Server verifies Pulse
// ---------------------------------------------------------------
echo "Step 2: Server verifies Pulse\n";
$verifier = new PulseVerifier($storage, $timeProvider);
$result = $verifier->verify(
pulseToken: $pulseToken,
rpId: $rpId,
nonceB64u: $nonceB64u
);
// ---------------------------------------------------------------
// Step 3: Server handles result
// ---------------------------------------------------------------
echo "Step 3: Server handles result\n\n";
// Outward response is IDENTICAL to normal success
$outwardResponse = json_encode(['status' => 'ok']);
echo " Outward response: $outwardResponse\n";
// Internal handling
if ($result->ok && $result->honey) {
echo "\n [INTERNAL] Duress detected!\n";
echo " [INTERNAL] Mode: " . $result->mode . "\n";
echo " [INTERNAL] Actions:\n";
echo " - Restrict account capabilities\n";
echo " - Block high-value transactions\n";
echo " - Trigger silent security alert\n";
echo " - Delay settlement\n";
echo " - Notify security team\n";
// Apply state updates
$storage->updateDeviceState($kid, $result->updates);
} elseif ($result->ok) {
echo "\n [INTERNAL] Normal authentication.\n";
$storage->updateDeviceState($kid, $result->updates);
} else {
echo "\n Verification FAILED: " . $result->error . "\n";
exit(1);
}
echo "\n=== Example 04 Complete ===\n";