Completed
Push — master ( bd2fc1...382c63 )
by rugk
02:34
created

CryptToolTests::testCreateKeyPair()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 7
nc 1
nop 0
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) {
0 ignored issues
show
Unused Code introduced by
The parameter $prefix is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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) {
0 ignored issues
show
Unused Code introduced by
The parameter $prefix is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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) {
0 ignored issues
show
Unused Code introduced by
The parameter $prefix is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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