diff --git a/service/src/results.rs b/service/src/results.rs index e93dd32..b1c2e12 100644 --- a/service/src/results.rs +++ b/service/src/results.rs @@ -23,10 +23,12 @@ impl From> for InsertResult { } } +// TODO: add peer id to result #[marine] pub struct WeightResult { pub success: bool, - pub weight: Vec, + pub weight: u32, + pub peer_id: String, pub error: String, } @@ -162,24 +164,24 @@ impl From> for VerifyTrustResult { } #[marine] -pub struct IssueCertificateResult { +pub struct AddTrustResult { pub success: bool, pub error: String, - pub cert: Certificate, + pub weight: u32, } -impl From> for IssueCertificateResult { - fn from(result: Result) -> Self { +impl From> for AddTrustResult { + fn from(result: Result) -> Self { match result { - Ok(cert) => IssueCertificateResult { + Ok(weight) => AddTrustResult { success: true, error: "".to_string(), - cert, + weight, }, - Err(e) => IssueCertificateResult { + Err(e) => AddTrustResult { success: false, error: format!("{}", e), - cert: Certificate { chain: vec![] }, + weight: u32::default(), }, } } diff --git a/service/src/service_api.rs b/service/src/service_api.rs index edc36b3..d7daff1 100644 --- a/service/src/service_api.rs +++ b/service/src/service_api.rs @@ -1,11 +1,11 @@ use crate::dto::{Certificate, Trust}; use crate::results::{ - AddRootResult, AllCertsResult, GetTrustMetadataResult, InsertResult, IssueCertificateResult, - IssueTrustResult, VerifyTrustResult, WeightResult, + AddRootResult, AddTrustResult, AllCertsResult, GetTrustMetadataResult, InsertResult, + IssueCertificateResult, IssueTrustResult, VerifyTrustResult, WeightResult, }; use crate::service_impl::{ - add_root_impl, get_all_certs_impl, get_trust_metadata_imp, get_weight_impl, insert_cert_impl, - insert_cert_impl_raw, issue_certificate_with_trust_checked_impl, + add_root_impl, add_trust_impl, get_all_certs_impl, get_trust_metadata_imp, get_weight_impl, + insert_cert_impl, insert_cert_impl_raw, issue_certificate_with_trust_checked_impl, issue_root_certificate_checked_impl, issue_trust_impl, verify_trust_impl, }; use marine_rs_sdk::{marine, CallParameters}; @@ -13,19 +13,19 @@ use marine_rs_sdk::{marine, CallParameters}; #[marine] /// add a certificate in string representation to trust graph if it is valid /// see `trust_graph::Certificate` class for string encoding/decoding -// TODO change `current_time` to time service -fn insert_cert_raw(certificate: String, current_time: u64) -> InsertResult { - insert_cert_impl_raw(certificate, current_time).into() +// TODO change `timestamp_sec` to time service +fn insert_cert_raw(certificate: String, timestamp_sec: u64) -> InsertResult { + insert_cert_impl_raw(certificate, timestamp_sec).into() } #[marine] /// add a certificate in JSON representation to trust graph if it is valid /// see `dto::Certificate` class for structure -fn insert_cert(certificate: Certificate, current_time: u64) -> InsertResult { - insert_cert_impl(certificate, current_time).into() +fn insert_cert(certificate: Certificate, timestamp_sec: u64) -> InsertResult { + insert_cert_impl(certificate, timestamp_sec).into() } -// TODO: pass current timestamp, return only valid, delete expired +// TODO: pass current timestamp, return only valid, delete expired, return max weight #[marine] fn get_weight(peer_id: String) -> WeightResult { get_weight_impl(peer_id).into() @@ -37,6 +37,8 @@ fn get_all_certs(issued_for: String) -> AllCertsResult { get_all_certs_impl(issued_for).into() } +// todo: add trust method + #[marine] /// could add only a host of a trust graph service fn add_root(peer_id: String, weight: u32) -> AddRootResult { @@ -71,27 +73,52 @@ fn issue_trust( issue_trust_impl(issued_for_peer_id, expires_at, issued_at, signed_metadata).into() } -// TODO: use "peer" "timestamp_sec" and check tetraplets #[marine] -fn verify_trust(trust: Trust, issuer_peer_id: String, cur_time: u64) -> VerifyTrustResult { - verify_trust_impl(trust, issuer_peer_id, cur_time).into() +fn verify_trust(trust: Trust, issuer_peer_id: String, timestamp_sec: u64) -> VerifyTrustResult { + verify_trust_impl(trust, issuer_peer_id, timestamp_sec).into() } #[marine] -fn issue_root_certificate_checked( - root_trust: Trust, - issued_trust: Trust, - cur_time: u64, -) -> IssueCertificateResult { - issue_root_certificate_checked_impl(root_trust, issued_trust, cur_time).into() +fn add_trust(trust: Trust, issuer_peer_id: String, timestamp_sec: u64) -> AddTrustResult { + add_trust_impl(trust, issuer_peer_id, timestamp_sec).into() } -#[marine] -fn issue_certificate_with_trust_checked( - cert: Certificate, - trust: Trust, - issued_by_peer_id: String, - cur_time: u64, -) -> IssueCertificateResult { - issue_certificate_with_trust_checked_impl(cert, trust, issued_by_peer_id, cur_time).into() -} +// service TrustGraph("trust-graph"): +// -- returns hash of metadata to sign +// get_trust_bytes(issued_for_peer_id: string, expires_at: u64, issued_at: u64) -> GetTrustMetadataResult +// +// -- issued_by needed to identify signature type (ed25519, rsa or secp256k1) +// issue_trust(issued_by_peer_id: string, issued_for_peer_id: string, expires_at: u64, issued_at: u64, signature: []u8) -> IssueTrustResult +// +// -- just verifying signatures, timestamp without inserting into local trust graph +// verify_trust(issued_by_peer_id: string, trust: Trust, timestamp_sec: u64) -> VerifyTrustResult +// +// -- checks signature, timestamp, try to find place to insert, returns max_weight if succeed +// add_trust(issued_by_peer_id: string, trust: Trust, timestamp_sec: u64) -> AddTrustResult +// +// -- add root trust with given weight +// add_root(peer_id: string, trust: Trust, weight: u32) -> AddRootResult +// +// -- return max weight if found, remove expired +// get_weight(issued_for: string, timestamp_sec: u32) -> WeightResult +// +// -- return all certs, remove expired +// get_all_certs(issued_for: string, timestamp_sec) -> AllCertsResult +// +// -- insert full certificate if possible +// insert_cert(certificate: Certificate, current_time: u64) -> InsertResult +// insert_cert_raw(certificate: string, current_time: u64) -> InsertResult +// +// -- returns hash of metadata to sign +// get_revoke_bytes(revoked_peer_id: string, revoked_at: u64, issued_at: u64) -> GetRevokeMetadataResult +// +// -- revoked_by needed to identify signature type (ed25519, rsa or secp256k1) +// issue_revoke(revoked_by_peer_id: string, revoked_peer_id: string, revoked_at: u64, signature: []u8) -> IssueRevokeResult +// +// -- checks signature, checks timestamp +// revoke(revoke: Revoke, timestamp_sec: u64) -> AddRevokeResult +// +// get_all_revocation(revoked_peer_id: string) +// +// -- TODO +// get_trust diff --git a/service/src/service_impl.rs b/service/src/service_impl.rs index b4e29c4..344bfd6 100644 --- a/service/src/service_impl.rs +++ b/service/src/service_impl.rs @@ -1,15 +1,37 @@ use crate::dto::{Certificate, DtoConversionError, Trust}; +use crate::service_impl::ServiceError::InvalidTimestampTetraplet; use crate::storage_impl::get_data; use fluence_keypair::error::DecodingError; use fluence_keypair::public_key::peer_id_to_fluence_pk; use fluence_keypair::{PublicKey, Signature}; use libp2p_core::PeerId; +use marine_rs_sdk::CallParameters; use std::convert::{Into, TryInto}; use std::str::FromStr; use std::time::Duration; use thiserror::Error as ThisError; use trust_graph::{current_time, CertificateError, TrustError, TrustGraphError}; +pub static TRUSTED_TIMESTAMP_SERVICE_ID: &str = "peer"; +pub static TRUSTED_TIMESTAMP_FUNCTION_NAME: &str = "timestamp_sec"; + +/// Check timestamps are generated on the current host with builtin ("peer" "timestamp_sec") +pub(crate) fn check_timestamp_tetraplets( + call_parameters: &CallParameters, + arg_number: usize, +) -> Result<(), ServiceError> { + let tetraplets = call_parameters + .tetraplets + .get(arg_number) + .ok_or(InvalidTimestampTetraplet)?; + let tetraplet = tetraplets.get(0).wrap_err(error_msg)?; + (tetraplet.service_id == TRUSTED_TIMESTAMP_SERVICE_ID + && tetraplet.function_name == TRUSTED_TIMESTAMP_FUNCTION_NAME + && tetraplet.peer_pk == call_parameters.host_id) + .then(|| ()) + .ok_or(InvalidTimestampTetraplet) +} + #[derive(ThisError, Debug)] pub enum ServiceError { #[error("peer id parse error: {0}")] @@ -46,6 +68,8 @@ pub enum ServiceError { #[source] TrustError, ), + #[error("you should use host peer.timestamp_sec to pass timestamp")] + InvalidTimestampTetraplet, } fn parse_peer_id(peer_id: String) -> Result { @@ -65,17 +89,17 @@ pub fn get_weight_impl(peer_id: String) -> Result, ServiceError> { Ok(weight) } -fn add_cert(certificate: trust_graph::Certificate, duration: u64) -> Result<(), ServiceError> { - let duration = Duration::from_millis(duration); +fn add_cert(certificate: trust_graph::Certificate, timestamp_sec: u64) -> Result<(), ServiceError> { + let timestamp_sec = Duration::from_secs(timestamp_sec); let mut tg = get_data().lock(); - tg.add(certificate, duration)?; + tg.add(certificate, timestamp_sec)?; Ok(()) } -pub fn insert_cert_impl_raw(certificate: String, duration: u64) -> Result<(), ServiceError> { +pub fn insert_cert_impl_raw(certificate: String, timestamp_sec: u64) -> Result<(), ServiceError> { let certificate = trust_graph::Certificate::from_str(&certificate)?; - add_cert(certificate, duration)?; + add_cert(certificate, timestamp_sec)?; Ok(()) } @@ -93,10 +117,10 @@ pub fn get_all_certs_impl(issued_for: String) -> Result, Servic Ok(certs.into_iter().map(|c| c.into()).collect()) } -pub fn insert_cert_impl(certificate: Certificate, duration: u64) -> Result<(), ServiceError> { +pub fn insert_cert_impl(certificate: Certificate, timestamp_sec: u64) -> Result<(), ServiceError> { let certificate: trust_graph::Certificate = certificate.try_into()?; - add_cert(certificate, duration)?; + add_cert(certificate, timestamp_sec)?; Ok(()) } @@ -138,45 +162,32 @@ pub fn issue_trust_impl( pub fn verify_trust_impl( trust: Trust, issuer_peer_id: String, - cur_time: u64, + timestamp_sec: u64, ) -> Result<(), ServiceError> { + check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 2)?; let public_key = extract_public_key(issuer_peer_id)?; trust_graph::Trust::verify( &trust.try_into()?, &public_key, - Duration::from_secs(cur_time), + Duration::from_secs(timestamp_sec), )?; Ok(()) } -pub fn issue_root_certificate_checked_impl( - root_trust: Trust, - issued_trust: Trust, - cur_time: u64, -) -> Result { - trust_graph::Certificate::new_from_root_trust( - root_trust.try_into()?, - issued_trust.try_into()?, - Duration::from_secs(cur_time), - ) - .map(|c| c.into()) - .map_err(ServiceError::CertError) -} - -pub fn issue_certificate_with_trust_checked_impl( - cert: Certificate, +pub fn add_trust_impl( trust: Trust, - issued_by: String, - cur_time: u64, -) -> Result { - let public_key = extract_public_key(issued_by)?; - trust_graph::Certificate::issue_with_trust( - public_key, + issuer_peer_id: String, + timestamp_sec: u64, +) -> Result<(), ServiceError> { + let public_key = extract_public_key(issuer_peer_id)?; + check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 2)?; + let mut tg = get_data().lock(); + tg.add_trust( trust.try_into()?, - &cert.try_into()?, - Duration::from_secs(cur_time), - ) - .map(|c| c.into()) - .map_err(ServiceError::CertError) + public_key, + Duration::from_secs(timestamp_sec), + )?; + + Ok(()) } diff --git a/service/trust-graph.aqua b/service/trust-graph.aqua index df930df..8ff3cae 100644 --- a/service/trust-graph.aqua +++ b/service/trust-graph.aqua @@ -40,19 +40,54 @@ data VerifyTrustResult: success: bool error: string +data AddTrustResult: + success: bool + error: string + weight: u32 + peer_id: u32 + data WeightResult: success: bool - weight: []u32 + weight: u32 + peer_id: string error: string service TrustGraph("trust-graph"): - add_root(peer_id: string, weight: u32) -> AddRootResult - get_all_certs(issued_for: string) -> AllCertsResult - get_trust_metadata(issued_for_peer_id: string, expires_at: u64, issued_at: u64) -> GetTrustMetadataResult - get_weight(peer_id: string) -> WeightResult + -- returns hash of metadata to sign + get_trust_bytes(issued_for_peer_id: string, expires_at: u64, issued_at: u64) -> GetTrustMetadataResult + + -- issued_by needed to identify signature type (ed25519, rsa or secp256k1) + issue_trust(issued_by_peer_id: string, issued_for_peer_id: string, expires_at: u64, issued_at: u64, signature: []u8) -> IssueTrustResult + + -- just verifying signatures, timestamp without inserting into local trust graph + verify_trust(issued_by_peer_id: string, trust: Trust, timestamp_sec: u64) -> VerifyTrustResult + + -- checks signature, timestamp, try to find place to insert, returns max_weight if succeed + add_trust(issued_by_peer_id: string, trust: Trust, timestamp_sec: u64) -> AddTrustResult + + -- add root trust with given weight + add_root(peer_id: string, trust: Trust, weight: u32) -> AddRootResult + + -- return max weight if found, remove expired + get_weight(issued_for: string, timestamp_sec: u32) -> WeightResult + + -- return all certs, remove expired + get_all_certs(issued_for: string, timestamp_sec) -> AllCertsResult + + -- insert full certificate if possible insert_cert(certificate: Certificate, current_time: u64) -> InsertResult insert_cert_raw(certificate: string, current_time: u64) -> InsertResult - issue_certificate_with_trust_checked(cert: Certificate, trust: Trust, issued_by_peer_id: string, cur_time: u64) -> IssueCertificateResult - issue_root_certificate_checked(root_trust: Trust, issued_trust: Trust, cur_time: u64) -> IssueCertificateResult - issue_trust(issued_for_peer_id: string, expires_at: u64, issued_at: u64, signed_metadata: []u8) -> IssueTrustResult - verify_trust(trust: Trust, issuer_peer_id: string, cur_time: u64) -> VerifyTrustResult + + -- returns hash of metadata to sign + get_revoke_bytes(revoked_peer_id: string, revoked_at: u64, issued_at: u64) -> GetRevokeMetadataResult + + -- revoked_by needed to identify signature type (ed25519, rsa or secp256k1) + issue_revoke(revoked_by_peer_id: string, revoked_peer_id: string, revoked_at: u64, signature: []u8) -> IssueRevokeResult + + -- checks signature, checks timestamp + revoke(revoke: Revoke, timestamp_sec: u64) -> AddRevokeResult + + get_all_revocation(revoked_peer_id: string) + + -- TODO + get_trust \ No newline at end of file diff --git a/src/trust_graph.rs b/src/trust_graph.rs index efb9e84..237fec1 100644 --- a/src/trust_graph.rs +++ b/src/trust_graph.rs @@ -68,6 +68,8 @@ pub enum TrustGraphError { #[source] RevokeError, ), + #[error("Path to {0} not found")] + AddTrustError(String), } impl From for TrustGraphError { @@ -92,7 +94,11 @@ where } /// Insert new root weight - pub fn add_root_weight(&mut self, pk: PublicKey, weight: Weight) -> Result<(), TrustGraphError> { + pub fn add_root_weight( + &mut self, + pk: PublicKey, + weight: Weight, + ) -> Result<(), TrustGraphError> { Ok(self.storage.add_root_weight(pk.into(), weight)?) } @@ -101,7 +107,35 @@ where Ok(self.storage.get(&pk.into())?) } - // TODO: remove cur_time from api, leave it for tests only + pub fn add_trust( + &mut self, + trust: T, + issued_by: P, + cur_time: Duration, + ) -> Result<(), TrustGraphError> + where + T: Borrow, + P: Borrow, + { + Trust::verify(trust, issued_by.borrow(), cur_time)?; + + let _parent_trust_node = + self.get(issued_by.clone().borrow())? + .ok_or(TrustGraphError::AddTrustError( + issued_by.borrow().to_peer_id().to_base58(), + ))?; + + let pk = trust.issued_for.clone().into(); + + let auth = Auth { + trust: trust.clone(), + issued_by, + }; + + self.storage + .update_auth(&pk, auth, &root_trust.issued_for, cur_time) + } + /// Certificate is a chain of trusts, add this chain to graph pub fn add(&mut self, cert: C, cur_time: Duration) -> Result<(), TrustGraphError> where @@ -123,13 +157,13 @@ where // Insert new TrustNode for this root_pk if there wasn't one if self.storage.get(&root_pk)?.is_none() { - let mut trust_node = TrustNode::new(root_trust.issued_for.clone(), cur_time); let root_auth = Auth { trust: root_trust.clone(), issued_by: root_trust.issued_for.clone(), }; - trust_node.update_auth(root_auth); - self.storage.insert(root_pk, trust_node)?; + + self.storage + .update_auth(&root_pk, root_auth, &root_trust.issued_for, cur_time)?; } // Insert remaining trusts to the graph @@ -188,7 +222,7 @@ where return Ok(None); } - let mut weight = std::u32::MAX; + let mut weight = u32::MAX; for cert in certs { let c = cert.borrow(); @@ -376,13 +410,15 @@ mod tests { let root_kp = KeyPair::generate_ed25519(); let second_kp = KeyPair::generate_ed25519(); - let mut cert = - Certificate::issue_root(&root_kp, second_kp.public(), expires_at, issued_at); + let mut cert = Certificate::issue_root(&root_kp, second_kp.public(), expires_at, issued_at); let mut key_pairs = vec![root_kp, second_kp]; for idx in 2..len { - let kp = keys.get(&idx).unwrap_or(&KeyPair::generate_ed25519()).clone(); + let kp = keys + .get(&idx) + .unwrap_or(&KeyPair::generate_ed25519()) + .clone(); let previous_kp = &key_pairs[idx - 1]; cert = Certificate::issue( &previous_kp, @@ -591,9 +627,15 @@ mod tests { let st = InMemoryStorage::new(); let mut graph = TrustGraph::new(st); // add first and last trusts as roots - graph.add_root_weight(cert.chain[0].clone().issued_for.into(), 1).unwrap(); - graph.add_root_weight(cert.chain[3].clone().issued_for.into(), 1).unwrap(); - graph.add_root_weight(cert.chain[5].clone().issued_for.into(), 1).unwrap(); + graph + .add_root_weight(cert.chain[0].clone().issued_for.into(), 1) + .unwrap(); + graph + .add_root_weight(cert.chain[3].clone().issued_for.into(), 1) + .unwrap(); + graph + .add_root_weight(cert.chain[5].clone().issued_for.into(), 1) + .unwrap(); graph.add(cert.clone(), current_time()).unwrap(); diff --git a/src/trust_graph_storage.rs b/src/trust_graph_storage.rs index 73ce478..599d053 100644 --- a/src/trust_graph_storage.rs +++ b/src/trust_graph_storage.rs @@ -118,8 +118,7 @@ impl Storage for InMemoryStorage { None => { let mut trust_node = TrustNode::new(issued_for.clone(), cur_time); trust_node.update_auth(auth); - self.nodes.insert(pk.clone(), trust_node); - Ok(()) + self.insert(pk.clone(), trust_node) } } }