From bf8201f4321a66f76bc725cad3ed21813e4e0404 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Mon, 21 Jan 2013 18:18:13 -0500 Subject: [PATCH] Start work on decryption --- lib/openpgp.php | 33 ++++++++++++++++++- lib/openpgp_phpseclib_crypt.php | 57 +++++++++++++++++++++++++++++++++ tests/phpseclib_suite.php | 29 +++++++++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 lib/openpgp_phpseclib_crypt.php diff --git a/lib/openpgp.php b/lib/openpgp.php index 0e929ac..0e63968 100644 --- a/lib/openpgp.php +++ b/lib/openpgp.php @@ -128,7 +128,7 @@ class OpenPGP_S2K { case 3: $s2k->hash_algorithm = ord($input{1}); $s2k->salt = substr($input, 2, 8); - $s2k->count = OpenPGP::decode_s2k_count($input{9}); + $s2k->count = OpenPGP::decode_s2k_count(ord($input{10})); $input = substr($input, 11); break; } @@ -154,6 +154,37 @@ class OpenPGP_S2K { } return $bytes; } + + function raw_hash($s) { + return hash(strtolower(OpenPGP_SignaturePacket::$hash_algorithms[$this->hash_algorithm]), $s, true); + } + + function sized_hash($s, $size) { + $hash = $this->raw_hash($s); + while(strlen($hash) < $size) { + $s = "\0" . $s; + $hash .= $this->raw_hash($s); + } + + return substr($hash, 0, $size); + } + + function iterate($s) { + if(strlen($s) >= $this->count) return $s; + $s = str_repeat($s, ceil($this->count / strlen($s))); + return substr($s, 0, $this->count); + } + + function make_key($pass, $size) { + switch($this->type) { + case 0: + return $this->sized_hash($pass, $size); + case 1: + return $this->sized_hash($this->salt . $pass, $size); + case 3: + return $this->sized_hash($this->iterate($this->salt . $pass), $size); + } + } } ////////////////////////////////////////////////////////////////////////////// diff --git a/lib/openpgp_phpseclib_crypt.php b/lib/openpgp_phpseclib_crypt.php new file mode 100644 index 0000000..cd14c04 --- /dev/null +++ b/lib/openpgp_phpseclib_crypt.php @@ -0,0 +1,57 @@ +symmetric_algorithm) { + case 7: + $cipher = new Crypt_AES(CRYPT_AES_MODE_CFB); + $cipher->setKeyLength(128); + break; + case 8: + $cipher = new Crypt_AES(CRYPT_AES_MODE_CFB); + $cipher->setKeyLength(192); + break; + case 9: + $cipher = new Crypt_AES(CRYPT_AES_MODE_CFB); + $cipher->setKeyLength(256); + break; + } + if(!$cipher) continue; // Unsupported cipher + + $cipher->setKey($p->s2k->make_key($pass, $cipher->key_size)); + $epacket = self::getEncryptedData($m); + $padAmount = $cipher->block_size - (strlen($epacket->data) % $cipher->block_size); + + if(strlen($p->encrypted_data) < 1) { + if($epacket instanceof OpenPGP_IntegrityProtectedDataPacket) { + $data = substr($cipher->decrypt($epacket->data . str_repeat("\0", $padAmount)), 0, strlen($epacket->data)); + $prefix = substr($data, 0, $cipher->block_size + 2); + $mdc = substr(substr($data, -22, 22), 2); + $data = substr($data, $cipher->block_size + 2, -22); + + $mkMDC = hash("sha1", $prefix . $data . "\xD3\x14", true); + if($mkMDC !== $mdc) return false; + + return OpenPGP_Message::parse($data); + } else { + // TODO (resync) + } + } else { + // TODO + } + } + } + } + + public static function getEncryptedData($m) { + foreach($m as $p) { + if($p instanceof OpenPGP_EncryptedDataPacket) return $p; + } + throw new Exception("Can only decrypt EncryptedDataPacket"); + } +} diff --git a/tests/phpseclib_suite.php b/tests/phpseclib_suite.php index 19dd665..559be3d 100644 --- a/tests/phpseclib_suite.php +++ b/tests/phpseclib_suite.php @@ -4,6 +4,7 @@ require_once dirname(__FILE__).'/../lib/openpgp.php'; require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php'; +require_once dirname(__FILE__).'/../lib/openpgp_phpseclib_crypt.php'; class MessageVerification extends PHPUnit_Framework_TestCase { public function oneMessageRSA($pkey, $path) { @@ -61,3 +62,31 @@ class KeyVerification extends PHPUnit_Framework_TestCase { $this->oneKeyRSA("helloKey.gpg"); } } + + +class Decryption extends PHPUnit_Framework_TestCase { + public function oneSymmetric($pass, $cnt, $path) { + $m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path)); + $m2 = OpenPGP_phpseclib_Crypt::decryptSymmetric($pass, $m); + while($m2[0] instanceof OpenPGP_CompressedDataPacket) $m2 = $m2[0]->data; + foreach($m2 as $p) { + if($p instanceof OpenPGP_LiteralDataPacket) { + $this->assertEquals($p->data, $cnt); + } + } + } + + public function testDecryptAES() { + $this->oneSymmetric("hello", "PGP\n", "symmetric-aes.gpg"); + } + +/* TODO + public function testDecryptSessionKey() { + $this->oneSymmetric("hello", "PGP\n", "symmetric-with-session-key.gpg"); + } + + public function testDecryptNoMDC() { + $this->oneSymmetric("hello", "PGP\n", "symmetric-no-mdc.gpg"); + } +*/ +}