/** * Encrypt data. * * @param string $text Plaintext. * * @return array Array of MPI values (c1, c2). */ public function encrypt($text) { $p_len = strlen($this->_key->key['p']); $length = $p_len - 11; if ($length <= 0) { return false; } $g = new BigInteger($this->_key->key['g'], 256); $p = new BigInteger($this->_key->key['p'], 256); $y = new BigInteger($this->_key->key['y'], 256); $out = array(); foreach (str_split($text, $length) as $m) { // EME-PKCS1-v1_5 encoding $psLen = $p_len - strlen($m) - 3; $ps = ''; while (($psLen2 = strlen($ps)) != $psLen) { $tmp = Random::String($psLen - $psLen2); $ps .= str_replace("", '', $tmp); } $em = new BigInteger(chr(0) . chr(2) . $ps . chr(0) . $m, 256); // End EME-PKCS1-v1_5 encoding $k = Horde_Pgp_Crypt_DSA::randomNumber($p); $c1 = $g->modPow($k, $p); $c2_base = $y->modPow($k, $p)->multiply($em)->divide($p); $c2 = $c2_base[1]; $out[] = str_pad($c1->toBytes(), $p_len, chr(0), STR_PAD_LEFT); $out[] = str_pad($c2->toBytes(), $p_len, chr(0), STR_PAD_LEFT); } return $out; }
/** * Generate a number that lies between 0 and q-1. * * @param \phpseclib\Math\BigInteger $q Max number. * * @return \phpseclib\Math\BigInteger Generated number. */ public static function randomNumber($q) { $bytes = strlen($q->toBytes()) + 8; $ints = $bytes + 1 >> 2; $cstring = Crypt\Random::String($ints); $random = ''; for ($i = 0; $i < $ints; ++$i) { $random .= pack('N', $cstring[$i]); } $c = new BigInteger(substr($random, 0, $bytes), 256); $one = new BigInteger(1); $result_base = $c->divide($q->subtract($one)); return $result_base[1]->add($one); }
/** * 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))); }