Skip to content

Commit

Permalink
Implement ShamirRecoveryClaimRecoverDeviceCtx::recover_device
Browse files Browse the repository at this point in the history
  • Loading branch information
vxgmichel committed Nov 29, 2024
1 parent 1a1118b commit d8d9e29
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 7 deletions.
3 changes: 2 additions & 1 deletion libparsec/crates/client/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ use libparsec_platform_async::lock::Mutex as AsyncMutex;

use libparsec_types::prelude::*;
pub use recovery_device::{
import_recovery_device, ClientExportRecoveryDeviceError, ImportRecoveryDeviceError,
import_recovery_device, register_new_device, ClientExportRecoveryDeviceError,
ImportRecoveryDeviceError,
};

// Re-exposed for public API
Expand Down
4 changes: 2 additions & 2 deletions libparsec/crates/client/src/client/recovery_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ impl From<SaveDeviceError> for ImportRecoveryDeviceError {
}
}

async fn register_new_device(
pub async fn register_new_device(
cmds: &AuthenticatedCmds,
new_device: &LocalDevice,
new_device_purpose: DevicePurpose,
Expand Down Expand Up @@ -242,7 +242,7 @@ pub(crate) fn generate_new_device_certificates(
}

#[derive(Debug, thiserror::Error)]
enum RegisterNewDeviceError {
pub enum RegisterNewDeviceError {
#[error("Component has stopped")]
Stopped,
#[error("Cannot reach the server")]
Expand Down
68 changes: 67 additions & 1 deletion libparsec/crates/client/src/invite/claimer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ use std::num::NonZeroU8;
use std::{path::PathBuf, sync::Arc};

use invited_cmds::latest::invite_claimer_step;
use libparsec_client_connection::AuthenticatedCmds;
use libparsec_client_connection::{protocol::invited_cmds, ConnectionError, InvitedCmds};
use libparsec_protocol::invited_cmds::v4::invite_info::ShamirRecoveryRecipient;
use libparsec_types::prelude::*;

use crate::client::register_new_device;
use crate::invite::common::{Throttle, WAIT_PEER_MAX_ATTEMPTS};
use crate::ClientConfig;

Expand Down Expand Up @@ -413,7 +415,71 @@ impl ShamirRecoveryClaimRecoverDeviceCtx {
self,
requested_device_label: DeviceLabel,
) -> Result<ShamirRecoveryClaimFinalizeCtx, ClaimInProgressError> {
panic!("{requested_device_label}")
let ciphered_data = {
use invited_cmds::latest::invite_shamir_recovery_reveal::{Rep, Req};

let rep = self
.cmds
.send(Req {
reveal_token: self.secret.reveal_token,
})
.await?;

match rep {
Rep::Ok { ciphered_data } => Ok(ciphered_data),
// TODO: specialize error
Rep::NotFound => Err(ClaimInProgressError::NotFound),
Rep::UnknownStatus { .. } => {
Err(anyhow::anyhow!("Unexpected server response: {:?}", rep).into())
}
}?
};

let mut recovery_device =
LocalDevice::decrypt_and_load(&ciphered_data, &self.secret.data_key)
.map_err(|e| ClaimInProgressError::Internal(anyhow::Error::msg(e)))?;

// When using the tested, the recovery device organization address is set to a placeholder address.
// This is because the organization address is not known yet when the testbed is initialized.
// In this case, we replace the placeholder address with the actual organization address.
if cfg!(test)
&& recovery_device
.organization_addr
.to_string()
.starts_with("parsec3://parsec.invalid/PlaceholderOrg")
{
recovery_device.organization_addr = ParsecOrganizationAddr::new(
self.cmds.addr(),
self.cmds.addr().organization_id().clone(),
recovery_device.organization_addr.root_verify_key().clone(),
);
}

let recovery_device = Arc::new(recovery_device);

let new_local_device =
LocalDevice::from_existing_device_for_user(&recovery_device, requested_device_label);

let recovery_cmds = AuthenticatedCmds::new(
&self.config.config_dir,
recovery_device.clone(),
self.config.proxy.clone(),
)?;

register_new_device(
&recovery_cmds,
&new_local_device,
DevicePurpose::Standard,
&recovery_device,
)
.await
// TODO: specialize error
.map_err(|e| ClaimInProgressError::Internal(e.into()))?;

Ok(ShamirRecoveryClaimFinalizeCtx {
config: self.config,
new_local_device: Arc::new(new_local_device),
})
}
}

Expand Down
19 changes: 16 additions & 3 deletions libparsec/crates/client/tests/unit/invite/shamir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ async fn shamir(tmp_path: TmpPath, env: &TestbedEnv) {
let available_device = alice_finalize_ctx.save_local_device(&access).await.unwrap();

// Checks
let expected_server_url = ParsecAddr::from(bob.organization_addr.clone())
.to_http_url(None)
.to_string();
p_assert_eq!(available_device.key_file_path, tmp_path.join("device.keys"));
p_assert_eq!(
available_device.organization_id,
Expand All @@ -222,14 +225,24 @@ async fn shamir(tmp_path: TmpPath, env: &TestbedEnv) {
p_assert_eq!(available_device.device_label, device_label);
p_assert_eq!(available_device.human_handle, alice.human_handle);
p_assert_eq!(available_device.ty, DeviceFileType::Password);
p_assert_eq!(available_device.server_url, "https://noserver.example.com/");
p_assert_eq!(
available_device.organization_id,
bob.organization_id().clone()
);
p_assert_eq!(available_device.server_url, expected_server_url);
p_assert_eq!(available_device.user_id, alice.user_id);
// created_on and protected_on date times not checked

// Check device can be loaded
let reloaded_new_local_device =
let reloaded_new_alice_device =
libparsec_platform_device_loader::load_device(&env.discriminant_dir, &access)
.await
.unwrap();
p_assert_eq!(reloaded_new_local_device, new_local_device);
p_assert_eq!(reloaded_new_alice_device, new_local_device);

let new_alice_client =
client_factory(&env.discriminant_dir, reloaded_new_alice_device.clone()).await;

// Test server connection by deleting the shamir recovery
new_alice_client.delete_shamir_recovery().await.unwrap();
}

0 comments on commit d8d9e29

Please sign in to comment.