|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* |
|
4
|
|
|
* The MIT License (MIT) |
|
5
|
|
|
* |
|
6
|
|
|
* Copyright (c) 2014-2016 Spomky-Labs |
|
7
|
|
|
* |
|
8
|
|
|
* This software may be modified and distributed under the terms |
|
9
|
|
|
* of the MIT license. See the LICENSE file for details. |
|
10
|
|
|
*/ |
|
11
|
|
|
|
|
12
|
|
|
namespace Jose; |
|
13
|
|
|
|
|
14
|
|
|
use Jose\Algorithm\JWAManagerInterface; |
|
15
|
|
|
use Jose\Algorithm\SignatureAlgorithmInterface; |
|
16
|
|
|
use Jose\Behaviour\HasCheckerManager; |
|
17
|
|
|
use Jose\Behaviour\HasJWAManager; |
|
18
|
|
|
use Jose\Behaviour\HasKeyChecker; |
|
19
|
|
|
use Jose\Checker\CheckerManagerInterface; |
|
20
|
|
|
use Jose\Object\JWKInterface; |
|
21
|
|
|
use Jose\Object\JWKSet; |
|
22
|
|
|
use Jose\Object\JWKSetInterface; |
|
23
|
|
|
use Jose\Object\JWSInterface; |
|
24
|
|
|
use Jose\Object\SignatureInterface; |
|
25
|
|
|
|
|
26
|
|
|
/** |
|
27
|
|
|
*/ |
|
28
|
|
|
final class Verifier implements VerifierInterface |
|
29
|
|
|
{ |
|
30
|
|
|
use HasKeyChecker; |
|
31
|
|
|
use HasJWAManager; |
|
32
|
|
|
use HasCheckerManager; |
|
33
|
|
|
|
|
34
|
|
|
/** |
|
35
|
|
|
* Loader constructor. |
|
36
|
|
|
* |
|
37
|
|
|
* @param \Jose\Algorithm\JWAManagerInterface $jwa_manager |
|
38
|
|
|
* @param \Jose\Checker\CheckerManagerInterface $checker_manager |
|
39
|
|
|
*/ |
|
40
|
|
|
public function __construct( |
|
41
|
|
|
JWAManagerInterface $jwa_manager, |
|
42
|
|
|
CheckerManagerInterface $checker_manager) |
|
43
|
|
|
{ |
|
44
|
|
|
$this->setJWAManager($jwa_manager); |
|
45
|
|
|
$this->setCheckerManager($checker_manager); |
|
46
|
|
|
} |
|
47
|
|
|
|
|
48
|
|
|
/** |
|
49
|
|
|
* {@inheritdoc} |
|
50
|
|
|
* |
|
51
|
|
|
* @throws \InvalidArgumentException |
|
52
|
|
|
*/ |
|
53
|
|
|
public function verifyWithKey(JWSInterface $jws, JWKInterface $jwk, $detached_payload = null) |
|
54
|
|
|
{ |
|
55
|
|
|
$jwk_set = new JWKSet(); |
|
56
|
|
|
$jwk_set = $jwk_set->addKey($jwk); |
|
57
|
|
|
|
|
58
|
|
|
return $this->verifyWithKeySet($jws, $jwk_set, $detached_payload); |
|
59
|
|
|
} |
|
60
|
|
|
|
|
61
|
|
|
/** |
|
62
|
|
|
* {@inheritdoc} |
|
63
|
|
|
* |
|
64
|
|
|
* @throws \InvalidArgumentException |
|
65
|
|
|
*/ |
|
66
|
|
|
public function verifyWithKeySet(JWSInterface $jws, JWKSetInterface $jwk_set, $detached_payload = null) |
|
67
|
|
|
{ |
|
68
|
|
|
$this->checkPayload($jws, $detached_payload); |
|
|
|
|
|
|
69
|
|
|
$this->checkJWKSet($jwk_set); |
|
70
|
|
|
|
|
71
|
|
|
foreach ($jws->getSignatures() as $signature) { |
|
72
|
|
|
$input = $signature->getEncodedProtectedHeaders().'.'.(null === $detached_payload ? $jws->getEncodedPayload() : $detached_payload); |
|
73
|
|
|
|
|
74
|
|
|
foreach ($jwk_set->getKeys() as $jwk) { |
|
75
|
|
|
$algorithm = $this->getAlgorithm($signature); |
|
76
|
|
|
try { |
|
77
|
|
|
$this->checkKeyUsage($jwk, 'verification'); |
|
78
|
|
|
$this->checkKeyAlgorithm($jwk, $algorithm->getAlgorithmName()); |
|
79
|
|
|
if (true === $algorithm->verify($jwk, $input, $signature->getSignature())) { |
|
80
|
|
|
$this->getCheckerManager()->checkJWT($jws); |
|
81
|
|
|
|
|
82
|
|
|
return true; |
|
83
|
|
|
} |
|
84
|
|
|
} catch (\Exception $e) { |
|
85
|
|
|
//We do nothing, we continue with other keys |
|
86
|
|
|
continue; |
|
87
|
|
|
} |
|
88
|
|
|
} |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
|
|
return false; |
|
92
|
|
|
} |
|
93
|
|
|
|
|
94
|
|
|
/** |
|
95
|
|
|
* @param \Jose\Object\JWKSetInterface $jwk_set |
|
96
|
|
|
*/ |
|
97
|
|
|
private function checkJWKSet(JWKSetInterface $jwk_set) |
|
98
|
|
|
{ |
|
99
|
|
|
if (0 === count($jwk_set)) { |
|
100
|
|
|
throw new \InvalidArgumentException('No key in the key set.'); |
|
101
|
|
|
} |
|
102
|
|
|
} |
|
103
|
|
|
|
|
104
|
|
|
/** |
|
105
|
|
|
* @param \Jose\Object\JWSInterface $jws |
|
106
|
|
|
* @param null $detached_payload |
|
107
|
|
|
*/ |
|
108
|
|
|
private function checkPayload(JWSInterface $jws, $detached_payload = null) |
|
109
|
|
|
{ |
|
110
|
|
|
if (null !== $detached_payload && !empty($jws->getEncodedPayload())) { |
|
111
|
|
|
throw new \InvalidArgumentException('A detached payload is set, but the JWS already has a payload.'); |
|
112
|
|
|
} |
|
113
|
|
|
} |
|
114
|
|
|
|
|
115
|
|
|
/** |
|
116
|
|
|
* @param \Jose\Object\SignatureInterface $signature |
|
117
|
|
|
* |
|
118
|
|
|
* @return \Jose\Algorithm\SignatureAlgorithmInterface|null |
|
119
|
|
|
*/ |
|
120
|
|
|
private function getAlgorithm(SignatureInterface $signature) |
|
121
|
|
|
{ |
|
122
|
|
|
$complete_headers = array_merge( |
|
123
|
|
|
$signature->getProtectedHeaders(), |
|
124
|
|
|
$signature->getHeaders() |
|
125
|
|
|
); |
|
126
|
|
|
if (!array_key_exists('alg', $complete_headers)) { |
|
127
|
|
|
throw new \InvalidArgumentException('No "alg" parameter set in the header.'); |
|
128
|
|
|
} |
|
129
|
|
|
|
|
130
|
|
|
$algorithm = $this->getJWAManager()->getAlgorithm($complete_headers['alg']); |
|
131
|
|
|
if (!$algorithm instanceof SignatureAlgorithmInterface) { |
|
132
|
|
|
throw new \RuntimeException(sprintf('The algorithm "%s" is not supported or does not implement SignatureInterface.', $complete_headers['alg'])); |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
return $algorithm; |
|
136
|
|
|
} |
|
137
|
|
|
} |
|
138
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.