1<?php
2
3namespace OAuth\OAuth1\Signature;
4
5use OAuth\Common\Consumer\CredentialsInterface;
6use OAuth\Common\Http\Uri\UriInterface;
7use OAuth\OAuth1\Signature\Exception\UnsupportedHashAlgorithmException;
8
9class Signature implements SignatureInterface
10{
11    /**
12     * @var Credentials
13     */
14    protected $credentials;
15
16    /**
17     * @var string
18     */
19    protected $algorithm;
20
21    /**
22     * @var string
23     */
24    protected $tokenSecret = null;
25
26    /**
27     * @param CredentialsInterface $credentials
28     */
29    public function __construct(CredentialsInterface $credentials)
30    {
31        $this->credentials = $credentials;
32    }
33
34    /**
35     * @param string $algorithm
36     */
37    public function setHashingAlgorithm($algorithm)
38    {
39        $this->algorithm = $algorithm;
40    }
41
42    /**
43     * @param string $token
44     */
45    public function setTokenSecret($token)
46    {
47        $this->tokenSecret = $token;
48    }
49
50    /**
51     * @param UriInterface $uri
52     * @param array        $params
53     * @param string       $method
54     *
55     * @return string
56     */
57    public function getSignature(UriInterface $uri, array $params, $method = 'POST')
58    {
59        parse_str($uri->getQuery(), $queryStringData);
60
61        foreach (array_merge($queryStringData, $params) as $key => $value) {
62            $signatureData[rawurlencode($key)] = rawurlencode($value);
63        }
64
65        ksort($signatureData);
66
67        // determine base uri
68        $baseUri = $uri->getScheme() . '://' . $uri->getRawAuthority();
69
70        if ('/' === $uri->getPath()) {
71            $baseUri .= $uri->hasExplicitTrailingHostSlash() ? '/' : '';
72        } else {
73            $baseUri .= $uri->getPath();
74        }
75
76        $baseString = strtoupper($method) . '&';
77        $baseString .= rawurlencode($baseUri) . '&';
78        $baseString .= rawurlencode($this->buildSignatureDataString($signatureData));
79
80        return base64_encode($this->hash($baseString));
81    }
82
83    /**
84     * @param array $signatureData
85     *
86     * @return string
87     */
88    protected function buildSignatureDataString(array $signatureData)
89    {
90        $signatureString = '';
91        $delimiter = '';
92        foreach ($signatureData as $key => $value) {
93            $signatureString .= $delimiter . $key . '=' . $value;
94
95            $delimiter = '&';
96        }
97
98        return $signatureString;
99    }
100
101    /**
102     * @return string
103     */
104    protected function getSigningKey()
105    {
106        $signingKey = rawurlencode($this->credentials->getConsumerSecret()) . '&';
107        if ($this->tokenSecret !== null) {
108            $signingKey .= rawurlencode($this->tokenSecret);
109        }
110
111        return $signingKey;
112    }
113
114    /**
115     * @param string $data
116     *
117     * @return string
118     *
119     * @throws UnsupportedHashAlgorithmException
120     */
121    protected function hash($data)
122    {
123        switch (strtoupper($this->algorithm)) {
124            case 'HMAC-SHA1':
125                return hash_hmac('sha1', $data, $this->getSigningKey(), true);
126            default:
127                throw new UnsupportedHashAlgorithmException(
128                    'Unsupported hashing algorithm (' . $this->algorithm . ') used.'
129                );
130        }
131    }
132}
133