Signer::verifySignature()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
/**
4
 * Copyright 2014 SURFnet bv
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
namespace Surfnet\YubikeyApiClient\Crypto;
20
21
use Surfnet\YubikeyApiClient\Exception\InvalidArgumentException;
22
23
class Signer
24
{
25
    /**
26
     * @var array Valid parameters in the response message
27
     */
28
    private static $validResponseParams = [
29
        'nonce',
30
        'otp',
31
        'sessioncounter',
32
        'sessionuse',
33
        'sl',
34
        'status',
35
        't',
36
        'timeout',
37
        'timestamp'
38
    ];
39
40
    /**
41
     * @var string The base64-decoded client secret
42
     */
43
    private $clientSecret;
44
45
    /**
46
     * @param string $clientSecret The base64-encoded client secret
47
     */
48
    public function __construct($clientSecret)
49
    {
50
        if (!is_string($clientSecret)) {
51
            throw new InvalidArgumentException('Client secret must be string.');
52
        }
53
54
        $this->clientSecret = base64_decode($clientSecret);
55
56
        if (!is_string($this->clientSecret) || base64_encode($this->clientSecret) !== $clientSecret) {
57
            throw new InvalidArgumentException('Given client secret is not a base64-decodable string.');
58
        }
59
    }
60
61
    /**
62
     * Signs an array by calculating a signature and setting it on the 'h' key.
63
     *
64
     * @param array $data
65
     * @return array
66
     */
67
    public function sign(array $data)
68
    {
69
        ksort($data);
70
71
        $queryString = $this->buildQueryString($data);
72
        $data['h'] = base64_encode(hash_hmac('sha1', $queryString, $this->clientSecret, true));
73
74
        return $data;
75
    }
76
77
    /**
78
     * Verifies that the signature in the 'h' key matches the expected signature.
79
     *
80
     * @param array $data
81
     * @return bool
82
     */
83
    public function verifySignature(array $data)
84
    {
85
        $signedData = array_intersect_key($data, array_flip(self::$validResponseParams));
86
        ksort($signedData);
87
88
        $queryString = $this->buildQueryString($signedData);
89
        $signature = base64_encode(hash_hmac('sha1', $queryString, $this->clientSecret, true));
90
91
        return hash_equals($signature, $data['h']);
92
    }
93
94
    /**
95
     * @param array $query
96
     * @return string
97
     */
98
    private function buildQueryString(array $query)
99
    {
100
        $queryString = '';
101
102
        foreach ($query as $key => $value) {
103
            $queryString .= '&' . sprintf('%s=%s', $key, $value);
104
        }
105
106
        return substr($queryString, 1);
107
    }
108
}
109