Skip to content

Commit

Permalink
Add purpose field to device certificate (used to flag recovery devi…
Browse files Browse the repository at this point in the history
…ces from regular ones)
  • Loading branch information
touilleMan committed Nov 5, 2024
1 parent 1d85510 commit 587abf1
Show file tree
Hide file tree
Showing 13 changed files with 511 additions and 89 deletions.
19 changes: 8 additions & 11 deletions libparsec/crates/client/src/certif/shamir_recovery_setup.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
// Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS

use std::{
collections::{HashMap, HashSet},
num::NonZeroU64,
sync::Arc,
};

use libparsec_client_connection::ConnectionError;
use libparsec_platform_storage::certificates::{GetCertificateError, UpTo};
use libparsec_protocol::authenticated_cmds::{
Expand All @@ -9,17 +15,7 @@ use libparsec_protocol::authenticated_cmds::{
shamir_recovery_setup::ShamirRecoverySetup,
},
};
use libparsec_types::{
anyhow, shamir_make_shares, thiserror, Bytes, CertificateSignerOwned, DateTime,
DeviceCertificate, DeviceLabel, InvitationToken, LocalDevice, MaybeRedacted, SecretKey,
ShamirRecoveryBriefCertificate, ShamirRecoverySecret, ShamirRecoveryShareCertificate,
ShamirRecoveryShareData, SigningKeyAlgorithm, UserID,
};
use std::{
collections::{HashMap, HashSet},
num::NonZeroU64,
sync::Arc,
};
use libparsec_types::prelude::*;

