Skip to content

Instantly share code, notes, and snippets.

@leonklingele
Last active March 11, 2017 18:21
Show Gist options
  • Save leonklingele/982e79ad34704f8651c9 to your computer and use it in GitHub Desktop.
Save leonklingele/982e79ad34704f8651c9 to your computer and use it in GitHub Desktop.
crypto_aead_chacha20poly1305_decrypt triggers E_ERROR when decryption / authentication fails
<?php
$crypto = new Crypt('cbe92e0557602e1bf9c05fffe8e54a809c1bdc5b3c3e2011b9153ce0ce672917', 'ok');
$enc = $crypto->encrypt('secretmessage');
// Here we prepend an 'a' -> Decryption should fail, i.e. return false
// It should NOT trigger an E_ERROR, as we can't handle that.
$dec = $crypto->decrypt('a' . $enc);
// This should always echo
echo 'Finished';
class Crypt {
private $key = null;
private $keyLength = Sodium::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES;
private $nonceLength = Sodium::CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES;
private $associatedData = 'test12345';
public function __construct($key, $associatedData) {
$this->setKey($key);
$this->setAD($associatedData);
}
public function __destruct() {
Sodium::sodium_memzero($this->key);
}
public function encrypt($data, $raw = false) {
$nonce = $this->generateRandomNonce();
$ciphertextWithNonce = $nonce . Sodium::crypto_aead_chacha20poly1305_encrypt(
$data,
$this->getAD(),
$nonce,
$this->getKey()
);
if ($raw) {
return $ciphertextWithNonce;
}
return base64_encode($ciphertextWithNonce);
}
public function decrypt($data, $raw = false) {
if (!$raw) {
$data = base64_decode($data, true);
if ($data === false) {
return false;
}
}
$nonce = mb_substr($data, 0, $this->nonceLength, '8bit');
$ciphertext = mb_substr($data, $this->nonceLength, null, '8bit');
return Sodium::crypto_aead_chacha20poly1305_decrypt(
$ciphertext,
$this->getAD(),
$nonce,
$this->getKey()
);
}
public function deriveKey($password, $length = 0) {
if ($length === 0) {
$length = $this->keyLength;
}
// Create a random salt
$salt = Sodium::randombytes_buf(Sodium::CRYPTO_PWHASH_SCRYPTSALSA208SHA256_SALTBYTES);
// Generate a stream of $length pseudo random bytes using the password and the salt
return Sodium::crypto_pwhash_scryptsalsa208sha256(
$length,
$password,
$salt,
Sodium::CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE,
Sodium::CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE
);
}
private function getKey() {
return $this->key;
}
public function setKey($key) {
$binKey = $this->hex2bin($key);
if (ctype_xdigit($key) && strlen($binKey) === $this->keyLength) {
$this->key = $binKey;
} else {
trigger_error('Invalid key. Key must be a ' . $this->keyLength . ' char binary string.', E_USER_ERROR);
}
}
private function getAD() {
return $this->associatedData;
}
private function setAD($associatedData) {
$this->associatedData = $associatedData;
}
private function generateRandomNonce() {
return Sodium::randombytes_buf($this->nonceLength);
}
public function bin2hex($bin) {
return Sodium::sodium_bin2hex($bin);
}
public function hex2bin($hex) {
return Sodium::sodium_hex2bin($hex);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment