-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathUpstreamMessage.php
118 lines (98 loc) · 4.26 KB
/
UpstreamMessage.php
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
<?php
namespace HMS\PushKit;
use InvalidArgumentException;
use stdClass;
/**
* Class HMS PushKit Upstream Message
*
* @link https://developer.huawei.com/consumer/en/doc/development/HMSCore-References/https-uplink-api-0000001050170915
* @author Martin Zeitler
*/
class UpstreamMessage {
private const ENV_VAR_HUAWEI_HMAC_VERIFICATION_KEY = 'Variable ENV_VAR_HUAWEI_HMAC_VERIFICATION_KEY is not set.';
private string|null $raw_body = null;
private string|null $hmac_verification_key;
private string|null $sender_token = null;
private string|null $package_name = null;
private string|null $message_id = null;
private string|false $message_data = false;
/** Constructor */
public function __construct( string|null $key ) {
if ($key == null) {
throw new InvalidArgumentException(self::ENV_VAR_HUAWEI_HMAC_VERIFICATION_KEY );
}
$this->hmac_verification_key = $key;
// $this->parse_request_body();
}
public function getRawBody(): string|null {
return $this->raw_body;
}
public function getSenderToken(): string|null {
return $this->sender_token;
}
public function getPackageName(): string|null {
return $this->package_name;
}
public function getMessageId(): string|null {
return $this->message_id;
}
public function getMessageData(): string|null {
return $this->message_data;
}
/**
* @param string $from Token of the app that sends uplink messages.
* @param string $category Package name of the app that sends uplink messages.
* @param string $message_id Unique ID generated by the app for a message.
* @param string $data Valid key-value pairs contained in the message.
* The parameter value needs to be encoded using Base64.
*/
private function parse_request_body(): bool {
if (isset($_SERVER['X-HW-SIGNATURE']) && !empty($_SERVER['X-HW-SIGNATURE'])) {
$this->raw_body = stream_get_contents(STDIN);
if ($this->hmac_verify( $this->raw_body, $_SERVER['X-HW-SIGNATURE'] )) {
$payload = json_decode( $this->raw_body );
$this->sender_token = $payload->from;
$this->package_name = $payload->category;
$this->message_id = $payload->message_id;
$this->message_data = base64_decode($payload->data);
return true;
}
}
return false;
}
/** Concatenate the input string, generate HMAC hash with SHA256 algorithm, then encode as base64. */
private function generate_signature( int $timestamp, string $nonce, string $data_str ): string {
$input = $timestamp.$nonce.$data_str;
$hmac = hash_hmac( 'sha256', $input, $this->hmac_verification_key );
return base64_encode( $hmac );
}
/** Convert the received signature string to object. */
private function parse_signature( string $signature ): stdClass {
$input = str_getcsv( $signature, '; ' );
$data = new stdClass();
$data->timestamp = (int) str_replace('timestamp=', '', $input[0]);
$data->nonce = (string) str_replace( ' nonce=', '', $input[1]);
$data->value = (string) str_replace( ' value=', '', $input[2]);
return $data;
}
public function hmac_verify( string|null $raw_body, string $signature ): bool {
if ($raw_body == null) {return false;}
/* Extract data-string from the raw body. */
$payload = json_decode( $raw_body );
$data_str = base64_decode( $payload->data );
/* Convert the received signature string to object. */
$signature = $this->parse_signature( $signature );
/* Generate a signature which to compare to. */
$generated = $this->generate_signature( $signature->timestamp, $signature->nonce, $data_str );
/* Compare the generated with the received signature. */
return $generated === $signature->value;
}
public function asObject(): object {
return (object) [
'sender_token' => $this->sender_token,
'package_name' => $this->package_name,
'message_id' => $this->message_id,
'message_data' => $this->message_data
];
}
}