1+ <?php
2+
3+ namespace Patchlevel \Hydrator \Cryptography ;
4+
5+ use Patchlevel \Hydrator \Cryptography \Cipher \Cipher ;
6+ use Patchlevel \Hydrator \Cryptography \Cipher \CipherKey ;
7+ use Patchlevel \Hydrator \Cryptography \Cipher \CipherKeyFactory ;
8+ use Patchlevel \Hydrator \Cryptography \Cipher \DecryptionFailed ;
9+ use Patchlevel \Hydrator \Cryptography \Cipher \EncryptionFailed ;
10+ use Patchlevel \Hydrator \Cryptography \Cipher \OpensslCipher ;
11+ use Patchlevel \Hydrator \Cryptography \Cipher \OpensslCipherKeyFactory ;
12+ use Patchlevel \Hydrator \Cryptography \Store \CipherKeyNotExists ;
13+ use Patchlevel \Hydrator \Cryptography \Store \CipherKeyStore ;
14+
15+ /**
16+ * @phpstan-type EncryptedDataV1 array{
17+ * __enc: 'v1',
18+ * data: non-empty-string,
19+ * method?: non-empty-string,
20+ * iv?: non-empty-string,
21+ * }
22+ */
23+ final class BaseCryptographer implements Cryptographer
24+ {
25+ public function __construct (
26+ private readonly Cipher $ cipher ,
27+ private readonly CipherKeyStore $ cipherKeyStore ,
28+ private readonly CipherKeyFactory $ cipherKeyFactory ,
29+ )
30+ {
31+ }
32+
33+ /**
34+ * @throws EncryptionFailed
35+ *
36+ * @return EncryptedDataV1
37+ */
38+ public function encrypt (string $ subjectId , mixed $ value ): array
39+ {
40+ try {
41+ $ cipherKey = $ this ->cipherKeyStore ->get ($ subjectId );
42+ } catch (CipherKeyNotExists ) {
43+ $ cipherKey = ($ this ->cipherKeyFactory )();
44+ $ this ->cipherKeyStore ->store ($ subjectId , $ cipherKey );
45+ }
46+
47+ return [
48+ '__enc ' => 'v1 ' ,
49+ 'data ' => $ this ->cipher ->encrypt ($ cipherKey , $ value ),
50+ 'method ' => $ cipherKey ->method ,
51+ 'iv ' => $ cipherKey ->iv ,
52+ ];
53+ }
54+
55+ /**
56+ * @param EncryptedDataV1 $encryptedData
57+ *
58+ * @throws CipherKeyNotExists
59+ * @throws DecryptionFailed
60+ */
61+ public function decrypt (string $ subjectId , mixed $ encryptedData ): mixed
62+ {
63+ $ cipherKey = $ this ->cipherKeyStore ->get ($ subjectId );
64+
65+ return $ this ->cipher ->decrypt (
66+ new CipherKey (
67+ $ cipherKey ->key ,
68+ $ encryptedData ['method ' ] ?? $ cipherKey ->method ,
69+ $ encryptedData ['iv ' ] ?? $ cipherKey ->iv
70+ ),
71+ $ encryptedData ['data ' ]
72+ );
73+ }
74+
75+ public function supports (mixed $ value ): bool
76+ {
77+ return is_array ($ value ) && array_key_exists ('__enc ' , $ value ) && $ value ['__enc ' ] === 'v1 ' ;
78+ }
79+
80+ /** @param non-empty-string $method */
81+ public static function createWithOpenssl (
82+ CipherKeyStore $ cryptoStore ,
83+ string $ method = OpensslCipherKeyFactory::DEFAULT_METHOD ,
84+ ): static {
85+ return new self (
86+ new OpensslCipher (),
87+ $ cryptoStore ,
88+ new OpensslCipherKeyFactory ($ method ),
89+ );
90+ }
91+ }
0 commit comments