Implement GPG key QR transfer

This commit is contained in:
2026-08-11 21:25:32 +02:00
parent b01cc8bb6d
commit 358ba7d46d
9 changed files with 2653 additions and 5 deletions

View File

@@ -3,7 +3,11 @@
//! Mechanical UniFFI exports for Apple presentation code.
use std::{error::Error, fmt, sync::Arc};
use std::{
error::Error,
fmt,
sync::{Arc, Mutex},
};
use ironstorage::{
config::ConfigError,
@@ -27,6 +31,12 @@ use ironstorage::{
MobileHomeErrorKind as StorageHomeErrorKind, MobileHomeFreshness as StorageHomeFreshness,
MobileHomePhase as StorageHomePhase,
},
mobile_key_transfer::{
MobileKeyTransferError as StorageKeyTransferError,
MobileKeyTransferKey as StorageKeyTransferKey,
MobileKeyTransferKind as StorageKeyTransferKind,
MobileKeyTransferProgress as StorageKeyTransferProgress,
},
mobile_onboarding::{
self, MobileOnboardingError as StorageOnboardingError,
MobileOnboardingErrorKind as StorageOnboardingErrorKind,
@@ -868,6 +878,213 @@ impl From<StorageAuthenticationState> for MobileAuthenticationState {
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, uniffi::Enum)]
pub enum MobileKeyTransferKind {
Public,
Private,
}
impl From<StorageKeyTransferKind> for MobileKeyTransferKind {
fn from(kind: StorageKeyTransferKind) -> Self {
match kind {
StorageKeyTransferKind::Public => Self::Public,
StorageKeyTransferKind::Private => Self::Private,
}
}
}
impl From<MobileKeyTransferKind> for StorageKeyTransferKind {
fn from(kind: MobileKeyTransferKind) -> Self {
match kind {
MobileKeyTransferKind::Public => Self::Public,
MobileKeyTransferKind::Private => Self::Private,
}
}
}
#[derive(Clone, uniffi::Record)]
pub struct MobileKeyTransferKey {
pub fingerprint: String,
pub title: String,
pub detail: String,
pub kind: MobileKeyTransferKind,
pub requires_passphrase: bool,
}
impl From<&StorageKeyTransferKey> for MobileKeyTransferKey {
fn from(key: &StorageKeyTransferKey) -> Self {
Self {
fingerprint: key.fingerprint().to_owned(),
title: key.title().to_owned(),
detail: key.detail().to_owned(),
kind: key.kind().into(),
requires_passphrase: key.requires_passphrase(),
}
}
}
#[derive(Clone, uniffi::Record)]
pub struct MobileKeyTransferFrame {
pub sequence: u32,
pub total: u32,
pub width: u32,
pub modules: Vec<u8>,
}
#[derive(Clone, uniffi::Record)]
pub struct MobileKeyTransferExport {
pub key: MobileKeyTransferKey,
pub frames: Vec<MobileKeyTransferFrame>,
}
#[derive(Clone, uniffi::Record)]
pub struct MobileKeyTransferProgress {
pub received: u32,
pub total: u32,
pub duplicate: bool,
pub key: Option<MobileKeyTransferKey>,
}
impl From<StorageKeyTransferProgress> for MobileKeyTransferProgress {
fn from(progress: StorageKeyTransferProgress) -> Self {
Self {
received: u32::try_from(progress.received()).unwrap_or(u32::MAX),
total: u32::try_from(progress.total()).unwrap_or(u32::MAX),
duplicate: progress.duplicate(),
key: progress.key().map(Into::into),
}
}
}
#[derive(Clone, uniffi::Record)]
pub struct MobileKeyTransferOutcome {
pub title: String,
pub detail: String,
}
#[derive(Debug, uniffi::Error)]
pub enum MobileKeyTransferFfiError {
Failed { message: String },
}
impl fmt::Display for MobileKeyTransferFfiError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Failed { message } => formatter.write_str(message),
}
}
}
impl Error for MobileKeyTransferFfiError {}
impl From<StorageKeyTransferError> for MobileKeyTransferFfiError {
fn from(error: StorageKeyTransferError) -> Self {
Self::Failed {
message: error.to_string(),
}
}
}
#[derive(uniffi::Object)]
pub struct MobileKeyTransfer {
service: ironstorage::mobile_key_transfer::MobileKeyTransferService,
}
#[uniffi::export]
impl MobileKeyTransfer {
pub fn keys(&self) -> Vec<MobileKeyTransferKey> {
self.service.keys().iter().map(Into::into).collect()
}
pub fn export(
&self,
fingerprint: String,
kind: MobileKeyTransferKind,
passphrase: Option<String>,
) -> Result<MobileKeyTransferExport, MobileKeyTransferFfiError> {
let exported = self.service.export(
&fingerprint,
kind.into(),
passphrase.map(|value| ironstorage::repository::SecretBytes::new(value.into_bytes())),
)?;
let frames = exported
.frames()
.iter()
.map(|frame| {
let matrix = frame.matrix();
let mut modules = Vec::with_capacity(matrix.width() * matrix.width());
for y in 0..matrix.width() {
for x in 0..matrix.width() {
modules.push(u8::from(matrix.is_dark(x, y).unwrap_or(false)));
}
}
MobileKeyTransferFrame {
sequence: u32::try_from(frame.sequence()).unwrap_or(u32::MAX),
total: u32::try_from(frame.total()).unwrap_or(u32::MAX),
width: u32::try_from(matrix.width()).unwrap_or(u32::MAX),
modules,
}
})
.collect();
Ok(MobileKeyTransferExport {
key: exported.key().into(),
frames,
})
}
pub fn importer(&self) -> Arc<MobileKeyTransferImport> {
Arc::new(MobileKeyTransferImport {
importer: Mutex::new(self.service.importer()),
})
}
}
#[derive(uniffi::Object)]
pub struct MobileKeyTransferImport {
importer: Mutex<ironstorage::mobile_key_transfer::MobileKeyTransferImport>,
}
#[uniffi::export]
impl MobileKeyTransferImport {
pub fn add_frame(
&self,
payload: String,
) -> Result<MobileKeyTransferProgress, MobileKeyTransferFfiError> {
self.importer
.lock()
.map_err(|_| MobileKeyTransferFfiError::Failed {
message: "the key-transfer session is unavailable".to_owned(),
})?
.add_frame(ironstorage::repository::SecretBytes::new(
payload.into_bytes(),
))
.map(Into::into)
.map_err(Into::into)
}
pub fn import(
&self,
passphrase: Option<String>,
make_default: bool,
) -> Result<MobileKeyTransferOutcome, MobileKeyTransferFfiError> {
let outcome = self
.importer
.lock()
.map_err(|_| MobileKeyTransferFfiError::Failed {
message: "the key-transfer session is unavailable".to_owned(),
})?
.import(
passphrase
.map(|value| ironstorage::repository::SecretBytes::new(value.into_bytes())),
make_default,
)?;
Ok(MobileKeyTransferOutcome {
title: outcome.title().to_owned(),
detail: outcome.detail().to_owned(),
})
}
}
#[derive(Debug, uniffi::Error)]
pub enum MobileAuthenticationFfiError {
Failed {
@@ -1388,6 +1605,13 @@ pub fn mobile_authentication() -> Result<Arc<MobileAuthentication>, MobileAuthen
}))
}
#[uniffi::export]
pub fn mobile_key_transfer() -> Result<Arc<MobileKeyTransfer>, MobileKeyTransferFfiError> {
Ok(Arc::new(MobileKeyTransfer {
service: ironstorage::mobile_key_transfer::MobileKeyTransferService::load()?,
}))
}
#[uniffi::export]
pub fn mobile_onboarding_operation(
server_url: String,