This commit is contained in:
DieMyst 2021-01-15 19:37:06 +03:00
commit f2afc13ea2
12 changed files with 3637 additions and 0 deletions

1533
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

21
Cargo.toml Normal file
View File

@ -0,0 +1,21 @@
[package]
name = "trust-graph"
version = "0.2.0"
authors = ["Fluence Labs"]
edition = "2018"
description = "trust graph"
license = "Apache-2.0"
[dependencies]
libp2p-core = { package = "fluence-fork-libp2p-core", version = "0.26.0" }
serde = { version = "=1.0.118", features = ["derive"] }
serde_json = "1.0.58"
bs58 = "0.3.1"
failure = "0.1.6"
log = "0.4.11"
ref-cast = "1.0.2"
derivative = "2.1.1"
ed25519-dalek = "1.0.1"
[dev-dependencies]
rand = "0.7.3"

532
src/certificate.rs Normal file
View File

@ -0,0 +1,532 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::ed25519::PublicKey;
use crate::key_pair::KeyPair;
use crate::trust::{Trust, TRUST_LEN};
use std::str::FromStr;
use std::time::Duration;
/// Serialization format of a certificate.
/// TODO
const FORMAT: &[u8; 2] = &[0, 0];
/// Serialization format version of a certificate.
/// TODO
const VERSION: &[u8; 4] = &[0, 0, 0, 0];
/// Chain of trusts started from self-signed root trust.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Certificate {
pub chain: Vec<Trust>,
}
impl Certificate {
pub fn new_unverified(chain: Vec<Trust>) -> Self {
Self { chain }
}
/// Creates new certificate with root trust (self-signed public key) from a key pair.
#[allow(dead_code)]
pub fn issue_root(
root_kp: &KeyPair,
for_pk: PublicKey,
expires_at: Duration,
issued_at: Duration,
) -> Self {
let root_expiration = Duration::from_secs(u64::max_value());
let root_trust = Trust::create(root_kp, root_kp.public_key(), root_expiration, issued_at);
let trust = Trust::create(root_kp, for_pk, expires_at, issued_at);
let chain = vec![root_trust, trust];
Self { chain }
}
/// Adds a new trust into chain of trust in certificate.
#[allow(dead_code)]
pub fn issue(
issued_by: &KeyPair,
for_pk: PublicKey,
extend_cert: &Certificate,
expires_at: Duration,
issued_at: Duration,
cur_time: Duration,
) -> Result<Self, String> {
if expires_at.lt(&issued_at) {
return Err("Expiration time should be greater than issued time.".to_string());
}
// first, verify given certificate
Certificate::verify(
extend_cert,
&[extend_cert.chain[0].issued_for.clone()],
cur_time,
)?;
let issued_by_pk = issued_by.public_key();
// check if `issued_by_pk` is allowed to issue a certificate (i.e., theres a trust for it in a chain)
let mut previous_trust_num: i32 = -1;
for pk_id in 0..extend_cert.chain.len() {
if extend_cert.chain[pk_id].issued_for == issued_by_pk {
previous_trust_num = pk_id as i32;
}
}
if previous_trust_num == -1 {
return Err("Your public key should be in certificate.".to_string());
};
// splitting old chain to add new trust after given public key
let mut new_chain = extend_cert
.chain
.split_at((previous_trust_num + 1) as usize)
.0
.to_vec();
let trust = Trust::create(issued_by, for_pk, expires_at, issued_at);
new_chain.push(trust);
Ok(Self { chain: new_chain })
}
/// Verifies that a certificate is valid and you trust to this certificate.
pub fn verify(
cert: &Certificate,
trusted_roots: &[PublicKey],
cur_time: Duration,
) -> Result<(), String> {
let chain = &cert.chain;
if chain.is_empty() {
return Err("The certificate must have at least 1 trust".to_string());
}
// 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))?;
if !trusted_roots.contains(&root.issued_for) {
return Err("Certificate does not contain a trusted root.".to_string());
}
// check if every element in a chain is not expired and has the correct signature
for trust_id in (1..chain.len()).rev() {
let trust = &chain[trust_id];
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
)
})?;
}
Ok(())
}
/// Convert certificate to byte format
/// 2 format + 4 version + (64 signature + 32 public key + 8 expiration) * number of trusts
#[allow(dead_code)]
pub fn encode(&self) -> Vec<u8> {
let mut encoded =
Vec::with_capacity(FORMAT.len() + VERSION.len() + TRUST_LEN * self.chain.len());
encoded.extend_from_slice(FORMAT);
encoded.extend_from_slice(VERSION);
for t in &self.chain {
encoded.extend(t.encode());
}
encoded
}
#[allow(dead_code)]
pub fn decode(arr: &[u8]) -> Result<Self, String> {
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());
}
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());
}
// TODO do match different formats and versions
let _format = &arr[0..1];
let _version = &arr[2..5];
let mut chain = Vec::with_capacity(number_of_trusts);
for i in 0..number_of_trusts {
let from = i * TRUST_LEN + 6;
let to = (i + 1) * TRUST_LEN + 6;
let slice = &arr[from..to];
let t = Trust::decode(slice)?;
chain.push(t);
}
Ok(Self { chain })
}
}
impl std::fmt::Display for Certificate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", bs58::encode(FORMAT).into_string())?;
writeln!(f, "{}", bs58::encode(VERSION).into_string())?;
for trust in self.chain.iter() {
writeln!(f, "{}", trust.to_string())?;
}
Ok(())
}
}
impl FromStr for Certificate {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let str_lines: Vec<&str> = s.lines().collect();
// TODO for future purposes
let _format = str_lines[0];
let _version = str_lines[1];
if (str_lines.len() - 2) % 4 != 0 {
return Err(format!("Incorrect format of the certificate: {}", s));
}
let num_of_trusts = (str_lines.len() - 2) / 4;
let mut trusts = Vec::with_capacity(num_of_trusts);
for i in (2..str_lines.len()).step_by(4) {
let trust = Trust::convert_from_strings(
str_lines[i],
str_lines[i + 1],
str_lines[i + 2],
str_lines[i + 3],
)?;
trusts.push(trust);
}
Ok(Self::new_unverified(trusts))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::misc::current_time;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
pub fn one_second() -> Duration {
Duration::from_secs(1)
}
pub fn one_minute() -> Duration {
Duration::from_secs(60)
}
pub fn one_year() -> Duration {
Duration::from_secs(31_557_600)
}
#[test]
pub fn test_string_encoding_decoding() {
let (_root_kp, second_kp, cert) = generate_root_cert();
let cur_time = current_time();
let third_kp = KeyPair::generate();
let new_cert = Certificate::issue(
&second_kp,
third_kp.key_pair.public(),
&cert,
cur_time.checked_add(one_second()).unwrap(),
cur_time,
cur_time,
)
.unwrap();
let serialized = new_cert.to_string();
let deserialized = Certificate::from_str(&serialized);
assert!(deserialized.is_ok());
let after_cert = deserialized.unwrap();
assert_eq!(&new_cert.chain[0], &after_cert.chain[0]);
assert_eq!(&new_cert, &after_cert);
}
#[test]
pub fn test_serialization_deserialization() {
let (_root_kp, second_kp, cert) = generate_root_cert();
let cur_time = current_time();
let third_kp = KeyPair::generate();
let new_cert = Certificate::issue(
&second_kp,
third_kp.key_pair.public(),
&cert,
cur_time.checked_add(one_second()).unwrap(),
cur_time,
cur_time,
)
.unwrap();
let serialized = new_cert.encode();
let deserialized = Certificate::decode(serialized.as_slice());
assert!(deserialized.is_ok());
let after_cert = deserialized.unwrap();
assert_eq!(&new_cert.chain[0], &after_cert.chain[0]);
assert_eq!(&new_cert, &after_cert);
}
#[test]
fn test_small_chain() {
let bad_cert = Certificate { chain: Vec::new() };
let check = Certificate::verify(&bad_cert, &[], current_time());
assert!(check.is_err());
}
fn generate_root_cert() -> (KeyPair, KeyPair, Certificate) {
let root_kp = KeyPair::generate();
let second_kp = KeyPair::generate();
let cur_time = current_time();
(
root_kp.clone(),
second_kp.clone(),
Certificate::issue_root(
&root_kp,
second_kp.public_key(),
cur_time.checked_add(one_year()).unwrap(),
cur_time,
),
)
}
#[test]
fn test_issue_cert() {
let (root_kp, second_kp, cert) = generate_root_cert();
let trusted_roots = [root_kp.public_key()];
// we don't need nanos for serialization, etc
let cur_time = Duration::from_secs(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() as u64,
);
let third_kp = KeyPair::generate();
let new_cert = Certificate::issue(
&second_kp,
third_kp.key_pair.public(),
&cert,
cur_time.checked_add(one_year()).unwrap(),
cur_time,
cur_time,
);
assert_eq!(new_cert.is_ok(), true);
let new_cert = new_cert.unwrap();
println!(
"root_kp:\n\tprivate: {}\n\tpublic: {}",
bs58::encode(root_kp.key_pair.secret()).into_string(),
bs58::encode(&root_kp.key_pair.public().encode().to_vec()).into_string()
);
println!(
"second_kp:\n\tprivate: {}\n\tpublic: {}",
bs58::encode(second_kp.key_pair.secret()).into_string(),
bs58::encode(&second_kp.key_pair.public().encode().to_vec()).into_string()
);
println!(
"third_kp:\n\tprivate: {}\n\tpublic: {}",
bs58::encode(third_kp.key_pair.secret()).into_string(),
bs58::encode(&third_kp.key_pair.public().encode().to_vec()).into_string()
);
println!("cert is\n{}", new_cert.to_string());
assert_eq!(new_cert.chain.len(), 3);
assert_eq!(new_cert.chain[0].issued_for, root_kp.public_key());
assert_eq!(new_cert.chain[1].issued_for, second_kp.public_key());
assert_eq!(new_cert.chain[2].issued_for, third_kp.public_key());
assert!(Certificate::verify(&new_cert, &trusted_roots, cur_time).is_ok());
}
#[test]
fn test_cert_expiration() {
let (root_kp, second_kp, cert) = generate_root_cert();
let trusted_roots = [root_kp.public_key()];
let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let third_kp = KeyPair::generate();
let new_cert = Certificate::issue(
&second_kp,
third_kp.key_pair.public(),
&cert,
cur_time.checked_sub(one_second()).unwrap(),
cur_time.checked_sub(one_minute()).unwrap(),
cur_time,
)
.unwrap();
assert!(Certificate::verify(&new_cert, &trusted_roots, cur_time).is_err());
}
#[test]
fn test_issue_in_chain_tail() {
let (root_kp, second_kp, cert) = generate_root_cert();
let trusted_roots = [root_kp.public_key()];
let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let third_kp = KeyPair::generate();
let fourth_kp = KeyPair::generate();
let new_cert = Certificate::issue(
&second_kp,
third_kp.key_pair.public(),
&cert,
cur_time.checked_add(one_second()).unwrap(),
cur_time,
cur_time,
)
.unwrap();
let new_cert = Certificate::issue(
&third_kp,
fourth_kp.key_pair.public(),
&new_cert,
cur_time.checked_add(one_second()).unwrap(),
cur_time,
cur_time,
);
assert_eq!(new_cert.is_ok(), true);
let new_cert = new_cert.unwrap();
assert_eq!(new_cert.chain.len(), 4);
assert_eq!(new_cert.chain[0].issued_for, root_kp.public_key());
assert_eq!(new_cert.chain[1].issued_for, second_kp.public_key());
assert_eq!(new_cert.chain[2].issued_for, third_kp.public_key());
assert_eq!(new_cert.chain[3].issued_for, fourth_kp.public_key());
assert!(Certificate::verify(&new_cert, &trusted_roots, cur_time).is_ok());
}
#[test]
fn test_issue_in_chain_body() {
let (root_kp, second_kp, cert) = generate_root_cert();
let trusted_roots = [root_kp.public_key()];
let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let third_kp = KeyPair::generate();
let fourth_kp = KeyPair::generate();
let new_cert = Certificate::issue(
&second_kp,
third_kp.key_pair.public(),
&cert,
cur_time.checked_add(one_second()).unwrap(),
cur_time,
cur_time,
)
.unwrap();
let new_cert = Certificate::issue(
&second_kp,
fourth_kp.key_pair.public(),
&new_cert,
cur_time.checked_add(one_second()).unwrap(),
cur_time,
cur_time,
);
assert_eq!(new_cert.is_ok(), true);
let new_cert = new_cert.unwrap();
assert_eq!(new_cert.chain.len(), 3);
assert_eq!(new_cert.chain[0].issued_for, root_kp.public_key());
assert_eq!(new_cert.chain[1].issued_for, second_kp.public_key());
assert_eq!(new_cert.chain[2].issued_for, fourth_kp.public_key());
assert!(Certificate::verify(&new_cert, &trusted_roots, cur_time).is_ok());
}
#[test]
fn test_no_cert_in_chain() {
let (_root_kp, _second_kp, cert) = generate_root_cert();
let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let bad_kp = KeyPair::generate();
let new_cert_bad = Certificate::issue(
&bad_kp,
bad_kp.key_pair.public(),
&cert,
cur_time.checked_add(one_second()).unwrap(),
cur_time,
cur_time,
);
assert_eq!(new_cert_bad.is_err(), true);
}
#[test]
fn test_no_trusted_root_in_chain() {
let (_root_kp, second_kp, cert) = generate_root_cert();
let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let trusted_roots = [second_kp.public_key()];
assert!(Certificate::verify(&cert, &trusted_roots, cur_time).is_err());
assert!(Certificate::verify(&cert, &[], cur_time).is_err());
}
#[test]
fn test_forged_cert() {
let (root_kp, _second_kp, cert) = generate_root_cert();
let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let trusted_roots = [root_kp.public_key()];
// forged cert
let mut bad_chain = cert.chain;
bad_chain.remove(0);
let bad_cert = Certificate { chain: bad_chain };
assert!(Certificate::verify(&bad_cert, &trusted_roots, cur_time).is_err());
}
#[test]
fn test_generate_root_cert() {
let (root_kp, second_kp, cert) = generate_root_cert();
let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let trusted_roots = [root_kp.public_key()];
assert_eq!(cert.chain.len(), 2);
assert_eq!(cert.chain[0].issued_for, root_kp.public_key());
assert_eq!(cert.chain[1].issued_for, second_kp.public_key());
assert!(Certificate::verify(&cert, &trusted_roots, cur_time).is_ok());
}
}

