@ 2018-04-25T06:39:59Z <?php
declare(strict_types=1);
namespace ParagonIE\BlogExampleCode;
/**
* Implements encryption RSA with PKCS1v1.5 padding
* using the Anti-BB'98 dance.
*
* @ref https://paragonie.com/b/_8qH-xPuGPUoSSsc
*/
class RSAPKCS1v15
{
public static function encrypt(string $message, string $publicKey): array
{
$key = \random_bytes(32);
$C = '';
// This defaults to PKCS1v1.5:
\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)];
}
public static function decrypt(string $CHex, string $DHex, string $privateKey): string
{
$kPrime = \random_bytes(32);
$C = \hex2bin($CHex);
$D = \hex2bin($DHex);
$k = '';
$success = \openssl_private_decrypt($C, $k, $privateKey);
// Do a constant-time swap:
$key = self::cswap32($k, (string) $kPrime, $success);
$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
);
}
/**
* Branch-less, timing safe version of
* $A = ($switch === 0 ? $A : $B);
*
* @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');
}
public static function chrToInt(string $chr): int
{
$chunk = \unpack('C', $chr);
return (int) ($chunk[1]);
}
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.
Output for 7.0.0 - 7.0.33 , 7.1.0 - 7.1.33 , 7.2.0 - 7.2.34 , 7.3.0 - 7.3.33 , 7.4.0 - 7.4.33 , 8.0.0 - 8.0.30 , 8.1.0 - 8.1.29 , 8.2.0 - 8.2.23 , 8.3.0 - 8.3.11 Fatal error: Uncaught Error: Call to undefined function openssl_public_encrypt() in /in/k4c0tj:19
Stack trace:
#0 /in/k4c0tj(137): ParagonIE\BlogExampleCode\RSAPKCS1v15::encrypt('This is a test ...', '-----BEGIN PUBL...')
#1 {main}
thrown in /in/k4c0tj on line 19
Process exited with code 255 . Output for 5.6.0 - 5.6.40 Warning: Unsupported declare 'strict_types' in /in/k4c0tj on line 2
Parse error: syntax error, unexpected ':', expecting ';' or '{' in /in/k4c0tj on line 13
Process exited with code 255 . preferences:dark mode live preview
72.69 ms | 407 KiB | 5 Q