RSA: changes to how encryption / signatures work

This commit is contained in:
terrafrost 2015-12-24 10:58:06 -06:00
parent 94fdbba2aa
commit 806249c7e9
7 changed files with 219 additions and 149 deletions

View File

@ -48,6 +48,7 @@ namespace phpseclib\Crypt;
use phpseclib\Crypt\Hash;
use phpseclib\Crypt\Random;
use phpseclib\Math\BigInteger;
use phpseclib\File\ASN1;
/**
* Pure-PHP PKCS#1 compliant implementation of RSA.
@ -67,26 +68,32 @@ class RSA
* Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
* (OAEP) for encryption / decryption.
*
* Uses sha1 by default.
* Uses sha256 by default
*
* @see self::setHash()
* @see self::setMGFHash()
*/
const ENCRYPTION_OAEP = 1;
const PADDING_OAEP = 1;
/**
* Use PKCS#1 padding.
*
* Although self::ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
* Although self::PADDING_OAEP / self::PADDING_PSS offers more security, including PKCS#1 padding is necessary for purposes of backwards
* compatibility with protocols (like SSH-1) written before OAEP's introduction.
*/
const ENCRYPTION_PKCS1 = 2;
const PADDING_PKCS1 = 2;
/**
* Do not use any padding
*
* Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy
* stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc.
*/
const ENCRYPTION_NONE = 3;
const PADDING_NONE = 3;
/**
* Use PKCS#1 padding with PKCS1 v1.5 compatability
*
* A PKCS1 v2.1 encrypted message may not successfully decrypt with a PKCS1 v1.5 implementation (such as OpenSSL).
*/
const PADDING_PKCS15_COMPAT = 6;
/**#@-*/
/**#@+
@ -98,19 +105,17 @@ class RSA
/**
* Use the Probabilistic Signature Scheme for signing
*
* Uses sha1 by default.
* Uses sha256 and 0 as the salt length
*
* @see self::setSaltLength()
* @see self::setMGFHash()
* @see self::setHash()
*/
const SIGNATURE_PSS = 1;
const PADDING_PSS = 4;
/**
* Use the PKCS#1 scheme by default.
*
* Although self::SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
* compatibility with protocols (like SSH-2) written before PSS's introduction.
* Use a relaxed version of PKCS#1 padding for signature verification
*/
const SIGNATURE_PKCS1 = 2;
const PADDING_RELAXED_PKCS1 = 5;
/**#@-*/
/**#@+
@ -283,22 +288,6 @@ class RSA
*/
var $mgfHLen;
/**
* Encryption mode
*
* @var int
* @access private
*/
var $encryptionMode = self::ENCRYPTION_OAEP;
/**
* Signature mode
*
* @var int
* @access private
*/
var $signatureMode = self::SIGNATURE_PSS;
/**
* Public Exponent
*
@ -394,10 +383,10 @@ class RSA
{
self::_initialize_static_variables();
$this->hash = new Hash('sha1');
$this->hash = new Hash('sha256');
$this->hLen = $this->hash->getLength();
$this->hashName = 'sha1';
$this->mgfHash = new Hash('sha1');
$this->hashName = 'sha256';
$this->mgfHash = new Hash('sha256');
$this->mgfHLen = $this->mgfHash->getLength();
}
@ -706,8 +695,6 @@ class RSA
$this->hLen = $key->hLen;
$this->sLen = $key->sLen;
$this->mgfHLen = $key->mgfHLen;
$this->encryptionMode = $key->encryptionMode;
$this->signatureMode = $key->signatureMode;
$this->password = $key->password;
if (is_object($key->hash)) {
@ -1233,7 +1220,7 @@ class RSA
/**
* Determines which hashing function should be used
*
* Used with signature production / verification and (if the encryption mode is self::ENCRYPTION_OAEP) encryption and
* Used with signature production / verification and (if the encryption mode is self::PADDING_OAEP) encryption and
* decryption. If $hash isn't supported, sha1 is used.
*
* @access public
@ -1253,8 +1240,8 @@ class RSA
$this->hashName = $hash;
break;
default:
$this->hash = new Hash('sha1');
$this->hashName = 'sha1';
$this->hash = new Hash('sha256');
$this->hashName = 'sha256';
}
$this->hLen = $this->hash->getLength();
}
@ -1262,7 +1249,7 @@ class RSA
/**
* Determines which hashing function should be used for the mask generation function
*
* The mask generation function is used by self::ENCRYPTION_OAEP and self::SIGNATURE_PSS and although it's
* The mask generation function is used by self::PADDING_OAEP and self::PADDING_PSS and although it's
* best if Hash and MGFHash are set to the same thing this is not a requirement.
*
* @access public
@ -1310,14 +1297,13 @@ class RSA
* @access private
* @param \phpseclib\Math\BigInteger $x
* @param int $xLen
* @throws \OutOfBoundsException if strlen($x) > $xLen
* @return string
* @return bool|string
*/
function _i2osp($x, $xLen)
{
$x = $x->toBytes();
if (strlen($x) > $xLen) {
throw new \OutOfBoundsException('Integer too large');
return false;
}
return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
}
@ -1470,13 +1456,12 @@ class RSA
*
* @access private
* @param \phpseclib\Math\BigInteger $m
* @throws \OutOfRangeException if $m < 0 or $m > $this->modulus
* @return \phpseclib\Math\BigInteger
* @return bool|\phpseclib\Math\BigInteger
*/
function _rsaep($m)
{
if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) {
throw new \OutOfRangeException('Message representative out of range');
return false;
}
return $this->_exponentiate($m);
}
@ -1488,13 +1473,12 @@ class RSA
*
* @access private
* @param \phpseclib\Math\BigInteger $c
* @throws \OutOfRangeException if $c < 0 or $c > $this->modulus
* @return \phpseclib\Math\BigInteger
* @return bool|\phpseclib\Math\BigInteger
*/
function _rsadp($c)
{
if ($c->compare(self::$zero) < 0 || $c->compare($this->modulus) > 0) {
throw new \OutOfRangeException('Ciphertext representative out of range');
return false;
}
return $this->_exponentiate($c);
}
@ -1506,13 +1490,12 @@ class RSA
*
* @access private
* @param \phpseclib\Math\BigInteger $m
* @throws \OutOfRangeException if $m < 0 or $m > $this->modulus
* @return \phpseclib\Math\BigInteger
* @return bool|\phpseclib\Math\BigInteger
*/
function _rsasp1($m)
{
if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) {
throw new \OutOfRangeException('Message representative out of range');
return false;
}
return $this->_exponentiate($m);
}
@ -1524,13 +1507,12 @@ class RSA
*
* @access private
* @param \phpseclib\Math\BigInteger $s
* @throws \OutOfRangeException if $s < 0 or $s > $this->modulus
* @return \phpseclib\Math\BigInteger
* @return bool|\phpseclib\Math\BigInteger
*/
function _rsavp1($s)
{
if ($s->compare(self::$zero) < 0 || $s->compare($this->modulus) > 0) {
throw new \OutOfRangeException('Signature representative out of range');
return false;
}
return $this->_exponentiate($s);
}
@ -1631,8 +1613,7 @@ class RSA
* @access private
* @param string $c
* @param string $l
* @throws \RuntimeException on decryption error
* @return string
* @return bool|string
*/
function _rsaes_oaep_decrypt($c, $l = '')
{
@ -1642,7 +1623,7 @@ class RSA
// be output.
if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
throw new \RuntimeException('Decryption error');
return false;
}
// RSA decryption
@ -1650,7 +1631,7 @@ class RSA
$c = $this->_os2ip($c);
$m = $this->_rsadp($c);
if ($m === false) {
throw new \RuntimeException('Decryption error');
return false;
}
$em = $this->_i2osp($m, $this->k);
@ -1667,11 +1648,11 @@ class RSA
$lHash2 = substr($db, 0, $this->hLen);
$m = substr($db, $this->hLen);
if ($lHash != $lHash2) {
throw new \RuntimeException('Decryption error');
return false;
}
$m = ltrim($m, chr(0));
if (ord($m[0]) != 1) {
throw new \RuntimeException('Decryption error');
return false;
}
// Output the message M
@ -1705,7 +1686,7 @@ class RSA
* @throws \OutOfBoundsException if strlen($m) > $this->k - 11
* @return string
*/
function _rsaes_pkcs1_v1_5_encrypt($m)
function _rsaes_pkcs1_v1_5_encrypt($m, $pkcs15_compat = false)
{
$mLen = strlen($m);
@ -1726,7 +1707,7 @@ class RSA
}
$type = 2;
// see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done
if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
if ($pkcs15_compat && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
$type = 1;
// "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF"
$ps = str_repeat("\xFF", $psLen);
@ -1761,15 +1742,14 @@ class RSA
*
* @access private
* @param string $c
* @throws \RuntimeException on decryption error
* @return string
* @return bool|string
*/
function _rsaes_pkcs1_v1_5_decrypt($c)
{
// Length checking
if (strlen($c) != $this->k) { // or if k < 11
throw new \RuntimeException('Decryption error');
return false;
}
// RSA decryption
@ -1778,21 +1758,21 @@ class RSA
$m = $this->_rsadp($c);
if ($m === false) {
throw new \RuntimeException('Decryption error');
return false;
}
$em = $this->_i2osp($m, $this->k);
// EME-PKCS1-v1_5 decoding
if (ord($em[0]) != 0 || ord($em[1]) > 2) {
throw new \RuntimeException('Decryption error');
return false;
}
$ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
$m = substr($em, strlen($ps) + 3);
if (strlen($ps) < 8) {
throw new \RuntimeException('Decryption error');
return false;
}
// Output M
@ -1820,7 +1800,7 @@ class RSA
$mHash = $this->hash->hash($m);
if ($emLen < $this->hLen + $sLen + 2) {
throw new \RuntimeException('Encoding error');
return false;
}
$salt = Random::string($sLen);
@ -1917,7 +1897,6 @@ class RSA
* @access private
* @param string $m
* @param string $s
* @throws \RuntimeException on invalid signature
* @return string
*/
function _rsassa_pss_verify($m, $s)
@ -1925,7 +1904,7 @@ class RSA
// Length checking
if (strlen($s) != $this->k) {
throw new \RuntimeException('Invalid signature');
return false;
}
// RSA verification
@ -1935,11 +1914,11 @@ class RSA
$s2 = $this->_os2ip($s);
$m2 = $this->_rsavp1($s2);
if ($m2 === false) {
throw new \RuntimeException('Invalid signature');
return false;
}
$em = $this->_i2osp($m2, $modBits >> 3);
if ($em === false) {
throw new \RuntimeException('Invalid signature');
return false;
}
// EMSA-PSS verification
@ -2013,9 +1992,11 @@ class RSA
{
// EMSA-PKCS1-v1_5 encoding
// If the encoding operation outputs "intended encoded message length too short," output "RSA modulus
// too short" and stop.
$em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
if ($em === false) {
throw new \LengthException('RSA modulus too short');
return false;
}
// RSA signature
@ -2036,16 +2017,15 @@ class RSA
*
* @access private
* @param string $m
* @throws \RuntimeException if the signature is invalid
* @throws \LengthException if the RSA modulus is too short
* @return string
* @return bool|string
*/
function _rsassa_pkcs1_v1_5_verify($m, $s)
{
// Length checking
if (strlen($s) != $this->k) {
throw new \RuntimeException('Invalid signature');
return false;
}
// RSA verification
@ -2053,17 +2033,20 @@ class RSA
$s = $this->_os2ip($s);
$m2 = $this->_rsavp1($s);
if ($m2 === false) {
throw new \RuntimeException('Invalid signature');
return false;
}
$em = $this->_i2osp($m2, $this->k);
if ($em === false) {
throw new \RuntimeException('Invalid signature');
return false;
}
// EMSA-PKCS1-v1_5 encoding
$em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
if ($em2 === false) {
// If the encoding operation outputs "intended encoded message length too short," output "RSA modulus
// too short" and stop.
try {
$em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
} catch (\LengthException $e) {
throw new \LengthException('RSA modulus too short');
}
@ -2072,55 +2055,133 @@ class RSA
}
/**
* Set Encryption Mode
* RSASSA-PKCS1-V1_5-VERIFY (relaxed matching)
*
* Valid values include self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1.
* Per {@link http://tools.ietf.org/html/rfc3447#page-43 RFC3447#page-43} PKCS1 v1.5
* specified the use BER encoding rather than DER encoding that PKCS1 v2.0 specified.
* This means that under rare conditions you can have a perfectly valid v1.5 signature
* that fails to validate with _rsassa_pkcs1_v1_5_verify(). PKCS1 v2.1 also recommends
* that if you're going to validate these types of signatures you "should indicate
* whether the underlying BER encoding is a DER encoding and hence whether the signature
* is valid with respect to the specification given in [PKCS1 v2.0+]". so if you do
* $rsa->getLastPadding() and get RSA::PADDING_RELAXED_PKCS1 back instead of
* RSA::PADDING_PKCS1... that means BER encoding was used.
*
* @access public
* @param int $mode
* @access private
* @param string $m
* @return bool|string
*/
function setEncryptionMode($mode)
function _rsassa_pkcs1_v1_5_relaxed_verify($m, $s)
{
$this->encryptionMode = $mode;
}
// Length checking
/**
* Set Signature Mode
*
* Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1
*
* @access public
* @param int $mode
*/
function setSignatureMode($mode)
{
$this->signatureMode = $mode;
if (strlen($s) != $this->k) {
return false;
}
// RSA verification
$s = $this->_os2ip($s);
$m2 = $this->_rsavp1($s);
if ($m2 === false) {
return false;
}
$em = $this->_i2osp($m2, $this->k);
if ($em === false) {
return false;
}
if ($this->_string_shift($em, 2) != "\0\1") {
return false;
}
$em = ltrim($em, "\xFF");
if ($this->_string_shift($em) != "\0") {
return false;
}
$asn1 = new ASN1();
$decoded = $asn1->decodeBER($em);
if (!is_array($decoded) || empty($decoded[0]) || strlen($em) > $decoded[0]['length']) {
return false;
}
$AlgorithmIdentifier = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'algorithm' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER),
'parameters' => array(
'type' => ASN1::TYPE_ANY,
'optional' => true
)
)
);
$DigestInfo = array(
'type' => ASN1::TYPE_SEQUENCE,
'children' => array(
'digestAlgorithm' => $AlgorithmIdentifier,
'digest' => array('type' => ASN1::TYPE_OCTET_STRING)
)
);
$oids = array(
'1.2.840.113549.2.2' => 'md2',
'1.2.840.113549.2.4' => 'md4', // from PKCS1 v1.5
'1.2.840.113549.2.5' => 'md5',
'1.3.14.3.2.26' => 'sha1',
'2.16.840.1.101.3.4.2.1' => 'sha256',
'2.16.840.1.101.3.4.2.2' => 'sha384',
'2.16.840.1.101.3.4.2.3' => 'sha512',
// from PKCS1 v2.2
//'2.16.840.1.101.3.4.2.5' => 'sha512/224',
//'2.16.840.1.101.3.4.2.6' => 'sha512/256',
);
$asn1->loadOIDs($oids);
$decoded = $asn1->asn1map($decoded[0], $DigestInfo);
if (!isset($decoded) || $decoded === false) {
return false;
}
if (!in_array($decoded['digestAlgorithm']['algorithm'], $oids)) {
return false;
}
$hash = new Hash($decoded['digestAlgorithm']['algorithm']);
$em = $hash->hash($m);
$em2 = base64_decode($decoded['digest']);
return $this->_equals($em, $em2);
}
/**
* Encryption
*
* Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
* Both self::PADDING_OAEP and self::PADDING_PKCS1 both place limits on how long $plaintext can be.
* If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
* be concatenated together.
*
* @see self::decrypt()
* @access public
* @param string $plaintext
* @param int $padding
* @return string
* @throws \LengthException if the RSA modulus is too short
*/
function encrypt($plaintext)
function encrypt($plaintext, $padding = self::PADDING_OAEP)
{
switch ($this->encryptionMode) {
case self::ENCRYPTION_NONE:
switch ($padding) {
case self::PADDING_NONE:
$plaintext = str_split($plaintext, $this->k);
$ciphertext = '';
foreach ($plaintext as $m) {
$ciphertext.= $this->_raw_encrypt($m);
}
return $ciphertext;
case self::ENCRYPTION_PKCS1:
case self::PADDING_PKCS15_COMPAT:
case self::PADDING_PKCS1:
$length = $this->k - 11;
if ($length <= 0) {
throw new \LengthException('RSA modulus too short (' . $this->k . ' bytes long; should be more than 11 bytes with PKCS1)');
@ -2129,14 +2190,14 @@ class RSA
$plaintext = str_split($plaintext, $length);
$ciphertext = '';
foreach ($plaintext as $m) {
$ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
$ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m, $padding == self::PADDING_PKCS15_COMPAT);
}
return $ciphertext;
//case self::ENCRYPTION_OAEP:
//case self::PADDING_OAEP:
default:
$length = $this->k - 2 * $this->hLen - 2;
if ($length <= 0) {
throw new \LengthException('RSA modulus too short (' . $this->k . ' bytes long; should be more than ' . (2 * $this->hLen - 2) . ' bytes with OAEP / ' . $this->hashName . ')');
throw new \LengthException('RSA modulus too short (' . $this->k . ' bytes long; should be more than ' . (2 * $this->hLen + 2) . ' bytes with OAEP / ' . $this->hashName . ')');
}
$plaintext = str_split($plaintext, $length);
@ -2154,9 +2215,10 @@ class RSA
* @see self::encrypt()
* @access public
* @param string $plaintext
* @param int|bool $padding
* @return string
*/
function decrypt($ciphertext)
function decrypt($ciphertext, $padding = self::PADDING_OAEP)
{
if ($this->k <= 0) {
return false;
@ -2167,14 +2229,14 @@ class RSA
$plaintext = '';
switch ($this->encryptionMode) {
case self::ENCRYPTION_NONE:
switch ($padding) {
case self::PADDING_NONE:
$decrypt = '_raw_encrypt';
break;
case self::ENCRYPTION_PKCS1:
case self::PADDING_PKCS1:
$decrypt = '_rsaes_pkcs1_v1_5_decrypt';
break;
//case self::ENCRYPTION_OAEP:
//case self::PADDING_OAEP:
default:
$decrypt = '_rsaes_oaep_decrypt';
}
@ -2196,18 +2258,20 @@ class RSA
* @see self::verify()
* @access public
* @param string $message
* @param int $padding
* @return string
*/
function sign($message)
function sign($message, $padding = self::PADDING_PSS)
{
if (empty($this->modulus) || empty($this->exponent)) {
return false;
}
switch ($this->signatureMode) {
case self::SIGNATURE_PKCS1:
switch ($padding) {
case self::PADDING_PKCS1:
case self::PADDING_RELAXED_PKCS1:
return $this->_rsassa_pkcs1_v1_5_sign($message);
//case self::SIGNATURE_PSS:
//case self::PADDING_PSS:
default:
return $this->_rsassa_pss_sign($message);
}
@ -2220,18 +2284,21 @@ class RSA
* @access public
* @param string $message
* @param string $signature
* @param int|bool $padding
* @return bool
*/
function verify($message, $signature)
function verify($message, $signature, $padding = self::PADDING_PSS)
{
if (empty($this->modulus) || empty($this->exponent)) {
return false;
}
switch ($this->signatureMode) {
case self::SIGNATURE_PKCS1:
switch ($padding) {
case self::PADDING_RELAXED_PKCS1:
return $this->_rsassa_pkcs1_v1_5_relaxed_verify($message, $signature);
case self::PADDING_PKCS1:
return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
//case self::SIGNATURE_PSS:
//case self::PADDING_PSS:
default:
return $this->_rsassa_pss_verify($message, $signature);
}

View File

@ -2149,8 +2149,7 @@ class X509
case 'sha384WithRSAEncryption':
case 'sha512WithRSAEncryption':
$rsa->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
if (!@$rsa->verify($signatureSubject, $signature)) {
if (!@$rsa->verify($signatureSubject, $signature, RSA::PADDING_PKCS1)) {
return false;
}
break;
@ -3671,9 +3670,8 @@ class X509
case 'sha384WithRSAEncryption':
case 'sha512WithRSAEncryption':
$key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
$key->setSignatureMode(RSA::SIGNATURE_PKCS1);
$this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject));
$this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject, RSA::PADDING_PKCS1));
return $this->currentCert;
default:
throw new UnsupportedAlgorithmException('Signature algorithm unsupported');

View File

@ -1302,8 +1302,7 @@ class SSH1
/*
$rsa = new RSA();
$rsa->load($key, 'raw');
$rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1);
return $rsa->encrypt($m);
return $rsa->encrypt($m, RSA::PADDING_PKCS1);
*/
// To quote from protocol-1.5.txt:

View File

@ -2306,8 +2306,7 @@ class SSH2
}
$packet = $part1 . chr(1) . $part2;
$privatekey->setSignatureMode(RSA::SIGNATURE_PKCS1);
$signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet));
$signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet), RSA::PADDING_PKCS1);
$signature = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($signature), $signature);
$packet.= pack('Na*', strlen($signature), $signature);
@ -4054,9 +4053,8 @@ class SSH2
$signature = $this->_string_shift($signature, $temp['length']);
$rsa = new RSA();
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
$rsa->load(array('e' => $e, 'n' => $n), 'raw');
if (!$rsa->verify($this->exchange_hash, $signature)) {
if (!$rsa->verify($this->exchange_hash, $signature, RSA::PADDING_PKCS1)) {
//user_error('Bad server signature');
return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
}

View File

@ -23,9 +23,8 @@ use phpseclib\System\SSH\Agent;
* Instantiation should only be performed by \phpseclib\System\SSH\Agent class.
* This could be thought of as implementing an interface that phpseclib\Crypt\RSA
* implements. ie. maybe a Net_SSH_Auth_PublicKey interface or something.
* The methods in this interface would be getPublicKey, setSignatureMode
* and sign since those are the methods phpseclib looks for to perform
* public key authentication.
* The methods in this interface would be getPublicKey and sign since those are the
* methods phpseclib looks for to perform public key authentication.
*
* @package SSH\Agent
* @author Jim Wigginton <terrafrost@php.net>
@ -114,30 +113,18 @@ class Identity
return !isset($format) ? $this->key->getPublicKey() : $this->key->getPublicKey($format);
}
/**
* Set Signature Mode
*
* Doesn't do anything as ssh-agent doesn't let you pick and choose the signature mode. ie.
* ssh-agent's only supported mode is \phpseclib\Crypt\RSA::SIGNATURE_PKCS1
*
* @param int $mode
* @access public
*/
function setSignatureMode($mode)
{
}
/**
* Create a signature
*
* See "2.6.2 Protocol 2 private key signature request"
*
* @param string $message
* @param int|bool $padding
* @return string
* @throws \RuntimeException on connection errors
* @access public
*/
function sign($message)
function sign($message, $padding = false)
{
// the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE
$packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, 0);

View File

@ -11,7 +11,7 @@ class Unit_Crypt_RSA_CreateKeyTest extends PhpseclibTestCase
{
public function testCreateKey()
{
extract(RSA::createKey(512));
extract(RSA::createKey(768));
$this->assertInstanceOf('\phpseclib\Crypt\RSA', $privatekey);
$this->assertInstanceOf('\phpseclib\Crypt\RSA', $publickey);
$this->assertNotEmpty("$privatekey");

View File

@ -32,17 +32,16 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
$rsa->load($privatekey);
$rsa->load($rsa->getPublicKey());
$rsa->setEncryptionMode(RSA::ENCRYPTION_NONE);
$expected = '105b92f59a87a8ad4da52c128b8c99491790ef5a54770119e0819060032fb9e772ed6772828329567f3d7e9472154c1530f8156ba7fd732f52ca1c06' .
'5a3f5ed8a96c442e4662e0464c97f133aed31262170201993085a589565d67cc9e727e0d087e3b225c8965203b271e38a499c92fc0d6502297eca712' .
'4d04bd467f6f1e7c';
$expected = pack('H*', $expected);
$result = $rsa->encrypt($plaintext);
$result = $rsa->encrypt($plaintext, RSA::PADDING_NONE);
$this->assertEquals($result, $expected);
$rsa->load($privatekey);
$this->assertEquals(trim($rsa->decrypt($result), "\0"), $plaintext);
$this->assertEquals(trim($rsa->decrypt($result, RSA::PADDING_NONE), "\0"), $plaintext);
}
/**
@ -51,6 +50,8 @@ U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
public function testPSSSigs()
{
$rsa = new RSA();
$rsa->setHash('sha1');
$rsa->setMGFHash('sha1');
$rsa->load('-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqGKukO1De7zhZj6+H0qtjTkVx
wTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFnc
@ -78,4 +79,24 @@ p0GbMJDyR4e9T04ZZwIDAQAB
$rsa->load(array('n' => $n, 'e' => $e));
$rsa->encrypt($plaintext);
}
public function testPKCS1LooseVerify()
{
$rsa = new RSA();
$rsa->load('-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAMuqkz8ij+ESAaNvgocVGmapjlrIldmhRo4h2NX4e6IXiCLTSxASQtY4
iqRnmyxqQSfaan2okTfQ6sP95bl8Qz8lgneW3ClC6RXG/wpJgsx7TXQ2kodlcKBF
m4k72G75QXhZ+I40ZG7cjBf1/9egakR0a0X0MpeOrKCzMBLv9+mpAgMBAAE=
-----END RSA PUBLIC KEY-----');
$message = base64_decode('MYIBLjAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNDA1MTUxNDM4MzRaMC8GCSqGSIb3DQEJBDEiBCBLzLIBGdOf0L2WRrIY' .
'9KTwiHnReBW48S9C7LNRaPp5mDCBwgYLKoZIhvcNAQkQAi8xgbIwga8wgawwgakEIJDB9ZGwihf+TaiwrHQNkNHkqbN8Nuws0e77QNObkvFZMIGEMHCkbjBs' .
'MQswCQYDVQQGEwJJVDEYMBYGA1UECgwPQXJ1YmFQRUMgUy5wLkEuMSEwHwYDVQQLDBhDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eUMxIDAeBgNVBAMMF0FydWJh' .
'UEVDIFMucC5BLiBORyBDQSAzAhAv4L3QcFssQNLDYN/Vu40R');
$sig = base64_decode('XDSZWw6IcUj8ICxRJf04HzF8stzoiFAZSR2a0Rw3ziZxTOT0/NVUYJO5+9TaaREXEgxuCLpgmA+6W2SWrrGoxbbNfaI90ZoKeOAws4IX+9RfiWuooibjKcvt' .
'GJYVVOCcjvQYxUUNbQ4EjCUonk3h7ECXfCCmWqbeq2LsyXeeYGE=');
$this->assertTrue($rsa->verify($message, $sig, RSA::PADDING_RELAXED_PKCS1));
}
}