mirror of
https://github.com/bitwarden/browser
synced 2026-02-12 06:23:38 +00:00
Implement rust fido2 for desktop mac and linux
This commit is contained in:
@@ -338,4 +338,15 @@ export abstract class BrowserPlatformUtilsService implements PlatformUtilsServic
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
supportsNativeWebauthn(): boolean {
|
||||
return false;
|
||||
}
|
||||
performNativeWebauthnAuthentication(
|
||||
challenge: string,
|
||||
credentials: Array<string>,
|
||||
origin: string,
|
||||
): Promise<string> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,4 +138,15 @@ export class CliPlatformUtilsService implements PlatformUtilsService {
|
||||
getAutofillKeyboardShortcut(): Promise<string> {
|
||||
return null;
|
||||
}
|
||||
|
||||
supportsNativeWebauthn(): boolean {
|
||||
return false;
|
||||
}
|
||||
performNativeWebauthnAuthentication(
|
||||
challenge: string,
|
||||
credentials: Array<string>,
|
||||
origin: string,
|
||||
): Promise<string> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
298
apps/desktop/desktop_native/Cargo.lock
generated
298
apps/desktop/desktop_native/Cargo.lock
generated
@@ -83,6 +83,45 @@ dependencies = [
|
||||
"x11rb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asn1-rs"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048"
|
||||
dependencies = [
|
||||
"asn1-rs-derive",
|
||||
"asn1-rs-impl",
|
||||
"displaydoc",
|
||||
"nom",
|
||||
"num-traits",
|
||||
"rusticata-macros",
|
||||
"thiserror",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asn1-rs-derive"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asn1-rs-impl"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-broadcast"
|
||||
version = "0.7.1"
|
||||
@@ -533,6 +572,29 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctap-hid-fido2"
|
||||
version = "3.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1452b85807c4da0e06a24df21886c643da501404d40a52da534ef08b8ec01779"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"anyhow",
|
||||
"base64",
|
||||
"byteorder",
|
||||
"cbc",
|
||||
"hex",
|
||||
"hidapi",
|
||||
"num",
|
||||
"pad",
|
||||
"ring",
|
||||
"serde",
|
||||
"serde_cbor",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"x509-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.2.8"
|
||||
@@ -622,6 +684,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.9"
|
||||
@@ -633,6 +701,20 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der-parser"
|
||||
version = "9.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553"
|
||||
dependencies = [
|
||||
"asn1-rs",
|
||||
"displaydoc",
|
||||
"nom",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"rusticata-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.11"
|
||||
@@ -666,6 +748,7 @@ dependencies = [
|
||||
"byteorder",
|
||||
"cbc",
|
||||
"core-foundation",
|
||||
"ctap-hid-fido2",
|
||||
"dirs",
|
||||
"ed25519",
|
||||
"futures",
|
||||
@@ -686,6 +769,8 @@ dependencies = [
|
||||
"scopeguard",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"ssh-encoding",
|
||||
"ssh-key",
|
||||
@@ -764,6 +849,17 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlib"
|
||||
version = "0.5.2"
|
||||
@@ -1143,6 +1239,12 @@ dependencies = [
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "1.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.1"
|
||||
@@ -1173,6 +1275,19 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hidapi"
|
||||
version = "2.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03b876ecf37e86b359573c16c8366bc3eba52b689884a0fc42ba3f67203d2a8b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
@@ -1489,6 +1604,30 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-complex",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint-dig"
|
||||
version = "0.8.4"
|
||||
@@ -1506,6 +1645,15 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
@@ -1532,6 +1680,17 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
@@ -1659,6 +1818,15 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oid-registry"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9"
|
||||
dependencies = [
|
||||
"asn1-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
@@ -1697,6 +1865,15 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pad"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2ad9b889f1b12e0b9ee24db044b5129150d5eada288edc800f789928dc8c0e3"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.2.1"
|
||||
@@ -2025,6 +2202,21 @@ dependencies = [
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"libc",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.9.6"
|
||||
@@ -2071,6 +2263,15 @@ dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusticata-macros"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.37"
|
||||
@@ -2084,6 +2285,18 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "salsa20"
|
||||
version = "0.10.2"
|
||||
@@ -2153,24 +2366,46 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.214"
|
||||
version = "1.0.215"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
|
||||
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.214"
|
||||
name = "serde_cbor"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
|
||||
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
|
||||
dependencies = [
|
||||
"half",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.215"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.133"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.19"
|
||||
@@ -2343,6 +2578,25 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.26.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
@@ -2360,6 +2614,17 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "6.2.2"
|
||||
@@ -2628,6 +2893,12 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "version-compare"
|
||||
version = "0.2.0"
|
||||
@@ -3087,6 +3358,23 @@ version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d"
|
||||
|
||||
[[package]]
|
||||
name = "x509-parser"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69"
|
||||
dependencies = [
|
||||
"asn1-rs",
|
||||
"data-encoding",
|
||||
"der-parser",
|
||||
"lazy_static",
|
||||
"nom",
|
||||
"oid-registry",
|
||||
"rusticata-macros",
|
||||
"thiserror",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xdg-home"
|
||||
version = "1.3.0"
|
||||
|
||||
@@ -60,6 +60,9 @@ rand_chacha = "=0.3.1"
|
||||
pkcs8 = { version = "=0.10.2", features = ["alloc", "encryption", "pem"] }
|
||||
rsa = "=0.9.6"
|
||||
ed25519 = { version = "=2.2.3", features = ["pkcs8"] }
|
||||
ctap-hid-fido2 = "3.5.2"
|
||||
serde = { version = "1.0.215", features = ["derive", "serde_derive"] }
|
||||
serde_json = "1.0.133"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
widestring = { version = "=1.1.0", optional = true }
|
||||
|
||||
92
apps/desktop/desktop_native/core/src/fido2_client/mod.rs
Normal file
92
apps/desktop/desktop_native/core/src/fido2_client/mod.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
use base64::{prelude::BASE64_URL_SAFE_NO_PAD, Engine};
|
||||
use ctap_hid_fido2::{fidokey::GetAssertionArgsBuilder, Cfg, FidoKeyHidFactory};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Fido2ClientError {
|
||||
WrongPin,
|
||||
NoCredentials,
|
||||
NoDevice,
|
||||
InvalidInput,
|
||||
AssertionError,
|
||||
}
|
||||
|
||||
pub fn authenticate(challenge: String, credentials: Vec<String>, rpid: String, pin: Option<String>) -> Result<String, Fido2ClientError> {
|
||||
let device = FidoKeyHidFactory::create(&Cfg::init()).map_err(|_| Fido2ClientError::NoDevice)?;
|
||||
let clientdata = format!(r#"{{"type":"webauthn.get","challenge":"{}","origin":"https://{}","crossOrigin": true}}"#, challenge, rpid);
|
||||
|
||||
let mut get_assertion_args = GetAssertionArgsBuilder::new(rpid.as_str(), clientdata.as_bytes());
|
||||
|
||||
let result = if let Some(pin) = pin {
|
||||
get_assertion_args = get_assertion_args.pin(&pin.as_str());
|
||||
let get_assertion_args = get_assertion_args.build();
|
||||
device.get_assertion_with_args(&get_assertion_args)
|
||||
} else {
|
||||
let mut get_assertion_args = get_assertion_args
|
||||
.without_pin_and_uv();
|
||||
for cred in credentials {
|
||||
let credid_bytes = BASE64_URL_SAFE_NO_PAD.decode(cred.as_bytes()).map_err(|_| Fido2ClientError::InvalidInput)?;
|
||||
get_assertion_args = get_assertion_args.credential_id(&credid_bytes);
|
||||
}
|
||||
let get_assertion_args = get_assertion_args.build();
|
||||
device.get_assertion_with_args(&get_assertion_args)
|
||||
};
|
||||
|
||||
let assertion = match result {
|
||||
Ok(assertion) => assertion,
|
||||
Err(_) => {
|
||||
return Err(Fido2ClientError::NoCredentials);
|
||||
}
|
||||
};
|
||||
|
||||
let assertion = assertion.get(0).ok_or(Fido2ClientError::AssertionError)?;
|
||||
|
||||
let twofa_token = TwoFactorAuthToken{
|
||||
id: BASE64_URL_SAFE_NO_PAD.encode(assertion.credential_id.as_slice()),
|
||||
raw_id: BASE64_URL_SAFE_NO_PAD.encode(assertion.credential_id.as_slice()),
|
||||
type_: "public-key".to_string(),
|
||||
response: WebauthnResponseData {
|
||||
authenticator_data: BASE64_URL_SAFE_NO_PAD.encode(
|
||||
&assertion.auth_data.as_slice()
|
||||
),
|
||||
client_data_json: BASE64_URL_SAFE_NO_PAD.encode(
|
||||
&clientdata.as_bytes()
|
||||
),
|
||||
signature: BASE64_URL_SAFE_NO_PAD.encode(
|
||||
&assertion.signature.as_slice()
|
||||
),
|
||||
},
|
||||
extensions: WebauthnExtensions {
|
||||
appid: Some(false),
|
||||
},
|
||||
};
|
||||
let twofa_token = serde_json::to_string(&twofa_token).map_err(|_| Fido2ClientError::AssertionError)?;
|
||||
|
||||
Ok(twofa_token)
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct TwoFactorAuthToken {
|
||||
id: String,
|
||||
#[serde(rename = "rawId")]
|
||||
raw_id: String,
|
||||
#[serde(rename = "type")]
|
||||
type_: String,
|
||||
extensions: WebauthnExtensions,
|
||||
#[serde(rename = "response")]
|
||||
response: WebauthnResponseData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct WebauthnExtensions {
|
||||
appid: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct WebauthnResponseData {
|
||||
#[serde(rename = "authenticatorData")]
|
||||
authenticator_data: String,
|
||||
#[serde(rename = "clientDataJson")]
|
||||
client_data_json: String,
|
||||
signature: String,
|
||||
}
|
||||
@@ -12,5 +12,7 @@ pub mod process_isolation;
|
||||
#[cfg(feature = "sys")]
|
||||
pub mod powermonitor;
|
||||
#[cfg(feature = "sys")]
|
||||
|
||||
pub mod ssh_agent;
|
||||
|
||||
#[cfg(feature = "sys")]
|
||||
pub mod fido2_client;
|
||||
|
||||
3
apps/desktop/desktop_native/napi/index.d.ts
vendored
3
apps/desktop/desktop_native/napi/index.d.ts
vendored
@@ -123,3 +123,6 @@ export declare namespace ipc {
|
||||
send(message: string): number
|
||||
}
|
||||
}
|
||||
export declare namespace fido2_hid_client {
|
||||
export function authenticate(challenge: string, credentials: Array<string>, rpid: string, pin: string): string
|
||||
}
|
||||
|
||||
@@ -522,3 +522,13 @@ pub mod ipc {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub mod fido2_hid_client {
|
||||
#[napi]
|
||||
pub fn authenticate(challenge: String, credentials: Vec<String>, rpid: String, pin: String) -> napi::Result<String> {
|
||||
let pin = if pin.is_empty() { None } else { Some(pin) };
|
||||
desktop_core::fido2_client::authenticate(challenge, credentials, rpid, pin)
|
||||
.map_err(|e| napi::Error::from_reason(format!("Error authenticating: {:?}", e)))
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ import { DesktopCredentialStorageListener } from "./platform/main/desktop-creden
|
||||
import { MainCryptoFunctionService } from "./platform/main/main-crypto-function.service";
|
||||
import { MainSshAgentService } from "./platform/main/main-ssh-agent.service";
|
||||
import { VersionMain } from "./platform/main/version.main";
|
||||
import { WebauthnListener } from "./platform/main/webauthn-listener";
|
||||
import { DesktopSettingsService } from "./platform/services/desktop-settings.service";
|
||||
import { ElectronLogMainService } from "./platform/services/electron-log.main.service";
|
||||
import { ElectronStorageService } from "./platform/services/electron-storage.service";
|
||||
@@ -75,6 +76,7 @@ export class Main {
|
||||
desktopAutofillSettingsService: DesktopAutofillSettingsService;
|
||||
versionMain: VersionMain;
|
||||
sshAgentService: MainSshAgentService;
|
||||
webauthnListener: WebauthnListener;
|
||||
|
||||
constructor() {
|
||||
// Set paths for portable builds
|
||||
@@ -254,6 +256,9 @@ export class Main {
|
||||
}
|
||||
});
|
||||
|
||||
this.webauthnListener = new WebauthnListener();
|
||||
this.webauthnListener.init();
|
||||
|
||||
new EphemeralValueStorageService();
|
||||
new SSOLocalhostCallbackService(this.environmentService, this.messagingService);
|
||||
}
|
||||
|
||||
18
apps/desktop/src/platform/main/webauthn-listener.ts
Normal file
18
apps/desktop/src/platform/main/webauthn-listener.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { ipcMain } from "electron";
|
||||
|
||||
import { fido2_hid_client } from "@bitwarden/desktop-napi";
|
||||
|
||||
export class WebauthnListener {
|
||||
constructor() {}
|
||||
|
||||
init() {
|
||||
ipcMain.handle("webauthn.authenticate", async (event: any, message: any) => {
|
||||
return fido2_hid_client.authenticate(
|
||||
message.challenge,
|
||||
message.credentials,
|
||||
message.origin,
|
||||
"",
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -119,6 +119,16 @@ const localhostCallbackService = {
|
||||
},
|
||||
};
|
||||
|
||||
const webauthn = {
|
||||
webauthnAuthenticate: (
|
||||
challenge: string,
|
||||
credentials: Array<string>,
|
||||
origin: string,
|
||||
): Promise<string> => {
|
||||
return ipcRenderer.invoke("webauthn.authenticate", { challenge, credentials, origin });
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
versions: {
|
||||
app: (): Promise<string> => ipcRenderer.invoke("appVersion"),
|
||||
@@ -190,6 +200,7 @@ export default {
|
||||
crypto,
|
||||
ephemeralStore,
|
||||
localhostCallbackService,
|
||||
webauthn,
|
||||
};
|
||||
|
||||
function deviceType(): DeviceType {
|
||||
|
||||
@@ -75,10 +75,20 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
|
||||
return (await this.getApplicationVersion()).split(/[+|-]/)[0].trim();
|
||||
}
|
||||
|
||||
// Temporarily restricted to only Windows until https://github.com/electron/electron/pull/28349
|
||||
// has been merged and an updated electron build is available.
|
||||
supportsWebAuthn(win: Window): boolean {
|
||||
return this.getDevice() === DeviceType.WindowsDesktop;
|
||||
return true;
|
||||
}
|
||||
|
||||
supportsNativeWebauthn(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
performNativeWebauthnAuthentication(
|
||||
challenge: string,
|
||||
credentials: Array<string>,
|
||||
origin: string,
|
||||
): Promise<string> {
|
||||
return ipc.platform.webauthn.webauthnAuthenticate(challenge, credentials, origin);
|
||||
}
|
||||
|
||||
supportsDuo(): boolean {
|
||||
|
||||
@@ -193,4 +193,15 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
|
||||
getAutofillKeyboardShortcut(): Promise<string> {
|
||||
return null;
|
||||
}
|
||||
|
||||
supportsNativeWebauthn(): boolean {
|
||||
return false;
|
||||
}
|
||||
performNativeWebauthnAuthentication(
|
||||
challenge: string,
|
||||
credentials: Array<string>,
|
||||
origin: string,
|
||||
): Promise<string> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +121,22 @@ export class TwoFactorAuthWebAuthnComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.platformUtilsService.supportsNativeWebauthn()) {
|
||||
const challenge = providerData.challenge;
|
||||
const rpId = providerData.rpId;
|
||||
const creds = providerData.allowCredentials as any as Array<string>;
|
||||
const credentials = creds.map((c: any) => {
|
||||
return c.id;
|
||||
});
|
||||
const resp = await this.platformUtilsService.performNativeWebauthnAuthentication(
|
||||
challenge,
|
||||
credentials,
|
||||
rpId,
|
||||
);
|
||||
this.token.emit(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
this.webAuthn.init(providerData);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,12 @@ export abstract class PlatformUtilsService {
|
||||
abstract getApplicationVersion(): Promise<string>;
|
||||
abstract getApplicationVersionNumber(): Promise<string>;
|
||||
abstract supportsWebAuthn(win: Window): boolean;
|
||||
abstract supportsNativeWebauthn(): boolean;
|
||||
abstract performNativeWebauthnAuthentication(
|
||||
challenge: string,
|
||||
credentials: Array<string>,
|
||||
origin: string,
|
||||
): Promise<string>;
|
||||
abstract supportsDuo(): boolean;
|
||||
/**
|
||||
* @deprecated use `@bitwarden/components/ToastService.showToast` instead
|
||||
|
||||
Reference in New Issue
Block a user