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.
Passed
Branch php72 (880eb0)
by Joni
05:58
created

JWS::sign()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4

Importance

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