by Enrico Zimuel
Senior Software Engineer
Rogue Wave Software, Inc.
Sunshine PHP 2019, Miami (FL), February 8
|
Cryptography is hard. Hard to design, hard to implement, hard to use, and hard to get right.
Attack based on information gained from the implementation of a computer system, rather than weaknesses in the implemented algorithm itself
Source: Protecting Against Side-Channel Attacks with an Ultra-Low Power Processor
An attacker measures the CPU time to perform some procedures involving a secret (e.g. encryption key). If this time depends on the secret, the attacker may be able to deduce information about the secret.
function compare(string $expected, string $actual): bool
{
$lenExpected = strlen($expected);
$lenActual = strlen($actual);
if ($lenExpected !== $lenActual) {
return false;
}
for($i=0; $i < $lenActual; $i++) {
if ($expected[$i] !== $actual[$i]) {
return false;
}
}
return true;
}
What information an attacker can deduce?
We need a constant-time string comparision function
In 2006 Adi Shamir, Eran Tromer, and Dag Arne Osvik used a timing attack to discover, in 65 milliseconds, the secret key used in widely deployed software for hard-disk encryption
A + B = C, A + C = D, A + D = E
P + P = 2P
Given P and Q find k such that Q = kP is hard!
// code1.php at https://github.com/ezimuel/sodium-php-talk
$msg = 'This is a super secret message!';
// Generating an encryption key and a nonce
$key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES); // 256 bit
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); // 24 bytes
// Encrypt
$ciphertext = sodium_crypto_secretbox($msg, $nonce, $key);
// Decrypt
$plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
echo $plaintext === $msg ? 'Success' : 'Error';
Note: the encryption is always authenticated, you need to store also nonce + ciphertext
// code2.php at https://github.com/ezimuel/sodium-php-talk
$msg = 'This is the message to authenticate!';
$key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES); // 256 bit
// Generate the Message Authentication Code
$mac = sodium_crypto_auth($msg, $key);
// Altering $mac or $msg, verification will fail
echo sodium_crypto_auth_verify($mac, $msg, $key) ? 'Success' : 'Error';
Note: the message is not encrypted
Algorithm: HMAC-SHA512
// code3.php at https://github.com/ezimuel/sodium-php-talk
$aliceKeypair = sodium_crypto_box_keypair();
$alicePublicKey = sodium_crypto_box_publickey($aliceKeypair);
$aliceSecretKey = sodium_crypto_box_secretkey($aliceKeypair);
$bobKeypair = sodium_crypto_box_keypair();
$bobPublicKey = sodium_crypto_box_publickey($bobKeypair); // 32 bytes
$bobSecretKey = sodium_crypto_box_secretkey($bobKeypair); // 32 bytes
$msg = 'Hi Bob, this is Alice!';
$nonce = random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES); // 24 bytes
$keyEncrypt = $aliceSecretKey . $bobPublicKey;
$ciphertext = sodium_crypto_box($msg, $nonce, $keyEncrypt);
$keyDecrypt = $bobSecretKey . $alicePublicKey;
$plaintext = sodium_crypto_box_open($ciphertext, $nonce, $keyDecrypt);
echo $plaintext === $msg ? 'Success' : 'Error';
Note: it provides confidentiality, integrity and non-repudiation
Algorithms: XSalsa20 to encrypt, Poly1305 for MAC, and XS25519 for key exchange
// code4.php at https://github.com/ezimuel/sodium-php-talk
$keypair = sodium_crypto_sign_keypair();
$publicKey = sodium_crypto_sign_publickey($keypair); // 32 bytes
$secretKey = sodium_crypto_sign_secretkey($keypair); // 64 bytes
$msg = 'This message is from Alice';
// Sign a message
$signedMsg = sodium_crypto_sign($msg, $secretKey);
// Or generate only the signature (detached mode)
$signature = sodium_crypto_sign_detached($msg, $secretKey); // 64 bytes
// Verify the signed message
$original = sodium_crypto_sign_open($signedMsg, $publicKey);
echo $original === $msg ? 'Signed msg ok' : 'Error signed msg';
// Verify the signature
echo sodium_crypto_sign_verify_detached($signature, $msg, $publicKey) ?
'Signature ok' : 'Error signature';
Note: the message is not encrypted, signedMsg includes signature + msg
Algorithm: Ed25519
// code5.php at https://github.com/ezimuel/sodium-php-talk
if (! sodium_crypto_aead_aes256gcm_is_available()) {
throw new \Exception("AES-GCM is not supported on this platform");
}
$msg = 'Super secret message!';
$key = random_bytes(SODIUM_CRYPTO_AEAD_AES256GCM_KEYBYTES);
$nonce = random_bytes(SODIUM_CRYPTO_AEAD_AES256GCM_NPUBBYTES);
// AEAD encryption
$ad = 'Additional public data';
$ciphertext = sodium_crypto_aead_aes256gcm_encrypt(
$msg,
$ad,
$nonce,
$key
);
// AEAD decryption
$decrypted = sodium_crypto_aead_aes256gcm_decrypt(
$ciphertext,
$ad,
$nonce,
$key
);
if ($decrypted === false) {
throw new \Exception("Decryption failed");
}
echo $decrypted === $msg ? 'OK' : 'Error';
Note: you need to store also ad and nonce + ciphertext
// code6.php at https://github.com/ezimuel/sodium-php-talk
$password = 'password';
$hash = sodium_crypto_pwhash_str(
$password,
SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
); // 97 bytes
echo sodium_crypto_pwhash_str_verify($hash, $password) ?
'OK' : 'Error';
An example of Argon2i hash:
$argon2id$v=19$m=65536,t=2,p=1$EF1BpShRmCYHN7ryxlhtBg$zLZO4IWjx3E...
// code7.php at https://github.com/ezimuel/sodium-php-talk
$password = 'password';
// Argon2i without Sodium
$hash = password_hash($password, PASSWORD_ARGON2I); // 95 bytes
// Argon2id with PHP 7.3+
$hash2 = password_hash($password, PASSWORD_ARGON2ID); // 96 bytes
echo password_verify($password, $hash) ? 'OK' : 'Error';
echo password_verify($password, $hash2) ? 'OK' : 'Error';
Comparing with Sodium:
$argon2id$v=19$m=65536,t=2,p=1$EF1BpShRmCYH... // 97 bytes, Sodium
$argon2id$v=19$m=1024,t=2,p=2$R3ZTLktFd1Shp.. // 96 bytes, PHP 7.3
$argon2i$v=19$m=1024,t=2,p=2$Y3pweEtMdS82SG... // 95 bytes, PHP 7.2
Note: password_hash() is not compatible with sodium_crypto_pwhash_str()
Example: generating a binary key of 32 bytes
// code8.php at https://github.com/ezimuel/sodium-php-talk
$password = 'password';
$salt = random_bytes(SODIUM_CRYPTO_PWHASH_SALTBYTES);
$key = sodium_crypto_pwhash(
32,
$password,
$salt,
SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);
Note: you need to store also the salt to generate the same key from password
Source code in this presentation:
ezimuel/sodium-php-talk
Contact me: enrico [at] zimuel.it
Follow me: @ezimuel