Skip to content

Commit

Permalink
wip libparsec
Browse files Browse the repository at this point in the history
  • Loading branch information
touilleMan committed Nov 21, 2023
1 parent 7935d52 commit 9d64de4
Show file tree
Hide file tree
Showing 7 changed files with 508 additions and 184 deletions.
322 changes: 210 additions & 112 deletions libparsec/crates/platform_storage/src/certificates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,128 +23,210 @@ pub enum UpTo {
Timestamp(DateTime),
}

#[derive(Debug, Clone, Copy)]
pub enum Topic {
Common,
Sequester,
Realm(VlobID),
Shamir,
}

impl Topic {
pub fn certif_types(self) -> &'static [&'static str] {
match self {
Topic::Common => {
static COMMON_TYPES: [&'static str; 4] = [
USER_CERTIFICATE_TYPE,
DEVICE_CERTIFICATE_TYPE,
REVOKED_USER_CERTIFICATE_TYPE,
USER_UPDATE_CERTIFICATE_TYPE,
];
&COMMON_TYPES
}
Topic::Sequester => {
static SEQUESTER_TYPES: [&'static str; 2] = [
SEQUESTER_AUTHORITY_CERTIFICATE_TYPE,
SEQUESTER_SERVICE_CERTIFICATE_TYPE,
];
&SEQUESTER_TYPES
}
Topic::Realm(_) => {
static REALM_TYPES: [&'static str; 1] = [REALM_ROLE_CERTIFICATE_TYPE];
&REALM_TYPES
}
Topic::Shamir => todo!(),
}
#[derive(Debug)]
pub struct PerTopicTimestamps {
common: Option<DateTime>,
sequester: Option<DateTime>,
realm: Vec<(VlobID, DateTime)>,
shamir: Option<DateTime>,
}

pub(super) trait Certificate {
/// Values for `certificate_type` column in `certificates` table
/// Note the fact their value is similar to the `type` field in certificates, this
/// is purely for simplicity as the two are totally decorrelated.
const TYPE: &'static str;

fn filters(&self) -> (&'static str, Option<String>, Option<String>);
fn timestamp(&self) -> DateTime;
}

pub(super) trait CertificateTopic {
const TYPES: &'static [&'static str];
/// `TYPES` should be updated everytime a new type of certificate is created for this
/// topic.
/// This assert function is a safeguard to ensure the compilation will fail in case
/// we forget to modify `TYPES` accordingly.
#[cfg(test)]
fn assert_types_and_structs_correspondance(certif: Self);
}

impl Certificate for UserCertificate {
const TYPE: &'static str = "user_certificate";
fn filters(&self) -> (&'static str, Option<String>, Option<String>) {
user_certificate_filters(self.user_id.clone())
}
fn timestamp(&self) -> DateTime {
self.timestamp
}
}

#[derive(Debug, thiserror::Error)]
pub enum GetCertificateError {
#[error("Certificate doesn't exist")]
NonExisting,
#[error("Certificate exists but is more recent than the provided timestamp")]
ExistButTooRecent {
certificate_timestamp: DateTime,
},
#[error(transparent)]
Internal(#[from] anyhow::Error),
impl Certificate for DeviceCertificate {
const TYPE: &'static str = "device_certificate";
fn filters(&self) -> (&'static str, Option<String>, Option<String>) {
device_certificate_filters(self.device_id.clone())
}
fn timestamp(&self) -> DateTime {
self.timestamp
}
}

#[derive(Debug, thiserror::Error)]
pub enum GetTimestampBoundsError {
#[error("Certificate index doesn't exist")]
NonExisting,
#[error(transparent)]
Internal(#[from] anyhow::Error),
impl Certificate for RevokedUserCertificate {
const TYPE: &'static str = "revoked_user_certificate";
fn filters(&self) -> (&'static str, Option<String>, Option<String>) {
revoked_user_certificate_filters(self.user_id.clone())
}
fn timestamp(&self) -> DateTime {
self.timestamp
}
}

// Values for `certificate_type` column in `certificates` table
// Note the fact their value is similar to the `type` field in certificates, this
// is purely for simplicity as the two are totally decorrelated.
const USER_CERTIFICATE_TYPE: &str = "user_certificate";
const DEVICE_CERTIFICATE_TYPE: &str = "device_certificate";
const REVOKED_USER_CERTIFICATE_TYPE: &str = "revoked_user_certificate";
const USER_UPDATE_CERTIFICATE_TYPE: &str = "user_update_certificate";
const REALM_ROLE_CERTIFICATE_TYPE: &str = "realm_role_certificate";
const SEQUESTER_AUTHORITY_CERTIFICATE_TYPE: &str = "sequester_authority_certificate";
const SEQUESTER_SERVICE_CERTIFICATE_TYPE: &str = "sequester_service_certificate";
impl Certificate for UserUpdateCertificate {
const TYPE: &'static str = "user_update_certificate";
fn filters(&self) -> (&'static str, Option<String>, Option<String>) {
user_update_certificate_filters(self.user_id.clone())
}
fn timestamp(&self) -> DateTime {
self.timestamp
}
}

fn users_certificates_filters() -> (&'static str, Option<String>, Option<String>) {
let filter1 = None;
let filter2 = None;
(USER_CERTIFICATE_TYPE, filter1, filter2)
impl Certificate for RealmRoleCertificate {
const TYPE: &'static str = "realm_role_certificate";
fn filters(&self) -> (&'static str, Option<String>, Option<String>) {
realm_role_certificate_filters(self.realm_id, self.user_id.clone())
}
fn timestamp(&self) -> DateTime {
self.timestamp
}
}

fn user_certificate_filters(user_id: UserID) -> (&'static str, Option<String>, Option<String>) {
let filter1 = Some(user_id.into());
let filter2 = None;
(USER_CERTIFICATE_TYPE, filter1, filter2)
impl Certificate for SequesterAuthorityCertificate {
const TYPE: &'static str = "sequester_authority_certificate";
fn filters(&self) -> (&'static str, Option<String>, Option<String>) {
sequester_authority_certificate_filters()
}
fn timestamp(&self) -> DateTime {
self.timestamp
}
}

fn revoked_user_certificate_filters(
user_id: UserID,
) -> (&'static str, Option<String>, Option<String>) {
let filter1 = Some(user_id.into());
let filter2 = None;
(REVOKED_USER_CERTIFICATE_TYPE, filter1, filter2)
impl Certificate for SequesterServiceCertificate {
const TYPE: &'static str = "sequester_service_certificate";
fn filters(&self) -> (&'static str, Option<String>, Option<String>) {
sequester_service_certificate_filters(self.service_id.clone())
}
fn timestamp(&self) -> DateTime {
self.timestamp
}
}

fn user_update_certificates_filters(
user_id: UserID,
) -> (&'static str, Option<String>, Option<String>) {
macro_rules! impl_assert_types_and_structs_correspondance {
($($name:ident),* $(,)?) => {
#[cfg(test)]
fn assert_types_and_structs_correspondance(certif: Self) {
match certif {
$(CommonTopicArcCertificate::$name(c) => assert_eq!(<c as Certificate>::TYPE, Self::TYPES[0]),)*
}
}
};
}

impl CertificateTopic for CommonTopicArcCertificate {
const TYPES: &'static [&'static str] = &[
<UserCertificate as Certificate>::TYPE,
<DeviceCertificate as Certificate>::TYPE,
<RevokedUserCertificate as Certificate>::TYPE,
<UserUpdateCertificate as Certificate>::TYPE,
];
impl_assert_types_and_structs_correspondance!(
User,
Device,
RevokedUser,
UserUpdate,
);
}

impl CertificateTopic for SequesterTopicArcCertificate {
const TYPES: &'static [&'static str] = &[
<SequesterAuthorityCertificate as Certificate>::TYPE,
<SequesterServiceCertificate as Certificate>::TYPE,
];
impl_assert_types_and_structs_correspondance!(
SequesterAuthorityCertificate,
SequesterServiceCertificate,
);
}

impl CertificateTopic for RealmTopicArcCertificate {
const TYPES: &'static [&'static str] = &[
<RealmRoleCertificate as Certificate>::TYPE,
];
impl_assert_types_and_structs_correspondance!(
RealmRole,
);
}

fn user_certificate_filters(user_id: UserID) -> (&'static str, Option<String>, Option<String>) {
let filter1 = Some(user_id.into());
let filter2 = None;
(USER_UPDATE_CERTIFICATE_TYPE, filter1, filter2)
(<UserCertificate as Certificate>::TYPE, filter1, filter2)
}

fn device_certificate_filters(
device_id: DeviceID,
) -> (&'static str, Option<String>, Option<String>) {
fn device_certificate_filters(device_id: DeviceID) -> (&'static str, Option<String>, Option<String>) {
let (user_id, device_name) = device_id.into();
// DeviceName is already unique enough, so we provide it as first filter
// to speed up database lookup
let filter1 = Some(device_name.into());
let filter2 = Some(user_id.into());
(DEVICE_CERTIFICATE_TYPE, filter1, filter2)
(<UserCertificate as Certificate>::TYPE, filter1, filter2)
}

/// Get all device certificates for a given realm
fn user_device_certificates_filters(
user_id: UserID,
) -> (&'static str, Option<String>, Option<String>) {
fn revoked_user_certificate_filters(user_id: UserID) -> (&'static str, Option<String>, Option<String>) {
let filter1 = Some(user_id.into());
let filter2 = None;
(<UserCertificate as Certificate>::TYPE, filter1, filter2)
}

fn user_update_certificate_filters(user_id: UserID) -> (&'static str, Option<String>, Option<String>) {
let filter1 = Some(user_id.into());
let filter2 = None;
(<UserCertificate as Certificate>::TYPE, filter1, filter2)
}

fn realm_role_certificate_filters(realm_id: VlobID, user_id: UserID) -> (&'static str, Option<String>, Option<String>) {
let filter1 = Some(realm_id.hex());
let filter2 = Some(user_id.into());
(<UserCertificate as Certificate>::TYPE, filter1, filter2)
}

fn sequester_authority_certificate_filters() -> (&'static str, Option<String>, Option<String>) {
// No filter is needed as there is a most one authority certificate
let filter1 = None;
let filter2 = None;
(<UserCertificate as Certificate>::TYPE, filter1, filter2)
}

fn sequester_service_certificate_filters(service_id: SequesterServiceID) -> (&'static str, Option<String>, Option<String>) {
let filter1 = Some(service_id.hex());
let filter2 = None;
(<UserCertificate as Certificate>::TYPE, filter1, filter2)
}

/// Get all user certificates
fn users_certificates_filters() -> (&'static str, Option<String>, Option<String>) {
let filter1 = None;
let filter2 = Some(user_id.into());
(DEVICE_CERTIFICATE_TYPE, filter1, filter2)
let filter2 = None;
(<DeviceCertificate as Certificate>::TYPE, filter1, filter2)
}

fn realm_role_certificate_filters(
realm_id: VlobID,
/// Get all device certificates for a given realm
fn user_device_certificates_filters(
user_id: UserID,
) -> (&'static str, Option<String>, Option<String>) {
let filter1 = Some(realm_id.hex());
let filter1 = None;
let filter2 = Some(user_id.into());
(REALM_ROLE_CERTIFICATE_TYPE, filter1, filter2)
(<DeviceCertificate as Certificate>::TYPE, filter1, filter2)
}

/// Get all realm role certificates for a given realm
Expand All @@ -153,7 +235,7 @@ fn realm_role_certificates_filters(
) -> (&'static str, Option<String>, Option<String>) {
let filter1 = Some(realm_id.hex());
let filter2 = None;
(REALM_ROLE_CERTIFICATE_TYPE, filter1, filter2)
(<RealmRoleCertificate as Certificate>::TYPE, filter1, filter2)
}

/// Get all realm role certificates for a given user
Expand All @@ -162,26 +244,34 @@ fn user_realm_role_certificates_filters(
) -> (&'static str, Option<String>, Option<String>) {
let filter1 = None;
let filter2 = Some(user_id.into());
(REALM_ROLE_CERTIFICATE_TYPE, filter1, filter2)
}

fn sequester_authority_certificate_filters() -> (&'static str, Option<String>, Option<String>) {
(SEQUESTER_AUTHORITY_CERTIFICATE_TYPE, None, None)
}

fn sequester_service_certificate_filters(
service_id: SequesterServiceID,
) -> (&'static str, Option<String>, Option<String>) {
let filter1 = Some(service_id.hex());
let filter2 = None;
(SEQUESTER_SERVICE_CERTIFICATE_TYPE, filter1, filter2)
(<RealmRoleCertificate as Certificate>::TYPE, filter1, filter2)
}

// Get all sequester service certificates
fn sequester_service_certificates_filters() -> (&'static str, Option<String>, Option<String>) {
let filter1 = None;
let filter2 = None;
(SEQUESTER_SERVICE_CERTIFICATE_TYPE, filter1, filter2)
(<SequesterServiceCertificate as Certificate>::TYPE, filter1, filter2)
}

#[derive(Debug, thiserror::Error)]
pub enum GetCertificateError {
#[error("Certificate doesn't exist")]
NonExisting,
#[error("Certificate exists but is more recent than the provided timestamp")]
ExistButTooRecent {
certificate_timestamp: DateTime,
},
#[error(transparent)]
Internal(#[from] anyhow::Error),
}

#[derive(Debug, thiserror::Error)]
pub enum GetTimestampBoundsError {
#[error("Certificate index doesn't exist")]
NonExisting,
#[error(transparent)]
Internal(#[from] anyhow::Error),
}

pub struct GetAnyCertificateData {
Expand Down Expand Up @@ -602,8 +692,8 @@ impl CertificatesStorage {

/// Return the last certificate timestamp we know about for the given topic, or `None`
/// if the storage is empty regarding this topic (and the topics it depends on).
pub async fn get_last_timestamp(&mut self, topic: Topic) -> anyhow::Result<Option<DateTime>> {
self.platform.get_last_timestamp(topic).await
pub async fn get_last_timestamps(&mut self) -> anyhow::Result<PerTopicTimestamps> {
self.platform.get_last_timestamps().await
}

/// Not if multiple results are possible, the highest index is kept (i.e. latest certificate)
Expand Down Expand Up @@ -653,15 +743,23 @@ impl<'a> CertificatesStorageForUpdateGuard<'a> {
/// Add a (already validated) certificate.
pub async fn add_certificate(
&mut self,
data: AddCertificateData,
certif: &impl Certificate,
encrypted: Vec<u8>,
) -> anyhow::Result<()> {
self.platform.add_certificate(data).await
let (certificate_type, filter1, filter2) = certif.filters();
self.platform.add_certificate(
certificate_type,
filter1,
filter2,
certif.timestamp(),
encrypted,
).await
}

/// Return the last certificate timestamp we know about for the given topic, or `None`
/// if the storage is empty regarding this topic (and the topics it depends on).
pub async fn get_last_timestamp(&mut self, topic: Topic) -> anyhow::Result<Option<DateTime>> {
self.platform.get_last_timestamp(topic).await
pub async fn get_last_timestamps(&mut self) -> anyhow::Result<PerTopicTimestamps> {
self.platform.get_last_timestamps().await
}

/// Not if multiple results are possible, the highest index is kept (i.e. latest certificate)
Expand Down
Loading

0 comments on commit 9d64de4

Please sign in to comment.