Completed
Push — master ( cd6056...0428a3 )
by Florent
02:54
created

Signer::createSignature()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 26
rs 8.8571
cc 3
eloc 17
nc 4
nop 4
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\HasJWAManager;
17
use Jose\Behaviour\HasKeyChecker;
18
use Jose\Behaviour\HasLogger;
19
use Jose\Object\JWKInterface;
20
use Jose\Object\JWSInterface;
21
use Jose\Object\Signature;
22
use Psr\Log\LoggerInterface;
23
use Psr\Log\LogLevel;
24
25
/**
26
 */
27
final class Signer implements SignerInterface
28
{
29
    use HasKeyChecker;
30
    use HasJWAManager;
31
    use HasLogger;
32
33
    /**
34
     * Signer constructor.
35
     *
36
     * @param \Jose\Algorithm\JWAManagerInterface $jwa_manager
37
     * @param \Psr\Log\LoggerInterface|null       $logger
38
     */
39
    public function __construct(JWAManagerInterface $jwa_manager,
40
                                LoggerInterface $logger = null
41
    ) {
42
        $this->setJWAManager($jwa_manager);
43
44
        if (null !== $logger) {
45
            $this->setLogger($logger);
46
        }
47
    }
48
49
    /**
50
     * {@inheritdoc}
51
     */
52
    public function addSignatureWithDetachedPayload(JWSInterface &$jws, JWKInterface $key, $detached_payload, array $protected_headers = [], array $headers = [])
53
    {
54
        $this->log(LogLevel::INFO, 'Trying to add a signature to the JWS object with detached payload', ['jws' => $jws, 'key' => $key, 'payload' => $detached_payload, 'protected' => $protected_headers, 'header' => $headers]);
55
        $signature = $this->createSignature($key, $detached_payload, $protected_headers, $headers);
56
57
        $jws = $jws->addSignature($signature);
58
        $this->log(LogLevel::INFO, 'Signature added');
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64
    public function addSignature(JWSInterface &$jws, JWKInterface $key, array $protected_headers = [], array $headers = [])
65
    {
66
        $this->log(LogLevel::INFO, 'Trying to add a signature to the JWS object', ['jws' => $jws, 'key' => $key, 'protected' => $protected_headers, 'header' => $headers]);
67
        if (null === $jws->getEncodedPayload()) {
68
            throw new \InvalidArgumentException('No payload.');
69
        }
70
        $signature = $this->createSignature($key, $jws->getEncodedPayload(), $protected_headers, $headers);
71
72
        $jws = $jws->addSignature($signature);
73
        $this->log(LogLevel::INFO, 'Signature added');
74
    }
75
76
    /**
77
     * @param \Jose\Object\JWKInterface $key
78
     * @param string                    $payload
79
     * @param array                     $protected_headers
80
     * @param array                     $headers
81
     *
82
     * @return \Jose\Object\Signature|\Jose\Object\SignatureInterface
83
     */
84
    private function createSignature(JWKInterface $key, $payload, array $protected_headers, array $headers)
85
    {
86
        $this->log(LogLevel::DEBUG, 'Creation of the signature');
87
        $this->checkKeyUsage($key, 'signature');
88
        $signature = new Signature();
89
        if (!empty($protected_headers)) {
90
            $this->log(LogLevel::DEBUG, 'The signature has of a protected header', ['protected' => $protected_headers]);
91
            $signature = $signature->withProtectedHeaders($protected_headers);
92
        }
93
        if (!empty($headers)) {
94
            $this->log(LogLevel::DEBUG, 'The signature has of a header', ['header' => $headers]);
95
            $signature = $signature->withHeaders($headers);
96
        }
97
98
        $signature_algorithm = $this->getSignatureAlgorithm($signature->getAllHeaders(), $key);
99
100
        $this->log(LogLevel::DEBUG, 'Trying to compute the signature');
101
        $value = $signature_algorithm->sign($key, $signature->getEncodedProtectedHeaders().'.'.$payload);
102
        $this->log(LogLevel::DEBUG, 'Signature computation done');
103
104
        $signature = $signature->withSignature($value);
105
106
        $this->log(LogLevel::DEBUG, 'The signature is done', ['signature' => $signature]);
107
108
        return $signature;
109
    }
110
111
    /**
112
     * @param array                     $complete_header The complete header
113
     * @param \Jose\Object\JWKInterface $key
114
     *
115
     * @return \Jose\Algorithm\SignatureAlgorithmInterface
116
     */
117
    private function getSignatureAlgorithm(array $complete_header, JWKInterface $key)
118
    {
119
        $this->log(LogLevel::DEBUG, 'Trying to find the algorithm used to sign');
120
        if (!array_key_exists('alg', $complete_header)) {
121
            $this->log(LogLevel::ERROR, 'No "alg" parameter set in the header');
122
            throw new \InvalidArgumentException('No "alg" parameter set in the header.');
123
        }
124
        $this->log(LogLevel::DEBUG, 'The algorithm is {alg}', ['alg' => $complete_header['alg']]);
125
126
        if ($key->has('alg') && $key->get('alg') !== $complete_header['alg']) {
127
            $this->log(LogLevel::ERROR, 'The algorithm {alg} is allowed with this key', ['alg' => $complete_header['alg']]);
128
            throw new \InvalidArgumentException(sprintf('The algorithm "%s" is allowed with this key.', $complete_header['alg']));
129
        }
130
131
        $signature_algorithm = $this->getJWAManager()->getAlgorithm($complete_header['alg']);
132
        if (!$signature_algorithm instanceof SignatureAlgorithmInterface) {
133
            $this->log(LogLevel::ERROR, 'The algorithm {alg} is not supported', ['alg' => $complete_header['alg']]);
134
            throw new \InvalidArgumentException(sprintf('The algorithm "%s" is not supported.', $complete_header['alg']));
135
        }
136
137
        $this->log(LogLevel::DEBUG, 'The algorithm {alg} is supported', ['alg' => $complete_header['alg'], 'handler' => $signature_algorithm]);
138
139
        return $signature_algorithm;
140
    }
141
}
142