[ 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-2019 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 Cose\Algorithm\Signature\RSA; 15 16 use function ceil; 17 use Cose\Algorithm\Signature\Signature; 18 use Cose\Key\Key; 19 use Cose\Key\RsaKey; 20 use function hash_equals; 21 use InvalidArgumentException; 22 use Jose\Component\Core\Util\BigInteger; 23 use Jose\Component\Core\Util\Hash; 24 use function mb_strlen; 25 use function mb_substr; 26 use function pack; 27 use function random_bytes; 28 use RuntimeException; 29 use function str_pad; 30 use function str_repeat; 31 32 /** 33 * @internal 34 */ 35 abstract class PSSRSA implements Signature 36 { 37 public function sign(string $data, Key $key): string 38 { 39 $key = $this->handleKey($key); 40 $modulusLength = mb_strlen($key->n(), '8bit'); 41 42 $em = $this->encodeEMSAPSS($data, 8 * $modulusLength - 1, $this->getHashAlgorithm()); 43 $message = BigInteger::createFromBinaryString($em); 44 $signature = $this->exponentiate($key, $message); 45 46 return $this->convertIntegerToOctetString($signature, $modulusLength); 47 } 48 49 public function verify(string $data, Key $key, string $signature): bool 50 { 51 $key = $this->handleKey($key); 52 $modulusLength = mb_strlen($key->n(), '8bit'); 53 54 if (mb_strlen($signature, '8bit') !== $modulusLength) { 55 throw new InvalidArgumentException('Invalid modulus length'); 56 } 57 $s2 = BigInteger::createFromBinaryString($signature); 58 $m2 = $this->exponentiate($key, $s2); 59 $em = $this->convertIntegerToOctetString($m2, $modulusLength); 60 $modBits = 8 * $modulusLength; 61 62 return $this->verifyEMSAPSS($data, $em, $modBits - 1, $this->getHashAlgorithm()); 63 } 64 65 private function handleKey(Key $key): RsaKey 66 { 67 return new RsaKey($key->getData()); 68 } 69 70 abstract protected function getHashAlgorithm(): Hash; 71 72 private function convertIntegerToOctetString(BigInteger $x, int $xLen): string 73 { 74 $x = $x->toBytes(); 75 if (mb_strlen($x, '8bit') > $xLen) { 76 throw new RuntimeException('Unable to convert the integer'); 77 } 78 79 return str_pad($x, $xLen, \chr(0), STR_PAD_LEFT); 80 } 81 82 /** 83 * MGF1. 84 */ 85 private function getMGF1(string $mgfSeed, int $maskLen, Hash $mgfHash): string 86 { 87 $t = ''; 88 $count = ceil($maskLen / $mgfHash->getLength()); 89 for ($i = 0; $i < $count; ++$i) { 90 $c = pack('N', $i); 91 $t .= $mgfHash->hash($mgfSeed.$c); 92 } 93 94 return mb_substr($t, 0, $maskLen, '8bit'); 95 } 96 97 /** 98 * EMSA-PSS-ENCODE. 99 */ 100 private function encodeEMSAPSS(string $message, int $modulusLength, Hash $hash): string 101 { 102 $emLen = ($modulusLength + 1) >> 3; 103 $sLen = $hash->getLength(); 104 $mHash = $hash->hash($message); 105 if ($emLen <= $hash->getLength() + $sLen + 2) { 106 throw new RuntimeException(); 107 } 108 $salt = random_bytes($sLen); 109 $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt; 110 $h = $hash->hash($m2); 111 $ps = str_repeat(\chr(0), $emLen - $sLen - $hash->getLength() - 2); 112 $db = $ps.\chr(1).$salt; 113 $dbMask = $this->getMGF1($h, $emLen - $hash->getLength() - 1, $hash); 114 $maskedDB = $db ^ $dbMask; 115 $maskedDB[0] = ~\chr(0xFF << ($modulusLength & 7)) & $maskedDB[0]; 116 117 return $maskedDB.$h.\chr(0xBC); 118 } 119 120 /** 121 * EMSA-PSS-VERIFY. 122 */ 123 private function verifyEMSAPSS(string $m, string $em, int $emBits, Hash $hash): bool 124 { 125 $emLen = ($emBits + 1) >> 3; 126 $sLen = $hash->getLength(); 127 $mHash = $hash->hash($m); 128 if ($emLen < $hash->getLength() + $sLen + 2) { 129 throw new InvalidArgumentException(); 130 } 131 if ($em[mb_strlen($em, '8bit') - 1] !== \chr(0xBC)) { 132 throw new InvalidArgumentException(); 133 } 134 $maskedDB = mb_substr($em, 0, -$hash->getLength() - 1, '8bit'); 135 $h = mb_substr($em, -$hash->getLength() - 1, $hash->getLength(), '8bit'); 136 $temp = \chr(0xFF << ($emBits & 7)); 137 if ((~$maskedDB[0] & $temp) !== $temp) { 138 throw new InvalidArgumentException(); 139 } 140 $dbMask = $this->getMGF1($h, $emLen - $hash->getLength() - 1, $hash/*MGF*/); 141 $db = $maskedDB ^ $dbMask; 142 $db[0] = ~\chr(0xFF << ($emBits & 7)) & $db[0]; 143 $temp = $emLen - $hash->getLength() - $sLen - 2; 144 if (mb_substr($db, 0, $temp, '8bit') !== str_repeat(\chr(0), $temp)) { 145 throw new InvalidArgumentException(); 146 } 147 if (1 !== \ord($db[$temp])) { 148 throw new InvalidArgumentException(); 149 } 150 $salt = mb_substr($db, $temp + 1, null, '8bit'); // should be $sLen long 151 $m2 = "\0\0\0\0\0\0\0\0".$mHash.$salt; 152 $h2 = $hash->hash($m2); 153 154 return hash_equals($h, $h2); 155 } 156 157 /** 158 * Exponentiate with or without Chinese Remainder Theorem. 159 * Operation with primes 'p' and 'q' is appox. 2x faster. 160 */ 161 public function exponentiate(RsaKey $key, BigInteger $c): BigInteger 162 { 163 if ($c->compare(BigInteger::createFromDecimal(0)) < 0 || $c->compare(BigInteger::createFromBinaryString($key->n())) > 0) { 164 throw new RuntimeException(); 165 } 166 if ($key->isPublic() || !$key->hasPrimes() || !$key->hasExponents() || !$key->hasCoefficient()) { 167 return $c->modPow(BigInteger::createFromBinaryString($key->e()), BigInteger::createFromBinaryString($key->n())); 168 } 169 170 $p = $key->primes()[0]; 171 $q = $key->primes()[1]; 172 $dP = $key->exponents()[0]; 173 $dQ = $key->exponents()[1]; 174 $qInv = BigInteger::createFromBinaryString($key->QInv()); 175 176 $m1 = $c->modPow($dP, $p); 177 $m2 = $c->modPow($dQ, $q); 178 $h = $qInv->multiply($m1->subtract($m2)->add($p))->mod($p); 179 180 return $m2->add($h->multiply($q)); 181 } 182 }
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 |