@ 2018-04-25T06:36:35Z <?php
declare(strict_types=1);
namespace ParagonIE\BlogExampleCode;
/**
* Class RSAPKCS1v15
* @package ParagonIE\BlogExampleCode
*
* @ref https://paragonie.com/blog/2018/04/protecting-rsa-based-protocols-against-adaptive-chosen-ciphertext-attacks
*/
class RSAPKCS1v15
{
/**
* @param string $message
* @param string $publicKey
*
* @return array<int, string>
* @throws \Exception
*/
public static function encrypt(string $message, string $publicKey): array
{
$key = \random_bytes(32);
$C = '';
\openssl_public_encrypt($key, $C, $publicKey);
$iv = \random_bytes(16);
$cipher = \openssl_encrypt(
$message,
'aes-256-ctr',
$key,
OPENSSL_RAW_DATA,
$iv
);
$mac = \hash_hmac('sha256', $iv . $cipher, $key, true);
$D = $mac . $iv . $cipher;
return [bin2hex($C), bin2hex($D)];
}
/**
* @param string $CHex
* @param string $DHex
* @param string $privateKey
*
* @return string
* @throws \Exception
*/
public static function decrypt(string $CHex, string $DHex, string $privateKey): string
{
$kPrime = \random_bytes(32);
$C = \hex2bin($CHex);
$D = \hex2bin($DHex);
$k = '';
$return = @\openssl_private_decrypt($C, $k, $privateKey);
// Do a constant-time swap:
$key = self::cswap32($k, (string) $kPrime, $return );
$mac = mb_substr($D, 0, 32, '8bit');
$iv = mb_substr($D, 32, 16, '8bit');
$cipher = mb_substr($D, 48, null, '8bit');
$recalc = \hash_hmac('sha256', $iv . $cipher, $key, true);
if (!\hash_equals($recalc, $mac)) {
throw new \Exception('Invalid MAC');
}
return \openssl_decrypt(
$cipher,
'aes-256-ctr',
$key,
OPENSSL_RAW_DATA,
$iv
);
}
/**
* @param string $A
* @param string $B
* @param bool $success
*
* @return string
*/
public static function cswap32(string $A, string $B, bool $success): string
{
$A .= \str_repeat("\x00", 32);
$B .= \str_repeat("\x00", 32);
/** @var int $mask 0 if $success is true, otherwise 255 */
$mask = (((int) $success) - 1) & 0xff;
$C = '';
for ($i = 0; $i < 32; ++$i) {
$C .= self::intToChr(
(self::chrToInt($A[$i]) ^ self::chrToInt($B[$i])) & $mask
);
}
return (string) mb_substr($A ^ $C, 0, 32, '8bit');
}
/**
* @param string $chr
*
* @return int
*/
public static function chrToInt(string $chr): int
{
$chunk = \unpack('C', $chr);
return (int) ($chunk[1]);
}
/**
* @param int $int
*
* @return string
*/
public static function intToChr(int $int): string
{
return \pack('C', $int);
}
}
$private_key = '-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAyaTgTt53ph3p5GHgwoGWwz5hRfWXSQA08NCOwe0FEgALWos9
GCjNFCd723nCHxBtN1qd74MSh/uN88JPIbwxKheDp4kxo4YMN5trPaF0e9G6Bj1N
02HnanxFLW+gmLbgYO/SZYfWF/M8yLBcu5Y1Ot0ZxDDDXS9wIQTtBE0ne3YbxgZJ
AZTU5XqyQ1DxdzYyC5lF6yBaR5UQtCYTnXAApVRuUI2Sd6L1E2vl9bSBumZ5IpNx
kRnAwIMjeTJB/0AIELh0mE5vwdihOCbdV6alUyhKC1+1w/FW6HWcp/JG1kKC8DPI
idZ78Bbqv9YFzkAbNni5eSBOsXVBKG78Zsc8owIDAQABAoIBAF22jLDa34yKdns3
qfd7to+C3D5hRzAcMn6Azvf9qc+VybEI6RnjTHxDZWK5EajSP4/sQ15e8ivUk0Jo
WdJ53feL+hnQvwsab28gghSghrxM2kGwGA1XgO+SVawqJt8SjvE+Q+//01ZKK0Oy
A0cDJjX3L9RoPUN/moMeAPFw0hqkFEhm72GSVCEY1eY+cOXmL3icxnsnlUD//SS9
q33RxF2y5oiW1edqcRqhW/7L1yYMbxHFUcxWh8WUwjn1AAhoCOUzF8ZB+0X/PPh+
1nYoq6xwqL0ZKDwrQ8SDhW/rNDLeO9gic5rl7EetRQRbFvsZ40AdsX2wU+lWFUkB
42AjuoECgYEA5z/CXqDFfZ8MXCPAOeui8y5HNDtu30aR+HOXsBDnRI8huXsGND04
FfmXR7nkghr08fFVDmE4PeKUk810YJb+IAJo8wrOZ0682n6yEMO58omqKin+iIUV
rPXLSLo5CChrqw2J4vgzolzPw3N5I8FJdLomb9FkrV84H+IviPIylyECgYEA3znw
AG29QX6ATEfFpGVOcogorHCntd4niaWCq5ne5sFL+EwLeVc1zD9yj1axcDelICDZ
xCZynU7kDnrQcFkT0bjH/gC8Jk3v7XT9l1UDDqC1b7rm/X5wFIZ/rmNa1rVZhL1o
/tKx5tvM2syJ1q95v7NdygFIEIW+qbIKbc6Wz0MCgYBsUZdQD+qx/xAhELX364I2
epTryHMUrs+tGygQVrqdiJX5dcDgM1TUJkdQV6jLsKjPs4Vt6OgZRMrnuLMsk02R
3M8gGQ25ok4f4nyyEZxGGWnVujn55KzUiYWhGWmhgp18UCkoYa59/Q9ss+gocV9h
B9j9Q43vD80QUjiF4z0DQQKBgC7XQX1VibkMim93QAnXGDcAS0ij+w02qKVBjcHk
b9mMBhz8GAxGOIu7ZJafYmxhwMyVGB0I1FQeEczYCJUKnBYN6Clsjg6bnBT/z5bJ
x/Jx1qCzX3Uh6vLjpjc5sf4L39Tyye1u2NXQmZPwB5x9BdcsFConSq/s4K1LJtUT
3KFxAoGBANGcQ8nObi3m4wROyKrkCWcWxFFMnpwxv0pW727Hn9wuaOs4UbesCnwm
pcMTfzGUDuzYXCtAq2pJl64HG6wsdkWmjBTJEpm6b9ibOBN3qFV2zQ0HyyKlMWxI
uVSj9gOo61hF7UH9XB6R4HRdlpBOuIbgAWZ46dkj9/HM9ovdP0Iy
-----END RSA PRIVATE KEY-----';
$public_key = '-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyaTgTt53ph3p5GHgwoGW
wz5hRfWXSQA08NCOwe0FEgALWos9GCjNFCd723nCHxBtN1qd74MSh/uN88JPIbwx
KheDp4kxo4YMN5trPaF0e9G6Bj1N02HnanxFLW+gmLbgYO/SZYfWF/M8yLBcu5Y1
Ot0ZxDDDXS9wIQTtBE0ne3YbxgZJAZTU5XqyQ1DxdzYyC5lF6yBaR5UQtCYTnXAA
pVRuUI2Sd6L1E2vl9bSBumZ5IpNxkRnAwIMjeTJB/0AIELh0mE5vwdihOCbdV6al
UyhKC1+1w/FW6HWcp/JG1kKC8DPIidZ78Bbqv9YFzkAbNni5eSBOsXVBKG78Zsc8
owIDAQAB
-----END PUBLIC KEY-----';
$in = 'This is a test message!';
list ($C, $D) = RSAPKCS1v15::encrypt($in, $public_key);
$start = $end = microtime(true);
$out = RSAPKCS1v15::decrypt($C, $D, $private_key);
$end = microtime(true);
var_dump($in, $out);
var_dump($end - $start);
// Flip the bits in one character of C to force an error
$Corig = $D;
$C = hex2bin($C);
$C[0] = RSAPKCS1v15::intToChr(
RSAPKCS1v15::chrToInt($C[0]) ^ 0xff
);
$C = bin2hex($C);
$start = $end = microtime(true);
try {
$out = RSAPKCS1v15::decrypt($C, $D, $private_key);
} catch (\Exception $ex) {
$end = microtime(true);
var_dump($end - $start);
if ($ex->getMessage() === 'Invalid MAC') {
echo 'Got the expected error message. No RSA hints given.', PHP_EOL;
}
}
$C = $Corig;
// Flip the bits in one character of D to force an error
$Dorig = $D;
$D = hex2bin($D);
$D[0] = RSAPKCS1v15::intToChr(
RSAPKCS1v15::chrToInt($D[0]) ^ 0xff
);
$D = bin2hex($D);
$start = $end = microtime(true);
try {
$out = RSAPKCS1v15::decrypt($C, $D, $private_key);
} catch (\Exception $ex) {
$end = microtime(true);
var_dump($end - $start);
if ($ex->getMessage() === 'Invalid MAC') {
echo 'Got the expected error message. No RSA hints given.', PHP_EOL;
}
}
$D = $Dorig;
Enable javascript to submit You have javascript disabled. You will not be able to edit any code.
Here you find the average performance (time & memory) of each version. A grayed out version indicates it didn't complete successfully (based on exit-code).
Version System time (s) User time (s) Memory (MiB) 7.3.1 0.006 0.009 16.21 7.3.0 0.009 0.003 16.33 7.2.13 0.008 0.008 16.52 7.2.12 0.005 0.010 16.48 7.2.11 0.005 0.009 16.63 7.2.10 0.008 0.006 16.41 7.2.9 0.003 0.008 16.52 7.2.8 0.007 0.009 16.56 7.2.7 0.008 0.006 16.51 7.2.6 0.008 0.006 16.32 7.2.5 0.013 0.007 16.74 7.2.4 0.039 0.011 17.20 7.2.3 0.033 0.013 17.21 7.2.2 0.053 0.013 17.17 7.2.1 0.039 0.012 17.20 7.2.0 0.040 0.012 17.25 7.1.25 0.008 0.007 15.41 7.1.16 0.161 0.022 17.20 7.1.15 0.137 0.026 16.97 7.1.14 0.145 0.026 17.16 7.1.13 0.118 0.017 17.25 7.1.12 0.118 0.018 17.19 7.1.11 0.097 0.019 16.75 7.1.10 0.107 0.016 16.68 7.1.9 0.130 0.023 16.52 7.1.8 0.145 0.015 16.86 7.1.7 0.092 0.017 15.71 7.1.6 0.173 0.028 33.75 7.1.5 0.110 0.024 33.35 7.1.4 0.101 0.028 32.54 7.1.3 0.128 0.016 32.48 7.1.2 0.119 0.021 32.75 7.1.1 0.109 0.026 15.10 7.1.0 0.117 0.014 15.08
preferences:dark mode live preview
37.39 ms | 401 KiB | 5 Q