Completed
Pull Request — master (#106)
by Florent
03:30 queued 32s
created

Signer::computeSignature()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 3 Features 0
Metric Value
c 7
b 3
f 0
dl 0
loc 23
rs 9.0856
cc 2
eloc 13
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 Assert\Assertion;
15
use Base64Url\Base64Url;
16
use Jose\Algorithm\SignatureAlgorithmInterface;
17
use Jose\Behaviour\CommonSigningMethods;
18
use Jose\Behaviour\HasJWAManager;
19
use Jose\Behaviour\HasKeyChecker;
20
use Jose\Factory\AlgorithmManagerFactory;
21
use Jose\Object\JWKInterface;
22
use Jose\Object\JWSInterface;
23
use Jose\Object\Signature;
24
use Jose\Object\SignatureInterface;
25
26
final class Signer implements SignerInterface
27
{
28
    use HasKeyChecker;
29
    use HasJWAManager;
30
    use CommonSigningMethods;
31
32
    /**
33
     * Signer constructor.
34
     *
35
     * @param string[]|\Jose\Algorithm\SignatureAlgorithmInterface[] $signature_algorithms
36
     */
37
    public function __construct(array $signature_algorithms)
38
    {
39
        $this->setSignatureAlgorithms($signature_algorithms);
40
41
        $this->setJWAManager(AlgorithmManagerFactory::createAlgorithmManager($signature_algorithms));
42
    }
43
44
    /**
45
     * {@inheritdoc}
46
     */
47
    public static function createSigner(array $signature_algorithms)
48
    {
49
        $signer = new self($signature_algorithms);
50
51
        return $signer;
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57
    public function sign(JWSInterface &$jws)
58
    {
59
        $nb_signatures = $jws->countSignatures();
60
61
        for ($i = 0; $i < $nb_signatures; $i++) {
62
            $this->computeSignature($jws, $jws->getSignature($i));
63
        }
64
    }
65
66
    /**
67
     * @param \Jose\Object\JWSInterface       $jws
68
     * @param \Jose\Object\SignatureInterface $signature
69
     */
70
    private function computeSignature(JWSInterface $jws, SignatureInterface &$signature)
71
    {
72
        if (null === $signature->getSignatureKey()) {
73
            return;
74
        }
75
        $this->checkKeyUsage($signature->getSignatureKey(), 'signature');
76
77
        $signature_algorithm = $this->getSignatureAlgorithm($signature->getAllHeaders(), $signature->getSignatureKey());
78
79
        $input = $this->getInputToSign($jws, $signature);
80
81
        $value = $signature_algorithm->sign(
82
            $signature->getSignatureKey(),
83
            $input
84
        );
85
86
87
        $signature = Signature::createSignatureFromLoadedData(
88
            $value,
89
            $signature->getEncodedProtectedHeaders(),
90
            $signature->getHeaders()
91
        );
92
    }
93
94
    /**
95
     * @param \Jose\Object\JWSInterface       $jws
96
     * @param \Jose\Object\SignatureInterface $signature
97
     *
98
     * @return string
99
     */
100
    private function getInputToSign(JWSInterface $jws, SignatureInterface $signature)
101
    {
102
        $this->checkB64HeaderAndCrit($signature);
103
        $encoded_protected_headers = $signature->getEncodedProtectedHeaders();
104
        $payload = $jws->getPayload();
105
        if (!$signature->hasProtectedHeader('b64') || true === $signature->getProtectedHeader('b64')) {
106
            $encoded_payload = Base64Url::encode(is_string($payload) ? $payload : json_encode($payload));
107
108
            return sprintf('%s.%s', $encoded_protected_headers, $encoded_payload);
109
        }
110
111
        return sprintf('%s.%s', $encoded_protected_headers, $payload);
112
    }
113
114
    /**
115
     * @param \Jose\Object\SignatureInterface $signature
116
     *
117
     * @throws \InvalidArgumentException
118
     */
119
    private function checkB64HeaderAndCrit(SignatureInterface $signature)
120
    {
121
        if (!$signature->hasProtectedHeader('b64')) {
122
            return;
123
        }
124
125
        Assertion::true($signature->hasProtectedHeader('crit'), 'The protected header parameter "crit" is mandatory when protected header parameter "b64" is set.');
126
        Assertion::isArray($signature->getProtectedHeader('crit'), 'The protected header parameter "crit" must be an array.');
127
        Assertion::inArray('b64', $signature->getProtectedHeader('crit'), 'The protected header parameter "crit" must contain "b64" when protected header parameter "b64" is set.');
128
    }
129
130
    /**
131
     * @param array                     $complete_header The complete header
132
     * @param \Jose\Object\JWKInterface $key
133
     *
134
     * @return \Jose\Algorithm\SignatureAlgorithmInterface
135
     */
136
    private function getSignatureAlgorithm(array $complete_header, JWKInterface $key)
137
    {
138
        Assertion::keyExists($complete_header, 'alg', 'No "alg" parameter set in the header.');
139
140
        Assertion::false(
141
            $key->has('alg') && $key->get('alg') !== $complete_header['alg'],
142
            sprintf('The algorithm "%s" is not allowed with this key.', $complete_header['alg'])
143
        );
144
145
        $signature_algorithm = $this->getJWAManager()->getAlgorithm($complete_header['alg']);
146
        Assertion::isInstanceOf($signature_algorithm, SignatureAlgorithmInterface::class, sprintf('The algorithm "%s" is not supported.', $complete_header['alg']));
147
148
        return $signature_algorithm;
149
    }
150
}
151