Skip to content

Commit d1d47c7

Browse files
committed
Remove libparsec_platform_pki dependency in libparsec_platform_device_loader
- Introduce `PkiDeviceOperations` trait that works similarly to `AccountVaultOperations`/`OpenBaoDeviceOperations` - Remove platform-specific code (replaced by using the methods defined in `PkiDeviceOperations`)
1 parent f306148 commit d1d47c7

File tree

7 files changed

+115
-124
lines changed

7 files changed

+115
-124
lines changed

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libparsec/crates/platform_device_loader/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ libparsec_client_connection = { workspace = true }
2020
libparsec_crypto = { workspace = true }
2121
libparsec_platform_async = { workspace = true }
2222
libparsec_platform_filesystem = { workspace = true }
23-
libparsec_platform_pki = { workspace = true }
2423
libparsec_testbed = { workspace = true, optional = true }
2524
libparsec_types = { workspace = true }
2625

libparsec/crates/platform_device_loader/src/device/load_ciphertext_key.rs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
use crate::{
44
platform, AccountVaultOperationsFetchOpaqueKeyError, DeviceAccessStrategy,
5-
DevicePrimaryProtectionStrategy, OpenBaoOperationsFetchOpaqueKeyError, RemoteOperationServer,
5+
DevicePrimaryProtectionStrategy, OpenBaoOperationsFetchOpaqueKeyError,
6+
PkiOperationsDecryptOpaqueKeyError, RemoteOperationServer,
67
};
78
use libparsec_types::prelude::*;
89

@@ -12,7 +13,6 @@ pub(crate) enum LoadCiphertextKeyError {
1213
#[error("Invalid data")]
1314
InvalidData,
1415
#[error("Decryption failed")]
15-
#[cfg_attr(target_arch = "wasm32", expect(dead_code))]
1616
DecryptionFailed,
1717
/// Note only a subset of load strategies requires server access to
1818
/// fetch an opaque key that itself protects the ciphertext key
@@ -64,9 +64,31 @@ pub(super) async fn load_ciphertext_key(
6464
}
6565
}
6666

