(dev/core#2258) CryptoToken - More defense against malformed input
[civicrm-core.git] / tests / phpunit / Civi / Crypto / CryptoTokenTest.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
10 */
11
12 namespace Civi\Crypto;
13
14 use Civi\Crypto\Exception\CryptoException;
15
16 /**
17 * Test major use-cases of the 'crypto.token' service.
18 */
19 class CryptoTokenTest extends \CiviUnitTestCase {
20
21 use CryptoTestTrait;
22
23 protected function setUp() {
24 parent::setUp();
25 \CRM_Utils_Hook::singleton()->setHook('civicrm_crypto', [$this, 'registerExampleKeys']);
26 }
27
28 public function testIsPlainText() {
29 $token = \Civi::service('crypto.token');
30
31 $this->assertFalse($token->isPlainText(chr(2)));
32 $this->assertFalse($token->isPlainText(chr(2) . 'asdf'));
33
34 $this->assertTrue($token->isPlainText(\CRM_Utils_Array::implodePadded(['a', 'b', 'c'])));
35 $this->assertTrue($token->isPlainText(""));
36 $this->assertTrue($token->isPlainText("\r"));
37 $this->assertTrue($token->isPlainText("\n"));
38 }
39
40 public function testDecryptInvalid() {
41 $cryptoToken = \Civi::service('crypto.token');
42 try {
43 $cryptoToken->decrypt(chr(2) . 'CTK0' . chr(2));
44 $this->fail("Expected CryptoException");
45 }
46 catch (CryptoException $e) {
47 $this->assertRegExp(';Cannot decrypt token. Invalid format.;', $e->getMessage());
48 }
49
50 $goodExample = $cryptoToken->encrypt('mess with me', 'UNIT-TEST');
51 $this->assertEquals('mess with me', $cryptoToken->decrypt($goodExample));
52
53 try {
54 $badExample = preg_replace(';CTK0;', 'ctk9', $goodExample);
55 $cryptoToken->decrypt($badExample);
56 $this->fail("Expected CryptoException");
57 }
58 catch (CryptoException $e) {
59 $this->assertRegExp(';Cannot decrypt token. Invalid format.;', $e->getMessage());
60 }
61 }
62
63 public function getExampleTokens() {
64 return [
65 // [ 'Plain text', 'Encryption Key ID', 'expectTokenRegex', 'expectTokenLen', 'expectPlain' ]
66 ['hello world. can you see me', 'plain', '/^hello world. can you see me/', 27, TRUE],
67 ['hello world. i am secret.', 'UNIT-TEST', '/^.CTK0.asdf-key-1./', 81, FALSE],
68 ['hello world. we b secret.', 'asdf-key-0', '/^.CTK0.asdf-key-0./', 81, FALSE],
69 ['hello world. u ur secret.', 'asdf-key-1', '/^.CTK0.asdf-key-1./', 81, FALSE],
70 ['hello world. he z secret.', 'asdf-key-2', '/^.CTK0.asdf-key-2./', 73, FALSE],
71 ['hello world. whos secret.', 'asdf-key-3', '/^.CTK0.asdf-key-3./', 125, FALSE],
72 ];
73 }
74
75 /**
76 * @param string $inputText
77 * @param string $inputKeyIdOrTag
78 * @param string $expectTokenRegex
79 * @param int $expectTokenLen
80 * @param bool $expectPlain
81 *
82 * @dataProvider getExampleTokens
83 */
84 public function testRoundtrip($inputText, $inputKeyIdOrTag, $expectTokenRegex, $expectTokenLen, $expectPlain) {
85 $token = \Civi::service('crypto.token')->encrypt($inputText, $inputKeyIdOrTag);
86 $this->assertRegExp($expectTokenRegex, $token);
87 $this->assertEquals($expectTokenLen, strlen($token));
88 $this->assertEquals($expectPlain, \Civi::service('crypto.token')->isPlainText($token));
89
90 $actualText = \Civi::service('crypto.token')->decrypt($token);
91 $this->assertEquals($inputText, $actualText);
92 }
93
94 public function testReadPlainTextWithoutRegistry() {
95 // This is performance optimization - don't initialize crypto.registry unless
96 // you actually need it.
97 $this->assertFalse(\Civi::container()->initialized('crypto.registry'));
98 $this->assertEquals("Hello world", \Civi::service('crypto.token')->decrypt("Hello world"));
99 $this->assertFalse(\Civi::container()->initialized('crypto.registry'));
100 }
101
102 }