Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 35 additions & 10 deletions sodium.php
Original file line number Diff line number Diff line change
@@ -1,23 +1,48 @@
<?php
use Gt\Cipher\EncryptionFailureException;
use Gt\Cipher\Message\DecryptionFailureException;

require("vendor/autoload.php");

$messageToTransmit = "Cipher test!";
$sharedKey = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
$iv = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);

$encryptedBytes = sodium_crypto_secretbox(
$messageToTransmit,
$iv,
$sharedKey
);
try {
$encryptedBytes = sodium_crypto_secretbox(
$messageToTransmit,
$iv,
$sharedKey
);
}
catch(Throwable $throwable) {
throw new EncryptionFailureException(
"Error encrypting cipher message",
previous: $throwable,
);
}
$cipher = base64_encode($encryptedBytes);

echo "Shared key: ", base64_encode($sharedKey), PHP_EOL;
echo "IV: ", base64_encode($iv), PHP_EOL;
echo "Cipher: ", base64_encode($cipher), PHP_EOL;

$decrypted = sodium_crypto_secretbox_open(
$encryptedBytes,
$iv,
$sharedKey,
);
try {
$decrypted = sodium_crypto_secretbox_open(
$encryptedBytes,
$iv,
$sharedKey,
);
}
catch(Throwable $throwable) {
throw new DecryptionFailureException(
"Error decrypting cipher message",
previous: $throwable,
);
}

if($decrypted === false) {
throw new DecryptionFailureException("Error decrypting cipher message");
}

echo "Decrypted: $decrypted", PHP_EOL;
20 changes: 14 additions & 6 deletions src/CipherText.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use Gt\Http\Uri;
use Psr\Http\Message\UriInterface;
use Stringable;
use Throwable;

