keymanager.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. <?php
  2. /**
  3. * @author Björn Schießle <schiessle@owncloud.com>
  4. * @author Clark Tomlinson <fallen013@gmail.com>
  5. * @author Thomas Müller <thomas.mueller@tmit.eu>
  6. *
  7. * @copyright Copyright (c) 2015, ownCloud, Inc.
  8. * @license AGPL-3.0
  9. *
  10. * This code is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU Affero General Public License, version 3,
  12. * as published by the Free Software Foundation.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU Affero General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public License, version 3,
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>
  21. *
  22. */
  23. namespace OCA\Encryption;
  24. use OC\Encryption\Exceptions\DecryptionFailedException;
  25. use OCA\Encryption\Crypto\Encryption;
  26. use OCA\Encryption\Exceptions\PrivateKeyMissingException;
  27. use OCA\Encryption\Exceptions\PublicKeyMissingException;
  28. use OCA\Encryption\Crypto\Crypt;
  29. use OCP\Encryption\Keys\IStorage;
  30. use OCP\IConfig;
  31. use OCP\ILogger;
  32. use OCP\IUserSession;
  33. class KeyManager {
  34. /**
  35. * @var Session
  36. */
  37. protected $session;
  38. /**
  39. * @var IStorage
  40. */
  41. private $keyStorage;
  42. /**
  43. * @var Crypt
  44. */
  45. private $crypt;
  46. /**
  47. * @var string
  48. */
  49. private $recoveryKeyId;
  50. /**
  51. * @var string
  52. */
  53. private $publicShareKeyId;
  54. /**
  55. * @var string UserID
  56. */
  57. private $keyId;
  58. /**
  59. * @var string
  60. */
  61. private $publicKeyId = 'publicKey';
  62. /**
  63. * @var string
  64. */
  65. private $privateKeyId = 'privateKey';
  66. /**
  67. * @var string
  68. */
  69. private $shareKeyId = 'shareKey';
  70. /**
  71. * @var string
  72. */
  73. private $fileKeyId = 'fileKey';
  74. /**
  75. * @var IConfig
  76. */
  77. private $config;
  78. /**
  79. * @var ILogger
  80. */
  81. private $log;
  82. /**
  83. * @var Util
  84. */
  85. private $util;
  86. /**
  87. * @param IStorage $keyStorage
  88. * @param Crypt $crypt
  89. * @param IConfig $config
  90. * @param IUserSession $userSession
  91. * @param Session $session
  92. * @param ILogger $log
  93. * @param Util $util
  94. */
  95. public function __construct(
  96. IStorage $keyStorage,
  97. Crypt $crypt,
  98. IConfig $config,
  99. IUserSession $userSession,
  100. Session $session,
  101. ILogger $log,
  102. Util $util
  103. ) {
  104. $this->util = $util;
  105. $this->session = $session;
  106. $this->keyStorage = $keyStorage;
  107. $this->crypt = $crypt;
  108. $this->config = $config;
  109. $this->log = $log;
  110. $this->recoveryKeyId = $this->config->getAppValue('encryption',
  111. 'recoveryKeyId');
  112. if (empty($this->recoveryKeyId)) {
  113. $this->recoveryKeyId = 'recoveryKey_' . substr(md5(time()), 0, 8);
  114. $this->config->setAppValue('encryption',
  115. 'recoveryKeyId',
  116. $this->recoveryKeyId);
  117. }
  118. $this->publicShareKeyId = $this->config->getAppValue('encryption',
  119. 'publicShareKeyId');
  120. if (empty($this->publicShareKeyId)) {
  121. $this->publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8);
  122. $this->config->setAppValue('encryption', 'publicShareKeyId', $this->publicShareKeyId);
  123. }
  124. $this->keyId = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false;
  125. $this->log = $log;
  126. }
  127. public function validateShareKey() {
  128. $shareKey = $this->getPublicShareKey();
  129. if (empty($shareKey)) {
  130. $keyPair = $this->crypt->createKeyPair();
  131. // Save public key
  132. $this->keyStorage->setSystemUserKey(
  133. $this->publicShareKeyId . '.publicKey', $keyPair['publicKey'],
  134. Encryption::ID);
  135. // Encrypt private key empty passphrase
  136. $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], '');
  137. $header = $this->crypt->generateHeader();
  138. $this->setSystemPrivateKey($this->publicShareKeyId, $header . $encryptedKey);
  139. }
  140. }
  141. /**
  142. * @return bool
  143. */
  144. public function recoveryKeyExists() {
  145. $key = $this->getRecoveryKey();
  146. return (!empty($key));
  147. }
  148. /**
  149. * get recovery key
  150. *
  151. * @return string
  152. */
  153. public function getRecoveryKey() {
  154. return $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.publicKey', Encryption::ID);
  155. }
  156. /**
  157. * get recovery key ID
  158. *
  159. * @return string
  160. */
  161. public function getRecoveryKeyId() {
  162. return $this->recoveryKeyId;
  163. }
  164. /**
  165. * @param $password
  166. * @return bool
  167. */
  168. public function checkRecoveryPassword($password) {
  169. $recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.privateKey', Encryption::ID);
  170. $decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey, $password);
  171. if ($decryptedRecoveryKey) {
  172. return true;
  173. }
  174. return false;
  175. }
  176. /**
  177. * @param string $uid
  178. * @param string $password
  179. * @param string $keyPair
  180. * @return bool
  181. */
  182. public function storeKeyPair($uid, $password, $keyPair) {
  183. // Save Public Key
  184. $this->setPublicKey($uid, $keyPair['publicKey']);
  185. $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $password, $uid);
  186. $header = $this->crypt->generateHeader();
  187. if ($encryptedKey) {
  188. $this->setPrivateKey($uid, $header . $encryptedKey);
  189. return true;
  190. }
  191. return false;
  192. }
  193. /**
  194. * @param string $password
  195. * @param array $keyPair
  196. * @return bool
  197. */
  198. public function setRecoveryKey($password, $keyPair) {
  199. // Save Public Key
  200. $this->keyStorage->setSystemUserKey($this->getRecoveryKeyId().
  201. '.publicKey',
  202. $keyPair['publicKey'],
  203. Encryption::ID);
  204. $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $password);
  205. $header = $this->crypt->generateHeader();
  206. if ($encryptedKey) {
  207. $this->setSystemPrivateKey($this->getRecoveryKeyId(), $header . $encryptedKey);
  208. return true;
  209. }
  210. return false;
  211. }
  212. /**
  213. * @param $userId
  214. * @param $key
  215. * @return bool
  216. */
  217. public function setPublicKey($userId, $key) {
  218. return $this->keyStorage->setUserKey($userId, $this->publicKeyId, $key, Encryption::ID);
  219. }
  220. /**
  221. * @param $userId
  222. * @param $key
  223. * @return bool
  224. */
  225. public function setPrivateKey($userId, $key) {
  226. return $this->keyStorage->setUserKey($userId,
  227. $this->privateKeyId,
  228. $key,
  229. Encryption::ID);
  230. }
  231. /**
  232. * write file key to key storage
  233. *
  234. * @param string $path
  235. * @param string $key
  236. * @return boolean
  237. */
  238. public function setFileKey($path, $key) {
  239. return $this->keyStorage->setFileKey($path, $this->fileKeyId, $key, Encryption::ID);
  240. }
  241. /**
  242. * set all file keys (the file key and the corresponding share keys)
  243. *
  244. * @param string $path
  245. * @param array $keys
  246. */
  247. public function setAllFileKeys($path, $keys) {
  248. $this->setFileKey($path, $keys['data']);
  249. foreach ($keys['keys'] as $uid => $keyFile) {
  250. $this->setShareKey($path, $uid, $keyFile);
  251. }
  252. }
  253. /**
  254. * write share key to the key storage
  255. *
  256. * @param string $path
  257. * @param string $uid
  258. * @param string $key
  259. * @return boolean
  260. */
  261. public function setShareKey($path, $uid, $key) {
  262. $keyId = $uid . '.' . $this->shareKeyId;
  263. return $this->keyStorage->setFileKey($path, $keyId, $key, Encryption::ID);
  264. }
  265. /**
  266. * Decrypt private key and store it
  267. *
  268. * @param string $uid userid
  269. * @param string $passPhrase users password
  270. * @return boolean
  271. */
  272. public function init($uid, $passPhrase) {
  273. $this->session->setStatus(Session::INIT_EXECUTED);
  274. try {
  275. $privateKey = $this->getPrivateKey($uid);
  276. $privateKey = $this->crypt->decryptPrivateKey($privateKey, $passPhrase, $uid);
  277. } catch (PrivateKeyMissingException $e) {
  278. return false;
  279. } catch (DecryptionFailedException $e) {
  280. return false;
  281. }
  282. if ($privateKey) {
  283. $this->session->setPrivateKey($privateKey);
  284. $this->session->setStatus(Session::INIT_SUCCESSFUL);
  285. return true;
  286. }
  287. return false;
  288. }
  289. /**
  290. * @param $userId
  291. * @return mixed
  292. * @throws PrivateKeyMissingException
  293. */
  294. public function getPrivateKey($userId) {
  295. $privateKey = $this->keyStorage->getUserKey($userId,
  296. $this->privateKeyId, Encryption::ID);
  297. if (strlen($privateKey) !== 0) {
  298. return $privateKey;
  299. }
  300. throw new PrivateKeyMissingException($userId);
  301. }
  302. /**
  303. * @param $path
  304. * @param $uid
  305. * @return string
  306. */
  307. public function getFileKey($path, $uid) {
  308. $encryptedFileKey = $this->keyStorage->getFileKey($path, $this->fileKeyId, Encryption::ID);
  309. if (is_null($uid)) {
  310. $uid = $this->getPublicShareKeyId();
  311. $shareKey = $this->getShareKey($path, $uid);
  312. $privateKey = $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.privateKey', Encryption::ID);
  313. $privateKey = $this->crypt->decryptPrivateKey($privateKey);
  314. } else {
  315. $shareKey = $this->getShareKey($path, $uid);
  316. $privateKey = $this->session->getPrivateKey();
  317. }
  318. if ($encryptedFileKey && $shareKey && $privateKey) {
  319. return $this->crypt->multiKeyDecrypt($encryptedFileKey,
  320. $shareKey,
  321. $privateKey);
  322. }
  323. return '';
  324. }
  325. /**
  326. * get the encrypted file key
  327. *
  328. * @param $path
  329. * @return string
  330. */
  331. public function getEncryptedFileKey($path) {
  332. $encryptedFileKey = $this->keyStorage->getFileKey($path,
  333. $this->fileKeyId, Encryption::ID);
  334. return $encryptedFileKey;
  335. }
  336. /**
  337. * delete share key
  338. *
  339. * @param string $path
  340. * @param string $keyId
  341. * @return boolean
  342. */
  343. public function deleteShareKey($path, $keyId) {
  344. return $this->keyStorage->deleteFileKey(
  345. $path,
  346. $keyId . '.' . $this->shareKeyId,
  347. Encryption::ID);
  348. }
  349. /**
  350. * @param $path
  351. * @param $uid
  352. * @return mixed
  353. */
  354. public function getShareKey($path, $uid) {
  355. $keyId = $uid . '.' . $this->shareKeyId;
  356. return $this->keyStorage->getFileKey($path, $keyId, Encryption::ID);
  357. }
  358. /**
  359. * check if user has a private and a public key
  360. *
  361. * @param string $userId
  362. * @return bool
  363. * @throws PrivateKeyMissingException
  364. * @throws PublicKeyMissingException
  365. */
  366. public function userHasKeys($userId) {
  367. $privateKey = $publicKey = true;
  368. try {
  369. $this->getPrivateKey($userId);
  370. } catch (PrivateKeyMissingException $e) {
  371. $privateKey = false;
  372. $exception = $e;
  373. }
  374. try {
  375. $this->getPublicKey($userId);
  376. } catch (PublicKeyMissingException $e) {
  377. $publicKey = false;
  378. $exception = $e;
  379. }
  380. if ($privateKey && $publicKey) {
  381. return true;
  382. } elseif (!$privateKey && !$publicKey) {
  383. return false;
  384. } else {
  385. throw $exception;
  386. }
  387. }
  388. /**
  389. * @param $userId
  390. * @return mixed
  391. * @throws PublicKeyMissingException
  392. */
  393. public function getPublicKey($userId) {
  394. $publicKey = $this->keyStorage->getUserKey($userId, $this->publicKeyId, Encryption::ID);
  395. if (strlen($publicKey) !== 0) {
  396. return $publicKey;
  397. }
  398. throw new PublicKeyMissingException($userId);
  399. }
  400. public function getPublicShareKeyId() {
  401. return $this->publicShareKeyId;
  402. }
  403. /**
  404. * get public key for public link shares
  405. *
  406. * @return string
  407. */
  408. public function getPublicShareKey() {
  409. return $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.publicKey', Encryption::ID);
  410. }
  411. /**
  412. * @param $purpose
  413. * @param bool $timestamp
  414. * @param bool $includeUserKeys
  415. */
  416. public function backupAllKeys($purpose, $timestamp = true, $includeUserKeys = true) {
  417. // $backupDir = $this->keyStorage->;
  418. }
  419. /**
  420. * @param string $uid
  421. */
  422. public function replaceUserKeys($uid) {
  423. $this->backupAllKeys('password_reset');
  424. $this->deletePublicKey($uid);
  425. $this->deletePrivateKey($uid);
  426. }
  427. /**
  428. * @param $uid
  429. * @return bool
  430. */
  431. public function deletePublicKey($uid) {
  432. return $this->keyStorage->deleteUserKey($uid, $this->publicKeyId, Encryption::ID);
  433. }
  434. /**
  435. * @param $uid
  436. * @return bool
  437. */
  438. private function deletePrivateKey($uid) {
  439. return $this->keyStorage->deleteUserKey($uid, $this->privateKeyId, Encryption::ID);
  440. }
  441. public function deleteAllFileKeys($path) {
  442. return $this->keyStorage->deleteAllFileKeys($path);
  443. }
  444. /**
  445. * @param array $userIds
  446. * @return array
  447. * @throws PublicKeyMissingException
  448. */
  449. public function getPublicKeys(array $userIds) {
  450. $keys = [];
  451. foreach ($userIds as $userId) {
  452. try {
  453. $keys[$userId] = $this->getPublicKey($userId);
  454. } catch (PublicKeyMissingException $e) {
  455. continue;
  456. }
  457. }
  458. return $keys;
  459. }
  460. /**
  461. * @param string $keyId
  462. * @return string returns openssl key
  463. */
  464. public function getSystemPrivateKey($keyId) {
  465. return $this->keyStorage->getSystemUserKey($keyId . '.' . $this->privateKeyId, Encryption::ID);
  466. }
  467. /**
  468. * @param string $keyId
  469. * @param string $key
  470. * @return string returns openssl key
  471. */
  472. public function setSystemPrivateKey($keyId, $key) {
  473. return $this->keyStorage->setSystemUserKey(
  474. $keyId . '.' . $this->privateKeyId,
  475. $key,
  476. Encryption::ID);
  477. }
  478. /**
  479. * add system keys such as the public share key and the recovery key
  480. *
  481. * @param array $accessList
  482. * @param array $publicKeys
  483. * @param string $uid
  484. * @return array
  485. * @throws PublicKeyMissingException
  486. */
  487. public function addSystemKeys(array $accessList, array $publicKeys, $uid) {
  488. if (!empty($accessList['public'])) {
  489. $publicShareKey = $this->getPublicShareKey();
  490. if (empty($publicShareKey)) {
  491. throw new PublicKeyMissingException($this->getPublicShareKeyId());
  492. }
  493. $publicKeys[$this->getPublicShareKeyId()] = $publicShareKey;
  494. }
  495. if ($this->recoveryKeyExists() &&
  496. $this->util->isRecoveryEnabledForUser($uid)) {
  497. $publicKeys[$this->getRecoveryKeyId()] = $this->getRecoveryKey();
  498. }
  499. return $publicKeys;
  500. }
  501. }