Skip to content

Commit d64f9ff

Browse files
Boyscouting. Update easydb to 2.7 to eliminate boolean workaround.
1 parent 2802917 commit d64f9ff

File tree

10 files changed

+110
-50
lines changed

10 files changed

+110
-50
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"ext-pdo": "*",
1919
"guzzlehttp/guzzle": "^6",
2020
"paragonie/blakechain": "^1",
21-
"paragonie/easydb": "^2",
21+
"paragonie/easydb": "^2.7",
2222
"paragonie/sapient": "^1",
2323
"paragonie/slim-sapient": "^1",
2424
"paragonie/sodium_compat": "^1.7",

src/Chronicle/Chronicle.php

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,10 @@ public static function extendBlakechain(
6969
$db->beginTransaction();
7070
/** @var array<string, string> $lasthash */
7171
$lasthash = $db->row(
72-
'SELECT currhash, hashstate FROM chronicle_chain ORDER BY id DESC LIMIT 1'
72+
'SELECT currhash, hashstate
73+
FROM chronicle_chain
74+
ORDER BY id DESC
75+
LIMIT 1'
7376
);
7477

7578
// Instantiate the Blakechain.
@@ -152,36 +155,30 @@ public static function errorResponse(
152155
);
153156
}
154157

155-
/**
156-
* If we're using SQLite, we need a 1 or a 0.
157-
* Otherwise, TRUE/FALSE is fine.
158-
*
159-
* @param bool $value
160-
* @return bool|int
161-
*/
162-
public static function getDatabaseBoolean(bool $value)
163-
{
164-
if (self::$easyDb->getDriver() === 'sqlite') {
165-
return $value ? 1 : 0;
166-
}
167-
return !empty($value);
168-
}
169-
170158
/**
171159
* Given a clients Public ID, retrieve their Ed25519 public key.
172160
*
173161
* @param string $clientId
162+
* @param bool $adminOnly
174163
* @return SigningPublicKey
175164
*
176165
* @throws ClientNotFound
177166
*/
178-
public static function getClientsPublicKey(string $clientId): SigningPublicKey
167+
public static function getClientsPublicKey(string $clientId, bool $adminOnly = false): SigningPublicKey
179168
{
180-
/** @var array<string, string> $sqlResult */
181-
$sqlResult = static::$easyDb->row(
182-
"SELECT * FROM chronicle_clients WHERE publicid = ?",
183-
$clientId
184-
);
169+
if ($adminOnly) {
170+
/** @var array<string, string> $sqlResult */
171+
$sqlResult = static::$easyDb->row(
172+
"SELECT * FROM chronicle_clients WHERE publicid = ? AND isAdmin",
173+
$clientId
174+
);
175+
} else {
176+
/** @var array<string, string> $sqlResult */
177+
$sqlResult = static::$easyDb->row(
178+
"SELECT * FROM chronicle_clients WHERE publicid = ?",
179+
$clientId
180+
);
181+
}
185182
if (empty($sqlResult)) {
186183
throw new ClientNotFound('Client not found');
187184
}

src/Chronicle/Handlers/Register.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ class Register implements HandlerInterface
3939
* @throws FilesystemException
4040
* @throws GuzzleException
4141
* @throws InvalidMessageException
42-
* @throws TargetNotFound
42+
* @throws SecurityViolation
4343
* @throws \SodiumException
44+
* @throws TargetNotFound
4445
*/
4546
public function __invoke(
4647
RequestInterface $request,
@@ -161,14 +162,18 @@ public function __invoke(
161162
* @return string
162163
*
163164
* @throws \PDOException
164-
* @throws \Exception
165+
* @throws SecurityViolation
165166
*/
166167
protected function createClient(array $post): string
167168
{
168169
$db = Chronicle::getDatabase();
169170
$now = (new \DateTime())->format(\DateTime::ATOM);
170171
do {
171-
$clientId = Base64UrlSafe::encode(\random_bytes(24));
172+
try {
173+
$clientId = Base64UrlSafe::encode(\random_bytes(24));
174+
} catch (\Throwable $ex) {
175+
throw new SecurityViolation('CSPRNG is broken');
176+
}
172177
} while ($db->exists('SELECT count(id) FROM chronicle_clients WHERE publicid = ?', $clientId));
173178

174179
$db->beginTransaction();
@@ -178,7 +183,7 @@ protected function createClient(array $post): string
178183
'publicid' => $clientId,
179184
'publickey' => $post['publickey'],
180185
'comment' => $post['comment'] ?? '',
181-
'isAdmin' => Chronicle::getDatabaseBoolean(false),
186+
'isAdmin' => false,
182187
'created' => $now,
183188
'modified' => $now
184189
]

src/Chronicle/Handlers/Revoke.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,11 @@ public function __invoke(
9191
$post['publickey']
9292
);
9393
if (!$found) {
94-
return Chronicle::errorResponse($response, 'Error: Client not found. It may have already been deleted.', 404);
94+
return Chronicle::errorResponse(
95+
$response,
96+
'Error: Client not found. It may have already been deleted.',
97+
404
98+
);
9599
}
96100
/** @var bool $isAdmin */
97101
$isAdmin = $db->cell(
@@ -100,15 +104,19 @@ public function __invoke(
100104
$post['publickey']
101105
);
102106
if ($isAdmin) {
103-
return Chronicle::errorResponse($response, 'You cannot delete administrators from this API.', 403);
107+
return Chronicle::errorResponse(
108+
$response,
109+
'You cannot delete administrators from this API.',
110+
403
111+
);
104112
}
105113

106114
$db->delete(
107115
'chronicle_clients',
108116
[
109117
'publicid' => $post['clientid'],
110118
'publickey' => $post['publickey'],
111-
'isAdmin' => Chronicle::getDatabaseBoolean(false)
119+
'isAdmin' => false
112120
]
113121
);
114122
if ($db->commit()) {

src/Chronicle/Middleware/CheckAdminSignature.php

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use ParagonIE\Chronicle\Chronicle;
66
use ParagonIE\Chronicle\Exception\ClientNotFound;
7-
use ParagonIE\ConstantTime\Base64UrlSafe;
87
use ParagonIE\Sapient\CryptographyKeys\SigningPublicKey;
98

109
/**
@@ -25,16 +24,6 @@ class CheckAdminSignature extends CheckClientSignature
2524
*/
2625
public function getPublicKey(string $clientId): SigningPublicKey
2726
{
28-
/** @var array<string, string> $sqlResult */
29-
$sqlResult = Chronicle::getDatabase()->row(
30-
"SELECT * FROM chronicle_clients WHERE publicid = ? AND isAdmin",
31-
$clientId
32-
);
33-
if (empty($sqlResult)) {
34-
throw new ClientNotFound('Client not found or is not an administrator.');
35-
}
36-
return new SigningPublicKey(
37-
Base64UrlSafe::decode($sqlResult['publickey'])
38-
);
27+
return Chronicle::getClientsPublicKey($clientId, true);
3928
}
4029
}

src/Chronicle/Middleware/CheckClientSignature.php

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,20 @@ public function getClientId(RequestInterface $request): string
4646
return (string) \array_shift($header);
4747
}
4848

49+
/**
50+
* Only selects a valid result if the client has isAdmin set to TRUE.
51+
*
52+
* @param string $clientId
53+
* @return SigningPublicKey
54+
*
55+
* @throws ClientNotFound
56+
*/
57+
public function getPublicKey(string $clientId): SigningPublicKey
58+
{
59+
// The second parameter gets overridden in CheckAdminSignature to TRUE:
60+
return Chronicle::getClientsPublicKey($clientId, false);
61+
}
62+
4963
/**
5064
* @param RequestInterface $request
5165
* @param ResponseInterface $response
@@ -69,15 +83,19 @@ public function __invoke(
6983

7084
try {
7185
/** @var SigningPublicKey $publicKey */
72-
$publicKey = Chronicle::getClientsPublicKey($clientId);
86+
$publicKey = $this->getPublicKey($clientId);
7387
} catch (ClientNotFound $ex) {
74-
return Chronicle::errorResponse($response, 'Invalid client', 403);
88+
return Chronicle::errorResponse($response, $ex->getMessage(), 403);
7589
}
7690

7791
try {
78-
$request = Chronicle::getSapient()->verifySignedRequest($request, $publicKey);
92+
$request = Chronicle::getSapient()
93+
->verifySignedRequest($request, $publicKey);
94+
7995
if ($request instanceof Request) {
80-
$serverPublicKey = Chronicle::getSigningKey()->getPublicKey()->getString();
96+
$serverPublicKey = Chronicle::getSigningKey()
97+
->getPublicKey()
98+
->getString();
8199
if (\hash_equals($serverPublicKey, $publicKey->getString())) {
82100
return Chronicle::errorResponse(
83101
$response,

src/Chronicle/Process/Attest.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111

1212
/**
1313
* Class Attest
14+
*
15+
* This process publishes the latest hash of each replicated Chronicle
16+
* onto the local instance, to create an immutable record of the replicated
17+
* Chronicles and provide greater resilience against malicious tampering.
18+
*
1419
* @package ParagonIE\Chronicle\Process
1520
*/
1621
class Attest
@@ -31,6 +36,8 @@ public function __construct(array $settings = [])
3136
}
3237

3338
/**
39+
* Do we need to run the attestation process?
40+
*
3441
* @return bool
3542
*
3643
* @throws FilesystemException
@@ -97,7 +104,15 @@ public function attestAll(): array
97104
foreach (Chronicle::getDatabase()->run('SELECT id, uniqueid FROM chronicle_replication_sources') as $row) {
98105
/** @var array<string, string> $latest */
99106
$latest = Chronicle::getDatabase()->row(
100-
"SELECT currhash, summaryhash FROM chronicle_replication_chain WHERE source = ? ORDER BY id DESC LIMIT 1",
107+
"SELECT
108+
currhash,
109+
summaryhash
110+
FROM
111+
chronicle_replication_chain
112+
WHERE
113+
source = ?
114+
ORDER BY id DESC
115+
LIMIT 1",
101116
$row['id']
102117
);
103118
$latest['source'] = $row['uniqueid'];

src/Chronicle/Process/CrossSign.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020

2121
/**
2222
* Class CrossSign
23+
*
24+
* Publish the latest hash onto another remote Chronicle instance.
25+
*
2326
* @package ParagonIE\Chronicle\Process
2427
*/
2528
class CrossSign

src/Chronicle/Process/Replicate.php

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919

2020
/**
2121
* Class Replicate
22+
*
23+
* Maintain a replica (mirror) of another Chronicle instance.
24+
* Unless Attestation is enabled, this doesn't affect the main
25+
* Chronicle; mirroring is separate.
26+
*
2227
* @package ParagonIE\Chronicle\Process
2328
*/
2429
class Replicate
@@ -131,7 +136,15 @@ protected function appendToChain(array $entry): bool
131136
$db->beginTransaction();
132137
/** @var array<string, string> $lasthash */
133138
$lasthash = $db->row(
134-
'SELECT currhash, hashstate FROM chronicle_replication_chain WHERE source = ? ORDER BY id DESC LIMIT 1',
139+
'SELECT
140+
currhash,
141+
hashstate
142+
FROM
143+
chronicle_replication_chain
144+
WHERE
145+
source = ?
146+
ORDER BY id DESC
147+
LIMIT 1',
135148
$this->id
136149
);
137150

@@ -157,7 +170,7 @@ protected function appendToChain(array $entry): bool
157170
);
158171
if (!$sigMatches) {
159172
$db->rollBack();
160-
throw new SecurityViolation('Invalid Ed25519 signature');
173+
throw new SecurityViolation('Invalid Ed25519 signature provided by source Chronicle.');
161174
}
162175

163176
/* Update the Blakechain */
@@ -202,7 +215,14 @@ protected function getLatestSummaryHash(): string
202215
{
203216
/** @var string $last */
204217
$last = Chronicle::getDatabase()->cell(
205-
"SELECT summaryhash FROM chronicle_replication_chain WHERE source = ? ORDER BY id DESC LIMIT 1",
218+
"SELECT
219+
summaryhash
220+
FROM
221+
chronicle_replication_chain
222+
WHERE
223+
source = ?
224+
ORDER BY id DESC
225+
LIMIT 1",
206226
$this->id
207227
);
208228
if (empty($last)) {

src/Chronicle/Scheduled.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public function __construct(array $settings = [])
3737
*
3838
* @return self
3939
*
40+
* @throws Exception\ChainAppendException
4041
* @throws Exception\FilesystemException
4142
* @throws Exception\ReplicationSourceNotFound
4243
* @throws Exception\SecurityViolation
@@ -96,9 +97,13 @@ public function doReplication(): self
9697
}
9798

9899
/**
100+
* Run the Attestation process (if it's scheduled).
101+
*
99102
* @return self
100103
*
104+
* @throws Exception\ChainAppendException
101105
* @throws Exception\FilesystemException
106+
* @throws \SodiumException
102107
*/
103108
public function doAttestation(): self
104109
{

0 commit comments

Comments
 (0)