1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @author Threema GmbH |
4
|
|
|
* @copyright Copyright (c) 2015-2016 Threema GmbH |
5
|
|
|
*/ |
6
|
|
|
|
7
|
|
|
|
8
|
|
|
|
9
|
|
|
namespace Threema\MsgApi\Tests; |
10
|
|
|
|
11
|
|
|
use Threema\Console\Common; |
12
|
|
|
use Threema\MsgApi\Messages\TextMessage; |
13
|
|
|
use Threema\MsgApi\Tools\CryptTool; |
14
|
|
|
|
15
|
|
|
class CryptToolTests extends \PHPUnit_Framework_TestCase { |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* test generating key pair |
19
|
|
|
*/ |
20
|
|
|
public function testCreateKeyPair() { |
21
|
|
|
$this->doTest(function(CryptTool $cryptTool, $prefix) { |
22
|
|
|
$this->assertNotNull($cryptTool, $prefix.' could not instance crypto tool'); |
23
|
|
|
$keyPair = $cryptTool->generateKeyPair(); |
24
|
|
|
$this->assertNotNull($keyPair, $prefix.': invalid key pair'); |
25
|
|
|
$this->assertNotNull($keyPair->privateKey, $prefix.': private key is null'); |
26
|
|
|
$this->assertNotNull($keyPair->publicKey, $prefix.': public key is null'); |
27
|
|
|
}); |
28
|
|
|
} |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* test generating random nonce |
32
|
|
|
*/ |
33
|
|
|
public function testRandomNonce() { |
34
|
|
|
$this->doTest(function(CryptTool $cryptTool, $prefix) { |
35
|
|
|
$randomNonce = $cryptTool->randomNonce(); |
36
|
|
|
$this->assertEquals(24, strlen($randomNonce), $prefix.': random nonce size not 24'); |
37
|
|
|
}); |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
public function testDecrypt() { |
41
|
|
|
/** @noinspection PhpUnusedParameterInspection */ |
42
|
|
|
$this->doTest(function(CryptTool $cryptTool, $prefix) { |
|
|
|
|
43
|
|
|
$nonce = '0a1ec5b67b4d61a1ef91f55e8ce0471fee96ea5d8596dfd0'; |
44
|
|
|
$box = '45181c7aed95a1c100b1b559116c61b43ce15d04014a805288b7d14bf3a993393264fe554794ce7d6007233e8ef5a0f1ccdd704f34e7c7b77c72c239182caf1d061d6fff6ffbbfe8d3b8f3475c2fe352e563aa60290c666b2e627761e32155e62f048b52ef2f39c13ac229f393c67811749467396ecd09f42d32a4eb419117d0451056ac18fac957c52b0cca67568e2d97e5a3fd829a77f914a1ad403c5909fd510a313033422ea5db71eaf43d483238612a54cb1ecfe55259b1de5579e67c6505df7d674d34a737edf721ea69d15b567bc2195ec67e172f3cb8d6842ca88c29138cc33e9351dbc1e4973a82e1cf428c1c763bb8f3eb57770f914a'; |
45
|
|
|
|
46
|
|
|
$privateKey = Common::getPrivateKey(Constants::otherPrivateKey); |
47
|
|
|
$this->assertNotNull($privateKey); |
48
|
|
|
|
49
|
|
|
$publicKey = Common::getPublicKey(Constants::myPublicKey); |
50
|
|
|
$this->assertNotNull($publicKey); |
51
|
|
|
|
52
|
|
|
$message = $cryptTool->decryptMessage($cryptTool->hex2bin($box), |
53
|
|
|
$cryptTool->hex2bin($privateKey), |
54
|
|
|
$cryptTool->hex2bin($publicKey), |
55
|
|
|
$cryptTool->hex2bin($nonce)); |
56
|
|
|
|
57
|
|
|
$this->assertNotNull($message); |
58
|
|
|
$this->assertTrue($message instanceof TextMessage); |
59
|
|
|
if($message instanceof TextMessage) { |
60
|
|
|
$this->assertEquals($message->getText(), 'Dies ist eine Testnachricht. äöü'); |
61
|
|
|
} |
62
|
|
|
}); |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
public function testEncrypt() { |
66
|
|
|
/** @noinspection PhpUnusedParameterInspection */ |
67
|
|
|
$this->doTest(function(CryptTool $cryptTool, $prefix) { |
|
|
|
|
68
|
|
|
$text = 'Dies ist eine Testnachricht. äöü'; |
69
|
|
|
$nonce = '0a1ec5b67b4d61a1ef91f55e8ce0471fee96ea5d8596dfd0'; |
70
|
|
|
|
71
|
|
|
$privateKey = Common::getPrivateKey(Constants::myPrivateKey); |
72
|
|
|
$this->assertNotNull($privateKey); |
73
|
|
|
|
74
|
|
|
$publicKey = Common::getPublicKey(Constants::otherPublicKey); |
75
|
|
|
$this->assertNotNull($publicKey); |
76
|
|
|
|
77
|
|
|
$message = $cryptTool->encryptMessageText($text, |
78
|
|
|
$cryptTool->hex2bin($privateKey), |
79
|
|
|
$cryptTool->hex2bin($publicKey), |
80
|
|
|
$cryptTool->hex2bin($nonce)); |
81
|
|
|
|
82
|
|
|
$this->assertNotNull($message); |
83
|
|
|
|
84
|
|
|
$box = $cryptTool->decryptMessage($message, |
85
|
|
|
$cryptTool->hex2bin(Common::getPrivateKey(Constants::otherPrivateKey)), |
86
|
|
|
$cryptTool->hex2bin(Common::getPublicKey(Constants::myPublicKey)), |
87
|
|
|
$cryptTool->hex2bin($nonce)); |
88
|
|
|
|
89
|
|
|
$this->assertNotNull($box); |
90
|
|
|
}); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
|
94
|
|
|
public function testDerivePublicKey() { |
95
|
|
|
$this->doTest(function(CryptTool $cryptTool, $prefix){ |
96
|
|
|
$publicKey = $cryptTool->derivePublicKey($cryptTool->hex2bin(Common::getPrivateKey(Constants::myPrivateKey))); |
97
|
|
|
$myPublicKey = $cryptTool->hex2bin(Common::getPublicKey(Constants::myPublicKey)); |
98
|
|
|
|
99
|
|
|
$this->assertEquals($publicKey, $myPublicKey, $prefix.' derive public key failed'); |
100
|
|
|
}); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
public function testEncryptImage() { |
104
|
|
|
$threemaIconContent = file_get_contents(dirname(__FILE__).'/threema.jpg'); |
105
|
|
|
|
106
|
|
|
/** @noinspection PhpUnusedParameterInspection */ |
107
|
|
|
$this->doTest(function(CryptTool $cryptTool, $prefix) use($threemaIconContent) { |
|
|
|
|
108
|
|
|
$privateKey = $cryptTool->hex2bin(Common::getPrivateKey(Constants::myPrivateKey)); |
109
|
|
|
$publicKey = $cryptTool->hex2bin(Common::getPublicKey(Constants::myPublicKey)); |
110
|
|
|
|
111
|
|
|
$otherPrivateKey = $cryptTool->hex2bin(Common::getPrivateKey(Constants::otherPrivateKey)); |
112
|
|
|
$otherPublicKey = $cryptTool->hex2bin(Common::getPublicKey(Constants::otherPublicKey)); |
113
|
|
|
|
114
|
|
|
$result = $cryptTool->encryptImage($threemaIconContent, $privateKey, $otherPublicKey); |
115
|
|
|
|
116
|
|
|
$decryptedImage = $cryptTool->decryptImage($result->getData(), $publicKey, $otherPrivateKey, $result->getNonce()); |
117
|
|
|
|
118
|
|
|
$this->assertEquals($decryptedImage, $threemaIconContent, 'decryption of image failed'); |
119
|
|
|
|
120
|
|
|
}); |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* test hex2bin and bin2hex |
125
|
|
|
*/ |
126
|
|
|
public function testHexBin() { |
127
|
|
|
$this->doTest(function(CryptTool $cryptTool, $prefix) { |
128
|
|
|
$testStr = Constants::myPrivateKeyExtract; |
129
|
|
|
|
130
|
|
|
// convert hex to bin |
131
|
|
|
$testStrBin = $cryptTool->hex2bin($testStr); |
132
|
|
|
$this->assertNotNull($testStrBin); |
133
|
|
|
$testStrBinPhp = hex2bin($testStr); |
134
|
|
|
|
135
|
|
|
// compare usual PHP conversion with crypt tool version |
136
|
|
|
$this->assertEquals($testStrBin, $testStrBinPhp, $prefix.': hex2bin returns different result than PHP-only implementation'); |
137
|
|
|
|
138
|
|
|
// convert back to hex |
139
|
|
|
$testStrHex = $cryptTool->bin2hex($testStrBin); |
140
|
|
|
$this->assertNotNull($testStrHex); |
141
|
|
|
$testStrHexPhp = bin2hex($testStrBin); |
142
|
|
|
|
143
|
|
|
// compare usual PHP conversion with crypt tool version |
144
|
|
|
$this->assertEquals($testStrHexPhp, $testStrHex, $prefix.': bin2hex returns different result than PHP-only implementation'); |
145
|
|
|
// compare with initial value |
146
|
|
|
$this->assertEquals($testStrHex, $testStr, $prefix.': binary string is different than initial string after conversions'); |
147
|
|
|
}); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* test compare functions to make sure they are resistant to timing attacks |
152
|
|
|
*/ |
153
|
|
|
public function testCompare() { |
154
|
|
|
$this->doTest(function(CryptTool $cryptTool, $prefix) { |
155
|
|
|
// make strings large enough |
156
|
|
|
$string1 = str_repeat(Constants::myPrivateKey, 100000); |
157
|
|
|
$string2 = str_repeat(Constants::otherPrivateKey, 100000); |
158
|
|
|
echo PHP_EOL; |
159
|
|
|
|
160
|
|
|
$humanDescr = [ |
161
|
|
|
'length' => 'different length', |
162
|
|
|
'diff' => 'same length, different content', |
163
|
|
|
'same' => 'same length, same content' |
164
|
|
|
]; |
165
|
|
|
|
166
|
|
|
// test different strings when comparing |
167
|
|
|
$comparisonResult = []; |
168
|
|
|
foreach(array( |
169
|
|
|
'length' => [$string1, $string1 . 'a'], |
170
|
|
|
'diff' => [$string1, $string2], |
171
|
|
|
'same' => [$string1, $string1] |
172
|
|
|
) as $testName => $strings) { |
173
|
|
|
for ($i=0; $i < 3; $i++) { |
174
|
|
|
// test run with delay |
175
|
|
|
$comparisonResult[$testName][$i] = $cryptTool->stringCompare($strings[0], $strings[1]); |
176
|
|
|
|
177
|
|
|
// check result |
178
|
|
|
if ($testName == 'length' || $testName == 'diff') { |
179
|
|
|
$this->assertEquals(false, $comparisonResult[$testName][$i], $prefix.': comparison of "'.$humanDescr[$testName].' #'.$i.'" is wrong: expected: false, got '.$comparisonResult[$testName][$i]); |
180
|
|
|
} else { |
181
|
|
|
$this->assertEquals(true, $comparisonResult[$testName][$i], $prefix.': comparison of "'.$humanDescr[$testName].' #'.$i.'" is wrong: expected: true, got '.$comparisonResult[$testName][$i]); |
182
|
|
|
} |
183
|
|
|
} |
184
|
|
|
} |
185
|
|
|
}); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* test variable deletion |
190
|
|
|
*/ |
191
|
|
|
public function testRemoveVar() { |
192
|
|
|
$this->doTest(function(CryptTool $cryptTool, $prefix) { |
193
|
|
|
foreach(array( |
194
|
|
|
'hex' => Constants::myPrivateKeyExtract, |
195
|
|
|
'bin' => $cryptTool->hex2bin(Constants::myPrivateKeyExtract) |
196
|
|
|
) as $key => $testVar) { |
197
|
|
|
// let it remove |
198
|
|
|
$cryptTool->removeVar($testVar); |
199
|
|
|
|
200
|
|
|
$this->assertEmpty($testVar, $prefix.': variable is not empty (test: '.$key.')'); |
201
|
|
|
$this->assertNull($testVar, $prefix.': variable is not null (test: '.$key.')'); |
202
|
|
|
} |
203
|
|
|
}); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
private function doTest(\Closure $c) { |
207
|
|
|
foreach(array( |
208
|
|
|
'Salt' => CryptTool::createInstance(CryptTool::TYPE_SALT), |
209
|
|
|
'Sodium' => CryptTool::createInstance(CryptTool::TYPE_SODIUM) |
210
|
|
|
) as $key => $instance) { |
211
|
|
|
|
212
|
|
|
if($instance === null) { |
213
|
|
|
echo $key.": could not instance crypt tool\n"; |
214
|
|
|
break; |
215
|
|
|
} |
216
|
|
|
/** @noinspection PhpUndefinedMethodInspection */ |
217
|
|
|
$this->assertTrue($instance->isSupported(), $key.' not supported'); |
218
|
|
|
$c->__invoke($instance, $key); |
219
|
|
|
} |
220
|
|
|
} |
221
|
|
|
} |
222
|
|
|
|
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.