diff --git a/lib/openpgp.php b/lib/openpgp.php index 3fdd9d9..927126c 100644 --- a/lib/openpgp.php +++ b/lib/openpgp.php @@ -113,6 +113,13 @@ class OpenPGP { class OpenPGP_S2K { public $type, $hash_algorithm, $salt, $count; + function __construct($salt='BADSALT', $hash_algorithm=10, $count=65536, $type=3) { + $this->type = $type; + $this->hash_algorithm = $hash_algorithm; + $this->salt = $salt; + $this->count = $count; + } + static function parse(&$input) { $s2k = new OpenPGP_S2k(); switch($s2k->type = ord($input{0})) { @@ -570,6 +577,13 @@ class OpenPGP_Packet { class OpenPGP_AsymmetricSessionKeyPacket extends OpenPGP_Packet { public $version, $keyid, $key_algorithm, $encrypted_data; + function __construct($key_algorithm='', $keyid='', $encrypted_data='', $version=3) { + $this->version = $version; + $this->keyid = substr($keyid, -16); + $this->key_algorithm = $key_algorithm; + $this->encrypted_data = $encrypted_data; + } + function read() { switch($this->version = ord($this->read_byte())) { case 3: @@ -1230,6 +1244,13 @@ class OpenPGP_SignaturePacket_EmbeddedSignaturePacket extends OpenPGP_SignatureP class OpenPGP_SymmetricSessionKeyPacket extends OpenPGP_Packet { public $version, $symmetric_algorithm, $s2k, $encrypted_data; + function __construct($s2k=NULL, $encrypted_data='', $symmetric_algorithm=9, $version=3) { + $this->version = $version; + $this->symmetric_algorithm = $symmetric_algorithm; + $this->s2k = $s2k; + $this->encrypted_data = $encrypted_data; + } + function read() { $this->version = ord($this->read_byte()); $this->symmetric_algorithm = ord($this->read_byte()); @@ -1775,6 +1796,10 @@ class OpenPGP_UserAttributePacket extends OpenPGP_Packet { class OpenPGP_IntegrityProtectedDataPacket extends OpenPGP_EncryptedDataPacket { public $version; + function __construct($data='', $version=1) { + $this->data = $data; + } + function read() { $this->version = ord($this->read_byte()); $this->data = $this->input; @@ -1791,7 +1816,24 @@ class OpenPGP_IntegrityProtectedDataPacket extends OpenPGP_EncryptedDataPacket { * @see http://tools.ietf.org/html/rfc4880#section-5.14 */ class OpenPGP_ModificationDetectionCodePacket extends OpenPGP_Packet { - // TODO + function __construct($sha1='') { + $this->data = $sha1; + } + + function read() { + $this->data = $this->input; + if(strlen($this->input) != 20) throw new Exception("Bad ModificationDetectionCodePacket"); + } + + function header_and_body() { + $body = $this->body(); // Get body first, we will need it's length + if(strlen($body) != 20) throw new Exception("Bad ModificationDetectionCodePacket"); + return array('header' => "\xD3\x14", 'body' => $body); + } + + function body() { + return $this->data; + } } /** diff --git a/lib/openpgp_crypt_aes_tripledes.php b/lib/openpgp_crypt_aes_tripledes.php index 8309819..46d5985 100644 --- a/lib/openpgp_crypt_aes_tripledes.php +++ b/lib/openpgp_crypt_aes_tripledes.php @@ -1,10 +1,49 @@ setKey($key); + + $to_encrypt = $prefix . $message->to_bytes(); + $mdc = new OpenPGP_ModificationDetectionCodePacket(hash('sha1', $to_encrypt . "\xD3\x14", true)); + $to_encrypt .= $mdc->to_bytes(); + $encrypted = array(new OpenPGP_IntegrityProtectedDataPacket($cipher->encrypt($to_encrypt))); + + if(!is_array($passphrases_and_keys) && !($passphrases_and_keys instanceof IteratorAggregate)) { + $passphrases_and_keys = (array)$passphrases_and_keys; + } + + foreach($passphrases_and_keys as $pass) { + if($pass instanceof OpenPGP_PublicKeyPacket) { + if(!in_array($pass->algorithm, array(1,2,3))) throw new Exception("Only RSA keys are supported."); + $crypt_rsa = new OpenPGP_Crypt_RSA($pass); + $rsa = $crypt_rsa->public_key(); + $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); + $esk = $rsa->encrypt(chr($symmetric_algorithm) . $key . pack('n', self::checksum($key))); + $esk = pack('n', OpenPGP::bitlength($esk)) . $esk; + array_unshift($encrypted, new OpenPGP_AsymmetricSessionKeyPacket($pass->algorithm, $pass->fingerprint(), $esk)); + } else if(is_string($pass)) { + $s2k = new OpenPGP_S2K(crypt_random() . crypt_random() . crypt_random()); + $cipher->setKey($s2k->make_key($pass, $key_bytes)); + $esk = $cipher->encrypt(chr($symmetric_algorithm) . $key); + array_unshift($encrypted, new OpenPGP_SymmetricSessionKeyPacket($s2k, $esk, $symmetric_algorithm)); + } + } + + return new OpenPGP_Message($encrypted); + } + public static function decryptSymmetric($pass, $m) { $epacket = self::getEncryptedData($m); @@ -41,16 +80,13 @@ class OpenPGP_Crypt_AES_TripleDES { if($packet->s2k_useage == 254) { $chk = substr($material, -20); $material = substr($material, 0, -20); - if($chk != hash('sha1', $material)) return NULL; + if($chk != hash('sha1', $material, true)) return NULL; } else { $chk = unpack('n', substr($material, -2)); $chk = reset($chk); $material = substr($material, 0, -2); - $mkChk = 0; - for($i = 0; $i < strlen($material); $i++) { - $mkChk = ($mkChk + ord($material{$i})) % 65536; - } + $mkChk = self::checksum($material); if($chk != $mkChk) return NULL; } @@ -139,4 +175,21 @@ class OpenPGP_Crypt_AES_TripleDES { } throw new Exception("Can only decrypt EncryptedDataPacket"); } + + public static function randomBytes($n) { + $key = ''; + for($i = 0; $i < $n; $i++) { + $key .= crypt_random(); + } + $s2k = new OpenPGP_S2K(crypt_random() . crypt_random() . crypt_random()); + return $s2k->make_key($key, $n); + } + + public static function checksum($s) { + $mkChk = 0; + for($i = 0; $i < strlen($s); $i++) { + $mkChk = ($mkChk + ord($s{$i})) % 65536; + } + return $mkChk; + } } diff --git a/phpunit.xml b/phpunit.xml index 41b0d95..a071d34 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -15,5 +15,13 @@ tests/phpseclib_suite.php + + + tests/phpseclib_suite.php + + + + tests/phpseclib_suite.php + diff --git a/tests/phpseclib_suite.php b/tests/phpseclib_suite.php index 8044fbf..e5ed903 100644 --- a/tests/phpseclib_suite.php +++ b/tests/phpseclib_suite.php @@ -111,3 +111,21 @@ class Decryption extends PHPUnit_Framework_TestCase { $this->assertSame(!!$skey, true); } } + +class Encryption extends PHPUnit_Framework_TestCase { + public function testEncryptSymmetric() { + $data = new OpenPGP_LiteralDataPacket('This is text.', array('format' => 'u', 'filename' => 'stuff.txt')); + $encrypted = OpenPGP_Crypt_AES_TripleDES::encrypt('secret', new OpenPGP_Message(array($data))); + $decrypted = OpenPGP_Crypt_AES_TripleDES::decryptSymmetric('secret', $encrypted); + $this->assertEquals($decrypted[0]->data, 'This is text.'); + } + + public function testEncryptAsymmetric() { + $key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg')); + $data = new OpenPGP_LiteralDataPacket('This is text.', array('format' => 'u', 'filename' => 'stuff.txt')); + $encrypted = OpenPGP_Crypt_AES_TripleDES::encrypt($key, new OpenPGP_Message(array($data))); + $decryptor = new OpenPGP_Crypt_RSA($key); + $decrypted = $decryptor->decrypt($encrypted); + $this->assertEquals($decrypted[0]->data, 'This is text.'); + } +}