Asymmetric decryption
This commit is contained in:
parent
641c07835b
commit
cd15aec6f9
@ -568,7 +568,37 @@ class OpenPGP_Packet {
|
||||
* @see http://tools.ietf.org/html/rfc4880#section-5.1
|
||||
*/
|
||||
class OpenPGP_AsymmetricSessionKeyPacket extends OpenPGP_Packet {
|
||||
// TODO
|
||||
public $version, $keyid, $key_algorithm, $encrypted_data;
|
||||
|
||||
function read() {
|
||||
switch($this->version = ord($this->read_byte())) {
|
||||
case 3:
|
||||
$rawkeyid = $this->read_bytes(8);
|
||||
$this->keyid = '';
|
||||
for($i = 0; $i < strlen($rawkeyid); $i++) { // Store KeyID in Hex
|
||||
$this->keyid .= sprintf('%02X',ord($rawkeyid{$i}));
|
||||
}
|
||||
|
||||
$this->key_algorithm = ord($this->read_byte());
|
||||
|
||||
$this->encrypted_data = $this->input;
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unsupported AsymmetricSessionKeyPacket version: " . $this->version);
|
||||
}
|
||||
}
|
||||
|
||||
function body() {
|
||||
$bytes = ord($this->version);
|
||||
|
||||
for($i = 0; $i < strlen($this->keyid); $i += 2) {
|
||||
$bytes .= chr(hexdec($this->keyid{$i}.$this->keyid{$i+1}));
|
||||
}
|
||||
|
||||
$bytes .= chr($this->key_algorithm);
|
||||
$bytes .= $this->encrypted_data;
|
||||
return $bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6,55 +6,67 @@ require_once 'Crypt/TripleDES.php';
|
||||
|
||||
class OpenPGP_Crypt_AES_TripleDES {
|
||||
public static function decryptSymmetric($pass, $m) {
|
||||
$epacket = self::getEncryptedData($m);
|
||||
|
||||
foreach($m as $p) {
|
||||
if($p instanceof OpenPGP_SymmetricSessionKeyPacket) {
|
||||
list($cipher, $key_bytes, $key_block_bytes) = self::getCipher($p->symmetric_algorithm);
|
||||
if(!$cipher) continue;
|
||||
$cipher->setKey($p->s2k->make_key($pass, $key_bytes));
|
||||
|
||||
if(strlen($p->encrypted_data) > 0) {
|
||||
list($cipher, $key_bytes, $key_block_bytes) = self::getCipher($p->symmetric_algorithm);
|
||||
if(!$cipher) continue;
|
||||
$cipher->setKey($p->s2k->make_key($pass, $key_bytes));
|
||||
|
||||
$padAmount = $key_block_bytes - (strlen($p->encrypted_data) % $key_block_bytes);
|
||||
$data = substr($cipher->decrypt($p->encrypted_data . str_repeat("\0", $padAmount)), 0, strlen($p->encrypted_data));
|
||||
list($cipher, $key_bytes, $key_block_bytes) = self::getCipher(ord($data{0}));
|
||||
if(!$cipher) continue;
|
||||
$cipher->setKey(substr($data, 1));
|
||||
}
|
||||
|
||||
$epacket = self::getEncryptedData($m);
|
||||
$padAmount = $key_block_bytes - (strlen($epacket->data) % $key_block_bytes);
|
||||
|
||||
if($epacket instanceof OpenPGP_IntegrityProtectedDataPacket) {
|
||||
$data = substr($cipher->decrypt($epacket->data . str_repeat("\0", $padAmount)), 0, strlen($epacket->data));
|
||||
$prefix = substr($data, 0, $key_block_bytes + 2);
|
||||
$mdc = substr(substr($data, -22, 22), 2);
|
||||
$data = substr($data, $key_block_bytes + 2, -22);
|
||||
|
||||
$mkMDC = hash("sha1", $prefix . $data . "\xD3\x14", true);
|
||||
if($mkMDC !== $mdc) return false;
|
||||
|
||||
try {
|
||||
$msg = OpenPGP_Message::parse($data);
|
||||
} catch (Exception $ex) { $msg = NULL; }
|
||||
if($msg) return $msg; /* Otherwise keep trying */
|
||||
$decrypted = self::decryptPacket($epacket, ord($data{0}), substr($data, 1));
|
||||
} else {
|
||||
// No MDC mean decrypt with resync
|
||||
$iv = substr($epacket->data, 2, $key_block_bytes);
|
||||
$edata = substr($epacket->data, $key_block_bytes + 2);
|
||||
|
||||
$cipher->setIV($iv);
|
||||
$data = substr($cipher->decrypt($edata . str_repeat("\0", $padAmount)), 0, strlen($edata));
|
||||
|
||||
try {
|
||||
$msg = OpenPGP_Message::parse($data);
|
||||
} catch (Exception $ex) { $msg = NULL; }
|
||||
if($msg) return $msg; /* Otherwise keep trying */
|
||||
list($cipher, $key_bytes, $key_block_bytes) = self::getCipher($p->symmetric_algorithm);
|
||||
$decrypted = self::decryptPacket($epacket, $p->symmetric_algorithm, $p->s2k->make_key($pass, $key_bytes));
|
||||
}
|
||||
|
||||
if($decrypted) return $decrypted;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL; /* If we get here, we failed */
|
||||
}
|
||||
|
||||
public static function decryptPacket($epacket, $symmetric_algorithm, $key) {
|
||||
list($cipher, $key_bytes, $key_block_bytes) = self::getCipher($symmetric_algorithm);
|
||||
if(!$cipher) return NULL;
|
||||
$cipher->setKey($key);
|
||||
|
||||
$padAmount = $key_block_bytes - (strlen($epacket->data) % $key_block_bytes);
|
||||
|
||||
if($epacket instanceof OpenPGP_IntegrityProtectedDataPacket) {
|
||||
$data = substr($cipher->decrypt($epacket->data . str_repeat("\0", $padAmount)), 0, strlen($epacket->data));
|
||||
$prefix = substr($data, 0, $key_block_bytes + 2);
|
||||
$mdc = substr(substr($data, -22, 22), 2);
|
||||
$data = substr($data, $key_block_bytes + 2, -22);
|
||||
|
||||
$mkMDC = hash("sha1", $prefix . $data . "\xD3\x14", true);
|
||||
if($mkMDC !== $mdc) return false;
|
||||
|
||||
try {
|
||||
$msg = OpenPGP_Message::parse($data);
|
||||
} catch (Exception $ex) { $msg = NULL; }
|
||||
if($msg) return $msg; /* Otherwise keep trying */
|
||||
} else {
|
||||
// No MDC mean decrypt with resync
|
||||
$iv = substr($epacket->data, 2, $key_block_bytes);
|
||||
$edata = substr($epacket->data, $key_block_bytes + 2);
|
||||
|
||||
$cipher->setIV($iv);
|
||||
$data = substr($cipher->decrypt($edata . str_repeat("\0", $padAmount)), 0, strlen($edata));
|
||||
|
||||
try {
|
||||
$msg = OpenPGP_Message::parse($data);
|
||||
} catch (Exception $ex) { $msg = NULL; }
|
||||
if($msg) return $msg; /* Otherwise keep trying */
|
||||
}
|
||||
|
||||
return NULL; /* Failed */
|
||||
}
|
||||
|
||||
public static function getCipher($algo) {
|
||||
switch($algo) {
|
||||
case 2:
|
||||
|
@ -12,6 +12,9 @@
|
||||
// From http://phpseclib.sourceforge.net/
|
||||
require 'Crypt/RSA.php';
|
||||
|
||||
require_once dirname(__FILE__).'/openpgp.php';
|
||||
@include_once dirname(__FILE__).'/openpgp_cryph_aes_tripledes.php'; /* For encrypt/decrypt */
|
||||
|
||||
class OpenPGP_Crypt_RSA {
|
||||
protected $key, $message;
|
||||
|
||||
@ -157,6 +160,64 @@ class OpenPGP_Crypt_RSA {
|
||||
return $packet;
|
||||
}
|
||||
|
||||
function decrypt($packet) {
|
||||
if(!is_object($packet)) $packet = OpenPGP_Message::parse($packet);
|
||||
|
||||
if($packet instanceof OpenPGP_SecretKeyPacket || $packet instanceof Crypt_RSA
|
||||
|| ($packet instanceof ArrayAccess && $packet[0] instanceof OpenPGP_SecretKeyPacket)) {
|
||||
$keys = $packet;
|
||||
$message = $this->message;
|
||||
} else {
|
||||
$keys = $this->key;
|
||||
$message = $packet;
|
||||
}
|
||||
|
||||
if(!$keys || !$message) return NULL; // Missing some data
|
||||
|
||||
if(!($keys instanceof Crypt_RSA)) {
|
||||
$keys = new self($keys);
|
||||
}
|
||||
|
||||
foreach($message as $p) {
|
||||
if($p instanceof OpenPGP_AsymmetricSessionKeyPacket) {
|
||||
if($keys instanceof Crypt_RSA) {
|
||||
$sk = self::try_decrypt_session($keys, $p->encyrpted_data);
|
||||
} else if(strlen(str_replace('0', '', $p->keyid)) < 1) {
|
||||
foreach($keys->key as $k) {
|
||||
$sk = self::try_decrypt_session(self::convert_private_key($k), $p->encyrpted_data);
|
||||
if($sk) break;
|
||||
}
|
||||
} else {
|
||||
$key = $keys->private_key($p->keyid);
|
||||
$sk = self::try_decrypt_session($key, substr($p->encrypted_data, 2));
|
||||
}
|
||||
|
||||
if(!$sk) continue;
|
||||
|
||||
$r = OpenPGP_Crypt_AES_TripleDES::decryptPacket(OpenPGP_Crypt_AES_TripleDES::getEncryptedData($message), $sk[0], $sk[1]);
|
||||
if($r) return $r;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL; /* Failed */
|
||||
}
|
||||
|
||||
static function try_decrypt_session($key, $edata) {
|
||||
$key->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
|
||||
$data = $key->decrypt($edata);
|
||||
$sk = substr($data, 1, strlen($data)-3);
|
||||
$chk = unpack('n', substr($data, -2));
|
||||
$chk = reset($chk);
|
||||
|
||||
$sk_chk = 0;
|
||||
for($i = 0; $i < strlen($sk); $i++) {
|
||||
$sk_chk = ($sk_chk + ord($sk{$i})) % 65536;
|
||||
}
|
||||
|
||||
if($sk_chk != $chk) return NULL;
|
||||
return array(ord($data{0}), $sk);
|
||||
}
|
||||
|
||||
static function crypt_rsa_key($mod, $exp, $hash='SHA256') {
|
||||
$rsa = new Crypt_RSA();
|
||||
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
|
||||
|
BIN
tests/data/hello.gpg
Normal file
BIN
tests/data/hello.gpg
Normal file
Binary file not shown.
@ -91,4 +91,16 @@ class Decryption extends PHPUnit_Framework_TestCase {
|
||||
public function testDecryptNoMDC() {
|
||||
$this->oneSymmetric("hello", "PGP\n", "symmetric-no-mdc.gpg");
|
||||
}
|
||||
|
||||
public function testDecryptAsymmetric() {
|
||||
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/hello.gpg'));
|
||||
$key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg'));
|
||||
$m2 = (new OpenPGP_Crypt_RSA($key))->decrypt($m);
|
||||
while($m2[0] instanceof OpenPGP_CompressedDataPacket) $m2 = $m2[0]->data;
|
||||
foreach($m2 as $p) {
|
||||
if($p instanceof OpenPGP_LiteralDataPacket) {
|
||||
$this->assertEquals($p->data, "hello\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user