diff --git a/phpseclib/Crypt/RSA.php b/phpseclib/Crypt/RSA.php index 9d0fbfef..ad416945 100644 --- a/phpseclib/Crypt/RSA.php +++ b/phpseclib/Crypt/RSA.php @@ -618,6 +618,63 @@ 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"; + } + $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 * diff --git a/phpseclib/Crypt/RSA/PublicKey.php b/phpseclib/Crypt/RSA/PublicKey.php index ac25e2e5..e6afa49b 100644 --- a/phpseclib/Crypt/RSA/PublicKey.php +++ b/phpseclib/Crypt/RSA/PublicKey.php @@ -97,16 +97,28 @@ 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); } catch (\LengthException $e) { + $exception = true; + } + + try { + $em3 = $this->emsa_pkcs1_v1_5_encode_witout_null($m, $this->k); + } catch (\LengthException $e) { + $exception = true; + } + + if ($exception) { throw new \LengthException('RSA modulus too short'); } // Compare - return hash_equals($em, $em2); + return hash_equals($em, $em2) || hash_equals($em, $em3); } /** diff --git a/phpseclib/File/ASN1.php b/phpseclib/File/ASN1.php index 0edd61e8..3195b4ed 100644 --- a/phpseclib/File/ASN1.php +++ b/phpseclib/File/ASN1.php @@ -499,7 +499,9 @@ abstract class ASN1 return false; } $current['content'] = self::decodeTime(substr($content, $content_pos), $tag); + break; default: + return false; } $start+= $length; diff --git a/tests/Unit/Crypt/RSA/ModeTest.php b/tests/Unit/Crypt/RSA/ModeTest.php index c1202a51..248a431c 100644 --- a/tests/Unit/Crypt/RSA/ModeTest.php +++ b/tests/Unit/Crypt/RSA/ModeTest.php @@ -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)); + } } diff --git a/tests/Unit/File/ASN1Test.php b/tests/Unit/File/ASN1Test.php index a38e1bf3..b775c101 100644 --- a/tests/Unit/File/ASN1Test.php +++ b/tests/Unit/File/ASN1Test.php @@ -414,4 +414,30 @@ class Unit_File_ASN1Test extends PhpseclibTestCase $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]); + } }