Skip to content

Commit

Permalink
Crypto Module
Browse files Browse the repository at this point in the history
  • Loading branch information
seba-aln committed Oct 2, 2023
1 parent e14149e commit fa5dc26
Show file tree
Hide file tree
Showing 12 changed files with 798 additions and 0 deletions.
2 changes: 2 additions & 0 deletions examples/Time.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
require_once __DIR__ . '/../vendor/autoload.php';

use PubNub\Models\Consumer\PNTimeResult;
use PubNub\Crypto\Cryptor;

$pnconfig = \PubNub\PNConfiguration::demoKeys();
$pubnub = new \PubNub\PubNub($pnconfig);

$result = $pubnub->time()->sync();

printf("Server Time is: %s", date("Y-m-d H:i:s", $result->getTimetoken()));

62 changes: 62 additions & 0 deletions src/PubNub/Crypto/AesCbcCryptor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace PubNub\Crypto;

use PubNub\Crypto\Payload as CryptoPayload;
use PubNub\Crypto\PaddingTrait;

class AesCbcCryptor extends Cryptor
{
use PaddingTrait;

public const CRYPTOR_ID = 'ACRH';
public const IV_LENGTH = 16;
public const BLOCK_SIZE = 16;
public const CIPHER_ALGO = 'aes-256-cbc';

protected string $cipherKey;

public function __construct(string $cipherKey)
{
$this->cipherKey = $cipherKey;
}

public function getIV(): string
{
return random_bytes(self::IV_LENGTH);
}

public function getCipherKey(): string
{
return $this->cipherKey;
}

protected function getSecret($cipherKey): string
{
$key = !is_null($cipherKey) ? $cipherKey : $this->cipherKey;
return hash("sha256", $key, true);
}

public function encrypt(string $text, ?string $cipherKey = null): CryptoPayload
{
$secret = $this->getSecret($cipherKey);
$iv = $this->getIV();
$encrypted = openssl_encrypt($text, self::CIPHER_ALGO, $secret, OPENSSL_RAW_DATA, $iv);
return new CryptoPayload($encrypted, $iv, self::CRYPTOR_ID);
}

public function decrypt(CryptoPayload $payload, ?string $cipherKey = null): string
{
$text = $payload->getData();
$secret = $this->getSecret($cipherKey);
$iv = $payload->getCryptorData();
$decrypted = openssl_decrypt($text, self::CIPHER_ALGO, $secret, OPENSSL_RAW_DATA, $iv);
$result = json_decode($decrypted);

if ($result === null) {
return $decrypted;
} else {
return $result;
}
}
}
13 changes: 13 additions & 0 deletions src/PubNub/Crypto/Cryptor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace PubNub\Crypto;

use PubNub\Crypto\Payload as CryptoPayload;

abstract class Cryptor
{
public const CRYPTOR_ID = null;

abstract public function encrypt(string $text, ?string $cipherKey = null): CryptoPayload;
abstract public function decrypt(CryptoPayload $payload, ?string $cipherKey = null): string;
}
44 changes: 44 additions & 0 deletions src/PubNub/Crypto/Header.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace PubNub\Crypto;

class Header
{
public const HEADER_VERSION = 1;
private string $sentinel;
private string $cryptorId;
private string $cryptorData;
private int $length;

public function __construct(
string $sentinel,
string $cryptorId,
string $cryptorData,
int $length
) {
$this->sentinel = $sentinel;
$this->cryptorId = $cryptorId;
$this->cryptorData = $cryptorData;
$this->length = $length;
}

public function getSentinel(): string
{
return $this->sentinel;
}

public function getCryptorId(): string
{
return $this->cryptorId;
}

public function getCryptorData(): string
{
return $this->cryptorData;
}

public function getLength(): int
{
return $this->length;
}
}
101 changes: 101 additions & 0 deletions src/PubNub/Crypto/LegacyCryptor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php

namespace PubNub\Crypto;

use PubNub\Crypto\Cryptor;
use PubNub\Exceptions\PubNubResponseParsingException;
use PubNub\Crypto\PaddingTrait;