71
src/certificate_serde.rs Normal file
View File

@ -0,0 +1,71 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::Certificate;
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::str::FromStr;
mod single {
#![allow(dead_code)]
use super::*;
pub fn serialize<S>(value: &Certificate, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
value.to_string().serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Certificate, D::Error>
where
D: Deserializer<'de>,
{
let str = String::deserialize(deserializer)?;
Certificate::from_str(&str)
.map_err(|e| Error::custom(format!("certificate deserialization failed for {:?}", e)))
}
}
pub mod vec {
use super::*;
use serde::ser::SerializeSeq;
pub fn serialize<S>(value: &[Certificate], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(value.len()))?;
for e in value {
let e = e.to_string();
seq.serialize_element(&e)?;
}
seq.end()
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Certificate>, D::Error>
where
D: Deserializer<'de>,
{
let v: Vec<String> = Vec::deserialize(deserializer)?;
v.into_iter()
.map(|e| {
Certificate::from_str(&e).map_err(|e| {
Error::custom(format!("certificate deserialization failed for {:?}", e))
})
})
.collect()
}
}

126
src/key_pair.rs Normal file
View File

@ -0,0 +1,126 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::ed25519::{Keypair as Libp2pKeyPair, PublicKey, SecretKey};
use ed25519_dalek::SignatureError;
use libp2p_core::identity::error::DecodingError;
use std::fmt;
pub type Signature = Vec<u8>;
/// An Ed25519 keypair.
#[derive(Clone, Debug)]
pub struct KeyPair {
pub key_pair: Libp2pKeyPair,
}
impl KeyPair {
/// Generate a new Ed25519 keypair.
#[allow(dead_code)]
pub fn generate() -> Self {
let kp = Libp2pKeyPair::generate();
kp.into()
}
pub fn from_bytes(sk_bytes: impl AsMut<[u8]>) -> Result<Self, DecodingError> {
let sk = SecretKey::from_bytes(sk_bytes)?;
Ok(Libp2pKeyPair::from(sk).into())
}
/// Encode the keypair into a byte array by concatenating the bytes
/// of the secret scalar and the compressed public point/
#[allow(dead_code)]
pub fn encode(&self) -> [u8; 64] {
self.key_pair.encode()
}
/// Decode a keypair from the format produced by `encode`.
#[allow(dead_code)]
pub fn decode(kp: &[u8]) -> Result<KeyPair, SignatureError> {
let kp = ed25519_dalek::Keypair::from_bytes(kp)?;
Ok(Self {
key_pair: kp.into(),
})
}
/// Get the public key of this keypair.
#[allow(dead_code)]
pub fn public_key(&self) -> PublicKey {
self.key_pair.public()
}
/// Sign a message using the private key of this keypair.
pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
self.key_pair.sign(msg)
}
/// Verify the Ed25519 signature on a message using the public key.
pub fn verify(pk: &PublicKey, msg: &[u8], signature: &[u8]) -> Result<(), String> {
if pk.verify(msg, signature) {
return Ok(());
}
Err("Signature is not valid.".to_string())
}
}
impl From<Libp2pKeyPair> for KeyPair {
fn from(kp: Libp2pKeyPair) -> Self {
Self { key_pair: kp }
}
}
/// Implement serde::Deserialize for KeyPair
impl<'de> serde::Deserialize<'de> for KeyPair {
fn deserialize<D>(deserializer: D) -> Result<KeyPair, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{Error, Unexpected, Visitor};
struct KeyPairVisitor;
impl<'de> Visitor<'de> for KeyPairVisitor {
type Value = KeyPair;
/// Error message stating what was expected
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("byte array or base58 string")
}
/// Implement deserialization from base58 string
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: Error,
{
bs58::decode(s)
.into_vec()
.map_err(|_| Error::invalid_value(Unexpected::Str(s), &self))
.and_then(|v| self.visit_bytes(v.as_slice()))
}
/// Implement deserialization from bytes
fn visit_bytes<E>(self, b: &[u8]) -> Result<Self::Value, E>
where
E: Error,
{
KeyPair::decode(b).map_err(|_| Error::invalid_value(Unexpected::Bytes(b), &self))
}
}
deserializer.deserialize_str(KeyPairVisitor)
}
}

