Encryption support

This commit is contained in:
Stephen Paul Weber 2013-01-26 17:01:26 -05:00
parent a56799955f
commit 7d776fd605
4 changed files with 127 additions and 6 deletions

View File

@ -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;
}
}
/**

View File

@ -1,10 +1,49 @@
<?php
require_once dirname(__FILE__).'/openpgp.php';
@include_once dirname(__FILE__).'/openpgp_crypt_rsa.php';
require_once 'Crypt/AES.php';
require_once 'Crypt/TripleDES.php';
require_once 'Crypt/Random.php';
class OpenPGP_Crypt_AES_TripleDES {
public static function encrypt($passphrases_and_keys, $message, $symmetric_algorithm=9) {
list($cipher, $key_bytes, $key_block_bytes) = self::getCipher($symmetric_algorithm);
$prefix = self::randomBytes($key_block_bytes);
$prefix .= substr($prefix, -2);
$key = self::randomBytes($key_bytes);
$cipher->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;
}
}

View File

@ -15,5 +15,13 @@
<testsuite name="KeyVerification">
<file>tests/phpseclib_suite.php</file>
</testsuite>
<testsuite name="Decryption">
<file>tests/phpseclib_suite.php</file>
</testsuite>
<testsuite name="Encryption">
<file>tests/phpseclib_suite.php</file>
</testsuite>
</testsuites>
</phpunit>

View File

@ -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.');
}
}