-
Notifications
You must be signed in to change notification settings - Fork 151
Expand file tree
/
Copy pathSignatureVerifier.php
More file actions
176 lines (154 loc) · 5.63 KB
/
SignatureVerifier.php
File metadata and controls
176 lines (154 loc) · 5.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
<?php
/**
* This file is part of the Cloudinary PHP package.
*
* (c) Cloudinary
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cloudinary\Utils;
use Cloudinary\Configuration\Configuration;
use Cloudinary\Utils;
use InvalidArgumentException;
/**
* Class SignatureVerifier
*
* @internal
*/
class SignatureVerifier
{
/**
* @var array of parameter => allowed_types for notification signature validator
*/
private static array $NOTIFICATION_VALIDATOR_ALLOWED_TYPES
= [
'body' => 'string',
'timestamp' => 'int|string',
'signature' => 'string',
'validFor' => 'int|string',
];
/**
* @var array of parameter => allowed_types for API response signature validator
*/
private static array $API_RESPONSE_VALIDATOR_ALLOWED_TYPES
= [
'publicId' => 'string',
'version' => 'int|string',
'signature' => 'string',
];
/**
* Verifies the authenticity of a notification signature
*
* @param ?string $body Json of the request's body
* @param int|string|null $timestamp Unix timestamp. Can be retrieved from the X-Cld-Timestamp header
* @param ?string $signature Actual signature. Can be retrieved from the X-Cld-Signature header
* @param int|string $validFor The desired time in seconds for considering the request valid
*
*
* @throws InvalidArgumentException In case a mandatory parameter is empty or of wrong type
*/
public static function verifyNotificationSignature(
?string $body,
int|string|null $timestamp,
string|null $signature,
int|string $validFor = 7200
): bool {
$paramsArray = [
'body' => $body,
'timestamp' => $timestamp,
'signature' => $signature,
'validFor' => $validFor,
];
self::validateParams($paramsArray, self::$NOTIFICATION_VALIDATOR_ALLOWED_TYPES);
if (Utils::unixTimeNow() - $timestamp > $validFor) {
return false;
}
$apiSecret = Configuration::instance()->cloud->apiSecret;
self::validateApiSecret($apiSecret);
$payloadToSign = $body . $timestamp;
$hmac = self::generateHmac($payloadToSign, $apiSecret);
return $hmac === $signature;
}
/**
* Verifies the authenticity of an API response signature
*
* @param string $publicId The public id of the asset as returned in the API response
* @param int|string $version The version of the asset as returned in the API response
* @param string $signature Actual signature. Can be retrieved from the X-Cld-Signature header
*
*
* @throws InvalidArgumentException in case a mandatory parameter is empty or of wrong type
*/
public static function verifyApiResponseSignature(string $publicId, int|string $version, string $signature): bool
{
$paramsArray = ['publicId' => $publicId, 'version' => $version, 'signature' => $signature];
self::validateParams($paramsArray, self::$API_RESPONSE_VALIDATOR_ALLOWED_TYPES);
$apiSecret = Configuration::instance()->cloud->apiSecret;
self::validateApiSecret($apiSecret);
$payloadToSign = 'public_id=' . $publicId . '&version=' . $version;
$hmac = self::generateHmac($payloadToSign, $apiSecret);
return $hmac === $signature;
}
/**
* Validates parameters
*
* @param array $params Parameters to validate
* @param array $allowedTypes The allowed type/s of the parameters. Pipe delimiter for multiple values
*
* @throws InvalidArgumentException In case a mandatory parameter is empty or of wrong type
*/
private static function validateParams(array $params, array $allowedTypes): void
{
foreach ($allowedTypes as $param => $types) {
if (empty($params[$param])) {
throw new InvalidArgumentException("$param parameter cannot be empty");
}
if (! self::paramValidator($params[$param], $types)) {
throw new InvalidArgumentException("$param must be one of the following types: $types");
}
}
}
/**
* Validates the type of single parameter
*
* @param mixed $param Parameter to validate
* @param string $type The allowed type/s of the parameter. Pipe delimiter for multiple values
*
*/
private static function paramValidator(mixed $param, string $type): bool
{
$allowedTypes = explode('|', $type);
foreach ($allowedTypes as $allowedType) {
$validationFunction = 'is_' . $allowedType;
if ($validationFunction($param)) {
return true;
}
}
return false;
}
/**
* Validates API secret
*
* @param ?string $apiSecret The API secret
*
* @throws InvalidArgumentException In case API secret is missing or invalid
*/
private static function validateApiSecret(?string $apiSecret): void
{
if (empty($apiSecret) || ! is_string($apiSecret)) {
throw new InvalidArgumentException('API Secret is invalid');
}
}
/**
* Generates hmac to compare against signature
*
* @param string $payloadToSign The payload to sign
* @param string $apiSecret The API secret
*
*/
public static function generateHmac(string $payloadToSign, string $apiSecret): string
{
return sha1($payloadToSign . $apiSecret);
}
}