Skip to content

Commit

Permalink
Merge pull request #10 from petrknap/aes256gcm
Browse files Browse the repository at this point in the history
Implemented AEAD AES-256 GCM
  • Loading branch information
petrknap authored Apr 21, 2024
2 parents 466c555 + 9f61ce5 commit d1fba11
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 0 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,23 @@ echo $pullStream->pull($ciphertextChunk2);
$xChaCha20Poly1305->eraseData($key);
```

### Symmetric block encryption with additional data

```php
use PetrKnap\CryptoSodium\Aead\Aes256Gcm;

$aes256Gcm = new Aes256Gcm();
$message = 'Hello World!';
$purpose = 'example';
$key = $aes256Gcm->generateKey();

$ciphertext = $aes256Gcm->encrypt($message, $key, additionalData: $purpose);

echo $aes256Gcm->decrypt($ciphertext, $key, additionalData: $purpose);

$aes256Gcm->eraseData($key);
```


---

Expand Down
4 changes: 4 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@
],
"homepage": "https://github.com/petrknap/php-crypto-sodium",
"keywords": [
"AEAD",
"AES-256",
"ChaCha20",
"Curve25519",
"Poly1305",
"Sodium",
"X25519",
"XChaCha20",
"asymmetric cryptography",
"authenticated encryption with associated data",
"authenticated encryption",
"block cipher",
"cryptography",
"decryption",
Expand Down
71 changes: 71 additions & 0 deletions src/Aead/Aes256Gcm.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

declare(strict_types=1);

namespace PetrKnap\CryptoSodium\Aead;

use PetrKnap\CryptoSodium\CiphertextWithNonce;
use PetrKnap\CryptoSodium\CryptoSodiumTrait;
use PetrKnap\CryptoSodium\DataEraser;
use PetrKnap\CryptoSodium\Exception;
use PetrKnap\CryptoSodium\KeyGenerator;
use PetrKnap\Shorts\Exception\MissingRequirement;
use SensitiveParameter;

/**
* @see sodium_crypto_aead_aes256gcm_encrypt()
*/
class Aes256Gcm implements KeyGenerator, DataEraser
{
use CryptoSodiumTrait;

public const HEADER_BYTES = SODIUM_CRYPTO_AEAD_AES256GCM_NPUBBYTES;

public function __construct()
{
if (!sodium_crypto_aead_aes256gcm_is_available()) {
throw new MissingRequirement(self::class, 'available', 'aes256gcm');
}
}

public function generateKey(): string
{
return sodium_crypto_aead_aes256gcm_keygen();
}

/**
* @throws Exception\CouldNotEncryptData
*/
public function encrypt(
string $message,
#[SensitiveParameter]
string &$key,
?string $nonce = null,
?string $additionalData = null,
): CiphertextWithNonce {
return $this->wrapEncryption(static function (string $message, string $nonce) use (&$key, $additionalData): string {
$additionalData ??= '';
return sodium_crypto_aead_aes256gcm_encrypt($message, $additionalData, $nonce, $key);
}, $message, $nonce, self::HEADER_BYTES);
}

/**
* @throws Exception\CouldNotDecryptData
*/
public function decrypt(
CiphertextWithNonce|string $ciphertext,
#[SensitiveParameter]
string &$key,
?string $nonce = null,
?string $additionalData = null,
): string {
return $this->wrapDecryption(static function (string $ciphertext, string $nonce) use (&$key, $additionalData): string {
$additionalData ??= '';
$message = sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $additionalData, $nonce, $key);
if ($message === false) {
throw new Exception\CouldNotDecryptData('sodium_crypto_aead_aes256gcm_decrypt', $ciphertext);
}
return $message;
}, $ciphertext, $nonce, self::HEADER_BYTES);
}
}
31 changes: 31 additions & 0 deletions tests/Aead/Aes256GcmTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace PetrKnap\CryptoSodium\Aead;

use PetrKnap\CryptoSodium\CiphertextWithNonce;
use PetrKnap\CryptoSodium\CryptoSodiumTestCase;

final class Aes256GcmTest extends CryptoSodiumTestCase
{
protected function setUp(): void
{
parent::setUp();
$this->instance = new Aes256Gcm();
$key = base64_decode('9+PiVpFGyLSks1UJlpgGL6VuxJ86zzaizKB5e0C3IE8=');
$additionalData = 'AD';
$this->ciphertextWithNonce = new CiphertextWithNonce(
ciphertext: base64_decode('sycH81OWv2uCxygkUDpBq+Jy7SBl6zSuWjh4zTQ='),
nonce: base64_decode('FJbSk+jybJUfvxrn'),
);
$this->encryptArgsSet = [
[self::MESSAGE, $key, $this->ciphertextWithNonce->nonce, $additionalData],
];
$this->decryptArgsSet = [
[$this->ciphertextWithNonce, $key, null, $additionalData],
[$this->ciphertextWithNonce->toString(), $key, null, $additionalData],
[$this->ciphertextWithNonce->ciphertext, $key, $this->ciphertextWithNonce->nonce, $additionalData],
];
}
}
1 change: 1 addition & 0 deletions tests/CryptoSodiumInterfaceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public function testIsImplementedByClass(string $className): void
public static function dataIsImplementedByClass(): array
{
return [
Aead\Aes256Gcm::class => [Aead\Aes256Gcm::class],
Box::class => [Box::class],
SecretBox::class => [SecretBox::class],
SecretStream\XChaCha20Poly1305::class => [SecretStream\XChaCha20Poly1305::class],
Expand Down
1 change: 1 addition & 0 deletions tests/ReadmeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public static function getExpectedOutputsOfPhpExamples(): iterable
SecretBox::class => 'Hello World!',
Box::class => 'Hello World!',
SecretStream\XChaCha20Poly1305::class => 'Hello World!',
Aead\Aes256Gcm::class => 'Hello World!',
];
}
}

0 comments on commit d1fba11

Please sign in to comment.