Completed
Push — master ( 05e2ef...22b3b9 )
by Florent
02:34
created

Signer::getSignatureAlgorithm()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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