Completed
Push — master ( 356550...41f07f )
by sebastian
03:01
created

JWS   D

Complexity

Total Complexity 37

Size/Duplication

Total Lines 282
Duplicated Lines 3.9 %

Coupling/Cohesion

Components 1
Dependencies 22

Importance

Changes 11
Bugs 3 Features 1
Metric Value
wmc 37
c 11
b 3
f 1
lcom 1
cbo 22
dl 11
loc 282
rs 4.9789

13 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 17 5
A setPayload() 0 5 1
B toCompactSerialization() 0 26 3
B sign() 2 33 6
A getEncodedPayload() 0 17 4
A setKey() 0 5 1
A fromCompactSerialization() 0 5 1
A getSigningAlgorithm() 0 4 1
A getType() 0 4 1
C verify() 9 64 10
A getPayload() 0 4 1
A fromHeaderClaimsAndSignature() 0 4 1
A take() 0 10 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * Copyright 2015 OpenStack Foundation
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 * http://www.apache.org/licenses/LICENSE-2.0
8
 * Unless required by applicable law or agreed to in writing, software
9
 * distributed under the License is distributed on an "AS IS" BASIS,
10
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
 * See the License for the specific language governing permissions and
12
 * limitations under the License.
13
 **/
14
15
namespace jws\impl;
16
17
use jwa\cryptographic_algorithms\digital_signatures\DigitalSignatureAlgorithm;
18
use jwa\cryptographic_algorithms\DigitalSignatures_MACs_Registry;
19
use jwa\cryptographic_algorithms\macs\MAC_Algorithm;
20
use jwk\exceptions\InvalidJWKAlgorithm;
21
use jwk\IAsymmetricJWK;
22
use jwk\IJWK;
23
use jwk\JSONWebKeyKeyOperationsValues;
24
use jwk\JSONWebKeyPublicKeyUseValues;
25
use jwk\JSONWebKeyVisibility;
26
use jws\exceptions\JWSInvalidJWKException;
27
use jws\exceptions\JWSInvalidPayloadException;
28
use jws\exceptions\JWSNotSupportedAlgorithm;
29
use jws\IJWS;
30
use jws\IJWSPayloadClaimSetSpec;
31
use jws\IJWSPayloadRawSpec;
32
use jws\IJWSPayloadSpec;
33
use jws\payloads\JWSPayloadFactory;
34
use jwt\IBasicJWT;
35
use jwt\IJOSEHeader;
36
use jwt\impl\JWT;
37
use jwt\impl\JWTSerializer;
38
use jwt\JOSEHeaderParam;
39
use jwt\RegisteredJOSEHeaderNames;
40
use jwt\utils\JOSEHeaderSerializer;
41
use jwt\utils\JWTClaimSetSerializer;
42
use jwt\utils\JWTRawSerializer;
43
use utils\json_types\JsonValue;
44
use utils\json_types\StringOrURI;
45
46
/**
47
 * Class JWS
48
 * @package jws\impl
49
 * @access private
50
 */
