[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 declare(strict_types=1); 4 5 /* 6 * The MIT License (MIT) 7 * 8 * Copyright (c) 2014-2020 Spomky-Labs 9 * 10 * This software may be modified and distributed under the terms 11 * of the MIT license. See the LICENSE file for details. 12 */ 13 14 namespace Jose\Component\Signature\Algorithm\Util; 15 16 use function chr; 17 use InvalidArgumentException; 18 use Jose\Component\Core\Util\BigInteger; 19 use Jose\Component\Core\Util\Hash; 20 use Jose\Component\Core\Util\RSAKey; 21 use function ord; 22 use RuntimeException; 23 24 /** 25 * @internal 26 */ 27 class RSA 28 { 29 /** 30 * Probabilistic Signature Scheme. 31 */ 32 public const SIGNATURE_PSS = 1; 33 34 /** 35 * Use the PKCS#1. 36 */ 37 public const SIGNATURE_PKCS1 = 2; 38 39 /** 40 * @throws RuntimeException if the data cannot be signed 41 * @throws InvalidArgumentException if the signature mode is not supported 42 */ 43 public static function sign(RSAKey $key, string $message, string $hash, int $mode): string 44 { 45 switch ($mode) { 46 case self::SIGNATURE_PSS: 47 return self::signWithPSS($key, $message, $hash); 48 49 case self::SIGNATURE_PKCS1: 50 $result = openssl_sign($message, $signature, $key->toPEM(), $hash); 51 if (true !== $result) { 52 throw new RuntimeException('Unable to sign the data'); 53 } 54 55 return $signature; 56 57 default: 58 throw new InvalidArgumentException('Unsupported mode.'); 59 } 60 } 61 62 /** 63 * Create a signature. 64 */ 65 public static function signWithPSS(RSAKey $key, string $message, string $hash): string 66 { 67 $em = self::encodeEMSAPSS($message, 8 * $key->getModulusLength() - 1, Hash::$hash()); 68 $message = BigInteger::createFromBinaryString($em); 69 $signature = RSAKey::exponentiate($key, $message); 70 71 return self::convertIntegerToOctetString($signature, $key->getModulusLength()); 72 } 73 74 /** 75 * Create a signature. 76 * 77 * @deprecated Please use openssl_sign 78 */ 79 public static function signWithPKCS15(RSAKey $key, string $message, string $hash): string 80 { 81 $em = self::encodeEMSA15($message, $key->getModulusLength(), Hash::$hash()); 82 $message = BigInteger::createFromBinaryString($em); 83 $signature = RSAKey::exponentiate($key, $message); 84 85 return self::convertIntegerToOctetString($signature, $key->getModulusLength()); 86 } 87 88 /** 89 * @throws InvalidArgumentException if the signature mode is not supported 90 */ 91 public static function verify(RSAKey $key, string $message, string $signature, string $hash, int $mode): bool 92 { 93 switch ($mode) { 94 case self::SIGNATURE_PSS: 95 return self::verifyWithPSS($key, $message, $signature, $hash); 96 97 case self::SIGNATURE_PKCS1: 98 return 1 === openssl_verify($message, $signature, $key->toPEM(), $hash); 99 100 default: 101 throw new InvalidArgumentException('Unsupported mode.'); 102 } 103 } 104 105 /** 106 * Verifies a signature. 107 * 108 * @throws RuntimeException if the signature cannot be verified 109 */ 110 public static function verifyWithPSS(RSAKey $key, string $message, string $signature, string $hash): bool 111 { 112 if (mb_strlen($signature, '8bit') !== $key->getModulusLength()) { 113 throw new RuntimeException(); 114 } 115 $s2 = BigInteger::createFromBinaryString($signature); 116 $m2 = RSAKey::exponentiate($key, $s2); 117 $em = self::convertIntegerToOctetString($m2, $key->getModulusLength()); 118 $modBits = 8 * $key->getModulusLength(); 119 120 return self::verifyEMSAPSS($message, $em, $modBits - 1, Hash::$hash()); 121 } 122 123 /** 124 * Verifies a signature. 125 * 126 * @deprecated Please use openssl_sign 127 * 128 * @throws RuntimeException if the signature cannot be verified 129 */ 130 public static function verifyWithPKCS15(RSAKey $key, string $message, string $signature, string $hash): bool 131 { 132 if (mb_strlen($signature, '8bit') !== $key->getModulusLength()) { 133 throw new RuntimeException(); 134 } 135 $signature = BigInteger::createFromBinaryString($signature); 136 $m2 = RSAKey::exponentiate($key, $signature); 137 $em = self::convertIntegerToOctetString($m2, $key->getModulusLength()); 138 139 return hash_equals($em, self::encodeEMSA15($message, $key->getModulusLength(), Hash::$hash())); 140 } 141 142 /** 143 * @throws RuntimeException if the value cannot be converted 144 */ 145 private static function convertIntegerToOctetString(BigInteger $x, int $xLen): string 146 { 147 $x = $x->toBytes(); 148 if (mb_strlen($x, '8bit') > $xLen) { 149 throw new RuntimeException(); 150 } 151 152 return str_pad($x, $xLen, chr(0), STR_PAD_LEFT); 153 } 154 155 /** 156 * MGF1. 157 */ 158 private static function getMGF1(string $mgfSeed, int $maskLen, Hash $mgfHash): string 159 { 160 $t = ''; 161 $count = ceil($maskLen / $mgfHash->getLength()); 162 for ($i = 0; $i < $count; ++$i) { 163 $c = pack('N', $i); 164 $t .= $mgfHash->hash($mgfSeed.$c); 165 } 166 167 return mb_substr($t, 0, $maskLen, '8bit'); 168 } 169 170 /** 171 * EMSA-PSS-ENCODE. 172 * 173 * @throws RuntimeException if the message length is invalid 174 */ 175 private static function encodeEMSAPSS(string $message, int $modulusLength, Hash $hash): string 176 { 177 $emLen = ($modulusLength + 1) >> 3; 178 $sLen = $hash->getLength(); 179 $mHash = $hash->hash($message); 180 if ($emLen <= $hash->getLength() + $sLen + 2) { 181 throw new RuntimeException(); 182 } 183 $salt = random_bytes($sLen); 184 $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt; 185 $h = $hash->hash($m2); 186 $ps = str_repeat(chr(0), $emLen - $sLen - $hash->getLength() - 2); 187 $db = $ps.chr(1).$salt; 188 $dbMask = self::getMGF1($h, $emLen - $hash->getLength() - 1, $hash); 189 $maskedDB = $db ^ $dbMask; 190 $maskedDB[0] = ~chr(0xFF << ($modulusLength & 7)) & $maskedDB[0]; 191 $em = $maskedDB.$h.chr(0xBC); 192 193 return $em; 194 } 195 196 /** 197 * EMSA-PSS-VERIFY. 198 * 199 * @throws InvalidArgumentException if the signature cannot be verified 200 */ 201 private static function verifyEMSAPSS(string $m, string $em, int $emBits, Hash $hash): bool 202 { 203 $emLen = ($emBits + 1) >> 3; 204 $sLen = $hash->getLength(); 205 $mHash = $hash->hash($m); 206 if ($emLen < $hash->getLength() + $sLen + 2) { 207 throw new InvalidArgumentException(); 208 } 209 if ($em[mb_strlen($em, '8bit') - 1] !== chr(0xBC)) { 210 throw new InvalidArgumentException(); 211 } 212 $maskedDB = mb_substr($em, 0, -$hash->getLength() - 1, '8bit'); 213 $h = mb_substr($em, -$hash->getLength() - 1, $hash->getLength(), '8bit'); 214 $temp = chr(0xFF << ($emBits & 7)); 215 if ((~$maskedDB[0] & $temp) !== $temp) { 216 throw new InvalidArgumentException(); 217 } 218 $dbMask = self::getMGF1($h, $emLen - $hash->getLength() - 1, $hash/*MGF*/); 219 $db = $maskedDB ^ $dbMask; 220 $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0]; 221 $temp = $emLen - $hash->getLength() - $sLen - 2; 222 if (mb_substr($db, 0, $temp, '8bit') !== str_repeat(chr(0), $temp)) { 223 throw new InvalidArgumentException(); 224 } 225 if (1 !== ord($db[$temp])) { 226 throw new InvalidArgumentException(); 227 } 228 $salt = mb_substr($db, $temp + 1, null, '8bit'); // should be $sLen long 229 $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt; 230 $h2 = $hash->hash($m2); 231 232 return hash_equals($h, $h2); 233 } 234 235 /** 236 * @throws RuntimeException if the value cannot be encoded 237 */ 238 private static function encodeEMSA15(string $m, int $emBits, Hash $hash): string 239 { 240 $h = $hash->hash($m); 241 $t = $hash->t(); 242 $t .= $h; 243 $tLen = mb_strlen($t, '8bit'); 244 if ($emBits < $tLen + 11) { 245 throw new RuntimeException(); 246 } 247 $ps = str_repeat(chr(0xFF), $emBits - $tLen - 3); 248 249 return "\0\1{$ps}\0{$t}"; 250 } 251 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |