]> git.mxchange.org Git - friendica-addons.git/blob - securemail/vendor/singpolyma/openpgp-php/lib/openpgp.php
securemail: update pgp library
[friendica-addons.git] / securemail / vendor / singpolyma / openpgp-php / lib / openpgp.php
1 <?php
2 // This is free and unencumbered software released into the public domain.
3 /**
4  * OpenPGP.php is a pure-PHP implementation of the OpenPGP Message Format
5  * (RFC 4880).
6  *
7  * @package OpenPGP
8  * @version 0.0.1
9  * @author  Arto Bendiken <arto.bendiken@gmail.com>
10  * @author  Stephen Paul Weber <singpolyma@singpolyma.net>
11  * @see     http://github.com/bendiken/openpgp-php
12  */
13
14 //////////////////////////////////////////////////////////////////////////////
15 // OpenPGP utilities
16
17 /**
18  * @see http://tools.ietf.org/html/rfc4880
19  */
20 class OpenPGP {
21   /**
22    * @see http://tools.ietf.org/html/rfc4880#section-6
23    * @see http://tools.ietf.org/html/rfc4880#section-6.2
24    * @see http://tools.ietf.org/html/rfc2045
25    */
26   static function enarmor($data, $marker = 'MESSAGE', array $headers = array()) {
27     $text = self::header($marker) . "\n";
28     foreach ($headers as $key => $value) {
29       $text .= $key . ': ' . (string)$value . "\n";
30     }
31     $text .= "\n" . base64_encode($data);
32     $text .= "\n".'=' . base64_encode(substr(pack('N', self::crc24($data)), 1)) . "\n";
33     $text .= self::footer($marker) . "\n";
34     return $text;
35   }
36
37   /**
38    * @see http://tools.ietf.org/html/rfc4880#section-6
39    * @see http://tools.ietf.org/html/rfc2045
40    */
41   static function unarmor($text, $header = 'PGP PUBLIC KEY BLOCK') {
42     $header = self::header($header);
43     $text = str_replace(array("\r\n", "\r"), array("\n", ''), $text);
44     if (($pos1 = strpos($text, $header)) !== FALSE &&
45         ($pos1 = strpos($text, "\n\n", $pos1 += strlen($header))) !== FALSE &&
46         ($pos2 = strpos($text, "\n=", $pos1 += 2)) !== FALSE) {
47       return base64_decode($text = substr($text, $pos1, $pos2 - $pos1));
48     }
49   }
50
51   /**
52    * @see http://tools.ietf.org/html/rfc4880#section-6.2
53    */
54   static function header($marker) {
55     return '-----BEGIN ' . strtoupper((string)$marker) . '-----';
56   }
57
58   /**
59    * @see http://tools.ietf.org/html/rfc4880#section-6.2
60    */
61   static function footer($marker) {
62     return '-----END ' . strtoupper((string)$marker) . '-----';
63   }
64
65   /**
66    * @see http://tools.ietf.org/html/rfc4880#section-6
67    * @see http://tools.ietf.org/html/rfc4880#section-6.1
68    */
69   static function crc24($data) {
70     $crc = 0x00b704ce;
71     for ($i = 0; $i < strlen($data); $i++) {
72       $crc ^= (ord($data[$i]) & 255) << 16;
73       for ($j = 0; $j < 8; $j++) {
74         $crc <<= 1;
75         if ($crc & 0x01000000) {
76           $crc ^= 0x01864cfb;
77         }
78       }
79     }
80     return $crc & 0x00ffffff;
81   }
82
83   /**
84    * @see http://tools.ietf.org/html/rfc4880#section-12.2
85    */
86   static function bitlength($data) {
87     return (strlen($data) - 1) * 8 + (int)floor(log(ord($data[0]), 2)) + 1;
88   }
89
90   static function decode_s2k_count($c) {
91     return ((int)16 + ($c & 15)) << (($c >> 4) + 6);
92   }
93
94   static function encode_s2k_count($iterations) {
95     if($iterations >= 65011712) return 255;
96
97     $count = $iterations >> 6;
98     $c = 0;
99     while($count >= 32) {
100       $count = $count >> 1;
101       $c++;
102     }
103     $result = ($c << 4) | ($count - 16);
104
105     if(OpenPGP::decode_s2k_count($result) < $iterations) {
106       return $result + 1;
107     }
108
109     return $result;
110   }
111 }
112
113 class OpenPGP_S2K {
114   public $type, $hash_algorithm, $salt, $count;
115
116   function __construct($salt='BADSALT', $hash_algorithm=10, $count=65536, $type=3) {
117     $this->type = $type;
118     $this->hash_algorithm = $hash_algorithm;
119     $this->salt = $salt;
120     $this->count = $count;
121   }
122
123   static function parse(&$input) {
124     $s2k = new OpenPGP_S2k();
125     switch($s2k->type = ord($input{0})) {
126       case 0:
127         $s2k->hash_algorithm = ord($input{1});
128         $input = substr($input, 2);
129         break;
130       case 1:
131         $s2k->hash_algorithm = ord($input{1});
132         $s2k->salt = substr($input, 2, 8);
133         $input = substr($input, 10);
134         break;
135       case 3:
136         $s2k->hash_algorithm = ord($input{1});
137         $s2k->salt = substr($input, 2, 8);
138         $s2k->count = OpenPGP::decode_s2k_count(ord($input{10}));
139         $input = substr($input, 11);
140         break;
141     }
142
143     return $s2k;
144   }
145
146   function to_bytes() {
147     $bytes = chr($this->type);
148     switch($this->type) {
149       case 0:
150         $bytes .= chr($this->hash_algorithm);
151         break;
152       case 1:
153         $bytes .= chr($this->hash_algorithm);
154         $bytes .= $this->salt;
155         break;
156       case 3:
157         $bytes .= chr($this->hash_algorithm);
158         $bytes .= $this->salt;
159         $bytes .= chr(OpenPGP::encode_s2k_count($this->count));
160         break;
161     }
162     return $bytes;
163   }
164
165   function raw_hash($s) {
166     return hash(strtolower(OpenPGP_SignaturePacket::$hash_algorithms[$this->hash_algorithm]), $s, true);
167   }
168
169   function sized_hash($s, $size) {
170     $hash = $this->raw_hash($s);
171     while(strlen($hash) < $size) {
172       $s = "\0" . $s;
173       $hash .= $this->raw_hash($s);
174     }
175
176     return substr($hash, 0, $size);
177   }
178
179   function iterate($s) {
180     if(strlen($s) >= $this->count) return $s;
181     $s = str_repeat($s, ceil($this->count / strlen($s)));
182     return substr($s, 0, $this->count);
183   }
184
185   function make_key($pass, $size) {
186     switch($this->type) {
187       case 0:
188         return $this->sized_hash($pass, $size);
189       case 1:
190         return $this->sized_hash($this->salt . $pass, $size);
191       case 3:
192         return $this->sized_hash($this->iterate($this->salt . $pass), $size);
193     }
194   }
195 }
196
197 //////////////////////////////////////////////////////////////////////////////
198 // OpenPGP messages
199
200 /**
201  * @see http://tools.ietf.org/html/rfc4880#section-4.1
202  * @see http://tools.ietf.org/html/rfc4880#section-11
203  * @see http://tools.ietf.org/html/rfc4880#section-11.3
204  */
205 class OpenPGP_Message implements IteratorAggregate, ArrayAccess {
206   public $uri = NULL;
207   public $packets = array();
208
209   static function parse_file($path) {
210     if (($msg = self::parse(file_get_contents($path)))) {
211       $msg->uri = preg_match('!^[\w\d]+://!', $path) ? $path : 'file://' . realpath($path);
212       return $msg;
213     }
214   }
215
216   /**
217    * @see http://tools.ietf.org/html/rfc4880#section-4.1
218    * @see http://tools.ietf.org/html/rfc4880#section-4.2
219    */
220   static function parse($input) {
221     if (is_resource($input)) {
222       return self::parse_stream($input);
223     }
224     if (is_string($input)) {
225       return self::parse_string($input);
226     }
227   }
228
229   static function parse_stream($input) {
230     return self::parse_string(stream_get_contents($input));
231   }
232
233   static function parse_string($input) {
234     $msg = new self;
235     while (($length = strlen($input)) > 0) {
236       if (($packet = OpenPGP_Packet::parse($input))) {
237         $msg[] = $packet;
238       }
239       if ($length == strlen($input)) { // is parsing stuck?
240         break;
241       }
242     }
243     return $msg;
244   }
245
246   function __construct(array $packets = array()) {
247     $this->packets = $packets;
248   }
249
250   function to_bytes() {
251     $bytes = '';
252     foreach($this as $p) {
253       $bytes .= $p->to_bytes();
254     }
255     return $bytes;
256   }
257
258   /**
259    * Extract signed objects from a well-formatted message
260    *
261    * Recurses into CompressedDataPacket
262    *
263    * @see http://tools.ietf.org/html/rfc4880#section-11
264    */
265   function signatures() {
266     $msg = $this;
267     while($msg[0] instanceof OpenPGP_CompressedDataPacket) $msg = $msg[0]->data;
268
269     $key = NULL;
270     $userid = NULL;
271     $subkey = NULL;
272     $sigs = array();
273     $final_sigs = array();
274
275     foreach($msg as $idx => $p) {
276       if($p instanceof OpenPGP_LiteralDataPacket) {
277         return array(array($p, array_values(array_filter($msg->packets, function($p) {
278           return $p instanceof OpenPGP_SignaturePacket;
279         }))));
280       } else if($p instanceof OpenPGP_PublicSubkeyPacket || $p instanceof OpenPGP_SecretSubkeyPacket) {
281         if($userid) {
282           array_push($final_sigs, array($key, $userid, $sigs));
283           $userid = NULL;
284         } else if($subkey) {
285           array_push($final_sigs, array($key, $subkey, $sigs));
286           $key = NULL;
287         }
288         $sigs = array();
289         $subkey = $p;
290       } else if($p instanceof OpenPGP_PublicKeyPacket) {
291         if($userid) {
292           array_push($final_sigs, array($key, $userid, $sigs));
293           $userid = NULL;
294         } else if($subkey) {
295           array_push($final_sigs, array($key, $subkey, $sigs));
296           $subkey = NULL;
297         } else if($key) {
298           array_push($final_sigs, array($key, $sigs));
299           $key = NULL;
300         }
301         $sigs = array();
302         $key = $p;
303       } else if($p instanceof OpenPGP_UserIDPacket) {
304         if($userid) {
305           array_push($final_sigs, array($key, $userid, $sigs));
306           $userid = NULL;
307         } else if($key) {
308           array_push($final_sigs, array($key, $sigs));
309         }
310         $sigs = array();
311         $userid = $p;
312       } else if($p instanceof OpenPGP_SignaturePacket) {
313         $sigs[] = $p;
314       }
315     }
316
317     if($userid) {
318       array_push($final_sigs, array($key, $userid, $sigs));
319     } else if($subkey) {
320       array_push($final_sigs, array($key, $subkey, $sigs));
321     } else if($key) {
322       array_push($final_sigs, array($key, $sigs));
323     }
324
325     return $final_sigs;
326   }
327
328   /**
329    * Function to extract verified signatures
330    * $verifiers is an array of callbacks formatted like array('RSA' => array('SHA256' => CALLBACK)) that take two parameters: raw message and signature packet
331    */
332   function verified_signatures($verifiers) {
333     $signed = $this->signatures();
334     $vsigned = array();
335
336     foreach($signed as $sign) {
337       $signatures = array_pop($sign);
338       $vsigs = array();
339
340       foreach($signatures as $sig) {
341         $verifier = $verifiers[$sig->key_algorithm_name()][$sig->hash_algorithm_name()];
342         if($verifier && $this->verify_one($verifier, $sign, $sig)) {
343           $vsigs[] = $sig;
344         }
345       }
346       array_push($sign, $vsigs);
347       $vsigned[] = $sign;
348     }
349
350     return $vsigned;
351   }
352
353   function verify_one($verifier, $sign, $sig) {
354     if($sign[0] instanceof OpenPGP_LiteralDataPacket) {
355       $sign[0]->normalize();
356       $raw = $sign[0]->data;
357     } else if(isset($sign[1]) && $sign[1] instanceof OpenPGP_UserIDPacket) {
358       $raw = implode('', array_merge($sign[0]->fingerprint_material(), array(chr(0xB4),
359         pack('N', strlen($sign[1]->body())), $sign[1]->body())));
360     } else if(isset($sign[1]) && ($sign[1] instanceof OpenPGP_PublicSubkeyPacket || $sign[1] instanceof OpenPGP_SecretSubkeyPacket)) {
361       $raw = implode('', array_merge($sign[0]->fingerprint_material(), $sign[1]->fingerprint_material()));
362     } else if($sign[0] instanceof OpenPGP_PublicKeyPacket) {
363       $raw = implode('', $sign[0]->fingerprint_material());
364     } else {
365       return NULL;
366     }
367     return call_user_func($verifier, $raw.$sig->trailer, $sig);
368   }
369
370   // IteratorAggregate interface
371
372   function getIterator() {
373     return new ArrayIterator($this->packets);
374   }
375
376   // ArrayAccess interface
377
378   function offsetExists($offset) {
379     return isset($this->packets[$offset]);
380   }
381
382   function offsetGet($offset) {
383     return $this->packets[$offset];
384   }
385
386   function offsetSet($offset, $value) {
387     return is_null($offset) ? $this->packets[] = $value : $this->packets[$offset] = $value;
388   }
389
390   function offsetUnset($offset) {
391     unset($this->packets[$offset]);
392   }
393 }
394
395 //////////////////////////////////////////////////////////////////////////////
396 // OpenPGP packets
397
398 /**
399  * OpenPGP packet.
400  *
401  * @see http://tools.ietf.org/html/rfc4880#section-4.1
402  * @see http://tools.ietf.org/html/rfc4880#section-4.3
403  */
404 class OpenPGP_Packet {
405   public $tag, $size, $data;
406
407   static function class_for($tag) {
408     return isset(self::$tags[$tag]) && class_exists(
409       $class = 'OpenPGP_' . self::$tags[$tag] . 'Packet') ? $class : __CLASS__;
410   }
411
412   /**
413    * Parses an OpenPGP packet.
414    * 
415    * Partial body lengths based on https://github.com/toofishes/python-pgpdump/blob/master/pgpdump/packet.py
416    *
417    * @see http://tools.ietf.org/html/rfc4880#section-4.2
418    */
419   static function parse(&$input) {
420     $packet = NULL;
421     if (strlen($input) > 0) {
422       $parser = ord($input[0]) & 64 ? 'parse_new_format' : 'parse_old_format';
423
424       $header_start0 = 0;
425       $consumed = 0;
426       $packet_data = "";
427       do {
428           list($tag, $data_offset, $data_length, $partial) = self::$parser($input, $header_start0);
429
430           $data_start0 = $header_start0 + $data_offset;
431           $header_start0 = $data_start0 + $data_length - 1;
432           $packet_data .= substr($input, $data_start0, $data_length);
433
434           $consumed += $data_offset + $data_length;
435           if ($partial) {
436               $consumed -= 1;
437           }
438       } while ($partial === true && $parser === 'parse_new_format');
439
440       if ($tag && ($class = self::class_for($tag))) {
441         $packet = new $class();
442         $packet->tag    = $tag;
443         $packet->input  = $packet_data;
444         $packet->length = strlen($packet_data);
445         $packet->read();
446         unset($packet->input);
447         unset($packet->length);
448       }
449       $input = substr($input, $consumed);
450     }
451     return $packet;
452   }
453
454   /**
455    * Parses a new-format (RFC 4880) OpenPGP packet.
456    *
457    * @see http://tools.ietf.org/html/rfc4880#section-4.2.2
458    */
459   static function parse_new_format($input, $header_start = 0) {
460     $tag = ord($input[0]) & 63;
461     $len = ord($input[$header_start + 1]);
462     if($len < 192) { // One octet length
463       return array($tag, 2, $len, false);
464     }
465     if($len > 191 && $len < 224) { // Two octet length
466       return array($tag, 3, (($len - 192) << 8) + ord($input[$header_start + 2]) + 192, false);
467     }
468     if($len == 255) { // Five octet length
469       $unpacked = unpack('N', substr($input, $header_start + 2, 4));
470       return array($tag, 6, reset($unpacked), false);
471     }
472     // Partial body lengths
473     return array($tag, 2, 1 << ($len & 0x1f), true);
474   }
475
476   /**
477    * Parses an old-format (PGP 2.6.x) OpenPGP packet.
478    *
479    * @see http://tools.ietf.org/html/rfc4880#section-4.2.1
480    */
481   static function parse_old_format($input) {
482     $len = ($tag = ord($input[0])) & 3;
483     $tag = ($tag >> 2) & 15;
484     switch ($len) {
485       case 0: // The packet has a one-octet length. The header is 2 octets long.
486         $head_length = 2;
487         $data_length = ord($input[1]);
488         break;
489       case 1: // The packet has a two-octet length. The header is 3 octets long.
490         $head_length = 3;
491         $data_length = unpack('n', substr($input, 1, 2));
492         $data_length = $data_length[1];
493         break;
494       case 2: // The packet has a four-octet length. The header is 5 octets long.
495         $head_length = 5;
496         $data_length = unpack('N', substr($input, 1, 4));
497         $data_length = $data_length[1];
498         break;
499       case 3: // The packet is of indeterminate length. The header is 1 octet long.
500         $head_length = 1;
501         $data_length = strlen($input) - $head_length;
502         break;
503     }
504     return array($tag, $head_length, $data_length, false);
505   }
506
507   function __construct($data=NULL) {
508     $this->tag = array_search(substr(substr(get_class($this), 8), 0, -6), self::$tags);
509     $this->data = $data;
510   }
511
512   function read() {
513   }
514
515   function body() {
516     return $this->data; // Will normally be overridden by subclasses
517   }
518
519   function header_and_body() {
520     $body = $this->body(); // Get body first, we will need it's length
521     $tag = chr($this->tag | 0xC0); // First two bits are 1 for new packet format
522     $size = chr(255).pack('N', strlen($body)); // Use 5-octet lengths
523     return array('header' => $tag.$size, 'body' => $body);
524   }
525
526   function to_bytes() {
527     $data = $this->header_and_body();
528     return $data['header'].$data['body'];
529   }
530
531   /**
532    * @see http://tools.ietf.org/html/rfc4880#section-3.5
533    */
534   function read_timestamp() {
535     return $this->read_unpacked(4, 'N');
536   }
537
538   /**
539    * @see http://tools.ietf.org/html/rfc4880#section-3.2
540    */
541   function read_mpi() {
542     $length = $this->read_unpacked(2, 'n');  // length in bits
543     $length = (int)floor(($length + 7) / 8); // length in bytes
544     return $this->read_bytes($length);
545   }
546
547   /**
548    * @see http://php.net/manual/en/function.unpack.php
549    */
550   function read_unpacked($count, $format) {
551     $unpacked = unpack($format, $this->read_bytes($count));
552     return reset($unpacked);
553   }
554
555   function read_byte() {
556     return ($bytes = $this->read_bytes()) ? $bytes[0] : NULL;
557   }
558
559   function read_bytes($count = 1) {
560     $bytes = substr($this->input, 0, $count);
561     $this->input = substr($this->input, $count);
562     return $bytes;
563   }
564
565   static $tags = array(
566      1 => 'AsymmetricSessionKey',      // Public-Key Encrypted Session Key
567      2 => 'Signature',                 // Signature Packet
568      3 => 'SymmetricSessionKey',       // Symmetric-Key Encrypted Session Key Packet
569      4 => 'OnePassSignature',          // One-Pass Signature Packet
570      5 => 'SecretKey',                 // Secret-Key Packet
571      6 => 'PublicKey',                 // Public-Key Packet
572      7 => 'SecretSubkey',              // Secret-Subkey Packet
573      8 => 'CompressedData',            // Compressed Data Packet
574      9 => 'EncryptedData',             // Symmetrically Encrypted Data Packet
575     10 => 'Marker',                    // Marker Packet
576     11 => 'LiteralData',               // Literal Data Packet
577     12 => 'Trust',                     // Trust Packet
578     13 => 'UserID',                    // User ID Packet
579     14 => 'PublicSubkey',              // Public-Subkey Packet
580     17 => 'UserAttribute',             // User Attribute Packet
581     18 => 'IntegrityProtectedData',    // Sym. Encrypted and Integrity Protected Data Packet
582     19 => 'ModificationDetectionCode', // Modification Detection Code Packet
583     60 => 'Experimental',              // Private or Experimental Values
584     61 => 'Experimental',              // Private or Experimental Values
585     62 => 'Experimental',              // Private or Experimental Values
586     63 => 'Experimental',              // Private or Experimental Values
587   );
588 }
589
590 /**
591  * OpenPGP Public-Key Encrypted Session Key packet (tag 1).
592  *
593  * @see http://tools.ietf.org/html/rfc4880#section-5.1
594  */
595 class OpenPGP_AsymmetricSessionKeyPacket extends OpenPGP_Packet {
596   public $version, $keyid, $key_algorithm, $encrypted_data;
597
598   function __construct($key_algorithm='', $keyid='', $encrypted_data='', $version=3) {
599     parent::__construct();
600     $this->version = $version;
601     $this->keyid = substr($keyid, -16);
602     $this->key_algorithm = $key_algorithm;
603     $this->encrypted_data = $encrypted_data;
604   }
605
606   function read() {
607     switch($this->version = ord($this->read_byte())) {
608       case 3:
609         $rawkeyid = $this->read_bytes(8);
610         $this->keyid = '';
611         for($i = 0; $i < strlen($rawkeyid); $i++) { // Store KeyID in Hex
612           $this->keyid .= sprintf('%02X',ord($rawkeyid{$i}));
613         }
614
615         $this->key_algorithm = ord($this->read_byte());
616
617         $this->encrypted_data = $this->input;
618         break;
619       default:
620         throw new Exception("Unsupported AsymmetricSessionKeyPacket version: " . $this->version);
621     }
622   }
623
624   function body() {
625     $bytes = chr($this->version);
626
627     for($i = 0; $i < strlen($this->keyid); $i += 2) {
628       $bytes .= chr(hexdec($this->keyid{$i}.$this->keyid{$i+1}));
629     }
630
631     $bytes .= chr($this->key_algorithm);
632     $bytes .= $this->encrypted_data;
633     return $bytes;
634   }
635 }
636
637 /**
638  * OpenPGP Signature packet (tag 2).
639  * Be sure to NULL the trailer if you update a signature packet!
640  *
641  * @see http://tools.ietf.org/html/rfc4880#section-5.2
642  */
643 class OpenPGP_SignaturePacket extends OpenPGP_Packet {
644   public $version, $signature_type, $hash_algorithm, $key_algorithm, $hashed_subpackets, $unhashed_subpackets, $hash_head;
645   public $trailer; // This is the literal bytes that get tacked on the end of the message when verifying the signature
646
647   function __construct($data=NULL, $key_algorithm=NULL, $hash_algorithm=NULL) {
648     parent::__construct();
649     $this->version = 4; // Default to version 4 sigs
650     if(is_string($this->hash_algorithm = $hash_algorithm)) {
651       $this->hash_algorithm = array_search($this->hash_algorithm, self::$hash_algorithms);
652     }
653     if(is_string($this->key_algorithm = $key_algorithm)) {
654       $this->key_algorithm = array_search($this->key_algorithm, OpenPGP_PublicKeyPacket::$algorithms);
655     }
656     if($data) { // If we have any data, set up the creation time
657       $this->hashed_subpackets = array(new OpenPGP_SignaturePacket_SignatureCreationTimePacket(time()));
658     }
659     if($data instanceof OpenPGP_LiteralDataPacket) {
660       $this->signature_type = ($data->format == 'b') ? 0x00 : 0x01;
661       $data->normalize();
662       $data = $data->data;
663     } else if($data instanceof OpenPGP_Message && $data[0] instanceof OpenPGP_PublicKeyPacket) {
664       // $data is a message with PublicKey first, UserID second
665       $key = implode('', $data[0]->fingerprint_material());
666       $user_id = $data[1]->body();
667       $data = $key . chr(0xB4) . pack('N', strlen($user_id)) . $user_id;
668     }
669     $this->data = $data; // Store to-be-signed data in here until the signing happens
670   }
671
672   /**
673    * $this->data must be set to the data to sign (done by constructor)
674    * $signers in the same format as $verifiers for OpenPGP_Message.
675    */
676   function sign_data($signers) {
677     $this->trailer = $this->calculate_trailer();
678     $signer = $signers[$this->key_algorithm_name()][$this->hash_algorithm_name()];
679     $this->data = call_user_func($signer, $this->data.$this->trailer);
680     $unpacked = unpack('n', substr(implode('',$this->data), 0, 2));
681     $this->hash_head = reset($unpacked);
682   }
683
684   function read() {
685     switch($this->version = ord($this->read_byte())) {
686       case 2:
687       case 3:
688         assert(ord($this->read_byte()) == 5);
689         $this->signature_type = ord($this->read_byte());
690         $creation_time = $this->read_timestamp();
691         $keyid = $this->read_bytes(8);
692         $keyidHex = '';
693         for($i = 0; $i < strlen($keyid); $i++) { // Store KeyID in Hex
694           $keyidHex .= sprintf('%02X',ord($keyid{$i}));
695         }
696
697         $this->hashed_subpackets = array();
698         $this->unhashed_subpackets = array(
699           new OpenPGP_SignaturePacket_SignatureCreationTimePacket($creation_time),
700           new OpenPGP_SignaturePacket_IssuerPacket($keyidHex)
701         );
702
703         $this->key_algorithm = ord($this->read_byte());
704         $this->hash_algorithm = ord($this->read_byte());
705         $this->hash_head = $this->read_unpacked(2, 'n');
706         $this->data = array();
707         while(strlen($this->input) > 0) {
708           $this->data[] = $this->read_mpi();
709         }
710         break;
711       case 4:
712         $this->signature_type = ord($this->read_byte());
713         $this->key_algorithm = ord($this->read_byte());
714         $this->hash_algorithm = ord($this->read_byte());
715         $this->trailer = chr(4).chr($this->signature_type).chr($this->key_algorithm).chr($this->hash_algorithm);
716
717         $hashed_size = $this->read_unpacked(2, 'n');
718         $hashed_subpackets = $this->read_bytes($hashed_size);
719         $this->trailer .= pack('n', $hashed_size).$hashed_subpackets;
720         $this->hashed_subpackets = self::get_subpackets($hashed_subpackets);
721
722         $this->trailer .= chr(4).chr(0xff).pack('N', 6 + $hashed_size);
723
724         $unhashed_size = $this->read_unpacked(2, 'n');
725         $this->unhashed_subpackets = self::get_subpackets($this->read_bytes($unhashed_size));
726
727         $this->hash_head = $this->read_unpacked(2, 'n');
728
729         $this->data = array();
730         while(strlen($this->input) > 0) {
731           $this->data[] = $this->read_mpi();
732         }
733         break;
734     }
735   }
736
737   function calculate_trailer() {
738     // The trailer is just the top of the body plus some crap
739     $body = $this->body_start();
740     return $body.chr(4).chr(0xff).pack('N', strlen($body));
741   }
742
743   function body_start() {
744     $body = chr(4).chr($this->signature_type).chr($this->key_algorithm).chr($this->hash_algorithm);
745
746     $hashed_subpackets = '';
747     foreach((array)$this->hashed_subpackets as $p) {
748       $hashed_subpackets .= $p->to_bytes();
749     }
750     $body .= pack('n', strlen($hashed_subpackets)).$hashed_subpackets;
751
752     return $body;
753   }
754
755   function body() {
756     switch($this->version) {
757       case 2:
758       case 3:
759         $body = chr($this->version) . chr(5) . chr($this->signature_type);
760
761         foreach((array)$this->unhashed_subpackets as $p) {
762           if($p instanceof OpenPGP_SignaturePacket_SignatureCreationTimePacket) {
763             $body .= pack('N', $p->data);
764             break;
765           }
766         }
767
768         foreach((array)$this->unhashed_subpackets as $p) {
769           if($p instanceof OpenPGP_SignaturePacket_IssuerPacket) {
770             for($i = 0; $i < strlen($p->data); $i += 2) {
771               $body .= chr(hexdec($p->data{$i}.$p->data{$i+1}));
772             }
773             break;
774           }
775         }
776
777         $body .= chr($this->key_algorithm);
778         $body .= chr($this->hash_algorithm);
779         $body .= pack('n', $this->hash_head);
780
781         foreach($this->data as $mpi) {
782           $body .= pack('n', OpenPGP::bitlength($mpi)).$mpi;
783         }
784
785         return $body;
786       case 4:
787         if(!$this->trailer) $this->trailer = $this->calculate_trailer();
788         $body = substr($this->trailer, 0, -6);
789
790         $unhashed_subpackets = '';
791         foreach((array)$this->unhashed_subpackets as $p) {
792           $unhashed_subpackets .= $p->to_bytes();
793         }
794         $body .= pack('n', strlen($unhashed_subpackets)).$unhashed_subpackets;
795
796         $body .= pack('n', $this->hash_head);
797
798         foreach((array)$this->data as $mpi) {
799           $body .= pack('n', OpenPGP::bitlength($mpi)).$mpi;
800         }
801
802         return $body;
803     }
804   }
805
806   function key_algorithm_name() {
807     return OpenPGP_PublicKeyPacket::$algorithms[$this->key_algorithm];
808   }
809
810   function hash_algorithm_name() {
811     return self::$hash_algorithms[$this->hash_algorithm];
812   }
813
814   function issuer() {
815     foreach($this->hashed_subpackets as $p) {
816       if($p instanceof OpenPGP_SignaturePacket_IssuerPacket) return $p->data;
817     }
818     foreach($this->unhashed_subpackets as $p) {
819       if($p instanceof OpenPGP_SignaturePacket_IssuerPacket) return $p->data;
820     }
821     return NULL;
822   }
823
824   /**
825    * @see http://tools.ietf.org/html/rfc4880#section-5.2.3.1
826    */
827   static function get_subpackets($input) {
828     $subpackets = array();
829     while(($length = strlen($input)) > 0) {
830       $subpackets[] = self::get_subpacket($input);
831       if($length == strlen($input)) { // Parsing stuck?
832         break;
833       }
834     }
835     return $subpackets;
836   }
837
838   static function get_subpacket(&$input) {
839     $len = ord($input[0]);
840     $length_of_length = 1;
841     // if($len < 192) One octet length, no furthur processing
842     if($len > 190 && $len < 255) { // Two octet length
843       $length_of_length = 2;
844       $len = (($len - 192) << 8) + ord($input[1]) + 192;
845     }
846     if($len == 255) { // Five octet length
847       $length_of_length = 5;
848       $unpacked = unpack('N', substr($input, 1, 4));
849       $len = reset($unpacked);
850     }
851     $input = substr($input, $length_of_length); // Chop off length header
852     $tag = ord($input[0]);
853     $class = self::class_for($tag);
854     if($class) {
855       $packet = new $class();
856       $packet->tag = $tag;
857       $packet->input = substr($input, 1, $len-1);
858       $packet->length = $len-1;
859       $packet->read();
860       unset($packet->input);
861       unset($packet->length);
862     }
863     $input = substr($input, $len); // Chop off the data from this packet
864     return $packet;
865   }
866
867   static $hash_algorithms = array(
868        1 => 'MD5',
869        2 => 'SHA1',
870        3 => 'RIPEMD160',
871        8 => 'SHA256',
872        9 => 'SHA384',
873       10 => 'SHA512',
874       11 => 'SHA224'
875     );
876
877   static $subpacket_types = array(
878       //0 => 'Reserved',
879       //1 => 'Reserved',
880       2 => 'SignatureCreationTime',
881       3 => 'SignatureExpirationTime',
882       4 => 'ExportableCertification',
883       5 => 'TrustSignature',
884       6 => 'RegularExpression',
885       7 => 'Revocable',
886       //8 => 'Reserved',
887       9 => 'KeyExpirationTime',
888       //10 => 'Placeholder for backward compatibility',
889       11 => 'PreferredSymmetricAlgorithms',
890       12 => 'RevocationKey',
891       //13 => 'Reserved',
892       //14 => 'Reserved',
893       //15 => 'Reserved',
894       16 => 'Issuer',
895       //17 => 'Reserved',
896       //18 => 'Reserved',
897       //19 => 'Reserved',
898       20 => 'NotationData',
899       21 => 'PreferredHashAlgorithms',
900       22 => 'PreferredCompressionAlgorithms',
901       23 => 'KeyServerPreferences',
902       24 => 'PreferredKeyServer',
903       25 => 'PrimaryUserID',
904       26 => 'PolicyURI',
905       27 => 'KeyFlags',
906       28 => 'SignersUserID',
907       29 => 'ReasonforRevocation',
908       30 => 'Features',
909       31 => 'SignatureTarget',
910       32 => 'EmbeddedSignature',
911     );
912
913   static function class_for($tag) {
914     if(!isset(self::$subpacket_types[$tag])) return 'OpenPGP_SignaturePacket_Subpacket';
915     return 'OpenPGP_SignaturePacket_'.self::$subpacket_types[$tag].'Packet';
916   }
917
918 }
919
920 class OpenPGP_SignaturePacket_Subpacket extends OpenPGP_Packet {
921   function __construct($data=NULL) {
922     parent::__construct($data);
923     $this->tag = array_search(substr(substr(get_class($this), 8+16), 0, -6), OpenPGP_SignaturePacket::$subpacket_types);
924   }
925
926   function header_and_body() {
927     $body = $this->body(); // Get body first, we will need it's length
928     $size = chr(255).pack('N', strlen($body)+1); // Use 5-octet lengths + 1 for tag as first packet body octet
929     $tag = chr($this->tag);
930     return array('header' => $size.$tag, 'body' => $body);
931   }
932
933   /* Defaults for unsupported packets */
934   function read() {
935     $this->data = $this->input;
936   }
937
938   function body() {
939     return $this->data;
940   }
941 }
942
943 /**
944  * @see http://tools.ietf.org/html/rfc4880#section-5.2.3.4
945  */
946 class OpenPGP_SignaturePacket_SignatureCreationTimePacket extends OpenPGP_SignaturePacket_Subpacket {
947   function read() {
948     $this->data = $this->read_timestamp();
949   }
950
951   function body() {
952     return pack('N', $this->data);
953   }
954 }
955
956 class OpenPGP_SignaturePacket_SignatureExpirationTimePacket extends OpenPGP_SignaturePacket_Subpacket {
957   function read() {
958     $this->data = $this->read_timestamp();
959   }
960
961   function body() {
962     return pack('N', $this->data);
963   }
964 }
965
966 class OpenPGP_SignaturePacket_ExportableCertificationPacket extends OpenPGP_SignaturePacket_Subpacket {
967   function read() {
968     $this->data = (ord($this->input) != 0);
969   }
970
971   function body() {
972     return chr($this->data ? 1 : 0);
973   }
974 }
975
976 class OpenPGP_SignaturePacket_TrustSignaturePacket extends OpenPGP_SignaturePacket_Subpacket {
977   function read() {
978     $this->depth = ord($this->input{0});
979     $this->trust = ord($this->input{1});
980   }
981
982   function body() {
983     return chr($this->depth) . chr($this->trust);
984   }
985 }
986
987 class OpenPGP_SignaturePacket_RegularExpressionPacket extends OpenPGP_SignaturePacket_Subpacket {
988   function read() {
989     $this->data = substr($this->input, 0, -1);
990   }
991
992   function body() {
993     return $this->data . chr(0);
994   }
995 }
996
997 class OpenPGP_SignaturePacket_RevocablePacket extends OpenPGP_SignaturePacket_Subpacket {
998   function read() {
999     $this->data = (ord($this->input) != 0);
1000   }
1001
1002   function body() {
1003     return chr($this->data ? 1 : 0);
1004   }
1005 }
1006
1007 class OpenPGP_SignaturePacket_KeyExpirationTimePacket extends OpenPGP_SignaturePacket_Subpacket {
1008   function read() {
1009     $this->data = $this->read_timestamp();
1010   }
1011
1012   function body() {
1013     return pack('N', $this->data);
1014   }
1015 }
1016
1017 class OpenPGP_SignaturePacket_PreferredSymmetricAlgorithmsPacket extends OpenPGP_SignaturePacket_Subpacket {
1018   function read() {
1019     $this->data = array();
1020     while(strlen($this->input) > 0) {
1021       $this->data[] = ord($this->read_byte());
1022     }
1023   }
1024
1025   function body() {
1026     $bytes = '';
1027     foreach($this->data as $algo) {
1028       $bytes .= chr($algo);
1029     }
1030     return $bytes;
1031   }
1032 }
1033
1034 class OpenPGP_SignaturePacket_RevocationKeyPacket extends OpenPGP_SignaturePacket_Subpacket {
1035    public $key_algorithm, $fingerprint, $sensitive;
1036
1037   function read() {
1038     // bitfield must have bit 0x80 set, says the spec
1039     $bitfield = ord($this->read_byte());
1040     $this->sensitive = $bitfield & 0x40 == 0x40;
1041     $this->key_algorithm = ord($this->read_byte());
1042
1043     $this->fingerprint = '';
1044     while(strlen($this->input) > 0) {
1045       $this->fingerprint .= sprintf('%02X',ord($this->read_byte()));
1046     }
1047   }
1048
1049   function body() {
1050     $bytes = '';
1051     $bytes .= chr(0x80 | ($this->sensitive ? 0x40 : 0x00));
1052     $bytes .= chr($this->key_algorithm);
1053
1054     for($i = 0; $i < strlen($this->fingerprint); $i += 2) {
1055       $bytes .= chr(hexdec($this->fingerprint{$i}.$this->fingerprint{$i+1}));
1056     }
1057
1058     return $bytes;
1059   }
1060 }
1061
1062 /**
1063  * @see http://tools.ietf.org/html/rfc4880#section-5.2.3.5
1064  */
1065 class OpenPGP_SignaturePacket_IssuerPacket extends OpenPGP_SignaturePacket_Subpacket {
1066   function read() {
1067     for($i = 0; $i < 8; $i++) { // Store KeyID in Hex
1068       $this->data .= sprintf('%02X',ord($this->read_byte()));
1069     }
1070   }
1071
1072   function body() {
1073     $bytes = '';
1074     for($i = 0; $i < strlen($this->data); $i += 2) {
1075       $bytes .= chr(hexdec($this->data{$i}.$this->data{$i+1}));
1076     }
1077     return $bytes;
1078   }
1079 }
1080
1081 class OpenPGP_SignaturePacket_NotationDataPacket extends OpenPGP_SignaturePacket_Subpacket {
1082   public $human_readable, $name;
1083
1084   function read() {
1085     $flags = $this->read_bytes(4);
1086     $namelen = $this->read_unpacked(2, 'n');
1087     $datalen = $this->read_unpacked(2, 'n');
1088     $this->human_readable = ord($flags[0]) & 0x80 == 0x80;
1089     $this->name = $this->read_bytes($namelen);
1090     $this->data = $this->read_bytes($datalen);
1091   }
1092
1093   function body () {
1094     return chr($this->human_readable ? 0x80 : 0x00) . "\0\0\0" .
1095       pack('n', strlen($this->name)) . pack('n', strlen($this->data)) .
1096       $this->name . $this->data;
1097   }
1098 }
1099
1100 class OpenPGP_SignaturePacket_PreferredHashAlgorithmsPacket extends OpenPGP_SignaturePacket_Subpacket {
1101   function read() {
1102     $this->data = array();
1103     while(strlen($this->input) > 0) {
1104       $this->data[] = ord($this->read_byte());
1105     }
1106   }
1107
1108   function body() {
1109     $bytes = '';
1110     foreach($this->data as $algo) {
1111       $bytes .= chr($algo);
1112     }
1113     return $bytes;
1114   }
1115 }
1116
1117 class OpenPGP_SignaturePacket_PreferredCompressionAlgorithmsPacket extends OpenPGP_SignaturePacket_Subpacket {
1118   function read() {
1119     $this->data = array();
1120     while(strlen($this->input) > 0) {
1121       $this->data[] = ord($this->read_byte());
1122     }
1123   }
1124
1125   function body() {
1126     $bytes = '';
1127     foreach($this->data as $algo) {
1128       $bytes .= chr($algo);
1129     }
1130     return $bytes;
1131   }
1132 }
1133
1134 class OpenPGP_SignaturePacket_KeyServerPreferencesPacket extends OpenPGP_SignaturePacket_Subpacket {
1135   public $no_modify;
1136
1137   function read() {
1138     $flags = ord($this->input);
1139     $this->no_modify = $flags & 0x80 == 0x80;
1140   }
1141
1142   function body() {
1143     return chr($this->no_modify ? 0x80 : 0x00);
1144   }
1145 }
1146
1147 class OpenPGP_SignaturePacket_PreferredKeyServerPacket extends OpenPGP_SignaturePacket_Subpacket {
1148   function read() {
1149     $this->data = $this->input;
1150   }
1151
1152   function body() {
1153     return $this->data;
1154   }
1155 }
1156
1157 class OpenPGP_SignaturePacket_PrimaryUserIDPacket extends OpenPGP_SignaturePacket_Subpacket {
1158   function read() {
1159     $this->data = (ord($this->input) != 0);
1160   }
1161
1162   function body() {
1163     return chr($this->data ? 1 : 0);
1164   }
1165
1166 }
1167
1168 class OpenPGP_SignaturePacket_PolicyURIPacket extends OpenPGP_SignaturePacket_Subpacket {
1169   function read() {
1170     $this->data = $this->input;
1171   }
1172
1173   function body() {
1174     return $this->data;
1175   }
1176 }
1177
1178 class OpenPGP_SignaturePacket_KeyFlagsPacket extends OpenPGP_SignaturePacket_Subpacket {
1179   function __construct($flags=array()) {
1180     parent::__construct();
1181     $this->flags = $flags;
1182   }
1183
1184   function read() {
1185     $this->flags = array();
1186     while($this->input) {
1187       $this->flags[] = ord($this->read_byte());
1188     }
1189   }
1190
1191   function body() {
1192     $bytes = '';
1193     foreach($this->flags as $f) {
1194       $bytes .= chr($f);
1195     }
1196     return $bytes;
1197   }
1198 }
1199
1200 class OpenPGP_SignaturePacket_SignersUserIDPacket extends OpenPGP_SignaturePacket_Subpacket {
1201   function read() {
1202     $this->data = $this->input;
1203   }
1204
1205   function body() {
1206     return $this->data;
1207   }
1208 }
1209
1210 class OpenPGP_SignaturePacket_ReasonforRevocationPacket extends OpenPGP_SignaturePacket_Subpacket {
1211   public $code;
1212
1213   function read() {
1214     $this->code = ord($this->read_byte());
1215     $this->data = $this->input;
1216   }
1217
1218   function body() {
1219     return chr($this->code) . $this->data;
1220   }
1221 }
1222
1223
1224 class OpenPGP_SignaturePacket_FeaturesPacket extends OpenPGP_SignaturePacket_KeyFlagsPacket {
1225   // Identical functionality to parent
1226 }
1227
1228 class OpenPGP_SignaturePacket_SignatureTargetPacket extends OpenPGP_SignaturePacket_Subpacket {
1229   public $key_algorithm, $hash_algorithm;
1230
1231   function read() {
1232     $this->key_algorithm = ord($this->read_byte());
1233     $this->hash_algorithm = ord($this->read_byte());
1234     $this->data = $this->input;
1235   }
1236
1237   function body() {
1238     return chr($this->key_algorithm) . chr($this->hash_algorithm) . $this->data;
1239   }
1240
1241 }
1242
1243 class OpenPGP_SignaturePacket_EmbeddedSignaturePacket extends OpenPGP_SignaturePacket {
1244   // TODO: This is duplicated from subpacket... improve?
1245   function __construct($data=NULL) {
1246     parent::__construct($data);
1247     $this->tag = array_search(substr(substr(get_class($this), 8+16), 0, -6), OpenPGP_SignaturePacket::$subpacket_types);
1248   }
1249
1250   function header_and_body() {
1251     $body = $this->body(); // Get body first, we will need it's length
1252     $size = chr(255).pack('N', strlen($body)+1); // Use 5-octet lengths + 1 for tag as first packet body octet
1253     $tag = chr($this->tag);
1254     return array('header' => $size.$tag, 'body' => $body);
1255   }
1256 }
1257
1258 /**
1259  * OpenPGP Symmetric-Key Encrypted Session Key packet (tag 3).
1260  *
1261  * @see http://tools.ietf.org/html/rfc4880#section-5.3
1262  */
1263 class OpenPGP_SymmetricSessionKeyPacket extends OpenPGP_Packet {
1264   public $version, $symmetric_algorithm, $s2k, $encrypted_data;
1265
1266   function __construct($s2k=NULL, $encrypted_data='', $symmetric_algorithm=9, $version=3) {
1267     parent::__construct();
1268     $this->version = $version;
1269     $this->symmetric_algorithm = $symmetric_algorithm;
1270     $this->s2k = $s2k;
1271     $this->encrypted_data = $encrypted_data;
1272   }
1273
1274   function read() {
1275     $this->version = ord($this->read_byte());
1276     $this->symmetric_algorithm = ord($this->read_byte());
1277     $this->s2k = OpenPGP_S2k::parse($this->input);
1278     $this->encrypted_data = $this->input;
1279   }
1280
1281   function body() {
1282     return chr($this->version) . chr($this->symmetric_algorithm) .
1283       $this->s2k->to_bytes() . $this->encrypted_data;
1284   }
1285 }
1286
1287 /**
1288  * OpenPGP One-Pass Signature packet (tag 4).
1289  *
1290  * @see http://tools.ietf.org/html/rfc4880#section-5.4
1291  */
1292 class OpenPGP_OnePassSignaturePacket extends OpenPGP_Packet {
1293   public $version, $signature_type, $hash_algorithm, $key_algorithm, $key_id, $nested;
1294   function read() {
1295     $this->version = ord($this->read_byte());
1296     $this->signature_type = ord($this->read_byte());
1297     $this->hash_algorithm = ord($this->read_byte());
1298     $this->key_algorithm = ord($this->read_byte());
1299     for($i = 0; $i < 8; $i++) { // Store KeyID in Hex
1300       $this->key_id .= sprintf('%02X',ord($this->read_byte()));
1301     }
1302     $this->nested = ord($this->read_byte());
1303   }
1304
1305   function body() {
1306     $body = chr($this->version).chr($this->signature_type).chr($this->hash_algorithm).chr($this->key_algorithm);
1307     for($i = 0; $i < strlen($this->key_id); $i += 2) {
1308       $body .= chr(hexdec($this->key_id{$i}.$this->key_id{$i+1}));
1309     }
1310     $body .= chr((int)$this->nested);
1311     return $body;
1312   }
1313 }
1314
1315 /**
1316  * OpenPGP Public-Key packet (tag 6).
1317  *
1318  * @see http://tools.ietf.org/html/rfc4880#section-5.5.1.1
1319  * @see http://tools.ietf.org/html/rfc4880#section-5.5.2
1320  * @see http://tools.ietf.org/html/rfc4880#section-11.1
1321  * @see http://tools.ietf.org/html/rfc4880#section-12
1322  */
1323 class OpenPGP_PublicKeyPacket extends OpenPGP_Packet {
1324   public $version, $timestamp, $algorithm;
1325   public $key, $key_id, $fingerprint;
1326   public $v3_days_of_validity;
1327
1328   function __construct($key=array(), $algorithm='RSA', $timestamp=NULL, $version=4) {
1329     parent::__construct();
1330     $this->key = $key;
1331     if(is_string($this->algorithm = $algorithm)) {
1332       $this->algorithm = array_search($this->algorithm, self::$algorithms);
1333     }
1334     $this->timestamp = $timestamp ? $timestamp : time();
1335     $this->version = $version;
1336
1337     if(count($this->key) > 0) {
1338       $this->key_id = substr($this->fingerprint(), -8);
1339     }
1340   }
1341
1342   // Find self signatures in a message, these often contain metadata about the key
1343   function self_signatures($message) {
1344     $sigs = array();
1345     $keyid16 = strtoupper(substr($this->fingerprint, -16));
1346     foreach($message as $p) {
1347       if($p instanceof OpenPGP_SignaturePacket) {
1348         if(strtoupper($p->issuer()) == $keyid16) {
1349           $sigs[] = $p;
1350         } else {
1351           foreach(array_merge($p->hashed_subpackets, $p->unhashed_subpackets) as $s) {
1352             if($s instanceof OpenPGP_SignaturePacket_EmbeddedSignaturePacket && strtoupper($s->issuer()) == $keyid16) {
1353               $sigs[] = $p;
1354               break;
1355             }
1356           }
1357         }
1358       } else if(count($sigs)) break; // After we've seen a self sig, the next non-sig stop all self-sigs
1359     }
1360     return $sigs;
1361   }
1362
1363   // Find expiry time of this key based on the self signatures in a message
1364   function expires($message) {
1365     foreach($this->self_signatures($message) as $p) {
1366       foreach(array_merge($p->hashed_subpackets, $p->unhashed_subpackets) as $s) {
1367         if($s instanceof OpenPGP_SignaturePacket_KeyExpirationTimePacket) {
1368           return $this->timestamp + $s->data;
1369         }
1370       }
1371     }
1372     return NULL; // Never expires
1373   }
1374
1375   /**
1376    * @see http://tools.ietf.org/html/rfc4880#section-5.5.2
1377    */
1378   function read() {
1379     switch ($this->version = ord($this->read_byte())) {
1380       case 3:
1381         $this->timestamp = $this->read_timestamp();
1382         $this->v3_days_of_validity = $this->read_unpacked(2, 'n');
1383         $this->algorithm = ord($this->read_byte());
1384         $this->read_key_material();
1385         break;
1386       case 4:
1387         $this->timestamp = $this->read_timestamp();
1388         $this->algorithm = ord($this->read_byte());
1389         $this->read_key_material();
1390     }
1391   }
1392
1393   /**
1394    * @see http://tools.ietf.org/html/rfc4880#section-5.5.2
1395    */
1396   function read_key_material() {
1397     foreach (self::$key_fields[$this->algorithm] as $field) {
1398       $this->key[$field] = $this->read_mpi();
1399     }
1400     $this->key_id = substr($this->fingerprint(), -8);
1401   }
1402
1403   function fingerprint_material() {
1404     switch ($this->version) {
1405       case 3:
1406         $material = array();
1407         foreach (self::$key_fields[$this->algorithm] as $i) {
1408           $material[] = pack('n', OpenPGP::bitlength($this->key[$i]));
1409           $material[] = $this->key[$i];
1410         }
1411         return $material;
1412       case 4:
1413         $head = array(
1414           chr(0x99), NULL,
1415           chr($this->version), pack('N', $this->timestamp),
1416           chr($this->algorithm),
1417         );
1418         $material = array();
1419         foreach (self::$key_fields[$this->algorithm] as $i) {
1420           $material[] = pack('n', OpenPGP::bitlength($this->key[$i]));
1421           $material[] = $this->key[$i];
1422         }
1423         $material = implode('', $material);
1424         $head[1] = pack('n', 6 + strlen($material));
1425         $head[] = $material;
1426         return $head;
1427     }
1428   }
1429
1430   /**
1431    * @see http://tools.ietf.org/html/rfc4880#section-12.2
1432    * @see http://tools.ietf.org/html/rfc4880#section-3.3
1433    */
1434   function fingerprint() {
1435     switch ($this->version) {
1436       case 2:
1437       case 3:
1438         return $this->fingerprint = strtoupper(md5(implode('', $this->fingerprint_material())));
1439       case 4:
1440         return $this->fingerprint = strtoupper(sha1(implode('', $this->fingerprint_material())));
1441     }
1442   }
1443
1444   function body() {
1445      switch ($this->version) {
1446       case 2:
1447       case 3:
1448         return implode('', array_merge(array(
1449             chr($this->version) . pack('N', $this->timestamp) .
1450               pack('n', $this->v3_days_of_validity) . chr($this->algorithm)
1451           ), $this->fingerprint_material())
1452         );
1453       case 4:
1454         return implode('', array_slice($this->fingerprint_material(), 2));
1455     }
1456   }
1457
1458   static $key_fields = array(
1459      1 => array('n', 'e'),           // RSA
1460     16 => array('p', 'g', 'y'),      // ELG-E
1461     17 => array('p', 'q', 'g', 'y'), // DSA
1462   );
1463
1464   static $algorithms = array(
1465        1 => 'RSA',
1466        2 => 'RSA',
1467        3 => 'RSA',
1468       16 => 'ELGAMAL',
1469       17 => 'DSA',
1470       18 => 'ECC',
1471       19 => 'ECDSA',
1472       21 => 'DH'
1473     );
1474
1475 }
1476
1477 /**
1478  * OpenPGP Public-Subkey packet (tag 14).
1479  *
1480  * @see http://tools.ietf.org/html/rfc4880#section-5.5.1.2
1481  * @see http://tools.ietf.org/html/rfc4880#section-5.5.2
1482  * @see http://tools.ietf.org/html/rfc4880#section-11.1
1483  * @see http://tools.ietf.org/html/rfc4880#section-12
1484  */
1485 class OpenPGP_PublicSubkeyPacket extends OpenPGP_PublicKeyPacket {
1486   // TODO
1487 }
1488
1489 /**
1490  * OpenPGP Secret-Key packet (tag 5).
1491  *
1492  * @see http://tools.ietf.org/html/rfc4880#section-5.5.1.3
1493  * @see http://tools.ietf.org/html/rfc4880#section-5.5.3
1494  * @see http://tools.ietf.org/html/rfc4880#section-11.2
1495  * @see http://tools.ietf.org/html/rfc4880#section-12
1496  */
1497 class OpenPGP_SecretKeyPacket extends OpenPGP_PublicKeyPacket {
1498   public $s2k_useage, $s2k, $symmetric_algorithm, $private_hash, $encrypted_data;
1499   function read() {
1500     parent::read(); // All the fields from PublicKey
1501     $this->s2k_useage = ord($this->read_byte());
1502     if($this->s2k_useage == 255 || $this->s2k_useage == 254) {
1503       $this->symmetric_algorithm = ord($this->read_byte());
1504       $this->s2k = OpenPGP_S2k::parse($this->input);
1505     } else if($this->s2k_useage > 0) {
1506       $this->symmetric_algorithm = $this->s2k_useage;
1507     }
1508     if($this->s2k_useage > 0) {
1509       $this->encrypted_data = $this->input; // Rest of input is MPIs and checksum (encrypted)
1510     } else {
1511       $this->key_from_input();
1512       $this->private_hash = $this->read_bytes(2); // TODO: Validate checksum?
1513     }
1514   }
1515
1516   static $secret_key_fields = array(
1517      1 => array('d', 'p', 'q', 'u'), // RSA
1518      2 => array('d', 'p', 'q', 'u'), // RSA-E
1519      3 => array('d', 'p', 'q', 'u'), // RSA-S
1520     16 => array('x'),                // ELG-E
1521     17 => array('x'),                // DSA
1522   );
1523
1524   function key_from_input() {
1525     foreach(self::$secret_key_fields[$this->algorithm] as $field) {
1526       $this->key[$field] = $this->read_mpi();
1527     }
1528   }
1529
1530   function body() {
1531     $bytes = parent::body() . chr($this->s2k_useage);
1532     $secret_material = NULL;
1533     if($this->s2k_useage == 255 || $this->s2k_useage == 254) {
1534       $bytes .= chr($this->symmetric_algorithm);
1535       $bytes .= $this->s2k->to_bytes();
1536     }
1537     if($this->s2k_useage > 0) {
1538       $bytes .= $this->encrypted_data;
1539     } else {
1540       $secret_material = '';
1541       foreach(self::$secret_key_fields[$this->algorithm] as $f) {
1542         $f = $this->key[$f];
1543         $secret_material .= pack('n', OpenPGP::bitlength($f));
1544         $secret_material .= $f;
1545       }
1546       $bytes .= $secret_material;
1547
1548       // 2-octet checksum
1549       $chk = 0;
1550       for($i = 0; $i < strlen($secret_material); $i++) {
1551         $chk = ($chk + ord($secret_material[$i])) % 65536;
1552       }
1553       $bytes .= pack('n', $chk);
1554     }
1555     return $bytes;
1556   }
1557 }
1558
1559 /**
1560  * OpenPGP Secret-Subkey packet (tag 7).
1561  *
1562  * @see http://tools.ietf.org/html/rfc4880#section-5.5.1.4
1563  * @see http://tools.ietf.org/html/rfc4880#section-5.5.3
1564  * @see http://tools.ietf.org/html/rfc4880#section-11.2
1565  * @see http://tools.ietf.org/html/rfc4880#section-12
1566  */
1567 class OpenPGP_SecretSubkeyPacket extends OpenPGP_SecretKeyPacket {
1568   // TODO
1569 }
1570
1571 /**
1572  * OpenPGP Compressed Data packet (tag 8).
1573  *
1574  * @see http://tools.ietf.org/html/rfc4880#section-5.6
1575  */
1576 class OpenPGP_CompressedDataPacket extends OpenPGP_Packet implements IteratorAggregate, ArrayAccess {
1577   public $algorithm;
1578   /* see http://tools.ietf.org/html/rfc4880#section-9.3 */
1579   static $algorithms = array(0 => 'Uncompressed', 1 => 'ZIP', 2 => 'ZLIB', 3 => 'BZip2');
1580   function read() {
1581     $this->algorithm = ord($this->read_byte());
1582     $this->data = $this->read_bytes($this->length);
1583     switch($this->algorithm) {
1584       case 0:
1585         $this->data = OpenPGP_Message::parse($this->data);
1586         break;
1587       case 1:
1588         $this->data = OpenPGP_Message::parse(gzinflate($this->data));
1589         break;
1590       case 2:
1591         $this->data = OpenPGP_Message::parse(gzuncompress($this->data));
1592         break;
1593       case 3:
1594         $this->data = OpenPGP_Message::parse(bzdecompress($this->data));
1595         break;
1596       default:
1597         /* TODO error? */
1598     }
1599   }
1600
1601   function body() {
1602     $body = chr($this->algorithm);
1603     switch($this->algorithm) {
1604       case 0:
1605         $body .= $this->data->to_bytes();
1606         break;
1607       case 1:
1608         $body .= gzdeflate($this->data->to_bytes());
1609         break;
1610       case 2:
1611         $body .= gzcompress($this->data->to_bytes());
1612         break;
1613       case 3:
1614         $body .= bzcompress($this->data->to_bytes());
1615         break;
1616       default:
1617         /* TODO error? */
1618     }
1619     return $body;
1620   }
1621
1622   // IteratorAggregate interface
1623
1624   function getIterator() {
1625     return new ArrayIterator($this->data->packets);
1626   }
1627
1628   // ArrayAccess interface
1629
1630   function offsetExists($offset) {
1631     return isset($this->data[$offset]);
1632   }
1633
1634   function offsetGet($offset) {
1635     return $this->data[$offset];
1636   }
1637
1638   function offsetSet($offset, $value) {
1639     return is_null($offset) ? $this->data[] = $value : $this->data[$offset] = $value;
1640   }
1641
1642   function offsetUnset($offset) {
1643     unset($this->data[$offset]);
1644   }
1645
1646 }
1647
1648 /**
1649  * OpenPGP Symmetrically Encrypted Data packet (tag 9).
1650  *
1651  * @see http://tools.ietf.org/html/rfc4880#section-5.7
1652  */
1653 class OpenPGP_EncryptedDataPacket extends OpenPGP_Packet {
1654   function read() {
1655     $this->data = $this->input;
1656   }
1657
1658   function body() {
1659     return $this->data;
1660   }
1661 }
1662
1663 /**
1664  * OpenPGP Marker packet (tag 10).
1665  *
1666  * @see http://tools.ietf.org/html/rfc4880#section-5.8
1667  */
1668 class OpenPGP_MarkerPacket extends OpenPGP_Packet {
1669   // TODO
1670 }
1671
1672 /**
1673  * OpenPGP Literal Data packet (tag 11).
1674  *
1675  * @see http://tools.ietf.org/html/rfc4880#section-5.9
1676  */
1677 class OpenPGP_LiteralDataPacket extends OpenPGP_Packet {
1678   public $format, $filename, $timestamp;
1679
1680   function __construct($data=NULL, $opt=array()) {
1681     parent::__construct();
1682     $this->data = $data;
1683     $this->format = isset($opt['format']) ? $opt['format'] : 'b';
1684     $this->filename = isset($opt['filename']) ? $opt['filename'] : 'data';
1685     $this->timestamp = isset($opt['timestamp']) ? $opt['timestamp'] : time();
1686   }
1687
1688   function normalize() {
1689     if($this->format == 'u' || $this->format == 't') { // Normalize line endings
1690       $this->data = str_replace("\n", "\r\n", str_replace("\r", "\n", str_replace("\r\n", "\n", $this->data)));
1691     }
1692   }
1693
1694   function read() {
1695     $this->size = $this->length - 1 - 4;
1696     $this->format = $this->read_byte();
1697     $filename_length = ord($this->read_byte());
1698     $this->size -= $filename_length;
1699     $this->filename = $this->read_bytes($filename_length);
1700     $this->timestamp = $this->read_timestamp();
1701     $this->data = $this->read_bytes($this->size);
1702   }
1703
1704   function body() {
1705     return $this->format.chr(strlen($this->filename)).$this->filename.pack('N', $this->timestamp).$this->data;
1706   }
1707 }
1708
1709 /**
1710  * OpenPGP Trust packet (tag 12).
1711  *
1712  * @see http://tools.ietf.org/html/rfc4880#section-5.10
1713  */
1714 class OpenPGP_TrustPacket extends OpenPGP_Packet {
1715   function read() {
1716     $this->data = $this->input;
1717   }
1718
1719   function body() {
1720     return $this->data;
1721   }
1722 }
1723
1724 /**
1725  * OpenPGP User ID packet (tag 13).
1726  *
1727  * @see http://tools.ietf.org/html/rfc4880#section-5.11
1728  * @see http://tools.ietf.org/html/rfc2822
1729  */
1730 class OpenPGP_UserIDPacket extends OpenPGP_Packet {
1731   public $name, $comment, $email;
1732
1733   function __construct($name='', $comment='', $email='') {
1734     parent::__construct();
1735     if(!$comment && !$email) {
1736       $this->input = $name;
1737       $this->read();
1738     } else {
1739       $this->name = $name;
1740       $this->comment = $comment;
1741       $this->email = $email;
1742     }
1743   }
1744
1745   function read() {
1746     $this->data = $this->input;
1747     // User IDs of the form: "name (comment) <email>"
1748     if (preg_match('/^([^\(]+)\(([^\)]+)\)\s+<([^>]+)>$/', $this->data, $matches)) {
1749       $this->name    = trim($matches[1]);
1750       $this->comment = trim($matches[2]);
1751       $this->email   = trim($matches[3]);
1752     }
1753     // User IDs of the form: "name <email>"
1754     else if (preg_match('/^([^<]+)\s+<([^>]+)>$/', $this->data, $matches)) {
1755       $this->name    = trim($matches[1]);
1756       $this->comment = NULL;
1757       $this->email   = trim($matches[2]);
1758     }
1759     // User IDs of the form: "name"
1760     else if (preg_match('/^([^<]+)$/', $this->data, $matches)) {
1761       $this->name    = trim($matches[1]);
1762       $this->comment = NULL;
1763       $this->email   = NULL;
1764     }
1765     // User IDs of the form: "<email>"
1766     else if (preg_match('/^<([^>]+)>$/', $this->data, $matches)) {
1767       $this->name    = NULL;
1768       $this->comment = NULL;
1769       $this->email   = trim($matches[2]);
1770     }
1771   }
1772
1773   function __toString() {
1774     $text = array();
1775     if ($this->name)    { $text[] = $this->name; }
1776     if ($this->comment) { $text[] = "({$this->comment})"; }
1777     if ($this->email)   { $text[] = "<{$this->email}>"; }
1778     return implode(' ', $text);
1779   }
1780
1781   function body() {
1782     return ''.$this; // Convert to string is the body
1783   }
1784 }
1785
1786 /**
1787  * OpenPGP User Attribute packet (tag 17).
1788  *
1789  * @see http://tools.ietf.org/html/rfc4880#section-5.12
1790  * @see http://tools.ietf.org/html/rfc4880#section-11.1
1791  */
1792 class OpenPGP_UserAttributePacket extends OpenPGP_Packet {
1793   public $packets;
1794
1795   // TODO
1796 }
1797
1798 /**
1799  * OpenPGP Sym. Encrypted Integrity Protected Data packet (tag 18).
1800  *
1801  * @see http://tools.ietf.org/html/rfc4880#section-5.13
1802  */
1803 class OpenPGP_IntegrityProtectedDataPacket extends OpenPGP_EncryptedDataPacket {
1804   public $version;
1805
1806   function __construct($data='', $version=1) {
1807     parent::__construct();
1808     $this->version = $version;
1809     $this->data = $data;
1810   }
1811
1812   function read() {
1813     $this->version = ord($this->read_byte());
1814     $this->data = $this->input;
1815   }
1816
1817   function body() {
1818     return chr($this->version) . $this->data;
1819   }
1820 }
1821
1822 /**
1823  * OpenPGP Modification Detection Code packet (tag 19).
1824  *
1825  * @see http://tools.ietf.org/html/rfc4880#section-5.14
1826  */
1827 class OpenPGP_ModificationDetectionCodePacket extends OpenPGP_Packet {
1828   function __construct($sha1='') {
1829     parent::__construct();
1830     $this->data = $sha1;
1831   }
1832
1833   function read() {
1834     $this->data = $this->input;
1835     if(strlen($this->input) != 20) throw new Exception("Bad ModificationDetectionCodePacket");
1836   }
1837
1838   function header_and_body() {
1839     $body = $this->body(); // Get body first, we will need it's length
1840     if(strlen($body) != 20) throw new Exception("Bad ModificationDetectionCodePacket");
1841     return array('header' => "\xD3\x14", 'body' => $body);
1842   }
1843
1844   function body() {
1845     return $this->data;
1846   }
1847 }
1848
1849 /**
1850  * OpenPGP Private or Experimental packet (tags 60..63).
1851  *
1852  * @see http://tools.ietf.org/html/rfc4880#section-4.3
1853  */
1854 class OpenPGP_ExperimentalPacket extends OpenPGP_Packet {}