-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactors the Passkey implementation to include more checks and a structure that is more aligned with the official specification. Notable changes: - _BrowserService_ no longer does the checks by itself. A new class _BrowserPasskeysClient_ constructs the relevant objects, acting as a client. _BrowserService_ only acts as a bridge between the client and _BrowserPasskeys_ (authenticator) and calls the relevant popups for user interaction. - A new helper class _PasskeyUtils_ includes the actual checks and parses the objects. - _BrowserPasskeys_ is pretty much intact, but some functions have been moved to PasskeyUtils. - Fixes Ed25519 encoding in _BrowserCBOR_. - Adds new error messages. - User confirmation for Passkey retrieval is also asked even if `discouraged` is used. This goes against the specification, but currently there's no other way to verify the user. - `cross-platform` is also accepted for compatibility. This could be removed if there's a potential issue with it. - Extension data is now handled correctly during Authentication. - Allowed and excluded credentials are now handled correctly. - `KPEX_PASSKEY_GENERATED_USER_ID` is renamed to `KPEX_PASSKEY_CREDENTIAL_ID` - Adds a new option "Allow localhost with Passkeys" to Browser Integration -> Advanced tab. By default it's not allowed to access HTTP sites, but `http://localhost` can be allowed for debugging and testing purposes for local servers. - Add tag `Passkey` to a Passkey entry, or an entry with an imported Passkey. Fixes #10287.
- Loading branch information
1 parent
067deb9
commit 717b465
Showing
33 changed files
with
1,298 additions
and
264 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright (C) 2023 KeePassXC Team <[email protected]> | ||
* Copyright (C) 2024 KeePassXC Team <[email protected]> | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
|
@@ -19,6 +19,7 @@ | |
#include "BrowserMessageBuilder.h" | ||
#ifdef WITH_XC_BROWSER_PASSKEYS | ||
#include "BrowserPasskeys.h" | ||
#include "PasskeyUtils.h" | ||
#endif | ||
#include "BrowserSettings.h" | ||
#include "core/Global.h" | ||
|
@@ -507,7 +508,7 @@ QJsonObject BrowserAction::handlePasskeysGet(const QJsonObject& json, const QStr | |
} | ||
|
||
const auto origin = browserRequest.getString("origin"); | ||
if (!origin.startsWith("https://")) { | ||
if (!passkeyUtils()->isOriginAllowedWithLocalhost(browserSettings()->allowLocalhostWithPasskeys(), origin)) { | ||
return getErrorReply(action, ERROR_PASSKEYS_INVALID_URL_PROVIDED); | ||
} | ||
|
||
|
@@ -540,8 +541,8 @@ QJsonObject BrowserAction::handlePasskeysRegister(const QJsonObject& json, const | |
} | ||
|
||
const auto origin = browserRequest.getString("origin"); | ||
if (!origin.startsWith("https://")) { | ||
return getErrorReply(action, ERROR_KEEPASS_ACTION_CANCELLED_OR_DENIED); | ||
if (!passkeyUtils()->isOriginAllowedWithLocalhost(browserSettings()->allowLocalhostWithPasskeys(), origin)) { | ||
return getErrorReply(action, ERROR_PASSKEYS_INVALID_URL_PROVIDED); | ||
} | ||
|
||
const auto keyList = getConnectionKeys(browserRequest); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright (C) 2023 KeePassXC Team <[email protected]> | ||
* Copyright (C) 2024 KeePassXC Team <[email protected]> | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
|
@@ -48,6 +48,16 @@ QByteArray BrowserCbor::cborEncodeAttestation(const QByteArray& authData) const | |
// https://w3c.github.io/webauthn/#authdata-attestedcredentialdata-credentialpublickey | ||
QByteArray BrowserCbor::cborEncodePublicKey(int alg, const QByteArray& first, const QByteArray& second) const | ||
{ | ||
const auto keyType = getCoseKeyType(alg); | ||
if (keyType == 0) { | ||
return {}; | ||
} | ||
|
||
const auto curveParameter = getCurveParameter(alg); | ||
if ((alg == WebAuthnAlgorithms::ES256 || alg == WebAuthnAlgorithms::EDDSA) && curveParameter == 0) { | ||
return {}; | ||
} | ||
|
||
QByteArray result; | ||
QCborStreamWriter writer(&result); | ||
|
||
|
@@ -56,15 +66,15 @@ QByteArray BrowserCbor::cborEncodePublicKey(int alg, const QByteArray& first, co | |
|
||
// Key type | ||
writer.append(1); | ||
writer.append(getCoseKeyType(alg)); | ||
writer.append(keyType); | ||
|
||
// Signature algorithm | ||
writer.append(3); | ||
writer.append(alg); | ||
|
||
// Curve parameter | ||
writer.append(-1); | ||
writer.append(getCurveParameter(alg)); | ||
writer.append(curveParameter); | ||
|
||
// Key x-coordinate | ||
writer.append(-2); | ||
|
@@ -80,7 +90,7 @@ QByteArray BrowserCbor::cborEncodePublicKey(int alg, const QByteArray& first, co | |
|
||
// Key type | ||
writer.append(1); | ||
writer.append(getCoseKeyType(alg)); | ||
writer.append(keyType); | ||
|
||
// Signature algorithm | ||
writer.append(3); | ||
|
@@ -96,21 +106,24 @@ QByteArray BrowserCbor::cborEncodePublicKey(int alg, const QByteArray& first, co | |
|
||
writer.endMap(); | ||
} else if (alg == WebAuthnAlgorithms::EDDSA) { | ||
// https://www.rfc-editor.org/rfc/rfc8152#section-13.2 | ||
writer.startMap(3); | ||
writer.startMap(4); | ||
|
||
// Key type | ||
writer.append(1); | ||
writer.append(keyType); | ||
|
||
// Algorithm | ||
writer.append(3); | ||
writer.append(alg); | ||
|
||
// Curve parameter | ||
writer.append(-1); | ||
writer.append(getCurveParameter(alg)); | ||
writer.append(curveParameter); | ||
|
||
// Public key | ||
writer.append(-2); | ||
writer.append(first); | ||
|
||
// Private key | ||
writer.append(-4); | ||
writer.append(second); | ||
|
||
writer.endMap(); | ||
} | ||
|
||
|
@@ -230,7 +243,7 @@ unsigned int BrowserCbor::getCurveParameter(int alg) const | |
case WebAuthnAlgorithms::EDDSA: | ||
return WebAuthnCurveKey::ED25519; | ||
default: | ||
return WebAuthnCurveKey::P256; | ||
return WebAuthnCurveKey::INVALID_CURVE_KEY; | ||
} | ||
} | ||
|
||
|
@@ -240,14 +253,15 @@ unsigned int BrowserCbor::getCoseKeyType(int alg) const | |
{ | ||
switch (alg) { | ||
case WebAuthnAlgorithms::ES256: | ||
return WebAuthnCoseKeyType::EC2; | ||
case WebAuthnAlgorithms::ES384: | ||
case WebAuthnAlgorithms::ES512: | ||
return WebAuthnCoseKeyType::EC2; | ||
return WebAuthnCoseKeyType::INVALID_COSE_KEY_TYPE; | ||
case WebAuthnAlgorithms::EDDSA: | ||
return WebAuthnCoseKeyType::OKP; | ||
case WebAuthnAlgorithms::RS256: | ||
return WebAuthnCoseKeyType::RSA; | ||
default: | ||
return WebAuthnCoseKeyType::EC2; | ||
return WebAuthnCoseKeyType::INVALID_COSE_KEY_TYPE; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright (C) 2023 KeePassXC Team <[email protected]> | ||
* Copyright (C) 2024 KeePassXC Team <[email protected]> | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
|
@@ -35,6 +35,7 @@ enum WebAuthnAlgorithms : int | |
// https://www.rfc-editor.org/rfc/rfc9053#section-7.1 | ||
enum WebAuthnCurveKey : int | ||
{ | ||
INVALID_CURVE_KEY = 0, | ||
P256 = 1, // EC2, NIST P-256, also known as secp256r1 | ||
P384 = 2, // EC2, NIST P-384, also known as secp384r1 | ||
P521 = 3, // EC2, NIST P-521, also known as secp521r1 | ||
|
@@ -48,6 +49,7 @@ enum WebAuthnCurveKey : int | |
// For RSA: https://www.rfc-editor.org/rfc/rfc8230#section-4 | ||
enum WebAuthnCoseKeyType : int | ||
{ | ||
INVALID_COSE_KEY_TYPE = 0, | ||
OKP = 1, // Octet Keypair | ||
EC2 = 2, // Elliptic Curve | ||
RSA = 3 // RSA | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright (C) 2023 KeePassXC Team <[email protected]> | ||
* Copyright (C) 2024 KeePassXC Team <[email protected]> | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
|
@@ -54,7 +54,15 @@ namespace | |
ERROR_PASSKEYS_REQUEST_CANCELED = 22, | ||
ERROR_PASSKEYS_INVALID_USER_VERIFICATION = 23, | ||
ERROR_PASSKEYS_EMPTY_PUBLIC_KEY = 24, | ||
ERROR_PASSKEYS_INVALID_URL_PROVIDED = 25 | ||
ERROR_PASSKEYS_INVALID_URL_PROVIDED = 25, | ||
ERROR_PASSKEYS_ORIGIN_NOT_ALLOWED = 26, | ||
ERROR_PASSKEYS_DOMAIN_IS_NOT_VALID = 27, | ||
ERROR_PASSKEYS_DOMAIN_RPID_MISMATCH = 28, | ||
ERROR_PASSKEYS_NO_SUPPORTED_ALGORITHMS = 29, | ||
ERROR_PASSKEYS_WAIT_FOR_LIFETIMER = 30, | ||
ERROR_PASSKEYS_UNKNOWN_ERROR = 31, | ||
ERROR_PASSKEYS_INVALID_CHALLENGE = 32, | ||
ERROR_PASSKEYS_INVALID_USER_ID = 33, | ||
}; | ||
} | ||
|
||
|
Oops, something went wrong.