summaryrefslogtreecommitdiff
path: root/classes/Crypt.php
blob: 2b3a7b7883464759033f8927fcfc9e42c5ac7988 (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
<?php
class Crypt {

	/** the only algo supported at the moment */
	private const ENCRYPT_ALGO = 'xchacha20poly1305_ietf';

	/** currently only generates keys using sodium_crypto_aead_chacha20poly1305_keygen() i.e. one supported Crypt::ENCRYPT_ALGO
	 * @return string random 256-bit (for ChaCha20-Poly1305) binary string
	*/
	static function generate_key() : string {
		return sodium_crypto_aead_chacha20poly1305_keygen();
	}

	/** encrypts provided ciphertext using Config::ENCRYPTION_KEY into an encrypted object
	 *
	 * @return array{'algo': string, 'nonce': string, 'payload': string} encrypted data object containing algo, nonce, and encrypted data
	*/
	static function encrypt_string(string $ciphertext) : array {
		$key = Config::get(Config::ENCRYPTION_KEY);

		if (!$key)
			throw new Exception("Crypt::encrypt_string() failed to encrypt - key is not available");

		$nonce = \random_bytes(\SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES);

		$payload = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt($ciphertext, '', $nonce, hex2bin($key));

		if ($payload) {
			$encrypted_data = [
				'algo' => self::ENCRYPT_ALGO,
				'nonce' => $nonce,
				'payload' => $payload,
			];

			return $encrypted_data;
		}

		throw new Exception("Crypt::encrypt_string() failed to encrypt ciphertext");
	}

	/** decrypts payload of a valid encrypted object using Config::ENCRYPTION_KEY
	 *
	 * @param array{'algo': string, 'nonce': string, 'payload': string} $encrypted_data
	 *
	 * @return string decrypted string payload
	 */
	static function decrypt_string(array $encrypted_data) : string {
		$key = Config::get(Config::ENCRYPTION_KEY);

		if (!$key)
			throw new Exception("Crypt::decrypt_string() failed to decrypt - key is not available");

		// only one is supported for the time being
		switch ($encrypted_data['algo']) {
			case self::ENCRYPT_ALGO:
				return sodium_crypto_aead_xchacha20poly1305_ietf_decrypt($encrypted_data['payload'], '', $encrypted_data['nonce'], hex2bin($key));
		}

		throw new Exception('Crypt::decrypt_string() failed to decrypt passed encrypted data object, unsupported algo: ' . $encrypted_data['algo']);
	}

}