Skip to content

Commit 27e60e2

Browse files
committed
Add initial Trust Mark Validation implementation
1 parent 927df12 commit 27e60e2

6 files changed

Lines changed: 573 additions & 29 deletions

File tree

README.md

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class Test
6060
)
6161
);
6262

63-
// Define the maximum cache TTL for federation artifacts. This will be used together with 'exp'
63+
// Define the maximum cache Time-To-Live (TTL) for federation artifacts. This will be used together with 'exp'
6464
// claim to resolve the maximum cache time for trust chains, entity statements, etc.
6565
$maxCacheDuration = new DateInterval('PT6H');
6666

@@ -101,7 +101,7 @@ try {
101101
],
102102
);
103103
} catch (\Throwable $exception) {
104-
$this->loggerService->error('Could not resolve trust chain: ' . $exception->getMessage())
104+
$this->logger->error('Could not resolve trust chain: ' . $exception->getMessage())
105105
return;
106106
}
107107

@@ -126,7 +126,7 @@ try {
126126
'https://trust-achor-id.example.org/',
127127
);
128128
} catch (\Throwable $exception) {
129-
$this->loggerService->error('Could not resolve trust chain: ' . $exception->getMessage())
129+
$this->logger->error('Could not resolve trust chain: ' . $exception->getMessage())
130130
return;
131131
}
132132

