diff --git a/example/src/Auth.php b/example/src/Auth.php index 9d1d2e8..160262e 100644 --- a/example/src/Auth.php +++ b/example/src/Auth.php @@ -88,14 +88,13 @@ public function getNonce() private function getPrincipalNameFromCertificate(X509 $userCertificate): string { + $givenName = CertificateData::getSubjectGivenName($userCertificate); $surname = CertificateData::getSubjectSurname($userCertificate); - $givenname = CertificateData::getSubjectGivenName($userCertificate); - if ($surname && $givenname) { - $principalName = $givenname . " " . $surname; + if ($givenName && $surname) { + return $givenName . " " . $surname; } else { - $principalName = CertificateData::getSubjectCN($userCertificate); + return CertificateData::getSubjectCN($userCertificate); } - return $principalName; } /** diff --git a/example/tpl/welcome.phtml b/example/tpl/welcome.phtml index 409b8a3..6901bbe 100644 --- a/example/tpl/welcome.phtml +++ b/example/tpl/welcome.phtml @@ -1,2 +1,2 @@ -

Hello

+

Hello

Logout

\ No newline at end of file diff --git a/src/authtoken/WebEidAuthToken.php b/src/authtoken/WebEidAuthToken.php index 1529868..3cf1e19 100644 --- a/src/authtoken/WebEidAuthToken.php +++ b/src/authtoken/WebEidAuthToken.php @@ -48,10 +48,6 @@ class WebEidAuthToken * @var string Format */ private ?string $format = null; - /** - * @var string App version - */ - private ?string $appVersion = null; public function __construct(string $authenticationTokenJSON) { @@ -76,10 +72,6 @@ public function __construct(string $authenticationTokenJSON) if (isset($jsonDecoded['format'])) { $this->format = $this->filterString('format', $jsonDecoded['format']); } - // appVersion - if (isset($jsonDecoded['appVersion'])) { - $this->appVersion = $this->filterString('appVersion', $jsonDecoded['appVersion']); - } } public function getUnverifiedCertificate(): ?string @@ -102,11 +94,6 @@ public function getFormat(): ?string return $this->format; } - public function getAppVersion(): ?string - { - return $this->appVersion; - } - private function filterString(string $key, $data): string { $type = gettype($data); diff --git a/src/certificate/CertificateData.php b/src/certificate/CertificateData.php index 666ad7a..e05a1ba 100644 --- a/src/certificate/CertificateData.php +++ b/src/certificate/CertificateData.php @@ -80,15 +80,14 @@ public static function getSubjectCountryCode(X509 $certificate): ?string /** * Get specified subject field from x509 certificate * - * @return string + * @return ?string */ private static function getField(X509 $certificate, string $fieldId): ?string { $result = $certificate->getSubjectDNProp($fieldId); if ($result) { - return join(" ", $result); - } - else { + return join(", ", $result); + } else { return null; } } diff --git a/src/ocsp/OcspRequest.php b/src/ocsp/OcspRequest.php index 2d7f4ff..9765d87 100644 --- a/src/ocsp/OcspRequest.php +++ b/src/ocsp/OcspRequest.php @@ -32,7 +32,7 @@ class OcspRequest { - private array $ocspRequest = []; + private array $ocspRequest; public function __construct() { @@ -41,8 +41,6 @@ public function __construct() $this->ocspRequest = [ "tbsRequest" => [ "version" => "v1", - "requestList" => [], - "requestExtensions" => [], ], ]; } @@ -62,18 +60,15 @@ public function addNonceExtension(string $nonce): void "critical" => false, "extnValue" => ASN1::encodeDER($nonce, ['type' => ASN1::TYPE_OCTET_STRING]), ]; - $this->ocspRequest["tbsRequest"][ - "requestExtensions" - ][] = $nonceExtension; + $this->ocspRequest["tbsRequest"]["requestExtensions"][] = $nonceExtension; } /** * @copyright 2022 Petr Muzikant pmuzikant@email.cz */ - public function getNonceExtension(): string + public function getNonceExtension(): ?string { - // TODO: the ?? '' is here only for v1.0 API compatibility. Remove this in version 1.2 and change the return type to ?string. - return AsnUtil::decodeNonceExtension($this->ocspRequest["tbsRequest"]["requestExtensions"]) ?? ''; + return AsnUtil::decodeNonceExtension($this->ocspRequest["tbsRequest"]["requestExtensions"] ?? []); } public function getEncodeDer(): string diff --git a/src/validator/AuthTokenSignatureValidator.php b/src/validator/AuthTokenSignatureValidator.php index 0e4a026..b3fa1ba 100644 --- a/src/validator/AuthTokenSignatureValidator.php +++ b/src/validator/AuthTokenSignatureValidator.php @@ -54,19 +54,19 @@ public function __construct(Uri $siteOrigin) public function validate(string $algorithm, string $signature, $publicKey, string $currentChallengeNonce): void { - $this->requireNotEmpty($algorithm, "algorithm"); - $this->requireNotEmpty($signature, "signature"); + if (empty($currentChallengeNonce)) { + throw new ChallengeNullOrEmptyException(); + } if (is_null($publicKey)) { throw new InvalidArgumentException("Public key is null"); } - if (empty($currentChallengeNonce)) { - throw new ChallengeNullOrEmptyException(); - } + $this->requireNotEmpty($algorithm, "algorithm"); + $this->requireNotEmpty($signature, "signature"); if (!in_array($algorithm, self::ALLOWED_SIGNATURE_ALGORITHMS)) { - throw new AuthTokenParseException("Invalid signature algorithm"); + throw new AuthTokenParseException("Unsupported signature algorithm"); } $decodedSignature = base64_decode($signature); diff --git a/src/validator/ocsp/OcspRequestBuilder.php b/src/validator/ocsp/OcspRequestBuilder.php index 1662f59..0ea5d70 100644 --- a/src/validator/ocsp/OcspRequestBuilder.php +++ b/src/validator/ocsp/OcspRequestBuilder.php @@ -26,6 +26,7 @@ namespace web_eid\web_eid_authtoken_validation_php\validator\ocsp; +use InvalidArgumentException; use web_eid\web_eid_authtoken_validation_php\ocsp\OcspRequest; use web_eid\web_eid_authtoken_validation_php\util\SecureRandom; @@ -58,6 +59,9 @@ public function enableOcspNonce(bool $ocspNonceEnabled): OcspRequestBuilder public function build(): OcspRequest { $ocspRequest = new OcspRequest(); + if (is_null($this->certificateId)) { + throw new InvalidArgumentException("Certificate Id must not be null"); + } $ocspRequest->addCertificateId($this->certificateId); if ($this->ocspNonceEnabled) { diff --git a/src/validator/ocsp/OcspServiceProvider.php b/src/validator/ocsp/OcspServiceProvider.php index f464210..8277d9d 100644 --- a/src/validator/ocsp/OcspServiceProvider.php +++ b/src/validator/ocsp/OcspServiceProvider.php @@ -26,6 +26,7 @@ namespace web_eid\web_eid_authtoken_validation_php\validator\ocsp; +use InvalidArgumentException; use phpseclib3\File\X509; use web_eid\web_eid_authtoken_validation_php\validator\ocsp\service\AiaOcspService; use web_eid\web_eid_authtoken_validation_php\validator\ocsp\service\AiaOcspServiceConfiguration; @@ -41,7 +42,8 @@ class OcspServiceProvider public function __construct(?DesignatedOcspServiceConfiguration $designatedOcspServiceConfiguration, AiaOcspServiceConfiguration $aiaOcspServiceConfiguration) { $this->designatedOcspService = !is_null($designatedOcspServiceConfiguration) ? new DesignatedOcspService($designatedOcspServiceConfiguration) : null; - $this->aiaOcspServiceConfiguration = $aiaOcspServiceConfiguration; + $this->aiaOcspServiceConfiguration = $aiaOcspServiceConfiguration ?? throw new InvalidArgumentException("AIA Ocsp Service Configuration must not be null"); + } /** diff --git a/src/validator/ocsp/OcspUrl.php b/src/validator/ocsp/OcspUrl.php index 6b08c90..539c249 100644 --- a/src/validator/ocsp/OcspUrl.php +++ b/src/validator/ocsp/OcspUrl.php @@ -28,6 +28,7 @@ use phpseclib3\File\X509; use GuzzleHttp\Psr7\Uri; use Exception; +use InvalidArgumentException; final class OcspUrl { @@ -43,6 +44,9 @@ public function __construct() */ public static function getOcspUri(X509 $certificate): ?Uri { + if (is_null($certificate)) { + throw new InvalidArgumentException("Certificate must not be null"); + } $authorityInformationAccess = $certificate->getExtension("id-pe-authorityInfoAccess"); if ($authorityInformationAccess) { foreach ($authorityInformationAccess as $accessDescription) { diff --git a/src/validator/ocsp/service/AiaOcspService.php b/src/validator/ocsp/service/AiaOcspService.php index 5b52ec4..a938ee2 100644 --- a/src/validator/ocsp/service/AiaOcspService.php +++ b/src/validator/ocsp/service/AiaOcspService.php @@ -35,6 +35,7 @@ use DateTime; use web_eid\web_eid_authtoken_validation_php\validator\ocsp\OcspResponseValidator; use Exception; +use InvalidArgumentException; /** * An OCSP service that uses the responders from the Certificates' Authority Information Access (AIA) extension. @@ -48,6 +49,9 @@ class AiaOcspService implements OcspService public function __construct(AiaOcspServiceConfiguration $configuration, X509 $certificate) { + if (is_null($configuration)) { + throw new InvalidArgumentException("Configuration cannot be null"); + } $this->url = self::getOcspAiaUrlFromCertificate($certificate); $this->trustedCACertificates = $configuration->getTrustedCACertificates(); $this->supportsNonce = !in_array($this->url->jsonSerialize(), $configuration->getNonceDisabledOcspUrls()->getUrlsArray()); diff --git a/tests/authtoken/WebEidAuthTokenTest.php b/tests/authtoken/WebEidAuthTokenTest.php index 5027ff9..bdc76a4 100644 --- a/tests/authtoken/WebEidAuthTokenTest.php +++ b/tests/authtoken/WebEidAuthTokenTest.php @@ -48,7 +48,6 @@ public function testValidateAuthTokenParameters(): void $this->assertEquals("RS256", $authToken->getAlgorithm()); $this->assertEquals("HBjNXIaUskXbfhzYQHvwjKDUWfNu4yxXZh", $authToken->getSignature()); $this->assertEquals("web-eid:1.0", $authToken->getFormat()); - $this->assertEquals("https://web-eid.eu/web-eid-app/releases/v2.0.0", $authToken->getAppVersion()); } public function testWhenNotAuthToken(): void diff --git a/tests/certificate/CertificateDataTest.php b/tests/certificate/CertificateDataTest.php index 0be5c92..30d0224 100644 --- a/tests/certificate/CertificateDataTest.php +++ b/tests/certificate/CertificateDataTest.php @@ -49,25 +49,14 @@ public function testWhenOrganizationCertificateThenSubjectCNAndIdCodeAndCountryC $this->assertEquals("EE", CertificateData::getSubjectCountryCode($cert)); } - public function testWhenOrganizationCertificateThenSubjectGivenNameAndSurnameAreNull(): void - { - $cert = Certificates::getOrganizationCert(); - $this->assertEquals(null, CertificateData::getSubjectGivenName($cert)); - $this->assertEquals(null, CertificateData::getSubjectSurname($cert)); - } - - public function testWhenOrganizationCertificateThenSucceeds(): void + public function testWhenOrganizationCertificateThenSubjectGivenNameAndSurnameAreEmptyAndSubjectCNSucceeds(): void { $cert = Certificates::getOrganizationCert(); + $givenName = CertificateData::getSubjectGivenName($cert); $surname = CertificateData::getSubjectSurname($cert); - $givenname = CertificateData::getSubjectGivenName($cert); - if ($surname && $givenname) { - $principalName = $givenname . " " . $surname; - } else { - $principalName = CertificateData::getSubjectCN($cert); - } - + $this->assertEmpty($givenName); + $this->assertEmpty($surname); + $principalName = CertificateData::getSubjectCN($cert); $this->assertEquals("Testijad.ee isikutuvastus", $principalName); - } } diff --git a/tests/ocsp/OcspRequestTest.php b/tests/ocsp/OcspRequestTest.php index f37324a..cbcc705 100644 --- a/tests/ocsp/OcspRequestTest.php +++ b/tests/ocsp/OcspRequestTest.php @@ -39,8 +39,6 @@ private function getRequest(): array return [ 'tbsRequest' => [ 'version' => 'v1', - 'requestList' => [], - 'requestExtensions' => [], ], ]; } @@ -101,4 +99,29 @@ public function testWhenGetNonceExtensionSuccess(): void $this->assertEquals("nonce", $request->getNonceExtension()); } + + public function testBinaryNonce(): void + { + // Create a nonce with a mixture of potentially problematic bytes, + // null bytes, control characters and high-byte values (above 0x7F), + // which are common sources of string decoding problems. + $nonce = "\0\1\2\3\4\5\6\7\x08\x09\x10\0" + . "\x0A\x0D\x1B" + . "\xE2\x82\xAC" + . "\xFF"; + $request = new OcspRequest(); + $request->addNonceExtension($nonce); + + $this->assertEquals($nonce, $request->getNonceExtension()); + } + + public function testOidNonce(): void + { + // Create a nonce that contains DER-encoded OID for SHA-256: 2.16.840.1.101.3.4.2.1. + $nonce = "\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01"; + $request = new OcspRequest(); + $request->addNonceExtension($nonce); + + $this->assertEquals($nonce, $request->getNonceExtension()); + } } diff --git a/tests/validator/AuthTokenAlgorithmTest.php b/tests/validator/AuthTokenAlgorithmTest.php index 3a8c1bd..fbe51b7 100644 --- a/tests/validator/AuthTokenAlgorithmTest.php +++ b/tests/validator/AuthTokenAlgorithmTest.php @@ -35,7 +35,7 @@ public function testWhenAlgorithmNoneThenValidationFails(): void $authToken = $this->replaceTokenField(self::VALID_AUTH_TOKEN, "algorithm", "NONE"); $this->expectException(AuthTokenParseException::class); - $this->expectExceptionMessage("Invalid signature algorithm"); + $this->expectExceptionMessage("Unsupported signature algorithm"); $this->validator->validate($authToken, self::VALID_AUTH_TOKEN); } @@ -53,7 +53,7 @@ public function testWhenAlgorithmInvalidThenParsingFails(): void $authToken = $this->replaceTokenField(self::VALID_AUTH_TOKEN, "algorithm", "\\u0000\\t\\ninvalid"); $this->expectException(AuthTokenParseException::class); - $this->expectExceptionMessage("Invalid signature algorithm"); + $this->expectExceptionMessage("Unsupported signature algorithm"); $this->validator->validate($authToken, self::VALID_AUTH_TOKEN); } }