mirror of
https://github.com/bitwarden/browser
synced 2026-01-02 16:43:19 +00:00
[BEEEP] [PM-565] Implement clipboard logic in rust (#4516)
Implement the Desktop clipboard logic natively using rust. This uses the arboard crate for clipboard functionality. This change consists of 3 portions: * Rust component. * Updating renderer to call main using electron ipc. * Update main to listen to renderer ipc and forward calls to the native clipboard module.
This commit is contained in:
558
apps/desktop/desktop_native/Cargo.lock
generated
558
apps/desktop/desktop_native/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,7 @@ manual_test = []
|
||||
[dependencies]
|
||||
aes = "=0.8.2"
|
||||
anyhow = "=1.0.71"
|
||||
arboard = { version = "=3.2.1", default-features = false }
|
||||
base64 = "=0.21.2"
|
||||
cbc = { version = "=0.1.2", features = ["alloc"] }
|
||||
napi = { version = "=2.13.1", features = ["async"] }
|
||||
@@ -31,7 +32,7 @@ typenum = "=1.16.0"
|
||||
napi-build = "=2.0.1"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
widestring = "=0.5.1"
|
||||
widestring = "=1.0.2"
|
||||
windows = { version = "=0.48.0", features = [
|
||||
"Foundation",
|
||||
"Security_Credentials_UI",
|
||||
|
||||
4
apps/desktop/desktop_native/index.d.ts
vendored
4
apps/desktop/desktop_native/index.d.ts
vendored
@@ -37,3 +37,7 @@ export namespace biometrics {
|
||||
ivB64: string
|
||||
}
|
||||
}
|
||||
export namespace clipboards {
|
||||
export function read(): Promise<string>
|
||||
export function write(text: string, password: boolean): Promise<void>
|
||||
}
|
||||
|
||||
@@ -206,7 +206,8 @@ if (!nativeBinding) {
|
||||
throw new Error(`Failed to load native binding`)
|
||||
}
|
||||
|
||||
const { passwords, biometrics } = nativeBinding
|
||||
const { passwords, biometrics, clipboards } = nativeBinding
|
||||
|
||||
module.exports.passwords = passwords
|
||||
module.exports.biometrics = biometrics
|
||||
module.exports.clipboards = clipboards
|
||||
|
||||
@@ -7,8 +7,8 @@ use rand::RngCore;
|
||||
use retry::delay::Fixed;
|
||||
use sha2::{Digest, Sha256};
|
||||
use windows::{
|
||||
h,
|
||||
core::{factory, HSTRING},
|
||||
h,
|
||||
Foundation::IAsyncOperation,
|
||||
Security::{
|
||||
Credentials::{
|
||||
@@ -241,7 +241,9 @@ fn set_focus(window: HWND) {
|
||||
impl KeyMaterial {
|
||||
fn digest_material(&self) -> String {
|
||||
match self.client_key_part_b64.as_deref() {
|
||||
Some(client_key_part_b64) => format!("{}|{}", self.os_key_part_b64, client_key_part_b64),
|
||||
Some(client_key_part_b64) => {
|
||||
format!("{}|{}", self.os_key_part_b64, client_key_part_b64)
|
||||
}
|
||||
None => self.os_key_part_b64.clone(),
|
||||
}
|
||||
}
|
||||
@@ -419,7 +421,14 @@ mod tests {
|
||||
let mut key_material = key_material();
|
||||
key_material.client_key_part_b64 = None;
|
||||
let result = key_material.derive_key().unwrap();
|
||||
assert_eq!(result, [81, 100, 62, 172, 151, 119, 182, 58, 123, 38, 129, 116, 209, 253, 66, 118, 218, 237, 236, 155, 201, 234, 11, 198, 229, 171, 246, 144, 71, 188, 84, 246].into());
|
||||
assert_eq!(
|
||||
result,
|
||||
[
|
||||
81, 100, 62, 172, 151, 119, 182, 58, 123, 38, 129, 116, 209, 253, 66, 118, 218,
|
||||
237, 236, 155, 201, 234, 11, 198, 229, 171, 246, 144, 71, 188, 84, 246
|
||||
]
|
||||
.into()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
56
apps/desktop/desktop_native/src/clipboard.rs
Normal file
56
apps/desktop/desktop_native/src/clipboard.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use anyhow::Result;
|
||||
use arboard::{Clipboard, Set};
|
||||
|
||||
pub fn read() -> Result<String> {
|
||||
let mut clipboard = Clipboard::new()?;
|
||||
|
||||
Ok(clipboard.get_text()?)
|
||||
}
|
||||
|
||||
pub fn write(text: &str, password: bool) -> Result<()> {
|
||||
let mut clipboard = Clipboard::new()?;
|
||||
|
||||
let set = clipboard_set(clipboard.set(), password);
|
||||
|
||||
set.text(text)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Exclude from windows clipboard history
|
||||
#[cfg(target_os = "windows")]
|
||||
fn clipboard_set(set: Set, password: bool) -> Set {
|
||||
use arboard::SetExtWindows;
|
||||
|
||||
if password {
|
||||
set.exclude_from_cloud().exclude_from_history()
|
||||
} else {
|
||||
set
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for clipboard to be available on linux
|
||||
#[cfg(target_os = "linux")]
|
||||
fn clipboard_set(set: Set, _password: bool) -> Set {
|
||||
use arboard::SetExtLinux;
|
||||
|
||||
set.wait()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn clipboard_set(set: Set, _password: bool) -> Set {
|
||||
set
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "manual_test", not(target_os = "linux")))]
|
||||
fn test_write_read() {
|
||||
let message = "Hello world!";
|
||||
|
||||
write(message, false).unwrap();
|
||||
assert_eq!(message, read().unwrap());
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
extern crate napi_derive;
|
||||
|
||||
mod biometric;
|
||||
mod clipboard;
|
||||
mod crypto;
|
||||
mod error;
|
||||
mod password;
|
||||
@@ -107,3 +108,17 @@ pub mod biometrics {
|
||||
pub iv_b64: String,
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub mod clipboards {
|
||||
#[napi]
|
||||
pub async fn read() -> napi::Result<String> {
|
||||
super::clipboard::read().map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub async fn write(text: String, password: bool) -> napi::Result<()> {
|
||||
super::clipboard::write(&text, password)
|
||||
.map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ pub fn set_password(service: &str, account: &str, password: &str) -> Result<()>
|
||||
let credential = CREDENTIALW {
|
||||
Flags: CRED_FLAGS(CRED_FLAGS_NONE),
|
||||
Type: CRED_TYPE_GENERIC,
|
||||
TargetName: PWSTR(unsafe { target_name.as_mut_ptr() }),
|
||||
TargetName: PWSTR(target_name.as_mut_ptr()),
|
||||
Comment: PWSTR::null(),
|
||||
LastWritten: last_written,
|
||||
CredentialBlobSize: credential_len,
|
||||
@@ -104,7 +104,7 @@ pub fn set_password(service: &str, account: &str, password: &str) -> Result<()>
|
||||
AttributeCount: 0,
|
||||
Attributes: std::ptr::null_mut(),
|
||||
TargetAlias: PWSTR::null(),
|
||||
UserName: PWSTR(unsafe { user_name.as_mut_ptr() }),
|
||||
UserName: PWSTR(user_name.as_mut_ptr()),
|
||||
};
|
||||
|
||||
let result = unsafe { CredWriteW(&credential, 0) };
|
||||
|
||||
Reference in New Issue
Block a user