46
src/lib.rs Normal file
View File

@ -0,0 +1,46 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#![recursion_limit = "512"]
#![warn(rust_2018_idioms)]
#![deny(
dead_code,
nonstandard_style,
unused_imports,
unused_mut,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
mod certificate;
pub mod certificate_serde;
mod key_pair;
mod misc;
mod public_key_hashable;
mod revoke;
mod trust;
mod trust_graph;
mod trust_node;
pub(crate) use libp2p_core::identity::ed25519;
pub use crate::certificate::Certificate;
pub use crate::key_pair::KeyPair;
pub use crate::misc::current_time;
pub use crate::public_key_hashable::PublicKeyHashable;
pub use crate::trust::Trust;
pub use crate::trust_graph::TrustGraph;

10
src/misc/mod.rs Normal file
View File

@ -0,0 +1,10 @@
use std::time::{Duration, SystemTime, UNIX_EPOCH};
pub fn current_time() -> Duration {
Duration::from_secs(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() as u64,
)
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::ed25519::PublicKey;
use ref_cast::RefCast;
use serde::Deserialize;
use std::{
fmt::{Display, Formatter},
hash::{Hash, Hasher},
};
/// Wrapper to use PublicKey in HashMap
#[derive(PartialEq, Eq, Debug, Clone, RefCast, Deserialize)]
#[repr(transparent)]
pub struct PublicKeyHashable(PublicKey);
#[allow(clippy::derive_hash_xor_eq)]
impl Hash for PublicKeyHashable {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write(&self.0.encode());
state.finish();
}
fn hash_slice<H: Hasher>(data: &[Self], state: &mut H)
where
Self: Sized,
{
// TODO check for overflow
let mut bytes: Vec<u8> = Vec::with_capacity(data.len() * 32);
for d in data {
bytes.extend_from_slice(&d.0.encode())
}
state.write(bytes.as_slice());
state.finish();
}
}
impl From<PublicKey> for PublicKeyHashable {
fn from(pk: PublicKey) -> Self {
Self(pk)
}
}
impl Into<PublicKey> for PublicKeyHashable {
fn into(self) -> PublicKey {
self.0
}
}
impl AsRef<PublicKey> for PublicKeyHashable {
fn as_ref(&self) -> &PublicKey {
&self.0
}
}
impl AsRef<PublicKeyHashable> for PublicKey {
fn as_ref(&self) -> &PublicKeyHashable {
PublicKeyHashable::ref_cast(self)
}
}
impl Display for PublicKeyHashable {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", bs58::encode(self.0.encode()).into_string())
}
}

