Completed
Pull Request — master (#3)
by Marcel
06:57 queued 04:01
created

Authenticator::doHMACSignature()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 2
crap 1
1
<?php
2
3
namespace UMA\Psr\Http\Message\HMAC;
4
5
use Psr\Http\Message\MessageInterface;
6
use Psr\Http\Message\RequestInterface;
7
use UMA\Psr\Http\Message\Serializer\MessageSerializer;
8
9
class Authenticator
10
{
11
    /**
12
     * @var Calculator
13
     */
14
    private $calculator;
15
16 72
    public function __construct()
17
    {
18 72
        $this->calculator = new Calculator();
19 72
    }
20
21
    /**
22
     * @param MessageInterface $message
23
     * @param string           $secret
24
     *
25
     * @return MessageInterface The signed message.
26
     *
27
     * @throws \InvalidArgumentException When $message is an implementation of
28
     *                                   MessageInterface that cannot be
29
     *                                   serialized and thus neither signed.
30
     */
31 70
    public function sign(MessageInterface $message, $secret)
32
    {
33 70
        $preSignedMessage = $message->withHeader(
34 70
            Specification::SIGN_HEADER,
35 70
            $this->getSignedHeadersString($message)
36 70
        );
37
38 70
        $serialization = MessageSerializer::serialize($preSignedMessage);
39
40 70
        return $preSignedMessage->withHeader(
41 70
            Specification::AUTH_HEADER,
42 70
            Specification::AUTH_PREFIX.' '.$this->calculator->hmac($serialization, $secret)
43 70
        );
44
    }
45
46
    /**
47
     * @param MessageInterface $message
48
     * @param string           $secret
49
     *
50
     * @return bool Signature verification outcome.
51
     *
52
     * @throws \InvalidArgumentException When $message is an implementation of
53
     *                                   MessageInterface that cannot be
54
     *                                   serialized and thus neither verified.
55
     */
56 72
    public function verify(MessageInterface $message, $secret)
57
    {
58 72
        if (0 === preg_match(
59 72
            '#^'.Specification::AUTH_PREFIX.' ([+/0-9A-Za-z]{43}=)$#',
60 72
            $message->getHeaderLine(Specification::AUTH_HEADER), $matches)
61 72
        ) {
62 2
            return false;
63
        }
64
65 70
        $signedHeaders = array_filter(explode(',', $message->getHeaderLine(Specification::SIGN_HEADER)));
66
67 70
        foreach ($message->getHeaders() as $name => $value) {
68 70
            if (!in_array(mb_strtolower($name), $signedHeaders)) {
69 70
                $message = $message->withoutHeader($name);
70 70
            }
71 70
        }
72
73 70
        $clientSideSignature = $matches[1];
74
75 70
        $serverSideSignature = $this->calculator
76 70
            ->hmac(MessageSerializer::serialize($message), $secret);
77
78 70
        return hash_equals($serverSideSignature, $clientSideSignature);
79
    }
80
81
    /**
82
     * @param MessageInterface $message
83
     *
84
     * @return string
85
     */
86 70
    private function getSignedHeadersString(MessageInterface $message)
87
    {
88 70
        $headers = array_keys(array_change_key_case($message->getHeaders(), CASE_LOWER));
89 70
        array_push($headers, mb_strtolower(Specification::SIGN_HEADER));
90
91
        // Some of the tested RequestInterface implementations do not include
92
        // the Host header in $message->getHeaders(), so it is explicitly set when needed
93 70
        if ($message instanceof RequestInterface && !in_array('host', $headers)) {
94 12
            array_push($headers, 'host');
95 12
        }
96
97
        // There is no guarantee about the order of the headers returned by
98
        // $message->getHeaders(), so they are explicitly sorted in order
99
        // to produce the exact same string regardless of the underlying implementation
100 70
        sort($headers);
101
102 70
        return implode(',', $headers);
103
    }
104
}
105