Merge pull request #2 from fluencelabs/error-handling

Error handling
This commit is contained in:
Dima 2021-02-15 13:04:00 +03:00 committed by GitHub
commit 2dc5df28cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 981 additions and 448 deletions

2
Cargo.lock generated
View File

@ -406,6 +406,7 @@ dependencies = [
"serde_json",
"serde_with",
"signature",
"thiserror",
]
[[package]]
@ -1428,6 +1429,7 @@ dependencies = [
"serde_json",
"serde_with",
"signature",
"thiserror",
]
[[package]]

View File

@ -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"

View File

@ -1,5 +0,0 @@
#!/bin/bash
fce build
mv target/wasm32-wasi/debug/trust-graph.wasm artifacts/
RUST_LOG="info" fce-repl Config.toml

View File

@ -1,58 +0,0 @@
use crate::storage_impl::get_data;
use fluence::fce;
use fluence_identity::KeyPair;
use std::time::Duration;
use trust_graph::Certificate;
use std::str::FromStr;
struct InsertResult {
ret_code: u32,
error: String,
}
// TODO: some sort of auth?
fn insert_cert(certificate: String, duration: u64) -> InsertResult {
let duration = Duration::from_millis(duration);
let certificate = Certificate::from_str(&certificate).unwrap();
let mut tg = get_data().lock();
tg.add(certificate, duration).unwrap();
return InsertResult {
ret_code: 0,
error: "".to_string()
}
}
#[fce]
fn looper() {
let mut a = 0;
while true {
a = a + 1;
log::info!("{}", a)
}
}
#[fce]
fn test() -> String {
let mut tg = get_data().lock();
let root_kp = KeyPair::generate();
let root_kp2 = KeyPair::generate();
let second_kp = KeyPair::generate();
let expires_at = Duration::new(15, 15);
let issued_at = Duration::new(5, 5);
let cert = Certificate::issue_root(&root_kp, second_kp.public_key(), expires_at, issued_at);
tg.add_root_weight(root_kp.public().into(), 0);
tg.add_root_weight(root_kp2.public().into(), 1);
tg.add(cert, Duration::new(10, 10));
let a = tg.get(second_kp.public_key());
let str = format!("{:?}", a);
log::info!("{}", &str);
str
}

View File

@ -1,192 +0,0 @@
// store list of trusts
// check if trust is already in list before adding
// if there is an older trust - don't add received trust
use core::convert::TryFrom;
use fluence_identity::public_key::PublicKey;
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use fce_sqlite_connector;
use fce_sqlite_connector::Connection;
use fce_sqlite_connector::Value;
use std::str::FromStr;
use std::time::Duration;
use trust_graph::{Auth, PublicKeyHashable, Revoke, Storage, TrustGraph, TrustNode, Weight};
use std::ops::Deref;
static INSTANCE: OnceCell<Mutex<TrustGraph>> = OnceCell::new();
pub fn get_data() -> &'static Mutex<TrustGraph> {
INSTANCE.get_or_init(|| {
let db_path = "/tmp/users123123.sqlite";
let connection = fce_sqlite_connector::open(db_path).unwrap();
let init_sql = "CREATE TABLE IF NOT EXISTS trustnodes(
public_key TEXT PRIMARY KEY,
trustnode BLOB NOT NULL
);
CREATE TABLE IF NOT EXISTS roots(
public_key TEXT,
weight INTEGER
);";
connection.execute(init_sql).expect("cannot connect to db");
Mutex::new(TrustGraph::new(Box::new(SqliteStorage::new(connection))))
})
}
pub struct SqliteStorage {
connection: Connection,
}
impl SqliteStorage {
pub fn new(connection: Connection) -> SqliteStorage {
SqliteStorage { connection }
}
}
impl Storage for SqliteStorage {
fn get(&self, pk: &PublicKeyHashable) -> Option<TrustNode> {
let mut cursor = self
.connection
.prepare("SELECT trustnode FROM trustnodes WHERE public_key = ?")
.expect("unexpected: 'get' request should be correct")
.cursor();
cursor
.bind(&[Value::String(format!("{}", pk))])
.expect("unexpected: 'public_key' field should be string");
match cursor.next().unwrap() {
Some(r) => {
log::info!("row: {:?}", r);
let tn_bin: &[u8] = r[0]
.as_binary()
.expect("unexpected: 'trustnode' in a table should be as binary");
log::info!("binary: {:?}", tn_bin);
let trust_node: TrustNode = rmp_serde::from_read_ref(tn_bin)
// let trust_node: TrustNode = bincode::deserialize(tn_bin)
// let trust_node: TrustNode = serde_bencode::de::from_bytes(tn_bin)
.expect("unexpected: 'trustnode' should be as correct binary");
log::info!("trustnode: {:?}", trust_node);
Some(trust_node)
}
None => None,
}
}
fn insert(&mut self, pk: PublicKeyHashable, node: TrustNode) {
let mut cursor = self
.connection
.prepare("INSERT OR REPLACE INTO trustnodes VALUES (?, ?)")
.unwrap()
.cursor();
let tn_vec = rmp_serde::to_vec(&node).unwrap();
// let tn_vec = bincode::serialize(&node).unwrap();
let tn_vec = serde_bencode::to_bytes(&node).unwrap();
log::info!("insert: {:?}", tn_vec);
cursor
.bind(&[Value::String(format!("{}", pk)), Value::Binary(tn_vec)])
.unwrap();
cursor.next().unwrap();
}
fn get_root_weight(&self, pk: &PublicKeyHashable) -> Option<Weight> {
let mut cursor = self
.connection
.prepare("SELECT public_key,weight FROM roots WHERE public_key = ?")
.unwrap()
.cursor();
cursor.bind(&[Value::String(format!("{}", pk))]).unwrap();
if let Some(row) = cursor.next().unwrap() {
log::info!("row: {:?}", row);
let w = u32::try_from(row[1].as_integer().unwrap()).unwrap();
Some(w)
} else {
None
}
}
fn add_root_weight(&mut self, pk: PublicKeyHashable, weight: Weight) {
log::info!("add root: {} weight: {}", pk, weight);
let mut cursor = self
.connection
.prepare("INSERT OR REPLACE INTO roots VALUES (?, ?)")
.unwrap()
.cursor();
cursor
.bind(&[
Value::String(format!("{}", pk)),
Value::Integer(i64::from(weight)),
])
.unwrap();
cursor.next().unwrap();
}
fn root_keys(&self) -> Vec<PublicKeyHashable> {
let mut cursor = self
.connection
.prepare("SELECT public_key,weight FROM roots")
.unwrap()
.cursor();
let mut roots = vec![];
while let Some(row) = cursor.next().unwrap() {
log::info!("row: {:?}", row);
let pk: PublicKeyHashable =
PublicKeyHashable::from_str(row[0].as_string().unwrap()).unwrap();
roots.push(pk)
}
roots
}
fn revoke(&mut self, pk: &PublicKeyHashable, revoke: Revoke) -> Result<(), String> {
match self.get(&pk) {
Some(mut trust_node) => {
trust_node.update_revoke(revoke);
self.insert(pk.clone(), trust_node);
Ok(())
}
None => Err("There is no trust with such PublicKey".to_string()),
}
}
fn update_auth(
&mut self,
pk: &PublicKeyHashable,
auth: Auth,
issued_for: &PublicKey,
cur_time: Duration,
) {
match self.get(&pk) {
Some(mut trust_node) => {
trust_node.update_auth(auth);
self.insert(pk.clone(), trust_node)
}
None => {
let mut trust_node = TrustNode::new(issued_for.clone(), cur_time);
trust_node.update_auth(auth);
self.insert(pk.clone(), trust_node);
}
}
}
}

1
identity/Cargo.lock generated
View File

@ -344,6 +344,7 @@ dependencies = [
"serde_json",
"serde_with",
"signature",
"thiserror",
]
[[package]]

View File

@ -16,3 +16,4 @@ rand = "0.7.0"
signature = "1.3.0"
ed25519 = "1.0.3"
serde_with = "1.6.0"
thiserror = "1.0.23"

View File

