mirror of
https://github.com/danog/phpseclib.git
synced 2024-12-14 01:57:20 +01:00
Merge branch '3.0'
This commit is contained in:
commit
1ed024c5a3
@ -618,6 +618,66 @@ abstract class RSA extends AsymmetricKey
|
||||
return $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* EMSA-PKCS1-V1_5-ENCODE (without NULL)
|
||||
*
|
||||
* Quoting https://tools.ietf.org/html/rfc8017#page-65,
|
||||
*
|
||||
* "The parameters field associated with id-sha1, id-sha224, id-sha256,
|
||||
* id-sha384, id-sha512, id-sha512/224, and id-sha512/256 should
|
||||
* generally be omitted, but if present, it shall have a value of type
|
||||
* NULL"
|
||||
*
|
||||
* @access private
|
||||
* @param string $m
|
||||
* @param int $emLen
|
||||
* @return string
|
||||
*/
|
||||
protected function emsa_pkcs1_v1_5_encode_without_null($m, $emLen)
|
||||
{
|
||||
$h = $this->hash->hash($m);
|
||||
|
||||
// see http://tools.ietf.org/html/rfc3447#page-43
|
||||
switch ($this->hash->getHash()) {
|
||||
case 'sha1':
|
||||
$t = "\x30\x1f\x30\x07\x06\x05\x2b\x0e\x03\x02\x1a\x04\x14";
|
||||
break;
|
||||
case 'sha256':
|
||||
$t = "\x30\x2f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x04\x20";
|
||||
break;
|
||||
case 'sha384':
|
||||
$t = "\x30\x3f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x04\x30";
|
||||
break;
|
||||
case 'sha512':
|
||||
$t = "\x30\x4f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x04\x40";
|
||||
break;
|
||||
// from https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf#page=40
|
||||
case 'sha224':
|
||||
$t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x04\x1c";
|
||||
break;
|
||||
case 'sha512/224':
|
||||
$t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x05\x04\x1c";
|
||||
break;
|
||||
case 'sha512/256':
|
||||
$t = "\x30\x2f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06\x04\x20";
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedAlgorithmException('md2 and md5 require NULLs');
|
||||
}
|
||||
$t.= $h;
|
||||
$tLen = strlen($t);
|
||||
|
||||
if ($emLen < $tLen + 11) {
|
||||
throw new \LengthException('Intended encoded message length too short');
|
||||
}
|
||||
|
||||
$ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
|
||||
|
||||
$em = "\0\1$ps\0$t";
|
||||
|
||||
return $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* MGF1
|
||||
*
|
||||
|
@ -20,6 +20,7 @@ use phpseclib3\Common\Functions\Strings;
|
||||
use phpseclib3\Crypt\Hash;
|
||||
use phpseclib3\Exception\NoKeyLoadedException;
|
||||
use phpseclib3\Exception\UnsupportedFormatException;
|
||||
use phpseclib3\Exception\UnsupportedAlgorithmException;
|
||||
use phpseclib3\Crypt\Random;
|
||||
use phpseclib3\Crypt\Common;
|
||||
use phpseclib3\File\ASN1\Maps\DigestInfo;
|
||||
@ -97,16 +98,32 @@ class PublicKey extends RSA implements Common\PublicKey
|
||||
|
||||
// EMSA-PKCS1-v1_5 encoding
|
||||
|
||||
$exception = 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);
|
||||
$r1 = hash_equals($em, $em2);
|
||||
} catch (\LengthException $e) {
|
||||
$exception = true;
|
||||
}
|
||||
|
||||
try {
|
||||
$em3 = $this->emsa_pkcs1_v1_5_encode_without_null($m, $this->k);
|
||||
$r2 = hash_equals($em, $em3);
|
||||
} catch (\LengthException $e) {
|
||||
$exception = true;
|
||||
} catch (UnsupportedAlgorithmException $e) {
|
||||
$r2 = false;
|
||||
}
|
||||
|
||||
if ($exception) {
|
||||
throw new \LengthException('RSA modulus too short');
|
||||
}
|
||||
|
||||
// Compare
|
||||
return hash_equals($em, $em2);
|
||||
return $r1 || $r2;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -188,6 +205,10 @@ class PublicKey extends RSA implements Common\PublicKey
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($decoded['digestAlgorithm']['parameters']) && $decoded['digestAlgorithm']['parameters'] !== ['null' => '']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$hash = $decoded['digestAlgorithm']['algorithm'];
|
||||
$hash = substr($hash, 0, 3) == 'id-' ?
|
||||
substr($hash, 3) :
|
||||
|
@ -239,7 +239,7 @@ abstract class ASN1
|
||||
$current = ['start' => $start];
|
||||
|
||||
$type = ord($encoded[$encoded_pos++]);
|
||||
$start++;
|
||||
$startOffset = 1;
|
||||
|
||||
$constructed = ($type >> 5) & 1;
|
||||
|
||||
@ -249,13 +249,20 @@ abstract class ASN1
|
||||
// process septets (since the eighth bit is ignored, it's not an octet)
|
||||
do {
|
||||
$temp = ord($encoded[$encoded_pos++]);
|
||||
$startOffset++;
|
||||
$loop = $temp >> 7;
|
||||
$tag <<= 7;
|
||||
$tag |= $temp & 0x7F;
|
||||
$start++;
|
||||
$temp &= 0x7F;
|
||||
// "bits 7 to 1 of the first subsequent octet shall not all be zero"
|
||||
if ($startOffset == 2 && $temp == 0) {
|
||||
return false;
|
||||
}
|
||||
$tag |= $temp;
|
||||
} while ($loop);
|
||||
}
|
||||
|
||||
$start+= $startOffset;
|
||||
|
||||
// Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13
|
||||
$length = ord($encoded[$encoded_pos++]);
|
||||
$start++;
|
||||
@ -349,13 +356,16 @@ abstract class ASN1
|
||||
switch ($tag) {
|
||||
case self::TYPE_BOOLEAN:
|
||||
// "The contents octets shall consist of a single octet." -- paragraph 8.2.1
|
||||
//if (strlen($content) != 1) {
|
||||
// return false;
|
||||
//}
|
||||
if ($constructed || strlen($content) != 1) {
|
||||
return false;
|
||||
}
|
||||
$current['content'] = (bool) ord($content[$content_pos]);
|
||||
break;
|
||||
case self::TYPE_INTEGER:
|
||||
case self::TYPE_ENUMERATED:
|
||||
if ($constructed) {
|
||||
return false;
|
||||
}
|
||||
$current['content'] = new BigInteger(substr($content, $content_pos), -256);
|
||||
break;
|
||||
case self::TYPE_REAL: // not currently supported
|
||||
@ -375,15 +385,15 @@ abstract class ASN1
|
||||
$last = count($temp) - 1;
|
||||
for ($i = 0; $i < $last; $i++) {
|
||||
// all subtags should be bit strings
|
||||
//if ($temp[$i]['type'] != self::TYPE_BIT_STRING) {
|
||||
// return false;
|
||||
//}
|
||||
if ($temp[$i]['type'] != self::TYPE_BIT_STRING) {
|
||||
return false;
|
||||
}
|
||||
$current['content'].= substr($temp[$i]['content'], 1);
|
||||
}
|
||||
// all subtags should be bit strings
|
||||
//if ($temp[$last]['type'] != self::TYPE_BIT_STRING) {
|
||||
// return false;
|
||||
//}
|
||||
if ($temp[$last]['type'] != self::TYPE_BIT_STRING) {
|
||||
return false;
|
||||
}
|
||||
$current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1);
|
||||
}
|
||||
break;
|
||||
@ -400,9 +410,9 @@ abstract class ASN1
|
||||
}
|
||||
$content_pos += $temp['length'];
|
||||
// all subtags should be octet strings
|
||||
//if ($temp['type'] != self::TYPE_OCTET_STRING) {
|
||||
// return false;
|
||||
//}
|
||||
if ($temp['type'] != self::TYPE_OCTET_STRING) {
|
||||
return false;
|
||||
}
|
||||
$current['content'].= $temp['content'];
|
||||
$length+= $temp['length'];
|
||||
}
|
||||
@ -413,12 +423,15 @@ abstract class ASN1
|
||||
break;
|
||||
case self::TYPE_NULL:
|
||||
// "The contents octets shall not contain any octets." -- paragraph 8.8.2
|
||||
//if (strlen($content)) {
|
||||
// return false;
|
||||
//}
|
||||
if ($constructed || strlen($content)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case self::TYPE_SEQUENCE:
|
||||
case self::TYPE_SET:
|
||||
if (!$constructed) {
|
||||
return false;
|
||||
}
|
||||
$offset = 0;
|
||||
$current['content'] = [];
|
||||
$content_len = strlen($content);
|
||||
@ -439,7 +452,13 @@ abstract class ASN1
|
||||
}
|
||||
break;
|
||||
case self::TYPE_OBJECT_IDENTIFIER:
|
||||
if ($constructed) {
|
||||
return false;
|
||||
}
|
||||
$current['content'] = self::decodeOID(substr($content, $content_pos));
|
||||
if ($current['content'] === false) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
/* Each character string type shall be encoded as if it had been declared:
|
||||
[UNIVERSAL x] IMPLICIT OCTET STRING
|
||||
@ -469,12 +488,20 @@ abstract class ASN1
|
||||
case self::TYPE_UTF8_STRING:
|
||||
// ????
|
||||
case self::TYPE_BMP_STRING:
|
||||
if ($constructed) {
|
||||
return false;
|
||||
}
|
||||
$current['content'] = substr($content, $content_pos);
|
||||
break;
|
||||
case self::TYPE_UTC_TIME:
|
||||
case self::TYPE_GENERALIZED_TIME:
|
||||
if ($constructed) {
|
||||
return false;
|
||||
}
|
||||
$current['content'] = self::decodeTime(substr($content, $content_pos), $tag);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
$start+= $length;
|
||||
@ -1143,6 +1170,11 @@ abstract class ASN1
|
||||
$oid = [];
|
||||
$pos = 0;
|
||||
$len = strlen($content);
|
||||
|
||||
if (ord($content[$len - 1]) & 0x80) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$n = new BigInteger();
|
||||
while ($pos < $len) {
|
||||
$temp = ord($content[$pos++]);
|
||||
|
@ -182,4 +182,25 @@ HERE;
|
||||
$this->assertSame(6, $rsa->getSaltLength());
|
||||
$this->assertEquals('sha1', $rsa->getMGFHash());
|
||||
}
|
||||
|
||||
public function testPKCS1SigWithoutNull()
|
||||
{
|
||||
$rsa = PublicKeyLoader::load([
|
||||
'n' => new BigInteger('0xE932AC92252F585B3A80A4DD76A897C8B7652952FE788F6EC8DD640587A1EE5647670A8AD
|
||||
4C2BE0F9FA6E49C605ADF77B5174230AF7BD50E5D6D6D6D28CCF0A886A514CC72E51D209CC7
|
||||
72A52EF419F6A953F3135929588EBE9B351FCA61CED78F346FE00DBB6306E5C2A4C6DFC3779
|
||||
AF85AB417371CF34D8387B9B30AE46D7A5FF5A655B8D8455F1B94AE736989D60A6F2FD5CADB
|
||||
FFBD504C5A756A2E6BB5CECC13BCA7503F6DF8B52ACE5C410997E98809DB4DC30D943DE4E81
|
||||
2A47553DCE54844A78E36401D13F77DC650619FED88D8B3926E3D8E319C80C744779AC5D6AB
|
||||
E252896950917476ECE5E8FC27D5F053D6018D91B502C4787558A002B9283DA7', 16),
|
||||
'e' => new BigInteger('3')
|
||||
]);
|
||||
|
||||
$message = 'hello world!';
|
||||
$signature = pack('H*', 'a0073057133ff3758e7e111b4d7441f1d8cbe4b2dd5ee4316a14264290dee5ed7f175716639bd9bb43a14e4f9fcb9e84dedd35e2205caac04828b2c053f68176d971ea88534dd2eeec903043c3469fc69c206b2a8694fd262488441ed8852280c3d4994e9d42bd1d575c7024095f1a20665925c2175e089c0d731471f6cc145404edf5559fd2276e45e448086f71c78d0cc6628fad394a34e51e8c10bc39bfe09ed2f5f742cc68bee899d0a41e4c75b7b80afd1c321d89ccd9fe8197c44624d91cc935dfa48de3c201099b5b417be748aef29248527e8bbb173cab76b48478d4177b338fe1f1244e64d7d23f07add560d5ad50b68d6649a49d7bc3db686daaa7');
|
||||
|
||||
$rsa = $rsa->withPadding(RSA::SIGNATURE_PKCS1);
|
||||
//$rsa = $rsa->withHash('sha256');
|
||||
$this->assertTrue($rsa->verify($message, $signature));
|
||||
}
|
||||
}
|
||||
|
@ -392,4 +392,52 @@ class Unit_File_ASN1Test extends PhpseclibTestCase
|
||||
|
||||
$this->assertIsArray($a);
|
||||
}
|
||||
|
||||
public function testNullGarbage()
|
||||
{
|
||||
$em = pack('H*', '3080305c0609608648016503040201054f8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888804207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9');
|
||||
$decoded = ASN1::decodeBER($em);
|
||||
$this->assertFalse($decoded[0]);
|
||||
|
||||
$em = pack('H*', '3080307f0609608648016503040201057288888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888804207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca90000');
|
||||
$decoded = ASN1::decodeBER($em);
|
||||
$this->assertFalse($decoded[0]);
|
||||
}
|
||||
|
||||
public function testOIDGarbage()
|
||||
{
|
||||
$em = pack('H*', '3080305c065860864801650304020188888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888050004207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9');
|
||||
$decoded = ASN1::decodeBER($em);
|
||||
$this->assertFalse($decoded[0]);
|
||||
|
||||
$em = pack('H*', '3080307f067d608648016503040201888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888804207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9');
|
||||
$decoded = ASN1::decodeBER($em);
|
||||
$this->assertFalse($decoded[0]);
|
||||
}
|
||||
|
||||
public function testConstructedMismatch()
|
||||
{
|
||||
$em = pack('H*', '1031300d0609608648016503040201050004207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9');
|
||||
$decoded = ASN1::decodeBER($em);
|
||||
$this->assertFalse($decoded[0]);
|
||||
|
||||
$em = pack('H*', '3031100d0609608648016503040201050004207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9');
|
||||
$decoded = ASN1::decodeBER($em);
|
||||
$this->assertFalse($decoded[0]);
|
||||
|
||||
$em = pack('H*', '3031300d2609608648016503040201050004207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9');
|
||||
$decoded = ASN1::decodeBER($em);
|
||||
$this->assertFalse($decoded[0]);
|
||||
|
||||
$em = pack('H*', '3031300d06096086480165030402012d0004207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9');
|
||||
$decoded = ASN1::decodeBER($em);
|
||||
$this->assertFalse($decoded[0]);
|
||||
}
|
||||
|
||||
public function testBadTagSecondOctet()
|
||||
{
|
||||
$em = pack('H*', '3033300f1f808080060960864801650304020104207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9');
|
||||
$decoded = ASN1::decodeBER($em);
|
||||
$this->assertFalse($decoded[0]);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user