51
final class JWS extends JWT implements IJWS
52
{
53
54
    /**
55
     * @var IJWK
56
     */
57
    private $jwk = null;
58
59
    /**
60
     * @var IJWSPayloadSpec
61
     */
62
    private $payload = null;
63
64
    /**
65
     * @param IJOSEHeader $header
66
     * @param IJWSPayloadSpec $payload
67
     * @param string $signature
68
     * @throws JWSNotSupportedAlgorithm
69
     */
70
    protected function __construct(IJOSEHeader $header, IJWSPayloadSpec $payload = null, $signature = '')
71
    {
72
73
        $claim_set = null;
74
75
        if(!is_null($payload) && $payload->isClaimSet() && $payload instanceof IJWSPayloadClaimSetSpec) {
76
            $header->addHeader(new JOSEHeaderParam(RegisteredJOSEHeaderNames::Type, new StringOrURI('JWT')));
77
            $claim_set = $payload->getClaimSet();
78
        }
79
80
        parent::__construct($header, $claim_set);
81
82
        if(!is_null($payload))
83
            $this->setPayload($payload);
84
85
        $this->signature = $signature;
86
    }
87
88
    /**
89
     * @param IJWSPayloadSpec $payload
90
     * @return IJWS
91
     */
92
    public function setPayload(IJWSPayloadSpec $payload)
93
    {
94
        $this->payload = $payload;
95
        return $this;
96
    }
97
98
    /**
99
     * @return string
100
     */
101
    public function toCompactSerialization()
102
    {
103
        if(!is_null($this->jwk->getId()))
104
            $this->header->addHeader(new JOSEHeaderParam(RegisteredJOSEHeaderNames::KeyID, $this->jwk->getId()));
105
106
        if($this->jwk instanceof IAsymmetricJWK)
107
        {
108
            // we should add the public key on the header
109
            $public_key = clone $this->jwk;
110
111
            $this->header->addHeader
112
            (
113
                new JOSEHeaderParam
114
                (
115
                    RegisteredJOSEHeaderNames::JSONWebKey,
116
                    new JsonValue
117
                    (
118
                        $public_key->setVisibility(JSONWebKeyVisibility::PublicOnly)
119
                    )
120
                )
121
            );
122
        }
123
124
        $this->sign();
125
        return parent::toCompactSerialization();
126
    }
127
128
    /**
129
     * @return $this
130
     * @throws JWSInvalidJWKException
131
     * @throws JWSInvalidPayloadException
132
     * @throws JWSNotSupportedAlgorithm
133
     */
134
    public function sign()
135
    {
136
137
        if(is_null($this->jwk))
138
            throw new JWSInvalidJWKException;
139
140 View Code Duplication
        if($this->jwk->getKeyUse()->getString() !== JSONWebKeyPublicKeyUseValues::Signature)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
141
            throw new JWSInvalidJWKException(sprintf('use %s not supported.', $this->jwk->getKeyUse()->getString()));
142
143
        $alg = DigitalSignatures_MACs_Registry::getInstance()->get($this->header->getAlgorithm()->getString());
144
145
        if(is_null($alg))
146
            throw new JWSNotSupportedAlgorithm(sprintf('alg %s.',$this->header->getAlgorithm()->getString()));
147
148
        $secured_input_bytes = JOSEHeaderSerializer::serialize($this->header) . IBasicJWT::SegmentSeparator .$this->getEncodedPayload();
149
150
        $key  = $this->jwk->getKey(JSONWebKeyKeyOperationsValues::ComputeDigitalSignatureOrMAC);
151
152
        if($alg instanceof DigitalSignatureAlgorithm)
153
        {
154
            $this->signature = $alg->sign($key, $secured_input_bytes);
0 ignored issues
show
Compatibility introduced by
$key of type object<security\Key> is not a sub-type of object<security\PrivateKey>. It seems like you assume a child interface of the interface security\Key to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
155
        }
156
        else if($alg instanceof MAC_Algorithm )
157
        {
158
            $this->signature = $alg->digest($key, $secured_input_bytes);
0 ignored issues
show
Compatibility introduced by
$key of type object<security\Key> is not a sub-type of object<security\SharedKey>. It seems like you assume a child interface of the interface security\Key to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
159
        }
160
        else
161
        {
162
            throw new JWSNotSupportedAlgorithm(sprintf('alg %s.',$this->header->getAlgorithm()->getString()));
163
        }
164
165
        return $this;
166
    }
167
168
    /**
169
     * @return string
170
     * @throws JWSInvalidPayloadException
171
     */
172
    public function getEncodedPayload()
173
    {
174
        if(is_null($this->payload))
175
            throw new JWSInvalidPayloadException('payload is not set!');
176
177
        $enc_payload = '';
178
179
        if($this->payload instanceof IJWSPayloadClaimSetSpec)
180
        {
181
            $enc_payload = JWTClaimSetSerializer::serialize($this->payload->getClaimSet());
182
        }
183
        else if($this->payload instanceof IJWSPayloadRawSpec)
184
        {
185
            $enc_payload = JWTRawSerializer::serialize($this->payload->getRaw());
186
        }
187
        return $enc_payload;
188
    }
189
190
    /**
191
     * @param IJWK $key
192
     * @return $this
193
     */
194
    public function setKey(IJWK $key)
195
    {
196
        $this->jwk = $key;
197
        return $this;
198
    }
199
200
    /**
201
     * @param string $compact_serialization
202
     * @return IJWS
203
     * @access private
204
     */
205
    static public function fromCompactSerialization($compact_serialization)
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
206
    {
207
        list($header, $payload, $signature) = JWTSerializer::deserialize($compact_serialization);
208
        return new JWS($header, JWSPayloadFactory::build($payload), $signature);
209
    }
210
211
    /**
212
     * @return StringOrURI
213
     */
214
    public function getSigningAlgorithm()
215
    {
216
        return $this->header->getAlgorithm();
217
    }
218
219
    /**
220
     * @return StringOrURI
221
     */
222
    public function getType()
223
    {
224
        return $this->header->getType();
225
    }
226
227
    /**
228
     * @param string $original_alg
229
     * @return bool
230
     * @throws InvalidJWKAlgorithm
231
     * @throws JWSInvalidJWKException
232
     * @throws JWSInvalidPayloadException
233
     * @throws JWSNotSupportedAlgorithm
234
     */
235
    public function verify($original_alg)
236
    {
237
        if(is_null($this->jwk))
238
            throw new JWSInvalidJWKException;
239
240 View Code Duplication
        if($this->jwk->getKeyUse()->getString() !== JSONWebKeyPublicKeyUseValues::Signature)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
241
            throw new JWSInvalidJWKException
242
            (
243
                sprintf
244
                (
245
                    'use %s not supported ',
246
                    $this->jwk->getKeyUse()->getString()
247
                )
248
            );
249
250
        if(is_null($this->jwk->getAlgorithm()))
251
            throw new InvalidJWKAlgorithm('algorithm intended for use with the key is not set! ');
252
253
        if(!is_null($this->jwk->getId()) && !is_null($this->header->getKeyID()) && $this->header->getKeyID()->getValue() != $this->jwk->getId()->getValue())
254
            throw new JWSInvalidJWKException
255
            (
256
                sprintf
257
                (
258
                    'original kid %s - current kid %s',
259
                    $this->header->getKeyID()->getValue(),
260
                    $this->jwk->getId()->getValue()
261
                )
262
            );
263
264
        $alg = DigitalSignatures_MACs_Registry::getInstance()->get($original_alg);
265
266
        if(is_null($alg))
267
            throw new JWSNotSupportedAlgorithm(sprintf('algo %s', $original_alg));
268
269
        $former_alg = $this->header->getAlgorithm()->getString();
270
271
        if($former_alg != $original_alg)
272
            throw new JWSNotSupportedAlgorithm
273
            (
274
                sprintf
275
                (
276
                    'former alg %s - original alg %s',
277
                    $former_alg,
278
                    $original_alg
279
                )
280
            );
281
282
        if($this->jwk->getAlgorithm()->getValue() !==  $original_alg)
283
            throw new InvalidJWKAlgorithm
284
            (
285
                sprintf
286
                (
287
                    'mismatch between algorithm intended for use with the key %s and the cryptographic algorithm used to secure the JWS %s',
288
                    $this->jwk->getAlgorithm()->getValue(),
289
                    $original_alg
290
                )
291
            );
292
293
        $secured_input_bytes = JOSEHeaderSerializer::serialize($this->header) . IBasicJWT::SegmentSeparator .$this->getEncodedPayload();
294
295
        // use public key / secret
296
        $key = $this->jwk->getKey(JSONWebKeyKeyOperationsValues::VerifyDigitalSignatureOrMAC);
297
        return $alg->verify($key, $secured_input_bytes, $this->signature);
298
    }
299
300
    /**
301
     * @return IJWSPayloadSpec
302
     */
303
    public function getPayload()
304
    {
305
        return $this->payload;
306
    }
307
308
     /**
309
     * @param IJOSEHeader $header
310
     * @param IJWSPayloadSpec $payload
311
     * @param string $signature
312
     * @return IJWS
313
     */
314
    static public function fromHeaderClaimsAndSignature(IJOSEHeader $header, IJWSPayloadSpec $payload = null , $signature = '')
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
315
    {
316
        return new JWS($header, $payload, $signature );
317
    }
318
319
    /**
320
     * @return array
321
     */
322
    public function take()
323
    {
324
        $payload = ($this->payload instanceof IJWSPayloadRawSpec) ?  $this->payload->getRaw() : $this->claim_set;
325
        return array
326
        (
327
            $this->header,
328
            $payload,
329
            $this->signature
330
        );
331
    }
332
}