Skip to main content

bitwarden_importers/importers/onepassword/convert/
ssh_key.rs

1//! The SSH Key category.
2
3use bitwarden_exporters::SshKey;
4use bitwarden_ssh::import::import_key;
5
6use super::{
7    claimed::{Claimed, section_fields},
8    value::{non_blank, render_value},
9};
10use crate::importers::onepassword::access::wire::VaultItemDetails;
11
12/// Builds an SSH key from the first field that carries a usable one.
13///
14/// 1Password keeps the key material in the field's SSH key attributes, with the private key in
15/// PKCS#8. Bitwarden expects OpenSSH, so `import_key` converts it and derives the public key and
16/// fingerprint. A field is skipped when it has no private key, when the key cannot be read, or
17/// when its value holds something other than the key. `None` means no field qualified.
18pub(super) fn ssh_key(details: &VaultItemDetails) -> Option<(SshKey, Claimed)> {
19    section_fields(details)
20        .enumerate()
21        .find_map(|(position, field)| {
22            let attributes = field.attributes.as_ref()?.ssh_key.as_ref()?;
23            let private_key = non_blank(attributes.private_key.as_deref()?)?;
24            // Claiming the field would lose a value that does not repeat the key.
25            if field
26                .value
27                .as_ref()
28                .and_then(|value| render_value(field.kind.as_deref(), value))
29                .is_some_and(|value| value != private_key)
30            {
31                return None;
32            }
33            // 1Password strips the passphrase when a key is added, so none is offered.
34            let key = import_key(private_key.to_string(), None).ok()?;
35
36            let key = SshKey {
37                private_key: key.private_key,
38                public_key: key.public_key,
39                fingerprint: key.fingerprint,
40            };
41            let claimed = Claimed {
42                designations: &[],
43                fields: vec![position],
44            };
45
46            Some((key, claimed))
47        })
48}