Passed
Push — master ( 7001f0...829472 )
by Roeland
15:18 queued 10s
created
lib/private/Encryption/Keys/Storage.php 1 patch
Indentation   +445 added lines, -445 removed lines patch added patch discarded remove patch
@@ -39,452 +39,452 @@
 block discarded – undo
39 39
 
40 40
 class Storage implements IStorage {
41 41
 
42
-	// hidden file which indicate that the folder is a valid key storage
43
-	public const KEY_STORAGE_MARKER = '.oc_key_storage';
44
-
45
-	/** @var View */
46
-	private $view;
47
-
48
-	/** @var Util */
49
-	private $util;
50
-
51
-	// base dir where all the file related keys are stored
52
-	/** @var string */
53
-	private $keys_base_dir;
54
-
55
-	// root of the key storage default is empty which means that we use the data folder
56
-	/** @var string */
57
-	private $root_dir;
58
-
59
-	/** @var string */
60
-	private $encryption_base_dir;
61
-
62
-	/** @var string */
63
-	private $backup_base_dir;
64
-
65
-	/** @var array */
66
-	private $keyCache = [];
67
-
68
-	/** @var ICrypto */
69
-	private $crypto;
70
-
71
-	/** @var IConfig */
72
-	private $config;
73
-
74
-	/**
75
-	 * @param View $view
76
-	 * @param Util $util
77
-	 */
78
-	public function __construct(View $view, Util $util, ICrypto $crypto, IConfig $config) {
79
-		$this->view = $view;
80
-		$this->util = $util;
81
-
82
-		$this->encryption_base_dir = '/files_encryption';
83
-		$this->keys_base_dir = $this->encryption_base_dir .'/keys';
84
-		$this->backup_base_dir = $this->encryption_base_dir .'/backup';
85
-		$this->root_dir = $this->util->getKeyStorageRoot();
86
-		$this->crypto = $crypto;
87
-		$this->config = $config;
88
-	}
89
-
90
-	/**
91
-	 * @inheritdoc
92
-	 */
93
-	public function getUserKey($uid, $keyId, $encryptionModuleId) {
94
-		$path = $this->constructUserKeyPath($encryptionModuleId, $keyId, $uid);
95
-		return base64_decode($this->getKeyWithUid($path, $uid));
96
-	}
97
-
98
-	/**
99
-	 * @inheritdoc
100
-	 */
101
-	public function getFileKey($path, $keyId, $encryptionModuleId) {
102
-		$realFile = $this->util->stripPartialFileExtension($path);
103
-		$keyDir = $this->getFileKeyDir($encryptionModuleId, $realFile);
104
-		$key = $this->getKey($keyDir . $keyId)['key'];
105
-
106
-		if ($key === '' && $realFile !== $path) {
107
-			// Check if the part file has keys and use them, if no normal keys
108
-			// exist. This is required to fix copyBetweenStorage() when we
109
-			// rename a .part file over storage borders.
110
-			$keyDir = $this->getFileKeyDir($encryptionModuleId, $path);
111
-			$key = $this->getKey($keyDir . $keyId)['key'];
112
-		}
113
-
114
-		return base64_decode($key);
115
-	}
116
-
117
-	/**
118
-	 * @inheritdoc
119
-	 */
120
-	public function getSystemUserKey($keyId, $encryptionModuleId) {
121
-		$path = $this->constructUserKeyPath($encryptionModuleId, $keyId, null);
122
-		return base64_decode($this->getKeyWithUid($path, null));
123
-	}
124
-
125
-	/**
126
-	 * @inheritdoc
127
-	 */
128
-	public function setUserKey($uid, $keyId, $key, $encryptionModuleId) {
129
-		$path = $this->constructUserKeyPath($encryptionModuleId, $keyId, $uid);
130
-		return $this->setKey($path, [
131
-			'key' => base64_encode($key),
132
-			'uid' => $uid,
133
-		]);
134
-	}
135
-
136
-	/**
137
-	 * @inheritdoc
138
-	 */
139
-	public function setFileKey($path, $keyId, $key, $encryptionModuleId) {
140
-		$keyDir = $this->getFileKeyDir($encryptionModuleId, $path);
141
-		return $this->setKey($keyDir . $keyId, [
142
-			'key' => base64_encode($key),
143
-		]);
144
-	}
145
-
146
-	/**
147
-	 * @inheritdoc
148
-	 */
149
-	public function setSystemUserKey($keyId, $key, $encryptionModuleId) {
150
-		$path = $this->constructUserKeyPath($encryptionModuleId, $keyId, null);
151
-		return $this->setKey($path, [
152
-			'key' => base64_encode($key),
153
-			'uid' => null,
154
-		]);
155
-	}
156
-
157
-	/**
158
-	 * @inheritdoc
159
-	 */
160
-	public function deleteUserKey($uid, $keyId, $encryptionModuleId) {
161
-		try {
162
-			$path = $this->constructUserKeyPath($encryptionModuleId, $keyId, $uid);
163
-			return !$this->view->file_exists($path) || $this->view->unlink($path);
164
-		} catch (NoUserException $e) {
165
-			// this exception can come from initMountPoints() from setupUserMounts()
166
-			// for a deleted user.
167
-			//
168
-			// It means, that:
169
-			// - we are not running in alternative storage mode because we don't call
170
-			// initMountPoints() in that mode
171
-			// - the keys were in the user's home but since the user was deleted, the
172
-			// user's home is gone and so are the keys
173
-			//
174
-			// So there is nothing to do, just ignore.
175
-		}
176
-	}
177
-
178
-	/**
179
-	 * @inheritdoc
180
-	 */
181
-	public function deleteFileKey($path, $keyId, $encryptionModuleId) {
182
-		$keyDir = $this->getFileKeyDir($encryptionModuleId, $path);
183
-		return !$this->view->file_exists($keyDir . $keyId) || $this->view->unlink($keyDir . $keyId);
184
-	}
185
-
186
-	/**
187
-	 * @inheritdoc
188
-	 */
189
-	public function deleteAllFileKeys($path) {
190
-		$keyDir = $this->getFileKeyDir('', $path);
191
-		return !$this->view->file_exists($keyDir) || $this->view->deleteAll($keyDir);
192
-	}
193
-
194
-	/**
195
-	 * @inheritdoc
196
-	 */
197
-	public function deleteSystemUserKey($keyId, $encryptionModuleId) {
198
-		$path = $this->constructUserKeyPath($encryptionModuleId, $keyId, null);
199
-		return !$this->view->file_exists($path) || $this->view->unlink($path);
200
-	}
201
-
202
-	/**
203
-	 * construct path to users key
204
-	 *
205
-	 * @param string $encryptionModuleId
206
-	 * @param string $keyId
207
-	 * @param string $uid
208
-	 * @return string
209
-	 */
210
-	protected function constructUserKeyPath($encryptionModuleId, $keyId, $uid) {
211
-		if ($uid === null) {
212
-			$path = $this->root_dir . '/' . $this->encryption_base_dir . '/' . $encryptionModuleId . '/' . $keyId;
213
-		} else {
214
-			$path = $this->root_dir . '/' . $uid . $this->encryption_base_dir . '/'
215
-				. $encryptionModuleId . '/' . $uid . '.' . $keyId;
216
-		}
217
-
218
-		return \OC\Files\Filesystem::normalizePath($path);
219
-	}
220
-
221
-	/**
222
-	 * @param string $path
223
-	 * @param string|null $uid
224
-	 * @return string
225
-	 * @throws ServerNotAvailableException
226
-	 *
227
-	 * Small helper function to fetch the key and verify the value for user and system keys
228
-	 */
229
-	private function getKeyWithUid(string $path, ?string $uid): string {
230
-		$data = $this->getKey($path);
231
-
232
-		if (!isset($data['key'])) {
233
-			throw new ServerNotAvailableException('Key is invalid');
234
-		}
235
-
236
-		if ($data['key'] === '') {
237
-			return '';
238
-		}
239
-
240
-		if (!array_key_exists('uid', $data) || $data['uid'] !== $uid) {
241
-			// If the migration is done we error out
242
-			$versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0');
243
-			if (version_compare($versionFromBeforeUpdate, '20.0.0.1', '<=')) {
244
-				return $data['key'];
245
-			}
246
-
247
-			if ($this->config->getSystemValueBool('encryption.key_storage_migrated', true)) {
248
-				throw new ServerNotAvailableException('Key has been modified');
249
-			} else {
250
-				//Otherwise we migrate
251
-				$data['uid'] = $uid;
252
-				$this->setKey($path, $data);
253
-			}
254
-		}
255
-
256
-		return $data['key'];
257
-	}
258
-
259
-	/**
260
-	 * read key from hard disk
261
-	 *
262
-	 * @param string $path to key
263
-	 * @return array containing key as base64encoded key, and possible the uid
264
-	 */
265
-	private function getKey($path): array {
266
-		$key = [
267
-			'key' => '',
268
-		];
269
-
270
-		if ($this->view->file_exists($path)) {
271
-			if (isset($this->keyCache[$path])) {
272
-				$key = $this->keyCache[$path];
273
-			} else {
274
-				$data = $this->view->file_get_contents($path);
275
-
276
-				// Version <20.0.0.1 doesn't have this
277
-				$versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0');
278
-				if (version_compare($versionFromBeforeUpdate, '20.0.0.1', '<=')) {
279
-					$key = [
280
-						'key' => base64_encode($data),
281
-					];
282
-				} else {
283
-					if ($this->config->getSystemValueBool('encryption.key_storage_migrated', true)) {
284
-						try {
285
-							$clearData = $this->crypto->decrypt($data);
286
-						} catch (\Exception $e) {
287
-							throw new ServerNotAvailableException('Could not decrypt key', 0, $e);
288
-						}
289
-
290
-						$dataArray = json_decode($clearData, true);
291
-						if ($dataArray === null) {
292
-							throw new ServerNotAvailableException('Invalid encryption key');
293
-						}
294
-
295
-						$key = $dataArray;
296
-					} else {
297
-						/*
42
+    // hidden file which indicate that the folder is a valid key storage
43
+    public const KEY_STORAGE_MARKER = '.oc_key_storage';
44
+
45
+    /** @var View */
46
+    private $view;
47
+
48
+    /** @var Util */
49
+    private $util;
50
+
51
+    // base dir where all the file related keys are stored
52
+    /** @var string */
53
+    private $keys_base_dir;
54
+
55
+    // root of the key storage default is empty which means that we use the data folder
56
+    /** @var string */
57
+    private $root_dir;
58
+
59
+    /** @var string */
60
+    private $encryption_base_dir;
61
+
62
+    /** @var string */
63
+    private $backup_base_dir;
64
+
65
+    /** @var array */
66
+    private $keyCache = [];
67
+
68
+    /** @var ICrypto */
69
+    private $crypto;
70
+
71
+    /** @var IConfig */
72
+    private $config;
73
+
74
+    /**
75
+     * @param View $view
76
+     * @param Util $util
77
+     */
78
+    public function __construct(View $view, Util $util, ICrypto $crypto, IConfig $config) {
79
+        $this->view = $view;
80
+        $this->util = $util;
81
+
82
+        $this->encryption_base_dir = '/files_encryption';
83
+        $this->keys_base_dir = $this->encryption_base_dir .'/keys';
84
+        $this->backup_base_dir = $this->encryption_base_dir .'/backup';
85
+        $this->root_dir = $this->util->getKeyStorageRoot();
86
+        $this->crypto = $crypto;
87
+        $this->config = $config;
88
+    }
89
+
90
+    /**
91
+     * @inheritdoc
92
+     */
93
+    public function getUserKey($uid, $keyId, $encryptionModuleId) {
94
+        $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, $uid);
95
+        return base64_decode($this->getKeyWithUid($path, $uid));
96
+    }
97
+
98
+    /**
99
+     * @inheritdoc
100
+     */
101
+    public function getFileKey($path, $keyId, $encryptionModuleId) {
102
+        $realFile = $this->util->stripPartialFileExtension($path);
103
+        $keyDir = $this->getFileKeyDir($encryptionModuleId, $realFile);
104
+        $key = $this->getKey($keyDir . $keyId)['key'];
105
+
106
+        if ($key === '' && $realFile !== $path) {
107
+            // Check if the part file has keys and use them, if no normal keys
108
+            // exist. This is required to fix copyBetweenStorage() when we
109
+            // rename a .part file over storage borders.
110
+            $keyDir = $this->getFileKeyDir($encryptionModuleId, $path);
111
+            $key = $this->getKey($keyDir . $keyId)['key'];
112
+        }
113
+
114
+        return base64_decode($key);
115
+    }
116
+
117
+    /**
118
+     * @inheritdoc
119
+     */
120
+    public function getSystemUserKey($keyId, $encryptionModuleId) {
121
+        $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, null);
122
+        return base64_decode($this->getKeyWithUid($path, null));
123
+    }
124
+
125
+    /**
126
+     * @inheritdoc
127
+     */
128
+    public function setUserKey($uid, $keyId, $key, $encryptionModuleId) {
129
+        $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, $uid);
130
+        return $this->setKey($path, [
131
+            'key' => base64_encode($key),
132
+            'uid' => $uid,
133
+        ]);
134
+    }
135
+
136
+    /**
137
+     * @inheritdoc
138
+     */
139
+    public function setFileKey($path, $keyId, $key, $encryptionModuleId) {
140
+        $keyDir = $this->getFileKeyDir($encryptionModuleId, $path);
141
+        return $this->setKey($keyDir . $keyId, [
142
+            'key' => base64_encode($key),
143
+        ]);
144
+    }
145
+
146
+    /**
147
+     * @inheritdoc
148
+     */
149
+    public function setSystemUserKey($keyId, $key, $encryptionModuleId) {
150
+        $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, null);
151
+        return $this->setKey($path, [
152
+            'key' => base64_encode($key),
153
+            'uid' => null,
154
+        ]);
155
+    }
156
+
157
+    /**
158
+     * @inheritdoc
159
+     */
160
+    public function deleteUserKey($uid, $keyId, $encryptionModuleId) {
161
+        try {
162
+            $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, $uid);
163
+            return !$this->view->file_exists($path) || $this->view->unlink($path);
164
+        } catch (NoUserException $e) {
165
+            // this exception can come from initMountPoints() from setupUserMounts()
166
+            // for a deleted user.
167
+            //
168
+            // It means, that:
169
+            // - we are not running in alternative storage mode because we don't call
170
+            // initMountPoints() in that mode
171
+            // - the keys were in the user's home but since the user was deleted, the
172
+            // user's home is gone and so are the keys
173
+            //
174
+            // So there is nothing to do, just ignore.
175
+        }
176
+    }
177
+
178
+    /**
179
+     * @inheritdoc
180
+     */
181
+    public function deleteFileKey($path, $keyId, $encryptionModuleId) {
182
+        $keyDir = $this->getFileKeyDir($encryptionModuleId, $path);
183
+        return !$this->view->file_exists($keyDir . $keyId) || $this->view->unlink($keyDir . $keyId);
184
+    }
185
+
186
+    /**
187
+     * @inheritdoc
188
+     */
189
+    public function deleteAllFileKeys($path) {
190
+        $keyDir = $this->getFileKeyDir('', $path);
191
+        return !$this->view->file_exists($keyDir) || $this->view->deleteAll($keyDir);
192
+    }
193
+
194
+    /**
195
+     * @inheritdoc
196
+     */
197
+    public function deleteSystemUserKey($keyId, $encryptionModuleId) {
198
+        $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, null);
199
+        return !$this->view->file_exists($path) || $this->view->unlink($path);
200
+    }
201
+
202
+    /**
203
+     * construct path to users key
204
+     *
205
+     * @param string $encryptionModuleId
206
+     * @param string $keyId
207
+     * @param string $uid
208
+     * @return string
209
+     */
210
+    protected function constructUserKeyPath($encryptionModuleId, $keyId, $uid) {
211
+        if ($uid === null) {
212
+            $path = $this->root_dir . '/' . $this->encryption_base_dir . '/' . $encryptionModuleId . '/' . $keyId;
213
+        } else {
214
+            $path = $this->root_dir . '/' . $uid . $this->encryption_base_dir . '/'
215
+                . $encryptionModuleId . '/' . $uid . '.' . $keyId;
216
+        }
217
+
218
+        return \OC\Files\Filesystem::normalizePath($path);
219
+    }
220
+
221
+    /**
222
+     * @param string $path
223
+     * @param string|null $uid
224
+     * @return string
225
+     * @throws ServerNotAvailableException
226
+     *
227
+     * Small helper function to fetch the key and verify the value for user and system keys
228
+     */
229
+    private function getKeyWithUid(string $path, ?string $uid): string {
230
+        $data = $this->getKey($path);
231
+
232
+        if (!isset($data['key'])) {
233
+            throw new ServerNotAvailableException('Key is invalid');
234
+        }
235
+
236
+        if ($data['key'] === '') {
237
+            return '';
238
+        }
239
+
240
+        if (!array_key_exists('uid', $data) || $data['uid'] !== $uid) {
241
+            // If the migration is done we error out
242
+            $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0');
243
+            if (version_compare($versionFromBeforeUpdate, '20.0.0.1', '<=')) {
244
+                return $data['key'];
245
+            }
246
+
247
+            if ($this->config->getSystemValueBool('encryption.key_storage_migrated', true)) {
248
+                throw new ServerNotAvailableException('Key has been modified');
249
+            } else {
250
+                //Otherwise we migrate
251
+                $data['uid'] = $uid;
252
+                $this->setKey($path, $data);
253
+            }
254
+        }
255
+
256
+        return $data['key'];
257
+    }
258
+
259
+    /**
260
+     * read key from hard disk
261
+     *
262
+     * @param string $path to key
263
+     * @return array containing key as base64encoded key, and possible the uid
264
+     */
265
+    private function getKey($path): array {
266
+        $key = [
267
+            'key' => '',
268
+        ];
269
+
270
+        if ($this->view->file_exists($path)) {
271
+            if (isset($this->keyCache[$path])) {
272
+                $key = $this->keyCache[$path];
273
+            } else {
274
+                $data = $this->view->file_get_contents($path);
275
+
276
+                // Version <20.0.0.1 doesn't have this
277
+                $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0');
278
+                if (version_compare($versionFromBeforeUpdate, '20.0.0.1', '<=')) {
279
+                    $key = [
280
+                        'key' => base64_encode($data),
281
+                    ];
282
+                } else {
283
+                    if ($this->config->getSystemValueBool('encryption.key_storage_migrated', true)) {
284
+                        try {
285
+                            $clearData = $this->crypto->decrypt($data);
286
+                        } catch (\Exception $e) {
287
+                            throw new ServerNotAvailableException('Could not decrypt key', 0, $e);
288
+                        }
289
+
290
+                        $dataArray = json_decode($clearData, true);
291
+                        if ($dataArray === null) {
292
+                            throw new ServerNotAvailableException('Invalid encryption key');
293
+                        }
294
+
295
+                        $key = $dataArray;
296
+                    } else {
297
+                        /*
298 298
 						 * Even if not all keys are migrated we should still try to decrypt it (in case some have moved).
299 299
 						 * However it is only a failure now if it is an array and decryption fails
300 300
 						 */
301
-						$fallback = false;
302
-						try {
303
-							$clearData = $this->crypto->decrypt($data);
304
-						} catch (\Throwable $e) {
305
-							$fallback = true;
306
-						}
307
-
308
-						if (!$fallback) {
309
-							$dataArray = json_decode($clearData, true);
310
-							if ($dataArray === null) {
311
-								throw new ServerNotAvailableException('Invalid encryption key');
312
-							}
313
-							$key = $dataArray;
314
-						} else {
315
-							$key = [
316
-								'key' => base64_encode($data),
317
-							];
318
-						}
319
-					}
320
-				}
321
-
322
-				$this->keyCache[$path] = $key;
323
-			}
324
-		}
325
-
326
-		return $key;
327
-	}
328
-
329
-	/**
330
-	 * write key to disk
331
-	 *
332
-	 *
333
-	 * @param string $path path to key directory
334
-	 * @param array $key key
335
-	 * @return bool
336
-	 */
337
-	private function setKey($path, $key) {
338
-		$this->keySetPreparation(dirname($path));
339
-
340
-		$versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0');
341
-		if (version_compare($versionFromBeforeUpdate, '20.0.0.1', '<=')) {
342
-			// Only store old format if this happens during the migration.
343
-			// TODO: Remove for 21
344
-			$data = base64_decode($key['key']);
345
-		} else {
346
-			// Wrap the data
347
-			$data = $this->crypto->encrypt(json_encode($key));
348
-		}
349
-
350
-		$result = $this->view->file_put_contents($path, $data);
351
-
352
-		if (is_int($result) && $result > 0) {
353
-			$this->keyCache[$path] = $key;
354
-			return true;
355
-		}
356
-
357
-		return false;
358
-	}
359
-
360
-	/**
361
-	 * get path to key folder for a given file
362
-	 *
363
-	 * @param string $encryptionModuleId
364
-	 * @param string $path path to the file, relative to data/
365
-	 * @return string
366
-	 */
367
-	private function getFileKeyDir($encryptionModuleId, $path) {
368
-		list($owner, $filename) = $this->util->getUidAndFilename($path);
369
-
370
-		// in case of system wide mount points the keys are stored directly in the data directory
371
-		if ($this->util->isSystemWideMountPoint($filename, $owner)) {
372
-			$keyPath = $this->root_dir . '/' . $this->keys_base_dir . $filename . '/';
373
-		} else {
374
-			$keyPath = $this->root_dir . '/' . $owner . $this->keys_base_dir . $filename . '/';
375
-		}
376
-
377
-		return Filesystem::normalizePath($keyPath . $encryptionModuleId . '/', false);
378
-	}
379
-
380
-	/**
381
-	 * move keys if a file was renamed
382
-	 *
383
-	 * @param string $source
384
-	 * @param string $target
385
-	 * @return boolean
386
-	 */
387
-	public function renameKeys($source, $target) {
388
-		$sourcePath = $this->getPathToKeys($source);
389
-		$targetPath = $this->getPathToKeys($target);
390
-
391
-		if ($this->view->file_exists($sourcePath)) {
392
-			$this->keySetPreparation(dirname($targetPath));
393
-			$this->view->rename($sourcePath, $targetPath);
394
-
395
-			return true;
396
-		}
397
-
398
-		return false;
399
-	}
400
-
401
-
402
-	/**
403
-	 * copy keys if a file was renamed
404
-	 *
405
-	 * @param string $source
406
-	 * @param string $target
407
-	 * @return boolean
408
-	 */
409
-	public function copyKeys($source, $target) {
410
-		$sourcePath = $this->getPathToKeys($source);
411
-		$targetPath = $this->getPathToKeys($target);
412
-
413
-		if ($this->view->file_exists($sourcePath)) {
414
-			$this->keySetPreparation(dirname($targetPath));
415
-			$this->view->copy($sourcePath, $targetPath);
416
-			return true;
417
-		}
418
-
419
-		return false;
420
-	}
421
-
422
-	/**
423
-	 * backup keys of a given encryption module
424
-	 *
425
-	 * @param string $encryptionModuleId
426
-	 * @param string $purpose
427
-	 * @param string $uid
428
-	 * @return bool
429
-	 * @since 12.0.0
430
-	 */
431
-	public function backupUserKeys($encryptionModuleId, $purpose, $uid) {
432
-		$source = $uid . $this->encryption_base_dir . '/' . $encryptionModuleId;
433
-		$backupDir = $uid . $this->backup_base_dir;
434
-		if (!$this->view->file_exists($backupDir)) {
435
-			$this->view->mkdir($backupDir);
436
-		}
437
-
438
-		$backupDir = $backupDir . '/' . $purpose . '.' . $encryptionModuleId . '.' . $this->getTimestamp();
439
-		$this->view->mkdir($backupDir);
440
-
441
-		return $this->view->copy($source, $backupDir);
442
-	}
443
-
444
-	/**
445
-	 * get the current timestamp
446
-	 *
447
-	 * @return int
448
-	 */
449
-	protected function getTimestamp() {
450
-		return time();
451
-	}
452
-
453
-	/**
454
-	 * get system wide path and detect mount points
455
-	 *
456
-	 * @param string $path
457
-	 * @return string
458
-	 */
459
-	protected function getPathToKeys($path) {
460
-		list($owner, $relativePath) = $this->util->getUidAndFilename($path);
461
-		$systemWideMountPoint = $this->util->isSystemWideMountPoint($relativePath, $owner);
462
-
463
-		if ($systemWideMountPoint) {
464
-			$systemPath = $this->root_dir . '/' . $this->keys_base_dir . $relativePath . '/';
465
-		} else {
466
-			$systemPath = $this->root_dir . '/' . $owner . $this->keys_base_dir . $relativePath . '/';
467
-		}
468
-
469
-		return  Filesystem::normalizePath($systemPath, false);
470
-	}
471
-
472
-	/**
473
-	 * Make preparations to filesystem for saving a key file
474
-	 *
475
-	 * @param string $path relative to the views root
476
-	 */
477
-	protected function keySetPreparation($path) {
478
-		// If the file resides within a subdirectory, create it
479
-		if (!$this->view->file_exists($path)) {
480
-			$sub_dirs = explode('/', ltrim($path, '/'));
481
-			$dir = '';
482
-			foreach ($sub_dirs as $sub_dir) {
483
-				$dir .= '/' . $sub_dir;
484
-				if (!$this->view->is_dir($dir)) {
485
-					$this->view->mkdir($dir);
486
-				}
487
-			}
488
-		}
489
-	}
301
+                        $fallback = false;
302
+                        try {
303
+                            $clearData = $this->crypto->decrypt($data);
304
+                        } catch (\Throwable $e) {
305
+                            $fallback = true;
306
+                        }
307
+
308
+                        if (!$fallback) {
309
+                            $dataArray = json_decode($clearData, true);
310
+                            if ($dataArray === null) {
311
+                                throw new ServerNotAvailableException('Invalid encryption key');
312
+                            }
313
+                            $key = $dataArray;
314
+                        } else {
315
+                            $key = [
316
+                                'key' => base64_encode($data),
317
+                            ];
318
+                        }
319
+                    }
320
+                }
321
+
322
+                $this->keyCache[$path] = $key;
323
+            }
324
+        }
325
+
326
+        return $key;
327
+    }
328
+
329
+    /**
330
+     * write key to disk
331
+     *
332
+     *
333
+     * @param string $path path to key directory
334
+     * @param array $key key
335
+     * @return bool
336
+     */
337
+    private function setKey($path, $key) {
338
+        $this->keySetPreparation(dirname($path));
339
+
340
+        $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0');
341
+        if (version_compare($versionFromBeforeUpdate, '20.0.0.1', '<=')) {
342
+            // Only store old format if this happens during the migration.
343
+            // TODO: Remove for 21
344
+            $data = base64_decode($key['key']);
345
+        } else {
346
+            // Wrap the data
347
+            $data = $this->crypto->encrypt(json_encode($key));
348
+        }
349
+
350
+        $result = $this->view->file_put_contents($path, $data);
351
+
352
+        if (is_int($result) && $result > 0) {
353
+            $this->keyCache[$path] = $key;
354
+            return true;
355
+        }
356
+
357
+        return false;
358
+    }
359
+
360
+    /**
361
+     * get path to key folder for a given file
362
+     *
363
+     * @param string $encryptionModuleId
364
+     * @param string $path path to the file, relative to data/
365
+     * @return string
366
+     */
367
+    private function getFileKeyDir($encryptionModuleId, $path) {
368
+        list($owner, $filename) = $this->util->getUidAndFilename($path);
369
+
370
+        // in case of system wide mount points the keys are stored directly in the data directory
371
+        if ($this->util->isSystemWideMountPoint($filename, $owner)) {
372
+            $keyPath = $this->root_dir . '/' . $this->keys_base_dir . $filename . '/';
373
+        } else {
374
+            $keyPath = $this->root_dir . '/' . $owner . $this->keys_base_dir . $filename . '/';
375
+        }
376
+
377
+        return Filesystem::normalizePath($keyPath . $encryptionModuleId . '/', false);
378
+    }
379
+
380
+    /**
381
+     * move keys if a file was renamed
382
+     *
383
+     * @param string $source
384
+     * @param string $target
385
+     * @return boolean
386
+     */
387
+    public function renameKeys($source, $target) {
388
+        $sourcePath = $this->getPathToKeys($source);
389
+        $targetPath = $this->getPathToKeys($target);
390
+
391
+        if ($this->view->file_exists($sourcePath)) {
392
+            $this->keySetPreparation(dirname($targetPath));
393
+            $this->view->rename($sourcePath, $targetPath);
394
+
395
+            return true;
396
+        }
397
+
398
+        return false;
399
+    }
400
+
401
+
402
+    /**
403
+     * copy keys if a file was renamed
404
+     *
405
+     * @param string $source
406
+     * @param string $target
407
+     * @return boolean
408
+     */
409
+    public function copyKeys($source, $target) {
410
+        $sourcePath = $this->getPathToKeys($source);
411
+        $targetPath = $this->getPathToKeys($target);
412
+
413
+        if ($this->view->file_exists($sourcePath)) {
414
+            $this->keySetPreparation(dirname($targetPath));
415
+            $this->view->copy($sourcePath, $targetPath);
416
+            return true;
417
+        }
418
+
419
+        return false;
420
+    }
421
+
422
+    /**
423
+     * backup keys of a given encryption module
424
+     *
425
+     * @param string $encryptionModuleId
426
+     * @param string $purpose
427
+     * @param string $uid
428
+     * @return bool
429
+     * @since 12.0.0
430
+     */
431
+    public function backupUserKeys($encryptionModuleId, $purpose, $uid) {
432
+        $source = $uid . $this->encryption_base_dir . '/' . $encryptionModuleId;
433
+        $backupDir = $uid . $this->backup_base_dir;
434
+        if (!$this->view->file_exists($backupDir)) {
435
+            $this->view->mkdir($backupDir);
436
+        }
437
+
438
+        $backupDir = $backupDir . '/' . $purpose . '.' . $encryptionModuleId . '.' . $this->getTimestamp();
439
+        $this->view->mkdir($backupDir);
440
+
441
+        return $this->view->copy($source, $backupDir);
442
+    }
443
+
444
+    /**
445
+     * get the current timestamp
446
+     *
447
+     * @return int
448
+     */
449
+    protected function getTimestamp() {
450
+        return time();
451
+    }
452
+
453
+    /**
454
+     * get system wide path and detect mount points
455
+     *
456
+     * @param string $path
457
+     * @return string
458
+     */
459
+    protected function getPathToKeys($path) {
460
+        list($owner, $relativePath) = $this->util->getUidAndFilename($path);
461
+        $systemWideMountPoint = $this->util->isSystemWideMountPoint($relativePath, $owner);
462
+
463
+        if ($systemWideMountPoint) {
464
+            $systemPath = $this->root_dir . '/' . $this->keys_base_dir . $relativePath . '/';
465
+        } else {
466
+            $systemPath = $this->root_dir . '/' . $owner . $this->keys_base_dir . $relativePath . '/';
467
+        }
468
+
469
+        return  Filesystem::normalizePath($systemPath, false);
470
+    }
471
+
472
+    /**
473
+     * Make preparations to filesystem for saving a key file
474
+     *
475
+     * @param string $path relative to the views root
476
+     */
477
+    protected function keySetPreparation($path) {
478
+        // If the file resides within a subdirectory, create it
479
+        if (!$this->view->file_exists($path)) {
480
+            $sub_dirs = explode('/', ltrim($path, '/'));
481
+            $dir = '';
482
+            foreach ($sub_dirs as $sub_dir) {
483
+                $dir .= '/' . $sub_dir;
484
+                if (!$this->view->is_dir($dir)) {
485
+                    $this->view->mkdir($dir);
486
+                }
487
+            }
488
+        }
489
+    }
490 490
 }
Please login to merge, or discard this patch.