diff --git a/lib/thirdpartylibs.xml b/lib/thirdpartylibs.xml index 56cc1e89214..c8d84e484c9 100644 --- a/lib/thirdpartylibs.xml +++ b/lib/thirdpartylibs.xml @@ -737,7 +737,7 @@ All rights reserved. webauthn WebAuthn - 2.1.1 + 2.2.2 MIT https://github.com/lbuchs/WebAuthn diff --git a/lib/webauthn/README.md b/lib/webauthn/README.md index 0fa46c34457..8d1cb7baac4 100644 --- a/lib/webauthn/README.md +++ b/lib/webauthn/README.md @@ -19,7 +19,8 @@ See /_test for a simple usage of this library. Check [webauthn.lubu.ch](https:// * packed ✅ * tpm ✅ -This library supports authenticators which are signed with a X.509 certificate or which are self attested. ECDAA is not supported. +> [!NOTE] +> This library supports authenticators which are signed with a X.509 certificate or which are self attested. ECDAA is not supported. ## Workflow @@ -60,39 +61,44 @@ their organization. ### no attestation just verify that the device is the same device used on registration. You can use 'none' attestation with this library if you only check 'none' as format. -* this is propably what you want to use if you want simple 2FA login protection like github, facebook, google, etc. + +> [!TIP] +> this is propably what you want to use if you want secure login for a public website. ### indirect attestation the browser may replace the AAGUID and attestation statement with a more privacy-friendly and/or more easily verifiable version of the same data (for example, by employing an anonymization CA). You can not validate against any root ca, if the browser uses a anonymization certificate. this library sets attestation to indirect, if you select multiple formats but don't provide any root ca. -* hybrid soultion, clients may be discouraged by browser warnings but then you know what device they're using (statistics rulez!) + +> [!TIP] +> hybrid soultion, clients may be discouraged by browser warnings but then you know what device they're using (statistics rulez!) ### direct attestation the browser proviedes data about the identificator device, the device can be identified uniquely. User could be tracked over multiple sites, because of that the browser may show a warning message about providing this data when register. this library sets attestation to direct, if you select multiple formats and provide root ca's. -* this is probably what you want if you know what devices your clients are using and make sure that only this devices are used. -## Client-side discoverable Credentials +> [!TIP] +> this is probably what you want if you know what devices your clients are using and make sure that only this devices are used. + +## Passkeys / Client-side discoverable Credentials A Client-side discoverable Credential Source is a public key credential source whose credential private key is stored in the authenticator, client or client device. Such client-side storage requires a resident credential capable authenticator. This is only supported by FIDO2 hardware, not by older U2F hardware. +>[!NOTE] +>Passkeys is a technique that allows sharing credentials stored on the device with other devices. So from a technical standpoint of the server, there is no difference to client-side discoverable credentials. The difference is only that the phone or computer system is automatically syncing the credentials between the user’s devices via a cloud service. The cross-device sync of passkeys is managed transparently by the OS. + ### How does it work? -In a typical **server-side key** process, the user provides their username (and sometimes password) -and the server responds with a list of all the public key credential identifiers that the user has registered. -The authenticator then selects the first credential identifier it issued and responds with a signature -that can be verified using the public key registered during the registration process. +In a typical server-side key management process, a user initiates a request by entering their username and, in some cases, their password. +The server validates the user's credentials and, upon successful authentication, retrieves a list of all public key identifiers associated with that user account. +This list is then returned to the authenticator, which selects the first credential identifier it issued and responds with a signature that can be verified using the public key registered during the registration process. In a client-side key process, the user does not need to provide a username or password. -Instead, the authenticator searches its own memory to see if it has saved a key for the relying party. +Instead, the authenticator searches its own memory to see if it has saved a key for the relying party (domain). If a key is found, the authentication process proceeds in the same way as it would if the server had sent a list of identifiers. There is no difference in the verification process. -Both Apple and Windows 10/11 (with Firefox and Chromium) support Resident Credential. -However, older operating systems such as Windows 7 do not support it and instead fall back to using FIDO U2F. - ### How can I use it with this library? #### on registration When calling `WebAuthn\WebAuthn->getCreateArgs`, set `$requireResidentKey` to true, @@ -100,21 +106,16 @@ to notify the authenticator that he should save the registration in its memory. #### on login When calling `WebAuthn\WebAuthn->getGetArgs`, don't provide any `$credentialIds` (the authenticator will look up the ids in its own memory and returns the user ID as userHandle). +Set the type of authenticator to `hybrid` (Passkey scanned via QR Code) and `internal` (Passkey stored on the device itself). #### disadvantage The RP ID (= domain) is saved on the authenticator. So If an authenticator is lost, its theoretically possible to find the services, which the authenticator is used and login there. -## Passkeys -Passkeys is a technique that allows sharing credentials stored on the device with other devices. So from a technical standpoint of the server, -there is no difference to client-side discoverable credentials. The difference is only that the phone or computer system is automatically -syncing the credentials between the user’s devices via a cloud service. The cross-device sync of passkeys is managed transparently by the OS. - -### Browser support +### device support Availability of built-in passkeys that automatically synchronize to all of a user’s devices: (see also [passkeys.dev/device-support](https://passkeys.dev/device-support/)) -* Apple: iOS 16 / iPadOS 16 / macOS Ventura -* Google: support in Android starting October 2022 -* Microsoft Windows is set to deliver support in 2023. -* Firefox see [Bugzilla](https://bugzilla.mozilla.org/show_bug.cgi?id=1792433) +* Apple iOS 16+ / iPadOS 16+ / macOS Ventura+ +* Android 9+ +* Microsoft Windows 11 23H2+ ## Requirements * PHP >= 8.0 with [OpenSSL](http://php.net/manual/en/book.openssl.php) and [Multibyte String](https://www.php.net/manual/en/book.mbstring.php) diff --git a/lib/webauthn/readme_moodle.txt b/lib/webauthn/readme_moodle.txt index e0615ecbde7..7d991bf454a 100644 --- a/lib/webauthn/readme_moodle.txt +++ b/lib/webauthn/readme_moodle.txt @@ -1,5 +1,6 @@ -WebAuthn 2.0.1 +WebAuthn -------------- + https://github.com/lbuchs/WebAuthn Instructions to import WebAuthn into Moodle: diff --git a/lib/webauthn/src/Attestation/AuthenticatorData.php b/lib/webauthn/src/Attestation/AuthenticatorData.php index 0a9eec8cf46..83462b12e5e 100644 --- a/lib/webauthn/src/Attestation/AuthenticatorData.php +++ b/lib/webauthn/src/Attestation/AuthenticatorData.php @@ -189,6 +189,24 @@ class AuthenticatorData { return $this->_flags->userVerified; } + /** + * returns true if the backup is eligible + * @return boolean + */ + public function getIsBackupEligible() + { + return $this->_flags->isBackupEligible; + } + + /** + * returns true if the current credential is backed up + * @return boolean + */ + public function getIsBackup() + { + return $this->_flags->isBackup; + } + // ----------------------------------------------- // PRIVATE // ----------------------------------------------- @@ -259,6 +277,8 @@ class AuthenticatorData { // named flags $flags->userPresent = $flags->bit_0; $flags->userVerified = $flags->bit_2; + $flags->isBackupEligible = $flags->bit_3; + $flags->isBackup = $flags->bit_4; $flags->attestedDataIncluded = $flags->bit_6; $flags->extensionDataIncluded = $flags->bit_7; return $flags; diff --git a/lib/webauthn/src/Attestation/Format/FormatBase.php b/lib/webauthn/src/Attestation/Format/FormatBase.php index 765af85d582..92f1010b409 100644 --- a/lib/webauthn/src/Attestation/Format/FormatBase.php +++ b/lib/webauthn/src/Attestation/Format/FormatBase.php @@ -89,7 +89,7 @@ abstract class FormatBase { /** * creates a PEM encoded chain file - * @return type + * @return string|null */ protected function _createX5cChainFile() { $content = ''; diff --git a/lib/webauthn/src/WebAuthn.php b/lib/webauthn/src/WebAuthn.php index 0da0aa64e6d..2d08a690fc4 100644 --- a/lib/webauthn/src/WebAuthn.php +++ b/lib/webauthn/src/WebAuthn.php @@ -30,6 +30,7 @@ class WebAuthn { private $_signatureCounter; private $_caFiles; private $_formats; + private $_androidKeyHashes; /** * Initialize a new WebAuthn server @@ -90,6 +91,23 @@ class WebAuthn { } } + /** + * add key hashes for android verification + * @param array $hashes + * @return void + */ + public function addAndroidKeyHashes($hashes) { + if (!\is_array($this->_androidKeyHashes)) { + $this->_androidKeyHashes = []; + } + + foreach ($hashes as $hash) { + if (is_string($hash)) { + $this->_androidKeyHashes[] = $hash; + } + } + } + /** * Returns the generated challenge to save for later validation * @return ByteBuffer @@ -397,6 +415,8 @@ class WebAuthn { $data->rootValid = $rootValid; $data->userPresent = $userPresent; $data->userVerified = $userVerified; + $data->isBackupEligible = $attestationObject->getAuthenticatorData()->getIsBackupEligible(); + $data->isBackedUp = $attestationObject->getAuthenticatorData()->getIsBackup(); return $data; } @@ -601,6 +621,10 @@ class WebAuthn { * @throws WebAuthnException */ private function _checkOrigin($origin) { + if (str_starts_with($origin, 'android:apk-key-hash:')) { + return $this->_checkAndroidKeyHashes($origin); + } + // https://www.w3.org/TR/webauthn/#rp-id // The origin's scheme must be https @@ -617,6 +641,19 @@ class WebAuthn { return \preg_match('/' . \preg_quote($this->_rpId) . '$/i', $host) === 1; } + /** + * checks if the origin value contains a known android key hash + * @param string $origin + * @return boolean + */ + private function _checkAndroidKeyHashes($origin) { + $parts = explode('android:apk-key-hash:', $origin); + if (count($parts) !== 2) { + return false; + } + return in_array($parts[1], $this->_androidKeyHashes, true); + } + /** * generates a new challange * @param int $length