2 // This is free and unencumbered software released into the public domain.
4 * OpenPGP_Crypt_RSA.php is a wrapper for using the classes from OpenPGP.php with Crypt_RSA
9 // From http://phpseclib.sourceforge.net/
10 require_once 'Crypt/RSA.php';
12 require_once dirname(__FILE__).'/openpgp.php';
13 @include_once dirname(__FILE__).'/openpgp_crypt_symmetric.php'; /* For encrypt/decrypt */
15 class OpenPGP_Crypt_RSA {
16 protected $key, $message;
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)
24 $this->message = $packet;
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;
40 // Get Crypt_RSA for the public key
41 function public_key($keyid=NULL) {
42 return self::convert_public_key($this->key($keyid));
45 // Get Crypt_RSA for the private key
46 function private_key($keyid=NULL) {
47 return self::convert_private_key($this->key($keyid));
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);
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));
64 if(!($packet instanceof Crypt_RSA)) {
65 $packet = new self($packet);
69 $verifier = function($m, $s) use($self, $packet) {
70 if(!($packet instanceof Crypt_RSA)) {
71 $key = $packet->public_key($s->issuer());
73 if(!$key) return false;
74 $key->setHash(strtolower($s->hash_algorithm_name()));
75 return $key->verify($m, reset($s->data));
79 return $m->verified_signatures(array('RSA' => array(
82 'SHA224' => $verifier,
83 'SHA256' => $verifier,
84 'SHA384' => $verifier,
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)) {
95 $packet = new OpenPGP_LiteralDataPacket($packet);
97 $packet = OpenPGP_Message::parse($packet);
101 if($packet instanceof OpenPGP_SecretKeyPacket || $packet instanceof Crypt_RSA
102 || ($packet instanceof ArrayAccess && $packet[0] instanceof OpenPGP_SecretKeyPacket)) {
104 $message = $this->message;
110 if(!$key || !$message) return NULL; // Missing some data
112 if($message instanceof OpenPGP_Message) {
113 $sign = $message->signatures();
114 $message = $sign[0][0];
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);
122 $key->setHash(strtolower($hash));
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));})));
128 return new OpenPGP_Message(array($sig, $message));
131 /** Pass a message with a key and userid packet to sign */
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);
140 $key = $this->private_key($keyid);
141 if(!$key || !$packet) return NULL; // Missing some data
143 if(!$keyid) $keyid = substr($this->key->fingerprint, -16);
144 $key->setHash(strtolower($hash));
147 foreach($packet as $p) {
148 if($p instanceof OpenPGP_SignaturePacket) $sig = $p;
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);
158 $sig->sign_data(array('RSA' => array($hash => function($data) use($key) {return array($key->sign($data));})));
163 function decrypt($packet) {
164 if(!is_object($packet)) $packet = OpenPGP_Message::parse($packet);
166 if($packet instanceof OpenPGP_SecretKeyPacket || $packet instanceof Crypt_RSA
167 || ($packet instanceof ArrayAccess && $packet[0] instanceof OpenPGP_SecretKeyPacket)) {
169 $message = $this->message;
175 if(!$keys || !$message) return NULL; // Missing some data
177 if(!($keys instanceof Crypt_RSA)) {
178 $keys = new self($keys);
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->encrypted_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->encrypted_data, 2));
191 $key = $keys->private_key($p->keyid);
192 $sk = self::try_decrypt_session($key, substr($p->encrypted_data, 2));
197 $r = OpenPGP_Crypt_Symmetric::decryptPacket(OpenPGP_Crypt_Symmetric::getEncryptedData($message), $sk[0], $sk[1]);
202 return NULL; /* Failed */
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));
213 for($i = 0; $i < strlen($sk); $i++) {
214 $sk_chk = ($sk_chk + ord($sk{$i})) % 65536;
217 if($sk_chk != $chk) return NULL;
218 return array(ord($data{0}), $sk);
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();
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];
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
241 $rsa = self::crypt_rsa_key($mod, $exp);
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']);
251 static function convert_public_key($packet) {
252 return self::convert_key($packet, false);
255 static function convert_private_key($packet) {
256 return self::convert_key($packet, true);