Repository navigation
Expand file tree
/
Copy pathChaCha20Poly1305.php
More file actions
133 lines (114 loc) · 4.63 KB
/
Copy pathChaCha20Poly1305.php
File metadata and controls
133 lines (114 loc) · 4.63 KB
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
131
132
133
<?php
declare(strict_types=1);
namespace CryptoFramework\Symmetric;
use CryptoFramework\Exceptions\CryptoException;
use CryptoFramework\Exceptions\DecryptionException;
use CryptoFramework\Interfaces\SymmetricCryptoInterface;
use CryptoFramework\Utils\RandomGenerator;
/**
* Implementação de criptografia simétrica usando ChaCha20-Poly1305
*/
final class ChaCha20Poly1305 implements SymmetricCryptoInterface
{
private const NONCE_LENGTH = 24; // 192 bits para ChaCha20-Poly1305 (libsodium)
public function __construct(
private readonly RandomGenerator $randomGenerator = new RandomGenerator()
) {
if (!extension_loaded('sodium')) {
throw new CryptoException("A extensão sodium não está disponível");
}
}
/**
* Criptografa dados usando ChaCha20-Poly1305
*
* @param string $data Dados a serem criptografados
* @param string $key Chave de criptografia (32 bytes)
* @param string|null $associatedData Dados associados para autenticação adicional
* @return string Dados criptografados (formato: base64)
* @throws CryptoException
*/
public function encrypt(string $data, string $key, ?string $associatedData = null): string
{
$this->validateKey($key);
try {
// Gerar nonce aleatório
$nonce = $this->randomGenerator->generateBytes(self::NONCE_LENGTH);
// Criptografar os dados
$ciphertext = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt(
$data,
$associatedData ?? '',
$nonce,
$key
);
// Combinar nonce e ciphertext em um único string
$encrypted = $nonce . $ciphertext;
// Retornar como base64 para facilitar o armazenamento
return base64_encode($encrypted);
} catch (\Exception $e) {
throw new CryptoException("Erro durante a criptografia: " . $e->getMessage(), previous: $e);
}
}
/**
* Descriptografa dados usando ChaCha20-Poly1305
*
* @param string $encryptedData Dados criptografados (formato: base64)
* @param string $key Chave de criptografia (32 bytes)
* @param string|null $associatedData Dados associados para autenticação adicional
* @return string Dados descriptografados
* @throws CryptoException|DecryptionException
*/
public function decrypt(string $encryptedData, string $key, ?string $associatedData = null): string
{
$this->validateKey($key);
try {
// Decodificar o base64
$encrypted = base64_decode($encryptedData, true);
if ($encrypted === false) {
throw new DecryptionException("Dados criptografados inválidos: não é um base64 válido");
}
// Verificar se os dados têm o tamanho mínimo esperado
if (strlen($encrypted) <= self::NONCE_LENGTH) {
throw new DecryptionException("Dados criptografados inválidos: tamanho insuficiente");
}
// Extrair nonce e ciphertext
$nonce = substr($encrypted, 0, self::NONCE_LENGTH);
$ciphertext = substr($encrypted, self::NONCE_LENGTH);
// Descriptografar os dados
$plaintext = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt(
$ciphertext,
$associatedData ?? '',
$nonce,
$key
);
if ($plaintext === false) {
throw new DecryptionException("Falha na descriptografia: autenticação falhou");
}
return $plaintext;
} catch (DecryptionException $e) {
throw $e;
} catch (\Exception $e) {
throw new CryptoException("Erro durante a descriptografia: " . $e->getMessage(), previous: $e);
}
}
/**
* Valida a chave de criptografia
*
* @param string $key Chave a ser validada
* @throws CryptoException
*/
private function validateKey(string $key): void
{
if (strlen($key) !== SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES) {
throw new CryptoException("A chave ChaCha20-Poly1305 deve ter exatamente " . SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES . " bytes");
}
}
/**
* Verifica se o algoritmo ChaCha20-Poly1305 está disponível
*
* @return bool True se o algoritmo estiver disponível
*/
public static function isAvailable(): bool
{
return extension_loaded('sodium');
}
}