67-
DevicePrimaryProtectionStrategy::PKI { .. } => {
68-
let ciphertext_key = platform::load_ciphertext_key_pki(device_file).await?;
69-
Ok(ciphertext_key)
67+
DevicePrimaryProtectionStrategy::PKI { operations, .. } => {
68+
if let DeviceFile::PKI(device) = device_file {
69+
if device.certificate_ref != *operations.certificate_ref() {
70+
return Err(LoadCiphertextKeyError::InvalidData);
71+
}
72+
73+
let raw = operations
74+
.decrypt_opaque_key(device.algorithm, device.encrypted_key.as_ref())
75+
.await
76+
.map_err(|err| match err {
77+
PkiOperationsDecryptOpaqueKeyError::UnsupportedAlgorithm => {
78+
LoadCiphertextKeyError::InvalidData
79+
}
80+
PkiOperationsDecryptOpaqueKeyError::DecryptionFailed(_) => {
81+
LoadCiphertextKeyError::DecryptionFailed
82+
}
83+
PkiOperationsDecryptOpaqueKeyError::Internal(e) => {
84+
LoadCiphertextKeyError::Internal(e)
85+
}
86+
})?;
87+
88+
Ok(raw)
89+
} else {
90+
Err(LoadCiphertextKeyError::InvalidData)
91+
}
7092
}
7193

7294
DevicePrimaryProtectionStrategy::AccountVault { operations, .. } => {

libparsec/crates/platform_device_loader/src/device/save.rs

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
// Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS
22

3-
use crate::{encrypt_device, platform, DevicePrimaryProtectionStrategy};
4-
use libparsec_platform_filesystem::{save_content, SaveContentError};
53
use std::path::PathBuf;
64

5+
use libparsec_platform_filesystem::{save_content, SaveContentError};
6+
use libparsec_types::prelude::*;
7+
78
use crate::{
8-
AccountVaultOperationsUploadOpaqueKeyError, AvailableDevice, DeviceSaveStrategy,
9-
OpenBaoOperationsUploadOpaqueKeyError, RemoteOperationServer,
9+
encrypt_device, platform, AccountVaultOperationsUploadOpaqueKeyError, AvailableDevice,
10+
DevicePrimaryProtectionStrategy, DeviceSaveStrategy, OpenBaoOperationsUploadOpaqueKeyError,
11+
PkiOperationsEncryptOpaqueKeyError, RemoteOperationServer,
1012
};
11-
use libparsec_types::prelude::*;
1213

1314
#[derive(Debug, thiserror::Error)]
1415
pub enum SaveDeviceError {
@@ -114,18 +115,51 @@ pub(crate) async fn save_device(
114115
save_content(&key_file, &file_content).await?;
115116
}
116117

117-
DevicePrimaryProtectionStrategy::PKI { certificate_ref } => {
118-
platform::save_device_pki(
119-
device,
120-
&created_on,
121-
&key_file,
122-
&server_addr,
123-
&protected_on,
124-
certificate_ref,
118+
DevicePrimaryProtectionStrategy::PKI { operations } => {
119+
// Generate a random key
120+
let key = SecretKey::generate();
121+
122+
// Encrypt the key using the public key related to a certificate from the store
123+
let (algorithm, encrypted_key) = operations
124+
.encrypt_opaque_key(key.as_ref())
125+
.await
126+
.map_err(|err| match err {
127+
PkiOperationsEncryptOpaqueKeyError::Internal(e) => SaveDeviceError::Internal(e),
128+
})?;
129+
130+
// May check if we are able to decrypt the encrypted key from the previous step
131+
assert_eq!(
132+
operations
133+
.decrypt_opaque_key(algorithm, encrypted_key.as_ref())
134+
.await
135+
.map_err(|e| SaveDeviceError::Internal(e.into()))?
136+
.as_ref(),
137+
key.as_ref()
138+
);
139+
140+
// Use the generated key to encrypt the device content
141+
let ciphertext = encrypt_device(device, &key, totp_opaque_key);
142+
143+
// Save
144+
let file_content = DeviceFile::PKI(DeviceFilePKI {
145+
created_on,
146+
protected_on,
147+
server_url: server_addr.clone(),
148+
organization_id: device.organization_id().to_owned(),
149+
user_id: device.user_id,
150+
device_id: device.device_id,
151+
human_handle: device.human_handle.to_owned(),
152+
device_label: device.device_label.to_owned(),
153+
certificate_ref: operations.certificate_ref().to_owned(),
154+
encrypted_key,
155+
ciphertext,
156+
algorithm,
125157
totp_opaque_key_id,
126-
totp_opaque_key,
127-
)
128-
.await?;
158+
});
159+
160+
let file_content = file_content.dump();
161+
162+
save_content(&key_file, &file_content).await?;
129163
}
130164

131165
DevicePrimaryProtectionStrategy::AccountVault { operations } => {

libparsec/crates/platform_device_loader/src/native/mod.rs

Lines changed: 0 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use crate::{
1010
encrypt_device, LoadCiphertextKeyError, LoadDeviceError, SaveDeviceError,
1111
PARSEC_BASE_CONFIG_DIR, PARSEC_BASE_DATA_DIR, PARSEC_BASE_HOME_DIR,
1212
};
13-
use libparsec_platform_pki::{decrypt_message, encrypt_message};
1413
use libparsec_types::prelude::*;
1514

1615
const KEYRING_SERVICE: &str = "parsec";
@@ -81,63 +80,6 @@ pub(crate) async fn save_device_keyring(
8180
Ok(())
8281
}
8382

84-
#[expect(clippy::too_many_arguments)]
85-
pub(crate) async fn save_device_pki(
86-
device: &LocalDevice,
87-
created_on: &DateTime,
88-
key_file: &Path,
89-
server_addr: &ParsecAddr,
90-
protected_on: &DateTime,
91-
certificate_ref: &X509CertificateReference,
92-
totp_opaque_key_id: Option<TOTPOpaqueKeyID>,
93-
totp_opaque_key: Option<&SecretKey>,
94-
) -> Result<(), SaveDeviceError> {
95-
// Generate a random key
96-
let secret_key = SecretKey::generate();
97-
98-
// Encrypt the key using the public key related to a certificate from the store
99-
let der = libparsec_platform_pki::get_der_encoded_certificate(certificate_ref)
100-
.await
101-
.map_err(|e| SaveDeviceError::Internal(e.into()))?;
102-
let (algorithm, encrypted_key) = encrypt_message(der, secret_key.as_ref())
103-
.await
104-
.map_err(|e| SaveDeviceError::Internal(e.into()))?;
105-
106-
// May check if we are able to decrypt the encrypted key from the previous step
107-
assert_eq!(
108-
decrypt_message(algorithm, &encrypted_key, certificate_ref)
109-
.await
110-
.map_err(|e| SaveDeviceError::Internal(e.into()))?
111-
.as_ref(),
112-
secret_key.as_ref()
113-
);
114-
115-
// Use the generated key to encrypt the device content
116-
let ciphertext = encrypt_device(device, &secret_key, totp_opaque_key);
117-
118-
// Save
119-
let file_content = DeviceFile::PKI(DeviceFilePKI {
120-
created_on: *created_on,
121-
protected_on: *protected_on,
122-
server_url: server_addr.clone(),
123-
organization_id: device.organization_id().to_owned(),
124-
user_id: device.user_id,
125-
device_id: device.device_id,
126-
human_handle: device.human_handle.to_owned(),
127-
device_label: device.device_label.to_owned(),
128-
certificate_ref: certificate_ref.to_owned(),
129-
encrypted_key,
130-
ciphertext,
131-
algorithm,
132-
totp_opaque_key_id,
133-
});
134-
135-
let file_content = file_content.dump();
136-
137-
save_content(key_file, &file_content).await?;
138-
Ok(())
139-
}
140-
14183
async fn generate_keyring_user(
14284
keyring_user_path: &Path,
14385
) -> Result<(SecretKey, String), SaveDeviceError> {
@@ -216,25 +158,6 @@ pub(super) async fn load_ciphertext_key_keyring(
216158
}
217159
}
218160

219-
pub(super) async fn load_ciphertext_key_pki(
220-
device_file: &DeviceFile,
221-
) -> Result<SecretKey, LoadCiphertextKeyError> {
222-
if let DeviceFile::PKI(device) = device_file {
223-
decrypt_message(
224-
device.algorithm,
225-
device.encrypted_key.as_ref(),
226-
&device.certificate_ref,
227-
)
228-
.await
229-
.map_err(|_| LoadCiphertextKeyError::InvalidData)?
230-
.as_ref()
231-
.try_into()
232-
.map_err(|_| LoadCiphertextKeyError::InvalidData)
233-
} else {
234-
Err(LoadCiphertextKeyError::InvalidData)
235-
}
236-
}
237-
238161
pub(super) fn get_default_data_base_dir() -> PathBuf {
239162
let mut path = if let Ok(data_dir) = std::env::var(PARSEC_BASE_DATA_DIR) {
240163
PathBuf::from(data_dir)

libparsec/crates/platform_device_loader/src/strategy.rs

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,41 @@ pub enum OpenBaoOperationsUploadOpaqueKeyError {
110110
BadServerResponse(anyhow::Error),
111111
}
112112

113+
/*
114+
* PKI operations
115+
*/
116+
117+
pub trait PkiDeviceOperations: std::fmt::Debug + Send + Sync {
118+
fn certificate_ref(&self) -> &X509CertificateReference;
119+
fn encrypt_opaque_key(
120+
&self,
121+
opaque_key: &[u8],
122+
) -> PinBoxFutureResult<(PKIEncryptionAlgorithm, Bytes), PkiOperationsEncryptOpaqueKeyError>;
123+
fn decrypt_opaque_key(
124+
&self,
125+
algorithm: PKIEncryptionAlgorithm,
126+
encrypted_opaque_key: &[u8],
127+
) -> PinBoxFutureResult<SecretKey, PkiOperationsDecryptOpaqueKeyError>;
128+
}
129+
130+
#[derive(Debug, thiserror::Error)]
131+
pub enum PkiOperationsEncryptOpaqueKeyError {
132+
// TODO: Add SCWS-specific errors here (typically if the SCWS service is not reachable)
133+
#[error(transparent)]
134+
Internal(anyhow::Error),
135+
}
136+
137+
#[derive(Debug, thiserror::Error)]
138+
pub enum PkiOperationsDecryptOpaqueKeyError {
139+
#[error("Unsupported algorithm")]
140+
UnsupportedAlgorithm,
141+
#[error("Decryption failed: {0}")]
142+
DecryptionFailed(anyhow::Error),
143+
// TODO: Add SCWS-specific errors here (typically if the SCWS service is not reachable)
144+
#[error(transparent)]
145+
Internal(anyhow::Error),
146+
}
147+
113148
/*
114149
* DeviceSaveStrategy
115150
*/
@@ -121,7 +156,7 @@ pub enum DevicePrimaryProtectionStrategy {
121156
password: Password,
122157
},
123158
PKI {
124-
certificate_ref: X509CertificateReference,
159+
operations: Arc<dyn PkiDeviceOperations>,
125160
},
126161
AccountVault {
127162
operations: Arc<dyn AccountVaultOperations>,
@@ -136,10 +171,8 @@ impl DevicePrimaryProtectionStrategy {
136171
match self {
137172
Self::Keyring => AvailableDeviceType::Keyring,
138173
Self::Password { .. } => AvailableDeviceType::Password,
139-
Self::PKI {
140-
certificate_ref, ..
141-
} => AvailableDeviceType::PKI {
142-
certificate_ref: certificate_ref.to_owned(),
174+
Self::PKI { operations, .. } => AvailableDeviceType::PKI {
175+
certificate_ref: operations.certificate_ref().to_owned(),
143176
},
144177
Self::AccountVault { .. } => AvailableDeviceType::AccountVault,
145178
Self::OpenBao { operations, .. } => AvailableDeviceType::OpenBao {

libparsec/crates/platform_device_loader/src/web/mod.rs

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,12 @@ pub(crate) async fn save_device_keyring(
1616
panic!("Keyring not supported on Web")
1717
}
1818

19-
pub(crate) async fn save_device_pki(
20-
_: &LocalDevice,
21-
_: &DateTime,
22-
_: &Path,
23-
_: &ParsecAddr,
24-
_: &DateTime,
25-
_: &X509CertificateReference,
26-
_: Option<TOTPOpaqueKeyID>,
27-
_: Option<&SecretKey>,
28-
) -> Result<(), SaveDeviceError> {
29-
panic!("PKI not supported on Web")
30-
}
31-
3219
pub(super) async fn load_ciphertext_key_keyring(
3320
_: &DeviceFile,
3421
) -> Result<SecretKey, LoadCiphertextKeyError> {
3522
panic!("Keyring not supported on Web")
3623
}
3724

38-
pub(super) async fn load_ciphertext_key_pki(
39-
_: &DeviceFile,
40-
) -> Result<SecretKey, LoadCiphertextKeyError> {
41-
panic!("PKI not supported on Web")
42-
}
43-
4425
pub(super) fn is_keyring_available() -> bool {
4526
false
4627
}

0 commit comments

Comments
 (0)