@ -79,11 +79,10 @@ impl KeyPair {
}
/// Verify the Ed25519 signature on a message using the public key.
pub fn verify(pk: &PublicKey, msg: &[u8], signature: &Signature) -> Result<(), String> {
pub fn verify(pk: &PublicKey, msg: &[u8], signature: &Signature) -> Result<(), SignatureError> {
// let signature = ed25519_dalek::Signature::from_bytes(signature)
// .map_err(|err| format!("Cannot convert bytes to a signature: {:?}", err))?;
pk.verify_strict(msg, signature)
.map_err(|err| format!("Signature verification failed: {:?}", err))
}
}
@ -144,8 +143,8 @@ impl<'de> serde::Deserialize<'de> for KeyPair {
impl Clone for KeyPair {
fn clone(&self) -> KeyPair {
let mut sk_bytes = self.key_pair.secret.to_bytes();
let secret = ed25519_dalek::SecretKey::from_bytes(&mut sk_bytes)
let sk_bytes = self.key_pair.secret.to_bytes();
let secret = ed25519_dalek::SecretKey::from_bytes(&sk_bytes)
.expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k");
let public = ed25519_dalek::PublicKey::from_bytes(&self.key_pair.public.to_bytes())
.expect("ed25519::PublicKey::from_bytes(to_bytes(k)) != k");

View File

@ -14,10 +14,20 @@
* limitations under the License.
*/
use crate::public_key::PKError::{FromBase58Error, FromBytesError};
use crate::signature::Signature;
use core::fmt::Debug;
use ed25519_dalek::SignatureError;
use serde::{Deserialize, Serialize};
use thiserror::Error as ThisError;
#[derive(ThisError, Debug)]
pub enum PKError {
#[error("Cannot decode public key from bytes: {0}")]
FromBytesError(#[source] SignatureError),
#[error("Cannot decode public key from base58 format: {0}")]
FromBase58Error(#[source] bs58::decode::Error),
}
#[derive(Copy, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct PublicKey(pub(crate) ed25519_dalek::PublicKey);
@ -37,9 +47,13 @@ impl PublicKey {
self.0.verify_strict(message, &signature.0)
}
pub fn from_bytes(bytes: &[u8]) -> Result<PublicKey, SignatureError> {
let pk = ed25519_dalek::PublicKey::from_bytes(bytes)?;
pub fn from_base58(str: &str) -> Result<PublicKey, PKError> {
let bytes = bs58::decode(str).into_vec().map_err(FromBase58Error)?;
Self::from_bytes(&bytes)
}
pub fn from_bytes(bytes: &[u8]) -> Result<PublicKey, PKError> {
let pk = ed25519_dalek::PublicKey::from_bytes(bytes).map_err(FromBytesError)?;
Ok(PublicKey(pk))
}

View File

@ -15,7 +15,19 @@
*/
use serde::{Deserialize, Serialize};
use signature::Error as SigError;
use signature::Signature as SigSignature;
use thiserror::Error as ThisError;
#[derive(ThisError, Debug)]
pub enum SignatureError {
#[error("{0}")]
Error(
#[from]
#[source]
SigError,
),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Signature(pub ed25519_dalek::Signature);
@ -33,8 +45,8 @@ impl Signature {
self.0.to_bytes()
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, String> {
let sig = ed25519_dalek::Signature::from_bytes(bytes).map_err(|err| err.to_string())?;
pub fn from_bytes(bytes: &[u8]) -> Result<Self, SignatureError> {
let sig = ed25519_dalek::Signature::from_bytes(bytes)?;
Ok(Signature(sig))
}
}

View File

@ -14,11 +14,17 @@
* limitations under the License.
*/
use crate::trust::{Trust, TRUST_LEN};
use crate::certificate::CertificateError::{
CertificateLengthError, DecodeError, DecodeTrustError, ExpirationError, IncorrectByteLength,
IncorrectCertificateFormat, KeyInCertificateError, MalformedRoot, NoTrustedRoot,
VerificationError,
};
use crate::trust::{Trust, TrustError, TRUST_LEN};
use fluence_identity::key_pair::KeyPair;
use fluence_identity::public_key::PublicKey;
use std::str::FromStr;
use std::time::Duration;
use thiserror::Error as ThisError;
/// Serialization format of a certificate.
/// TODO
@ -33,6 +39,35 @@ pub struct Certificate {
pub chain: Vec<Trust>,
}
#[derive(ThisError, Debug)]
pub enum CertificateError {
#[error("Incorrect format of the certificate: {0}")]
IncorrectCertificateFormat(String),
#[error("Incorrect length of an array. Should be 2 bytes of a format, 4 bytes of a version and 104 bytes for each trust")]
IncorrectByteLength,
#[error("Error while decoding a trust in a certificate: {0}")]
DecodeError(#[source] TrustError),
#[error("Certificate is expired. Issued at {issued_at} and expired at {expires_at}")]
ExpirationError {
expires_at: String,
issued_at: String,
},
#[error("Certificate does not contain a trusted root.")]
NoTrustedRoot,
#[error("Root trust did not pass verification: {0}")]
MalformedRoot(#[source] TrustError),
#[error("There is no `issued_by` public key in a certificate")]
KeyInCertificateError,
#[error("The certificate must have at least 1 trust")]
CertificateLengthError,
#[error("Cannot convert trust number {0} from string: {1}")]
DecodeTrustError(usize, #[source] TrustError),
#[error("Trust {0} in chain did not pass verification: {1}")]
VerificationError(usize, #[source] TrustError),
#[error("there cannot be paths without any nodes after adding verified certificates")]
Unexpected,
}
impl Certificate {
pub fn new_unverified(chain: Vec<Trust>) -> Self {
Self { chain }
@ -65,17 +100,16 @@ impl Certificate {
expires_at: Duration,
issued_at: Duration,
cur_time: Duration,
) -> Result<Self, String> {
) -> Result<Self, CertificateError> {
if expires_at.lt(&issued_at) {
return Err("Expiration time should be greater than issued time.".to_string());
return Err(ExpirationError {
expires_at: format!("{:?}", expires_at),
issued_at: format!("{:?}", issued_at),
});
}
// first, verify given certificate
Certificate::verify(
extend_cert,
&[extend_cert.chain[0].issued_for.clone()],
cur_time,
)?;
Certificate::verify(extend_cert, &[extend_cert.chain[0].issued_for], cur_time)?;
let issued_by_pk = issued_by.public_key();
@ -88,7 +122,7 @@ impl Certificate {
}
if previous_trust_num == -1 {
return Err("Your public key should be in certificate.".to_string());
return Err(KeyInCertificateError);
};
// splitting old chain to add new trust after given public key
@ -110,19 +144,18 @@ impl Certificate {
cert: &Certificate,
trusted_roots: &[PublicKey],
cur_time: Duration,
) -> Result<(), String> {
) -> Result<(), CertificateError> {
let chain = &cert.chain;
if chain.is_empty() {
return Err("The certificate must have at least 1 trust".to_string());
return Err(CertificateLengthError);
}
// check root trust and its existence in trusted roots list
let root = &chain[0];
Trust::verify(root, &root.issued_for, cur_time)
.map_err(|e| format!("Root trust did not pass verification: {}", e))?;
Trust::verify(root, &root.issued_for, cur_time).map_err(MalformedRoot)?;
if !trusted_roots.contains(&root.issued_for) {
return Err("Certificate does not contain a trusted root.".to_string());
return Err(NoTrustedRoot);
}
// check if every element in a chain is not expired and has the correct signature
@ -131,12 +164,8 @@ impl Certificate {
let trust_giver = &chain[trust_id - 1];
Trust::verify(trust, &trust_giver.issued_for, cur_time).map_err(|e| {
format!(
"Trust {} in chain did not pass verification: {}",
trust_id, e
)
})?;
Trust::verify(trust, &trust_giver.issued_for, cur_time)
.map_err(|e| VerificationError(trust_id, e))?;
}
Ok(())
@ -159,16 +188,16 @@ impl Certificate {
}
#[allow(dead_code)]
pub fn decode(arr: &[u8]) -> Result<Self, String> {
pub fn decode(arr: &[u8]) -> Result<Self, CertificateError> {
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());
return Err(IncorrectByteLength);
}
let number_of_trusts = trusts_offset / TRUST_LEN;
if number_of_trusts < 2 {
return Err("The certificate must have at least 2 trusts.".to_string());
return Err(CertificateLengthError);
}
// TODO do match different formats and versions
@ -181,7 +210,7 @@ impl Certificate {
let from = i * TRUST_LEN + 6;
let to = (i + 1) * TRUST_LEN + 6;
let slice = &arr[from..to];
let t = Trust::decode(slice)?;
let t = Trust::decode(slice).map_err(DecodeError)?;
chain.push(t);
}
@ -201,7 +230,7 @@ impl std::fmt::Display for Certificate {
}
impl FromStr for Certificate {
type Err = String;
type Err = CertificateError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let str_lines: Vec<&str> = s.lines().collect();
@ -211,7 +240,7 @@ impl FromStr for Certificate {
let _version = str_lines[1];
if (str_lines.len() - 2) % 4 != 0 {
return Err(format!("Incorrect format of the certificate: {}", s));
return Err(IncorrectCertificateFormat(s.to_string()));
}
let num_of_trusts = (str_lines.len() - 2) / 4;
@ -223,7 +252,8 @@ impl FromStr for Certificate {
str_lines[i + 1],
str_lines[i + 2],
str_lines[i + 3],
)?;
)
.map_err(|e| DecodeTrustError(i, e))?;
trusts.push(trust);
}

View File

@ -36,11 +36,11 @@ mod trust_graph;
mod trust_graph_storage;
mod trust_node;
pub use crate::certificate::Certificate;
pub use crate::certificate::{Certificate, CertificateError};
pub use crate::misc::current_time;
pub use crate::public_key_hashable::PublicKeyHashable;
pub use crate::revoke::Revoke;
pub use crate::trust::Trust;
pub use crate::trust_graph::{TrustGraph, Weight};
pub use crate::trust_graph_storage::Storage;
pub use crate::trust_graph::{TrustGraph, TrustGraphError, Weight};
pub use crate::trust_graph_storage::{Storage, StorageError};
pub use crate::trust_node::{Auth, TrustNode};

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
use fluence_identity::public_key::PublicKey;
use fluence_identity::public_key::{PKError, PublicKey};
use core::fmt;
use ref_cast::RefCast;
@ -82,15 +82,10 @@ impl Display for PublicKeyHashable {
}
impl FromStr for PublicKeyHashable {
type Err = String;
type Err = PKError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let bytes = bs58::decode(s)
.into_vec()
.map_err(|err| format!("Invalid string '{}': {}", s, err))?;
let pk = PublicKey::from_bytes(&bytes)
.map_err(|err| format!("Invalid bytes {:?}: {}", bytes, err))?;
let pk = PublicKey::from_base58(s)?;
Ok(PublicKeyHashable::from(pk))
}
}

View File

@ -14,12 +14,21 @@
* limitations under the License.
*/
use crate::revoke::RevokeError::IncorrectSignature;
use crate::trust::{EXPIRATION_LEN, PK_LEN};
use ed25519_dalek::SignatureError;
use fluence_identity::key_pair::KeyPair;
use fluence_identity::public_key::PublicKey;
use fluence_identity::signature::Signature;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use thiserror::Error as ThisError;
#[derive(ThisError, Debug)]
pub enum RevokeError {
#[error("Signature is incorrect: {0}")]
IncorrectSignature(#[source] SignatureError),
}
/// "A document" that cancels trust created before.
/// TODO delete pk from Revoke (it is already in a trust node)
@ -69,13 +78,13 @@ impl Revoke {
}
/// Verifies that revocation is cryptographically correct.
pub fn verify(revoke: &Revoke) -> Result<(), String> {
pub fn verify(revoke: &Revoke) -> Result<(), RevokeError> {
let msg = Revoke::signature_bytes(&revoke.pk, revoke.revoked_at);
revoke
.revoked_by
.verify_strict(msg.as_slice(), &revoke.signature)
.map_err(|err| format!("Revoke has incorrect signature: {:?}", err))
.map_err(IncorrectSignature)
}
}

View File

@ -14,13 +14,18 @@
* limitations under the License.
*/
use crate::trust::TrustError::{
Base58DecodeError, DecodePublicKeyError, IncorrectTrustLength, ParseError, SignatureError,
};
use derivative::Derivative;
use fluence_identity::key_pair::KeyPair;
use fluence_identity::public_key::PublicKey;
use fluence_identity::signature::Signature;
use fluence_identity::public_key::{PKError, PublicKey};
use fluence_identity::signature::{Signature, SignatureError as SigError};
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use std::num::ParseIntError;
use std::time::Duration;
use thiserror::Error as ThisError;
pub const SIG_LEN: usize = 64;
pub const PK_LEN: usize = 32;
@ -55,6 +60,46 @@ 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(
#[from]
#[source]
ed25519_dalek::SignatureError,
),
/// Errors occured on trust decoding from differrent formats
#[error("Cannot decode the public key: {0} in the trust: {1}")]
DecodePublicKeyError(String, #[source] PKError),
#[error("Cannot parse `{0}` field in the trust '{1}': {2}")]
ParseError(String, String, #[source] ParseIntError),
#[error("Cannot decode `{0}` from base58 format in the trust '{1}': {2}")]
Base58DecodeError(String, String, #[source] bs58::decode::Error),
#[error("Cannot decode a signature from bytes: {0}")]
SignatureFromBytesError(#[from] SigError),
#[error("{0}")]
PublicKeyError(
#[from]
#[source]
PKError,
),
#[error(
"Trust length should be 104: public key(32) + signature(64) + expiration date(8), was: {0}"
)]
IncorrectTrustLength(usize),
}
impl Trust {
#[allow(dead_code)]
pub fn new(
@ -90,15 +135,19 @@ 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 Err(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(SignatureError)?;
Ok(())
}
@ -132,17 +181,15 @@ impl Trust {
/// Decode a trust from a byte array as produced by `encode`.
#[allow(dead_code)]
pub fn decode(arr: &[u8]) -> Result<Self, String> {
pub fn decode(arr: &[u8]) -> Result<Self, TrustError> {
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 Err(IncorrectTrustLength(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])?;
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)?;
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());
@ -154,28 +201,22 @@ impl Trust {
Ok(Self {
issued_for: pk,
signature: signature,
signature,
expires_at: expiration_date,
issued_at: issued_date,
})
}
fn bs58_str_to_vec(str: &str, field: &str) -> Result<Vec<u8>, String> {
bs58::decode(str).into_vec().map_err(|e| {
format!(
"Cannot decode `{}` from base58 format in the trust '{}': {}",
field, str, e
)
})
fn bs58_str_to_vec(str: &str, field: &str) -> Result<Vec<u8>, TrustError> {
bs58::decode(str)
.into_vec()
.map_err(|e| Base58DecodeError(field.to_string(), str.to_string(), e))
}
fn str_to_duration(str: &str, field: &str) -> Result<Duration, String> {
let secs = str.parse().map_err(|e| {
format!(
"Cannot parse `{}` field in the trust '{}': {}",
field, str, e
)
})?;
fn str_to_duration(str: &str, field: &str) -> Result<Duration, TrustError> {
let secs = str
.parse()
.map_err(|e| ParseError(field.to_string(), str.to_string(), e))?;
Ok(Duration::from_secs(secs))
}
@ -184,19 +225,15 @@ impl Trust {
signature: &str,
expires_at: &str,
issued_at: &str,
) -> Result<Self, String> {
) -> Result<Self, TrustError> {
// 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!(
"Cannot decode the public key: {} in the trust '{}'",
issued_for, e
)
})?;
let issued_for = PublicKey::from_bytes(issued_for_bytes.as_slice())
.map_err(|e| DecodePublicKeyError(issued_for.to_string(), 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)?;
// Duration
let expires_at = Self::str_to_duration(expires_at, "expires_at")?;

View File

@ -14,16 +14,25 @@
* limitations under the License.
*/
use crate::certificate::Certificate;
use crate::public_key_hashable::PublicKeyHashable;
use crate::certificate::CertificateError::{CertificateLengthError, Unexpected};
use crate::certificate::{Certificate, CertificateError};
use crate::public_key_hashable::PublicKeyHashable as PK;
use crate::revoke::Revoke;
use crate::revoke::RevokeError;
use crate::trust::Trust;
use crate::trust_graph::TrustGraphError::{
CertificateCheckError, EmptyChain, InternalStorageError, NoRoot,
};
use crate::trust_graph_storage::Storage;
use crate::trust_node::{Auth, TrustNode};
use crate::StorageError;
use fluence_identity::public_key::PublicKey;
use std::borrow::Borrow;
use std::collections::{HashSet, VecDeque};
use std::convert::{From, Into};
use std::result::Result;
use std::time::Duration;
use thiserror::Error as ThisError;
/// for simplicity, we store `n` where Weight = 1/n^2
pub type Weight = u32;
@ -32,35 +41,75 @@ pub type Weight = u32;
/// TODO serialization/deserialization
/// TODO export a certificate from graph
#[allow(dead_code)]
pub struct TrustGraph {
storage: Box<dyn Storage + Send + Sync>,
pub struct TrustGraph<S>
where
S: Storage,
{
storage: Box<S>,
}
#[derive(ThisError, Debug)]
pub enum TrustGraphError {
#[error("Internal storage error: {0}")]
InternalStorageError(Box<dyn StorageError>),
#[error("There is no root for this certificate.")]
NoRoot,
#[error("Chain is empty")]
EmptyChain,
#[error("Certificate check error: {0}")]
CertificateCheckError(
#[from]
#[source]
CertificateError,
),
#[error("Error on revoking a trust: {0}")]
RevokeCheckError(
#[from]
#[source]
RevokeError,
),
}
impl<T: StorageError + 'static> From<T> for TrustGraphError {
fn from(err: T) -> Self {
InternalStorageError(Box::new(err))
}
}
impl From<TrustGraphError> for String {
fn from(err: TrustGraphError) -> Self {
format!("{}", err)
}
}
#[allow(dead_code)]
impl TrustGraph {
pub fn new(storage: Box<dyn Storage + Send + Sync>) -> Self {
Self { storage: storage }
impl<S> TrustGraph<S>
where
S: Storage,
{
pub fn new(storage: Box<S>) -> Self {
Self { 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: PK, weight: Weight) -> Result<(), TrustGraphError> {
Ok(self.storage.add_root_weight(pk, weight)?)
}
/// Get trust by public key
pub fn get(&self, pk: PublicKey) -> Option<TrustNode> {
self.storage.get(&pk.into())
pub fn get(&self, pk: PublicKey) -> Result<Option<TrustNode>, TrustGraphError> {
Ok(self.storage.get(&pk.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<C>(&mut self, cert: C, cur_time: Duration) -> Result<(), String>
pub fn add<C>(&mut self, cert: C, cur_time: Duration) -> Result<(), TrustGraphError>
where
C: Borrow<Certificate>,
{
let roots: Vec<PublicKey> = self
.storage
.root_keys()
.root_keys()?
.iter()
.cloned()
.map(Into::into)
@ -69,18 +118,18 @@ impl TrustGraph {
Certificate::verify(cert.borrow(), roots.as_slice(), cur_time)?;
let mut chain = cert.borrow().chain.iter();
let root_trust = chain.next().ok_or("empty chain")?;
let root_pk: PublicKeyHashable = root_trust.issued_for.clone().into();
let root_trust = chain.next().ok_or(EmptyChain)?;
let root_pk: PK = 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() {
let mut trust_node = TrustNode::new(root_trust.issued_for.clone(), cur_time);
if self.storage.get(&root_pk)?.is_none() {
let mut trust_node = TrustNode::new(root_trust.issued_for, cur_time);
let root_auth = Auth {
trust: root_trust.clone(),
issued_by: root_trust.issued_for.clone(),
issued_by: root_trust.issued_for,
};
trust_node.update_auth(root_auth);
self.storage.insert(root_pk, trust_node);
self.storage.insert(root_pk, trust_node)?;
}
// Insert remaining trusts to the graph
@ -90,11 +139,11 @@ impl TrustGraph {
let auth = Auth {
trust: trust.clone(),
issued_by: previous_trust.issued_for.clone(),
issued_by: previous_trust.issued_for,
};
self.storage
.update_auth(&pk, auth, &root_trust.issued_for, cur_time);
.update_auth(&pk, auth, &root_trust.issued_for, cur_time)?;
previous_trust = trust;
}
@ -103,31 +152,31 @@ impl TrustGraph {
}
/// Get the maximum weight of trust for one public key.
pub fn weight<P>(&self, pk: P) -> Option<Weight>
pub fn weight<P>(&self, pk: P) -> Result<Option<Weight>, TrustGraphError>
where
P: Borrow<PublicKey>,
{
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())? {
return Ok(Some(weight));
}
let roots: Vec<PublicKey> = self
.storage
.root_keys()
.root_keys()?
.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)
let certs = self.get_all_certs(pk, roots.as_slice())?;
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<C, I>(&self, certs: I) -> Option<Weight>
pub fn certificates_weight<C, I>(&self, certs: I) -> Result<Option<Weight>, TrustGraphError>
where
C: Borrow<Certificate>,
I: IntoIterator<Item = C>,
@ -135,25 +184,31 @@ 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;
for cert in certs {
let cert = cert.borrow();
let c = cert.borrow();
let first = c
.chain
.first()
.ok_or(CertificateCheckError(CertificateLengthError))?;
let root_weight = self
.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");
.get_root_weight(first.issued_for.as_ref())?
.ok_or(NoRoot)?;
// 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)
weight = std::cmp::min(weight, root_weight + c.chain.len() as u32 - 1)
}
Some(weight)
Ok(Some(weight))
}
/// BF search for all converging paths (chains) in the graph
@ -162,8 +217,8 @@ impl TrustGraph {
fn bf_search_paths(
&self,
node: &TrustNode,
roots: HashSet<&PublicKeyHashable>,
) -> Vec<Vec<Auth>> {
roots: HashSet<&PK>,
) -> Result<Vec<Vec<Auth>>, TrustGraphError> {
// queue to collect all chains in the trust graph (each chain is a path in the trust graph)
let mut chains_queue: VecDeque<Vec<Auth>> = VecDeque::new();
@ -187,10 +242,8 @@ impl TrustGraph {
let auths: Vec<Auth> = self
.storage
.get(&last.issued_by.clone().into())
.expect(
"there cannot be paths without any nodes after adding verified certificates",
)
.get(&last.issued_by.clone().into())?
.ok_or(CertificateCheckError(Unexpected))?
.authorizations()
.cloned()
.collect();
@ -212,7 +265,7 @@ impl TrustGraph {
// - that trust must converge to one of the root weights
// - there should be more than 1 trust in the chain
let self_signed = last.issued_by == last.trust.issued_for;
let issued_by: &PublicKeyHashable = last.issued_by.as_ref();
let issued_by: &PK = last.issued_by.as_ref();
let converges_to_root = roots.contains(issued_by);
if self_signed && converges_to_root && cur_chain.len() > 1 {
@ -220,26 +273,30 @@ impl TrustGraph {
}
}
terminated_chains
Ok(terminated_chains)
}
// TODO: remove `roots` argument from api, leave it for tests and internal usage only
/// Get all possible certificates where `issued_for` will be the last element of the chain
/// and one of the destinations is the root of this chain.
pub fn get_all_certs<P>(&self, issued_for: P, roots: &[PublicKey]) -> Vec<Certificate>
pub fn get_all_certs<P>(
&self,
issued_for: P,
roots: &[PublicKey],
) -> Result<Vec<Certificate>, TrustGraphError>
where
P: Borrow<PublicKey>,
{
// get all auths (edges) for issued public key
let issued_for_node = self.storage.get(issued_for.borrow().as_ref());
let issued_for_node = self.storage.get(issued_for.borrow().as_ref())?;
let roots = roots.iter().map(|pk| pk.as_ref());
let keys = self.storage.root_keys();
let keys = self.storage.root_keys()?;
let roots = keys.iter().chain(roots).collect();
match issued_for_node {
Some(node) => self
.bf_search_paths(&node, roots)
Some(node) => Ok(self
.bf_search_paths(&node, roots)?
.iter()
.map(|auths| {
// TODO: can avoid cloning here by returning &Certificate
@ -256,18 +313,18 @@ impl TrustGraph {
);
c.chain.len() > 1
})
.collect(),
None => Vec::new(),
.collect()),
None => Ok(Vec::new()),
}
}
/// Mark public key as revoked.
pub fn revoke(&mut self, revoke: Revoke) -> Result<(), String> {
pub fn revoke(&mut self, revoke: Revoke) -> Result<(), TrustGraphError> {
Revoke::verify(&revoke)?;
let pk: PublicKeyHashable = revoke.pk.clone().into();
let pk: PK = revoke.pk.clone().into();
self.storage.revoke(&pk, revoke)
Ok(self.storage.revoke(&pk, revoke)?)
}
/// Check information about new certificates and about revoked certificates.
@ -313,7 +370,7 @@ mod tests {
keys: HashMap<usize, KeyPair>,
expires_at: Duration,
issued_at: Duration,
) -> (Vec<KeyPair>, Certificate) {
) -> Result<(Vec<KeyPair>, Certificate), TrustGraphError> {
assert!(len > 2);
let root_kp = KeyPair::generate();
@ -335,18 +392,17 @@ mod tests {
// TODO: why `issued_at = issued_at - 60 seconds`?
issued_at.checked_sub(Duration::from_secs(60)).unwrap(),
current_time(),
)
.unwrap();
)?;
key_pairs.push(kp);
}
(key_pairs, cert)
Ok((key_pairs, cert))
}
fn generate_cert_with_len(
len: usize,
keys: HashMap<usize, KeyPair>,
) -> (Vec<KeyPair>, Certificate) {
) -> Result<(Vec<KeyPair>, Certificate), TrustGraphError> {
let cur_time = current_time();
let far_future = cur_time.checked_add(one_minute()).unwrap();
@ -390,7 +446,8 @@ mod tests {
chain_keys.insert(5, key_pair1.clone());
chain_keys.insert(6, key_pair2.clone());
let (key_pairs1, cert1) = generate_cert_with(10, chain_keys, far_future * 2, far_future);
let (key_pairs1, cert1) =
generate_cert_with(10, chain_keys, far_future * 2, far_future).expect("");
// Use key_pair1 and key_pair2 for 7th and 8th trust in the cert chain
let mut chain_keys = HashMap::new();
@ -398,7 +455,7 @@ mod tests {
chain_keys.insert(8, key_pair2.clone());
let (key_pairs2, cert2) =
generate_cert_with(10, chain_keys, far_far_future * 2, far_far_future);
generate_cert_with(10, chain_keys, far_far_future * 2, far_far_future).unwrap();
let st = Box::new(InMemoryStorage::new());
let mut graph = TrustGraph::new(st);
@ -408,7 +465,7 @@ mod tests {
graph.add_root_weight(root2_pk.into(), 0);
graph.add(cert1, cur_time).unwrap();
let node2 = graph.get(key_pair2.public_key()).unwrap();
let node2 = graph.get(key_pair2.public_key()).unwrap().unwrap();
let auth_by_kp1 = node2
.authorizations()
.find(|a| a.issued_by == key_pair1.public_key())
@ -418,7 +475,7 @@ mod tests {
graph.add(cert2, cur_time).unwrap();
let node2 = graph.get(key_pair2.public_key()).unwrap();
let node2 = graph.get(key_pair2.public_key()).unwrap().unwrap();
let auth_by_kp1 = node2
.authorizations()
.find(|a| a.issued_by == key_pair1.public_key())
@ -429,7 +486,7 @@ mod tests {
#[test]
fn test_one_cert_in_graph() {
let (key_pairs, cert1) = generate_cert_with_len(10, HashMap::new());
let (key_pairs, cert1) = generate_cert_with_len(10, HashMap::new()).unwrap();
let last_trust = cert1.chain[9].clone();
let st = Box::new(InMemoryStorage::new());
@ -440,16 +497,16 @@ mod tests {
graph.add(cert1, current_time()).unwrap();
let w1 = graph.weight(key_pairs[0].public_key()).unwrap();
let w1 = graph.weight(key_pairs[0].public_key()).unwrap().unwrap();
assert_eq!(w1, 1);
let w2 = graph.weight(key_pairs[1].public_key()).unwrap();
let w2 = graph.weight(key_pairs[1].public_key()).unwrap().unwrap();
assert_eq!(w2, 2);
let w3 = graph.weight(key_pairs[9].public_key()).unwrap();
let w3 = graph.weight(key_pairs[9].public_key()).unwrap().unwrap();
assert_eq!(w3, 10);
let node = graph.get(key_pairs[9].public_key()).unwrap();
let node = graph.get(key_pairs[9].public_key()).unwrap().unwrap();
let auths: Vec<&Auth> = node.authorizations().collect();
assert_eq!(auths.len(), 1);
@ -467,14 +524,14 @@ mod tests {
chain_keys.insert(5, key_pair2.clone());
chain_keys.insert(7, key_pair3.clone());
let (key_pairs1, cert1) = generate_cert_with_len(10, chain_keys);
let (key_pairs1, cert1) = generate_cert_with_len(10, chain_keys).unwrap();
let mut chain_keys = HashMap::new();
chain_keys.insert(7, key_pair1.clone());
chain_keys.insert(6, key_pair2.clone());
chain_keys.insert(5, key_pair3.clone());
let (key_pairs2, cert2) = generate_cert_with_len(10, chain_keys);
let (key_pairs2, cert2) = generate_cert_with_len(10, chain_keys).unwrap();
let st = Box::new(InMemoryStorage::new());
let mut graph = TrustGraph::new(st);
@ -494,12 +551,12 @@ mod tests {
let revoke2 = Revoke::create(&key_pairs2[5], key_pairs2[6].public_key(), current_time());
graph.revoke(revoke2).unwrap();
let w1 = graph.weight(key_pair1.public_key()).unwrap();
let w1 = graph.weight(key_pair1.public_key()).unwrap().unwrap();
// all upper trusts are revoked for this public key
let w2 = graph.weight(key_pair2.public_key());
let w3 = graph.weight(key_pair3.public_key()).unwrap();
let w_last1 = graph.weight(last_pk1).unwrap();
let w_last2 = graph.weight(last_pk2).unwrap();
let w2 = graph.weight(key_pair2.public_key()).unwrap();
let w3 = graph.weight(key_pair3.public_key()).unwrap().unwrap();
let w_last1 = graph.weight(last_pk1).unwrap().unwrap();
let w_last2 = graph.weight(last_pk2).unwrap().unwrap();
assert_eq!(w1, 4);
assert_eq!(w2.is_none(), true);
@ -510,7 +567,7 @@ mod tests {
#[test]
fn test_get_one_cert() {
let (key_pairs, cert) = generate_cert_with_len(5, HashMap::new());
let (key_pairs, cert) = generate_cert_with_len(5, HashMap::new()).unwrap();
let st = Box::new(InMemoryStorage::new());
let mut graph = TrustGraph::new(st);
@ -519,7 +576,9 @@ mod tests {
graph.add(cert.clone(), current_time()).unwrap();
let certs = graph.get_all_certs(key_pairs.last().unwrap().public_key(), &[root1_pk]);
let certs = graph
.get_all_certs(key_pairs.last().unwrap().public_key(), &[root1_pk])
.unwrap();
assert_eq!(certs.len(), 1);
assert_eq!(certs[0], cert);
@ -527,7 +586,7 @@ mod tests {
#[test]
fn test_chain_from_root_to_another_root() {
let (_, cert) = generate_cert_with_len(6, HashMap::new());
let (_, cert) = generate_cert_with_len(6, HashMap::new()).unwrap();
let st = Box::new(InMemoryStorage::new());
let mut graph = TrustGraph::new(st);
@ -539,7 +598,7 @@ mod tests {
graph.add(cert.clone(), current_time()).unwrap();
let t = cert.chain[5].clone();
let certs = graph.get_all_certs(t.issued_for, &[]);
let certs = graph.get_all_certs(t.issued_for, &[]).unwrap();
assert_eq!(certs.len(), 1);
}
@ -555,21 +614,21 @@ mod tests {
chain_keys.insert(3, key_pair2.clone());
chain_keys.insert(4, key_pair3.clone());
let (key_pairs1, cert1) = generate_cert_with_len(5, chain_keys);
let (key_pairs1, cert1) = generate_cert_with_len(5, chain_keys).unwrap();
let mut chain_keys = HashMap::new();
chain_keys.insert(4, key_pair1.clone());
chain_keys.insert(3, key_pair2.clone());
chain_keys.insert(2, key_pair3.clone());
let (key_pairs2, cert2) = generate_cert_with_len(5, chain_keys);
let (key_pairs2, cert2) = generate_cert_with_len(5, chain_keys).unwrap();
let mut chain_keys = HashMap::new();
chain_keys.insert(3, key_pair1.clone());
chain_keys.insert(4, key_pair2.clone());
chain_keys.insert(2, key_pair3.clone());
let (key_pairs3, cert3) = generate_cert_with_len(5, chain_keys);
let (key_pairs3, cert3) = generate_cert_with_len(5, chain_keys).unwrap();
let st = Box::new(InMemoryStorage::new());
let mut graph = TrustGraph::new(st);
@ -586,17 +645,23 @@ mod tests {
let roots_values = [root1_pk, root2_pk, root3_pk];
let certs1 = graph.get_all_certs(key_pair1.public_key(), &roots_values);
let certs1 = graph
.get_all_certs(key_pair1.public_key(), &roots_values)
.unwrap();
let lenghts1: Vec<usize> = certs1.iter().map(|c| c.chain.len()).collect();
let check_lenghts1: Vec<usize> = vec![3, 4, 4, 5, 5];
assert_eq!(lenghts1, check_lenghts1);
let certs2 = graph.get_all_certs(key_pair2.public_key(), &roots_values);
let certs2 = graph
.get_all_certs(key_pair2.public_key(), &roots_values)
.unwrap();
let lenghts2: Vec<usize> = certs2.iter().map(|c| c.chain.len()).collect();
let check_lenghts2: Vec<usize> = vec![4, 4, 4, 5, 5];
assert_eq!(lenghts2, check_lenghts2);
let certs3 = graph.get_all_certs(key_pair3.public_key(), &roots_values);
let certs3 = graph
.get_all_certs(key_pair3.public_key(), &roots_values)
.unwrap();
let lenghts3: Vec<usize> = certs3.iter().map(|c| c.chain.len()).collect();
let check_lenghts3: Vec<usize> = vec![3, 3, 5];
assert_eq!(lenghts3, check_lenghts3);

View File

@ -1,32 +1,39 @@
use crate::public_key_hashable::PublicKeyHashable;
use crate::public_key_hashable::PublicKeyHashable as PK;
use crate::revoke::Revoke;
use crate::trust_graph::Weight;
use crate::trust_graph_storage::InMemoryStorageError::RevokeError;
use crate::trust_node::{Auth, TrustNode};
use fluence_identity::public_key::PublicKey;
use std::collections::HashMap;
use std::fmt::Display;
use std::time::Duration;
use thiserror::Error as ThisError;
pub trait StorageError: std::error::Error + Display {}
pub trait Storage {
fn get(&self, pk: &PublicKeyHashable) -> Option<TrustNode>;
fn insert(&mut self, pk: PublicKeyHashable, node: TrustNode);
type Error: StorageError + 'static;
fn get_root_weight(&self, pk: &PublicKeyHashable) -> Option<Weight>;
fn add_root_weight(&mut self, pk: PublicKeyHashable, weight: Weight);
fn root_keys(&self) -> Vec<PublicKeyHashable>;
fn revoke(&mut self, pk: &PublicKeyHashable, revoke: Revoke) -> Result<(), String>;
fn get(&self, pk: &PK) -> Result<Option<TrustNode>, Self::Error>;
fn insert(&mut self, pk: PK, node: TrustNode) -> Result<(), Self::Error>;
fn get_root_weight(&self, pk: &PK) -> Result<Option<Weight>, Self::Error>;
fn add_root_weight(&mut self, pk: PK, weight: Weight) -> Result<(), Self::Error>;
fn root_keys(&self) -> Result<Vec<PK>, Self::Error>;
fn revoke(&mut self, pk: &PK, revoke: Revoke) -> Result<(), Self::Error>;
fn update_auth(
&mut self,
pk: &PublicKeyHashable,
pk: &PK,
auth: Auth,
issued_for: &PublicKey,
cur_time: Duration,
);
) -> Result<(), Self::Error>;
}
#[derive(Debug, Default)]
pub struct InMemoryStorage {
nodes: HashMap<PublicKeyHashable, TrustNode>,
root_weights: HashMap<PublicKeyHashable, Weight>,
nodes: HashMap<PK, TrustNode>,
root_weights: HashMap<PK, Weight>,
}
impl InMemoryStorage {
@ -38,7 +45,7 @@ impl InMemoryStorage {
.collect();
Self {
nodes: HashMap::new(),
root_weights: root_weights,
root_weights,
}
}
@ -51,52 +58,68 @@ impl InMemoryStorage {
}
}
#[derive(ThisError, Debug)]
pub enum InMemoryStorageError {
#[error("InMemoryStorageError::RevokeError {0:?}")]
RevokeError(String),
}
impl StorageError for InMemoryStorageError {}
impl Storage for InMemoryStorage {
fn get(&self, pk: &PublicKeyHashable) -> Option<TrustNode> {
self.nodes.get(pk).cloned()
type Error = InMemoryStorageError;
fn get(&self, pk: &PK) -> Result<Option<TrustNode>, Self::Error> {
Ok(self.nodes.get(pk).cloned())
}
fn insert(&mut self, pk: PublicKeyHashable, node: TrustNode) {
&self.nodes.insert(pk, node);
fn insert(&mut self, pk: PK, node: TrustNode) -> Result<(), Self::Error> {
self.nodes.insert(pk, node);
Ok(())
}
fn get_root_weight(&self, pk: &PublicKeyHashable) -> Option<Weight> {
self.root_weights.get(pk).cloned()
fn get_root_weight(&self, pk: &PK) -> Result<Option<Weight>, 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: PK, weight: Weight) -> Result<(), Self::Error> {
self.root_weights.insert(pk, weight);
Ok(())
}
fn root_keys(&self) -> Vec<PublicKeyHashable> {
self.root_weights.keys().cloned().map(Into::into).collect()
fn root_keys(&self) -> Result<Vec<PK>, 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: &PK, 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 => Err(RevokeError(
"There is no trust with such PublicKey".to_string(),
)),
}
}
fn update_auth(
&mut self,
pk: &PublicKeyHashable,
pk: &PK,
auth: Auth,
issued_for: &PublicKey,
cur_time: Duration,
) {
) -> Result<(), Self::Error> {
match self.nodes.get_mut(&pk) {
Some(trust_node) => {
trust_node.update_auth(auth);
Ok(())
}
None => {
let mut trust_node = TrustNode::new(issued_for.clone(), cur_time);
let mut trust_node = TrustNode::new(*issued_for, cur_time);
trust_node.update_auth(auth);
self.nodes.insert(pk.clone(), trust_node);
Ok(())
}
}
}

View File

@ -441,6 +441,7 @@ dependencies = [
"serde_json",
"serde_with",
"signature",
"thiserror",
]
[[package]]
@ -1537,6 +1538,7 @@ dependencies = [
"serde_json",
"serde_with",
"signature",
"thiserror",
]
[[package]]
@ -1556,6 +1558,7 @@ dependencies = [
"rmp-serde",
"serde_bencode",
"serde_json",
"thiserror",
"trust-graph",
]

View File

@ -25,3 +25,4 @@ bs58 = "0.3.1"
rmp-serde = "0.15.0"
bincode = "1.3.1"
serde_bencode = "^0.2.3"
thiserror = "1.0.23"

8
wasm/run-repl.sh Executable file
View File

@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
fce build
rm artifacts/trust-graph.wasm
mv -f target/wasm32-wasi/debug/trust-graph.wasm artifacts/
RUST_LOG="info" fce-repl Config.toml

101
wasm/src/dto.rs Normal file
View File

@ -0,0 +1,101 @@
use fluence::fce;
use fluence_identity::public_key::PKError;
use fluence_identity::signature::SignatureError;
use fluence_identity::{PublicKey, Signature};
use std::convert::TryFrom;
use std::time::Duration;
use thiserror::Error as ThisError;
#[derive(ThisError, Debug)]
pub enum DtoConversionError {
#[error("Cannot convert base58 string to bytes: {0}")]
Base58Error(
#[from]
#[source]
bs58::decode::Error,
),
#[error("Cannot convert string to PublicKey: {0}")]
PublicKeyDecodeError(
#[from]
#[source]
PKError,
),
#[error("Cannot convert string to PublicKey: {0}")]
SignatureDecodeError(
#[from]
#[source]
SignatureError,
),
}
#[fce]
pub struct Certificate {
pub chain: Vec<Trust>,
}
impl From<trust_graph::Certificate> for Certificate {
fn from(c: trust_graph::Certificate) -> Self {
let chain: Vec<Trust> = c.chain.into_iter().map(|t| t.into()).collect();
return Certificate { chain };
}
}
impl TryFrom<Certificate> for trust_graph::Certificate {
type Error = DtoConversionError;
fn try_from(c: Certificate) -> Result<Self, Self::Error> {
let chain: Result<Vec<trust_graph::Trust>, DtoConversionError> = c
.chain
.into_iter()
.map(|t| trust_graph::Trust::try_from(t))
.collect();
let chain = chain?;
return Ok(trust_graph::Certificate { chain });
}
}
#[fce]
pub struct Trust {
/// For whom this certificate is issued, base58
pub issued_for: String,
/// Expiration date of a trust, in secs
pub expires_at: u64,
/// Signature of a previous trust in a chain.
/// Signature is self-signed if it is a root trust, base58
pub signature: String,
/// Creation time of a trust, in secs
pub issued_at: u64,
}
impl TryFrom<Trust> for trust_graph::Trust {
type Error = DtoConversionError;
fn try_from(t: Trust) -> Result<Self, Self::Error> {
let issued_for = PublicKey::from_base58(&t.issued_for)?;
let signature = bs58::decode(&t.signature).into_vec()?;
let signature = Signature::from_bytes(&signature)?;
let expires_at = Duration::from_secs(t.expires_at);
let issued_at = Duration::from_secs(t.issued_at);
return Ok(trust_graph::Trust {
issued_for,
expires_at,
signature,
issued_at,
});
}
}
impl From<trust_graph::Trust> for Trust {
fn from(t: trust_graph::Trust) -> Self {
let issued_for = bs58::encode(t.issued_for.to_bytes()).into_string();
let signature = bs58::encode(t.signature.to_bytes()).into_string();
let expires_at = t.expires_at.as_secs();
let issued_at = t.issued_at.as_secs();
return Trust {
issued_for,
expires_at,
signature,
issued_at,
};
}
}

View File

@ -1,6 +1,9 @@
use fluence::WasmLoggerBuilder;
mod dto;
mod results;
mod service_api;
mod service_impl;
mod storage_impl;
pub fn main() {

93
wasm/src/results.rs Normal file
View File

@ -0,0 +1,93 @@
use crate::dto::Certificate;
use crate::service_impl::ServiceError;
use fluence::fce;
#[fce]
pub struct InsertResult {
pub ret_code: u32,
pub error: String,
}
impl From<Result<(), ServiceError>> for InsertResult {
fn from(result: Result<(), ServiceError>) -> Self {
match result {
Ok(()) => InsertResult {
ret_code: 0,
error: "".to_string(),
},
Err(e) => InsertResult {
ret_code: 1,
error: format!("{}", e),
},
}
}
}
#[fce]
pub struct WeightResult {
pub ret_code: u32,
pub weight: Vec<u32>,
pub error: String,
}
impl From<Result<Option<u32>, ServiceError>> for WeightResult {
fn from(result: Result<Option<u32>, ServiceError>) -> Self {
match result {
Ok(wo) => WeightResult {
ret_code: 0,
weight: wo.map(|w| vec![w]).unwrap_or(vec![]),
error: "".to_string(),
},
Err(e) => WeightResult {
ret_code: 1,
weight: vec![],
error: format!("{}", e),
},
}
}
}
#[fce]
pub struct AllCertsResult {
pub ret_code: u32,
pub certificates: Vec<Certificate>,
pub error: String,
}
impl From<Result<Vec<Certificate>, ServiceError>> for AllCertsResult {
fn from(result: Result<Vec<Certificate>, ServiceError>) -> Self {
match result {
Ok(certs) => AllCertsResult {
ret_code: 0,
certificates: certs,
error: "".to_string(),
},
Err(e) => AllCertsResult {
ret_code: 1,
certificates: vec![],
error: format!("{}", e),
},
}
}
}
#[fce]
pub struct AddRootResult {
pub ret_code: u32,
pub error: String,
}
impl From<Result<(), ServiceError>> for AddRootResult {
fn from(result: Result<(), ServiceError>) -> Self {
match result {
Ok(()) => AddRootResult {
ret_code: 0,
error: "".to_string(),
},
Err(e) => AddRootResult {
ret_code: 1,
error: format!("{}", e),
},
}
}
}

75
wasm/src/service_api.rs Normal file
View File

@ -0,0 +1,75 @@
use crate::dto::Certificate;
use crate::results::{AddRootResult, AllCertsResult, InsertResult, WeightResult};
use crate::service_impl::{
add_root_impl, get_all_certs_impl, get_weight_impl, insert_cert_impl, insert_cert_impl_raw,
};
use fluence::{fce, CallParameters};
#[fce]
/// 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()
}
#[fce]
/// 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()
}
#[fce]
fn get_weight(public_key: String) -> WeightResult {
get_weight_impl(public_key).into()
}
#[fce]
fn get_all_certs(issued_for: String) -> AllCertsResult {
get_all_certs_impl(issued_for).into()
}
#[fce]
/// could add only a host of a trust graph service
fn add_root(pk: String, weight: u32) -> AddRootResult {
let call_parameters: CallParameters = fluence::get_call_parameters();
let init_peer_id = call_parameters.init_peer_id.clone();
if call_parameters.host_id == init_peer_id {
add_root_impl(pk, weight).into()
} else {
return AddRootResult {
ret_code: 1,
error: "Root could add only a host of trust graph service".to_string(),
};
}
}
// TODO rewrite test after #[fce_test] will be implemented
// #[fce]
// fn test() -> String {
// let mut tg = get_data().lock();
//
// let root_kp = KeyPair::generate();
// let root_kp2 = KeyPair::generate();
// let second_kp = KeyPair::generate();
//
// let expires_at = Duration::new(15, 15);
// let issued_at = Duration::new(5, 5);
//
// let cert = trust_graph::Certificate::issue_root(
// &root_kp,
// second_kp.public_key(),
// expires_at,
// issued_at,
// );
// tg.add_root_weight(root_kp.public().into(), 0).unwrap();
// tg.add_root_weight(root_kp2.public().into(), 1).unwrap();
// tg.add(cert, Duration::new(10, 10)).unwrap();
//
// let a = tg.get(second_kp.public_key()).unwrap();
// let str = format!("{:?}", a);
// log::info!("{}", &str);
//
// str
// }

86
wasm/src/service_impl.rs Normal file
View File

@ -0,0 +1,86 @@
use crate::dto::{Certificate, DtoConversionError};
use crate::storage_impl::get_data;
use fluence_identity::public_key::PKError;
use fluence_identity::PublicKey;
use std::convert::{Into, TryInto};
use std::str::FromStr;
use std::time::Duration;
use thiserror::Error as ThisError;
use trust_graph::{CertificateError, TrustGraphError};
#[derive(ThisError, Debug)]
pub enum ServiceError {
#[error("{0}")]
PublicKeyDecodeError(
#[from]
#[source]
PKError,
),
#[error("{0}")]
TGError(
#[from]
#[source]
TrustGraphError,
),
#[error("{0}")]
CertError(
#[from]
#[source]
CertificateError,
),
#[error("{0}")]
DtoError(
#[from]
#[source]
DtoConversionError,
),
}
pub fn get_weight_impl(public_key: String) -> Result<Option<u32>, ServiceError> {
let tg = get_data().lock();
let public_key = string_to_public_key(public_key)?;
let weight = tg.weight(public_key)?;
Ok(weight)
}
fn add_cert(certificate: trust_graph::Certificate, duration: u64) -> Result<(), ServiceError> {
let duration = Duration::from_millis(duration);
let mut tg = get_data().lock();
tg.add(certificate, duration)?;
Ok(())
}
pub fn insert_cert_impl_raw(certificate: String, duration: u64) -> Result<(), ServiceError> {
let certificate = trust_graph::Certificate::from_str(&certificate)?;
add_cert(certificate, duration)?;
Ok(())
}
fn string_to_public_key(public_key: String) -> Result<PublicKey, ServiceError> {
let public_key = PublicKey::from_base58(&public_key)?;
Ok(public_key)
}
pub fn get_all_certs_impl(issued_for: String) -> Result<Vec<Certificate>, ServiceError> {
let tg = get_data().lock();
let public_key = string_to_public_key(issued_for)?;
let certs = tg.get_all_certs(public_key, &[])?;
Ok(certs.into_iter().map(|c| c.into()).collect())
}
pub fn insert_cert_impl(certificate: Certificate, duration: u64) -> Result<(), ServiceError> {
let certificate: trust_graph::Certificate = certificate.try_into()?;
add_cert(certificate, duration)?;
Ok(())
}
pub fn add_root_impl(pk: String, weight: u32) -> Result<(), ServiceError> {
let mut tg = get_data().lock();
let pk = PublicKey::from_base58(&pk)?.into();
tg.add_root_weight(pk, weight)?;
Ok(())
}

229
wasm/src/storage_impl.rs Normal file
View File

@ -0,0 +1,229 @@
// store list of trusts
// check if trust is already in list before adding
// if there is an older trust - don't add received trust
use crate::storage_impl::SQLiteStorageError::{
PublcKeyNotFound, PublicKeyConversion, PublicKeyFromStr, TrustNodeConversion,
WeightConversionDB,
};
use core::convert::TryFrom;
use fce_sqlite_connector;
use fce_sqlite_connector::Connection;
use fce_sqlite_connector::Error as InternalSqliteError;
use fce_sqlite_connector::Value;
use fluence_identity::public_key::PublicKey;
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use rmp_serde::decode::Error as RmpDecodeError;
use rmp_serde::encode::Error as RmpEncodeError;
use std::convert::From;
use std::str::FromStr;
use std::time::Duration;
use thiserror::Error as ThisError;
use trust_graph::{
Auth, PublicKeyHashable as PK, Revoke, Storage, StorageError, TrustGraph, TrustNode, Weight,
};
static INSTANCE: OnceCell<Mutex<TrustGraph<SQLiteStorage>>> = OnceCell::new();
pub fn get_data() -> &'static Mutex<TrustGraph<SQLiteStorage>> {
INSTANCE.get_or_init(|| {
let db_path = "/tmp/users123123.sqlite";
let connection = fce_sqlite_connector::open(db_path).unwrap();
let init_sql = "CREATE TABLE IF NOT EXISTS trustnodes(
public_key TEXT PRIMARY KEY,
trustnode BLOB NOT NULL
);
CREATE TABLE IF NOT EXISTS roots(
public_key TEXT,
weight INTEGER
);";
connection.execute(init_sql).expect("cannot connect to db");
Mutex::new(TrustGraph::new(Box::new(SQLiteStorage::new(connection))))
})
}
pub struct SQLiteStorage {
connection: Connection,
}
impl SQLiteStorage {
pub fn new(connection: Connection) -> SQLiteStorage {
SQLiteStorage { connection }
}
}
#[derive(ThisError, Debug)]
pub enum SQLiteStorageError {
#[error("{0}")]
SQLiteError(
#[from]
#[source]
InternalSqliteError,
),
#[error("{0}")]
PublicKeyFromStr(String),
#[error("{0}")]
EncodeError(
#[from]
#[source]
RmpEncodeError,
),
#[error("{0}")]
DecodeError(
#[from]
#[source]
RmpDecodeError,
),
#[error("Cannot convert weight as integer from DB")]
WeightConversionDB,
#[error("Cannot convert public key as binary from DB")]
PublicKeyConversion,
#[error("Cannot convert trust node as binary from DB")]
TrustNodeConversion,
#[error("Cannot revoke. There is no trust with such PublicKey")]
PublcKeyNotFound,
}
impl From<SQLiteStorageError> for String {
fn from(err: SQLiteStorageError) -> Self {
err.into()
}
}
impl StorageError for SQLiteStorageError {}
impl Storage for SQLiteStorage {
type Error = SQLiteStorageError;
fn get(&self, pk: &PK) -> Result<Option<TrustNode>, Self::Error> {
let mut cursor = self
.connection
.prepare("SELECT trustnode FROM trustnodes WHERE public_key = ?")?
.cursor();
cursor.bind(&[Value::String(format!("{}", pk))])?;
match cursor.next().unwrap() {
Some(r) => {
log::info!("row: {:?}", r);
let tn_bin: &[u8] = r[0].as_binary().ok_or(TrustNodeConversion)?;
log::info!("binary: {:?}", tn_bin);
let trust_node: TrustNode = rmp_serde::from_read_ref(tn_bin)?;
log::info!("trustnode: {:?}", trust_node);
Ok(Some(trust_node))
}
None => Ok(None),
}
}
fn insert(&mut self, pk: PK, node: TrustNode) -> Result<(), Self::Error> {
let mut cursor = self
.connection
.prepare("INSERT OR REPLACE INTO trustnodes VALUES (?, ?)")?
.cursor();
let tn_vec = rmp_serde::to_vec(&node)?;
log::info!("insert: {:?}", tn_vec);
cursor.bind(&[Value::String(format!("{}", pk)), Value::Binary(tn_vec)])?;
cursor.next()?;
Ok({})
}
fn get_root_weight(&self, pk: &PK) -> Result<Option<Weight>, Self::Error> {
let mut cursor = self
.connection
.prepare("SELECT public_key,weight FROM roots WHERE public_key = ?")?
.cursor();
cursor.bind(&[Value::String(format!("{}", pk))])?;
if let Some(row) = cursor.next()? {
log::info!("row: {:?}", row);
let w = u32::try_from(row[1].as_integer().ok_or(WeightConversionDB)?)
.map_err(|_e| WeightConversionDB)?;
Ok(Some(w))
} else {
Ok(None)
}
}
fn add_root_weight(&mut self, pk: PK, weight: Weight) -> Result<(), Self::Error> {
log::info!("add root: {} weight: {}", pk, weight);
let mut cursor = self
.connection
.prepare("INSERT OR REPLACE INTO roots VALUES (?, ?)")?
.cursor();
cursor.bind(&[
Value::String(format!("{}", pk)),
Value::Integer(i64::from(weight)),
])?;
cursor.next()?;
Ok({})
}
fn root_keys(&self) -> Result<Vec<PK>, Self::Error> {
let mut cursor = self
.connection
.prepare("SELECT public_key,weight FROM roots")?
.cursor();
let mut roots = vec![];
while let Some(row) = cursor.next()? {
log::info!("row: {:?}", row);
let pk = row[0].as_string().ok_or(PublicKeyConversion)?;
let pk: PK = PK::from_str(pk).map_err(|e| PublicKeyFromStr(e.to_string()))?;
roots.push(pk)
}
Ok(roots)
}
fn revoke(&mut self, pk: &PK, revoke: Revoke) -> Result<(), Self::Error> {
match self.get(&pk)? {
Some(mut trust_node) => {
trust_node.update_revoke(revoke);
self.insert(pk.clone(), trust_node)?;
Ok(())
}
None => Err(PublcKeyNotFound),
}
}
fn update_auth(
&mut self,
pk: &PK,
auth: Auth,
issued_for: &PublicKey,
cur_time: Duration,
) -> Result<(), Self::Error> {
match self.get(&pk)? {
Some(mut trust_node) => {
trust_node.update_auth(auth);
self.insert(pk.clone(), trust_node)
}
None => {
let mut trust_node = TrustNode::new(issued_for.clone(), cur_time);
trust_node.update_auth(auth);
self.insert(pk.clone(), trust_node)
}
}
}
}