mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 22:33:35 +00:00
[PM-22271] Switch to SDK argon2 implementation, and drop other impls (#15401)
* Switch to SDK argon2 implementation * Cleanup and update to the latest sdk * Update package lock * Remove copy patch * Fix builds * Fix test build * Remove error * Fix tests * Fix build * Run prettier * Remove argon2 references * Regenerate index.d.ts for desktop_native napi * Replace mocked crypto function service type
This commit is contained in:
@@ -92,11 +92,6 @@ const moduleRules = [
|
||||
test: /\.[jt]sx?$/,
|
||||
loader: "@ngtools/webpack",
|
||||
},
|
||||
{
|
||||
test: /argon2(-simd)?\.wasm$/,
|
||||
loader: "base64-loader",
|
||||
type: "javascript/auto",
|
||||
},
|
||||
];
|
||||
|
||||
const requiredPlugins = [
|
||||
@@ -290,7 +285,6 @@ const mainConfig = {
|
||||
clean: true,
|
||||
},
|
||||
module: {
|
||||
noParse: /argon2(-simd)?\.wasm$/,
|
||||
rules: moduleRules,
|
||||
},
|
||||
experiments: {
|
||||
@@ -365,13 +359,7 @@ if (manifestVersion == 2) {
|
||||
test: /\.tsx?$/,
|
||||
loader: "ts-loader",
|
||||
},
|
||||
{
|
||||
test: /argon2(-simd)?\.wasm$/,
|
||||
loader: "base64-loader",
|
||||
type: "javascript/auto",
|
||||
},
|
||||
],
|
||||
noParse: /argon2(-simd)?\.wasm$/,
|
||||
},
|
||||
cache:
|
||||
ENV !== "development"
|
||||
|
||||
@@ -59,14 +59,12 @@
|
||||
},
|
||||
"pkg": {
|
||||
"assets": [
|
||||
"./build/**/*",
|
||||
"../../node_modules/argon2/**/*"
|
||||
"./build/**/*"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@koa/multer": "4.0.0",
|
||||
"@koa/router": "13.1.0",
|
||||
"argon2": "0.41.1",
|
||||
"big-integer": "1.6.52",
|
||||
"browser-hrtime": "1.1.8",
|
||||
"chalk": "4.1.2",
|
||||
|
||||
34
apps/desktop/desktop_native/Cargo.lock
generated
34
apps/desktop/desktop_native/Cargo.lock
generated
@@ -135,19 +135,6 @@ dependencies = [
|
||||
"x11rb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argon2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"blake2",
|
||||
"cpufeatures",
|
||||
"password-hash",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ashpd"
|
||||
version = "0.11.0"
|
||||
@@ -452,15 +439,6 @@ dependencies = [
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
@@ -887,7 +865,6 @@ dependencies = [
|
||||
"aes",
|
||||
"anyhow",
|
||||
"arboard",
|
||||
"argon2",
|
||||
"ashpd",
|
||||
"base64",
|
||||
"bitwarden-russh",
|
||||
@@ -2264,17 +2241,6 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
|
||||
@@ -12,7 +12,6 @@ publish = false
|
||||
aes = "=0.8.4"
|
||||
anyhow = "=1.0.94"
|
||||
arboard = { version = "=3.6.0", default-features = false }
|
||||
argon2 = "=0.5.3"
|
||||
ashpd = "=0.11.0"
|
||||
base64 = "=0.22.1"
|
||||
bindgen = "=0.72.0"
|
||||
|
||||
@@ -23,7 +23,6 @@ anyhow = { workspace = true }
|
||||
arboard = { workspace = true, features = [
|
||||
"wayland-data-control",
|
||||
] }
|
||||
argon2 = { workspace = true, features = ["zeroize"] }
|
||||
base64 = { workspace = true }
|
||||
byteorder = { workspace = true }
|
||||
cbc = { workspace = true, features = ["alloc"] }
|
||||
|
||||
@@ -5,7 +5,7 @@ use aes::cipher::{
|
||||
BlockEncryptMut, KeyIvInit,
|
||||
};
|
||||
|
||||
use crate::error::{CryptoError, KdfParamError, Result};
|
||||
use crate::error::{CryptoError, Result};
|
||||
|
||||
use super::CipherString;
|
||||
|
||||
@@ -33,53 +33,3 @@ pub fn encrypt_aes256(
|
||||
|
||||
Ok(CipherString::AesCbc256_B64 { iv, data })
|
||||
}
|
||||
|
||||
pub fn argon2(
|
||||
secret: &[u8],
|
||||
salt: &[u8],
|
||||
iterations: u32,
|
||||
memory: u32,
|
||||
parallelism: u32,
|
||||
) -> Result<[u8; 32]> {
|
||||
use argon2::*;
|
||||
|
||||
let params = Params::new(memory, iterations, parallelism, Some(32)).map_err(|e| {
|
||||
KdfParamError::InvalidParams(format!("Argon2 parameters are invalid: {e}",))
|
||||
})?;
|
||||
let argon = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
|
||||
|
||||
let mut hash = [0u8; 32];
|
||||
argon
|
||||
.hash_password_into(secret, salt, &mut hash)
|
||||
.map_err(|e| KdfParamError::InvalidParams(format!("Argon2 hashing failed: {e}",)))?;
|
||||
|
||||
// Argon2 is using some stack memory that is not zeroed. Eventually some function will
|
||||
// overwrite the stack, but we use this trick to force the used stack to be zeroed.
|
||||
#[inline(never)]
|
||||
fn clear_stack() {
|
||||
std::hint::black_box([0u8; 4096]);
|
||||
}
|
||||
clear_stack();
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_argon2() {
|
||||
let test_hash: [u8; 32] = [
|
||||
112, 200, 85, 209, 100, 4, 246, 146, 117, 180, 152, 44, 103, 198, 75, 14, 166, 77, 201,
|
||||
22, 62, 178, 87, 224, 95, 209, 253, 68, 166, 209, 47, 218,
|
||||
];
|
||||
let secret = b"supersecurepassword";
|
||||
let salt = b"mail@example.com";
|
||||
let iterations = 3;
|
||||
let memory = 1024 * 64;
|
||||
let parallelism = 4;
|
||||
|
||||
let hash = argon2(secret, salt, iterations, memory, parallelism).unwrap();
|
||||
assert_eq!(hash, test_hash,);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ pub enum Error {
|
||||
|
||||
#[error("Cryptography Error, {0}")]
|
||||
Crypto(#[from] CryptoError),
|
||||
#[error("KDF Parameter Error, {0}")]
|
||||
KdfParam(#[from] KdfParamError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
||||
3
apps/desktop/desktop_native/napi/index.d.ts
vendored
3
apps/desktop/desktop_native/napi/index.d.ts
vendored
@@ -195,9 +195,6 @@ export declare namespace autofill {
|
||||
completeError(clientId: number, sequenceNumber: number, error: string): number
|
||||
}
|
||||
}
|
||||
export declare namespace crypto {
|
||||
export function argon2(secret: Buffer, salt: Buffer, iterations: number, memory: number, parallelism: number): Promise<Buffer>
|
||||
}
|
||||
export declare namespace passkey_authenticator {
|
||||
export function register(): void
|
||||
}
|
||||
|
||||
@@ -798,25 +798,6 @@ pub mod autofill {
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub mod crypto {
|
||||
use napi::bindgen_prelude::Buffer;
|
||||
|
||||
#[napi]
|
||||
pub async fn argon2(
|
||||
secret: Buffer,
|
||||
salt: Buffer,
|
||||
iterations: u32,
|
||||
memory: u32,
|
||||
parallelism: u32,
|
||||
) -> napi::Result<Buffer> {
|
||||
desktop_core::crypto::argon2(&secret, &salt, iterations, memory, parallelism)
|
||||
.map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||
.map(|v| v.to_vec())
|
||||
.map(Buffer::from)
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub mod passkey_authenticator {
|
||||
#[napi]
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
|
||||
import { WebCryptoFunctionService } from "@bitwarden/common/key-management/crypto/services/web-crypto-function.service";
|
||||
|
||||
export class RendererCryptoFunctionService
|
||||
extends WebCryptoFunctionService
|
||||
implements CryptoFunctionService
|
||||
{
|
||||
constructor(win: Window | typeof global) {
|
||||
super(win);
|
||||
}
|
||||
|
||||
// We can't use the `argon2-browser` implementation because it loads WASM and the Content Security Policy doesn't allow it.
|
||||
// Rather than trying to weaken the policy, we'll just use the Node.js implementation though the IPC channel.
|
||||
// Note that the rest of the functions on this service will be inherited from the WebCryptoFunctionService, as those work just fine.
|
||||
async argon2(
|
||||
password: string | Uint8Array,
|
||||
salt: string | Uint8Array,
|
||||
iterations: number,
|
||||
memory: number,
|
||||
parallelism: number,
|
||||
): Promise<Uint8Array> {
|
||||
if (typeof password === "string") {
|
||||
password = new TextEncoder().encode(password);
|
||||
}
|
||||
if (typeof salt === "string") {
|
||||
salt = new TextEncoder().encode(salt);
|
||||
}
|
||||
|
||||
return await ipc.platform.crypto.argon2(password, salt, iterations, memory, parallelism);
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,7 @@ import { ClientType } from "@bitwarden/common/enums";
|
||||
import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service";
|
||||
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
|
||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||
import { WebCryptoFunctionService } from "@bitwarden/common/key-management/crypto/services/web-crypto-function.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
||||
import { DefaultProcessReloadService } from "@bitwarden/common/key-management/services/default-process-reload.service";
|
||||
import {
|
||||
@@ -140,7 +141,6 @@ import { DesktopFileDownloadService } from "./desktop-file-download.service";
|
||||
import { DesktopSetPasswordJitService } from "./desktop-set-password-jit.service";
|
||||
import { InitService } from "./init.service";
|
||||
import { NativeMessagingManifestService } from "./native-messaging-manifest.service";
|
||||
import { RendererCryptoFunctionService } from "./renderer-crypto-function.service";
|
||||
import { DesktopSetInitialPasswordService } from "./set-initial-password/desktop-set-initial-password.service";
|
||||
|
||||
const RELOAD_CALLBACK = new SafeInjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
@@ -296,7 +296,7 @@ const safeProviders: SafeProvider[] = [
|
||||
}),
|
||||
safeProvider({
|
||||
provide: CryptoFunctionServiceAbstraction,
|
||||
useClass: RendererCryptoFunctionService,
|
||||
useClass: WebCryptoFunctionService,
|
||||
deps: [WINDOW],
|
||||
}),
|
||||
safeProvider({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
|
||||
import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
|
||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
@@ -13,7 +14,6 @@ import {
|
||||
} from "@bitwarden/key-management";
|
||||
|
||||
import { WindowMain } from "../../main/window.main";
|
||||
import { MainCryptoFunctionService } from "../../platform/main/main-crypto-function.service";
|
||||
|
||||
import { MainBiometricsService } from "./main-biometrics.service";
|
||||
import OsBiometricsServiceLinux from "./os-biometrics-linux.service";
|
||||
@@ -35,7 +35,7 @@ describe("MainBiometricsService", function () {
|
||||
const windowMain = mock<WindowMain>();
|
||||
const logService = mock<LogService>();
|
||||
const biometricStateService = mock<BiometricStateService>();
|
||||
const cryptoFunctionService = mock<MainCryptoFunctionService>();
|
||||
const cryptoFunctionService = mock<CryptoFunctionService>();
|
||||
const encryptService = mock<EncryptService>();
|
||||
|
||||
it("Should call the platformspecific methods", async () => {
|
||||
|
||||
@@ -28,8 +28,9 @@ import { DefaultSingleUserStateProvider } from "@bitwarden/common/platform/state
|
||||
import { DefaultStateProvider } from "@bitwarden/common/platform/state/implementations/default-state.provider";
|
||||
import { StateEventRegistrarService } from "@bitwarden/common/platform/state/state-event-registrar.service";
|
||||
import { MemoryStorageService as MemoryStorageServiceForStateProviders } from "@bitwarden/common/platform/state/storage/memory-storage.service";
|
||||
import { DefaultBiometricStateService } from "@bitwarden/key-management";
|
||||
/* eslint-enable import/no-restricted-paths */
|
||||
import { DefaultBiometricStateService } from "@bitwarden/key-management";
|
||||
import { NodeCryptoFunctionService } from "@bitwarden/node/services/node-crypto-function.service";
|
||||
|
||||
import { MainSshAgentService } from "./autofill/main/main-ssh-agent.service";
|
||||
import { DesktopAutofillSettingsService } from "./autofill/services/desktop-autofill-settings.service";
|
||||
@@ -46,7 +47,6 @@ import { WindowMain } from "./main/window.main";
|
||||
import { NativeAutofillMain } from "./platform/main/autofill/native-autofill.main";
|
||||
import { ClipboardMain } from "./platform/main/clipboard.main";
|
||||
import { DesktopCredentialStorageListener } from "./platform/main/desktop-credential-storage-listener";
|
||||
import { MainCryptoFunctionService } from "./platform/main/main-crypto-function.service";
|
||||
import { VersionMain } from "./platform/main/version.main";
|
||||
import { DesktopSettingsService } from "./platform/services/desktop-settings.service";
|
||||
import { ElectronLogMainService } from "./platform/services/electron-log.main.service";
|
||||
@@ -68,7 +68,7 @@ export class Main {
|
||||
desktopCredentialStorageListener: DesktopCredentialStorageListener;
|
||||
mainBiometricsIpcListener: MainBiometricsIPCListener;
|
||||
desktopSettingsService: DesktopSettingsService;
|
||||
mainCryptoFunctionService: MainCryptoFunctionService;
|
||||
mainCryptoFunctionService: NodeCryptoFunctionService;
|
||||
migrationRunner: MigrationRunner;
|
||||
ssoUrlService: SsoUrlService;
|
||||
|
||||
@@ -140,8 +140,7 @@ export class Main {
|
||||
|
||||
this.i18nService = new I18nMainService("en", "./locales/", globalStateProvider);
|
||||
|
||||
this.mainCryptoFunctionService = new MainCryptoFunctionService();
|
||||
this.mainCryptoFunctionService.init();
|
||||
this.mainCryptoFunctionService = new NodeCryptoFunctionService();
|
||||
|
||||
const stateEventRegistrarService = new StateEventRegistrarService(
|
||||
globalStateProvider,
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import { ipcMain } from "electron";
|
||||
|
||||
import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
|
||||
import { crypto } from "@bitwarden/desktop-napi";
|
||||
import { NodeCryptoFunctionService } from "@bitwarden/node/services/node-crypto-function.service";
|
||||
|
||||
export class MainCryptoFunctionService
|
||||
extends NodeCryptoFunctionService
|
||||
implements CryptoFunctionService
|
||||
{
|
||||
init() {
|
||||
ipcMain.handle(
|
||||
"crypto.argon2",
|
||||
async (
|
||||
event,
|
||||
opts: {
|
||||
password: Uint8Array;
|
||||
salt: Uint8Array;
|
||||
iterations: number;
|
||||
memory: number;
|
||||
parallelism: number;
|
||||
},
|
||||
) => {
|
||||
return await crypto.argon2(
|
||||
Buffer.from(opts.password),
|
||||
Buffer.from(opts.salt),
|
||||
opts.iterations,
|
||||
opts.memory,
|
||||
opts.parallelism,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -98,17 +98,6 @@ const nativeMessaging = {
|
||||
},
|
||||
};
|
||||
|
||||
const crypto = {
|
||||
argon2: (
|
||||
password: Uint8Array,
|
||||
salt: Uint8Array,
|
||||
iterations: number,
|
||||
memory: number,
|
||||
parallelism: number,
|
||||
): Promise<Uint8Array> =>
|
||||
ipcRenderer.invoke("crypto.argon2", { password, salt, iterations, memory, parallelism }),
|
||||
};
|
||||
|
||||
const ephemeralStore = {
|
||||
setEphemeralValue: (key: string, value: string): Promise<void> =>
|
||||
ipcRenderer.invoke("setEphemeralValue", { key, value }),
|
||||
|
||||
@@ -41,11 +41,6 @@ const common = {
|
||||
},
|
||||
type: "asset/resource",
|
||||
},
|
||||
{
|
||||
test: /argon2(-simd)?\.wasm$/,
|
||||
loader: "base64-loader",
|
||||
type: "javascript/auto",
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [],
|
||||
@@ -154,13 +149,7 @@ const renderer = {
|
||||
test: /[\/\\]@angular[\/\\].+\.js$/,
|
||||
parser: { system: true },
|
||||
},
|
||||
{
|
||||
test: /argon2(-simd)?\.wasm$/,
|
||||
loader: "base64-loader",
|
||||
type: "javascript/auto",
|
||||
},
|
||||
],
|
||||
noParse: /argon2(-simd)?\.wasm$/,
|
||||
},
|
||||
experiments: {
|
||||
asyncWebAssembly: true,
|
||||
|
||||
@@ -90,11 +90,6 @@ const moduleRules = [
|
||||
test: /\.[jt]sx?$/,
|
||||
loader: "@ngtools/webpack",
|
||||
},
|
||||
{
|
||||
test: /argon2(-simd)?\.wasm$/,
|
||||
loader: "base64-loader",
|
||||
type: "javascript/auto",
|
||||
},
|
||||
];
|
||||
|
||||
const plugins = [
|
||||
@@ -404,7 +399,6 @@ const webpackConfig = {
|
||||
clean: true,
|
||||
},
|
||||
module: {
|
||||
noParse: /argon2(-simd)?\.wasm$/,
|
||||
rules: moduleRules,
|
||||
},
|
||||
experiments: {
|
||||
|
||||
Reference in New Issue
Block a user