OpenPGP PHP
 All Classes Namespaces Functions Variables
openpgp_crypt_rsa.php
1 <?php
2 // This is free and unencumbered software released into the public domain.
9 // From http://phpseclib.sourceforge.net/
10 require 'Crypt/RSA.php';
11 
12 require_once dirname(__FILE__).'/openpgp.php';
13 @include_once dirname(__FILE__).'/openpgp_crypt_symmetric.php'; /* For encrypt/decrypt */
14 
16  protected $key, $message;
17 
18  // Construct a wrapper object from a key or a message packet
19  function __construct($packet) {
20  if(!is_object($packet)) $packet = OpenPGP_Message::parse($packet);
21  if($packet instanceof OpenPGP_PublicKeyPacket || $packet[0] instanceof OpenPGP_PublicKeyPacket) { // If it's a key (other keys are subclasses of this one)
22  $this->key = $packet;
23  } else {
24  $this->message = $packet;
25  }
26  }
27 
28  function key($keyid=NULL) {
29  if(!$this->key) return NULL; // No key
30  if($this->key instanceof OpenPGP_Message) {
31  foreach($this->key as $p) {
32  if($p instanceof OpenPGP_PublicKeyPacket) {
33  if(!$keyid || strtoupper(substr($p->fingerprint, strlen($keyid)*-1)) == strtoupper($keyid)) return $p;
34  }
35  }
36  }
37  return $this->key;
38  }
39 
40  // Get Crypt_RSA for the public key
41  function public_key($keyid=NULL) {
42  return self::convert_public_key($this->key($keyid));
43  }
44 
45  // Get Crypt_RSA for the private key
46  function private_key($keyid=NULL) {
47  return self::convert_private_key($this->key($keyid));
48  }
49 
50  // Pass a message to verify with this key, or a key (OpenPGP or Crypt_RSA) to check this message with
51  // Second optional parameter to specify which signature to verify (if there is more than one)
52  function verify($packet) {
53  $self = $this; // For old PHP
54  if(!is_object($packet)) $packet = OpenPGP_Message::parse($packet);
55  if(!$this->message) {
56  $m = $packet;
57  $verifier = function($m, $s) use($self) {
58  $key = $self->public_key($s->issuer());
59  if(!$key) return false;
60  $key->setHash(strtolower($s->hash_algorithm_name()));
61  return $key->verify($m, reset($s->data));
62  };
63  } else {
64  if(!($packet instanceof Crypt_RSA)) {
65  $packet = new self($packet);
66  }
67 
68  $m = $this->message;
69  $verifier = function($m, $s) use($self, $packet) {
70  if(!($packet instanceof Crypt_RSA)) {
71  $key = $packet->public_key($s->issuer());
72  }
73  if(!$key) return false;
74  $key->setHash(strtolower($s->hash_algorithm_name()));
75  return $key->verify($m, reset($s->data));
76  };
77  }
78 
79  return $m->verified_signatures(array('RSA' => array(
80  'MD5' => $verifier,
81  'SHA1' => $verifier,
82  'SHA224' => $verifier,
83  'SHA256' => $verifier,
84  'SHA384' => $verifier,
85  'SHA512' => $verifier
86  )));
87  }
88 
89  // Pass a message to sign with this key, or a secret key to sign this message with
90  // Second parameter is hash algorithm to use (default SHA256)
91  // Third parameter is the 16-digit key ID to use... defaults to the key id in the key packet
92  function sign($packet, $hash='SHA256', $keyid=NULL) {
93  if(!is_object($packet)) {
94  if($this->key) {
95  $packet = new OpenPGP_LiteralDataPacket($packet);
96  } else {
97  $packet = OpenPGP_Message::parse($packet);
98  }
99  }
100 
101  if($packet instanceof OpenPGP_SecretKeyPacket || $packet instanceof Crypt_RSA
102  || ($packet instanceof ArrayAccess && $packet[0] instanceof OpenPGP_SecretKeyPacket)) {
103  $key = $packet;
105  } else {
106  $key = $this->key;
107  $message = $packet;
108  }
109 
110  if(!$key || !$message) return NULL; // Missing some data
111 
112  if($message instanceof OpenPGP_Message) {
113  $sign = $message->signatures();
114  $message = $sign[0][0];
115  }
116 
117  if(!($key instanceof Crypt_RSA)) {
118  $key = new self($key);
119  if(!$keyid) $keyid = substr($key->key()->fingerprint, -16, 16);
120  $key = $key->private_key($keyid);
121  }
122  $key->setHash(strtolower($hash));
123 
124  $sig = new OpenPGP_SignaturePacket($message, 'RSA', strtoupper($hash));
125  $sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_IssuerPacket($keyid);
126  $sig->sign_data(array('RSA' => array($hash => function($data) use($key) {return array($key->sign($data));})));
127 
128  return new OpenPGP_Message(array($sig, $message));
129  }
130 
132  // TODO: merge this with the normal sign function
133  function sign_key_userid($packet, $hash='SHA256', $keyid=NULL) {
134  if(is_array($packet)) {
135  $packet = new OpenPGP_Message($packet);
136  } else if(!is_object($packet)) {
137  $packet = OpenPGP_Message::parse($packet);
138  }
139 
140  $key = $this->private_key($keyid);
141  if(!$key || !$packet) return NULL; // Missing some data
142 
143  if(!$keyid) $keyid = substr($this->key->fingerprint, -16);
144  $key->setHash(strtolower($hash));
145 
146  $sig = NULL;
147  foreach($packet as $p) {
148  if($p instanceof OpenPGP_SignaturePacket) $sig = $p;
149  }
150  if(!$sig) {
151  $sig = new OpenPGP_SignaturePacket($packet, 'RSA', strtoupper($hash));
152  $sig->signature_type = 0x13;
153  $sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_KeyFlagsPacket(array(0x01, 0x02));
154  $sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_IssuerPacket($keyid);
155  $packet[] = $sig;
156  }
157 
158  $sig->sign_data(array('RSA' => array($hash => function($data) use($key) {return array($key->sign($data));})));
159 
160  return $packet;
161  }
162 
163  function decrypt($packet) {
164  if(!is_object($packet)) $packet = OpenPGP_Message::parse($packet);
165 
166  if($packet instanceof OpenPGP_SecretKeyPacket || $packet instanceof Crypt_RSA
167  || ($packet instanceof ArrayAccess && $packet[0] instanceof OpenPGP_SecretKeyPacket)) {
168  $keys = $packet;
170  } else {
171  $keys = $this->key;
172  $message = $packet;
173  }
174 
175  if(!$keys || !$message) return NULL; // Missing some data
176 
177  if(!($keys instanceof Crypt_RSA)) {
178  $keys = new self($keys);
179  }
180 
181  foreach($message as $p) {
182  if($p instanceof OpenPGP_AsymmetricSessionKeyPacket) {
183  if($keys instanceof Crypt_RSA) {
184  $sk = self::try_decrypt_session($keys, substr($p->encyrpted_data, 2));
185  } else if(strlen(str_replace('0', '', $p->keyid)) < 1) {
186  foreach($keys->key as $k) {
187  $sk = self::try_decrypt_session(self::convert_private_key($k), substr($p->encyrpted_data, 2));
188  if($sk) break;
189  }
190  } else {
191  $key = $keys->private_key($p->keyid);
192  $sk = self::try_decrypt_session($key, substr($p->encrypted_data, 2));
193  }
194 
195  if(!$sk) continue;
196 
198  if($r) return $r;
199  }
200  }
201 
202  return NULL; /* Failed */
203  }
204 
205  static function try_decrypt_session($key, $edata) {
206  $key->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
207  $data = $key->decrypt($edata);
208  $sk = substr($data, 1, strlen($data)-3);
209  $chk = unpack('n', substr($data, -2));
210  $chk = reset($chk);
211 
212  $sk_chk = 0;
213  for($i = 0; $i < strlen($sk); $i++) {
214  $sk_chk = ($sk_chk + ord($sk{$i})) % 65536;
215  }
216 
217  if($sk_chk != $chk) return NULL;
218  return array(ord($data{0}), $sk);
219  }
220 
221  static function crypt_rsa_key($mod, $exp, $hash='SHA256') {
222  $rsa = new Crypt_RSA();
223  $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
224  $rsa->setHash(strtolower($hash));
225  $rsa->modulus = new Math_BigInteger($mod, 256);
226  $rsa->k = strlen($rsa->modulus->toBytes());
227  $rsa->exponent = new Math_BigInteger($exp, 256);
228  $rsa->setPublicKey();
229  return $rsa;
230  }
231 
232  static function convert_key($packet, $private=false) {
233  if(!is_object($packet)) $packet = OpenPGP_Message::parse($packet);
234  if($packet instanceof OpenPGP_Message) $packet = $packet[0];
235 
236  $mod = $packet->key['n'];
237  $exp = $packet->key['e'];
238  if($private) $exp = $packet->key['d'];
239  if(!$exp) return NULL; // Packet doesn't have needed data
240 
241  $rsa = self::crypt_rsa_key($mod, $exp);
242 
243  if($private) {
244  if($packet->key['p'] && $packet->key['q']) $rsa->primes = array($packet->key['p'], $packet->key['q']);
245  if($packet->key['u']) $rsa->coefficients = array($packet->key['u']);
246  }
247 
248  return $rsa;
249  }
250 
251  static function convert_public_key($packet) {
252  return self::convert_key($packet, false);
253  }
254 
255  static function convert_private_key($packet) {
256  return self::convert_key($packet, true);
257  }
258 
259 }
260 
261 ?>