public static function encrypt($passphrases_and_keys, $message, $symmetric_algorithm = 9) { list($cipher, $key_bytes, $key_block_bytes) = self::getCipher($symmetric_algorithm); if (!$cipher) { throw new Exception("Unsupported cipher"); } $prefix = crypt_random_string($key_block_bytes); $prefix .= substr($prefix, -2); $key = crypt_random_string($key_bytes); $cipher->setKey($key); $to_encrypt = $prefix . $message->to_bytes(); $mdc = new OpenPGP_ModificationDetectionCodePacket(hash('sha1', $to_encrypt . "Ó", true)); $to_encrypt .= $mdc->to_bytes(); $encrypted = array(new OpenPGP_IntegrityProtectedDataPacket($cipher->encrypt($to_encrypt))); if (!is_array($passphrases_and_keys) && !$passphrases_and_keys instanceof IteratorAggregate) { $passphrases_and_keys = (array) $passphrases_and_keys; } foreach ($passphrases_and_keys as $pass) { if ($pass instanceof OpenPGP_PublicKeyPacket) { if (!in_array($pass->algorithm, array(1, 2, 3))) { throw new Exception("Only RSA keys are supported."); } $crypt_rsa = new OpenPGP_Crypt_RSA($pass); $rsa = $crypt_rsa->public_key(); $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); $esk = $rsa->encrypt(chr($symmetric_algorithm) . $key . pack('n', self::checksum($key))); $esk = pack('n', OpenPGP::bitlength($esk)) . $esk; array_unshift($encrypted, new OpenPGP_AsymmetricSessionKeyPacket($pass->algorithm, $pass->fingerprint(), $esk)); } else { if (is_string($pass)) { $s2k = new OpenPGP_S2K(crypt_random_string(10)); $cipher->setKey($s2k->make_key($pass, $key_bytes)); $esk = $cipher->encrypt(chr($symmetric_algorithm) . $key); array_unshift($encrypted, new OpenPGP_SymmetricSessionKeyPacket($s2k, $esk, $symmetric_algorithm)); } } } return new OpenPGP_Message($encrypted); }
function body() { $bytes = parent::body() . chr($this->s2k_useage); $secret_material = NULL; if ($this->s2k_useage == 255 || $this->s2k_useage == 254) { $bytes .= chr($this->symmetric_algorithm); $bytes .= $this->s2k->to_bytes(); } if ($this->s2k_useage > 0) { $bytes .= $this->encrypted_data; } else { $secret_material = ''; foreach (self::$secret_key_fields[$this->algorithm] as $f) { $f = $this->key[$f]; $secret_material .= pack('n', OpenPGP::bitlength($f)); $secret_material .= $f; } $bytes .= $secret_material; // 2-octet checksum $chk = 0; for ($i = 0; $i < strlen($secret_material); $i++) { $chk = ($chk + ord($secret_material[$i])) % 65536; } $bytes .= pack('n', $chk); } return $bytes; }
/** * @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 = md5($this->key['n'] . $this->key['e']); case 4: $head = array(chr(0x99), NULL, chr($this->version), pack('N', $this->timestamp), chr($this->algorithm)); $material = array(); foreach ($this->key as $data) { $material[] = pack('n', OpenPGP::bitlength($data)); $material[] = $data; } $material = implode('', $material); $head[1] = pack('n', 6 + strlen($material)); return $this->fingerprint = sha1(implode('', $head) . $material); } }
/** * Encrypt data. * * @param mixed $key The list of public keys used to encrypt or a list * of passphrases. * @param mixed $data The data to be PGP encrypted. * @param array $opts Additional options: * - cipher: (integer) Cipher algorithm. * - compress: (integer) Compression algorithm. * * @param Horde_Pgp_Element_Message Encrypted message. */ protected function _encrypt($key, $data, $opts) { $msg = $this->_compressMessageOb($this->_getMessageOb($data), $opts['compress']); /* Following code adapted from OpenPGP_Crypt_Symmetric::encrypt(). */ list($cipher, $key_bytes, $block_bytes) = OpenPGP_Crypt_Symmetric::getCipher($opts['cipher']); $prefix = crypt_random_string($block_bytes); $prefix .= substr($prefix, -2); $to_encrypt = $prefix . $msg->to_bytes(); $mdc = new OpenPGP_ModificationDetectionCodePacket(hash('sha1', $to_encrypt . "Ó", true)); /* This is the symmetric encryption session key. */ $ckey = crypt_random_string($key_bytes); $cipher->setKey($ckey); /* This is the symmetrically encrypted version of plaintext. */ $encrypted = array(new OpenPGP_IntegrityProtectedDataPacket($cipher->encrypt($to_encrypt . $mdc->to_bytes()))); /* Now we need to encrypt the symmetric session key into the various * session key encrypted entities. */ foreach ($key as $k) { /* Symmetric encryption. */ if (is_string($k)) { $s2k = new OpenPGP_S2K(crypt_random_string(8, 2)); // SHA-1 $cipher->setKey($s2k->make_key($k, $key_bytes)); $encrypted[] = new OpenPGP_SymmetricSessionKeyPacket($s2k, $cipher->encrypt(chr($opts['cipher']) . $ckey), $opts['cipher']); continue; } /* Public key encryption. */ switch ($k->algorithm) { case 1: case 2: case 3: $rsa = new OpenPGP_Crypt_RSA($k); $pk = $rsa->public_key(); $pk->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); break; case 16: $pk = new Horde_Pgp_Crypt_Elgamal($k); break; } $pk_encrypt = $pk->encrypt(chr($opts['cipher']) . $ckey . pack('n', OpenPGP_Crypt_Symmetric::checksum($ckey))); $esk = array(); foreach (is_array($pk_encrypt) ? $pk_encrypt : array($pk_encrypt) as $val) { $esk[] = pack('n', OpenPGP::bitlength($val)) . $val; } $encrypted[] = new OpenPGP_AsymmetricSessionKeyPacket($k->algorithm, $k->fingerprint(), implode('', $esk)); } return new Horde_Pgp_Element_Message(new OpenPGP_Message(array_reverse($encrypted))); }