class CipherText implements Stringable {
private string $bytes;
Expand All @@ -13,12 +14,19 @@ public function __construct(
private InitVector $iv,
private Key $key,
) {

$this->bytes = sodium_crypto_secretbox(
$data,
$this->iv->getBytes(),
$this->key->getBytes(),
);
try {
$this->bytes = sodium_crypto_secretbox(
$data,
$this->iv->getBytes(),
$this->key->getBytes(),
);
}
catch(Throwable $throwable) {
throw new EncryptionFailureException(
"Error encrypting cipher message",
previous: $throwable,
);
}
}

public function __toString():string {
Expand Down
22 changes: 15 additions & 7 deletions src/EncryptedUri.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<?php
namespace Gt\Cipher;

use Gt\Cipher\Message\DecryptionFailureException;
use Gt\Cipher\Message\PlainTextMessage;
use Gt\Http\Uri;
use Psr\Http\Message\UriInterface;
use Throwable;

class EncryptedUri {
private string $encryptedBytes;
Expand Down Expand Up @@ -39,13 +39,21 @@ public function __construct(
}

public function decryptMessage(Key $key):PlainTextMessage {
$decrypted = sodium_crypto_secretbox_open(
$this->encryptedBytes,
$this->iv->getBytes(),
$key->getBytes(),
);
try {
$decrypted = sodium_crypto_secretbox_open(
$this->encryptedBytes,
$this->iv->getBytes(),
$key->getBytes(),
);
}
catch(Throwable $throwable) {
throw new UriDecryptionFailureException(
"Error decrypting cipher URI",
previous: $throwable,
);
}
if($decrypted === false) {
throw new DecryptionFailureException("Error decrypting cipher message");
throw new UriDecryptionFailureException("Error decrypting cipher URI");
}
return new PlainTextMessage(
$decrypted,
Expand Down
4 changes: 4 additions & 0 deletions src/EncryptionFailureException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
namespace Gt\Cipher;

class EncryptionFailureException extends CipherException {}
19 changes: 14 additions & 5 deletions src/Message/EncryptedMessage.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,23 @@
namespace Gt\Cipher\Message;

use Gt\Cipher\Key;
use Throwable;

class EncryptedMessage extends AbstractMessage {
public function decrypt(Key $key):PlainTextMessage {
$decrypted = sodium_crypto_secretbox_open(
base64_decode($this->data),
$this->iv->getBytes(),
$key->getBytes(),
);
try {
$decrypted = sodium_crypto_secretbox_open(
base64_decode($this->data),
$this->iv->getBytes(),
$key->getBytes(),
);
}
catch(Throwable $throwable) {
throw new DecryptionFailureException(
"Error decrypting cipher message",
previous: $throwable,
);
}
if($decrypted === false) {
throw new DecryptionFailureException("Error decrypting cipher message");
}
Expand Down
4 changes: 4 additions & 0 deletions src/UriDecryptionFailureException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
namespace Gt\Cipher;

class UriDecryptionFailureException extends CipherException {}
14 changes: 14 additions & 0 deletions test/phpunit/CipherTextTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
namespace Gt\Cipher\Test;

use Gt\Cipher\CipherText;
use Gt\Cipher\EncryptionFailureException;
use Gt\Cipher\InitVector;
use Gt\Cipher\Key;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -51,4 +52,17 @@ public function testGetUri():void {
self::assertSame($baseUri, $uri->getScheme() . "://" . $uri->getAuthority());
self::assertSame(http_build_query($expectedQueryParts), $uri->getQuery());
}

public function testConstruct_wrapsSodiumFailure():void {
$data = "test data";
$iv = self::createMock(InitVector::class);
$iv->method("getBytes")
->willReturn(str_repeat("0", SODIUM_CRYPTO_SECRETBOX_NONCEBYTES));
$key = self::createMock(Key::class);
$key->method("getBytes")
->willReturn("bad");

self::expectException(EncryptionFailureException::class);
new CipherText($data, $iv, $key);
}
}
27 changes: 23 additions & 4 deletions test/phpunit/EncryptedUriTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
use Gt\Cipher\EncryptedUri;
use Gt\Cipher\InitVector;
use Gt\Cipher\Key;
use Gt\Cipher\Message\DecryptionFailureException;
use Gt\Cipher\MissingQueryStringException;
use Gt\Cipher\UriDecryptionFailureException;
use PHPUnit\Framework\TestCase;

class EncryptedUriTest extends TestCase {
Expand Down Expand Up @@ -38,19 +38,38 @@ public function testConstruct_missingCipherValue():void {
}

public function testDecryptMessage_error():void {
$ivString = base64_encode(str_repeat("0", SODIUM_CRYPTO_SECRETBOX_NONCEBYTES));
$ivString = base64_encode(
str_repeat("0", SODIUM_CRYPTO_SECRETBOX_NONCEBYTES)
);
$uri = "https://example.com/test/?cipher=0000&iv=$ivString";
$sut = new EncryptedUri($uri);

$key = self::createMock(Key::class);
$key->method("getBytes")
->willReturn(str_repeat("0", SODIUM_CRYPTO_SECRETBOX_KEYBYTES));
self::expectException(DecryptionFailureException::class);
self::expectException(UriDecryptionFailureException::class);
$sut->decryptMessage($key);
}

public function testDecryptMessage_wrapsSodiumFailure():void {
$ivString = base64_encode(
str_repeat("0", SODIUM_CRYPTO_SECRETBOX_NONCEBYTES)
);
$uri = "https://example.com/test/?cipher=0000&iv=$ivString";
$sut = new EncryptedUri($uri);

$key = self::createMock(Key::class);
$key->method("getBytes")
->willReturn("bad");

self::expectException(UriDecryptionFailureException::class);
$sut->decryptMessage($key);
}

public function testDecryptMessage():void {
$uri = "https://example.com/?cipher=lmEClve%2FuhmK32ghM0%2BA%2FI%2Btysm00AL37YD6eg%3D%3D&iv=UVunn3laPVnK4CfHZuS2AnvJ1KfsPM1r";
$uri = "https://example.com/"
. "?cipher=lmEClve%2FuhmK32ghM0%2BA%2FI%2Btysm00AL37YD6eg%3D%3D"
. "&iv=UVunn3laPVnK4CfHZuS2AnvJ1KfsPM1r";
$sut = new EncryptedUri($uri);

$key = self::createMock(Key::class);
Expand Down
14 changes: 14 additions & 0 deletions test/phpunit/Message/EncryptedMessageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,18 @@ public function testDecrypt_failure():void {
self::expectException(DecryptionFailureException::class);
$sut->decrypt($key);
}

public function testDecrypt_wrapsSodiumFailure():void {
$iv = self::createMock(InitVector::class);
$iv->method("getBytes")
->willReturn(str_repeat("0", SODIUM_CRYPTO_SECRETBOX_NONCEBYTES));
$sut = new EncryptedMessage("badly formed data", $iv);

$key = self::createMock(Key::class);
$key->method("getBytes")
->willReturn("bad");

self::expectException(DecryptionFailureException::class);
$sut->decrypt($key);
}
}
Loading