Skip to content

Commit

Permalink
[PM-10413] ssh keygen on web and browser (#12176)
Browse files Browse the repository at this point in the history
* Move desktop to sdk ssh-key generation

* Add ssh keygen support on web and browser

* Move ssh keygen on all clients behind feature flag

* Update package lock

* Fix linting

* Fix build

* Fix build

* Remove rand_chacha

* Move libc to linux-only target

* Remove async-streams dep

* Make generateSshKey private

* Remove async from generate ssh key

* Update cargo lock

* Fix sdk init for ssh key generation

* Update index.d.ts

* Fix build on browser

* Fix build

* Fix build by updating libc dependency
  • Loading branch information
quexten authored Jan 8, 2025
1 parent 3949aae commit bb2961f
Show file tree
Hide file tree
Showing 21 changed files with 142 additions and 162 deletions.
2 changes: 1 addition & 1 deletion apps/browser/src/popup/services/services.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ const safeProviders: SafeProvider[] = [
}),
safeProvider({
provide: SdkClientFactory,
useFactory: (logService) =>
useFactory: (logService: LogService) =>
flagEnabled("sdk") ? new BrowserSdkClientFactory(logService) : new NoopSdkClientFactory(),
deps: [LogService],
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@
<i class="bwi bwi-sticky-note" slot="start" aria-hidden="true"></i>
{{ "note" | i18n }}
</a>
<a
bitMenuItem
[routerLink]="['/add-cipher']"
[queryParams]="buildQueryParams(cipherType.SshKey)"
*ngIf="sshKeysEnabled"
>
<i class="bwi bwi-key" slot="start" aria-hidden="true"></i>
{{ "typeSshKey" | i18n }}
</a>
<bit-menu-divider></bit-menu-divider>
<button type="button" bitMenuItem (click)="openFolderDialog()">
<i class="bwi bwi-folder" slot="start" aria-hidden="true"></i>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { ComponentFixture, TestBed } from "@angular/core/testing";
import { ActivatedRoute, RouterLink } from "@angular/router";
import { mock } from "jest-mock-extended";

import { JslibModule } from "@bitwarden/angular/jslib.module";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
Expand Down Expand Up @@ -45,6 +47,7 @@ describe("NewItemDropdownV2Component", () => {

await TestBed.configureTestingModule({
imports: [
JslibModule,
CommonModule,
RouterLink,
ButtonModule,
Expand All @@ -53,6 +56,8 @@ describe("NewItemDropdownV2Component", () => {
NewItemDropdownV2Component,
],
providers: [
{ provide: I18nService, useValue: { t: (key: string) => key } },
{ provide: ConfigService, useValue: { getFeatureFlag: () => Promise.resolve(false) } },
{ provide: DialogService, useValue: dialogServiceMock },
{ provide: I18nService, useValue: i18nServiceMock },
{ provide: ActivatedRoute, useValue: activatedRouteMock },
Expand Down Expand Up @@ -82,7 +87,7 @@ describe("NewItemDropdownV2Component", () => {
jest.spyOn(BrowserPopupUtils, "inPopout").mockReturnValue(false);
jest.spyOn(Utils, "getHostname").mockReturnValue("example.com");

const params = component.buildQueryParams(CipherType.Login);
const params = await component.buildQueryParams(CipherType.Login);

expect(params).toEqual({
type: CipherType.Login.toString(),
Expand All @@ -94,66 +99,66 @@ describe("NewItemDropdownV2Component", () => {
});
});

it("should build query params for a Login cipher when popped out", () => {
it("should build query params for a Login cipher when popped out", async () => {
component.initialValues = {
collectionId: "777-888-999",
} as NewItemInitialValues;

jest.spyOn(BrowserPopupUtils, "inPopout").mockReturnValue(true);

const params = component.buildQueryParams(CipherType.Login);
const params = await component.buildQueryParams(CipherType.Login);

expect(params).toEqual({
type: CipherType.Login.toString(),
collectionId: "777-888-999",
});
});

it("should build query params for a secure note", () => {
it("should build query params for a secure note", async () => {
component.initialValues = {
collectionId: "777-888-999",
} as NewItemInitialValues;

const params = component.buildQueryParams(CipherType.SecureNote);
const params = await component.buildQueryParams(CipherType.SecureNote);

expect(params).toEqual({
type: CipherType.SecureNote.toString(),
collectionId: "777-888-999",
});
});

it("should build query params for an Identity", () => {
it("should build query params for an Identity", async () => {
component.initialValues = {
collectionId: "777-888-999",
} as NewItemInitialValues;

const params = component.buildQueryParams(CipherType.Identity);
const params = await component.buildQueryParams(CipherType.Identity);

expect(params).toEqual({
type: CipherType.Identity.toString(),
collectionId: "777-888-999",
});
});

it("should build query params for a Card", () => {
it("should build query params for a Card", async () => {
component.initialValues = {
collectionId: "777-888-999",
} as NewItemInitialValues;

const params = component.buildQueryParams(CipherType.Card);
const params = await component.buildQueryParams(CipherType.Card);

expect(params).toEqual({
type: CipherType.Card.toString(),
collectionId: "777-888-999",
});
});

it("should build query params for a SshKey", () => {
it("should build query params for a SshKey", async () => {
component.initialValues = {
collectionId: "777-888-999",
} as NewItemInitialValues;

const params = component.buildQueryParams(CipherType.SshKey);
const params = await component.buildQueryParams(CipherType.SshKey);

expect(params).toEqual({
type: CipherType.SshKey.toString(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// @ts-strict-ignore
import { CommonModule } from "@angular/common";
import { Component, Input, OnInit } from "@angular/core";
import { RouterLink } from "@angular/router";
import { Router, RouterLink } from "@angular/router";

import { JslibModule } from "@bitwarden/angular/jslib.module";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
import { CipherType } from "@bitwarden/common/vault/enums";
Expand Down Expand Up @@ -35,14 +37,20 @@ export class NewItemDropdownV2Component implements OnInit {
*/
@Input()
initialValues: NewItemInitialValues;
constructor(
private router: Router,
private dialogService: DialogService,
private configService: ConfigService,
) {}

constructor(private dialogService: DialogService) {}
sshKeysEnabled = false;

async ngOnInit() {
this.sshKeysEnabled = await this.configService.getFeatureFlag(FeatureFlag.SSHKeyVaultItem);
this.tab = await BrowserApi.getTabFromCurrentWindow();
}

buildQueryParams(type: CipherType): AddEditQueryParams {
async buildQueryParams(type: CipherType): Promise<AddEditQueryParams> {
const poppedOut = BrowserPopupUtils.inPopout(window);

const loginDetails: { uri?: string; name?: string } = {};
Expand Down
24 changes: 0 additions & 24 deletions apps/desktop/desktop_native/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions apps/desktop/desktop_native/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,10 @@ arboard = { version = "=3.4.1", default-features = false, features = [
"wayland-data-control",
] }
argon2 = { version = "=0.5.3", features = ["zeroize"] }
async-stream = "=0.3.6"
base64 = "=0.22.1"
byteorder = "=1.5.0"
cbc = { version = "=0.1.2", features = ["alloc"] }
homedir = "=0.3.4"
libc = "=0.2.169"
pin-project = "=1.1.7"
dirs = "=5.0.1"
futures = "=0.3.31"
Expand All @@ -55,7 +53,6 @@ tokio-stream = { version = "=0.1.15", features = ["net"] }
tokio-util = { version = "=0.7.12", features = ["codec"] }
thiserror = "=1.0.69"
typenum = "=1.17.0"
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"] }
Expand Down Expand Up @@ -87,6 +84,7 @@ desktop_objc = { path = "../objc" }

[target.'cfg(target_os = "linux")'.dependencies]
oo7 = "=0.3.3"
libc = "=0.2.169"

zbus = { version = "=4.4.0", optional = true }
zbus_polkit = { version = "=4.0.0", optional = true }
45 changes: 0 additions & 45 deletions apps/desktop/desktop_native/core/src/ssh_agent/generator.rs

This file was deleted.

1 change: 0 additions & 1 deletion apps/desktop/desktop_native/core/src/ssh_agent/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ mod platform_ssh_agent;
#[cfg(any(target_os = "linux", target_os = "macos"))]
mod peercred_unix_listener_stream;

pub mod generator;
pub mod importer;
pub mod peerinfo;
#[derive(Clone)]
Expand Down
1 change: 0 additions & 1 deletion apps/desktop/desktop_native/napi/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ export declare namespace sshagent {
export function lock(agentState: SshAgentState): void
export function importKey(encodedKey: string, password: string): SshKeyImportResult
export function clearKeys(agentState: SshAgentState): void
export function generateKeypair(keyAlgorithm: string): Promise<SshKey>
export class SshAgentState { }
}
export declare namespace processisolations {
Expand Down
8 changes: 0 additions & 8 deletions apps/desktop/desktop_native/napi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,14 +362,6 @@ pub mod sshagent {
.clear_keys()
.map_err(|e| napi::Error::from_reason(e.to_string()))
}

#[napi]
pub async fn generate_keypair(key_algorithm: String) -> napi::Result<SshKey> {
desktop_core::ssh_agent::generator::generate_keypair(key_algorithm)
.await
.map_err(|e| napi::Error::from_reason(e.to_string()))
.map(|k| k.into())
}
}

#[napi]
Expand Down
6 changes: 0 additions & 6 deletions apps/desktop/src/platform/main/main-ssh-agent.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,6 @@ export class MainSshAgentService {
private logService: LogService,
private messagingService: MessagingService,
) {
ipcMain.handle(
"sshagent.generatekey",
async (event: any, { keyAlgorithm }: { keyAlgorithm: string }): Promise<sshagent.SshKey> => {
return await sshagent.generateKeypair(keyAlgorithm);
},
);
ipcMain.handle(
"sshagent.importkey",
async (
Expand Down
3 changes: 0 additions & 3 deletions apps/desktop/src/platform/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,6 @@ const sshAgent = {
signRequestResponse: async (requestId: number, accepted: boolean) => {
await ipcRenderer.invoke("sshagent.signrequestresponse", { requestId, accepted });
},
generateKey: async (keyAlgorithm: string): Promise<ssh.SshKey> => {
return await ipcRenderer.invoke("sshagent.generatekey", { keyAlgorithm });
},
lock: async () => {
return await ipcRenderer.invoke("sshagent.lock");
},
Expand Down
10 changes: 0 additions & 10 deletions apps/desktop/src/vault/app/vault/add-edit.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -512,16 +512,6 @@ <h2 class="box-header">
[ngClass]="{ 'bwi-eye': !showPrivateKey, 'bwi-eye-slash': showPrivateKey }"
></i>
</button>
<button
type="button"
class="row-btn"
appStopClick
appA11yTitle="{{ 'regenerateSshKey' | i18n }}"
(click)="generateSshKey()"
*ngIf="cipher.edit || !editMode"
>
<i class="bwi bwi-lg bwi-generate" aria-hidden="true"></i>
</button>
</div>
</div>
<div class="box-content-row box-content-row-flex" appBoxRow>
Expand Down
Loading

0 comments on commit bb2961f

Please sign in to comment.