121
src/revoke.rs Normal file
View File

@ -0,0 +1,121 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::ed25519::PublicKey;
use crate::key_pair::KeyPair;
use crate::key_pair::Signature;
use crate::trust::{EXPIRATION_LEN, PK_LEN};
use std::time::Duration;
/// "A document" that cancels trust created before.
/// TODO delete pk from Revoke (it is already in a trust node)
#[derive(Debug, Clone)]
pub struct Revoke {
/// who is revoked
pub pk: PublicKey,
/// date when revocation was created
pub revoked_at: Duration,
/// the issuer of this revocation
pub revoked_by: PublicKey,
/// proof of this revocation
signature: Signature,
}
impl Revoke {
#[allow(dead_code)]
fn new(
pk: PublicKey,
revoked_by: PublicKey,
revoked_at: Duration,
signature: Signature,
) -> Self {
Self {
pk,
revoked_at,
revoked_by,
signature,
}
}
/// Creates new revocation signed by a revoker.
#[allow(dead_code)]
pub fn create(revoker: &KeyPair, to_revoke: PublicKey, revoked_at: Duration) -> Self {
let msg = Revoke::signature_bytes(&to_revoke, revoked_at);
let signature = revoker.sign(&msg);
Revoke::new(to_revoke, revoker.public_key(), revoked_at, signature)
}
fn signature_bytes(pk: &PublicKey, revoked_at: Duration) -> Vec<u8> {
let mut msg = Vec::with_capacity(PK_LEN + EXPIRATION_LEN);
msg.extend_from_slice(&pk.encode());
msg.extend_from_slice(&(revoked_at.as_secs() as u64).to_le_bytes());
msg
}
/// Verifies that revocation is cryptographically correct.
pub fn verify(revoke: &Revoke) -> Result<(), String> {
let msg = Revoke::signature_bytes(&revoke.pk, revoke.revoked_at);
if !revoke
.revoked_by
.verify(msg.as_slice(), revoke.signature.as_slice())
{
return Err("Revoke has incorrect signature.".to_string());
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gen_revoke_and_validate() {
let revoker = KeyPair::generate();
let to_revoke = KeyPair::generate();
let duration = Duration::new(100, 0);
let revoke = Revoke::create(&revoker, to_revoke.public_key(), duration);
assert_eq!(Revoke::verify(&revoke).is_ok(), true);
}
#[test]
fn test_validate_corrupted_revoke() {
let revoker = KeyPair::generate();
let to_revoke = KeyPair::generate();
let duration = Duration::new(100, 0);
let revoke = Revoke::create(&revoker, to_revoke.public_key(), duration);
let duration2 = Duration::new(95, 0);
let corrupted_revoke = Revoke::new(
to_revoke.public_key(),
revoker.public_key(),
duration2,
revoke.signature,
);
assert_eq!(Revoke::verify(&corrupted_revoke).is_ok(), false);
}
}

277
src/trust.rs Normal file
View File

@ -0,0 +1,277 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::ed25519::PublicKey;
use crate::key_pair::{KeyPair, Signature};
use derivative::Derivative;
use std::convert::TryInto;
use std::time::Duration;
pub const SIG_LEN: usize = 64;
pub const PK_LEN: usize = 32;
pub const EXPIRATION_LEN: usize = 8;
pub const ISSUED_LEN: usize = 8;
pub const METADATA_LEN: usize = PK_LEN + EXPIRATION_LEN + ISSUED_LEN;
pub const TRUST_LEN: usize = SIG_LEN + PK_LEN + EXPIRATION_LEN + ISSUED_LEN;
/// One element in chain of trust in a certificate.
/// TODO delete pk from Trust (it is already in a trust node)
#[derive(Clone, PartialEq, Derivative, Eq)]
#[derivative(Debug)]
pub struct Trust {
/// For whom this certificate is issued
#[derivative(Debug(format_with = "show_pubkey"))]
pub issued_for: PublicKey,
/// Expiration date of a trust.
pub expires_at: Duration,
/// Signature of a previous trust in a chain.
/// Signature is self-signed if it is a root trust.
#[derivative(Debug(format_with = "show_sig"))]
pub signature: Signature,
/// Creation time of a trust
pub issued_at: Duration,
}
fn show_pubkey(key: &PublicKey, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "{}", bs58::encode(key.encode()).into_string())
}
fn show_sig(sig: &[u8], f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "{}", bs58::encode(sig).into_string())
}
impl Trust {
#[allow(dead_code)]
pub fn new(
issued_for: PublicKey,
expires_at: Duration,
issued_at: Duration,
signature: Signature,
) -> Self {
Self {
issued_for,
expires_at,
issued_at,
signature,
}
}
pub fn create(
issued_by: &KeyPair,
issued_for: PublicKey,
expires_at: Duration,
issued_at: Duration,
) -> Self {
let msg = Self::metadata_bytes(&issued_for, expires_at, issued_at);
let signature = issued_by.sign(&msg);
Self {
issued_for,
expires_at,
signature,
issued_at,
}
}
/// Verifies that authorization is cryptographically correct.
pub fn verify(trust: &Trust, issued_by: &PublicKey, cur_time: Duration) -> Result<(), String> {
if trust.expires_at < cur_time {
return Err("Trust in chain is expired.".to_string());
}
let msg: &[u8] =
&Self::metadata_bytes(&trust.issued_for, trust.expires_at, trust.issued_at);
KeyPair::verify(issued_by, msg, trust.signature.as_slice())?;
Ok(())
}
fn metadata_bytes(pk: &PublicKey, expires_at: Duration, issued_at: Duration) -> [u8; 48] {
let pk_encoded = pk.encode();
let expires_at_encoded: [u8; EXPIRATION_LEN] = (expires_at.as_secs() as u64).to_le_bytes();
let issued_at_encoded: [u8; ISSUED_LEN] = (issued_at.as_secs() as u64).to_le_bytes();
let mut metadata = [0; METADATA_LEN];
metadata[..PK_LEN].clone_from_slice(&pk_encoded[..PK_LEN]);
metadata[PK_LEN..PK_LEN + EXPIRATION_LEN]
.clone_from_slice(&expires_at_encoded[0..EXPIRATION_LEN]);
metadata[PK_LEN + EXPIRATION_LEN..METADATA_LEN]
.clone_from_slice(&issued_at_encoded[0..ISSUED_LEN]);
metadata
}
/// Encode the trust into a byte array
#[allow(dead_code)]
pub fn encode(&self) -> Vec<u8> {
let mut vec = Vec::with_capacity(TRUST_LEN);
vec.extend_from_slice(&self.issued_for.encode());
vec.extend_from_slice(&self.signature.as_slice());
vec.extend_from_slice(&(self.expires_at.as_secs() as u64).to_le_bytes());
vec.extend_from_slice(&(self.issued_at.as_secs() as u64).to_le_bytes());
vec
}
/// Decode a trust from a byte array as produced by `encode`.
#[allow(dead_code)]
pub fn decode(arr: &[u8]) -> Result<Self, String> {
if arr.len() != TRUST_LEN {
return Err(
format!("Trust length should be 104: public key(32) + signature(64) + expiration date(8), was: {}", arr.len()),
);
}
let pk = PublicKey::decode(&arr[0..PK_LEN]).map_err(|err| err.to_string())?;
let signature = &arr[PK_LEN..PK_LEN + SIG_LEN];
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());
let expiration_date = Duration::from_secs(expiration_date);
let issued_bytes = &arr[PK_LEN + SIG_LEN + EXPIRATION_LEN..TRUST_LEN];
let issued_date = u64::from_le_bytes(issued_bytes.try_into().unwrap());
let issued_date = Duration::from_secs(issued_date);
Ok(Self {
issued_for: pk,
signature: signature.to_vec(),
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 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
)
})?;
Ok(Duration::from_secs(secs))
}
pub fn convert_from_strings(
issued_for: &str,
signature: &str,
expires_at: &str,
issued_at: &str,
) -> Result<Self, String> {
// PublicKey
let issued_for_bytes = Self::bs58_str_to_vec(issued_for, "issued_for")?;
let issued_for = PublicKey::decode(issued_for_bytes.as_slice()).map_err(|e| {
format!(
"Cannot decode the public key: {} in the trust '{}'",
issued_for, e
)
})?;
// 64 bytes signature
let signature = Self::bs58_str_to_vec(signature, "signature")?;
// Duration
let expires_at = Self::str_to_duration(expires_at, "expires_at")?;
// Duration
let issued_at = Self::str_to_duration(issued_at, "issued_at")?;
Ok(Trust::new(issued_for, expires_at, issued_at, signature))
}
}
impl ToString for Trust {
fn to_string(&self) -> String {
let issued_for = bs58::encode(self.issued_for.encode()).into_string();
let signature = bs58::encode(self.signature.as_slice()).into_string();
let expires_at = (self.expires_at.as_secs() as u64).to_string();
let issued_at = (self.issued_at.as_secs() as u64).to_string();
format!(
"{}\n{}\n{}\n{}",
issued_for, signature, expires_at, issued_at
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gen_revoke_and_validate() {
let truster = KeyPair::generate();
let trusted = KeyPair::generate();
let current = Duration::new(100, 0);
let duration = Duration::new(1000, 0);
let issued_at = Duration::new(10, 0);
let trust = Trust::create(&truster, trusted.public_key(), duration, issued_at);
assert_eq!(
Trust::verify(&trust, &truster.public_key(), current).is_ok(),
true
);
}
#[test]
fn test_validate_corrupted_revoke() {
let truster = KeyPair::generate();
let trusted = KeyPair::generate();
let current = Duration::new(1000, 0);
let issued_at = Duration::new(10, 0);
let trust = Trust::create(&truster, trusted.public_key(), current, issued_at);
let corrupted_duration = Duration::new(1234, 0);
let corrupted_trust = Trust::new(
trust.issued_for,
trust.expires_at,
corrupted_duration,
trust.signature,
);
assert!(Trust::verify(&corrupted_trust, &truster.public_key(), current).is_err());
}
#[test]
fn test_encode_decode() {
let truster = KeyPair::generate();
let trusted = KeyPair::generate();
let current = Duration::new(1000, 0);
let issued_at = Duration::new(10, 0);
let trust = Trust::create(&truster, trusted.public_key(), current, issued_at);
let encoded = trust.encode();
let decoded = Trust::decode(encoded.as_slice()).unwrap();
assert_eq!(trust, decoded);
}
}

618
src/trust_graph.rs Normal file
View File

@ -0,0 +1,618 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::certificate::Certificate;
use crate::ed25519::PublicKey;
use crate::public_key_hashable::PublicKeyHashable;
use crate::revoke::Revoke;
use crate::trust::Trust;
use crate::trust_node::{Auth, TrustNode};
use std::borrow::Borrow;
use std::collections::hash_map::Entry;
use std::collections::hash_map::Entry::Occupied;
use std::collections::{HashMap, HashSet, VecDeque};
use std::fmt::Debug;
use std::time::Duration;
/// for simplicity, we store `n` where Weight = 1/n^2
type Weight = u32;
/// Graph to efficiently calculate weights of certificates and get chains of certificates.
/// TODO serialization/deserialization
/// TODO export a certificate from graph
#[allow(dead_code)]
#[derive(Debug, Default)]
pub struct TrustGraph {
// TODO abstract this into a trait with key access methods
// TODO: add docs on fields
nodes: HashMap<PublicKeyHashable, TrustNode>,
root_weights: HashMap<PublicKeyHashable, Weight>,
}
#[allow(dead_code)]
impl TrustGraph {
pub fn new(root_weights: Vec<(PublicKey, Weight)>) -> Self {
Self {
nodes: HashMap::new(),
root_weights: root_weights
.into_iter()
.map(|(k, w)| (k.into(), w))
.collect(),
}
}
/// Insert new root weights
pub fn add_root_weights<P: Into<PublicKeyHashable>>(&mut self, weights: Vec<(P, Weight)>) {
self.root_weights
.extend(weights.into_iter().map(|(k, w)| (k.into(), w)))
}
/// Get trust by public key
pub fn get(&self, pk: PublicKey) -> Option<&TrustNode> {
self.nodes.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>
where
C: Borrow<Certificate>,
{
let roots: Vec<PublicKey> = self.root_weights.keys().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)?;
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();
// Insert new TrustNode for this root_pk if there wasn't one
if self.nodes.get_mut(&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.nodes.insert(root_pk, trust_node);
}
// Insert remaining trusts to the graph
let mut previous_trust = root_trust;
for trust in chain {
let pk = trust.issued_for.clone().into();
let auth = Auth {
trust: trust.clone(),
issued_by: previous_trust.issued_for.clone(),
};
match self.nodes.get_mut(&pk) {
Some(trust_node) => {
trust_node.update_auth(auth);
}
None => {
let mut trust_node = TrustNode::new(root_trust.issued_for.clone(), cur_time);
trust_node.update_auth(auth);
self.nodes.insert(pk, trust_node);
}
}
previous_trust = trust;
}
Ok(())
}
/// Get the maximum weight of trust for one public key.
pub fn weight<P>(&self, pk: P) -> Option<Weight>
where
P: Borrow<PublicKey>,
{
if let Some(weight) = self.root_weights.get(pk.borrow().as_ref()) {
return Some(*weight);
}
let roots: Vec<PublicKey> = self
.root_weights
.keys()
.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)
}
/// 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>
where
C: Borrow<Certificate>,
I: IntoIterator<Item = C>,
{
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()?;
let mut weight = std::u32::MAX;
for cert in certs {
let cert = cert.borrow();
let root_weight = *self
.root_weights
.get(cert.chain.first()?.issued_for.as_ref())
// This panic shouldn't happen // TODO: why?
.expect("first trust in chain must be in root_weights");
// 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)
}
/// BF search for all converging paths (chains) in the graph
/// TODO could be optimized with closure, that will calculate the weight on the fly
/// TODO or store auths to build certificates
fn bf_search_paths(
&self,
node: &TrustNode,
roots: HashSet<&PublicKeyHashable>,
) -> Vec<Vec<Auth>> {
// 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();
let node_auths: Vec<Auth> = node.authorizations().cloned().collect();
// put all auth in the queue as the first possible paths through the graph
for auth in node_auths {
chains_queue.push_back(vec![auth]);
}
// List of all chains that converge (terminate) to known roots
let mut terminated_chains: Vec<Vec<Auth>> = Vec::new();
while !chains_queue.is_empty() {
let cur_chain = chains_queue
.pop_front()
.expect("`chains_queue` always has at least one element");
let last = cur_chain
.last()
.expect("`cur_chain` always has at least one element");
let auths: Vec<Auth> = self
.nodes
.get(&last.issued_by.clone().into())
.expect(
"there cannot be paths without any nodes after adding verified certificates",
)
.authorizations()
.cloned()
.collect();
for auth in auths {
// if there is auth, that we not visited in the current chain, copy chain and append this auth
if !cur_chain
.iter()
.any(|a| a.trust.issued_for == auth.issued_by)
{
let mut new_chain = cur_chain.clone();
new_chain.push(auth);
chains_queue.push_back(new_chain);
}
}
// to be considered a valid chain, the chain must:
// - end with a self-signed trust
// - 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 converges_to_root = roots.contains(last.issued_by.as_ref());
if self_signed && converges_to_root && cur_chain.len() > 1 {
terminated_chains.push(cur_chain);
}
}
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>
where
P: Borrow<PublicKey>,
{
// get all auths (edges) for issued public key
let issued_for_node = self.nodes.get(issued_for.borrow().as_ref());
let roots = roots.iter().map(|pk| pk.as_ref());
let roots = self.root_weights.keys().chain(roots).collect();
match issued_for_node {
Some(node) => self
.bf_search_paths(node, roots)
.iter()
.map(|auths| {
// TODO: can avoid cloning here by returning &Certificate
let trusts: Vec<Trust> =
auths.iter().map(|auth| auth.trust.clone()).rev().collect();
Certificate::new_unverified(trusts)
})
.filter(|c| {
// Certificate with one trust means nothing, gotta be a bug. Checking for it here.
debug_assert!(
c.chain.len() > 1,
"certificate with chain of len 1 arose: {:#?}",
c
);
c.chain.len() > 1
})
.collect(),
None => Vec::new(),
}
}
/// Mark public key as revoked.
pub fn revoke(&mut self, revoke: Revoke) -> Result<(), String> {
Revoke::verify(&revoke)?;
let pk: PublicKeyHashable = revoke.pk.clone().into();
match self.nodes.entry(pk) {
Occupied(mut entry) => {
entry.get_mut().update_revoke(revoke);
Ok(())
}
Entry::Vacant(_) => Err("There is no trust with such PublicKey".to_string()),
}
}
/// Check information about new certificates and about revoked certificates.
/// Do it once per some time
// TODO
fn maintain() {}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::key_pair::KeyPair;
use crate::misc::current_time;
use failure::_core::time::Duration;
pub fn one_minute() -> Duration {
Duration::new(60, 0)
}
fn generate_root_cert() -> (KeyPair, KeyPair, Certificate) {
let root_kp = KeyPair::generate();
let second_kp = KeyPair::generate();
let cur_time = current_time();
(
root_kp.clone(),
second_kp.clone(),
Certificate::issue_root(
&root_kp,
second_kp.public_key(),
cur_time.checked_add(one_minute()).unwrap(),
cur_time,
),
)
}
fn generate_cert_with(
len: usize,
// Map of index to keypair. These key pairs will be used in trust chains at the given indexes
keys: HashMap<usize, KeyPair>,
expires_at: Duration,
issued_at: Duration,
) -> (Vec<KeyPair>, Certificate) {
assert!(len > 2);
let root_kp = KeyPair::generate();
let second_kp = KeyPair::generate();
let mut cert =
Certificate::issue_root(&root_kp, second_kp.public_key(), 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()).clone();
let previous_kp = &key_pairs[idx - 1];
cert = Certificate::issue(
&previous_kp,
kp.public_key(),
&cert,
expires_at,
// 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)
}
fn generate_cert_with_len(
len: usize,
keys: HashMap<usize, KeyPair>,
) -> (Vec<KeyPair>, Certificate) {
let cur_time = current_time();
let far_future = cur_time.checked_add(one_minute()).unwrap();
generate_cert_with(len, keys, far_future, cur_time)
}
#[test]
fn test_add_cert_without_trusted_root() {
let (_, _, cert) = generate_root_cert();
let cur_time = current_time();
let mut graph = TrustGraph::default();
let addition = graph.add(cert, cur_time);
assert_eq!(addition.is_ok(), false);
}
#[test]
fn test_add_cert() {
let (root, _, cert) = generate_root_cert();
let mut graph = TrustGraph::default();
graph.root_weights.insert(root.key_pair.public().into(), 0);
let addition = graph.add(cert, current_time());
assert_eq!(addition.is_ok(), true);
}
#[test]
fn test_add_certs_with_same_trusts_and_different_expirations() {
let cur_time = current_time();
let far_future = cur_time + Duration::from_secs(10);
let far_far_future = cur_time + Duration::from_secs(900);
let key_pair1 = KeyPair::generate();
let key_pair2 = KeyPair::generate();
// Use key_pair1 and key_pair2 for 5th and 6th trust in the cert chain
let mut chain_keys = HashMap::new();
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);
// Use key_pair1 and key_pair2 for 7th and 8th trust in the cert chain
let mut chain_keys = HashMap::new();
chain_keys.insert(7, key_pair1.clone());
chain_keys.insert(8, key_pair2.clone());
let (key_pairs2, cert2) =
generate_cert_with(10, chain_keys, far_far_future * 2, far_far_future);
let mut graph = TrustGraph::default();
let root1_pk = key_pairs1[0].public_key();
let root2_pk = key_pairs2[0].public_key();
graph.root_weights.insert(root1_pk.into(), 1);
graph.root_weights.insert(root2_pk.into(), 0);
graph.add(cert1, cur_time).unwrap();
let node2 = graph.get(key_pair2.public_key()).unwrap();
let auth_by_kp1 = node2
.authorizations()
.find(|a| a.issued_by == key_pair1.public_key())
.unwrap();
assert_eq!(auth_by_kp1.trust.expires_at, far_future * 2);
graph.add(cert2, cur_time).unwrap();
let node2 = graph.get(key_pair2.public_key()).unwrap();
let auth_by_kp1 = node2
.authorizations()
.find(|a| a.issued_by == key_pair1.public_key())
.unwrap();
assert_eq!(auth_by_kp1.trust.expires_at, far_far_future * 2);
}
#[test]
fn test_one_cert_in_graph() {
let (key_pairs, cert1) = generate_cert_with_len(10, HashMap::new());
let last_trust = cert1.chain[9].clone();
let mut graph = TrustGraph::default();
let root_pk = key_pairs[0].public_key();
graph.root_weights.insert(root_pk.into(), 1);
graph.add(cert1, current_time()).unwrap();
let w1 = graph.weight(key_pairs[0].public_key()).unwrap();
assert_eq!(w1, 1);
let w2 = graph.weight(key_pairs[1].public_key()).unwrap();
assert_eq!(w2, 2);
let w3 = graph.weight(key_pairs[9].public_key()).unwrap();
assert_eq!(w3, 10);
let node = graph.get(key_pairs[9].public_key()).unwrap();
let auths: Vec<&Auth> = node.authorizations().collect();
assert_eq!(auths.len(), 1);
assert_eq!(auths[0].trust, last_trust);
}
#[test]
fn test_cycles_in_graph() {
let key_pair1 = KeyPair::generate();
let key_pair2 = KeyPair::generate();
let key_pair3 = KeyPair::generate();
let mut chain_keys = HashMap::new();
chain_keys.insert(3, key_pair1.clone());
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 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 mut graph = TrustGraph::default();
let root1_pk = key_pairs1[0].public_key();
let root2_pk = key_pairs2[0].public_key();
graph.root_weights.insert(root1_pk.into(), 1);
graph.root_weights.insert(root2_pk.into(), 0);
let last_pk1 = cert1.chain[9].issued_for.clone();
let last_pk2 = cert2.chain[9].issued_for.clone();
graph.add(cert1, current_time()).unwrap();
graph.add(cert2, current_time()).unwrap();
let revoke1 = Revoke::create(&key_pairs1[3], key_pairs1[4].public_key(), current_time());
graph.revoke(revoke1).unwrap();
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();
// 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();
assert_eq!(w1, 4);
assert_eq!(w2.is_none(), true);
assert_eq!(w3, 5);
assert_eq!(w_last1, 7);
assert_eq!(w_last2, 6);
}
#[test]
fn test_get_one_cert() {
let (key_pairs, cert) = generate_cert_with_len(5, HashMap::new());
let mut graph = TrustGraph::default();
let root1_pk = key_pairs[0].public_key();
graph.root_weights.insert(root1_pk.clone().into(), 1);
graph.add(cert.clone(), current_time()).unwrap();
let certs = graph.get_all_certs(key_pairs.last().unwrap().public_key(), &[root1_pk]);
assert_eq!(certs.len(), 1);
assert_eq!(certs[0], cert);
}
#[test]
fn test_chain_from_root_to_another_root() {
let (_, cert) = generate_cert_with_len(6, HashMap::new());
let mut graph = TrustGraph::default();
// add first and last trusts as roots
graph
.root_weights
.insert(cert.chain[0].clone().issued_for.into(), 1);
graph
.root_weights
.insert(cert.chain[3].clone().issued_for.into(), 1);
graph
.root_weights
.insert(cert.chain[5].clone().issued_for.into(), 1);
graph.add(cert.clone(), current_time()).unwrap();
let t = cert.chain[5].clone();
let certs = graph.get_all_certs(t.issued_for, &[]);
assert_eq!(certs.len(), 1);
}
#[test]
fn test_find_certs() {
let key_pair1 = KeyPair::generate();
let key_pair2 = KeyPair::generate();
let key_pair3 = KeyPair::generate();
let mut chain_keys = HashMap::new();
chain_keys.insert(2, key_pair1.clone());
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 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 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 mut graph = TrustGraph::default();
let root1_pk = key_pairs1[0].public_key();
let root2_pk = key_pairs2[0].public_key();
let root3_pk = key_pairs3[0].public_key();
graph.root_weights.insert(root1_pk.clone().into(), 1);
graph.root_weights.insert(root2_pk.clone().into(), 0);
graph.root_weights.insert(root3_pk.clone().into(), 0);
graph.add(cert1, current_time()).unwrap();
graph.add(cert2, current_time()).unwrap();
graph.add(cert3, current_time()).unwrap();
let roots_values = [root1_pk, root2_pk, root3_pk];
let certs1 = graph.get_all_certs(key_pair1.public_key(), &roots_values);
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 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 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);
}
}

202
src/trust_node.rs Normal file
View File

@ -0,0 +1,202 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::ed25519::PublicKey;
use crate::public_key_hashable::PublicKeyHashable;
use crate::revoke::Revoke;
use crate::trust::Trust;
use failure::_core::time::Duration;
use std::collections::HashMap;
#[derive(Debug, Clone)]
enum TrustRelation {
Auth(Auth),
Revoke(Revoke),
}
impl TrustRelation {
/// Returns timestamp of when this relation was created
pub fn issued_at(&self) -> Duration {
match self {
TrustRelation::Auth(auth) => auth.trust.issued_at,
TrustRelation::Revoke(revoke) => revoke.revoked_at,
}
}
/// Returns public key of the creator of this relation
pub fn issued_by(&self) -> &PublicKey {
match self {
TrustRelation::Auth(auth) => &auth.issued_by,
TrustRelation::Revoke(revoke) => &revoke.revoked_by,
}
}
}
/// Represents who give a certificate
#[derive(Debug, Clone)]
pub struct Auth {
/// proof of this authorization
pub trust: Trust,
/// the issuer of this authorization
pub issued_by: PublicKey,
}
/// An element of trust graph that store relations (trust or revoke)
/// that given by some owners of public keys.
#[derive(Debug)]
pub struct TrustNode {
/// identity key of this element
pub pk: PublicKey,
/// one public key could be authorized or revoked by multiple certificates
trust_relations: HashMap<PublicKeyHashable, TrustRelation>,
/// for maintain
pub verified_at: Duration,
}
#[allow(dead_code)]
impl TrustNode {
pub fn new(pk: PublicKey, verified_at: Duration) -> Self {
Self {
pk,
trust_relations: HashMap::new(),
verified_at,
}
}
pub fn get_auth(&self, pk: PublicKey) -> Option<Auth> {
match self.trust_relations.get(&pk.into()) {
Some(TrustRelation::Auth(auth)) => Some(auth.clone()),
_ => None,
}
}
pub fn get_revoke(&self, pk: PublicKey) -> Option<Revoke> {
match self.trust_relations.get(&pk.into()) {
Some(TrustRelation::Revoke(rev)) => Some(rev.clone()),
_ => None,
}
}
pub fn authorizations(&self) -> impl Iterator<Item = &Auth> + '_ {
self.trust_relations.values().filter_map(|tr| {
if let TrustRelation::Auth(auth) = tr {
Some(auth)
} else {
None
}
})
}
pub fn revocations(&self) -> impl Iterator<Item = &Revoke> + '_ {
self.trust_relations.values().filter_map(|tr| {
if let TrustRelation::Revoke(revoke) = tr {
Some(revoke)
} else {
None
}
})
}
/// Adds authorization. If the trust node already has this authorization,
/// add auth with later expiration date.
pub fn update_auth(&mut self, auth: Auth) {
self.update_relation(TrustRelation::Auth(auth));
}
// insert new trust relation, ignore if there is another one with same public key
fn insert(&mut self, pk: PublicKeyHashable, tr: TrustRelation) {
self.trust_relations.insert(pk, tr);
}
fn update_relation(&mut self, relation: TrustRelation) {
let issued_by = relation.issued_by().as_ref();
match self.trust_relations.get(issued_by) {
Some(TrustRelation::Auth(auth)) => {
if auth.trust.issued_at < relation.issued_at() {
self.insert(issued_by.clone(), relation)
}
}
Some(TrustRelation::Revoke(existed_revoke)) => {
if existed_revoke.revoked_at < relation.issued_at() {
self.insert(issued_by.clone(), relation)
}
}
None => self.insert(issued_by.clone(), relation),
};
}
pub fn update_revoke(&mut self, revoke: Revoke) {
self.update_relation(TrustRelation::Revoke(revoke));
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use crate::key_pair::KeyPair;
use super::*;
#[test]
fn test_auth_and_revoke_trust_node() {
let kp = KeyPair::generate();
let now = Duration::new(50, 0);
let past = Duration::new(5, 0);
let future = Duration::new(500, 0);
let mut trust_node = TrustNode {
pk: kp.public_key(),
trust_relations: HashMap::new(),
verified_at: now,
};
let truster = KeyPair::generate();
let revoke = Revoke::create(&truster, kp.public_key(), now);
trust_node.update_revoke(revoke);
assert!(trust_node.get_revoke(truster.public_key()).is_some());
let old_trust = Trust::create(&truster, kp.public_key(), Duration::new(60, 0), past);
let old_auth = Auth {
trust: old_trust,
issued_by: truster.public_key(),
};
trust_node.update_auth(old_auth);
assert!(trust_node.get_revoke(truster.public_key()).is_some());
assert!(trust_node.get_auth(truster.public_key()).is_none());
let trust = Trust::create(&truster, kp.public_key(), Duration::new(60, 0), future);
let auth = Auth {
trust,
issued_by: truster.public_key(),
};
trust_node.update_auth(auth);
assert!(trust_node.get_auth(truster.public_key()).is_some());
assert!(trust_node.get_revoke(truster.public_key()).is_none());
}
}