openpgp-php/lib/OpenPGP.php

233 lines
5.1 KiB
PHP
Raw Normal View History

2020-06-07 06:25:59 +00:00
<?php
// This is free and unencumbered software released into the public domain.
/**
* OpenPGP.php is a pure-PHP implementation of the OpenPGP Message Format
* (RFC 4880).
*
* @package OpenPGP
* @author Arto Bendiken <arto.bendiken@gmail.com>
* @author Stephen Paul Weber <singpolyma@singpolyma.net>
* @author Deon George <deon@leenooks.net>
* @see http://github.com/bendiken/openpgp-php
*/
namespace Leenooks;
2020-06-18 12:03:56 +00:00
use Illuminate\Support\Arr;
2020-06-24 12:37:14 +00:00
use phpseclib\Crypt\RSA as Crypt_RSA;
2020-06-07 06:25:59 +00:00
use Leenooks\OpenPGP\Exceptions\PacketTagException;
/**
* @see http://tools.ietf.org/html/rfc4880
*/
class OpenPGP
{
const VERSION = [0,5,0];
2020-06-18 12:03:56 +00:00
private $key = NULL;
2020-06-07 06:25:59 +00:00
2020-06-24 12:37:14 +00:00
// Functions
2020-06-07 06:25:59 +00:00
/**
2020-06-18 12:03:56 +00:00
* @see http://tools.ietf.org/html/rfc4880#section-12.2
2020-06-07 06:25:59 +00:00
*/
2020-06-18 12:03:56 +00:00
static function bitlength($data)
2020-06-07 06:25:59 +00:00
{
2020-06-18 12:03:56 +00:00
return (strlen($data) - 1) * 8 + (int)floor(log(ord($data[0]), 2)) + 1;
2020-06-07 06:25:59 +00:00
}
/**
2020-06-18 12:03:56 +00:00
* 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
2020-06-07 06:25:59 +00:00
{
2020-06-18 12:03:56 +00:00
$result = new self;
2020-06-07 06:25:59 +00:00
2020-06-18 12:03:56 +00:00
$rsa = new Crypt_RSA;
$rsa->loadKey(Arr::get($rsa->createKey($keysize),'privatekey'));
2020-06-07 06:25:59 +00:00
2020-06-18 12:03:56 +00:00
$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()
));
2020-06-07 06:25:59 +00:00
2020-06-18 12:03:56 +00:00
$wkey = new OpenPGP\Crypt\RSA($nkey);
2020-06-07 06:25:59 +00:00
2020-06-18 12:03:56 +00:00
$uid = new OpenPGP\UserIDPacket($name,$comment,$email);
$result->key = $wkey->sign_key_userid([$nkey,$uid]);
return $result;
2020-06-07 06:25:59 +00:00
}
/**
2020-06-18 12:03:56 +00:00
* @see http://tools.ietf.org/html/rfc4880#section-6
* @see http://tools.ietf.org/html/rfc4880#section-6.1
*/
2020-06-07 06:25:59 +00:00
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;
}
2020-06-18 12:03:56 +00:00
static function decode_s2k_count($c)
2020-06-07 06:25:59 +00:00
{
2020-06-18 12:03:56 +00:00
return ((int)16 + ($c & 15)) << (($c >> 4) + 6);
2020-06-07 06:25:59 +00:00
}
2020-06-24 12:37:14 +00:00
public function decrypt($data)
{
$decryptor = new OpenPGP\Crypt\RSA($this->key);
return $decryptor->decrypt($data);
}
2020-06-18 12:03:56 +00:00
/**
* @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
*/
2020-06-24 12:37:14 +00:00
static function enarmor1($data,$marker='MESSAGE',array $headers=[])
2020-06-07 06:25:59 +00:00
{
2020-06-18 12:03:56 +00:00
$text = self::header($marker)."\n";
foreach ($headers as $key => $value) {
$text .= $key.': '.(string)$value."\n";
}
$text .= "\n".wordwrap(base64_encode($data),76,"\n",true);
$text .= "\n".'='.base64_encode(substr(pack('N',self::crc24($data)),1))."\n";
$text .= self::footer($marker)."\n";
return $text;
2020-06-07 06:25:59 +00:00
}
2020-06-24 12:37:14 +00:00
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]));
}
2020-06-07 06:25:59 +00:00
static function encode_s2k_count($iterations)
{
if($iterations >= 65011712) return 255;
$count = $iterations >> 6;
$c = 0;
while($count >= 32) {
$count = $count >> 1;
$c++;
}
$result = ($c << 4) | ($count - 16);
if (OpenPGP::decode_s2k_count($result) < $iterations) {
return $result + 1;
}
return $result;
}
2020-06-18 12:03:56 +00:00
/**
* @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).'-----';
}
2020-06-24 12:37:14 +00:00
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();
}
2020-06-18 12:03:56 +00:00
/**
* @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));
}
}
2020-06-24 12:37:14 +00:00
public function verify(OpenPGP\Message $data)
{
$verify = new OpenPGP\Crypt\RSA($this->key);
return $verify->verify($data);
}
2020-06-07 06:25:59 +00:00
}