Updates to pass unit testing

This commit is contained in:
Deon George 2020-06-18 22:03:56 +10:00
parent 7d259b251f
commit 358f28273c
18 changed files with 248 additions and 176 deletions

View File

@ -21,7 +21,8 @@
"phpseclib/phpseclib": "^2.0 !=2.0.8" "phpseclib/phpseclib": "^2.0 !=2.0.8"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^5.0" "symfony/var-dumper": "^5.0",
"phpunit/phpunit": "^8.0"
}, },
"suggest": { "suggest": {
"ext-mcrypt": "required if you use encryption cast5" "ext-mcrypt": "required if you use encryption cast5"

View File

@ -13,6 +13,9 @@
namespace Leenooks; namespace Leenooks;
use Illuminate\Support\Arr;
use phpseclib\Crypt\RSA as Crypt_RSA;
use Leenooks\OpenPGP\Exceptions\PacketTagException; use Leenooks\OpenPGP\Exceptions\PacketTagException;
/** /**
@ -21,6 +24,78 @@ use Leenooks\OpenPGP\Exceptions\PacketTagException;
class OpenPGP class OpenPGP
{ {
const VERSION = [0,5,0]; const VERSION = [0,5,0];
private $key = NULL;
/**
* @see http://tools.ietf.org/html/rfc4880#section-12.2
*/
static function bitlength($data)
{
return (strlen($data) - 1) * 8 + (int)floor(log(ord($data[0]), 2)) + 1;
}
/**
* Create a PGP Key
*
* @todo Incomplete and untested.
*
* @param string $name
* @param string $email
* @param string $comment
* @param int $keysize
* @return OpenPGP
*/
static function create(string $name,string $email,string $comment,int $keysize=512): self
{
$result = new self;
$rsa = new Crypt_RSA;
$rsa->loadKey(Arr::get($rsa->createKey($keysize),'privatekey'));
$nkey = new OpenPGP\SecretKeyPacket(array(
'n' => $rsa->modulus->toBytes(),
'e' => $rsa->publicExponent->toBytes(),
'd' => $rsa->exponent->toBytes(),
'p' => $rsa->primes[2]->toBytes(),
'q' => $rsa->primes[1]->toBytes(),
'u' => $rsa->coefficients[2]->toBytes()
));
$wkey = new OpenPGP\Crypt\RSA($nkey);
$uid = new OpenPGP\UserIDPacket($name,$comment,$email);
$result->key = $wkey->sign_key_userid([$nkey,$uid]);
return $result;
}
/**
* @see http://tools.ietf.org/html/rfc4880#section-6
* @see http://tools.ietf.org/html/rfc4880#section-6.1
*/
static function crc24($data): int
{
$crc = 0x00b704ce;
for ($i = 0; $i < strlen($data); $i++) {
$crc ^= (ord($data[$i]) & 255) << 16;
for ($j = 0; $j < 8; $j++) {
$crc <<= 1;
if ($crc & 0x01000000) {
$crc ^= 0x01864cfb;
}
}
}
return $crc & 0x00ffffff;
}
static function decode_s2k_count($c)
{
return ((int)16 + ($c & 15)) << (($c >> 4) + 6);
}
/** /**
* @see http://tools.ietf.org/html/rfc4880#section-6 * @see http://tools.ietf.org/html/rfc4880#section-6
@ -42,75 +117,6 @@ class OpenPGP
return $text; return $text;
} }
/**
* @see http://tools.ietf.org/html/rfc4880#section-6
* @see http://tools.ietf.org/html/rfc2045
*/
static function unarmor($text,$header='PGP PUBLIC KEY BLOCK')
{
$header = self::header($header);
$text = str_replace(["\r\n","\r"],["\n",''],$text);
if (($pos1=strpos($text,$header)) !== FALSE
&& ($pos1=strpos($text,"\n\n",$pos1+=strlen($header))) !== FALSE
&& ($pos2=strpos($text,"\n=",$pos1+=2)) !== FALSE)
{
return base64_decode($text=substr($text,$pos1,$pos2-$pos1));
}
}
/**
* @see http://tools.ietf.org/html/rfc4880#section-6.2
*/
static protected function header($marker): string
{
return '-----BEGIN '.strtoupper((string)$marker).'-----';
}
/**
* @see http://tools.ietf.org/html/rfc4880#section-6.2
*/
static protected function footer($marker): string
{
return'-----END '.strtoupper((string)$marker).'-----';
}
/**
* @see http://tools.ietf.org/html/rfc4880#section-6
* @see http://tools.ietf.org/html/rfc4880#section-6.1
*/
static function crc24($data): int
{
$crc = 0x00b704ce;
for ($i = 0; $i < strlen($data); $i++) {
$crc ^= (ord($data[$i]) & 255) << 16;
for ($j = 0; $j < 8; $j++) {
$crc <<= 1;
if ($crc & 0x01000000) {
$crc ^= 0x01864cfb;
}
}
}
return $crc & 0x00ffffff;
}
/**
* @see http://tools.ietf.org/html/rfc4880#section-12.2
*/
static function bitlength($data)
{
return (strlen($data) - 1) * 8 + (int)floor(log(ord($data[0]), 2)) + 1;
}
static function decode_s2k_count($c)
{
return ((int)16 + ($c & 15)) << (($c >> 4) + 6);
}
static function encode_s2k_count($iterations) static function encode_s2k_count($iterations)
{ {
if($iterations >= 65011712) return 255; if($iterations >= 65011712) return 255;
@ -130,4 +136,38 @@ class OpenPGP
return $result; return $result;
} }
/**
* @see http://tools.ietf.org/html/rfc4880#section-6.2
*/
static protected function footer($marker): string
{
return'-----END '.strtoupper((string)$marker).'-----';
}
/**
* @see http://tools.ietf.org/html/rfc4880#section-6.2
*/
static protected function header($marker): string
{
return '-----BEGIN '.strtoupper((string)$marker).'-----';
}
/**
* @see http://tools.ietf.org/html/rfc4880#section-6
* @see http://tools.ietf.org/html/rfc2045
*/
static function unarmor($text,$header='PGP PUBLIC KEY BLOCK')
{
$header = self::header($header);
$text = str_replace(["\r\n","\r"],["\n",''],$text);
if (($pos1=strpos($text,$header)) !== FALSE
&& ($pos1=strpos($text,"\n\n",$pos1+=strlen($header))) !== FALSE
&& ($pos2=strpos($text,"\n=",$pos1+=2)) !== FALSE)
{
return base64_decode($text=substr($text,$pos1,$pos2-$pos1));
}
}
} }

