55use ParagonIE \Halite \Alerts \CannotPerformOperation ;
66use ParagonIE \Halite \Halite ;
77use ParagonIE \Halite \KeyFactory ;
8+ use ParagonIE \Halite \Symmetric \AuthenticationKey ;
89use ParagonIE \Halite \Symmetric \Crypto ;
910use ParagonIE \Halite \Symmetric \EncryptionKey ;
1011use ParagonIE \HiddenString \HiddenString ;
1314 * Decorates a storage adapter to encrypt and decrypt object data and the keys
1415 * by which the data are stored.
1516 */
16- class EncryptedStorageAdapter implements StorageAdapterInterface
17+ class EncryptedStorageAdapter extends AbstractEncryptedStorageAdapter implements StorageAdapterInterface
1718{
18- const CFG_ENCRYPTION_KEY = 'encryption_key ' ;
19- const CFG_ENCRYPTION_KEY_PATH = 'encryption_key_path ' ;
20- const CFG_STORAGE_ADAPTER = 'storage_adapter ' ;
21-
19+ protected $ authenticationKey ;
2220 protected $ encryptionKey ;
2321 protected $ storageAdapter ;
2422
2523 public static function build (array $ config )
2624 {
27- if (!isset ($ config [self ::CFG_ENCRYPT_STORAGE_ADAPTER ])
28- || !$ config [self ::CFG_ENCRYPT_STORAGE_ADAPTER ] instanceof StorageAdapterInterface
25+ if (!isset ($ config [self ::CFG_STORAGE_ADAPTER ])
26+ || !$ config [self ::CFG_STORAGE_ADAPTER ] instanceof StorageAdapterInterface
2927 ) {
3028 throw new \InvalidArgumentException (
3129 'The build configuration for this storage adapter is missing an instance of StorageAdapterInterface, keyed as " '
@@ -34,6 +32,31 @@ public static function build(array $config)
3432 );
3533 }
3634
35+ if (isset ($ config [self ::CFG_AUTHENTICATION_KEY ])) {
36+ if (!$ config [self ::CFG_AUTHENTICATION_KEY ] instanceof AuthenticationKey) {
37+ throw new \InvalidArgumentException (
38+ '" ' . self ::CFG_AUTHENTICATION_KEY . '" must be an instance of AuthenticationKey. '
39+ );
40+ }
41+ $ authenticationKey = $ config [self ::CFG_AUTHENTICATION_KEY ];
42+ } elseif (isset ($ config [self ::CFG_AUTHENTICATION_KEY_PATH ])) {
43+ try {
44+ $ authenticationKey = KeyFactory::loadAuthenticationKey ($ config [self ::CFG_AUTHENTICATION_KEY_PATH ]);
45+ } catch (CannotPerformOperation $ e ) {
46+ throw new \InvalidArgumentException (
47+ '" ' . self ::CFG_AUTHENTICATION_KEY_PATH . '" must be a readable file. '
48+ );
49+ }
50+ } else {
51+ throw new \InvalidArgumentException (
52+ 'The build configuration for this storage adapter is missing an authentication key (" '
53+ . self ::CFG_AUTHENTICATION_KEY
54+ . '" or " '
55+ . self ::CFG_AUTHENTICATION_KEY_PATH
56+ . '"). '
57+ );
58+ }
59+
3760 if (isset ($ config [self ::CFG_ENCRYPTION_KEY ])) {
3861 if (!$ config [self ::CFG_ENCRYPTION_KEY ] instanceof EncryptionKey) {
3962 throw new \InvalidArgumentException (
@@ -60,89 +83,97 @@ public static function build(array $config)
6083 }
6184
6285 return new self (
63- $ config [self ::CFG_ENCRYPT_STORAGE_ADAPTER ],
86+ $ config [self ::CFG_STORAGE_ADAPTER ],
87+ $ authenticationKey ,
6488 $ encryptionKey
6589 );
6690 }
6791
6892 public function __construct (
6993 StorageAdapterInterface $ storageAdapter ,
94+ AuthenticationKey $ authenticationKey ,
7095 EncryptionKey $ encryptionKey
7196 ) {
7297 $ this ->storageAdapter = $ storageAdapter ;
98+ $ this ->authenticationKey = $ authenticationKey ;
7399 $ this ->encryptionKey = $ encryptionKey ;
74100 }
75101
76- public function setAdapter ( StorageAdapterInterface $ storageAdapter )
102+ public function setAuthenticationKey ( AuthenticationKey $ authenticationKey )
77103 {
78- $ this ->storageAdapter = $ storageAdapter ;
79- }
80-
81- public function setEncryptionKey (EncryptionKey $ encryptionKey )
82- {
83- $ this ->encryptionKey = $ encryptionKey ;
104+ $ this ->authenticationKey = $ authenticationKey ;
84105 }
85106
86107 public function setData ($ key , $ data )
87108 {
88109 try {
89- $ encryptedStorageKey = Crypto::encrypt (
90- new HiddenString ( $ key) ,
91- $ this ->encryptionKey ,
110+ $ authenticStorageKey = Crypto::authenticate (
111+ $ key ,
112+ $ this ->authenticationKey ,
92113 Halite::ENCODE_BASE64URLSAFE
93114 );
94115 } catch (CannotPerformOperation $ e ) {
95- throw new EncryptionFailureException ('Failed to encrypt the storage key. ' , null , $ e );
116+ throw new EncryptionFailureException ('Failed to hash the storage key. ' , null , $ e );
96117 }
97118
98119 try {
99- $ encryptedData = Crypto::encrypt (
100- new HiddenString ($ data ),
120+ $ encryptedBlob = Crypto::encrypt (
121+ new HiddenString ($ key . $ data ),
101122 $ this ->encryptionKey ,
102123 Halite::ENCODE_BASE64URLSAFE
103124 );
104125 } catch (CannotPerformOperation $ e ) {
105- throw new EncryptionFailureException ('Failed to encrypt the object data. ' , null , $ e );
126+ throw new EncryptionFailureException ('Failed to encrypt the storage key and object data. ' , null , $ e );
106127 }
107128
108- return $ this ->storageAdapter ->setData ($ encryptedStorageKey , $ encryptedData );
129+ return $ this ->storageAdapter ->setData ($ authenticStorageKey , $ encryptedBlob );
109130 }
110131
111132 public function getData ($ key )
112133 {
113134 try {
114- $ encryptedStorageKey = Crypto::encrypt (
115- new HiddenString ( $ key) ,
116- $ this ->encryptionKey ,
135+ $ authenticStorageKey = Crypto::authenticate (
136+ $ key ,
137+ $ this ->authenticationKey ,
117138 Halite::ENCODE_BASE64URLSAFE
118139 );
119140 } catch (CannotPerformOperation $ e ) {
120- throw new EncryptionFailureException ('Failed to encrypt the storage key. ' , null , $ e );
141+ throw new EncryptionFailureException ('Failed to hash the storage key. ' , null , $ e );
121142 }
122143
123- $ encryptedData = $ this ->storageAdapter ->getData ($ encryptedStorageKey );
144+ $ encryptedBlob = $ this ->storageAdapter ->getData ($ authenticStorageKey );
124145
125146 try {
126- $ plaintextData = (string ) Crypto::decrypt ($ encryptedData , $ this ->encryptionKey );
147+ $ plaintextBlob = (string ) Crypto::decrypt ($ encryptedBlob , $ this ->encryptionKey );
127148 } catch (CannotPerformOperation $ e ) {
128- throw new EncryptionFailureException ('Failed to decrypt the object data. ' , null , $ e );
149+ throw new EncryptionFailureException ('Failed to decrypt the storage key and object data. ' , null , $ e );
129150 }
130151
131- return $ plaintextData ;
152+ $ storageKeyLength = \strlen ($ key );
153+
154+ if (false === \hash_equals ($ key , \substr ($ plaintextBlob , 0 , $ storageKeyLength ))) {
155+ // The $plaintextBlob was definitely encrypted with our encryption key,
156+ // but the storage key is not the one in that blob.
157+ throw new EncryptionFailureException (
158+ 'The object data is not the expected one for the supplied storage key. The store has been corrupted or tampered with. '
159+ );
160+ }
161+
162+ return \substr ($ plaintextBlob , $ storageKeyLength );
132163 }
133164
134165 public function deleteData ($ key )
135166 {
136167 try {
137- $ encryptedStorageKey = Crypto::encrypt (
138- new HiddenString ( $ key) ,
139- $ this ->encryptionKey ,
168+ $ authenticStorageKey = Crypto::authenticate (
169+ $ key ,
170+ $ this ->authenticationKey ,
140171 Halite::ENCODE_BASE64URLSAFE
141172 );
142173 } catch (CannotPerformOperation $ e ) {
143- throw new EncryptionFailureException ('Failed to encrypt the storage key. ' , null , $ e );
174+ throw new EncryptionFailureException ('Failed to hash the storage key. ' , null , $ e );
144175 }
145176
146- return $ this ->storageAdapter ->deleteData ($ encryptedStorageKey );
177+ return $ this ->storageAdapter ->deleteData ($ authenticStorageKey );
147178 }
148179}
0 commit comments