1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @author Threema GmbH |
4
|
|
|
* @copyright Copyright (c) 2015-2016 Threema GmbH |
5
|
|
|
*/ |
6
|
|
|
|
7
|
|
|
namespace Threema\MsgApi\Tools; |
8
|
|
|
|
9
|
|
|
use Threema\Core\Exception; |
10
|
|
|
use Threema\Core\KeyPair; |
11
|
|
|
use Threema\Core\AssocArray; |
12
|
|
|
use Threema\MsgApi\Commands\Results\UploadFileResult; |
13
|
|
|
use Threema\MsgApi\Exceptions\BadMessageException; |
14
|
|
|
use Threema\MsgApi\Exceptions\DecryptionFailedException; |
15
|
|
|
use Threema\MsgApi\Exceptions\UnsupportedMessageTypeException; |
16
|
|
|
use Threema\MsgApi\Messages\DeliveryReceipt; |
17
|
|
|
use Threema\MsgApi\Messages\FileMessage; |
18
|
|
|
use Threema\MsgApi\Messages\ImageMessage; |
19
|
|
|
use Threema\MsgApi\Messages\TextMessage; |
20
|
|
|
use Threema\MsgApi\Messages\ThreemaMessage; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Interface CryptTool |
24
|
|
|
* Contains static methods to do various Threema cryptography related tasks. |
25
|
|
|
* |
26
|
|
|
* @package Threema\MsgApi\Tool |
27
|
|
|
*/ |
28
|
|
|
abstract class CryptTool { |
29
|
|
|
const TYPE_SODIUM = 'sodium'; |
30
|
|
|
const TYPE_SALT = 'salt'; |
31
|
|
|
|
32
|
|
|
const FILE_NONCE = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"; |
33
|
|
|
const FILE_THUMBNAIL_NONCE = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"; |
34
|
|
|
/** |
35
|
|
|
* @var CryptTool |
36
|
|
|
*/ |
37
|
|
|
private static $instance = null; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* Prior libsodium |
41
|
|
|
* |
42
|
|
|
* @return CryptTool |
43
|
|
|
*/ |
44
|
|
|
public static function getInstance() { |
45
|
|
|
if(null === self::$instance) { |
46
|
|
|
foreach(array( |
47
|
|
|
function() { |
48
|
|
|
return self::createInstance(self::TYPE_SODIUM); |
49
|
|
|
}, |
50
|
|
|
function() { |
51
|
|
|
return self::createInstance(self::TYPE_SALT); |
52
|
|
|
}) as $instanceGenerator) { |
53
|
|
|
$i = $instanceGenerator->__invoke(); |
54
|
|
|
if(null !== $i) { |
55
|
|
|
self::$instance = $i; |
56
|
|
|
break; |
57
|
|
|
} |
58
|
|
|
} |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
return self::$instance; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* @param string $type |
66
|
|
|
* @return null|CryptTool null on unknown type |
67
|
|
|
*/ |
68
|
|
|
public static function createInstance($type) { |
69
|
|
|
switch($type) { |
70
|
|
|
case self::TYPE_SODIUM: |
71
|
|
|
$instance = new CryptToolSodium(); |
72
|
|
|
if(false === $instance->isSupported()) { |
73
|
|
|
//try to instance old version of sodium wrapper |
74
|
|
|
/** @noinspection PhpDeprecationInspection */ |
75
|
|
|
$instance = new CryptToolSodiumDep(); |
|
|
|
|
76
|
|
|
} |
77
|
|
|
return $instance->isSupported() ? $instance :null; |
78
|
|
|
case self::TYPE_SALT: |
79
|
|
|
$instance = new CryptToolSalt(); |
80
|
|
|
return $instance->isSupported() ? $instance :null; |
81
|
|
|
default: |
82
|
|
|
return null; |
83
|
|
|
} |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
const MESSAGE_ID_LEN = 8; |
87
|
|
|
const BLOB_ID_LEN = 16; |
88
|
|
|
const IMAGE_FILE_SIZE_LEN = 4; |
89
|
|
|
const IMAGE_NONCE_LEN = 24; |
90
|
|
|
|
91
|
|
|
const EMAIL_HMAC_KEY = "\x30\xa5\x50\x0f\xed\x97\x01\xfa\x6d\xef\xdb\x61\x08\x41\x90\x0f\xeb\xb8\xe4\x30\x88\x1f\x7a\xd8\x16\x82\x62\x64\xec\x09\xba\xd7"; |
92
|
|
|
const PHONENO_HMAC_KEY = "\x85\xad\xf8\x22\x69\x53\xf3\xd9\x6c\xfd\x5d\x09\xbf\x29\x55\x5e\xb9\x55\xfc\xd8\xaa\x5e\xc4\xf9\xfc\xd8\x69\xe2\x58\x37\x07\x23"; |
93
|
|
|
|
94
|
|
|
protected function __construct() {} |
95
|
|
|
protected function __clone() {} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* Encrypt a text message. |
99
|
|
|
* |
100
|
|
|
* @param string $text the text to be encrypted (max. 3500 bytes) |
101
|
|
|
* @param string $senderPrivateKey the private key of the sending ID |
102
|
|
|
* @param string $recipientPublicKey the public key of the receiving ID |
103
|
|
|
* @param string $nonce the nonce to be used for the encryption (usually 24 random bytes) |
104
|
|
|
* @return string encrypted box |
105
|
|
|
*/ |
106
|
|
|
final public function encryptMessageText($text, $senderPrivateKey, $recipientPublicKey, $nonce) { |
107
|
|
|
/* prepend type byte (0x01) to message data */ |
108
|
|
|
$textBytes = "\x01" . $text; |
109
|
|
|
|
110
|
|
|
/* determine random amount of PKCS7 padding */ |
111
|
|
|
$padbytes = $this->generatePadBytes(); |
112
|
|
|
|
113
|
|
|
/* append padding */ |
114
|
|
|
$textBytes .= str_repeat(chr($padbytes), $padbytes); |
115
|
|
|
|
116
|
|
|
return $this->makeBox($textBytes, $nonce, $senderPrivateKey, $recipientPublicKey); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* @param UploadFileResult $uploadFileResult the result of the upload |
121
|
|
|
* @param EncryptResult $encryptResult the result of the image encryption |
122
|
|
|
* @param string $senderPrivateKey the private key of the sending ID (as binary) |
123
|
|
|
* @param string $recipientPublicKey the public key of the receiving ID (as binary) |
124
|
|
|
* @param string $nonce the nonce to be used for the encryption (usually 24 random bytes) |
125
|
|
|
* @return string |
126
|
|
|
*/ |
127
|
|
|
final public function encryptImageMessage( |
128
|
|
|
UploadFileResult $uploadFileResult, |
129
|
|
|
EncryptResult $encryptResult, |
130
|
|
|
$senderPrivateKey, |
131
|
|
|
$recipientPublicKey, |
132
|
|
|
$nonce) { |
133
|
|
|
$message = "\x02" . $this->hex2bin($uploadFileResult->getBlobId()); |
134
|
|
|
$message .= pack('V', $encryptResult->getSize()); |
135
|
|
|
$message .= $encryptResult->getNonce(); |
136
|
|
|
|
137
|
|
|
/* determine random amount of PKCS7 padding */ |
138
|
|
|
$padbytes = $this->generatePadBytes(); |
139
|
|
|
|
140
|
|
|
/* append padding */ |
141
|
|
|
$message .= str_repeat(chr($padbytes), $padbytes); |
142
|
|
|
|
143
|
|
|
return $this->makeBox($message, $nonce, $senderPrivateKey, $recipientPublicKey); |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
final public function encryptFileMessage(UploadFileResult $uploadFileResult, |
147
|
|
|
EncryptResult $encryptResult, |
148
|
|
|
UploadFileResult $thumbnailUploadFileResult = null, |
149
|
|
|
FileAnalysisResult $fileAnalysisResult, |
150
|
|
|
$senderPrivateKey, |
151
|
|
|
$recipientPublicKey, |
152
|
|
|
$nonce) { |
153
|
|
|
|
154
|
|
|
|
155
|
|
|
$messageContent = array( |
156
|
|
|
'b' => $uploadFileResult->getBlobId(), |
157
|
|
|
'k' => $this->bin2hex($encryptResult->getKey()), |
158
|
|
|
'm' => $fileAnalysisResult->getMimeType(), |
159
|
|
|
'n' => $fileAnalysisResult->getFileName(), |
160
|
|
|
's' => $fileAnalysisResult->getSize(), |
161
|
|
|
'i' => 0 |
162
|
|
|
); |
163
|
|
|
|
164
|
|
|
if($thumbnailUploadFileResult !== null && strlen($thumbnailUploadFileResult->getBlobId()) > 0) { |
165
|
|
|
$messageContent['t'] = $thumbnailUploadFileResult->getBlobId(); |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
$message = "\x17" . json_encode($messageContent); |
169
|
|
|
|
170
|
|
|
/* determine random amount of PKCS7 padding */ |
171
|
|
|
$padbytes = $this->generatePadBytes(); |
172
|
|
|
|
173
|
|
|
/* append padding */ |
174
|
|
|
$message .= str_repeat(chr($padbytes), $padbytes); |
175
|
|
|
|
176
|
|
|
return $this->makeBox($message, $nonce, $senderPrivateKey, $recipientPublicKey); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* make a box |
181
|
|
|
* |
182
|
|
|
* @param string $data |
183
|
|
|
* @param string $nonce |
184
|
|
|
* @param string $senderPrivateKey |
185
|
|
|
* @param string $recipientPublicKey |
186
|
|
|
* @return string encrypted box |
187
|
|
|
*/ |
188
|
|
|
abstract protected function makeBox($data, $nonce, $senderPrivateKey, $recipientPublicKey); |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* make a secret box |
192
|
|
|
* |
193
|
|
|
* @param $data |
194
|
|
|
* @param $nonce |
195
|
|
|
* @param $key |
196
|
|
|
* @return mixed |
197
|
|
|
*/ |
198
|
|
|
abstract protected function makeSecretBox($data, $nonce, $key); |
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* decrypt a box |
202
|
|
|
* |
203
|
|
|
* @param string $box as binary |
204
|
|
|
* @param string $recipientPrivateKey as binary |
205
|
|
|
* @param string $senderPublicKey as binary |
206
|
|
|
* @param string $nonce as binary |
207
|
|
|
* @return string |
208
|
|
|
*/ |
209
|
|
|
abstract protected function openBox($box, $recipientPrivateKey, $senderPublicKey, $nonce); |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* decrypt a secret box |
213
|
|
|
* |
214
|
|
|
* @param string $box as binary |
215
|
|
|
* @param string $nonce as binary |
216
|
|
|
* @param string $key as binary |
217
|
|
|
* @return string as binary |
218
|
|
|
*/ |
219
|
|
|
abstract protected function openSecretBox($box, $nonce, $key); |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* @param string $box |
223
|
|
|
* @param string $recipientPrivateKey |
224
|
|
|
* @param string $senderPublicKey |
225
|
|
|
* @param string $nonce |
226
|
|
|
* @return ThreemaMessage the decrypted message |
227
|
|
|
* @throws BadMessageException |
228
|
|
|
* @throws DecryptionFailedException |
229
|
|
|
* @throws UnsupportedMessageTypeException |
230
|
|
|
*/ |
231
|
|
|
final public function decryptMessage($box, $recipientPrivateKey, $senderPublicKey, $nonce) { |
232
|
|
|
|
233
|
|
|
$data = $this->openBox($box, $recipientPrivateKey, $senderPublicKey, $nonce); |
234
|
|
|
|
235
|
|
|
if (null === $data || strlen($data) == 0) { |
236
|
|
|
throw new DecryptionFailedException(); |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/* remove padding */ |
240
|
|
|
$padbytes = ord($data[strlen($data)-1]); |
241
|
|
|
$realDataLength = strlen($data) - $padbytes; |
242
|
|
|
if ($realDataLength < 1) { |
243
|
|
|
throw new BadMessageException(); |
244
|
|
|
} |
245
|
|
|
$data = substr($data, 0, $realDataLength); |
246
|
|
|
|
247
|
|
|
/* first byte of data is type */ |
248
|
|
|
$type = ord($data[0]); |
249
|
|
|
|
250
|
|
|
$pos = 1; |
251
|
|
|
$piece = function($length) use(&$pos, $data) { |
252
|
|
|
$d = substr($data, $pos, $length); |
253
|
|
|
$pos += $length; |
254
|
|
|
return $d; |
255
|
|
|
}; |
256
|
|
|
|
257
|
|
|
switch ($type) { |
258
|
|
|
case TextMessage::TYPE_CODE: |
259
|
|
|
/* Text message */ |
260
|
|
|
if ($realDataLength < 2) { |
261
|
|
|
throw new BadMessageException(); |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
return new TextMessage(substr($data, 1)); |
265
|
|
|
case DeliveryReceipt::TYPE_CODE: |
266
|
|
|
/* Delivery receipt */ |
267
|
|
|
if ($realDataLength < (self::MESSAGE_ID_LEN-2) || (($realDataLength - 2) % self::MESSAGE_ID_LEN) != 0) { |
268
|
|
|
throw new BadMessageException(); |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
$receiptType = ord($data[1]); |
272
|
|
|
$messageIds = str_split(substr($data, 2), self::MESSAGE_ID_LEN); |
273
|
|
|
|
274
|
|
|
return new DeliveryReceipt($receiptType, $messageIds); |
275
|
|
|
case ImageMessage::TYPE_CODE: |
276
|
|
|
/* Image Message */ |
277
|
|
|
if ($realDataLength != 1 + self::BLOB_ID_LEN + self::IMAGE_FILE_SIZE_LEN + self::IMAGE_NONCE_LEN) { |
278
|
|
|
throw new BadMessageException(); |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
$blobId = $piece->__invoke(self::BLOB_ID_LEN); |
282
|
|
|
$length = $piece->__invoke(self::IMAGE_FILE_SIZE_LEN); |
283
|
|
|
$nonce = $piece->__invoke(self::IMAGE_NONCE_LEN); |
284
|
|
|
return new ImageMessage($this->bin2hex($blobId), $this->bin2hex($length), $nonce); |
285
|
|
|
case FileMessage::TYPE_CODE: |
286
|
|
|
/* Image Message */ |
287
|
|
|
$decodeResult = json_decode(substr($data, 1), true); |
288
|
|
|
if(null === $decodeResult || false === $decodeResult) { |
289
|
|
|
throw new BadMessageException(); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
$values = AssocArray::byJsonString(substr($data, 1), array('b', 't', 'k', 'm', 'n', 's')); |
293
|
|
|
if(null === $values) { |
294
|
|
|
throw new BadMessageException(); |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
return new FileMessage( |
298
|
|
|
$values->getValue('b'), |
299
|
|
|
$values->getValue('t'), |
300
|
|
|
$values->getValue('k'), |
301
|
|
|
$values->getValue('m'), |
302
|
|
|
$values->getValue('n'), |
303
|
|
|
$values->getValue('s')); |
304
|
|
|
default: |
305
|
|
|
throw new UnsupportedMessageTypeException(); |
306
|
|
|
} |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
/** |
310
|
|
|
* Generate a new key pair. |
311
|
|
|
* |
312
|
|
|
* @return KeyPair the new key pair |
313
|
|
|
*/ |
314
|
|
|
abstract public function generateKeyPair(); |
315
|
|
|
|
316
|
|
|
/** |
317
|
|
|
* Hashes an email address for identity lookup. |
318
|
|
|
* |
319
|
|
|
* @param string $email the email address |
320
|
|
|
* @return string the email hash (hex) |
321
|
|
|
*/ |
322
|
|
|
final public function hashEmail($email) { |
323
|
|
|
$emailClean = strtolower(trim($email)); |
324
|
|
|
return hash_hmac('sha256', $emailClean, self::EMAIL_HMAC_KEY); |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* Hashes an phone number address for identity lookup. |
329
|
|
|
* |
330
|
|
|
* @param string $phoneNo the phone number (in E.164 format, no leading +) |
331
|
|
|
* @return string the phone number hash (hex) |
332
|
|
|
*/ |
333
|
|
|
final public function hashPhoneNo($phoneNo) { |
334
|
|
|
$phoneNoClean = preg_replace("/[^0-9]/", "", $phoneNo); |
335
|
|
|
return hash_hmac('sha256', $phoneNoClean, self::PHONENO_HMAC_KEY); |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
abstract protected function createRandom($size); |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Generate a random nonce. |
342
|
|
|
* |
343
|
|
|
* @return string random nonce |
344
|
|
|
*/ |
345
|
|
|
final public function randomNonce() { |
346
|
|
|
return $this->createRandom(\Salt::box_NONCE); |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
/** |
350
|
|
|
* Generate a symmetric key |
351
|
|
|
* @return mixed |
352
|
|
|
*/ |
353
|
|
|
final public function symmetricKey() { |
354
|
|
|
return $this->createRandom(32); |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
/** |
358
|
|
|
* Derive the public key |
359
|
|
|
* |
360
|
|
|
* @param string $privateKey as binary |
361
|
|
|
* @return string as binary |
362
|
|
|
*/ |
363
|
|
|
abstract public function derivePublicKey($privateKey); |
364
|
|
|
|
365
|
|
|
/** |
366
|
|
|
* Check if implementation supported |
367
|
|
|
* @return bool |
368
|
|
|
*/ |
369
|
|
|
abstract public function isSupported(); |
370
|
|
|
|
371
|
|
|
/** |
372
|
|
|
* Validate crypt tool |
373
|
|
|
* |
374
|
|
|
* @return bool |
375
|
|
|
* @throws Exception |
376
|
|
|
*/ |
377
|
|
|
abstract public function validate(); |
378
|
|
|
|
379
|
|
|
/** |
380
|
|
|
* @param $data |
381
|
|
|
* @return EncryptResult |
382
|
|
|
*/ |
383
|
|
|
public final function encryptFile($data) { |
384
|
|
|
$key = $this->symmetricKey(); |
385
|
|
|
$box = $this->makeSecretBox($data, self::FILE_NONCE, $key); |
386
|
|
|
return new EncryptResult($box, $key, self::FILE_NONCE, strlen($box)); |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
/** |
390
|
|
|
* @param string $data as binary |
391
|
|
|
* @param string $key as binary |
392
|
|
|
* @return null|string |
393
|
|
|
*/ |
394
|
|
|
public final function decryptFile($data, $key) { |
395
|
|
|
$result = $this->openSecretBox($data, self::FILE_NONCE, $key); |
396
|
|
|
return false === $result ? null : $result; |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
/** |
400
|
|
|
* @param string $data |
401
|
|
|
* @param string $key |
402
|
|
|
* @return EncryptResult |
403
|
|
|
*/ |
404
|
|
|
public final function encryptFileThumbnail($data, $key) { |
405
|
|
|
$box = $this->makeSecretBox($data, self::FILE_THUMBNAIL_NONCE, $key); |
406
|
|
|
return new EncryptResult($box, $key, self::FILE_THUMBNAIL_NONCE, strlen($box)); |
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
public final function decryptFileThumbnail($data, $key) { |
410
|
|
|
$result = $this->openSecretBox($data, self::FILE_THUMBNAIL_NONCE, $key); |
411
|
|
|
return false === $result ? null : $result; |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
/** |
415
|
|
|
* @param string $imageData |
416
|
|
|
* @param string $privateKey as binary |
417
|
|
|
* @param string $publicKey as binary |
418
|
|
|
* @return EncryptResult |
419
|
|
|
*/ |
420
|
|
|
public final function encryptImage($imageData, $privateKey, $publicKey) { |
421
|
|
|
$nonce = $this->randomNonce(); |
422
|
|
|
|
423
|
|
|
$box = $this->makeBox( |
424
|
|
|
$imageData, |
425
|
|
|
$nonce, |
426
|
|
|
$privateKey, |
427
|
|
|
$publicKey |
428
|
|
|
); |
429
|
|
|
|
430
|
|
|
return new EncryptResult($box, null, $nonce, strlen($box)); |
431
|
|
|
} |
432
|
|
|
|
433
|
|
|
/** |
434
|
|
|
* @param string $data as binary |
435
|
|
|
* @param string $publicKey as binary |
436
|
|
|
* @param string $privateKey as binary |
437
|
|
|
* @param string $nonce as binary |
438
|
|
|
* @return string |
439
|
|
|
*/ |
440
|
|
|
public final function decryptImage($data, $publicKey, $privateKey, $nonce) { |
441
|
|
|
return $this->openBox($data, |
442
|
|
|
$privateKey, |
443
|
|
|
$publicKey, |
444
|
|
|
$nonce); |
445
|
|
|
} |
446
|
|
|
|
447
|
|
|
/** |
448
|
|
|
* determine random amount of PKCS7 padding |
449
|
|
|
* @return int |
450
|
|
|
*/ |
451
|
|
|
private function generatePadBytes() { |
452
|
|
|
$padbytes = 0; |
453
|
|
|
while($padbytes < 1 || $padbytes > 255) { |
454
|
|
|
$padbytes = ord($this->createRandom(1)); |
455
|
|
|
} |
456
|
|
|
return $padbytes; |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
public function __toString() { |
460
|
|
|
return 'CryptTool '.$this->getName(); |
461
|
|
|
} |
462
|
|
|
|
463
|
|
|
/** |
464
|
|
|
* Converts a binary string to an hexdecimal string. |
465
|
|
|
* |
466
|
|
|
* This is the same as PHP's bin2hex() implementation, but it is resistant to |
467
|
|
|
* timing attacks. |
468
|
|
|
* |
469
|
|
|
* @param string $binaryString The binary string to convert |
470
|
|
|
* @return string |
471
|
|
|
*/ |
472
|
|
|
public function bin2hex($binaryString) |
473
|
|
|
{ |
474
|
|
|
return bin2hex($binaryString); |
475
|
|
|
} |
476
|
|
|
|
477
|
|
|
/** |
478
|
|
|
* Converts an hexdecimal string to a binary string. |
479
|
|
|
* |
480
|
|
|
* This is the same as PHP's hex2bin() implementation, but it is resistant to |
481
|
|
|
* timing attacks. |
482
|
|
|
* Note that the default implementation does not support $ignore currrently and will |
483
|
|
|
* throw an error. Only when libsodium >= 0.22 is used, this is supported. |
484
|
|
|
* |
485
|
|
|
* @param string $hexString The hex string to convert |
486
|
|
|
* @param string|null $ignore (optional) Characters to ignore |
487
|
|
|
* @throws \Threema\Core\Exception |
488
|
|
|
* @return string |
489
|
|
|
*/ |
490
|
|
|
public function hex2bin($hexString, $ignore = null) |
491
|
|
|
{ |
492
|
|
|
if ($ignore !== null) { |
493
|
|
|
throw new Exception('$ignore parameter is not supported'); |
494
|
|
|
} |
495
|
|
|
return hex2bin($hexString); |
496
|
|
|
} |
497
|
|
|
|
498
|
|
|
/** |
499
|
|
|
* Compares two strings in a secure way. |
500
|
|
|
* |
501
|
|
|
* This is the same as PHP's strcmp() implementation, but it is resistant to |
502
|
|
|
* timing attacks. |
503
|
|
|
* |
504
|
|
|
* @link https://paragonie.com/book/pecl-libsodium/read/03-utilities-helpers.md#compare |
505
|
|
|
* @param string $str1 The first string |
506
|
|
|
* @param string $str2 The second string |
507
|
|
|
* @return bool |
508
|
|
|
*/ |
509
|
|
|
public function stringCompare($str1, $str2) |
510
|
|
|
{ |
511
|
|
|
if (function_exists('hash_equals')) { |
512
|
|
|
return hash_equals($str1, $str2); |
513
|
|
|
} else { |
514
|
|
|
// check variable type manually |
515
|
|
|
if (!is_string($str1) || !is_string($str2)) { |
516
|
|
|
return false; |
517
|
|
|
} |
518
|
|
|
|
519
|
|
|
// fast comparison: check string length |
520
|
|
|
if (strlen($str1) != strlen($str2)) { |
521
|
|
|
return false; |
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
# PHP implementation of hash_equals |
525
|
|
|
# partly taken from https://github.com/symfony/polyfill-php56/blob/master/Php56.php#L45-L51 |
526
|
|
|
# |
527
|
|
|
# Note that this is really slow!! |
528
|
|
|
# |
529
|
|
|
$ret = 0; |
530
|
|
|
$length = strlen($str1); |
531
|
|
|
for ($i = 0; $i < $length; ++$i) { |
532
|
|
|
$ret |= ord($str1[$i]) ^ ord($str2[$i]); |
533
|
|
|
} |
534
|
|
|
return 0 === $ret; |
535
|
|
|
} |
536
|
|
|
} |
537
|
|
|
|
538
|
|
|
/** |
539
|
|
|
* Unsets/removes a variable. |
540
|
|
|
* |
541
|
|
|
* Note: the PHP implementation here provides no security, but if you use |
542
|
|
|
* Libsodium, the variable will be deleted in a better way. |
543
|
|
|
* |
544
|
|
|
* @param string $var A variable, passed by reference |
545
|
|
|
*/ |
546
|
|
|
public function removeVar(&$var) |
547
|
|
|
{ |
548
|
|
|
// overwrite var (128x0), quite certainly not secure at all |
549
|
|
|
$var = '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; |
550
|
|
|
$var = null; |
551
|
|
|
// actually this does not erase the content of the variable |
552
|
|
|
unset($var); |
553
|
|
|
} |
554
|
|
|
|
555
|
|
|
/** |
556
|
|
|
* Name of the CryptTool |
557
|
|
|
* @return string |
558
|
|
|
*/ |
559
|
|
|
abstract public function getName(); |
560
|
|
|
|
561
|
|
|
/** |
562
|
|
|
* Description of the CryptTool |
563
|
|
|
* @return string |
564
|
|
|
*/ |
565
|
|
|
abstract public function getDescription(); |
566
|
|
|
} |
567
|
|
|
|
This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.