Failed Conditions
Push — v7 ( 318c5f...2b71c7 )
by Florent
03:43
created

Signer::getInputToSign()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.2
c 0
b 0
f 0
cc 4
eloc 8
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 Base64Url\Base64Url;
15
use Jose\Algorithm\JWAManager;
16
use Jose\Algorithm\SignatureAlgorithmInterface;
17
use Jose\Factory\AlgorithmManagerFactory;
18
use Jose\Object\JWKInterface;
19
use Jose\Object\JWSInterface;
20
use Jose\Object\Signature;
21
use Jose\Object\SignatureInterface;
22
23
final class Signer
24
{
25
    /**
26
     * @var JWAManager
27
     */
28
    private $jwaManager;
29
30
    use Behaviour\HasKeyChecker;
31
    use Behaviour\CommonSigningMethods;
32
33
    /**
34
     * Signer constructor.
35
     *
36
     * @param string[]|SignatureAlgorithmInterface[] $signature_algorithms
37
     */
38
    public function __construct(array $signature_algorithms)
39
    {
40
        $this->setSignatureAlgorithms($signature_algorithms);
41
        $this->jwaManager = AlgorithmManagerFactory::createAlgorithmManager($signature_algorithms);
42
    }
43
44
    /**
45
     * @param array $signature_algorithms
46
     *
47
     * @return Signer
48
     */
49
    public static function createSigner(array $signature_algorithms): Signer
50
    {
51
        $signer = new self($signature_algorithms);
52
53
        return $signer;
54
    }
55
56
    /**
57
     * @param JWSInterface $jws
58
     */
59
    public function sign(JWSInterface &$jws)
60
    {
61
        $nb_signatures = $jws->countSignatures();
62
63
        for ($i = 0; $i < $nb_signatures; $i++) {
64
            $this->computeSignature($jws, $jws->getSignature($i));
65
        }
66
    }
67
68
    /**
69
     * @param JWSInterface       $jws
70
     * @param SignatureInterface $signature
71
     */
72
    private function computeSignature(JWSInterface $jws, SignatureInterface &$signature)
73
    {
74
        if (null === $signature->getSignatureKey()) {
75
            return;
76
        }
77
        $this->checkKeyUsage($signature->getSignatureKey(), 'signature');
78
79
        $signature_algorithm = $this->getSignatureAlgorithm($signature->getAllHeaders(), $signature->getSignatureKey());
80
81
        $input = $this->getInputToSign($jws, $signature);
82
83
        $value = $signature_algorithm->sign(
84
            $signature->getSignatureKey(),
85
            $input
86
        );
87
88
        $signature = Signature::createSignatureFromLoadedData(
89
            $value,
90
            $signature->getEncodedProtectedHeaders(),
91
            $signature->getHeaders()
92
        );
93
    }
94
95
    /**
96
     * @param JWSInterface       $jws
97
     * @param SignatureInterface $signature
98
     *
99
     * @return string
100
     */
101
    private function getInputToSign(JWSInterface $jws, SignatureInterface $signature): string
102
    {
103
        $this->checkB64HeaderAndCrit($signature);
104
        $encoded_protected_headers = $signature->getEncodedProtectedHeaders();
105
        $payload = $jws->getPayload();
106
        if (!$signature->hasProtectedHeader('b64') || true === $signature->getProtectedHeader('b64')) {
107
            $encoded_payload = Base64Url::encode(is_string($payload) ? $payload : json_encode($payload));
108
109
            return sprintf('%s.%s', $encoded_protected_headers, $encoded_payload);
110
        }
111
112
        return sprintf('%s.%s', $encoded_protected_headers, $payload);
113
    }
114
115
    /**
116
     * @param SignatureInterface $signature
117
     *
118
     * @throws \InvalidArgumentException
119
     */
120
    private function checkB64HeaderAndCrit(SignatureInterface $signature)
121
    {
122
        if (!$signature->hasProtectedHeader('b64')) {
123
            return;
124
        }
125
126
        if (false == $signature->hasProtectedHeader('crit')) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
127
            throw new \InvalidArgumentException('The protected header parameter "crit" is mandatory when protected header parameter "b64" is set.');
128
        }
129
        $critHeader = $signature->getProtectedHeader('crit');
130
        if (!is_array($critHeader)) {
131
            throw new \InvalidArgumentException('The protected header parameter "crit" must be an array.');
132
        }
133
        if (!in_array('b64', $critHeader)) {
134
            throw new \InvalidArgumentException('The protected header parameter "crit" must contain "b64" when protected header parameter "b64" is set.');
135
        }
136
    }
137
138
    /**
139
     * @param array        $complete_headers The complete headers
140
     * @param JWKInterface $key
141
     *
142
     * @return SignatureAlgorithmInterface
143
     */
144
    private function getSignatureAlgorithm(array $complete_headers, JWKInterface $key): SignatureAlgorithmInterface
145
    {
146
        if (!array_key_exists('alg', $complete_headers)) {
147
            throw new \InvalidArgumentException('No "alg" parameter set in the headers.');
148
        }
149
        if ($key->has('alg') && $key->get('alg') !== $complete_headers['alg']) {
150
            throw new \InvalidArgumentException(sprintf('The algorithm "%s" is not allowed with this key.', $complete_headers['alg']));
151
        }
152
153
        $algorithm = $this->jwaManager->getAlgorithm($complete_headers['alg']);
154
        if (!$algorithm instanceof SignatureAlgorithmInterface) {
155
            throw new \InvalidArgumentException(sprintf('The algorithm "%s" is not supported or is not a signature algorithm.', $complete_headers['alg']));
156
        }
157
158
        return $algorithm;
159
    }
160
}
161