CWIS Developer Documentation

- Home
- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

RestAPIHelper.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: RestAPIHelper
4 #
5 # Part of the Collection Workflow Integration System (CWIS)
6 # Copyright 2017 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu/cwis/
8 #
9
10 class RestAPIHelper
11 {
22 public function __construct($APIUrl, $APIPassword,
23 $CheckForDuplicateFn, $RegisterMessageFn)
24 {
25 $this->APIUrl = $APIUrl;
26 $this->APIPassword = $APIPassword;
27 $this->CheckForDuplicateFn = $CheckForDuplicateFn;
28 $this->RegisterMessageFn = $RegisterMessageFn;
29 }
30
37 public function DoRestCommand($Params)
38 {
39 # build an encrypted message
40 $PostData = $this->EncodeEncryptedMessage($Params);
41
42 # set up curl to do the post
43 $Context = curl_init();
44
45 # enable cookie handling
46 curl_setopt($Context, CURLOPT_COOKIEFILE, '');
47
48 # use our configured endpoint
49 curl_setopt($Context, CURLOPT_URL, $this->APIUrl);
50
51 # get results back as a string
52 curl_setopt($Context, CURLOPT_RETURNTRANSFER, TRUE);
53
54 # send data in a POST
55 curl_setopt($Context, CURLOPT_POST, TRUE);
56
57 # load the POST data
58 curl_setopt($Context, CURLOPT_POSTFIELDS, http_build_query($PostData));
59
60 # fetch the data
61 $Data = curl_exec($Context);
62
63 # attempt to parse the reply into an encrypted envelope
64 $Result = json_decode($Data, TRUE);
65 if ($Result === NULL)
66 {
67 return array(
68 "Status" => "Error",
69 "Message" => "Could not parse PostData.",
70 "Data" => $Data,
71 );
72 }
73
74 # attempt to decode the encrypted envelope
75 $Result = $this->DecodeEncryptedMessage($Result);
76
77 # if we decoded the envelope, return the message contents
78 if ($Result["Status"] == "OK")
79 {
80 $Result = $Result["Data"];
81 }
82
83 return $Result;
84 }
85
91 public function EncodeEncryptedMessage($Data)
92 {
93 # create an envelope for our message, put the provided data inside
94 $Env = array();
95 $Env["Version"] = "3";
96 $Env["Timestamp"] = time();
97 $Env["Cookie"] = base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM));
98 $Env["Data"] = $Data;
99
100 # generate full key from provided password
101 $FullKey = hash("sha512", $this->APIPassword, TRUE);
102
103 # split into encryption and MAC keys
104 $EncKey = mb_substr($FullKey, 0, 32, '8bit');
105 $MacKey = mb_substr($FullKey, 32, 32, '8bit');
106
107 # generate a random IV (initialization vector), required by
108 # AES (and for most ciphers) to provide some randomness in the
109 # data and prevent identical messages from having identical
110 # encrypted content
111
112 # the mcrypt extension is deprecated, but unfortunately
113 # mcrypt_create_iv() is the only function available in both PHP
114 # 5.x and PHP 7.x that reliably provides randomness.
115 # see slides # 24-28 in "Using Encryption in PHP" at
116 # http://otscripts.com/wp-content/uploads/2016/10/UsingEncryption-v02.pdf
117
118 $IV = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
119
120 # encrypt and base64 our payload
121 $Payload = base64_encode(openssl_encrypt(
122 json_encode($Env), "aes-256-cbc", $EncKey, OPENSSL_RAW_DATA, $IV));
123
124 # base64 encode our IV
125 $IV = base64_encode($IV);
126
127 # construct data we will POST
128 $PostData = array(
129 "IV" => $IV,
130 "Payload" => $Payload,
131 "MAC" => base64_encode(hash_hmac("sha256", $IV.":".$Payload, $MacKey, TRUE)),
132 );
133
134 return $PostData;
135 }
136
146 public function DecodeEncryptedMessage($PostData)
147 {
148 # verify that the provided POST data has the correct elements
149 if (!isset($PostData["MAC"]) || !isset($PostData["Payload"]) ||
150 !isset($PostData["IV"]))
151 {
152 return array(
153 "Status" => "Error",
154 "Message" => "PostData lacks required elements.");
155 }
156
157 # generate full key from provided password
158 $FullKey = hash("sha512", $this->APIPassword, TRUE);
159
160 # split into encryption and MAC keys
161 $EncKey = mb_substr($FullKey, 0, 32, '8bit');
162 $MacKey = mb_substr($FullKey, 32, 32, '8bit');
163
164 # compute MAC
165 $MAC = hash_hmac(
166 "sha256", $PostData["IV"].":".$PostData["Payload"], $MacKey, TRUE);
167
168 # check MAC, bail if it was not valid
169 if (!hash_equals($MAC, base64_decode($PostData["MAC"])))
170 {
171 return array(
172 "Status" => "Error",
173 "Message" => "HMAC validation failure -- message is corrupted.");
174 }
175
176 # strip base64 encoding from payload and IV
177 $Payload = base64_decode($PostData["Payload"]);
178 $IV = base64_decode($PostData["IV"]);
179
180 # decrypt the payload to get the envelope
181 $Env = openssl_decrypt(
182 $Payload, "aes-256-cbc", $EncKey, OPENSSL_RAW_DATA, $IV);
183
184 # attempt to unserialize the envelope, bailing on failure
185 $Env = json_decode($Env, TRUE);
186 if ($Env === NULL)
187 {
188 return array(
189 "Status" => "Error",
190 "Message" => "Could not decode message envelope.");
191 }
192
193 # check that the envelope contains all the required headers
194 if (!isset($Env["Version"]) || !isset($Env["Timestamp"]) ||
195 !isset($Env["Cookie"]) || !isset($Env["Data"]) )
196 {
197 return array(
198 "Status" => "Error",
199 "Message" => "Payload did not include all required parameters.");
200 }
201
202 # check that this is an envelope in a version we understand
203 if ($Env["Version"] != "3")
204 {
205 return array(
206 "Status" => "Error",
207 "Message" => "Message was not version 3.");
208 }
209
210 # check that this envelope isn't too old
211 if (time() - $Env["Timestamp"] > 300)
212 {
213 return array(
214 "Status" => "Error",
215 "Message" => "Message is more than 5 minutes old.");
216 }
217
218 # check if this is a duplicate message
219 if (call_user_func($this->CheckForDuplicateFn,
220 $Env["Timestamp"], $Env["Cookie"]))
221 {
222 return array(
223 "Status" => "Error",
224 "Message" => "This is a duplicate message");
225 }
226
227 call_user_func($this->RegisterMessageFn,
228 $Env["Timestamp"], $Env["Cookie"]);
229
230 return array(
231 "Status" => "OK",
232 "Data" => $Env["Data"]);
233 }
234 }
RestAPIHelper\EncodeEncryptedMessage
EncodeEncryptedMessage($Data)
Construct an encrypted message packet from provided data.
Definition: RestAPIHelper.php:91
RestAPIHelper\DoRestCommand
DoRestCommand($Params)
Run a REST API command against a remote site.
Definition: RestAPIHelper.php:37
RestAPIHelper\__construct
__construct($APIUrl, $APIPassword, $CheckForDuplicateFn, $RegisterMessageFn)
Constructor.
Definition: RestAPIHelper.php:22
PHP
PHP
Definition: RSSClient--Test.php:12
RestAPIHelper\DecodeEncryptedMessage
DecodeEncryptedMessage($PostData)
Decrypt an encrypted message packet.
Definition: RestAPIHelper.php:146
RestAPIHelper
Definition: RestAPIHelper.php:10