1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-10 05:13:29 +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:
Bernd Schoolmann
2025-07-15 11:53:58 +02:00
committed by GitHub
parent 1315e7c37c
commit 8250e40c6c
27 changed files with 44 additions and 697 deletions

View File

@@ -398,17 +398,7 @@
reviewers: ["team:team-vault-dev"],
},
{
matchPackageNames: [
"@types/argon2-browser",
"aes",
"argon2",
"argon2-browser",
"big-integer",
"cbc",
"rsa",
"russh-cryptovec",
"sha2",
],
matchPackageNames: ["aes", "big-integer", "cbc", "rsa", "russh-cryptovec", "sha2"],
description: "Key Management owned dependencies",
commitMessagePrefix: "[deps] KM:",
reviewers: ["team:team-key-management-dev"],

View File

@@ -163,10 +163,6 @@ jobs:
FILES=$(find . -maxdepth 1 -type f)
for FILE in $FILES; do cp "$FILE" browser-source/; done
# Copy patches to the Browser source directory
mkdir -p browser-source/patches
cp -r patches/* browser-source/patches
# Copy apps/browser to the Browser source directory
mkdir -p browser-source/apps/browser
cp -r apps/browser/* browser-source/apps/browser

View File

@@ -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"

View File

@@ -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",

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"] }

View File

@@ -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,);
}
}

View File

@@ -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)]

View File

@@ -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
}

View File

@@ -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]

View File

@@ -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);
}
}

View File

@@ -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({

View File

@@ -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 () => {

View File

@@ -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,

View File

@@ -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,
);
},
);
}
}

View File

@@ -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 }),

View File

@@ -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,

View File

@@ -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: {

View File

@@ -12,13 +12,6 @@ export abstract class CryptoFunctionService {
algorithm: "sha256" | "sha512",
iterations: number,
): Promise<Uint8Array>;
abstract argon2(
password: string | Uint8Array,
salt: string | Uint8Array,
iterations: number,
memory: number,
parallelism: number,
): Promise<Uint8Array>;
abstract hkdf(
ikm: Uint8Array,
salt: string | Uint8Array,

View File

@@ -1,4 +1,3 @@
import * as argon2 from "argon2-browser";
import * as forge from "node-forge";
import { EncryptionType } from "../../../platform/enums";
@@ -14,7 +13,6 @@ import { CryptoFunctionService } from "../abstractions/crypto-function.service";
export class WebCryptoFunctionService implements CryptoFunctionService {
private crypto: Crypto;
private subtle: SubtleCrypto;
private wasmSupported: boolean;
constructor(globalContext: { crypto: Crypto }) {
if (globalContext?.crypto?.subtle == null) {
@@ -24,7 +22,6 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
}
this.crypto = globalContext.crypto;
this.subtle = this.crypto.subtle;
this.wasmSupported = this.checkIfWasmSupported();
}
async pbkdf2(
@@ -55,33 +52,6 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
return new Uint8Array(buffer);
}
async argon2(
password: string | Uint8Array,
salt: string | Uint8Array,
iterations: number,
memory: number,
parallelism: number,
): Promise<Uint8Array> {
if (!this.wasmSupported) {
throw "Webassembly support is required for the Argon2 KDF feature.";
}
const passwordArr = new Uint8Array(this.toBuf(password));
const saltArr = new Uint8Array(this.toBuf(salt));
const result = await argon2.hash({
pass: passwordArr,
salt: saltArr,
time: iterations,
mem: memory,
parallelism: parallelism,
hashLen: 32,
type: argon2.ArgonType.Argon2id,
});
argon2.unloadRuntime();
return result.hash;
}
async hkdf(
ikm: Uint8Array,
salt: string | Uint8Array,
@@ -442,21 +412,4 @@ export class WebCryptoFunctionService implements CryptoFunctionService {
private toWebCryptoAesMode(mode: "cbc" | "ecb"): string {
return mode === "cbc" ? "AES-CBC" : "AES-ECB";
}
// ref: https://stackoverflow.com/a/47880734/1090359
private checkIfWasmSupported(): boolean {
try {
if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") {
const module = new WebAssembly.Module(
Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00),
);
if (module instanceof WebAssembly.Module) {
return new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
}
}
} catch {
return false;
}
return false;
}
}

View File

@@ -6,6 +6,7 @@ import { PBKDF2KdfConfig, Argon2KdfConfig } from "@bitwarden/key-management";
import { CryptoFunctionService } from "../../key-management/crypto/abstractions/crypto-function.service";
import { CsprngArray } from "../../types/csprng";
import { SdkLoadService } from "../abstractions/sdk/sdk-load.service";
import { EncryptionType } from "../enums";
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
@@ -77,27 +78,30 @@ describe("KeyGenerationService", () => {
describe("deriveKeyFromPassword", () => {
it("should derive a 32 byte key from a password using pbkdf2", async () => {
const password = "password";
const salt = "salt";
const password = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
const salt = new Uint8Array([9, 10, 11, 12]);
const kdfConfig = new PBKDF2KdfConfig(600_000);
cryptoFunctionService.pbkdf2.mockResolvedValue(new Uint8Array(32));
Object.defineProperty(SdkLoadService, "Ready", {
value: Promise.resolve(),
configurable: true,
});
const key = await sut.deriveKeyFromPassword(password, salt, kdfConfig);
expect(key.inner().type).toEqual(EncryptionType.AesCbc256_B64);
});
it("should derive a 32 byte key from a password using argon2id", async () => {
const password = "password";
const salt = "salt";
const password = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
const salt = new Uint8Array([9, 10, 11, 12]);
const kdfConfig = new Argon2KdfConfig(3, 16, 4);
cryptoFunctionService.hash.mockResolvedValue(new Uint8Array(32));
cryptoFunctionService.argon2.mockResolvedValue(new Uint8Array(32));
Object.defineProperty(SdkLoadService, "Ready", {
value: Promise.resolve(),
configurable: true,
});
const key = await sut.deriveKeyFromPassword(password, salt, kdfConfig);
expect(key.inner().type).toEqual(EncryptionType.AesCbc256_B64);
});
});

View File

@@ -2,11 +2,13 @@
// @ts-strict-ignore
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
// eslint-disable-next-line no-restricted-imports
import { KdfConfig, PBKDF2KdfConfig, Argon2KdfConfig, KdfType } from "@bitwarden/key-management";
import { KdfConfig } from "@bitwarden/key-management";
import { PureCrypto } from "@bitwarden/sdk-internal";
import { CryptoFunctionService } from "../../key-management/crypto/abstractions/crypto-function.service";
import { CsprngArray } from "../../types/csprng";
import { KeyGenerationService as KeyGenerationServiceAbstraction } from "../abstractions/key-generation.service";
import { SdkLoadService } from "../abstractions/sdk/sdk-load.service";
import { EncryptionType } from "../enums";
import { Utils } from "../misc/utils";
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
@@ -47,38 +49,17 @@ export class KeyGenerationService implements KeyGenerationServiceAbstraction {
salt: string | Uint8Array,
kdfConfig: KdfConfig,
): Promise<SymmetricCryptoKey> {
let key: Uint8Array = null;
if (kdfConfig.kdfType == null || kdfConfig.kdfType === KdfType.PBKDF2_SHA256) {
if (kdfConfig.iterations == null) {
kdfConfig.iterations = PBKDF2KdfConfig.ITERATIONS.defaultValue;
}
key = await this.cryptoFunctionService.pbkdf2(password, salt, "sha256", kdfConfig.iterations);
} else if (kdfConfig.kdfType == KdfType.Argon2id) {
if (kdfConfig.iterations == null) {
kdfConfig.iterations = Argon2KdfConfig.ITERATIONS.defaultValue;
}
if (kdfConfig.memory == null) {
kdfConfig.memory = Argon2KdfConfig.MEMORY.defaultValue;
}
if (kdfConfig.parallelism == null) {
kdfConfig.parallelism = Argon2KdfConfig.PARALLELISM.defaultValue;
}
const saltHash = await this.cryptoFunctionService.hash(salt, "sha256");
key = await this.cryptoFunctionService.argon2(
password,
saltHash,
kdfConfig.iterations,
kdfConfig.memory * 1024, // convert to KiB from MiB
kdfConfig.parallelism,
);
} else {
throw new Error("Unknown Kdf.");
if (typeof password === "string") {
password = new TextEncoder().encode(password);
}
return new SymmetricCryptoKey(key);
if (typeof salt === "string") {
salt = new TextEncoder().encode(salt);
}
await SdkLoadService.Ready;
return new SymmetricCryptoKey(
PureCrypto.derive_kdf_material(password, salt, kdfConfig.toSdkConfig()),
);
}
async stretchKey(key: SymmetricCryptoKey): Promise<SymmetricCryptoKey> {

View File

@@ -33,29 +33,6 @@ export class NodeCryptoFunctionService implements CryptoFunctionService {
});
}
async argon2(
password: string | Uint8Array,
salt: string | Uint8Array,
iterations: number,
memory: number,
parallelism: number,
): Promise<Uint8Array> {
const nodePassword = this.toNodeValue(password);
const nodeSalt = this.toNodeBuffer(this.toUint8Buffer(salt));
const argon2 = await import("argon2");
const hash = await argon2.hash(nodePassword, {
salt: nodeSalt,
raw: true,
hashLength: 32,
timeCost: iterations,
memoryCost: memory,
parallelism: parallelism,
type: argon2.argon2id,
});
return this.toUint8Buffer(hash);
}
// ref: https://tools.ietf.org/html/rfc5869
async hkdf(
ikm: Uint8Array,

269
package-lock.json generated
View File

@@ -7,7 +7,6 @@
"": {
"name": "@bitwarden/clients",
"version": "0.0.0",
"hasInstallScript": true,
"license": "GPL-3.0",
"workspaces": [
"apps/*",
@@ -36,8 +35,6 @@
"@nx/eslint": "21.1.2",
"@nx/jest": "21.1.2",
"@nx/js": "21.1.2",
"argon2": "0.41.1",
"argon2-browser": "1.18.0",
"big-integer": "1.6.52",
"bootstrap": "4.6.0",
"braintree-web-drop-in": "1.44.0",
@@ -64,7 +61,6 @@
"oidc-client-ts": "2.4.1",
"open": "10.1.2",
"papaparse": "5.5.3",
"patch-package": "8.0.0",
"proper-lockfile": "4.1.2",
"qrcode-parser": "2.1.3",
"qrious": "4.0.2",
@@ -102,7 +98,6 @@
"@storybook/test-runner": "0.22.0",
"@storybook/theming": "8.6.12",
"@storybook/web-components-webpack5": "8.6.12",
"@types/argon2-browser": "1.18.4",
"@types/chrome": "0.0.306",
"@types/firefox-webext-browser": "120.0.4",
"@types/inquirer": "8.2.10",
@@ -206,7 +201,6 @@
"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",
@@ -9873,15 +9867,6 @@
"node": ">=4.2.0"
}
},
"node_modules/@phc/format": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz",
"integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/@phenomnomnominal/tsquery": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz",
@@ -12071,13 +12056,6 @@
"@types/node": "*"
}
},
"node_modules/@types/argon2-browser": {
"version": "1.18.4",
"resolved": "https://registry.npmjs.org/@types/argon2-browser/-/argon2-browser-1.18.4.tgz",
"integrity": "sha512-K/PHAEKzdCY4mCRhgUTBcuTxeaJyLoPcd5pJ1UFSTb/FAPjj3TCK4EM/DvNmVtDzkQBMD5peJjtch3kVQDf4YQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/aria-query": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
@@ -14799,27 +14777,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/argon2": {
"version": "0.41.1",
"resolved": "https://registry.npmjs.org/argon2/-/argon2-0.41.1.tgz",
"integrity": "sha512-dqCW8kJXke8Ik+McUcMDltrbuAWETPyU6iq+4AhxqKphWi7pChB/Zgd/Tp/o8xRLbg8ksMj46F/vph9wnxpTzQ==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@phc/format": "^1.0.0",
"node-addon-api": "^8.1.0",
"node-gyp-build": "^4.8.1"
},
"engines": {
"node": ">=16.17.0"
}
},
"node_modules/argon2-browser": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/argon2-browser/-/argon2-browser-1.18.0.tgz",
"integrity": "sha512-ImVAGIItnFnvET1exhsQB7apRztcoC5TnlSqernMJDUjbc/DLq3UEYeXFrLPrlaIl8cVfwnXb6wX2KpFf2zxHw==",
"license": "MIT"
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -16390,6 +16347,7 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
"integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.0",
@@ -18210,6 +18168,7 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0",
@@ -20914,15 +20873,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/find-yarn-workspace-root": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz",
"integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==",
"license": "Apache-2.0",
"dependencies": {
"micromatch": "^4.0.2"
}
},
"node_modules/flat": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
@@ -21939,6 +21889,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0"
@@ -23621,6 +23572,7 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
"dev": true,
"license": "MIT"
},
"node_modules/isbinaryfile": {
@@ -25682,25 +25634,6 @@
"dev": true,
"license": "BSD-2-Clause"
},
"node_modules/json-stable-stringify": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.3.0.tgz",
"integrity": "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==",
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.8",
"call-bound": "^1.0.4",
"isarray": "^2.0.5",
"jsonify": "^0.0.1",
"object-keys": "^1.1.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
@@ -25744,15 +25677,6 @@
"graceful-fs": "^4.1.6"
}
},
"node_modules/jsonify": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz",
"integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==",
"license": "Public Domain",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/jsonparse": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
@@ -25875,15 +25799,6 @@
"node": ">=0.10.0"
}
},
"node_modules/klaw-sync": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
"integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.1.11"
}
},
"node_modules/kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@@ -29102,15 +29017,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/node-addon-api": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.4.0.tgz",
"integrity": "sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg==",
"license": "MIT",
"engines": {
"node": "^18 || ^20 || >= 21"
}
},
"node_modules/node-api-version": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz",
@@ -30703,6 +30609,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -31585,171 +31492,6 @@
"tslib": "^2.0.3"
}
},
"node_modules/patch-package": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz",
"integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==",
"license": "MIT",
"dependencies": {
"@yarnpkg/lockfile": "^1.1.0",
"chalk": "^4.1.2",
"ci-info": "^3.7.0",
"cross-spawn": "^7.0.3",
"find-yarn-workspace-root": "^2.0.0",
"fs-extra": "^9.0.0",
"json-stable-stringify": "^1.0.2",
"klaw-sync": "^6.0.0",
"minimist": "^1.2.6",
"open": "^7.4.2",
"rimraf": "^2.6.3",
"semver": "^7.5.3",
"slash": "^2.0.0",
"tmp": "^0.0.33",
"yaml": "^2.2.2"
},
"bin": {
"patch-package": "index.js"
},
"engines": {
"node": ">=14",
"npm": ">5"
}
},
"node_modules/patch-package/node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/patch-package/node_modules/fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"license": "MIT",
"dependencies": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/patch-package/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/patch-package/node_modules/is-docker": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
"license": "MIT",
"bin": {
"is-docker": "cli.js"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/patch-package/node_modules/is-wsl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
"license": "MIT",
"dependencies": {
"is-docker": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/patch-package/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/patch-package/node_modules/open": {
"version": "7.4.2",
"resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz",
"integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==",
"license": "MIT",
"dependencies": {
"is-docker": "^2.0.0",
"is-wsl": "^2.1.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/patch-package/node_modules/rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"license": "ISC",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
}
},
"node_modules/patch-package/node_modules/slash": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
"integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/patch-package/node_modules/tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
"license": "MIT",
"dependencies": {
"os-tmpdir": "~1.0.2"
},
"engines": {
"node": ">=0.6.0"
}
},
"node_modules/path-browserify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
@@ -34504,6 +34246,7 @@
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"dev": true,
"license": "MIT",
"dependencies": {
"define-data-property": "^1.1.4",

View File

@@ -29,8 +29,7 @@
"build-storybook": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" ng run components:build-storybook",
"build-storybook:ci": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" ng run components:build-storybook --webpack-stats-json",
"test-stories": "test-storybook --url http://localhost:6006",
"test-stories:watch": "test-stories --watch",
"postinstall": "patch-package"
"test-stories:watch": "test-stories --watch"
},
"workspaces": [
"apps/*",
@@ -62,7 +61,6 @@
"@storybook/test-runner": "0.22.0",
"@storybook/theming": "8.6.12",
"@storybook/web-components-webpack5": "8.6.12",
"@types/argon2-browser": "1.18.4",
"@types/chrome": "0.0.306",
"@types/firefox-webext-browser": "120.0.4",
"@types/inquirer": "8.2.10",
@@ -172,8 +170,6 @@
"@nx/eslint": "21.1.2",
"@nx/jest": "21.1.2",
"@nx/js": "21.1.2",
"argon2": "0.41.1",
"argon2-browser": "1.18.0",
"big-integer": "1.6.52",
"bootstrap": "4.6.0",
"braintree-web-drop-in": "1.44.0",
@@ -200,7 +196,6 @@
"oidc-client-ts": "2.4.1",
"open": "10.1.2",
"papaparse": "5.5.3",
"patch-package": "8.0.0",
"proper-lockfile": "4.1.2",
"qrcode-parser": "2.1.3",
"qrious": "4.0.2",

View File

@@ -1,67 +0,0 @@
diff --git a/node_modules/argon2-browser/lib/argon2.js b/node_modules/argon2-browser/lib/argon2.js
index ffa77a5..98b2f13 100644
--- a/node_modules/argon2-browser/lib/argon2.js
+++ b/node_modules/argon2-browser/lib/argon2.js
@@ -78,16 +78,27 @@
if (global.loadArgon2WasmBinary) {
return global.loadArgon2WasmBinary();
}
+
+ const simd = checkIfSIMDSupported();
+
if (typeof require === 'function') {
- return Promise.resolve(require('../dist/argon2.wasm')).then(
- (wasmModule) => {
- return decodeWasmBinary(wasmModule);
- }
- );
+ if (simd) {
+ return Promise.resolve(require('../dist/argon2-simd.wasm')).then(
+ (wasmModule) => {
+ return decodeWasmBinary(wasmModule);
+ }
+ );
+ } else {
+ return Promise.resolve(require('../dist/argon2.wasm')).then(
+ (wasmModule) => {
+ return decodeWasmBinary(wasmModule);
+ }
+ );
+ }
}
const wasmPath =
global.argon2WasmPath ||
- 'node_modules/argon2-browser/dist/argon2.wasm';
+ 'node_modules/argon2-browser/dist/argon2' + (simd? "-simd" : "") + '.wasm';
return fetch(wasmPath)
.then((response) => response.arrayBuffer())
.then((ab) => new Uint8Array(ab));
@@ -351,7 +362,28 @@
loadModule._module.unloadRuntime();
delete loadModule._promise;
delete loadModule._module;
+ if (typeof require !== 'undefined') {
+ delete require.cache[require.resolve('../dist/argon2.js')]
+ }
+ }
+ }
+
+ // ref: https://stackoverflow.com/a/47880734/1090359
+ // ref: https://github.com/GoogleChromeLabs/wasm-feature-detect/blob/main/src/detectors/simd/module.wat (translated with wat2wasm)
+ function checkIfSIMDSupported() {
+ try {
+ if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") {
+ const module = new WebAssembly.Module(
+ Uint8Array.of(0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7b, 0x03, 0x02, 0x01, 0x00, 0x0a, 0x0a, 0x01, 0x08, 0x00, 0x41, 0x00, 0xfd, 0x0f, 0xfd, 0x62, 0x0b)
+ );
+ if (module instanceof WebAssembly.Module) {
+ return new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
+ }
+ }
+ } catch {
+ return false;
}
+ return false;
}
return {