View File

@ -31,7 +31,7 @@ class AsymmetricSessionKeyPacket extends Packet
// Store KeyID in Hex // Store KeyID in Hex
for ($i=0;$i<strlen($rawkeyid);$i++) { for ($i=0;$i<strlen($rawkeyid);$i++) {
$this->keyid .= sprintf('%02X',ord($rawkeyid{$i})); $this->keyid .= sprintf('%02X',ord($rawkeyid[$i]));
} }
$this->key_algorithm = ord($this->read_byte()); $this->key_algorithm = ord($this->read_byte());
@ -49,7 +49,7 @@ class AsymmetricSessionKeyPacket extends Packet
$bytes = chr($this->version); $bytes = chr($this->version);
for ($i=0;$i<strlen($this->keyid);$i+= 2) { for ($i=0;$i<strlen($this->keyid);$i+= 2) {
$bytes .= chr(hexdec($this->keyid{$i}.$this->keyid{$i+1})); $bytes .= chr(hexdec($this->keyid[$i].$this->keyid[$i+1]));
} }
$bytes .= chr($this->key_algorithm); $bytes .= chr($this->key_algorithm);

View File

@ -80,19 +80,19 @@ class CompressedDataPacket extends Packet implements \IteratorAggregate, \ArrayA
switch($this->algorithm) { switch($this->algorithm) {
case 0: case 0:
$this->data = OpenPGP\Message::parse($this->data); $this->data = Message::parse($this->data);
break; break;
case 1: case 1:
$this->data = OpenPGP\Message::parse(gzinflate($this->data)); $this->data = Message::parse(gzinflate($this->data));
break; break;
case 2: case 2:
$this->data = OpenPGP\Message::parse(gzuncompress($this->data)); $this->data = Message::parse(gzuncompress($this->data));
break; break;
case 3: case 3:
$this->data = OpenPGP\Message::parse(bzdecompress($this->data)); $this->data = Message::parse(bzdecompress($this->data));
break; break;
default: default:

View File

@ -24,12 +24,13 @@ class RSA
// Construct a wrapper object from a key or a message packet // Construct a wrapper object from a key or a message packet
function __construct($packet) function __construct($packet)
{ {
if (!is_object($packet)) if (! is_object($packet))
$packet = OpenPGP\Message::parse($packet); $packet = OpenPGP\Message::parse($packet);
// If it's a key (other keys are subclasses of this one) // If it's a key (other keys are subclasses of this one)
if ($packet instanceof OpenPGP\PublicKeyPacket || $packet[0] instanceof OpenPGP\PublicKeyPacket) { if ($packet instanceof OpenPGP\PublicKeyPacket || $packet[0] instanceof OpenPGP\PublicKeyPacket) {
$this->key = $packet; $this->key = $packet;
} else { } else {
$this->message = $packet; $this->message = $packet;
} }
@ -155,7 +156,7 @@ class RSA
$key->setHash(strtolower($hash)); $key->setHash(strtolower($hash));
$sig = new SignaturePacket($message,'RSA',strtoupper($hash)); $sig = new OpenPGP\SignaturePacket($message,'RSA',strtoupper($hash));
$sig->hashed_subpackets[] = new OpenPGP\SignaturePacket\IssuerPacket($keyid); $sig->hashed_subpackets[] = new OpenPGP\SignaturePacket\IssuerPacket($keyid);
$sig->sign_data(['RSA'=>[$hash => function($data) use($key) {return [$key->sign($data)];}]]); $sig->sign_data(['RSA'=>[$hash => function($data) use($key) {return [$key->sign($data)];}]]);
@ -180,6 +181,7 @@ class RSA
if (! $keyid) if (! $keyid)
$keyid = substr($this->key->fingerprint,-16); $keyid = substr($this->key->fingerprint,-16);
$key->setHash(strtolower($hash)); $key->setHash(strtolower($hash));
$sig = NULL; $sig = NULL;
@ -282,13 +284,13 @@ class RSA
$sk_chk = 0; $sk_chk = 0;
for ($i=0;$i<strlen($sk);$i++) { for ($i=0;$i<strlen($sk);$i++) {
$sk_chk = ($sk_chk+ord($sk{$i}))%65536; $sk_chk = ($sk_chk+ord($sk[$i]))%65536;
} }
if ($sk_chk != $chk) if ($sk_chk != $chk)
return NULL; return NULL;
return array(ord($data{0}),$sk); return array(ord($data[0]),$sk);
} }
static function crypt_rsa_key($mod,$exp,$hash='SHA256') static function crypt_rsa_key($mod,$exp,$hash='SHA256')

View File

@ -103,7 +103,7 @@ class Symmetric
$cipher->setKey($p->s2k->make_key($pass, $key_bytes)); $cipher->setKey($p->s2k->make_key($pass, $key_bytes));
$padAmount = $key_block_bytes - (strlen($p->encrypted_data) % $key_block_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)); $data = substr($cipher->decrypt($p->encrypted_data . str_repeat("\0", $padAmount)), 0, strlen($p->encrypted_data));
$decrypted = self::decryptPacket($epacket, ord($data{0}), substr($data, 1)); $decrypted = self::decryptPacket($epacket, ord($data[0]), substr($data, 1));
} else { } else {
list($cipher,$key_bytes,$key_block_bytes) = self::getCipher($p->symmetric_algorithm); list($cipher,$key_bytes,$key_block_bytes) = self::getCipher($p->symmetric_algorithm);
@ -208,7 +208,6 @@ class Symmetric
try { try {
$msg = OpenPGP\Message::parse($data); $msg = OpenPGP\Message::parse($data);
dump(['data'=>$data,'msg'=>$msg]);
} catch (Exception $ex) { } catch (Exception $ex) {
$msg = NULL; $msg = NULL;
@ -246,7 +245,7 @@ class Symmetric
switch($algo) { switch($algo) {
case NULL: case NULL:
case 0: case 0:
throw new Exception("Data is already unencrypted"); throw new \Exception("Data is already unencrypted");
case 2: case 2:
$cipher = new Crypt_TripleDES(Crypt_TripleDES::MODE_CFB); $cipher = new Crypt_TripleDES(Crypt_TripleDES::MODE_CFB);
@ -256,9 +255,9 @@ class Symmetric
case 3: case 3:
if (class_exists('OpenSSLWrapper')) { if (class_exists('OpenSSLWrapper')) {
$cipher = new OpenSSLWrapper("CAST5-CFB"); $cipher = new \OpenSSLWrapper("CAST5-CFB");
} else if(defined('MCRYPT_CAST_128')) { } else if(defined('MCRYPT_CAST_128')) {
$cipher = new MCryptWrapper(MCRYPT_CAST_128); $cipher = new \MCryptWrapper(MCRYPT_CAST_128);
} }
break; break;
@ -320,7 +319,7 @@ class Symmetric
$mkChk = 0; $mkChk = 0;
for($i = 0; $i < strlen($s); $i++) { for($i = 0; $i < strlen($s); $i++) {
$mkChk = ($mkChk + ord($s{$i})) % 65536; $mkChk = ($mkChk + ord($s[$i])) % 65536;
} }
return $mkChk; return $mkChk;

View File

@ -16,7 +16,7 @@ class OnePassSignaturePacket extends Packet
{ {
$body = chr($this->version).chr($this->signature_type).chr($this->hash_algorithm).chr($this->key_algorithm); $body = chr($this->version).chr($this->signature_type).chr($this->hash_algorithm).chr($this->key_algorithm);
for($i = 0; $i < strlen($this->key_id); $i += 2) { for($i = 0; $i < strlen($this->key_id); $i += 2) {
$body .= chr(hexdec($this->key_id{$i}.$this->key_id{$i+1})); $body .= chr(hexdec($this->key_id[$i].$this->key_id[$i+1]));
} }
$body .= chr((int)$this->nested); $body .= chr((int)$this->nested);
return $body; return $body;

View File

@ -182,9 +182,12 @@ abstract class Packet
{ {
// Make sure our tag is set in our packet class. // Make sure our tag is set in our packet class.
try { try {
if (is_null($this->tag)) if (is_null($this->tag) AND get_class($this) != 'Leenooks\OpenPGP\SignaturePacket\Subpacket')
throw new PacketTagException('Missing tag in '.get_class($this)); throw new PacketTagException('Missing tag in '.get_class($this));
} catch (\Exception $e) { } catch (\Exception $e) {
debug_print_backtrace(0,5);
dump($this);
dd($e->getMessage()); dd($e->getMessage());
} }
@ -197,7 +200,7 @@ abstract class Packet
'substr2'=>substr(substr(get_class($this),strlen("Leenooks\OpenPGP")+1),0,-6), 'substr2'=>substr(substr(get_class($this),strlen("Leenooks\OpenPGP")+1),0,-6),
'tags: '=>serialize(self::$tags)]); 'tags: '=>serialize(self::$tags)]);
$this->tag = array_search(substr(substr(get_class($this),strlen("Leenooks\OpenPGP")+1),0,-6),self::$tags); //$this->tag = array_search(substr(substr(get_class($this),strlen("Leenooks\OpenPGP")+1),0,-6),self::$tags);
$this->data = $data; $this->data = $data;
} }
@ -220,6 +223,11 @@ abstract class Packet
return ['header'=>$tag.$size,'body'=>$body]; return ['header'=>$tag.$size,'body'=>$body];
} }
public function tag(): int
{
return $this->tag;
}
function to_bytes() function to_bytes()
{ {
$data = $this->header_and_body(); $data = $this->header_and_body();

View File

@ -3,7 +3,6 @@
namespace Leenooks\OpenPGP; namespace Leenooks\OpenPGP;
use Leenooks\OpenPGP; use Leenooks\OpenPGP;
use Leenooks\OpenPGP_SignaturePacket;
class S2K class S2K
{ {
@ -21,22 +20,22 @@ class S2K
{ {
$s2k = new self; $s2k = new self;
switch($s2k->type = ord($input{0})) { switch($s2k->type = ord($input[0])) {
case 0: case 0:
$s2k->hash_algorithm = ord($input{1}); $s2k->hash_algorithm = ord($input[1]);
$input = substr($input,2); $input = substr($input,2);
break; break;
case 1: case 1:
$s2k->hash_algorithm = ord($input{1}); $s2k->hash_algorithm = ord($input[1]);
$s2k->salt = substr($input,2,8); $s2k->salt = substr($input,2,8);
$input = substr($input,10); $input = substr($input,10);
break; break;
case 3: case 3:
$s2k->hash_algorithm = ord($input{1}); $s2k->hash_algorithm = ord($input[1]);
$s2k->salt = substr($input,2,8); $s2k->salt = substr($input,2,8);
$s2k->count = OpenPGP::decode_s2k_count(ord($input{10})); $s2k->count = OpenPGP::decode_s2k_count(ord($input[10]));
$input = substr($input,11); $input = substr($input,11);
break; break;
} }
@ -47,42 +46,42 @@ class S2K
function to_bytes() function to_bytes()
{ {
$bytes = chr($this->type); $bytes = chr($this->type);
switch($this->type) { switch($this->type) {
case 0: case 0:
$bytes .= chr($this->hash_algorithm); $bytes .= chr($this->hash_algorithm);
break; break;
case 1: case 1:
if (strlen($this->salt) != 8) if (strlen($this->salt) != 8)
throw new Exception('Invalid salt length'); throw new Exception('Invalid salt length');
$bytes .= chr($this->hash_algorithm); $bytes .= chr($this->hash_algorithm);
$bytes .= $this->salt; $bytes .= $this->salt;
break; break;
case 3: case 3:
if (strlen($this->salt) != 8) if (strlen($this->salt) != 8)
throw new Exception('Invalid salt length'); throw new Exception('Invalid salt length');
$bytes .= chr($this->hash_algorithm); $bytes .= chr($this->hash_algorithm);
$bytes .= $this->salt; $bytes .= $this->salt;
$bytes .= chr(OpenPGP::encode_s2k_count($this->count)); $bytes .= chr(OpenPGP::encode_s2k_count($this->count));
break; break;
} }
return $bytes; return $bytes;
} }
function raw_hash($s) function raw_hash($s)
{ {
return hash(strtolower(OpenPGP_SignaturePacket::$hash_algorithms[$this->hash_algorithm]),$s,true); return hash(strtolower(SignaturePacket::$hash_algorithms[$this->hash_algorithm]),$s,true);
} }
function sized_hash($s,$size) function sized_hash($s,$size)
{ {
$hash = $this->raw_hash($s); $hash = $this->raw_hash($s);
while(strlen($hash) < $size) { while(strlen($hash) < $size) {
$s = "\0".$s; $s = "\0".$s;
$hash .= $this->raw_hash($s); $hash .= $this->raw_hash($s);
@ -95,9 +94,9 @@ class S2K
{ {
if (strlen($s) >= $this->count) if (strlen($s) >= $this->count)
return $s; return $s;
$s = str_repeat($s,ceil($this->count/strlen($s))); $s = str_repeat($s,ceil($this->count/strlen($s)));
return substr($s,0,$this->count); return substr($s,0,$this->count);
} }
@ -106,10 +105,10 @@ class S2K
switch($this->type) { switch($this->type) {
case 0: case 0:
return $this->sized_hash($pass, $size); return $this->sized_hash($pass, $size);
case 1: case 1:
return $this->sized_hash($this->salt . $pass, $size); return $this->sized_hash($this->salt . $pass, $size);
case 3: case 3:
return $this->sized_hash($this->iterate($this->salt . $pass), $size); return $this->sized_hash($this->iterate($this->salt . $pass), $size);
} }

View File

@ -62,11 +62,11 @@ class SecretKeyPacket extends PublicKeyPacket
function read() function read()
{ {
parent::read(); // All the fields from PublicKey parent::read(); // All the fields from PublicKey
$this->s2k_useage = ord($this->read_byte()); $this->s2k_useage = ord($this->read_byte());
if($this->s2k_useage == 255 || $this->s2k_useage == 254) { if($this->s2k_useage == 255 || $this->s2k_useage == 254) {
$this->symmetric_algorithm = ord($this->read_byte()); $this->symmetric_algorithm = ord($this->read_byte());
$this->s2k = OpenPGP\S2K::parse($this->input); $this->s2k = S2K::parse($this->input);
} else if($this->s2k_useage > 0) { } else if($this->s2k_useage > 0) {
$this->symmetric_algorithm = $this->s2k_useage; $this->symmetric_algorithm = $this->s2k_useage;
} }

View File

@ -12,7 +12,6 @@ use Leenooks\OpenPGP;
*/ */
class SignaturePacket extends Packet class SignaturePacket extends Packet
{ {
protected static $DEBUG = FALSE;
protected $tag = 2; protected $tag = 2;
public $version, $signature_type, $hash_algorithm, $key_algorithm, $hashed_subpackets, $unhashed_subpackets, $hash_head; public $version, $signature_type, $hash_algorithm, $key_algorithm, $hashed_subpackets, $unhashed_subpackets, $hash_head;
@ -119,7 +118,7 @@ class SignaturePacket extends Packet
foreach ((array)$this->unhashed_subpackets as $p) { foreach ((array)$this->unhashed_subpackets as $p) {
if ($p instanceof SignaturePacket\IssuerPacket) { if ($p instanceof SignaturePacket\IssuerPacket) {
for($i = 0; $i < strlen($p->data); $i += 2) { for($i = 0; $i < strlen($p->data); $i += 2) {
$body .= chr(hexdec($p->data{$i}.$p->data{$i+1})); $body .= chr(hexdec($p->data[$i].$p->data[$i+1]));
} }
break; break;
@ -189,9 +188,15 @@ class SignaturePacket extends Packet
static function get_subpacket(&$input) static function get_subpacket(&$input)
{ {
if (self::$DEBUG)
dump(['method'=>__METHOD__,'input'=>$input]);
$len = ord($input[0]); $len = ord($input[0]);
$length_of_length = 1; $length_of_length = 1;
if (self::$DEBUG)
dump(['len'=>$len]);
// if($len < 192) One octet length, no furthur processing // if($len < 192) One octet length, no furthur processing
if ($len > 190 && $len < 255) { // Two octet length if ($len > 190 && $len < 255) { // Two octet length
$length_of_length = 2; $length_of_length = 2;
@ -203,17 +208,27 @@ class SignaturePacket extends Packet
$length_of_length = 5; $length_of_length = 5;
$unpacked = unpack('N', substr($input, 1, 4)); $unpacked = unpack('N', substr($input, 1, 4));
$len = reset($unpacked); $len = reset($unpacked);
if (self::$DEBUG)
dump(['len'=>$len,'unpacked'=>$unpacked]);
} }
$input = substr($input, $length_of_length); // Chop off length header $input = substr($input,$length_of_length); // Chop off length header
$tag = ord($input[0]); $tag = ord($input[0]);
$class = self::class_for($tag); $class = self::class_for($tag);
if (self::$DEBUG) if (self::$DEBUG)
dump(['class'=>$class,'tag'=>$tag]); dump(['class'=>$class,'tag'=>$tag]);
if ($class) { if ($class) {
$packet = new $class; $packet = new $class;
// In case we got the catch all class.
if ($class == 'Leenooks\OpenPGP\SignaturePacket\Subpacket')
$packet->setTag($tag);
if ($packet->tag() !== $tag)
throw new OpenPGP\Exceptions\PacketTagException(sprintf('Packet tag [%s] doesnt match tag [%s]?',$packet->tag(),$tag));
//$packet->tag = $tag; // @todo Tag should already be set. //$packet->tag = $tag; // @todo Tag should already be set.
$packet->input = substr($input, 1, $len-1); $packet->input = substr($input, 1, $len-1);
$packet->length = $len-1; $packet->length = $len-1;
@ -288,7 +303,7 @@ class SignaturePacket extends Packet
// Store KeyID in Hex // Store KeyID in Hex
for ($i=0;$i<strlen($keyid);$i++) { for ($i=0;$i<strlen($keyid);$i++) {
$keyidHex .= sprintf('%02X',ord($keyid{$i})); $keyidHex .= sprintf('%02X',ord($keyid[$i]));
} }
$this->hashed_subpackets = []; $this->hashed_subpackets = [];

View File

@ -13,7 +13,7 @@ class IssuerPacket extends Subpacket
{ {
$bytes = ''; $bytes = '';
for($i = 0; $i < strlen($this->data); $i += 2) { for($i = 0; $i < strlen($this->data); $i += 2) {
$bytes .= chr(hexdec($this->data{$i}.$this->data{$i+1})); $bytes .= chr(hexdec($this->data[$i].$this->data[$i+1]));
} }
return $bytes; return $bytes;
} }

View File

@ -17,7 +17,7 @@ class RevocationKeyPacket extends Subpacket
$bytes .= chr($this->key_algorithm); $bytes .= chr($this->key_algorithm);
for($i = 0; $i < strlen($this->fingerprint); $i += 2) { for($i = 0; $i < strlen($this->fingerprint); $i += 2) {
$bytes .= chr(hexdec($this->fingerprint{$i}.$this->fingerprint{$i+1})); $bytes .= chr(hexdec($this->fingerprint[$i].$this->fingerprint[$i+1]));
} }
return $bytes; return $bytes;

View File

@ -2,6 +2,7 @@
namespace Leenooks\OpenPGP\SignaturePacket; namespace Leenooks\OpenPGP\SignaturePacket;
use Leenooks\OpenPGP\Exceptions\PacketTagException;
use Leenooks\OpenPGP\Packet; use Leenooks\OpenPGP\Packet;
class Subpacket extends Packet class Subpacket extends Packet
@ -21,10 +22,18 @@ class Subpacket extends Packet
return ['header'=>$size.$tag,'body'=>$body]; return ['header'=>$size.$tag,'body'=>$body];
} }
/* Defaults for unsupported packets */ /* Defaults for unsupported packets */
function read() function read()
{ {
$this->data = $this->input; $this->data = $this->input;
} }
public function setTag(int $tag): void
{
if (get_class($this) !== Subpacket::class)
throw new PacketTagException('Attempting to set a tag for invalid class: ',get_class($this));
$this->tag = $tag;
}
} }

View File

@ -31,7 +31,7 @@ class SymmetricSessionKeyPacket extends Packet
{ {
$this->version = ord($this->read_byte()); $this->version = ord($this->read_byte());
$this->symmetric_algorithm = ord($this->read_byte()); $this->symmetric_algorithm = ord($this->read_byte());
$this->s2k = OpenPGP\S2k::parse($this->input); $this->s2k = S2k::parse($this->input);
$this->encrypted_data = $this->input; $this->encrypted_data = $this->input;
} }
} }

3
test.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
vendor/bin/phpunit --stop-on-defect --stop-on-error

View File

@ -1,16 +1,12 @@
<?php <?php
/* The tests which require phpseclib */ use Leenooks\OpenPGP;
require_once dirname(__FILE__).'/../lib/openpgp.php'; class MessageVerification extends PHPUnit\Framework\TestCase {
require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_symmetric.php';
class MessageVerification extends PHPUnit_Framework_TestCase {
public function oneMessageRSA($pkey, $path) { public function oneMessageRSA($pkey, $path) {
$pkeyM = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $pkey)); $pkeyM = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $pkey));
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path)); $m = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
$verify = new OpenPGP_Crypt_RSA($pkeyM); $verify = new OpenPGP\Crypt\RSA($pkeyM);
$this->assertSame($verify->verify($m), $m->signatures()); $this->assertSame($verify->verify($m), $m->signatures());
} }
@ -31,11 +27,11 @@ class MessageVerification extends PHPUnit_Framework_TestCase {
} }
public function testSigningMessages() { public function testSigningMessages() {
$wkey = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg')); $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')); $data = new OpenPGP\LiteralDataPacket('This is text.', array('format' => 'u', 'filename' => 'stuff.txt'));
$sign = new OpenPGP_Crypt_RSA($wkey); $sign = new OpenPGP\Crypt\RSA($wkey);
$m = $sign->sign($data)->to_bytes(); $m = $sign->sign($data)->to_bytes();
$reparsedM = OpenPGP_Message::parse($m); $reparsedM = OpenPGP\Message::parse($m);
$this->assertSame($sign->verify($reparsedM), $reparsedM->signatures()); $this->assertSame($sign->verify($reparsedM), $reparsedM->signatures());
} }
@ -51,10 +47,10 @@ class MessageVerification extends PHPUnit_Framework_TestCase {
} }
class KeyVerification extends PHPUnit_Framework_TestCase { class KeyVerification extends PHPUnit\Framework\TestCase {
public function oneKeyRSA($path) { public function oneKeyRSA($path) {
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path)); $m = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
$verify = new OpenPGP_Crypt_RSA($m); $verify = new OpenPGP\Crypt\RSA($m);
$this->assertSame($verify->verify($m), $m->signatures()); $this->assertSame($verify->verify($m), $m->signatures());
} }
@ -64,13 +60,13 @@ class KeyVerification extends PHPUnit_Framework_TestCase {
} }
class Decryption extends PHPUnit_Framework_TestCase { class Decryption extends PHPUnit\Framework\TestCase {
public function oneSymmetric($pass, $cnt, $path) { public function oneSymmetric($pass, $cnt, $path) {
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path)); $m = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
$m2 = OpenPGP_Crypt_Symmetric::decryptSymmetric($pass, $m); $m2 = OpenPGP\Crypt\Symmetric::decryptSymmetric($pass, $m);
while($m2[0] instanceof OpenPGP_CompressedDataPacket) $m2 = $m2[0]->data; while($m2[0] instanceof OpenPGP\CompressedDataPacket) $m2 = $m2[0]->data;
foreach($m2 as $p) { foreach($m2 as $p) {
if($p instanceof OpenPGP_LiteralDataPacket) { if($p instanceof OpenPGP\LiteralDataPacket) {
$this->assertEquals($p->data, $cnt); $this->assertEquals($p->data, $cnt);
} }
} }
@ -93,7 +89,7 @@ class Decryption extends PHPUnit_Framework_TestCase {
} }
public function testDecryptTwofish() { public function testDecryptTwofish() {
if(OpenPGP_Crypt_Symmetric::getCipher(10)[0]) { if(OpenPGP\Crypt\Symmetric::getCipher(10)[0]) {
$this->oneSymmetric("hello", "PGP\n", "symmetric-twofish.gpg"); $this->oneSymmetric("hello", "PGP\n", "symmetric-twofish.gpg");
} }
} }
@ -107,30 +103,30 @@ class Decryption extends PHPUnit_Framework_TestCase {
} }
public function testDecryptAsymmetric() { public function testDecryptAsymmetric() {
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/hello.gpg')); $m = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/hello.gpg'));
$key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg')); $key = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg'));
$decryptor = new OpenPGP_Crypt_RSA($key); $decryptor = new OpenPGP\Crypt\RSA($key);
$m2 = $decryptor->decrypt($m); $m2 = $decryptor->decrypt($m);
while($m2[0] instanceof OpenPGP_CompressedDataPacket) $m2 = $m2[0]->data; while($m2[0] instanceof OpenPGP\CompressedDataPacket) $m2 = $m2[0]->data;
foreach($m2 as $p) { foreach($m2 as $p) {
if($p instanceof OpenPGP_LiteralDataPacket) { if($p instanceof OpenPGP\LiteralDataPacket) {
$this->assertEquals($p->data, "hello\n"); $this->assertEquals($p->data, "hello\n");
} }
} }
} }
public function testDecryptRoundtrip() { public function testDecryptRoundtrip() {
$m = new OpenPGP_Message(array(new OpenPGP_LiteralDataPacket("hello\n"))); $m = new OpenPGP\Message(array(new OpenPGP\LiteralDataPacket("hello\n")));
$key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg')); $key = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg'));
$em = OpenPGP_Crypt_Symmetric::encrypt($key, $m); $em = OpenPGP\Crypt\Symmetric::encrypt($key, $m);
foreach($key as $packet) { foreach($key as $packet) {
if(!($packet instanceof OpenPGP_SecretKeyPacket)) continue; if(!($packet instanceof OpenPGP\SecretKeyPacket)) continue;
$decryptor = new OpenPGP_Crypt_RSA($packet); $decryptor = new OpenPGP\Crypt\RSA($packet);
$m2 = $decryptor->decrypt($em); $m2 = $decryptor->decrypt($em);
foreach($m2 as $p) { foreach($m2 as $p) {
if($p instanceof OpenPGP_LiteralDataPacket) { if($p instanceof OpenPGP\LiteralDataPacket) {
$this->assertEquals($p->data, "hello\n"); $this->assertEquals($p->data, "hello\n");
} }
} }
@ -138,32 +134,32 @@ class Decryption extends PHPUnit_Framework_TestCase {
} }
public function testDecryptSecretKey() { public function testDecryptSecretKey() {
$key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/encryptedSecretKey.gpg')); $key = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/encryptedSecretKey.gpg'));
$skey = OpenPGP_Crypt_Symmetric::decryptSecretKey("hello", $key[0]); $skey = OpenPGP\Crypt\Symmetric::decryptSecretKey("hello", $key[0]);
$this->assertSame(!!$skey, true); $this->assertSame(!!$skey, true);
} }
public function testEncryptSecretKeyRoundtrip() { public function testEncryptSecretKeyRoundtrip() {
$key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg')); $key = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg'));
$enkey = OpenPGP_Crypt_Symmetric::encryptSecretKey("password", $key[0]); $enkey = OpenPGP\Crypt\Symmetric::encryptSecretKey("password", $key[0]);
$skey = OpenPGP_Crypt_Symmetric::decryptSecretKey("password", $enkey); $skey = OpenPGP\Crypt\Symmetric::decryptSecretKey("password", $enkey);
$this->assertEquals($key[0], $skey); $this->assertEquals($key[0], $skey);
} }
public function testAlreadyDecryptedSecretKey() { public function testAlreadyDecryptedSecretKey() {
$this->expectException(Exception::class); $this->expectException(Exception::class);
$this->expectExceptionMessage("Data is already unencrypted"); $this->expectExceptionMessage("Data is already unencrypted");
$key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg')); $key = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg'));
OpenPGP_Crypt_Symmetric::decryptSecretKey("hello", $key[0]); OpenPGP\Crypt\Symmetric::decryptSecretKey("hello", $key[0]);
} }
} }
class Encryption extends PHPUnit_Framework_TestCase { class Encryption extends PHPUnit\Framework\TestCase {
public function oneSymmetric($algorithm) { public function oneSymmetric($algorithm) {
$data = new OpenPGP_LiteralDataPacket('This is text.', array('format' => 'u', 'filename' => 'stuff.txt')); $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\Crypt\Symmetric::encrypt('secret', new OpenPGP\Message(array($data)), $algorithm);
$encrypted = OpenPGP_Message::parse($encrypted->to_bytes()); $encrypted = OpenPGP\Message::parse($encrypted->to_bytes());
$decrypted = OpenPGP_Crypt_Symmetric::decryptSymmetric('secret', $encrypted); $decrypted = OpenPGP\Crypt\Symmetric::decryptSymmetric('secret', $encrypted);
$this->assertEquals($decrypted[0]->data, 'This is text.'); $this->assertEquals($decrypted[0]->data, 'This is text.');
} }
@ -192,17 +188,17 @@ class Encryption extends PHPUnit_Framework_TestCase {
} }
public function testEncryptSymmetricTwofish() { public function testEncryptSymmetricTwofish() {
if(OpenPGP_Crypt_Symmetric::getCipher(10)[0]) { if(OpenPGP\Crypt\Symmetric::getCipher(10)[0]) {
$this->oneSymmetric(10); $this->oneSymmetric(10);
} }
} }
public function testEncryptAsymmetric() { public function testEncryptAsymmetric() {
$key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg')); $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')); $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\Crypt\Symmetric::encrypt($key, new OpenPGP\Message(array($data)));
$encrypted = OpenPGP_Message::parse($encrypted->to_bytes()); $encrypted = OpenPGP\Message::parse($encrypted->to_bytes());
$decryptor = new OpenPGP_Crypt_RSA($key); $decryptor = new OpenPGP\Crypt\RSA($key);
$decrypted = $decryptor->decrypt($encrypted); $decrypted = $decryptor->decrypt($encrypted);
$this->assertEquals($decrypted[0]->data, 'This is text.'); $this->assertEquals($decrypted[0]->data, 'This is text.');
} }

View File

@ -1,12 +1,12 @@
<?php <?php
require_once dirname(__FILE__).'/../lib/openpgp.php'; use Leenooks\OpenPGP;
class Serialization extends PHPUnit_Framework_TestCase { class Serialization extends PHPUnit\Framework\TestCase {
public function oneSerialization($path) { public function oneSerialization($path) {
$in = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path)); $in = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
$mid = $in->to_bytes(); $mid = $in->to_bytes();
$out = OpenPGP_Message::parse($mid); $out = OpenPGP\Message::parse($mid);
$this->assertEquals($in, $out); $this->assertEquals($in, $out);
} }
@ -375,9 +375,9 @@ class Serialization extends PHPUnit_Framework_TestCase {
} }
} }
class Fingerprint extends PHPUnit_Framework_TestCase { class Fingerprint extends PHPUnit\Framework\TestCase {
public function oneFingerprint($path, $kf) { public function oneFingerprint($path, $kf) {
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path)); $m = OpenPGP\Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
$this->assertEquals($m[0]->fingerprint(), $kf); $this->assertEquals($m[0]->fingerprint(), $kf);
} }