42 if (CipherEnum::CIPHER_CHACHA20() !== $cipher) {
43 throw new \InvalidArgumentException(
'Unsupported cipher');
46 if (!($padding instanceof
None)) {
47 throw new \InvalidArgumentException(
48 'ChaCha20 does not need any padding ' .
49 '(hint: use fpoirotte\Cryptal\Padding\None)' 54 throw new \InvalidArgumentException(
'Invalid tag length: must be 16 to enable AEAD, 0 to disable');
57 if (32 !== strlen(
$key)) {
58 throw new \InvalidArgumentException(
'Invalid key length');
63 $this->cipher = $cipher;
66 protected static function quarterRound(&$a, &$b, &$c, &$d)
71 $d = (($d & 0xFFFF) << 16) | (($d >> 16) & 0xFFFF);
76 $b = (($b & 0xFFFFF) << 12) | (($b >> 20) & 0xFFF);
81 $d = (($d & 0xFFFFFF) << 8) | (($d >> 24) & 0xFF);
86 $b = (($b & 0x1FFFFFF) << 7) | (($b >> 25) & 0x7F);
89 protected function block($iv, $counter)
92 $block = array_values(
93 unpack(
'V*',
'expand 32-byte k' . $this->key . $counter . $iv)
97 for ($i = 0; $i < 10; $i++) {
98 static::quarterRound($block[ 0], $block[ 4], $block[ 8], $block[12]);
99 static::quarterRound($block[ 1], $block[ 5], $block[ 9], $block[13]);
100 static::quarterRound($block[ 2], $block[ 6], $block[10], $block[14]);
101 static::quarterRound($block[ 3], $block[ 7], $block[11], $block[15]);
103 static::quarterRound($block[ 0], $block[ 5], $block[10], $block[15]);
104 static::quarterRound($block[ 1], $block[ 6], $block[11], $block[12]);
105 static::quarterRound($block[ 2], $block[ 7], $block[ 8], $block[13]);
106 static::quarterRound($block[ 3], $block[ 4], $block[ 9], $block[14]);
110 for ($i = 0; $i < 16; $i++) {
111 $res .= pack(
'V', ($block[$i] + $init[$i]) & 0xFFFFFFFF);
116 protected function basicXcrypt($plain, $iv, $counter = 0)
119 if (strlen($iv) !== $ivSize) {
120 throw new \InvalidArgumentException(
"Invalid Initialization Vector (should be $ivSize bytes long)");
123 $len = strlen($plain);
124 $m = ($len >> 6) + (($len % 64) > 0);
126 for ($i = 0; $i < $m; $i++) {
127 $c = gmp_strval(gmp_add($counter, $i), 16);
128 $c = pack(
'H*', str_pad($c, (16 - $ivSize) << 1,
'0', STR_PAD_LEFT));
129 $keyStream .= $this->block($iv, strrev($c));
131 return $plain ^ $keyStream;
134 public function encrypt($iv, $data, &$tag = null, $aad =
'')
136 if (!$this->tagLength) {
137 return $this->basicXcrypt($data, $iv, 0);
140 $polyKey = substr($this->block($iv, str_repeat(
"\x00", 4)), 0, 32);
141 $ciphertext = $this->basicXcrypt($data, $iv, 1);
142 $pad1 = str_repeat(
"\x00", (16 - (strlen($aad) % 16)) % 16);
143 $pad2 = str_repeat(
"\x00", (16 - (strlen($ciphertext) % 16)) % 16);
144 $aadLen = pack(
'V*', strlen($aad), 0);
145 $ctLen = pack(
'V*', strlen($ciphertext), 0);
147 MacEnum::MAC_POLY1305(),
148 CipherEnum::CIPHER_CHACHA20(),
150 $aad . $pad1 . $ciphertext . $pad2 . $aadLen . $ctLen,
157 public function decrypt($iv, $data, $tag = null, $aad =
'')
159 if (!$this->tagLength) {
160 return $this->basicXcrypt($data, $iv, 0);
163 $polyKey = substr($this->block($iv, str_repeat(
"\x00", 4)), 0, 32);
164 $pad1 = str_repeat(
"\x00", (16 - (strlen($aad) % 16)) % 16);
165 $pad2 = str_repeat(
"\x00", (16 - (strlen($data) % 16)) % 16);
166 $aadLen = pack(
'V*', strlen($aad), 0);
167 $ctLen = pack(
'V*', strlen($data), 0);
169 MacEnum::MAC_POLY1305(),
170 CipherEnum::CIPHER_CHACHA20(),
172 $aad . $pad1 . $data . $pad2 . $aadLen . $ctLen,
177 if ($outTag !== $tag) {
178 throw new \InvalidArgumentException(
'Invalid tag');
181 return $this->basicXcrypt($data, $iv, 1);
198 return $this->cipher;
encrypt($iv, $data, &$tag=null, $aad= '')
decrypt($iv, $data, $tag=null, $aad= '')
__construct(CipherEnum $cipher, ModeEnum $mode, PaddingInterface $padding, $key, $tagLength=self::DEFAULT_TAG_LENGTH)
$key
Secret key used to encrypt/decrypt data.
static mac(MacEnum $macAlgorithm, SubAlgorithmAbstractEnum $innerAlgorithm, $key, $data, $nonce= '', $raw=false)
$tagLength
Tag length in bytes; 16 when AEAD is enabled, 0 otherwise.