From 0b10c8b53366729417d6226ae89a665f9e2d61b6 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sat, 30 Mar 2024 19:03:49 +0100 Subject: [PATCH] Bugs/rsapss ssa oid (#54) * Add RSASSA-PSS encryption algorithm support Implemented support for the RSASSA-PSS encryption algorithm which involved adding new Classes and modifying existing ones. The RSAPSSSSAEncryptionAlgorithmIdentifier and RSASSAPSSPrivateKey Classes were added while the OneAsymmetricKey Class was updated to recognize the RSASSA-PSS encryption. This enhances compatibility with a wider range of RSA key types. * Update phpstan baseline file A new phpstan baseline rule has been added for the RSAPSSSSAEncryptionAlgorithmIdentifier class. This update aligns with the addition of the RSASSA-PSS encryption algorithm functionality, and improves the static code analysis and detection of potential issues. --- phpstan-baseline.neon | 5 + ...RSAPSSSSAEncryptionAlgorithmIdentifier.php | 63 +++++ .../Asymmetric/OneAsymmetricKey.php | 7 +- .../Asymmetric/RSA/RSASSAPSSPrivateKey.php | 226 ++++++++++++++++++ 4 files changed, 299 insertions(+), 2 deletions(-) create mode 100644 src/CryptoTypes/AlgorithmIdentifier/Asymmetric/RSAPSSSSAEncryptionAlgorithmIdentifier.php create mode 100644 src/CryptoTypes/Asymmetric/RSA/RSASSAPSSPrivateKey.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 0d8111a..5fd2f56 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -115,6 +115,11 @@ parameters: count: 1 path: src/CryptoTypes/AlgorithmIdentifier/Asymmetric/RSAEncryptionAlgorithmIdentifier.php + - + message: "#^Method SpomkyLabs\\\\Pki\\\\CryptoTypes\\\\AlgorithmIdentifier\\\\Asymmetric\\\\RSAPSSSSAEncryptionAlgorithmIdentifier\\:\\:paramsASN1\\(\\) never returns null so it can be removed from the return type\\.$#" + count: 1 + path: src/CryptoTypes/AlgorithmIdentifier/Asymmetric/RSAPSSSSAEncryptionAlgorithmIdentifier.php + - message: "#^Parameter \\#2 \\$initializationVector of method SpomkyLabs\\\\Pki\\\\CryptoTypes\\\\AlgorithmIdentifier\\\\Cipher\\\\CipherAlgorithmIdentifier\\:\\:__construct\\(\\) expects string, string\\|null given\\.$#" count: 1 diff --git a/src/CryptoTypes/AlgorithmIdentifier/Asymmetric/RSAPSSSSAEncryptionAlgorithmIdentifier.php b/src/CryptoTypes/AlgorithmIdentifier/Asymmetric/RSAPSSSSAEncryptionAlgorithmIdentifier.php new file mode 100644 index 0000000..34aebd0 --- /dev/null +++ b/src/CryptoTypes/AlgorithmIdentifier/Asymmetric/RSAPSSSSAEncryptionAlgorithmIdentifier.php @@ -0,0 +1,63 @@ +asNull(); + return self::create(); + } + + /** + * @return NullType + */ + protected function paramsASN1(): ?Element + { + return NullType::create(); + } +} diff --git a/src/CryptoTypes/Asymmetric/OneAsymmetricKey.php b/src/CryptoTypes/Asymmetric/OneAsymmetricKey.php index ec231a2..f7f29bf 100644 --- a/src/CryptoTypes/Asymmetric/OneAsymmetricKey.php +++ b/src/CryptoTypes/Asymmetric/OneAsymmetricKey.php @@ -24,6 +24,7 @@ use SpomkyLabs\Pki\CryptoTypes\Asymmetric\RFC8410\Curve448\Ed448PrivateKey; use SpomkyLabs\Pki\CryptoTypes\Asymmetric\RFC8410\Curve448\X448PrivateKey; use SpomkyLabs\Pki\CryptoTypes\Asymmetric\RSA\RSAPrivateKey; +use SpomkyLabs\Pki\CryptoTypes\Asymmetric\RSA\RSASSAPSSPrivateKey; use UnexpectedValueException; use function in_array; @@ -181,10 +182,12 @@ public function privateKey(): PrivateKey { $algo = $this->algorithmIdentifier(); switch ($algo->oid()) { - // RSA (including RSASSA-PSS) + // RSA case AlgorithmIdentifier::OID_RSA_ENCRYPTION: - case AlgorithmIdentifier::OID_RSASSA_PSS_ENCRYPTION: return RSAPrivateKey::fromDER($this->privateKeyData); + // RSASSA-PSS + case AlgorithmIdentifier::OID_RSASSA_PSS_ENCRYPTION: + return RSASSAPSSPrivateKey::fromDER($this->privateKeyData); // elliptic curve case AlgorithmIdentifier::OID_EC_PUBLIC_KEY: $pk = ECPrivateKey::fromDER($this->privateKeyData); diff --git a/src/CryptoTypes/Asymmetric/RSA/RSASSAPSSPrivateKey.php b/src/CryptoTypes/Asymmetric/RSA/RSASSAPSSPrivateKey.php new file mode 100644 index 0000000..89703e8 --- /dev/null +++ b/src/CryptoTypes/Asymmetric/RSA/RSASSAPSSPrivateKey.php @@ -0,0 +1,226 @@ +at(0) + ->asInteger() + ->intNumber(); + if ($version !== 0) { + throw new UnexpectedValueException('Version must be 0.'); + } + // helper function get integer from given index + $get_int = static fn ($idx) => $seq->at($idx) + ->asInteger() + ->number(); + $n = $get_int(1); + $e = $get_int(2); + $d = $get_int(3); + $p = $get_int(4); + $q = $get_int(5); + $dp = $get_int(6); + $dq = $get_int(7); + $qi = $get_int(8); + return self::create($n, $e, $d, $p, $q, $dp, $dq, $qi); + } + + /** + * Initialize from DER data. + */ + public static function fromDER(string $data): self + { + return self::fromASN1(UnspecifiedType::fromDER($data)->asSequence()); + } + + /** + * @see PrivateKey::fromPEM() + */ + public static function fromPEM(PEM $pem): self + { + $pk = parent::fromPEM($pem); + if (! ($pk instanceof self)) { + throw new UnexpectedValueException('Not an RSA private key.'); + } + return $pk; + } + + /** + * Get modulus. + * + * @return string Base 10 integer + */ + public function modulus(): string + { + return $this->modulus; + } + + /** + * Get public exponent. + * + * @return string Base 10 integer + */ + public function publicExponent(): string + { + return $this->publicExponent; + } + + /** + * Get private exponent. + * + * @return string Base 10 integer + */ + public function privateExponent(): string + { + return $this->privateExponent; + } + + /** + * Get first prime factor. + * + * @return string Base 10 integer + */ + public function prime1(): string + { + return $this->prime1; + } + + /** + * Get second prime factor. + * + * @return string Base 10 integer + */ + public function prime2(): string + { + return $this->prime2; + } + + /** + * Get first factor exponent. + * + * @return string Base 10 integer + */ + public function exponent1(): string + { + return $this->exponent1; + } + + /** + * Get second factor exponent. + * + * @return string Base 10 integer + */ + public function exponent2(): string + { + return $this->exponent2; + } + + /** + * Get CRT coefficient of the second factor. + * + * @return string Base 10 integer + */ + public function coefficient(): string + { + return $this->coefficient; + } + + public function algorithmIdentifier(): AlgorithmIdentifierType + { + return RSAPSSSSAEncryptionAlgorithmIdentifier::create(); + } + + /** + * @return RSAPublicKey + */ + public function publicKey(): PublicKey + { + return RSAPublicKey::create($this->modulus, $this->publicExponent); + } + + /** + * Generate ASN.1 structure. + */ + public function toASN1(): Sequence + { + return Sequence::create( + Integer::create(0), + Integer::create($this->modulus), + Integer::create($this->publicExponent), + Integer::create($this->privateExponent), + Integer::create($this->prime1), + Integer::create($this->prime2), + Integer::create($this->exponent1), + Integer::create($this->exponent2), + Integer::create($this->coefficient) + ); + } + + public function toDER(): string + { + return $this->toASN1() + ->toDER(); + } + + public function toPEM(): PEM + { + return PEM::create(PEM::TYPE_PRIVATE_KEY, $this->toDER()); + } +}