@@ -145,7 +145,7 @@ try {
145145
/** @var \SimpleSAML\OpenID\Federation\TrustChain $trustChain */
146146
$metadata = $trustChain->getResolvedMetadata($entityType);
147147
} catch (\Throwable $exception) {
148-
$this->loggerService->error(
148+
$this->logger->error(
149149
sprintf(
150150
'Error resolving metadata for entity type %s. Error: %s.',
151151
$entityType->value,
@@ -156,7 +156,7 @@ try {
156156
}
157157

158158
if (is_null($metadata)) {
159-
$this->loggerService->error(
159+
$this->logger->error(
160160
sprintf(
161161
'No metadata available for entity type %s.',
162162
$entityType->value,
@@ -191,9 +191,52 @@ $trustAnchorEntityId = $trustAnchorConfigurationStatement->getIssuer();
191191
try {
192192
$trustAnchorConfigurationStatement->verifyWithKeySet($trustAnchorJwks);
193193
} catch (\Throwable $exception) {
194-
$this->loggerService->error('Could not verify trust anchor configuration statement signature: ' .
194+
$this->logger->error('Could not verify trust anchor configuration statement signature: ' .
195195
$exception->getMessage());
196196
return;
197197
}
198198

199199
```
200+
201+
### Validating Trust Marks
202+
203+
Federation tools expose Trust Mark Validator with several methods for validating Trust Marks, with the most common
204+
one being the one to validate Trust Mark for some entity simply based on the Trust Mark ID.
205+
206+
If cache is utilized, Trust Mark validation will be cached with cache TTL being the minimum expiration
207+
time of Trust Mark, Leaf Entity Statement or `maxCacheDuration`, whatever is smaller.
208+
209+
```php
210+
// ...
211+
212+
/** @var \SimpleSAML\OpenID\Federation $federationTools */
213+
/** @var \SimpleSAML\OpenID\Federation\TrustChain $trustChain */
214+
215+
216+
// Trust Mark ID that you want to validate.
217+
$trustMarkId = 'https://example.com/trust-mark/member';
218+
// Leaf for which you want to validate the Trust Mark with ID above.
219+
$leafEntityConfigurationStatement = $trustChain->getResolvedLeaf();
220+
// Trust Anchor under which you want to validate Trust Mark.
221+
$trustAnchorConfigurationStatement = $trustChain->getResolvedTrustAnchor();
222+
223+
try {
224+
// Example which queries cache for previously validated Trust Mark, and does formal validation if not cached.
225+
$federationTools->trustMarkValidator()->fromCacheOrDoForTrustMarkId(
226+
$trustMarkId,
227+
$leafEntityConfigurationStatement,
228+
$trustAnchorConfigurationStatement,
229+
);
230+
231+
// Example which always does formal validation (does not use cache).
232+
$federationTools->trustMarkValidator()->doForTrustMarkId(
233+
$trustMarkId,
234+
$leafEntityConfigurationStatement,
235+
$trustAnchorConfigurationStatement,
236+
);
237+
} catch (\Throwable $exception) {
238+
$this->logger->error('Trust Mark validation failed. Error was: ' . $exception->getMessage());
239+
return;
240+
}
241+
242+
```

src/Federation/TrustMarkValidator.php

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public function isValidationCachedFor(
3939
string $trustAnchorId,
4040
): bool {
4141
if (is_null($this->cacheDecorator)) {
42+
$this->logger?->debug('Cache not available, skipping.');
4243
return false;
4344
}
4445

@@ -90,7 +91,7 @@ public function isValidationCachedFor(
9091
* @throws \SimpleSAML\OpenID\Exceptions\TrustMarkException
9192
* @throws \Psr\SimpleCache\InvalidArgumentException
9293
*/
93-
public function forTrustMarkId(
94+
public function fromCacheOrDoForTrustMarkId(
9495
string $trustMarkId,
9596
EntityStatement $leafEntityConfiguration,
9697
EntityStatement $trustAnchorEntityConfiguration,
@@ -105,6 +106,21 @@ public function forTrustMarkId(
105106
return;
106107
}
107108

109+
$this->doForTrustMarkId($trustMarkId, $leafEntityConfiguration, $trustAnchorEntityConfiguration);
110+
}
111+
112+
/**
113+
* @param non-empty-string $trustMarkId
114+
* @throws \SimpleSAML\OpenID\Exceptions\EntityStatementException
115+
* @throws \SimpleSAML\OpenID\Exceptions\InvalidValueException
116+
* @throws \SimpleSAML\OpenID\Exceptions\JwsException
117+
* @throws \SimpleSAML\OpenID\Exceptions\TrustMarkException
118+
*/
119+
public function doForTrustMarkId(
120+
string $trustMarkId,
121+
EntityStatement $leafEntityConfiguration,
122+
EntityStatement $trustAnchorEntityConfiguration,
123+
): void {
108124
$this->logger?->debug(
109125
sprintf(
110126
'Validating Trust Mark %s for leaf entity %s under Trust Anchor %s.',
@@ -201,12 +217,14 @@ public function forTrustMarkId(
201217
}
202218
}
203219

204-
throw new TrustMarkException(sprintf(
205-
'Could not validate Trust Mark %s for leaf entity %s under Trust Anchor %s.',
206-
$trustMarkId,
207-
$leafEntityConfiguration->getIssuer(),
208-
$trustAnchorEntityConfiguration->getIssuer(),
209-
));
220+
throw new TrustMarkException(
221+
sprintf(
222+
'Could not validate Trust Mark %s for leaf entity %s under Trust Anchor %s.',
223+
$trustMarkId,
224+
$leafEntityConfiguration->getIssuer(),
225+
$trustAnchorEntityConfiguration->getIssuer(),
226+
),
227+
);
210228
}
211229

212230
/**
@@ -218,7 +236,7 @@ public function forTrustMarkId(
218236
* @throws \SimpleSAML\OpenID\Exceptions\TrustMarkException
219237
* @throws \Psr\SimpleCache\InvalidArgumentException
220238
*/
221-
public function forTrustMarksClaimValue(
239+
public function fromCacheOrDoForTrustMarksClaimValue(
222240
TrustMarksClaimValue $trustMarksClaimValue,
223241
EntityStatement $leafEntityConfiguration,
224242
EntityStatement $trustAnchorEntityConfiguration,
@@ -249,7 +267,7 @@ public function forTrustMarksClaimValue(
249267
* @throws \SimpleSAML\OpenID\Exceptions\TrustMarkDelegationException
250268
* @throws \SimpleSAML\OpenID\Exceptions\TrustMarkException
251269
*/
252-
protected function doForTrustMarksClaimValue(
270+
public function doForTrustMarksClaimValue(
253271
TrustMarksClaimValue $trustMarksClaimValue,
254272
EntityStatement $leafEntityConfiguration,
255273
EntityStatement $trustAnchorEntityConfiguration,
@@ -332,7 +350,7 @@ public function validateTrustMarksClaimValue(
332350
* @throws \SimpleSAML\OpenID\Exceptions\JwksException
333351
* @throws \Psr\SimpleCache\InvalidArgumentException
334352
*/
335-
public function forTrustMark(
353+
public function fromCacheOrDoForTrustMark(
336354
TrustMark $trustMark,
337355
EntityStatement $leafEntityConfiguration,
338356
EntityStatement $trustAnchorEntityConfiguration,
@@ -363,7 +381,7 @@ public function forTrustMark(
363381
* @throws \SimpleSAML\OpenID\Exceptions\TrustMarkException
364382
* @throws \SimpleSAML\OpenID\Exceptions\JwksException
365383
*/
366-
protected function doForTrustMark(
384+
public function doForTrustMark(
367385
TrustMark $trustMark,
368386
EntityStatement $leafEntityConfiguration,
369387
EntityStatement $trustAnchorEntityConfiguration,
@@ -384,9 +402,9 @@ protected function doForTrustMark(
384402
$trustAnchorEntityConfiguration,
385403
);
386404

387-
$trustMarkIssuerEntityStatement = $trustMarkIssuerTrustChain->getResolvedLeaf();
405+
$trustMarkIssuerEntityConfiguration = $trustMarkIssuerTrustChain->getResolvedLeaf();
388406

389-
$this->validateTrustMarkSignature($trustMark, $trustMarkIssuerEntityStatement);
407+
$this->validateTrustMarkSignature($trustMark, $trustMarkIssuerEntityConfiguration);
390408

391409
$this->validateTrustMarkDelegation($trustMark, $trustAnchorEntityConfiguration);
392410

@@ -425,7 +443,7 @@ protected function doForTrustMark(
425443
$trustAnchorEntityConfiguration->getIssuer(),
426444
);
427445
} catch (Throwable $exception) {
428-
$this->logger?->debug(sprintf(
446+
$this->logger?->error(sprintf(
429447
'Error caching Trust Mark %s validation for leaf entity %s under Trust Anchor %s with TTL' .
430448
' %s. Error wa: %s.',
431449
$trustMark->getIdentifier(),
@@ -525,19 +543,19 @@ public function validateTrustChainForTrustMarkIssuer(
525543
*/
526544
public function validateTrustMarkSignature(
527545
TrustMark $trustMark,
528-
EntityStatement $trustMarkIssuerEntityStatement,
546+
EntityStatement $trustMarkIssuerEntityConfiguration,
529547
): void {
530548
$this->logger?->debug('Validating Trust Mark signature.');
531549
try {
532-
$trustMark->verifyWithKeySet($trustMarkIssuerEntityStatement->getJwks()->getValue());
550+
$trustMark->verifyWithKeySet($trustMarkIssuerEntityConfiguration->getJwks()->getValue());
533551
} catch (Throwable $exception) {
534552
$error = sprintf(
535553
'Trust Mark signature validation failed with error: %s',
536554
$exception->getMessage(),
537555
);
538556
$this->logger?->error(
539557
$error,
540-
['trustMarkIssuerJwks' => $trustMarkIssuerEntityStatement->getJwks()],
558+
['trustMarkIssuerJwks' => $trustMarkIssuerEntityConfiguration->getJwks()],
541559
);
542560
throw new TrustMarkException($error);
543561
}
@@ -563,7 +581,7 @@ public function validateTrustMarkDelegation(
563581
if (is_null($trustMarkOwnersBag)) {
564582
$this->logger?->debug(
565583
sprintf(
566-
'Trust Anchor %s does not define Trust Mark Owners, skipping delegation validation.',
584+
'Trust Anchor %s does not define Trust Mark Owners. Skipping delegation validation.',
567585
$trustAnchorEntityConfiguration->getIssuer(),
568586
),
569587
);

0 commit comments

Comments
 (0)