use crate::{
CertificateBasedActionOutcome, CertificateOps, EventTooMuchDriftWithServerClock,
Expand Down Expand Up @@ -185,6 +181,7 @@ async fn create_shamir_recovery_device(
let device_cert = DeviceCertificate {
author: CertificateSignerOwned::User(author.device_id),
timestamp,
purpose: DevicePurpose::ShamirRecovery,
user_id: recovery_device.user_id,
device_id: recovery_device.device_id,
device_label: MaybeRedacted::Real(recovery_device.device_label.clone()),
Expand Down
32 changes: 23 additions & 9 deletions libparsec/crates/client/src/client/recovery_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,11 @@ use libparsec_platform_device_loader::{
};
use libparsec_platform_storage::certificates::PerTopicLastTimestamps;
use libparsec_protocol::authenticated_cmds::v4::device_create;
use libparsec_types::{
AvailableDevice, DeviceLabel, DeviceSaveStrategy, LocalDevice, SecretKeyPassphrase,
};
use libparsec_types::prelude::*;

use crate::{
greater_timestamp, CertifPollServerError, GreaterTimestampOffset, InvalidCertificateError,
};
use libparsec_types::prelude::*;

use super::Client;

Expand All @@ -28,8 +25,13 @@ pub async fn export_recovery_device(
.await;

// save recovery device
let latest_known_timestamp =
register_new_device(&client.cmds, &recovery_device, &client.device).await?;
let latest_known_timestamp = register_new_device(
&client.cmds,
&recovery_device,
DevicePurpose::PassphraseRecovery,
&client.device,
)
.await?;

let latest_known_timestamp = PerTopicLastTimestamps::new_for_common(latest_known_timestamp);

Expand Down Expand Up @@ -176,13 +178,16 @@ impl From<SaveDeviceError> for ImportRecoveryDeviceError {
async fn register_new_device(
cmds: &AuthenticatedCmds,
new_device: &LocalDevice,
new_device_purpose: DevicePurpose,
author: &LocalDevice,
) -> Result<DateTime, RegisterNewDeviceError> {
// Loop is needed to deal with server requiring greater timestamp
let mut timestamp = author.now();

loop {
let outcome = internal_register_new_device(cmds, new_device, author, timestamp).await?;
let outcome =
internal_register_new_device(cmds, new_device, new_device_purpose, author, timestamp)
.await?;

match outcome {
DeviceInternalsOutcome::Done(timestamp) => return Ok(timestamp),
Expand All @@ -207,12 +212,14 @@ pub(crate) struct DeviceCertificatesBytes {
/// generates certificates for new device signed by author at now
pub(crate) fn generate_new_device_certificates(
new_device: &LocalDevice,
new_device_purpose: DevicePurpose,
author: &LocalDevice,
now: DateTime,
) -> DeviceCertificatesBytes {
let device_cert = DeviceCertificate {
author: CertificateSignerOwned::User(author.device_id),
timestamp: now,
purpose: new_device_purpose,
user_id: new_device.user_id,
device_id: new_device.device_id,
device_label: MaybeRedacted::Real(new_device.device_label.clone()),
Expand Down Expand Up @@ -271,13 +278,14 @@ enum DeviceInternalsOutcome {
async fn internal_register_new_device(
cmds: &AuthenticatedCmds,
new_device: &LocalDevice,
new_device_purpose: DevicePurpose,
author: &LocalDevice,
now: DateTime,
) -> Result<DeviceInternalsOutcome, RegisterNewDeviceError> {
let DeviceCertificatesBytes {
full: device_certificate,
redacted: redacted_device_certificate,
} = generate_new_device_certificates(new_device, author, now);
} = generate_new_device_certificates(new_device, new_device_purpose, author, now);
match cmds
.send(device_create::Req {
device_certificate,
Expand Down Expand Up @@ -343,7 +351,13 @@ pub async fn import_recovery_device(

// 1) Upload the device on the server

register_new_device(&cmds, &new_device, &recovery_device).await?;
register_new_device(
&cmds,
&new_device,
DevicePurpose::Standard,
&recovery_device,
)
.await?;

// 2) Save the device on disk

Expand Down
4 changes: 4 additions & 0 deletions libparsec/crates/client/src/invite/greeter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,7 @@ fn create_new_signed_user_certificates(
let device_certificate = DeviceCertificate {
author: CertificateSignerOwned::User(author.device_id),
timestamp,
purpose: DevicePurpose::Standard,
user_id,
device_id,
device_label: MaybeRedacted::Real(device_label.clone()),
Expand All @@ -1007,6 +1008,7 @@ fn create_new_signed_user_certificates(
let redacted_device_certificate = DeviceCertificate {
author: CertificateSignerOwned::User(author.device_id),
timestamp,
purpose: DevicePurpose::Standard,
user_id,
device_id,
device_label: MaybeRedacted::Redacted(DeviceLabel::new_redacted(device_id)),
Expand Down Expand Up @@ -1050,6 +1052,7 @@ fn create_new_signed_device_certificates(
let device_certificate = DeviceCertificate {
author: CertificateSignerOwned::User(author.device_id),
timestamp,
purpose: DevicePurpose::Standard,
user_id: author.user_id,
device_id,
device_label: MaybeRedacted::Real(device_label),
Expand All @@ -1060,6 +1063,7 @@ fn create_new_signed_device_certificates(
let redacted_device_certificate = DeviceCertificate {
author: CertificateSignerOwned::User(author.device_id),
timestamp,
purpose: DevicePurpose::Standard,
user_id: author.user_id,
device_id,
device_label: MaybeRedacted::Redacted(DeviceLabel::new_redacted(device_id)),
Expand Down
1 change: 1 addition & 0 deletions libparsec/crates/client/src/invite/organization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ pub async fn bootstrap_organization(
let mut device_certificate = DeviceCertificate {
author: CertificateSignerOwned::Root,
timestamp,
purpose: DevicePurpose::Standard,
user_id: device.user_id,
device_id: device.device_id,
device_label: MaybeRedacted::Real(device.device_label.clone()),
Expand Down
1 change: 1 addition & 0 deletions libparsec/crates/client/tests/unit/client/revoke_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ async fn loop_require_greater_timestamp(env: &TestbedEnv) {
user_id: alice.user_id,
device_id,
author: Some(alice.device_id).into(),
purpose: DevicePurpose::Standard,
device_label: MaybeRedacted::Redacted(DeviceLabel::new_redacted(device_id)),
verify_key: alice.verify_key(),
algorithm: SigningKeyAlgorithm::Ed25519,
Expand Down
20 changes: 15 additions & 5 deletions libparsec/crates/client/tests/unit/client/shamir_setup_create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ use libparsec_types::prelude::*;

use super::utils::client_factory;

#[parsec_test(testbed = "coolorg", with_server)]
#[parsec_test(testbed = "coolorg")]
async fn ok(env: &TestbedEnv) {
let alice = env.local_device("alice@dev1");
let client = client_factory(&env.discriminant_dir, alice).await;
let client = client_factory(&env.discriminant_dir, alice.clone()).await;

// Mock requests to server
let new_shamir_certificates: Arc<Mutex<Vec<Bytes>>> = Arc::default();
Expand All @@ -26,11 +26,21 @@ async fn ok(env: &TestbedEnv) {
// 1) Create setup and device
{
let new_device_certificates = new_device_certificates.clone();
let alice = alice.clone();
move |req: authenticated_cmds::latest::device_create::Req| {
let device_certificate = dbg!(req.device_certificate);
// Ensure the device is of the right type
let new_device = DeviceCertificate::verify_and_load(
&req.device_certificate,
&alice.verify_key(),
CertificateSignerRef::User(&alice.device_id),
None,
)
.unwrap();
p_assert_eq!(new_device.purpose, DevicePurpose::ShamirRecovery);

let mut certs = new_device_certificates.lock().unwrap();
certs.push(device_certificate);
dbg!(authenticated_cmds::latest::device_create::Rep::Ok)
certs.push(req.device_certificate);
authenticated_cmds::latest::device_create::Rep::Ok
}
},
{
Expand Down
3 changes: 3 additions & 0 deletions libparsec/crates/platform_storage/tests/unit/certificates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ async fn get_last_timestamps(mut timestamps: TimestampGenerator, env: &TestbedEn
timestamp: t4,
user_id,
device_id,
purpose: DevicePurpose::Standard,
// Not meaningful for the test
author: CertificateSignerOwned::Root,
device_label: MaybeRedacted::Redacted(DeviceLabel::new_redacted(device_id)),
Expand Down Expand Up @@ -537,6 +538,7 @@ async fn get_certificate(mut timestamps: TimestampGenerator, env: &TestbedEnv) {
&DeviceCertificate {
timestamp: t6,
device_id,
purpose: DevicePurpose::Standard,
// Not meaningful for the test
author: CertificateSignerOwned::Root,
user_id,
Expand Down Expand Up @@ -1121,6 +1123,7 @@ async fn forget_all_certificates(mut timestamps: TimestampGenerator, env: &Testb
&DeviceCertificate {
timestamp: t4,
device_id,
purpose: DevicePurpose::Standard,
user_id,
// Not meaningful for the test
author: CertificateSignerOwned::Root,
Expand Down
3 changes: 3 additions & 0 deletions libparsec/crates/testbed/src/template/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@ impl TestbedEventBootstrapOrganization {
let mut certif = DeviceCertificate {
author: CertificateSignerOwned::Root,
timestamp: self.timestamp,
purpose: DevicePurpose::Standard,
user_id: self.first_user_id,
device_id: self.first_user_first_device_id,
device_label: MaybeRedacted::Redacted(DeviceLabel::new_redacted(
Expand Down Expand Up @@ -994,6 +995,7 @@ impl TestbedEventNewUser {
let mut certif = DeviceCertificate {
author: CertificateSignerOwned::User(self.author),
timestamp: self.timestamp,
purpose: DevicePurpose::Standard,
user_id: self.user_id,
device_id: self.first_device_id,
device_label: MaybeRedacted::Redacted(DeviceLabel::new_redacted(
Expand Down Expand Up @@ -1044,6 +1046,7 @@ single_certificate_event!(
let mut certif = DeviceCertificate {
author: CertificateSignerOwned::User(e.author),
timestamp: e.timestamp,
purpose: DevicePurpose::Standard,
user_id: e.user_id,
device_id: e.device_id,
device_label: MaybeRedacted::Redacted(DeviceLabel::new_redacted(e.device_id)),
Expand Down
3 changes: 3 additions & 0 deletions libparsec/crates/tests_fixtures/src/trustchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub fn alice_device_certif(alice: &Device) -> DeviceCertificate {
DeviceCertificate {
author: CertificateSignerOwned::Root,
timestamp: DateTime::from_ymd_hms_us(2000, 1, 1, 0, 0, 0, 0).unwrap(),
purpose: DevicePurpose::Standard,
user_id: alice.user_id,
device_id: alice.device_id,
device_label: MaybeRedacted::Real(alice.device_label.clone()),
Expand All @@ -70,6 +71,7 @@ pub fn bob_device_certif(bob: &Device) -> DeviceCertificate {
DeviceCertificate {
author: CertificateSignerOwned::Root,
timestamp: DateTime::from_ymd_hms_us(2000, 1, 1, 0, 0, 0, 0).unwrap(),
purpose: DevicePurpose::Standard,
user_id: bob.user_id,
device_id: bob.device_id,
device_label: MaybeRedacted::Real(bob.device_label.clone()),
Expand All @@ -84,6 +86,7 @@ pub fn mallory_device_certif(mallory: &Device) -> DeviceCertificate {
DeviceCertificate {
author: CertificateSignerOwned::Root,
timestamp: DateTime::from_ymd_hms_us(2000, 1, 1, 0, 0, 0, 0).unwrap(),
purpose: DevicePurpose::Standard,
user_id: mallory.user_id,
device_id: mallory.device_id,
device_label: MaybeRedacted::Real(mallory.device_label.clone()),
Expand Down
27 changes: 27 additions & 0 deletions libparsec/crates/types/schema/certif/device_certificate.json5
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
"label": "DeviceCertificate",
"type": "device_certificate",
"other_fields": [
{
"name": "purpose",
// Field added in Parsec v3.2
// Defaults to Standard if field is not present
"type": "NonRequiredOption<DevicePurpose>"
},
{
"name": "author",
"type": "CertificateSignerOwned"
Expand Down Expand Up @@ -41,6 +47,27 @@
"discriminant_value": "ED25519"
}
]
},
{
"name": "DevicePurpose",
"variants": [
{
"name": "Standard",
"discriminant_value": "STANDARD"
},
{
"name": "ShamirRecovery",
"discriminant_value": "SHAMIR_RECOVERY"
},
{
"name": "PassphraseRecovery",
"discriminant_value": "PASSPHRASE_RECOVERY"
},
{
"name": "WebAuth",
"discriminant_value": "WEB_AUTH"
}
]
}
]
}
4 changes: 4 additions & 0 deletions libparsec/crates/types/src/certif.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ pub struct DeviceCertificate {
pub author: CertificateSignerOwned,
pub timestamp: DateTime,

pub purpose: DevicePurpose,
pub user_id: UserID,
pub device_id: DeviceID,
pub device_label: MaybeRedacted<DeviceLabel>,
Expand All @@ -518,6 +519,7 @@ impl DeviceCertificate {
Self {
author: self.author,
timestamp: self.timestamp,
purpose: self.purpose,
user_id: self.user_id,
device_id: self.device_id,
device_label,
Expand Down Expand Up @@ -557,6 +559,7 @@ impl From<DeviceCertificateData> for DeviceCertificate {
Self {
author: data.author,
timestamp: data.timestamp,
purpose: data.purpose.unwrap_or(DevicePurpose::Standard),
user_id: data.user_id,
device_id: data.device_id,
device_label,
Expand All @@ -576,6 +579,7 @@ impl From<DeviceCertificate> for DeviceCertificateData {
ty: Default::default(),
author: obj.author,
timestamp: obj.timestamp,
purpose: Some(obj.purpose),
user_id: obj.user_id,
device_id: obj.device_id,
device_label,
Expand Down
9 changes: 6 additions & 3 deletions libparsec/crates/types/src/fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ use rstest::fixture;
use libparsec_crypto::{PrivateKey, PublicKey, SecretKey, SigningKey, VerifyKey};

use crate::{
CertificateSignerOwned, DeviceCertificate, DeviceID, DeviceLabel, Duration, HumanHandle,
LocalDevice, MaybeRedacted, OrganizationID, ParsecOrganizationAddr, PrivateKeyAlgorithm,
SigningKeyAlgorithm, TimeProvider, UserCertificate, UserID, UserProfile, VlobID,
CertificateSignerOwned, DeviceCertificate, DeviceID, DeviceLabel, DevicePurpose, Duration,
HumanHandle, LocalDevice, MaybeRedacted, OrganizationID, ParsecOrganizationAddr,
PrivateKeyAlgorithm, SigningKeyAlgorithm, TimeProvider, UserCertificate, UserID, UserProfile,
VlobID,
};
// Re-expose `DateTime` to simplify use of `timestamp` fixture
pub use crate::DateTime;
Expand Down Expand Up @@ -252,6 +253,7 @@ pub fn device_certificate(alice: &Device, bob: &Device, timestamp: DateTime) ->
DeviceCertificate {
author: CertificateSignerOwned::User(alice.device_id),
timestamp,
purpose: DevicePurpose::Standard,
user_id: bob.user_id,
device_id: bob.device_id,
device_label: MaybeRedacted::Real(bob.device_label.clone()),
Expand All @@ -267,6 +269,7 @@ pub fn redacted_device_certificate(alice: &Device, bob: &Device, timestamp: Date
DeviceCertificate {
author: CertificateSignerOwned::User(alice.device_id),
timestamp,
purpose: DevicePurpose::Standard,
user_id: bob.user_id,
device_id: bob.device_id,
device_label: MaybeRedacted::Real(DeviceLabel::new_redacted(bob.device_id)),
Expand Down
Loading

0 comments on commit 587abf1

Please sign in to comment.