openpgp-php/lib/OpenPgP/PublicKeyPacket.php

210 lines
5.1 KiB
PHP

<?php
namespace Leenooks\OpenPGP;
use Leenooks\OpenPGP;
/**
* OpenPGP Public-Key packet (tag 6).
*
* @see http://tools.ietf.org/html/rfc4880#section-5.5.1.1
* @see http://tools.ietf.org/html/rfc4880#section-5.5.2
* @see http://tools.ietf.org/html/rfc4880#section-11.1
* @see http://tools.ietf.org/html/rfc4880#section-12
*/
class PublicKeyPacket extends Packet
{
protected $tag = 6;
public $version, $timestamp, $algorithm;
public $key, $key_id, $fingerprint;
public $v3_days_of_validity;
static $key_fields = array(
1 => array('n', 'e'), // RSA
16 => array('p', 'g', 'y'), // ELG-E
17 => array('p', 'q', 'g', 'y'), // DSA
);
static $algorithms = [
1 => 'RSA',
2 => 'RSA',
3 => 'RSA',
16 => 'ELGAMAL',
17 => 'DSA',
18 => 'ECC',
19 => 'ECDSA',
21 => 'DH'
];
function __construct($key=[],$algorithm='RSA',$timestamp=NULL,$version=4)
{
parent::__construct();
if ($key instanceof PublicKeyPacket) {
$this->algorithm = $key->algorithm;
$this->key = array();
// Restrict to only the fields we need
foreach (self::$key_fields[$this->algorithm] as $field) {
$this->key[$field] = $key->key[$field];
}
$this->key_id = $key->key_id;
$this->fingerprint = $key->fingerprint;
$this->timestamp = $key->timestamp;
$this->version = $key->version;
$this->v3_days_of_validity = $key->v3_days_of_validity;
} else {
$this->key = $key;
if (is_string($this->algorithm = $algorithm)) {
$this->algorithm = array_search($this->algorithm,self::$algorithms);
}
$this->timestamp = $timestamp ? $timestamp : time();
$this->version = $version;
if (count($this->key) > 0) {
$this->key_id = substr($this->fingerprint(),-8);
}
}
}
function body()
{
switch ($this->version) {
case 2:
case 3:
return implode('', array_merge(array(
chr($this->version) . pack('N', $this->timestamp) .
pack('n', $this->v3_days_of_validity) . chr($this->algorithm)
), $this->fingerprint_material())
);
case 4:
return implode('', array_slice($this->fingerprint_material(), 2));
}
}
// Find expiry time of this key based on the self signatures in a message
function expires($message)
{
foreach ($this->self_signatures($message) as $p) {
foreach (array_merge($p->hashed_subpackets, $p->unhashed_subpackets) as $s) {
if ($s instanceof SignaturePacket\KeyExpirationTimePacket) {
return $this->timestamp + $s->data;
}
}
}
// Never expires
return NULL;
}
/**
* @see http://tools.ietf.org/html/rfc4880#section-12.2
* @see http://tools.ietf.org/html/rfc4880#section-3.3
*/
function fingerprint()
{
switch ($this->version) {
case 2:
case 3:
return $this->fingerprint = strtoupper(md5(implode('', $this->fingerprint_material())));
case 4:
return $this->fingerprint = strtoupper(sha1(implode('', $this->fingerprint_material())));
}
}
function fingerprint_material()
{
switch ($this->version) {
case 3:
$material = array();
foreach (self::$key_fields[$this->algorithm] as $i) {
$material[] = pack('n', OpenPGP::bitlength($this->key[$i]));
$material[] = $this->key[$i];
}
return $material;
case 4:
$head = array(
chr(0x99), NULL,
chr($this->version), pack('N', $this->timestamp),
chr($this->algorithm),
);
$material = [];
foreach (self::$key_fields[$this->algorithm] as $i) {
$material[] = pack('n', OpenPGP::bitlength($this->key[$i]));
$material[] = $this->key[$i];
}
$material = implode('', $material);
$head[1] = pack('n', 6 + strlen($material));
$head[] = $material;
return $head;
}
}
/**
* @see http://tools.ietf.org/html/rfc4880#section-5.5.2
*/
function read()
{
switch ($this->version = ord($this->read_byte())) {
case 3:
$this->timestamp = $this->read_timestamp();
$this->v3_days_of_validity = $this->read_unpacked(2, 'n');
$this->algorithm = ord($this->read_byte());
$this->read_key_material();
break;
case 4:
$this->timestamp = $this->read_timestamp();
$this->algorithm = ord($this->read_byte());
$this->read_key_material();
break;
}
}
/**
* @see http://tools.ietf.org/html/rfc4880#section-5.5.2
*/
function read_key_material()
{
foreach (self::$key_fields[$this->algorithm] as $field) {
$this->key[$field] = $this->read_mpi();
}
$this->key_id = substr($this->fingerprint(), -8);
}
// Find self signatures in a message, these often contain metadata about the key
function self_signatures($message)
{
$sigs = [];
$keyid16 = strtoupper(substr($this->fingerprint,-16));
foreach ($message as $p) {
if ($p instanceof OpenPGP\SignaturePacket) {
if (strtoupper($p->issuer()) == $keyid16) {
$sigs[] = $p;
} else {
foreach (array_merge($p->hashed_subpackets, $p->unhashed_subpackets) as $s) {
if ($s instanceof SignaturePacket\EmbeddedSignaturePacket && strtoupper($s->issuer()) == $keyid16) {
$sigs[] = $p;
break;
}
}
}
// After we've seen a self sig, the next non-sig stop all self-sigs
} elseif (count($sigs))
break;
}
return $sigs;
}
}