GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

JWS::validateWithJWK()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Sop\JWX\JWS;
6
7
use Sop\JWX\JWA\JWA;
8
use Sop\JWX\JWK\JWK;
9
use Sop\JWX\JWK\JWKSet;
10
use Sop\JWX\JWS\Algorithm\SignatureAlgorithmFactory;
11
use Sop\JWX\JWT\Header\Header;
12
use Sop\JWX\JWT\Header\JOSE;
13
use Sop\JWX\JWT\Parameter\CriticalParameter;
14
use Sop\JWX\JWT\Parameter\JWTParameter;
15
use Sop\JWX\Util\Base64;
16
17
/**
18
 * Class to represent JWS structure.
19
 *
20
 * @see https://tools.ietf.org/html/rfc7515#section-3
21
 */
22
class JWS
23
{
24
    /**
25
     * Protected header.
26
     *
27
     * @var Header
28
     */
29
    protected $_protectedHeader;
30
31
    /**
32
     * Payload.
33
     *
34
     * @var string
35
     */
36
    protected $_payload;
37
38
    /**
39
     * Input value for the signature computation.
40
     *
41
     * @var string
42
     */
43
    protected $_signatureInput;
44
45
    /**
46
     * Signature.
47
     *
48
     * @var string
49
     */
50
    protected $_signature;
51
52
    /**
53
     * Constructor.
54
     *
55
     * @param Header $protected_header JWS Protected Header
56
     * @param string $payload          JWS Payload
57
     * @param string $signature_input  Input value for the signature computation
58
     * @param string $signature        JWS Signature
59
     */
60 33
    protected function __construct(Header $protected_header, string $payload,
61
        string $signature_input, string $signature)
62
    {
63 33
        $this->_protectedHeader = $protected_header;
64 33
        $this->_payload = $payload;
65 33
        $this->_signatureInput = $signature_input;
66 33
        $this->_signature = $signature;
67 33
    }
68
69
    /**
70
     * Convert JWS to string.
71
     */
72 1
    public function __toString(): string
73
    {
74 1
        return $this->toCompact();
75
    }
76
77
    /**
78
     * Initialize from a compact serialization.
79
     */
80 6
    public static function fromCompact(string $data): self
81
    {
82 6
        return self::fromParts(explode('.', $data));
83
    }
84
85
    /**
86
     * Initialize from the parts of a compact serialization.
87
     *
88
     * @throws \UnexpectedValueException
89
     */
90 18
    public static function fromParts(array $parts): self
91
    {
92 18
        if (3 !== count($parts)) {
93 1
            throw new \UnexpectedValueException(
94 1
                'Invalid JWS compact serialization.');
95
        }
96 17
        $header = Header::fromJSON(Base64::urlDecode($parts[0]));
97 17
        $b64 = $header->hasB64Payload() ? $header->B64Payload()->value() : true;
98 17
        $payload = $b64 ? Base64::urlDecode($parts[1]) : $parts[1];
99 17
        $signature_input = $parts[0] . '.' . $parts[1];
100 17
        $signature = Base64::urlDecode($parts[2]);
101 17
        return new self($header, $payload, $signature_input, $signature);
102
    }
103
104
    /**
105
     * Initialize by signing the payload with given algorithm.
106
     *
107
     * @param string             $payload JWS Payload
108
     * @param SignatureAlgorithm $algo    Signature algorithm
109
     * @param null|Header        $header  Desired header. Algorithm specific
110
     *                                    parameters are added automatically.
111
     *
112
     * @throws \RuntimeException If signature computation fails
113
     */
114 16
    public static function sign(string $payload, SignatureAlgorithm $algo,
115
        ?Header $header = null): self
116
    {
117 16
        if (!isset($header)) {
118 4
            $header = new Header();
119
        }
120 16
        $header = $header->withParameters(...$algo->headerParameters());
121
        // ensure that if b64 parameter is used, it's marked critical
122 16
        if ($header->hasB64Payload()) {
123 4
            if (!$header->hasCritical()) {
124 2
                $crit = new CriticalParameter(JWTParameter::P_B64);
125
            } else {
126 2
                $crit = $header->critical()->withParamName(JWTParameter::P_B64);
127
            }
128 4
            $header = $header->withParameters($crit);
129
        }
130 16
        $signature_input = self::_generateSignatureInput($payload, $header);
131 16
        $signature = $algo->computeSignature($signature_input);
132 16
        return new self($header, $payload, $signature_input, $signature);
133
    }
134
135
    /**
136
     * Get JOSE header.
137
     */
138 22
    public function header(): JOSE
139
    {
140 22
        return new JOSE($this->_protectedHeader);
141
    }
142
143
    /**
144
     * Get the signature algorithm name.
145
     */
146 20
    public function algorithmName(): string
147
    {
148 20
        return $this->header()->algorithm()->value();
149
    }
150
151
    /**
152
     * Check whether JWS is unsecured, that is, contains no signature.
153
     */
154 10
    public function isUnsecured(): bool
155
    {
156 10
        return JWA::ALGO_NONE === $this->algorithmName();
157
    }
158
159
    /**
160
     * Get the payload.
161
     */
162 6
    public function payload(): string
163
    {
164 6
        return $this->_payload;
165
    }
166
167
    /**
168
     * Get the signature.
169
     */
170 5
    public function signature(): string
171
    {
172 5
        return $this->_signature;
173
    }
174
175
    /**
176
     * Validate the signature using explicit algorithm.
177
     *
178
     * @throws \UnexpectedValueException If using different signature algorithm
179
     *                                   then specified by the header
180
     * @throws \RuntimeException         If signature computation fails
181
     *
182
     * @return bool True if signature is valid
183
     */
184 15
    public function validate(SignatureAlgorithm $algo): bool
185
    {
186 15
        if ($algo->algorithmParamValue() !== $this->algorithmName()) {
187 1
            throw new \UnexpectedValueException('Invalid signature algorithm.');
188
        }
189 14
        return $algo->validateSignature($this->_signatureInput, $this->_signature);
190
    }
191
192
    /**
193
     * Validate the signature using the given JWK.
194
     *
195
     * Signature algorithm is determined from the header.
196
     *
197
     * @param JWK $jwk JSON Web Key
198
     *
199
     * @throws \RuntimeException If algorithm initialization fails
200
     *
201
     * @return bool True if signature is valid
202
     */
203 4
    public function validateWithJWK(JWK $jwk): bool
204
    {
205 4
        $algo = SignatureAlgorithm::fromJWK($jwk, $this->header());
206 4
        return $this->validate($algo);
207
    }
208
209
    /**
210
     * Validate the signature using a key from the given JWK set.
211
     *
212
     * Correct key shall be sought by the key ID indicated by the header.
213
     *
214
     * @param JWKSet $set Set of JSON Web Keys
215
     *
216
     * @throws \RuntimeException If algorithm initialization fails
217
     *
218
     * @return bool True if signature is valid
219
     */
220 5
    public function validateWithJWKSet(JWKSet $set): bool
221
    {
222 5
        if (!count($set)) {
223 1
            throw new \RuntimeException('No keys.');
224
        }
225 4
        $factory = new SignatureAlgorithmFactory($this->header());
226 4
        $algo = $factory->algoByKeys($set);
227 3
        return $this->validate($algo);
228
    }
229
230
    /**
231
     * Convert to compact serialization.
232
     */
233 13
    public function toCompact(): string
234
    {
235 13
        return Base64::urlEncode($this->_protectedHeader->toJSON()) . '.' .
236 13
             $this->_encodedPayload() . '.' .
237 13
             Base64::urlEncode($this->_signature);
238
    }
239
240
    /**
241
     * Convert to compact serialization with payload detached.
242
     */
243 2
    public function toCompactDetached(): string
244
    {
245 2
        return Base64::urlEncode($this->_protectedHeader->toJSON()) . '..' .
246 2
             Base64::urlEncode($this->_signature);
247
    }
248
249
    /**
250
     * Get the payload encoded for serialization.
251
     */
252 13
    protected function _encodedPayload(): string
253
    {
254 13
        $b64 = true;
255 13
        if ($this->_protectedHeader->hasB64Payload()) {
256 2
            $b64 = $this->_protectedHeader->B64Payload()->value();
257
        }
258 13
        return $b64 ? Base64::urlEncode($this->_payload) : $this->_payload;
259
    }
260
261
    /**
262
     * Generate input for the signature computation.
263
     *
264
     * @param string $payload Payload
265
     * @param Header $header  Protected header
266
     */
267 16
    protected static function _generateSignatureInput(string $payload,
268
        Header $header): string
269
    {
270 16
        $b64 = $header->hasB64Payload() ? $header->B64Payload()->value() : true;
271 16
        $data = Base64::urlEncode($header->toJSON()) . '.';
272 16
        $data .= $b64 ? Base64::urlEncode($payload) : $payload;
273 16
        return $data;
274
    }
275
}
276