From 9936e839a49f70757433acb8aabea954906c1eb5 Mon Sep 17 00:00:00 2001 From: Deon George Date: Wed, 24 Jun 2020 22:37:14 +1000 Subject: [PATCH] Update objects to use __toString() --- lib/OpenPGP.php | 64 ++++++++++++++++++- lib/OpenPgP/Crypt/Symmetric.php | 2 +- lib/OpenPgP/Message.php | 13 +++- .../ModificationDetectionCodePacket.php | 2 +- lib/OpenPgP/Packet.php | 26 ++++---- lib/OpenPgP/PublicKeyPacket.php | 9 +++ lib/OpenPgP/SignaturePacket.php | 4 +- .../EmbeddedSignaturePacket.php | 12 ++-- .../SignaturePacket/PolicyURIPacket.php | 5 -- .../PreferredKeyServerPacket.php | 5 -- .../SignaturePacket/SignersUserIDPacket.php | 5 -- lib/OpenPgP/SignaturePacket/Subpacket.php | 19 +++--- lib/OpenPgP/UserIDPacket.php | 14 ++-- tests/phpseclib_suite.php | 31 +++++---- tests/suite.php | 12 ++-- 15 files changed, 145 insertions(+), 78 deletions(-) diff --git a/lib/OpenPGP.php b/lib/OpenPGP.php index 1f17b88..9f17472 100644 --- a/lib/OpenPGP.php +++ b/lib/OpenPGP.php @@ -14,8 +14,8 @@ namespace Leenooks; use Illuminate\Support\Arr; -use phpseclib\Crypt\RSA as Crypt_RSA; +use phpseclib\Crypt\RSA as Crypt_RSA; use Leenooks\OpenPGP\Exceptions\PacketTagException; /** @@ -26,6 +26,8 @@ class OpenPGP const VERSION = [0,5,0]; private $key = NULL; + // Functions + /** * @see http://tools.ietf.org/html/rfc4880#section-12.2 */ @@ -97,12 +99,18 @@ class OpenPGP return ((int)16 + ($c & 15)) << (($c >> 4) + 6); } + public function decrypt($data) + { + $decryptor = new OpenPGP\Crypt\RSA($this->key); + return $decryptor->decrypt($data); + } + /** * @see http://tools.ietf.org/html/rfc4880#section-6 * @see http://tools.ietf.org/html/rfc4880#section-6.2 * @see http://tools.ietf.org/html/rfc2045 */ - static function enarmor($data,$marker='MESSAGE',array $headers=[]) + static function enarmor1($data,$marker='MESSAGE',array $headers=[]) { $text = self::header($marker)."\n"; @@ -117,6 +125,16 @@ class OpenPGP return $text; } + protected function enarmor(string $data,$marker='MESSAGE',array $headers=[]): string + { + return static::enarmor1($data,$marker,$headers); + } + + public function encrypt(OpenPGP\LiteralDataPacket $data) + { + return OpenPGP\Crypt\Symmetric::encrypt($this->key,new OpenPGP\Message([$data])); + } + static function encode_s2k_count($iterations) { if($iterations >= 65011712) return 255; @@ -153,6 +171,42 @@ class OpenPGP return '-----BEGIN '.strtoupper((string)$marker).'-----'; } + public function key() + { + return $this->key; + } + + static function load(string $data,$marker='MESSAGE',bool $binary=TRUE): self + { + $result = new self; + + $result->key = OpenPGP\Message::parse( + $binary ? $data : OpenPGP::unarmor($data,$marker) + ); + + return $result; + } + + public function private(): string + { + return $this->enarmor((string)$this->key,'PRIVATE KEY'); + } + + protected function publicKey(): OpenPGP\PublicKeyPacket + { + return $this->key[0]; + } + + public function public(): string + { + return $this->enarmor((string)$this->publicKey(),'PUBLIC KEY'); + } + + public function signatures() + { + return $this->key->signatures(); + } + /** * @see http://tools.ietf.org/html/rfc4880#section-6 * @see http://tools.ietf.org/html/rfc2045 @@ -170,4 +224,10 @@ class OpenPGP return base64_decode($text=substr($text,$pos1,$pos2-$pos1)); } } + + public function verify(OpenPGP\Message $data) + { + $verify = new OpenPGP\Crypt\RSA($this->key); + return $verify->verify($data); + } } \ No newline at end of file diff --git a/lib/OpenPgP/Crypt/Symmetric.php b/lib/OpenPgP/Crypt/Symmetric.php index e7359bb..490491a 100644 --- a/lib/OpenPgP/Crypt/Symmetric.php +++ b/lib/OpenPgP/Crypt/Symmetric.php @@ -35,7 +35,7 @@ class Symmetric $to_encrypt = $prefix.$message->to_bytes(); $mdc = new OpenPGP\ModificationDetectionCodePacket(hash('sha1',$to_encrypt."\xD3\x14",true)); - $to_encrypt .= $mdc->to_bytes(); + $to_encrypt .= (string)$mdc; if (static::$DEBUG) dump(['to_encrypt'=>$to_encrypt]); diff --git a/lib/OpenPgP/Message.php b/lib/OpenPgP/Message.php index 8a5b268..8c7dac8 100644 --- a/lib/OpenPgP/Message.php +++ b/lib/OpenPgP/Message.php @@ -48,6 +48,17 @@ class Message implements \IteratorAggregate,\ArrayAccess $this->packets = $packets; } + public function __toString() + { + $result = ''; + + foreach ($this as $p) { + $result .= (string)$p; + } + + return $result; + } + /** * @see http://tools.ietf.org/html/rfc4880#section-4.1 * @see http://tools.ietf.org/html/rfc4880#section-4.2 @@ -192,7 +203,7 @@ class Message implements \IteratorAggregate,\ArrayAccess $bytes = ''; foreach ($this as $p) { - $bytes .= $p->to_bytes(); + $bytes .= (string)$p; } return $bytes; diff --git a/lib/OpenPgP/ModificationDetectionCodePacket.php b/lib/OpenPgP/ModificationDetectionCodePacket.php index 8bf5119..37a2fe2 100644 --- a/lib/OpenPgP/ModificationDetectionCodePacket.php +++ b/lib/OpenPgP/ModificationDetectionCodePacket.php @@ -11,7 +11,7 @@ class ModificationDetectionCodePacket extends Packet { protected $tag = 19; - function header_and_body(): array + protected function header_and_body(): array { // Get body first, we will need it's length $body = $this->body(); diff --git a/lib/OpenPgP/Packet.php b/lib/OpenPgP/Packet.php index 3e8e3d0..236b43b 100644 --- a/lib/OpenPgP/Packet.php +++ b/lib/OpenPgP/Packet.php @@ -40,6 +40,13 @@ abstract class Packet 63 => 'Experimental', // Private or Experimental Values ]; + public function __toString() + { + $data = $this->header_and_body(); + + return $data['header'].$data['body']; + } + static function class_for($tag) { return (isset(self::$tags[$tag]) AND class_exists($class='Leenooks\OpenPGP\\'.self::$tags[$tag].'Packet')) @@ -214,13 +221,17 @@ abstract class Packet { } - function header_and_body(): array + protected function header_and_body(): array { - $body = $this->body(); // Get body first, we will need it's length - $size = chr(255).pack('N',strlen($body)); // Use 5-octet lengths $tag = chr($this->tag|0xC0); // First two bits are 1 for new packet format - return ['header'=>$tag.$size,'body'=>$body]; + return ['header'=>$tag.$this->size(),'body'=>$this->body()]; + } + + protected function size(): string + { + // Use 5-octet lengths + return chr(255).pack('N',strlen($this->body())); } public function tag(): int @@ -228,13 +239,6 @@ abstract class Packet return $this->tag; } - function to_bytes() - { - $data = $this->header_and_body(); - - return $data['header'].$data['body']; - } - /** * @see http://tools.ietf.org/html/rfc4880#section-3.5 */ diff --git a/lib/OpenPgP/PublicKeyPacket.php b/lib/OpenPgP/PublicKeyPacket.php index 18ba5a3..80d9556 100644 --- a/lib/OpenPgP/PublicKeyPacket.php +++ b/lib/OpenPgP/PublicKeyPacket.php @@ -38,9 +38,15 @@ class PublicKeyPacket extends Packet function __construct($key=[],$algorithm='RSA',$timestamp=NULL,$version=4) { + if (self::$DEBUG) + dump(['CREATE'=>__METHOD__,'key'=>$key,'alg'=>$algorithm,'ts'=>$timestamp,'version'=>$version]); + parent::__construct(); if ($key instanceof PublicKeyPacket) { + if (self::$DEBUG) + dump('key is PublicKeyPacket'); + $this->algorithm = $key->algorithm; $this->key = array(); @@ -56,6 +62,9 @@ class PublicKeyPacket extends Packet $this->v3_days_of_validity = $key->v3_days_of_validity; } else { + if (self::$DEBUG) + dump(['key'=>$key]); + $this->key = $key; if (is_string($this->algorithm = $algorithm)) { $this->algorithm = array_search($this->algorithm,self::$algorithms); diff --git a/lib/OpenPgP/SignaturePacket.php b/lib/OpenPgP/SignaturePacket.php index 83d9579..99927a7 100644 --- a/lib/OpenPgP/SignaturePacket.php +++ b/lib/OpenPgP/SignaturePacket.php @@ -143,7 +143,7 @@ class SignaturePacket extends Packet $unhashed_subpackets = ''; foreach((array)$this->unhashed_subpackets as $p) { - $unhashed_subpackets .= $p->to_bytes(); + $unhashed_subpackets .= (string)$p; } $body .= pack('n',strlen($unhashed_subpackets)).$unhashed_subpackets; @@ -164,7 +164,7 @@ class SignaturePacket extends Packet $hashed_subpackets = ''; foreach((array)$this->hashed_subpackets as $p) { - $hashed_subpackets .= $p->to_bytes(); + $hashed_subpackets .= (string)$p; } $body .= pack('n',strlen($hashed_subpackets)).$hashed_subpackets; diff --git a/lib/OpenPgP/SignaturePacket/EmbeddedSignaturePacket.php b/lib/OpenPgP/SignaturePacket/EmbeddedSignaturePacket.php index f827afc..bdcfb96 100644 --- a/lib/OpenPgP/SignaturePacket/EmbeddedSignaturePacket.php +++ b/lib/OpenPgP/SignaturePacket/EmbeddedSignaturePacket.php @@ -11,12 +11,14 @@ class EmbeddedSignaturePacket extends SignaturePacket { protected $tag = 32; - function header_and_body(): array + protected function header_and_body(): array { - $body = $this->body(); // Get body first, we will need it's length - $size = chr(255).pack('N',strlen($body)+1); // Use 5-octet lengths + 1 for tag as first packet body octet - $tag = chr($this->tag); + return ['header'=>$this->size().chr($this->tag),'body'=>$this->body()]; + } - return ['header'=>$size.$tag,'body'=>$body]; + protected function size(): string + { + // Use 5-octet lengths + 1 for tag as first packet body octet + return chr(255).pack('N',strlen($this->body())+1); } } \ No newline at end of file diff --git a/lib/OpenPgP/SignaturePacket/PolicyURIPacket.php b/lib/OpenPgP/SignaturePacket/PolicyURIPacket.php index eb20fb8..64b001f 100644 --- a/lib/OpenPgP/SignaturePacket/PolicyURIPacket.php +++ b/lib/OpenPgP/SignaturePacket/PolicyURIPacket.php @@ -9,11 +9,6 @@ class PolicyURIPacket extends Subpacket { protected $tag = 26; - function body() - { - return $this->data; - } - function read() { $this->data = $this->input; diff --git a/lib/OpenPgP/SignaturePacket/PreferredKeyServerPacket.php b/lib/OpenPgP/SignaturePacket/PreferredKeyServerPacket.php index 50ee8d5..4fc3245 100644 --- a/lib/OpenPgP/SignaturePacket/PreferredKeyServerPacket.php +++ b/lib/OpenPgP/SignaturePacket/PreferredKeyServerPacket.php @@ -9,11 +9,6 @@ class PreferredKeyServerPacket extends Subpacket { protected $tag = 24; - function body() - { - return $this->data; - } - function read() { $this->data = $this->input; diff --git a/lib/OpenPgP/SignaturePacket/SignersUserIDPacket.php b/lib/OpenPgP/SignaturePacket/SignersUserIDPacket.php index 269ba16..a483bf8 100644 --- a/lib/OpenPgP/SignaturePacket/SignersUserIDPacket.php +++ b/lib/OpenPgP/SignaturePacket/SignersUserIDPacket.php @@ -9,11 +9,6 @@ class SignersUserIDPacket extends Subpacket { protected $tag = 28; - function body() - { - return $this->data; - } - function read() { $this->data = $this->input; diff --git a/lib/OpenPgP/SignaturePacket/Subpacket.php b/lib/OpenPgP/SignaturePacket/Subpacket.php index ea4d8e3..4e53e99 100644 --- a/lib/OpenPgP/SignaturePacket/Subpacket.php +++ b/lib/OpenPgP/SignaturePacket/Subpacket.php @@ -9,18 +9,9 @@ class Subpacket extends Packet { protected $tag = NULL; - function body() + protected function header_and_body(): array { - return $this->data; - } - - function header_and_body(): array - { - $body = $this->body(); // Get body first, we will need it's length - $size = chr(255).pack('N',strlen($body)+1); // Use 5-octet lengths + 1 for tag as first packet body octet - $tag = chr($this->tag); - - return ['header'=>$size.$tag,'body'=>$body]; + return ['header'=>$this->size().chr($this->tag),'body'=>$this->body()]; } /* Defaults for unsupported packets */ @@ -36,4 +27,10 @@ class Subpacket extends Packet $this->tag = $tag; } + + protected function size(): string + { + // Use 5-octet lengths + 1 for tag as first packet body octet + return chr(255).pack('N',strlen($this->body())+1); + } } \ No newline at end of file diff --git a/lib/OpenPgP/UserIDPacket.php b/lib/OpenPgP/UserIDPacket.php index 3bff6ee..25a519e 100644 --- a/lib/OpenPgP/UserIDPacket.php +++ b/lib/OpenPgP/UserIDPacket.php @@ -16,11 +16,11 @@ class UserIDPacket extends Packet function __construct($name='',$comment='',$email='') { parent::__construct(); - + if (! $comment && ! $email) { $this->input = $name; $this->read(); - + } else { $this->name = $name; $this->comment = $comment; @@ -28,7 +28,7 @@ class UserIDPacket extends Packet } } - function __toString() + public function display() { $text = []; @@ -41,7 +41,7 @@ class UserIDPacket extends Packet function body() { - return ''.$this; // Convert to string is the body + return ''.$this->display(); // Convert to string is the body } function read() @@ -53,21 +53,21 @@ class UserIDPacket extends Packet $this->comment = trim($matches[2]); $this->email = trim($matches[3]); } - + // User IDs of the form: "name " else if (preg_match('/^([^<]+)\s+<([^>]+)>$/', $this->data, $matches)) { $this->name = trim($matches[1]); $this->comment = NULL; $this->email = trim($matches[2]); } - + // User IDs of the form: "name" else if (preg_match('/^([^<]+)$/', $this->data, $matches)) { $this->name = trim($matches[1]); $this->comment = NULL; $this->email = NULL; } - + // User IDs of the form: "" else if (preg_match('/^<([^>]+)>$/', $this->data, $matches)) { $this->name = NULL; diff --git a/tests/phpseclib_suite.php b/tests/phpseclib_suite.php index ca7bb6c..3fa8b4a 100644 --- a/tests/phpseclib_suite.php +++ b/tests/phpseclib_suite.php @@ -3,12 +3,11 @@ use Leenooks\OpenPGP; class MessageVerification extends PHPUnit\Framework\TestCase { - public function oneMessageRSA($pkey, $path) { - $pkeyM = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $pkey)); - $m = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path)); - $verify = new OpenPGP\Crypt\RSA($pkeyM); - $this->assertSame($verify->verify($m), $m->signatures()); - } + public function oneMessageRSA($pkey, $path) { + $pkeyM = OpenPGP::load(file_get_contents(dirname(__FILE__).'/data/'.$pkey)); + $m = OpenPGP::load(file_get_contents(dirname(__FILE__).'/data/'.$path)); + $this->assertSame($pkeyM->verify($m->key()), $m->signatures()); + } public function testUncompressedOpsRSA() { $this->oneMessageRSA('pubring.gpg', 'uncompressed-ops-rsa.gpg'); @@ -26,14 +25,14 @@ class MessageVerification extends PHPUnit\Framework\TestCase { $this->oneMessageRSA('pubring.gpg', 'compressedsig-bzip2.gpg'); } - public function testSigningMessages() { - $wkey = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg')); - $data = new OpenPGP\LiteralDataPacket('This is text.', array('format' => 'u', 'filename' => 'stuff.txt')); - $sign = new OpenPGP\Crypt\RSA($wkey); - $m = $sign->sign($data)->to_bytes(); - $reparsedM = OpenPGP\Message::parse($m); - $this->assertSame($sign->verify($reparsedM), $reparsedM->signatures()); - } + public function testSigningMessages() { + $wkey = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg')); + $data = new OpenPGP\LiteralDataPacket('This is text.', array('format' => 'u', 'filename' => 'stuff.txt')); + $sign = new OpenPGP\Crypt\RSA($wkey); + $m = (string)$sign->sign($data); + $reparsedM = OpenPGP\Message::parse($m); + $this->assertSame($sign->verify($reparsedM), $reparsedM->signatures()); + } /* public function testUncompressedOpsDSA() { @@ -158,7 +157,7 @@ class Encryption extends PHPUnit\Framework\TestCase { public function oneSymmetric($algorithm) { $data = new OpenPGP\LiteralDataPacket('This is text.', array('format' => 'u', 'filename' => 'stuff.txt')); $encrypted = OpenPGP\Crypt\Symmetric::encrypt('secret', new OpenPGP\Message(array($data)), $algorithm); - $encrypted = OpenPGP\Message::parse($encrypted->to_bytes()); + $encrypted = OpenPGP\Message::parse((string)$encrypted); $decrypted = OpenPGP\Crypt\Symmetric::decryptSymmetric('secret', $encrypted); $this->assertEquals($decrypted[0]->data, 'This is text.'); } @@ -197,7 +196,7 @@ class Encryption extends PHPUnit\Framework\TestCase { $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\Symmetric::encrypt($key, new OpenPGP\Message(array($data))); - $encrypted = OpenPGP\Message::parse($encrypted->to_bytes()); + $encrypted = OpenPGP\Message::parse((string)$encrypted); $decryptor = new OpenPGP\Crypt\RSA($key); $decrypted = $decryptor->decrypt($encrypted); $this->assertEquals($decrypted[0]->data, 'This is text.'); diff --git a/tests/suite.php b/tests/suite.php index 3dda496..d93338d 100644 --- a/tests/suite.php +++ b/tests/suite.php @@ -3,12 +3,12 @@ use Leenooks\OpenPGP; class Serialization extends PHPUnit\Framework\TestCase { - public function oneSerialization($path) { - $in = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path)); - $mid = $in->to_bytes(); - $out = OpenPGP\Message::parse($mid); - $this->assertEquals($in, $out); - } + public function oneSerialization($path) { + $in = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path)); + $mid = (string)$in; + $out = OpenPGP\Message::parse($mid); + $this->assertEquals($in, $out); + } public function test000001006public_key() { $this->oneSerialization("000001-006.public_key");