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/ 32 $CheckForDuplicateFn, $RegisterMessageFn)
34 $this->APIUrl = $APIUrl;
35 $this->APIPassword = $APIPassword;
36 $this->CheckForDuplicateFn = $CheckForDuplicateFn;
37 $this->RegisterMessageFn = $RegisterMessageFn;
48 # build an encrypted message 51 # set up curl to do the post 52 $Context = curl_init();
54 # enable cookie handling 55 curl_setopt($Context, CURLOPT_COOKIEFILE,
'');
57 # use our configured endpoint 58 curl_setopt($Context, CURLOPT_URL, $this->APIUrl);
60 # get results back as a string 61 curl_setopt($Context, CURLOPT_RETURNTRANSFER, TRUE);
64 curl_setopt($Context, CURLOPT_POST, TRUE);
67 curl_setopt($Context, CURLOPT_POSTFIELDS, http_build_query($PostData));
70 $Data = curl_exec($Context);
72 # attempt to parse the reply into an encrypted envelope 73 $Result = json_decode($Data, TRUE);
78 "Message" =>
"Could not parse PostData.",
83 # attempt to decode the encrypted envelope 86 # if we decoded the envelope, return the message contents 87 if ($Result[
"Status"] ==
"OK")
89 $Result = $Result[
"Data"];
102 # create an envelope for our message, put the provided data inside 104 $Env[
"Version"] =
"3";
105 $Env[
"Timestamp"] = time();
106 $Env[
"Cookie"] = base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM));
107 $Env[
"Data"] = $Data;
109 # generate full key from provided password 110 $FullKey = hash(
"sha512", $this->APIPassword, TRUE);
112 # split into encryption and MAC keys 113 $EncKey = mb_substr($FullKey, 0, 32,
'8bit');
114 $MacKey = mb_substr($FullKey, 32, 32,
'8bit');
116 # generate a random IV (initialization vector), required by 117 # AES (and for most ciphers) to provide some randomness in the 118 # data and prevent identical messages from having identical 121 # the mcrypt extension is deprecated, but unfortunately 122 # mcrypt_create_iv() is the only function available in both PHP 123 # 5.x and PHP 7.x that reliably provides randomness. 124 # see slides # 24-28 in "Using Encryption in PHP" at 125 # http://otscripts.com/wp-content/uploads/2016/10/UsingEncryption-v02.pdf 127 $IV = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
129 # encrypt and base64 our payload 130 $Payload = base64_encode(openssl_encrypt(
131 json_encode($Env),
"aes-256-cbc", $EncKey, OPENSSL_RAW_DATA, $IV));
133 # base64 encode our IV 134 $IV = base64_encode($IV);
136 # construct data we will POST 139 "Payload" => $Payload,
140 "MAC" => base64_encode(hash_hmac(
"sha256", $IV.
":".$Payload, $MacKey, TRUE)),
157 # verify that the provided POST data has the correct elements 158 if (!isset($PostData[
"MAC"]) || !isset($PostData[
"Payload"]) ||
159 !isset($PostData[
"IV"]))
163 "Message" =>
"PostData lacks required elements.");
166 # generate full key from provided password 167 $FullKey = hash(
"sha512", $this->APIPassword, TRUE);
169 # split into encryption and MAC keys 170 $EncKey = mb_substr($FullKey, 0, 32,
'8bit');
171 $MacKey = mb_substr($FullKey, 32, 32,
'8bit');
175 "sha256", $PostData[
"IV"].
":".$PostData[
"Payload"], $MacKey, TRUE);
177 # check MAC, bail if it was not valid 178 if (!hash_equals($MAC, base64_decode($PostData[
"MAC"])))
182 "Message" =>
"HMAC validation failure -- message is corrupted.");
185 # strip base64 encoding from payload and IV 186 $Payload = base64_decode($PostData[
"Payload"]);
187 $IV = base64_decode($PostData[
"IV"]);
189 # decrypt the payload to get the envelope 190 $Env = openssl_decrypt(
191 $Payload,
"aes-256-cbc", $EncKey, OPENSSL_RAW_DATA, $IV);
193 # attempt to unserialize the envelope, bailing on failure 194 $Env = json_decode($Env, TRUE);
199 "Message" =>
"Could not decode message envelope.");
202 # check that the envelope contains all the required headers 203 if (!isset($Env[
"Version"]) || !isset($Env[
"Timestamp"]) ||
204 !isset($Env[
"Cookie"]) || !isset($Env[
"Data"]) )
208 "Message" =>
"Payload did not include all required parameters.");
211 # check that this is an envelope in a version we understand 212 if ($Env[
"Version"] !=
"3")
216 "Message" =>
"Message was not version 3.");
219 # check that this envelope isn't too old 220 if (time() - $Env[
"Timestamp"] > 300)
224 "Message" =>
"Message is more than 5 minutes old.");
227 # check if this is a duplicate message 228 if (call_user_func($this->CheckForDuplicateFn,
229 $Env[
"Timestamp"], $Env[
"Cookie"]))
233 "Message" =>
"This is a duplicate message");
236 call_user_func($this->RegisterMessageFn,
237 $Env[
"Timestamp"], $Env[
"Cookie"]);
241 "Data" => $Env[
"Data"]);
EncodeEncryptedMessage($Data)
Construct an encrypted message packet from provided data.
DoRestCommand($Params)
Run a REST API command against a remote site.
__construct($APIUrl, $APIPassword, $CheckForDuplicateFn, $RegisterMessageFn)
Constructor.
DecodeEncryptedMessage($PostData)
Decrypt an encrypted message packet.
This class provides a general-purpose library for encrypted REST calls and responses.