KeyManager.php 16 KB

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