diff --git a/composer.lock b/composer.lock index e63e8450c..5cc3981ad 100644 --- a/composer.lock +++ b/composer.lock @@ -5836,16 +5836,16 @@ }, { "name": "web-auth/cose-lib", - "version": "v3.3.1", + "version": "v3.3.9", "source": { "type": "git", "url": "https://github.com/web-auth/cose-lib.git", - "reference": "eea6fae63ff5c81bf98c115b1be5f38a69682c16" + "reference": "ed172d2dc1a6b87b5c644c07c118cd30c1b3819b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/web-auth/cose-lib/zipball/eea6fae63ff5c81bf98c115b1be5f38a69682c16", - "reference": "eea6fae63ff5c81bf98c115b1be5f38a69682c16", + "url": "https://api.github.com/repos/web-auth/cose-lib/zipball/ed172d2dc1a6b87b5c644c07c118cd30c1b3819b", + "reference": "ed172d2dc1a6b87b5c644c07c118cd30c1b3819b", "shasum": "" }, "require": { @@ -5883,7 +5883,7 @@ "RFC8152" ], "support": { - "source": "https://github.com/web-auth/cose-lib/tree/v3.3.1" + "source": "https://github.com/web-auth/cose-lib/tree/v3.3.9" }, "funding": [ { @@ -5895,11 +5895,11 @@ "type": "patreon" } ], - "time": "2021-01-09T13:31:01+00:00" + "time": "2021-05-02T19:57:09+00:00" }, { "name": "web-auth/metadata-service", - "version": "v3.3.1", + "version": "v3.3.9", "source": { "type": "git", "url": "https://github.com/web-auth/webauthn-metadata-service.git", @@ -5952,22 +5952,32 @@ "webauthn" ], "support": { - "source": "https://github.com/web-auth/webauthn-metadata-service/tree/v3.3.1" + "source": "https://github.com/web-auth/webauthn-metadata-service/tree/v3.3.9" }, + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], "time": "2021-01-09T13:31:01+00:00" }, { "name": "web-auth/webauthn-lib", - "version": "v3.3.1", + "version": "v3.3.9", "source": { "type": "git", "url": "https://github.com/web-auth/webauthn-lib.git", - "reference": "e411527a41c1013512fccdfce61681eb36484c77" + "reference": "04b98ee3d39cb79dad68a7c15c297c085bf66bfe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/web-auth/webauthn-lib/zipball/e411527a41c1013512fccdfce61681eb36484c77", - "reference": "e411527a41c1013512fccdfce61681eb36484c77", + "url": "https://api.github.com/repos/web-auth/webauthn-lib/zipball/04b98ee3d39cb79dad68a7c15c297c085bf66bfe", + "reference": "04b98ee3d39cb79dad68a7c15c297c085bf66bfe", "shasum": "" }, "require": { @@ -6024,7 +6034,7 @@ "webauthn" ], "support": { - "source": "https://github.com/web-auth/webauthn-lib/tree/v3.3.1" + "source": "https://github.com/web-auth/webauthn-lib/tree/v3.3.9" }, "funding": [ { @@ -6036,7 +6046,7 @@ "type": "patreon" } ], - "time": "2021-01-09T13:31:01+00:00" + "time": "2021-04-19T20:22:20+00:00" } ], "packages-dev": [], diff --git a/composer/installed.json b/composer/installed.json index ff287524a..c2a3bbf75 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -6078,17 +6078,17 @@ }, { "name": "web-auth/cose-lib", - "version": "v3.3.1", - "version_normalized": "3.3.1.0", + "version": "v3.3.9", + "version_normalized": "3.3.9.0", "source": { "type": "git", "url": "https://github.com/web-auth/cose-lib.git", - "reference": "eea6fae63ff5c81bf98c115b1be5f38a69682c16" + "reference": "ed172d2dc1a6b87b5c644c07c118cd30c1b3819b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/web-auth/cose-lib/zipball/eea6fae63ff5c81bf98c115b1be5f38a69682c16", - "reference": "eea6fae63ff5c81bf98c115b1be5f38a69682c16", + "url": "https://api.github.com/repos/web-auth/cose-lib/zipball/ed172d2dc1a6b87b5c644c07c118cd30c1b3819b", + "reference": "ed172d2dc1a6b87b5c644c07c118cd30c1b3819b", "shasum": "" }, "require": { @@ -6099,7 +6099,7 @@ "fgrosse/phpasn1": "^2.1", "php": ">=7.2" }, - "time": "2021-01-09T13:31:01+00:00", + "time": "2021-05-02T19:57:09+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -6128,7 +6128,7 @@ "RFC8152" ], "support": { - "source": "https://github.com/web-auth/cose-lib/tree/v3.3.1" + "source": "https://github.com/web-auth/cose-lib/tree/v3.3.9" }, "funding": [ { @@ -6144,8 +6144,8 @@ }, { "name": "web-auth/metadata-service", - "version": "v3.3.1", - "version_normalized": "3.3.1.0", + "version": "v3.3.9", + "version_normalized": "3.3.9.0", "source": { "type": "git", "url": "https://github.com/web-auth/webauthn-metadata-service.git", @@ -6200,23 +6200,33 @@ "webauthn" ], "support": { - "source": "https://github.com/web-auth/webauthn-metadata-service/tree/v3.3.1" + "source": "https://github.com/web-auth/webauthn-metadata-service/tree/v3.3.9" }, + "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], "install-path": "../web-auth/metadata-service" }, { "name": "web-auth/webauthn-lib", - "version": "v3.3.1", - "version_normalized": "3.3.1.0", + "version": "v3.3.9", + "version_normalized": "3.3.9.0", "source": { "type": "git", "url": "https://github.com/web-auth/webauthn-lib.git", - "reference": "e411527a41c1013512fccdfce61681eb36484c77" + "reference": "04b98ee3d39cb79dad68a7c15c297c085bf66bfe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/web-auth/webauthn-lib/zipball/e411527a41c1013512fccdfce61681eb36484c77", - "reference": "e411527a41c1013512fccdfce61681eb36484c77", + "url": "https://api.github.com/repos/web-auth/webauthn-lib/zipball/04b98ee3d39cb79dad68a7c15c297c085bf66bfe", + "reference": "04b98ee3d39cb79dad68a7c15c297c085bf66bfe", "shasum": "" }, "require": { @@ -6245,7 +6255,7 @@ "web-token/jwt-signature-algorithm-eddsa": "Recommended for the AndroidSafetyNet Attestation Statement support", "web-token/jwt-signature-algorithm-rsa": "Mandatory for the AndroidSafetyNet Attestation Statement support" }, - "time": "2021-01-09T13:31:01+00:00", + "time": "2021-04-19T20:22:20+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -6275,7 +6285,7 @@ "webauthn" ], "support": { - "source": "https://github.com/web-auth/webauthn-lib/tree/v3.3.1" + "source": "https://github.com/web-auth/webauthn-lib/tree/v3.3.9" }, "funding": [ { diff --git a/composer/installed.php b/composer/installed.php index 83cbaea87..4401ba0f5 100644 --- a/composer/installed.php +++ b/composer/installed.php @@ -854,17 +854,17 @@ 'dev_requirement' => false, ), 'web-auth/cose-lib' => array( - 'pretty_version' => 'v3.3.1', - 'version' => '3.3.1.0', + 'pretty_version' => 'v3.3.9', + 'version' => '3.3.9.0', 'type' => 'library', 'install_path' => __DIR__ . '/../web-auth/cose-lib', 'aliases' => array(), - 'reference' => 'eea6fae63ff5c81bf98c115b1be5f38a69682c16', + 'reference' => 'ed172d2dc1a6b87b5c644c07c118cd30c1b3819b', 'dev_requirement' => false, ), 'web-auth/metadata-service' => array( - 'pretty_version' => 'v3.3.1', - 'version' => '3.3.1.0', + 'pretty_version' => 'v3.3.9', + 'version' => '3.3.9.0', 'type' => 'library', 'install_path' => __DIR__ . '/../web-auth/metadata-service', 'aliases' => array(), @@ -872,12 +872,12 @@ 'dev_requirement' => false, ), 'web-auth/webauthn-lib' => array( - 'pretty_version' => 'v3.3.1', - 'version' => '3.3.1.0', + 'pretty_version' => 'v3.3.9', + 'version' => '3.3.9.0', 'type' => 'library', 'install_path' => __DIR__ . '/../web-auth/webauthn-lib', 'aliases' => array(), - 'reference' => 'e411527a41c1013512fccdfce61681eb36484c77', + 'reference' => '04b98ee3d39cb79dad68a7c15c297c085bf66bfe', 'dev_requirement' => false, ), ), diff --git a/composer/package-versions-deprecated/src/PackageVersions/Versions.php b/composer/package-versions-deprecated/src/PackageVersions/Versions.php index cd4506a0a..f2609387c 100644 --- a/composer/package-versions-deprecated/src/PackageVersions/Versions.php +++ b/composer/package-versions-deprecated/src/PackageVersions/Versions.php @@ -117,9 +117,9 @@ final class Versions 'symfony/translation' => 'v4.4.25@dfe132c5c6d89f90ce7f961742cc532e9ca16dd4', 'symfony/translation-contracts' => 'v2.4.0@95c812666f3e91db75385749fe219c5e494c7f95', 'thecodingmachine/safe' => 'v1.3.3@a8ab0876305a4cdaef31b2350fcb9811b5608dbc', - 'web-auth/cose-lib' => 'v3.3.1@eea6fae63ff5c81bf98c115b1be5f38a69682c16', - 'web-auth/metadata-service' => 'v3.3.1@8488d3a832a38cc81c670fce05de1e515c6e64b1', - 'web-auth/webauthn-lib' => 'v3.3.1@e411527a41c1013512fccdfce61681eb36484c77', + 'web-auth/cose-lib' => 'v3.3.9@ed172d2dc1a6b87b5c644c07c118cd30c1b3819b', + 'web-auth/metadata-service' => 'v3.3.9@8488d3a832a38cc81c670fce05de1e515c6e64b1', + 'web-auth/webauthn-lib' => 'v3.3.9@04b98ee3d39cb79dad68a7c15c297c085bf66bfe', 'nextcloud/3rdparty' => 'dev-master@b712fcb86411da2f8547100e28f3e586dee52ead', ); diff --git a/web-auth/cose-lib/src/Algorithm/ManagerFactory.php b/web-auth/cose-lib/src/Algorithm/ManagerFactory.php index 2fadacbc1..050377e32 100644 --- a/web-auth/cose-lib/src/Algorithm/ManagerFactory.php +++ b/web-auth/cose-lib/src/Algorithm/ManagerFactory.php @@ -14,7 +14,6 @@ namespace Cose\Algorithm; use Assert\Assertion; -use function Safe\sprintf; class ManagerFactory { diff --git a/web-auth/cose-lib/src/Algorithm/Signature/ECDSA/ECDSA.php b/web-auth/cose-lib/src/Algorithm/Signature/ECDSA/ECDSA.php index 7198cbf9f..6aa39e2ba 100644 --- a/web-auth/cose-lib/src/Algorithm/Signature/ECDSA/ECDSA.php +++ b/web-auth/cose-lib/src/Algorithm/Signature/ECDSA/ECDSA.php @@ -17,7 +17,6 @@ use Cose\Algorithm\Signature\Signature; use Cose\Key\Ec2Key; use Cose\Key\Key; -use function Safe\openssl_sign; abstract class ECDSA implements Signature { diff --git a/web-auth/cose-lib/src/Algorithm/Signature/ECDSA/ECSignature.php b/web-auth/cose-lib/src/Algorithm/Signature/ECDSA/ECSignature.php index b8adc61b6..1a0dc90b5 100644 --- a/web-auth/cose-lib/src/Algorithm/Signature/ECDSA/ECSignature.php +++ b/web-auth/cose-lib/src/Algorithm/Signature/ECDSA/ECSignature.php @@ -19,7 +19,6 @@ use InvalidArgumentException; use function mb_strlen; use function mb_substr; -use function Safe\hex2bin; use function str_pad; use const STR_PAD_LEFT; @@ -53,12 +52,17 @@ public static function toAsn1(string $signature, int $length): string $totalLength = $lengthR + $lengthS + self::BYTE_SIZE + self::BYTE_SIZE; $lengthPrefix = $totalLength > self::ASN1_MAX_SINGLE_BYTE ? self::ASN1_LENGTH_2BYTES : ''; - return hex2bin( + $bin = hex2bin( self::ASN1_SEQUENCE .$lengthPrefix.dechex($totalLength) .self::ASN1_INTEGER.dechex($lengthR).$pointR .self::ASN1_INTEGER.dechex($lengthS).$pointS ); + if (false === $bin) { + throw new InvalidArgumentException('Unable to convert into ASN.1'); + } + + return $bin; } public static function fromAsn1(string $signature, int $length): string @@ -78,7 +82,12 @@ public static function fromAsn1(string $signature, int $length): string $pointR = self::retrievePositiveInteger(self::readAsn1Integer($message, $position)); $pointS = self::retrievePositiveInteger(self::readAsn1Integer($message, $position)); - return hex2bin(str_pad($pointR, $length, '0', STR_PAD_LEFT).str_pad($pointS, $length, '0', STR_PAD_LEFT)); + $bin = hex2bin(str_pad($pointR, $length, '0', STR_PAD_LEFT).str_pad($pointS, $length, '0', STR_PAD_LEFT)); + if (false === $bin) { + throw new InvalidArgumentException('Unable to convert from ASN.1'); + } + + return $bin; } private static function octetLength(string $data): int diff --git a/web-auth/cose-lib/src/Algorithm/Signature/RSA/PSSRSA.php b/web-auth/cose-lib/src/Algorithm/Signature/RSA/PSSRSA.php index 52213a875..8f87dd7c6 100644 --- a/web-auth/cose-lib/src/Algorithm/Signature/RSA/PSSRSA.php +++ b/web-auth/cose-lib/src/Algorithm/Signature/RSA/PSSRSA.php @@ -27,7 +27,6 @@ use function ord; use function random_bytes; use RuntimeException; -use function Safe\pack; use function str_pad; use function str_repeat; diff --git a/web-auth/cose-lib/src/Algorithm/Signature/RSA/RSA.php b/web-auth/cose-lib/src/Algorithm/Signature/RSA/RSA.php index e439f1b57..79fdb665e 100644 --- a/web-auth/cose-lib/src/Algorithm/Signature/RSA/RSA.php +++ b/web-auth/cose-lib/src/Algorithm/Signature/RSA/RSA.php @@ -17,7 +17,7 @@ use Cose\Algorithm\Signature\Signature; use Cose\Key\Key; use Cose\Key\RsaKey; -use function Safe\openssl_sign; +use InvalidArgumentException; abstract class RSA implements Signature { @@ -26,7 +26,9 @@ public function sign(string $data, Key $key): string $key = $this->handleKey($key); Assertion::true($key->isPrivate(), 'The key is not private'); - openssl_sign($data, $signature, $key->asPem(), $this->getHashAlgorithm()); + if (false === openssl_sign($data, $signature, $key->asPem(), $this->getHashAlgorithm())) { + throw new InvalidArgumentException('Unable to sign the data'); + } return $signature; } diff --git a/web-auth/cose-lib/src/Key/Ec2Key.php b/web-auth/cose-lib/src/Key/Ec2Key.php index 76aee1ac8..064b705ee 100644 --- a/web-auth/cose-lib/src/Key/Ec2Key.php +++ b/web-auth/cose-lib/src/Key/Ec2Key.php @@ -21,7 +21,6 @@ use FG\ASN1\Universal\ObjectIdentifier; use FG\ASN1\Universal\OctetString; use FG\ASN1\Universal\Sequence; -use function Safe\sprintf; class Ec2Key extends Key { diff --git a/web-auth/cose-lib/src/Key/Key.php b/web-auth/cose-lib/src/Key/Key.php index 8941b8df4..423371a73 100644 --- a/web-auth/cose-lib/src/Key/Key.php +++ b/web-auth/cose-lib/src/Key/Key.php @@ -15,7 +15,6 @@ use function array_key_exists; use Assert\Assertion; -use function Safe\sprintf; class Key { diff --git a/web-auth/cose-lib/src/Key/RsaKey.php b/web-auth/cose-lib/src/Key/RsaKey.php index b541425e6..151ac06a7 100644 --- a/web-auth/cose-lib/src/Key/RsaKey.php +++ b/web-auth/cose-lib/src/Key/RsaKey.php @@ -21,8 +21,7 @@ use FG\ASN1\Universal\NullObject; use FG\ASN1\Universal\ObjectIdentifier; use FG\ASN1\Universal\Sequence; -use function Safe\sprintf; -use function Safe\unpack; +use InvalidArgumentException; class RsaKey extends Key { @@ -189,7 +188,12 @@ public function asPem(): string private function fromBase64ToInteger(string $value): string { - $hex = current(unpack('H*', $value)); + $data = unpack('H*', $value); + if (false === $data) { + throw new InvalidArgumentException('Unable to convert to an integer'); + } + + $hex = current($data); return BigInteger::fromBase($hex, 16)->toBase(10); } diff --git a/web-auth/webauthn-lib/src/AttestationStatement/AndroidSafetyNetAttestationStatementSupport.php b/web-auth/webauthn-lib/src/AttestationStatement/AndroidSafetyNetAttestationStatementSupport.php index bab03d493..6ef7de681 100644 --- a/web-auth/webauthn-lib/src/AttestationStatement/AndroidSafetyNetAttestationStatementSupport.php +++ b/web-auth/webauthn-lib/src/AttestationStatement/AndroidSafetyNetAttestationStatementSupport.php @@ -15,6 +15,7 @@ use Assert\Assertion; use InvalidArgumentException; +use Jose\Component\Core\Algorithm as AlgorithmInterface; use Jose\Component\Core\AlgorithmManager; use Jose\Component\Core\Util\JsonConverter; use Jose\Component\KeyManagement\JWKFactory; @@ -277,9 +278,11 @@ private function initJwsVerifier(): void Algorithm\ES256::class, Algorithm\ES384::class, Algorithm\ES512::class, Algorithm\EdDSA::class, ]; + /* @var AlgorithmInterface[] $algorithms */ $algorithms = []; - foreach ($algorithmClasses as $key => $algorithm) { + foreach ($algorithmClasses as $algorithm) { if (class_exists($algorithm)) { + /* @var AlgorithmInterface $algorithm */ $algorithms[] = new $algorithm(); } } diff --git a/web-auth/webauthn-lib/src/AttestationStatement/AppleAttestationStatementSupport.php b/web-auth/webauthn-lib/src/AttestationStatement/AppleAttestationStatementSupport.php index c4279544e..3f942e472 100644 --- a/web-auth/webauthn-lib/src/AttestationStatement/AppleAttestationStatementSupport.php +++ b/web-auth/webauthn-lib/src/AttestationStatement/AppleAttestationStatementSupport.php @@ -61,7 +61,7 @@ public function load(array $attestation): AttestationStatement Assertion::allString($certificates, 'The attestation statement value "x5c" must be a list with at least one certificate.'); $certificates = CertificateToolbox::convertAllDERToPEM($certificates); - return AttestationStatement::createBasic($attestation['fmt'], $attestation['attStmt'], new CertificateTrustPath($certificates)); + return AttestationStatement::createAnonymizationCA($attestation['fmt'], $attestation['attStmt'], new CertificateTrustPath($certificates)); } public function isValid(string $clientDataJSONHash, AttestationStatement $attestationStatement, AuthenticatorData $authenticatorData): bool diff --git a/web-auth/webauthn-lib/src/AttestationStatement/AttestationStatement.php b/web-auth/webauthn-lib/src/AttestationStatement/AttestationStatement.php index f84d52897..3de8bfb05 100644 --- a/web-auth/webauthn-lib/src/AttestationStatement/AttestationStatement.php +++ b/web-auth/webauthn-lib/src/AttestationStatement/AttestationStatement.php @@ -27,6 +27,7 @@ class AttestationStatement implements JsonSerializable public const TYPE_SELF = 'self'; public const TYPE_ATTCA = 'attca'; public const TYPE_ECDAA = 'ecdaa'; + public const TYPE_ANONCA = 'anonca'; /** * @var string @@ -99,6 +100,11 @@ public static function createEcdaa(string $fmt, array $attStmt, TrustPath $trust return new self($fmt, $attStmt, self::TYPE_ECDAA, $trustPath); } + public static function createAnonymizationCA(string $fmt, array $attStmt, TrustPath $trustPath): self + { + return new self($fmt, $attStmt, self::TYPE_ANONCA, $trustPath); + } + public function getFmt(): string { return $this->fmt; diff --git a/web-auth/webauthn-lib/src/AuthenticatorAssertionResponseValidator.php b/web-auth/webauthn-lib/src/AuthenticatorAssertionResponseValidator.php index 8400ba9c3..f196b1ea2 100644 --- a/web-auth/webauthn-lib/src/AuthenticatorAssertionResponseValidator.php +++ b/web-auth/webauthn-lib/src/AuthenticatorAssertionResponseValidator.php @@ -22,6 +22,7 @@ use Cose\Key\Key; use function count; use function in_array; +use function is_string; use Psr\Http\Message\ServerRequestInterface; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; @@ -169,7 +170,7 @@ public function check(string $credentialId, AuthenticatorAssertionResponse $auth Assertion::true(hash_equals($rpIdHash, $authenticatorAssertionResponse->getAuthenticatorData()->getRpIdHash()), 'rpId hash mismatch.'); /* @see 7.2.12 */ - //Nothing to do. The verification of the bit is done during the authenticator data loading + Assertion::true($authenticatorAssertionResponse->getAuthenticatorData()->isUserPresent(), 'User was not present'); /* @see 7.2.13 */ if (AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED === $publicKeyCredentialRequestOptions->getUserVerification()) { Assertion::true($authenticatorAssertionResponse->getAuthenticatorData()->isUserVerified(), 'User authentication required.'); @@ -249,14 +250,15 @@ private function isCredentialIdAllowed(string $credentialId, array $allowedCrede private function getFacetId(string $rpId, AuthenticationExtensionsClientInputs $authenticationExtensionsClientInputs, ?AuthenticationExtensionsClientOutputs $authenticationExtensionsClientOutputs): string { - switch (true) { - case null === $authenticationExtensionsClientOutputs: - case !$authenticationExtensionsClientOutputs->has('appid'): - case true !== $authenticationExtensionsClientOutputs->get('appid'): - case !$authenticationExtensionsClientInputs->has('appid'): - return $rpId; - default: - return $authenticationExtensionsClientInputs->get('appid'); + if (null === $authenticationExtensionsClientOutputs || !$authenticationExtensionsClientInputs->has('appid') || !$authenticationExtensionsClientOutputs->has('appid')) { + return $rpId; } + $appId = $authenticationExtensionsClientInputs->get('appid')->value(); + $wasUsed = $authenticationExtensionsClientOutputs->get('appid')->value(); + if (!is_string($appId) || true !== $wasUsed) { + return $rpId; + } + + return $appId; } } diff --git a/web-auth/webauthn-lib/src/AuthenticatorAttestationResponseValidator.php b/web-auth/webauthn-lib/src/AuthenticatorAttestationResponseValidator.php index f3e5a15df..b6ee834e4 100644 --- a/web-auth/webauthn-lib/src/AuthenticatorAttestationResponseValidator.php +++ b/web-auth/webauthn-lib/src/AuthenticatorAttestationResponseValidator.php @@ -17,6 +17,7 @@ use function count; use function in_array; use InvalidArgumentException; +use function is_string; use LogicException; use Psr\Http\Message\ServerRequestInterface; use Psr\Log\LoggerInterface; @@ -169,7 +170,7 @@ public function check(AuthenticatorAttestationResponse $authenticatorAttestation Assertion::true(hash_equals($rpIdHash, $attestationObject->getAuthData()->getRpIdHash()), 'rpId hash mismatch.'); /* @see 7.1.10 */ - //Nothing to do. The verification of the bit is done during the authenticator data loading + Assertion::true($attestationObject->getAuthData()->isUserPresent(), 'User was not present'); /* @see 7.1.11 */ if (AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED === $publicKeyCredentialCreationOptions->getAuthenticatorSelection()->getUserVerification()) { Assertion::true($attestationObject->getAuthData()->isUserVerified(), 'User authentication required.'); @@ -369,14 +370,15 @@ private function getAttestationType(AttestationStatement $attestationStatement): private function getFacetId(string $rpId, AuthenticationExtensionsClientInputs $authenticationExtensionsClientInputs, ?AuthenticationExtensionsClientOutputs $authenticationExtensionsClientOutputs): string { - switch (true) { - case null === $authenticationExtensionsClientOutputs: - case !$authenticationExtensionsClientOutputs->has('appid'): - case true !== $authenticationExtensionsClientOutputs->get('appid'): - case !$authenticationExtensionsClientInputs->has('appid'): - return $rpId; - default: - return $authenticationExtensionsClientInputs->get('appid'); + if (null === $authenticationExtensionsClientOutputs || !$authenticationExtensionsClientInputs->has('appid') || !$authenticationExtensionsClientOutputs->has('appid')) { + return $rpId; + } + $appId = $authenticationExtensionsClientInputs->get('appid')->value(); + $wasUsed = $authenticationExtensionsClientOutputs->get('appid')->value(); + if (!is_string($appId) || true !== $wasUsed) { + return $rpId; } + + return $appId; } } diff --git a/web-auth/webauthn-lib/src/PublicKeyCredentialSource.php b/web-auth/webauthn-lib/src/PublicKeyCredentialSource.php index 091b52665..68f958ddb 100644 --- a/web-auth/webauthn-lib/src/PublicKeyCredentialSource.php +++ b/web-auth/webauthn-lib/src/PublicKeyCredentialSource.php @@ -75,10 +75,15 @@ class PublicKeyCredentialSource implements JsonSerializable */ protected $counter; + /** + * @var array|null + */ + protected $otherUI; + /** * @param string[] $transports */ - public function __construct(string $publicKeyCredentialId, string $type, array $transports, string $attestationType, TrustPath $trustPath, UuidInterface $aaguid, string $credentialPublicKey, string $userHandle, int $counter) + public function __construct(string $publicKeyCredentialId, string $type, array $transports, string $attestationType, TrustPath $trustPath, UuidInterface $aaguid, string $credentialPublicKey, string $userHandle, int $counter, ?array $otherUI = null) { $this->publicKeyCredentialId = $publicKeyCredentialId; $this->type = $type; @@ -89,6 +94,7 @@ public function __construct(string $publicKeyCredentialId, string $type, array $ $this->counter = $counter; $this->attestationType = $attestationType; $this->trustPath = $trustPath; + $this->otherUI = $otherUI; } public function getPublicKeyCredentialId(): string @@ -162,6 +168,18 @@ public function setCounter(int $counter): void $this->counter = $counter; } + public function getOtherUI(): ?array + { + return $this->otherUI; + } + + public function setOtherUI(?array $otherUI): self + { + $this->otherUI = $otherUI; + + return $this; + } + /** * @param mixed[] $data */ @@ -169,6 +187,9 @@ public static function createFromArray(array $data): self { $keys = array_keys(get_class_vars(self::class)); foreach ($keys as $key) { + if ('otherUI' === $key) { + continue; + } Assertion::keyExists($data, $key, sprintf('The parameter "%s" is missing', $key)); } switch (true) { @@ -190,7 +211,8 @@ public static function createFromArray(array $data): self $uuid, Base64Url::decode($data['credentialPublicKey']), Base64Url::decode($data['userHandle']), - $data['counter'] + $data['counter'], + $data['otherUI'] ?? null ); } catch (Throwable $throwable) { throw new InvalidArgumentException('Unable to load the data', $throwable->getCode(), $throwable); @@ -212,6 +234,7 @@ public function jsonSerialize(): array 'credentialPublicKey' => Base64Url::encode($this->credentialPublicKey), 'userHandle' => Base64Url::encode($this->userHandle), 'counter' => $this->counter, + 'otherUI' => $this->otherUI, ]; } } diff --git a/web-auth/webauthn-lib/src/PublicKeyCredentialUserEntity.php b/web-auth/webauthn-lib/src/PublicKeyCredentialUserEntity.php index 41ff3a94c..b0446e26f 100644 --- a/web-auth/webauthn-lib/src/PublicKeyCredentialUserEntity.php +++ b/web-auth/webauthn-lib/src/PublicKeyCredentialUserEntity.php @@ -32,6 +32,7 @@ class PublicKeyCredentialUserEntity extends PublicKeyCredentialEntity public function __construct(string $name, string $id, string $displayName, ?string $icon = null) { parent::__construct($name, $icon); + Assertion::maxLength($id, 64, 'User ID max length is 64 bytes', 'id', '8bit'); $this->id = $id; $this->displayName = $displayName; } diff --git a/web-auth/webauthn-lib/src/Server.php b/web-auth/webauthn-lib/src/Server.php index 157bbf1d8..662c41ade 100644 --- a/web-auth/webauthn-lib/src/Server.php +++ b/web-auth/webauthn-lib/src/Server.php @@ -25,6 +25,7 @@ use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use Webauthn\AttestationStatement\AndroidKeyAttestationStatementSupport; use Webauthn\AttestationStatement\AndroidSafetyNetAttestationStatementSupport; use Webauthn\AttestationStatement\AttestationObjectLoader; @@ -88,17 +89,17 @@ class Server private $metadataStatementRepository; /** - * @var ClientInterface + * @var ClientInterface|null */ private $httpClient; /** - * @var string + * @var string|null */ private $googleApiKey; /** - * @var RequestFactoryInterface + * @var RequestFactoryInterface|null */ private $requestFactory; @@ -108,7 +109,7 @@ class Server private $counterChecker; /** - * @var LoggerInterface|null + * @var LoggerInterface */ private $logger; @@ -117,12 +118,13 @@ class Server */ private $securedRelyingPartyId = []; - public function __construct(PublicKeyCredentialRpEntity $relyingParty, PublicKeyCredentialSourceRepository $publicKeyCredentialSourceRepository, ?MetadataStatementRepository $metadataStatementRepository) + public function __construct(PublicKeyCredentialRpEntity $relyingParty, PublicKeyCredentialSourceRepository $publicKeyCredentialSourceRepository, ?MetadataStatementRepository $metadataStatementRepository = null) { if (null !== $metadataStatementRepository) { @trigger_error('The argument "metadataStatementRepository" is deprecated since version 3.3 and will be removed in 4.0. Please use the method "setMetadataStatementRepository".', E_USER_DEPRECATED); } $this->rpEntity = $relyingParty; + $this->logger = new NullLogger(); $this->coseAlgorithmManagerFactory = new ManagerFactory(); $this->coseAlgorithmManagerFactory->add('RS1', new RSA\RS1()); @@ -142,7 +144,7 @@ public function __construct(PublicKeyCredentialRpEntity $relyingParty, PublicKey $this->publicKeyCredentialSourceRepository = $publicKeyCredentialSourceRepository; $this->tokenBindingHandler = new IgnoreTokenBindingHandler(); $this->extensionOutputCheckerHandler = new ExtensionOutputCheckerHandler(); - $this->setMetadataStatementRepository($metadataStatementRepository); + $this->metadataStatementRepository = $metadataStatementRepository; } public function setMetadataStatementRepository(MetadataStatementRepository $metadataStatementRepository): self @@ -262,9 +264,9 @@ public function loadAndCheckAttestationResponse(string $data, PublicKeyCredentia $this->publicKeyCredentialSourceRepository, $this->tokenBindingHandler, $this->extensionOutputCheckerHandler, - $this->metadataStatementRepository, - $this->logger + $this->metadataStatementRepository ); + $authenticatorAttestationResponseValidator->setLogger($this->logger); return $authenticatorAttestationResponseValidator->check($authenticatorResponse, $publicKeyCredentialCreationOptions, $serverRequest, $this->securedRelyingPartyId); } @@ -288,9 +290,9 @@ public function loadAndCheckAssertionResponse(string $data, PublicKeyCredentialR $this->tokenBindingHandler, $this->extensionOutputCheckerHandler, $this->coseAlgorithmManagerFactory->create($this->selectedAlgorithms), - $this->counterChecker, - $this->logger + $this->counterChecker ); + $authenticatorAssertionResponseValidator->setLogger($this->logger); return $authenticatorAssertionResponseValidator->check( $publicKeyCredential->getRawId(), @@ -332,11 +334,13 @@ private function getAttestationStatementSupportManager(): AttestationStatementSu $attestationStatementSupportManager->add(new FidoU2FAttestationStatementSupport()); if (class_exists(RS256::class) && class_exists(JWKFactory::class)) { $androidSafetyNetAttestationStatementSupport = new AndroidSafetyNetAttestationStatementSupport(); - $androidSafetyNetAttestationStatementSupport - ->enableApiVerification($this->httpClient, $this->googleApiKey, $this->requestFactory) - ->setLeeway(2000) - ->setMaxAge(60000) - ; + if (null !== $this->httpClient && null !== $this->googleApiKey && null !== $this->requestFactory) { + $androidSafetyNetAttestationStatementSupport + ->enableApiVerification($this->httpClient, $this->googleApiKey, $this->requestFactory) + ->setLeeway(2000) + ->setMaxAge(60000) + ; + } $attestationStatementSupportManager->add($androidSafetyNetAttestationStatementSupport); } $attestationStatementSupportManager->add(new AndroidKeyAttestationStatementSupport());