Completed
Push — master ( 7aad34...96e43e )
by Florent
02:35
created

Verifier::checkPayload()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 3
eloc 3
nc 2
nop 2
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
        $this->checkSignaturess($jws);
71
72
        $nb_signatures = $jws->countSignatures();
73
74
        for ($i = 0; $i < $nb_signatures; $i++) {
75
            $signature = $jws->getSignature($i);
76
            $input = $signature->getEncodedProtectedHeaders().'.'.(null === $detached_payload ? $jws->getEncodedPayload() : $detached_payload);
77
78
            foreach ($jwk_set->getKeys() as $jwk) {
79
                $algorithm = $this->getAlgorithm($signature);
80
                try {
81
                    $this->checkKeyUsage($jwk, 'verification');
82
                    $this->checkKeyAlgorithm($jwk, $algorithm->getAlgorithmName());
83
                    if (true === $algorithm->verify($jwk, $input, $signature->getSignature())) {
84
                        $this->getCheckerManager()->checkJWT($jws);
85
86
                        return $i;
87
                    }
88
                } catch (\Exception $e) {
89
                    //We do nothing, we continue with other keys
90
                    continue;
91
                }
92
            }
93
        }
94
95
        return false;
96
    }
97
98
    /**
99
     * @param \Jose\Object\JWSInterface $jws
100
     */
101
    private function checkSignaturess(JWSInterface $jws)
102
    {
103
        if (0 === $jws->countSignatures()) {
104
            throw new \InvalidArgumentException('The JWS does not contain any signature.');
105
        }
106
    }
107
108
    /**
109
     * @param \Jose\Object\JWKSetInterface $jwk_set
110
     */
111
    private function checkJWKSet(JWKSetInterface $jwk_set)
112
    {
113
        if (0 === count($jwk_set)) {
114
            throw new \InvalidArgumentException('No key in the key set.');
115
        }
116
    }
117
118
    /**
119
     * @param \Jose\Object\JWSInterface $jws
120
     * @param null|string               $detached_payload
121
     */
122
    private function checkPayload(JWSInterface $jws, $detached_payload = null)
123
    {
124
        if (null !== $detached_payload && !empty($jws->getEncodedPayload())) {
125
            throw new \InvalidArgumentException('A detached payload is set, but the JWS already has a payload.');
126
        }
127
    }
128
129
    /**
130
     * @param \Jose\Object\SignatureInterface $signature
131
     *
132
     * @return \Jose\Algorithm\SignatureAlgorithmInterface|null
133
     */
134
    private function getAlgorithm(SignatureInterface $signature)
135
    {
136
        $complete_headers = array_merge(
137
            $signature->getProtectedHeaders(),
138
            $signature->getHeaders()
139
        );
140
        if (!array_key_exists('alg', $complete_headers)) {
141
            throw new \InvalidArgumentException('No "alg" parameter set in the header.');
142
        }
143
144
        $algorithm = $this->getJWAManager()->getAlgorithm($complete_headers['alg']);
145
        if (!$algorithm instanceof SignatureAlgorithmInterface) {
146
            throw new \RuntimeException(sprintf('The algorithm "%s" is not supported or does not implement SignatureInterface.', $complete_headers['alg']));
147
        }
148
149
        return $algorithm;
150
    }
151
}
152