class LegacyCryptor extends Cryptor
{
use PaddingTrait;

public const CRYPTOR_ID = '0000';
public const IV_LENGTH = 16;
public const BLOCK_SIZE = 16;
public const CIPHER_ALGO = 'aes-256-cbc';
protected const STATIC_IV = '0123456789012345';

protected string $cipherKey;
protected bool $useRandomIV;

public function __construct(string $key, bool $useRandomIV)
{
$this->cipherKey = $key;
$this->useRandomIV = $useRandomIV;
}

public function getIV(): string
{
if (!$this->useRandomIV) {
return self::STATIC_IV;
}
return random_bytes(static::IV_LENGTH);
}

public function getCipherKey(): string
{
return $this->cipherKey;
}

public function encrypt(string $text, ?string $cipherKey = null): Payload
{
$iv = $this->getIV();
$shaCipherKey = substr(hash("sha256", $this->cipherKey), 0, 32);
$padded = $this->pad($text);
$encrypted = openssl_encrypt($text, self::CIPHER_ALGO, $shaCipherKey, OPENSSL_RAW_DATA, $iv);
if ($this->useRandomIV) {
$encryptedWithIV = $iv . $encrypted;
} else {
$encryptedWithIV = $encrypted;
}
return new Payload($encryptedWithIV, '', self::CRYPTOR_ID);
}

public function decrypt(Payload $payload, ?string $cipherKey = null): string
{
$text = $payload->getData();
if (strlen($text) === 0) {
throw new PubNubResponseParsingException("Decryption error: message is empty");
}

if (is_array($text)) {
if (array_key_exists("pn_other", $text)) {
$text = $text["pn_other"];
} else {
if (is_array($text)) {
throw new PubNubResponseParsingException("Decryption error: message is not a string");
} else {
throw new PubNubResponseParsingException("Decryption error: pn_other object key missing");
}
}
} elseif (!is_string($text)) {
throw new PubNubResponseParsingException("Decryption error: message is not a string or object");
}

$shaCipherKey = substr(hash("sha256", $this->cipherKey), 0, 32);

if ($this->useRandomIV) {
$iv = substr($text, 0, 16);
$data = substr($text, 16);
} else {
$iv = self::STATIC_IV;
$data = $text;
}
$decrypted = openssl_decrypt($data, 'aes-256-cbc', $shaCipherKey, OPENSSL_RAW_DATA, $iv);

if ($decrypted === false) {
throw new PubNubResponseParsingException("Decryption error: " . openssl_error_string());
}

$unPadded = $this->depad($decrypted);

$result = json_decode($unPadded);

if ($result === null) {
return $unPadded;
} else {
return $result;
}
}
}
48 changes: 48 additions & 0 deletions src/PubNub/Crypto/PaddingTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace PubNub\Crypto;

trait PaddingTrait
{
public const BLOCK_SIZE = 16;

/**
* Pad $text to multiple of $blockSize lenght using PKCS5Padding schema
*
* @param string $text
* @param int $blockSize
* @return string
*/
public function pad(string $text, int $blockSize = self::BLOCK_SIZE)
{
$pad = $blockSize - (strlen($text) % $blockSize);
return $text . str_repeat(chr($pad), $pad);
}

/**
* Remove padding from $text using PKCS5Padding schema
*
* @param string $text
* @param int $blockSize
* @return string
*/
public function depad($data, $blockSize = self::BLOCK_SIZE)
{
$length = strlen($data);
if ($length == 0) {
return $data;
}

$padLength = substr($data, -1);

if (ord($padLength) <= $blockSize) {
for ($i = $length - 2; $i > 0; $i--) {
if (ord($data [$i] != $padLength)) {
break;
}
}
return substr($data, 0, $i + 1);
}
return $data;
}
}
32 changes: 32 additions & 0 deletions src/PubNub/Crypto/Payload.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace PubNub\Crypto;

class Payload
{
private string $data;
private ?string $cryptorData;
private ?string $cryptorId;

public function __construct(string $data, ?string $cryptorData = null, ?string $cryptorId = null)
{
$this->data = $data;
$this->cryptorData = $cryptorData;
$this->cryptorId = $cryptorId;
}

public function getData(): string
{
return $this->data;
}

public function getCryptorData(): ?string
{
return $this->cryptorData;
}

public function getCryptorId(): ?string
{
return $this->cryptorId;
}
}
Loading

0 comments on commit fa5dc26

Please sign in to comment.