Allow existing GPG keys with cloned stores

This commit is contained in:
hugo committed 2026-08-17 17:00:31 +02:00
1 parent 0b7c938b23
commit a8a59d496b
3 files changed
+252 -40

No files matched your search

+52 -7
View File
@@ -145,7 +145,7 @@ pub struct MobileKeyTransferService {
impl MobileKeyTransferService {
pub fn load() -> Result<Self, MobileKeyTransferError> {
let config = Config::load(None)?;
let keys = KeyStore::load(config.key_material())?;
let keys = load_keys_or_empty(config.key_material())?;
Ok(Self { config, keys })
}
@@ -316,16 +316,24 @@ impl MobileKeyTransferImport {
}
if matches!(self.destination, MobileKeyTransferDestination::LocalSetup) {
if key.kind != MobileKeyTransferKind::Private || !make_default {
if key.kind != MobileKeyTransferKind::Private {
return Err(MobileKeyTransferError::PublicDefault);
}
let outcome = crate::mobile_onboarding::MobileOnboardingOperation::default()
.setup_local_imported(
let operation = crate::mobile_onboarding::MobileOnboardingOperation::default();
let outcome = if make_default {
operation.setup_local_imported(
SecretBytes::new(armor.expose().to_vec()),
passphrase,
overwrite,
)
.map_err(MobileKeyTransferError::LocalSetup)?;
} else {
operation.stage_local_imported(
SecretBytes::new(armor.expose().to_vec()),
passphrase,
overwrite,
)
}
.map_err(MobileKeyTransferError::LocalSetup)?;
return Ok(MobileKeyTransferOutcome {
title: outcome.title().to_owned(),
detail: outcome.detail().to_owned(),
@@ -334,7 +342,7 @@ impl MobileKeyTransferImport {
let MobileKeyTransferDestination::Configured(config) = &self.destination else {
unreachable!("local setup returned above")
};
let existing = KeyStore::load(config.key_material())?;
let existing = load_keys_or_empty(config.key_material())?;
if let Some(found) = existing
.infos()
.find(|info| info.fingerprint().as_str() == key.fingerprint)
@@ -389,6 +397,13 @@ impl MobileKeyTransferImport {
}
}
fn load_keys_or_empty(path: &Path) -> Result<KeyStore, CryptoError> {
match KeyStore::load(path) {
Err(CryptoError::NoKeyMaterial) => Ok(KeyStore::new()),
result => result,
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum MobileKeyTransferError {
Configuration,
@@ -640,7 +655,7 @@ fn set_private_permissions(_temporary: &TempFile<'_>) -> Result<(), MobileKeyTra
mod tests {
use super::{
MobileKeyTransferError, MobileKeyTransferImport, MobileKeyTransferKind,
MobileKeyTransferService, encode_multipart,
MobileKeyTransferService, encode_multipart, load_keys_or_empty,
};
use crate::{config::Config, crypto::KeyStore, repository::SecretBytes};
use tempfile::tempdir;
@@ -800,6 +815,36 @@ mod tests {
Ok(())
}
#[test]
fn cloned_store_with_empty_key_directory_can_import_its_private_key()
-> Result<(), Box<dyn std::error::Error>> {
let fixture = configured_key("placeholder", b"")?;
std::fs::remove_file(fixture.key_path.join("placeholder"))?;
let service = MobileKeyTransferService {
keys: load_keys_or_empty(&fixture.key_path)?,
config: fixture.config.clone(),
};
assert!(service.keys().is_empty());
let mut importer = service.importer();
importer.add_frame(SecretBytes::new(ALICE_SECRET.to_vec()))?;
importer.import(
Some(SecretBytes::new(b"fixture-alice-passphrase".to_vec())),
true,
false,
)?;
let keys = KeyStore::load(&fixture.key_path)?;
assert!(keys.resolve(ALICE_FINGERPRINT).is_ok());
assert_eq!(
Config::load(Some(fixture.config.source()))?
.default_key()
.as_str(),
ALICE_FINGERPRINT
);
Ok(())
}
struct Fixture {
_root: tempfile::TempDir,
config: Config,