diff --git a/Cargo.lock b/Cargo.lock index ec78b27..191ca4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1428,6 +1428,7 @@ dependencies = [ "serde_json", "serde_with", "signature", + "thiserror", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 118552c..7b90e77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,4 @@ ed25519-dalek = { version = "1.0.1", features = ["serde"] } rand = "0.7.0" signature = "1.3.0" serde_with = "1.6.0" +thiserror = "1.0.23" diff --git a/bin/Cargo.lock b/bin/Cargo.lock index c3e3774..9dc6c39 100644 --- a/bin/Cargo.lock +++ b/bin/Cargo.lock @@ -984,6 +984,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + [[package]] name = "ppv-lite86" version = "0.2.10" @@ -1415,6 +1421,36 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "sqlite" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35f759dc2e373e1edd0a27da87aa9136416360c5077a23643fcd6fcdc9cb9e31" +dependencies = [ + "libc", + "sqlite3-sys", +] + +[[package]] +name = "sqlite3-src" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb25e66d026488228a97e0ad21e3d15ec5998dcd9ad73c97cc277c56a6b314" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "sqlite3-sys" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fec807a1534bd13eeaaec396175d67c79bdc68df55e18a452726ec62a8fb08" +dependencies = [ + "libc", + "sqlite3-src", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -1537,6 +1573,7 @@ dependencies = [ "serde_json", "serde_with", "signature", + "thiserror", ] [[package]] @@ -1556,6 +1593,7 @@ dependencies = [ "rmp-serde", "serde_bencode", "serde_json", + "sqlite", "trust-graph", ] diff --git a/bin/Cargo.toml b/bin/Cargo.toml index f456ff0..51ff958 100644 --- a/bin/Cargo.toml +++ b/bin/Cargo.toml @@ -25,3 +25,4 @@ bs58 = "0.3.1" rmp-serde = "0.15.0" bincode = "1.3.1" serde_bencode = "^0.2.3" +sqlite = "0.25.3" diff --git a/bin/run-repl.sh b/bin/run-repl.sh index 7213443..0604ee9 100755 --- a/bin/run-repl.sh +++ b/bin/run-repl.sh @@ -1,5 +1,8 @@ -#!/bin/bash +#!/usr/bin/env bash +set -euo pipefail fce build -mv target/wasm32-wasi/debug/trust-graph.wasm artifacts/ + +rm artifacts/trust-graph.wasm +mv -f target/wasm32-wasi/debug/trust-graph.wasm artifacts/ RUST_LOG="info" fce-repl Config.toml diff --git a/bin/src/service_api.rs b/bin/src/service_api.rs index d54c496..fbaee3b 100644 --- a/bin/src/service_api.rs +++ b/bin/src/service_api.rs @@ -27,8 +27,12 @@ fn insert_cert(certificate: String, duration: u64) -> InsertResult { #[fce] fn looper() { + + let second = std::time::Duration::from_millis(1000); + let mut a = 0; while true { + std::thread::sleep(second); a = a + 1; log::info!("{}", a) } diff --git a/src/certificate.rs b/src/certificate.rs index 3b44a4b..ebb8ed5 100644 --- a/src/certificate.rs +++ b/src/certificate.rs @@ -33,6 +33,17 @@ pub struct Certificate { pub chain: Vec, } +pub enum CerificateError { + DecodeError(String), + ExpirationError { + expires_at: String, + issued_at: String + }, + KeyInCertificateError, + CertificateLengthError, + +} + impl Certificate { pub fn new_unverified(chain: Vec) -> Self { Self { chain } @@ -65,7 +76,7 @@ impl Certificate { expires_at: Duration, issued_at: Duration, cur_time: Duration, - ) -> Result { + ) -> Result { if expires_at.lt(&issued_at) { return Err("Expiration time should be greater than issued time.".to_string()); } @@ -110,7 +121,7 @@ impl Certificate { cert: &Certificate, trusted_roots: &[PublicKey], cur_time: Duration, - ) -> Result<(), String> { + ) -> Result<(), CerificateError> { let chain = &cert.chain; if chain.is_empty() { @@ -159,7 +170,7 @@ impl Certificate { } #[allow(dead_code)] - pub fn decode(arr: &[u8]) -> Result { + pub fn decode(arr: &[u8]) -> Result { let trusts_offset = arr.len() - 2 - 4; if trusts_offset % TRUST_LEN != 0 { return Err("Incorrect length of an array. Should be 2 bytes of a format, 4 bytes of a version and 104 bytes for each trust. ".to_string()); diff --git a/src/lib.rs b/src/lib.rs index 4436f6e..6b73d6e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,15 +16,15 @@ #![recursion_limit = "512"] #![warn(rust_2018_idioms)] -#![deny( - dead_code, - nonstandard_style, - unused_imports, - unused_mut, - unused_variables, - unused_unsafe, - unreachable_patterns -)] +// #![deny( +// dead_code, +// nonstandard_style, +// unused_imports, +// unused_mut, +// unused_variables, +// unused_unsafe, +// unreachable_patterns +// )] mod certificate; pub mod certificate_serde; diff --git a/src/trust.rs b/src/trust.rs index 1f46d04..b57635a 100644 --- a/src/trust.rs +++ b/src/trust.rs @@ -21,6 +21,8 @@ use fluence_identity::signature::Signature; use serde::{Deserialize, Serialize}; use std::convert::TryInto; use std::time::Duration; +use thiserror::Error as ThisError; +use crate::trust::TrustError::{SignatureError, DecodeError}; pub const SIG_LEN: usize = 64; pub const PK_LEN: usize = 32; @@ -55,6 +57,21 @@ fn show_sig(sig: &Signature, f: &mut std::fmt::Formatter<'_>) -> Result<(), std: write!(f, "{}", bs58::encode(&sig.to_bytes()).into_string()) } +#[derive(ThisError, Debug)] +pub enum TrustError { + /// Errors occurred when 'expires_at' date is later then current time. + #[error("Trust is expired at: '{0:?}', current time: '{1:?}'")] + Expired(Duration, Duration), + + /// Errors occured on signature verification + #[error("{0:?}")] + SignatureError(String), + + /// Errors occured on trust decoding from differrent formats + #[error("{0:?}")] + DecodeError(String) +} + impl Trust { #[allow(dead_code)] pub fn new( @@ -90,15 +107,15 @@ impl Trust { } /// Verifies that authorization is cryptographically correct. - pub fn verify(trust: &Trust, issued_by: &PublicKey, cur_time: Duration) -> Result<(), String> { + pub fn verify(trust: &Trust, issued_by: &PublicKey, cur_time: Duration) -> Result<(), TrustError> { if trust.expires_at < cur_time { - return Err("Trust in chain is expired.".to_string()); + return TrustError::Expired(trust.expires_at, cur_time); } let msg: &[u8] = &Self::metadata_bytes(&trust.issued_for, trust.expires_at, trust.issued_at); - KeyPair::verify(issued_by, msg, &trust.signature)?; + KeyPair::verify(issued_by, msg, &trust.signature).map_err(|e| SignatureError(e))?; Ok(()) } @@ -132,17 +149,20 @@ impl Trust { /// Decode a trust from a byte array as produced by `encode`. #[allow(dead_code)] - pub fn decode(arr: &[u8]) -> Result { + pub fn decode(arr: &[u8]) -> Result { if arr.len() != TRUST_LEN { - return Err( - format!("Trust length should be 104: public key(32) + signature(64) + expiration date(8), was: {}", arr.len()), + return DecodeError( + format!("Trust length should be 104: public key(32) + signature(64) + expiration date(8), was: {}", + arr.len()) ); } - let pk = PublicKey::from_bytes(&arr[0..PK_LEN]).map_err(|err| err.to_string())?; + let pk = PublicKey::from_bytes(&arr[0..PK_LEN]) + .map_err(|err| DecodeError(format!("Cannot decode a public key: {}", err.to_string())))?; let signature = &arr[PK_LEN..PK_LEN + SIG_LEN]; - let signature = Signature::from_bytes(signature).map_err(|err| err.to_string())?; + let signature = Signature::from_bytes(signature) + .map_err(|err| DecodeError(format!("Cannot decode a signature: {}", err.to_string())))?; let expiration_bytes = &arr[PK_LEN + SIG_LEN..PK_LEN + SIG_LEN + EXPIRATION_LEN]; let expiration_date = u64::from_le_bytes(expiration_bytes.try_into().unwrap()); @@ -160,7 +180,7 @@ impl Trust { }) } - fn bs58_str_to_vec(str: &str, field: &str) -> Result, String> { + fn bs58_str_to_vec(str: &str, field: &str) -> Result, TrustError> { bs58::decode(str).into_vec().map_err(|e| { format!( "Cannot decode `{}` from base58 format in the trust '{}': {}", @@ -169,12 +189,12 @@ impl Trust { }) } - fn str_to_duration(str: &str, field: &str) -> Result { + fn str_to_duration(str: &str, field: &str) -> Result { let secs = str.parse().map_err(|e| { - format!( + DecodeError(format!( "Cannot parse `{}` field in the trust '{}': {}", field, str, e - ) + )) })?; Ok(Duration::from_secs(secs)) } @@ -184,19 +204,19 @@ impl Trust { signature: &str, expires_at: &str, issued_at: &str, - ) -> Result { + ) -> Result { // PublicKey let issued_for_bytes = Self::bs58_str_to_vec(issued_for, "issued_for")?; let issued_for = PublicKey::from_bytes(issued_for_bytes.as_slice()).map_err(|e| { - format!( + DecodeError(format!( "Cannot decode the public key: {} in the trust '{}'", issued_for, e - ) + )) })?; // 64 bytes signature let signature = Self::bs58_str_to_vec(signature, "signature")?; - let signature = Signature::from_bytes(&signature).map_err(|err| err.to_string())?; + let signature = Signature::from_bytes(&signature).map_err(|err| DecodeError(err.to_string()))?; // Duration let expires_at = Self::str_to_duration(expires_at, "expires_at")?; diff --git a/src/trust_graph.rs b/src/trust_graph.rs index 7f40878..a4f66af 100644 --- a/src/trust_graph.rs +++ b/src/trust_graph.rs @@ -14,16 +14,17 @@ * limitations under the License. */ -use crate::certificate::Certificate; +use crate::certificate::{Certificate, CerificateError}; use crate::public_key_hashable::PublicKeyHashable; use crate::revoke::Revoke; use crate::trust::Trust; -use crate::trust_graph_storage::Storage; +use crate::trust_graph_storage::{Storage, StorageError}; use crate::trust_node::{Auth, TrustNode}; use fluence_identity::public_key::PublicKey; use std::borrow::Borrow; use std::collections::{HashSet, VecDeque}; use std::time::Duration; +use crate::trust_graph::TrustGraphError::{InternalStorageError, CertificateCheckError}; /// for simplicity, we store `n` where Weight = 1/n^2 pub type Weight = u32; @@ -32,48 +33,60 @@ pub type Weight = u32; /// TODO serialization/deserialization /// TODO export a certificate from graph #[allow(dead_code)] -pub struct TrustGraph { - storage: Box, +pub struct TrustGraph where S: Storage { + storage: Box, +} + +pub enum TrustGraphError { + InternalStorageError(String), + CertificateCheckError(CerificateError) +} + +impl Into for CerificateError { + fn into(self) -> TrustGraphError { + CertificateCheckError(self) + } } #[allow(dead_code)] -impl TrustGraph { - pub fn new(storage: Box) -> Self { +impl TrustGraph where S: Storage { + pub fn new(storage: Box) -> Self { Self { storage: storage } } /// Insert new root weight - pub fn add_root_weight(&mut self, pk: PublicKeyHashable, weight: Weight) { - self.storage.add_root_weight(pk, weight) + pub fn add_root_weight(&mut self, pk: PublicKeyHashable, weight: Weight) -> Result<(), TrustGraphError> { + self.storage.add_root_weight(pk, weight).map_err(|e| InternalStorageError(e.into())) } /// Get trust by public key - pub fn get(&self, pk: PublicKey) -> Option { - self.storage.get(&pk.into()) + pub fn get(&self, pk: PublicKey) -> Result, TrustGraphError> { + self.storage.get(&pk.into()).map_err(|e| InternalStorageError(e.into())) } // TODO: remove cur_time from api, leave it for tests only /// Certificate is a chain of trusts, add this chain to graph - pub fn add(&mut self, cert: C, cur_time: Duration) -> Result<(), String> + pub fn add(&mut self, cert: C, cur_time: Duration) -> Result<(), TrustGraphError> where C: Borrow, { let roots: Vec = self .storage .root_keys() + .map_err(|e| InternalStorageError(e.into()))? .iter() .cloned() .map(Into::into) .collect(); // Check that certificate is valid and converges to one of the known roots - Certificate::verify(cert.borrow(), roots.as_slice(), cur_time)?; + Certificate::verify(cert.borrow(), roots.as_slice(), cur_time).map_err(|e| e.into())?; let mut chain = cert.borrow().chain.iter(); - let root_trust = chain.next().ok_or("empty chain")?; + let root_trust = chain.next().ok_or("empty chain").map_err(|e| InternalStorageError(e.into()))?; let root_pk: PublicKeyHashable = root_trust.issued_for.clone().into(); // Insert new TrustNode for this root_pk if there wasn't one - if self.storage.get(&root_pk).is_none() { + if self.storage.get(&root_pk).map_err(|e| InternalStorageError(e.into()))?.is_none() { let mut trust_node = TrustNode::new(root_trust.issued_for.clone(), cur_time); let root_auth = Auth { trust: root_trust.clone(), @@ -103,31 +116,32 @@ impl TrustGraph { } /// Get the maximum weight of trust for one public key. - pub fn weight

(&self, pk: P) -> Option + pub fn weight

(&self, pk: P) -> Result, TrustGraphError> where P: Borrow, { - if let Some(weight) = self.storage.get_root_weight(pk.borrow().as_ref()) { - return Some(weight); + if let Some(weight) = self.storage.get_root_weight(pk.borrow().as_ref()).map_err(|e| InternalStorageError(e.into()))? { + return Ok(Some(weight)); } let roots: Vec = self .storage .root_keys() + .map_err(|e| InternalStorageError(e.into()))? .iter() .map(|pk| pk.clone().into()) .collect(); // get all possible certificates from the given public key to all roots in the graph let certs = self.get_all_certs(pk, roots.as_slice()); - self.certificates_weight(certs) + Ok(self.certificates_weight(certs)?) } /// Calculate weight from given certificates /// Returns None if there is no such public key /// or some trust between this key and a root key is revoked. /// TODO handle non-direct revocations - pub fn certificates_weight(&self, certs: I) -> Option + pub fn certificates_weight(&self, certs: I) -> Result, TrustGraphError> where C: Borrow, I: IntoIterator, @@ -135,7 +149,9 @@ impl TrustGraph { let mut certs = certs.into_iter().peekable(); // if there are no certificates for the given public key, there is no info about this public key // or some elements of possible certificate chains was revoked - certs.peek()?; + if certs.peek().is_none() { + return Ok(None) + } let mut weight = std::u32::MAX; @@ -146,14 +162,14 @@ impl TrustGraph { .storage .get_root_weight(cert.chain.first()?.issued_for.as_ref()) // This panic shouldn't happen // TODO: why? - .expect("first trust in chain must be in root_weights"); + .map_err(|e| InternalStorageError(e.into()))?; // certificate weight = root weight + 1 * every other element in the chain // (except root, so the formula is `root weight + chain length - 1`) weight = std::cmp::min(weight, root_weight + cert.chain.len() as u32 - 1) } - Some(weight) + Ok(Some(weight)) } /// BF search for all converging paths (chains) in the graph diff --git a/src/trust_graph_storage.rs b/src/trust_graph_storage.rs index 2f8d566..d171d78 100644 --- a/src/trust_graph_storage.rs +++ b/src/trust_graph_storage.rs @@ -5,22 +5,31 @@ use crate::trust_node::{Auth, TrustNode}; use fluence_identity::public_key::PublicKey; use std::collections::HashMap; use std::time::Duration; +use thiserror::Error as ThisError; +use crate::trust_graph_storage::InMemoryStorageError::RevokeError; + +pub trait StorageError {} + + pub trait Storage { - fn get(&self, pk: &PublicKeyHashable) -> Option; - fn insert(&mut self, pk: PublicKeyHashable, node: TrustNode); - fn get_root_weight(&self, pk: &PublicKeyHashable) -> Option; - fn add_root_weight(&mut self, pk: PublicKeyHashable, weight: Weight); - fn root_keys(&self) -> Vec; - fn revoke(&mut self, pk: &PublicKeyHashable, revoke: Revoke) -> Result<(), String>; + type Error: StorageError + Into; + + fn get(&self, pk: &PublicKeyHashable) -> Result, Self::Error>; + fn insert(&mut self, pk: PublicKeyHashable, node: TrustNode) -> Result<(), Self::Error>; + + fn get_root_weight(&self, pk: &PublicKeyHashable) -> Result, Self::Error>; + fn add_root_weight(&mut self, pk: PublicKeyHashable, weight: Weight) -> Result<(), Self::Error>; + fn root_keys(&self) -> Result, Self::Error>; + fn revoke(&mut self, pk: &PublicKeyHashable, revoke: Revoke) -> Result<(), Self::Error>; fn update_auth( &mut self, pk: &PublicKeyHashable, auth: Auth, issued_for: &PublicKey, cur_time: Duration, - ); + ) -> Result<(), Self::Error>; } #[derive(Debug, Default)] @@ -51,34 +60,47 @@ impl InMemoryStorage { } } +#[derive(ThisError, Debug)] +pub enum InMemoryStorageError { + + #[error("{0:?}")] + RevokeError(String) +} + +impl StorageError for InMemoryStorage {} + impl Storage for InMemoryStorage { - fn get(&self, pk: &PublicKeyHashable) -> Option { - self.nodes.get(pk).cloned() + + type Error = InMemoryStorageError; + + fn get(&self, pk: &PublicKeyHashable) -> Result, Self::Error> { + Ok(self.nodes.get(pk).cloned()) } - fn insert(&mut self, pk: PublicKeyHashable, node: TrustNode) { + fn insert(&mut self, pk: PublicKeyHashable, node: TrustNode) -> Result<(), Self::Error> { &self.nodes.insert(pk, node); + Ok(()) } - fn get_root_weight(&self, pk: &PublicKeyHashable) -> Option { - self.root_weights.get(pk).cloned() + fn get_root_weight(&self, pk: &PublicKeyHashable) -> Result, Self::Error> { + Ok(self.root_weights.get(pk).cloned()) } - fn add_root_weight(&mut self, pk: PublicKeyHashable, weight: Weight) { - &self.root_weights.insert(pk, weight); + fn add_root_weight(&mut self, pk: PublicKeyHashable, weight: Weight) -> Result<(), Self::Error> { + Ok(&self.root_weights.insert(pk, weight)); } - fn root_keys(&self) -> Vec { - self.root_weights.keys().cloned().map(Into::into).collect() + fn root_keys(&self) -> Result, Self::Error> { + Ok(self.root_weights.keys().cloned().map(Into::into).collect()) } - fn revoke(&mut self, pk: &PublicKeyHashable, revoke: Revoke) -> Result<(), String> { + fn revoke(&mut self, pk: &PublicKeyHashable, revoke: Revoke) -> Result<(), Self::Error> { match self.nodes.get_mut(&pk) { Some(trust_node) => { trust_node.update_revoke(revoke); Ok(()) } - None => Err("There is no trust with such PublicKey".to_string()), + None => RevokeError("There is no trust with such PublicKey".to_string()), } }