Welcome to mirror list, hosted at ThFree Co, Russian Federation.

crypto.php « security « private « lib - github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 44fe2fc032627aa587d2d0ced2dddd69f9637003 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
<?php
/**
 * Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com>
 * This file is licensed under the Affero General Public License version 3 or
 * later.
 * See the COPYING-README file.
 */


namespace OC\Security;

use Crypt_AES;
use Crypt_Hash;
use OCP\Security\ICrypto;
use OCP\Security\StringUtils;
use OCP\IConfig;

/**
 * Class Crypto provides a high-level encryption layer using AES-CBC. If no key has been provided
 * it will use the secret defined in config.php as key. Additionally the message will be HMAC'd.
 *
 * Usage:
 * $encryptWithDefaultPassword = \OC::$server->getCrypto()->encrypt('EncryptedText');
 * $encryptWithCustompassword = \OC::$server->getCrypto()->encrypt('EncryptedText', 'password');
 *
 * @package OC\Security
 */
class Crypto implements ICrypto {
	/** @var Crypt_AES $cipher */
	private $cipher;
	/** @var int */
	private $ivLength = 16;
	/** @var IConfig */
	private $config;

	/**
	 * @param IConfig $config
	 */
	function __construct(IConfig $config) {
		$this->cipher = new Crypt_AES();
		$this->config = $config;
	}

	/**
	 * Custom implementation of hex2bin since the function is only available starting
	 * with PHP 5.4
	 *
	 * @TODO Remove this once 5.3 support for ownCloud is dropped
	 * @param $message
	 * @return string
	 */
	protected static function hexToBin($message) {
		if (function_exists('hex2bin')) {
			return hex2bin($message);
		}

		return pack("H*", $message);
	}

	/**
	 * @param string $message The message to authenticate
	 * @param string $password Password to use (defaults to `secret` in config.php)
	 * @return string Calculated HMAC
	 */
	public function calculateHMAC($message, $password = '') {
		if($password === '') {
			$password = $this->config->getSystemValue('secret');
		}

		// Append an "a" behind the password and hash it to prevent reusing the same password as for encryption
		$password = hash('sha512', $password . 'a');

		$hash = new Crypt_Hash('sha512');
		$hash->setKey($password);
		return $hash->hash($message);
	}

	/**
	 * Encrypts a value and adds an HMAC (Encrypt-Then-MAC)
	 * @param string $plaintext
	 * @param string $password Password to encrypt, if not specified the secret from config.php will be taken
	 * @return string Authenticated ciphertext
	 */
	public function encrypt($plaintext, $password = '') {
		if($password === '') {
			$password = $this->config->getSystemValue('secret');
		}
		$this->cipher->setPassword($password);

		$iv = \OC_Util::generateRandomBytes($this->ivLength);
		$this->cipher->setIV($iv);

		$ciphertext = bin2hex($this->cipher->encrypt($plaintext));
		$hmac = bin2hex($this->calculateHMAC($ciphertext.$iv, $password));

		return $ciphertext.'|'.$iv.'|'.$hmac;
	}

	/**
	 * Decrypts a value and verifies the HMAC (Encrypt-Then-Mac)
	 * @param string $authenticatedCiphertext
	 * @param string $password Password to encrypt, if not specified the secret from config.php will be taken
	 * @return string plaintext
	 * @throws \Exception If the HMAC does not match
	 */
	public function decrypt($authenticatedCiphertext, $password = '') {
		if($password === '') {
			$password = $this->config->getSystemValue('secret');
		}
		$this->cipher->setPassword($password);

		$parts = explode('|', $authenticatedCiphertext);
		if(sizeof($parts) !== 3) {
			throw new \Exception('Authenticated ciphertext could not be decoded.');
		}

		$ciphertext = self::hexToBin($parts[0]);
		$iv = $parts[1];
		$hmac = self::hexToBin($parts[2]);

		$this->cipher->setIV($iv);

		if(!StringUtils::equals($this->calculateHMAC($parts[0].$parts[1], $password), $hmac)) {
			throw new \Exception('HMAC does not match.');
		}

		return $this->cipher->decrypt($ciphertext);
	}

}