/* * 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::trust::TrustError::{DecodeError, SignatureError}; use derivative::Derivative; use fluence_identity::key_pair::KeyPair; use fluence_identity::public_key::PublicKey; use fluence_identity::signature::Signature; use serde::{Deserialize, Serialize}; use std::convert::TryInto; use std::time::Duration; use thiserror::Error as ThisError; 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, Serialize, Deserialize)] #[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.to_bytes()).into_string()) } fn show_sig(sig: &Signature, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { write!(f, "{}", bs58::encode(&sig.to_bytes()).into_string()) } #[derive(ThisError, Debug)] pub enum TrustError { /// Errors occurred when 'expires_at' date is later then current time. #[error("Trust is expired at: '{0:?}', current time: '{1:?}'")] Expired(Duration, Duration), /// Errors occured on signature verification #[error("{0}")] SignatureError(String), /// Errors occured on trust decoding from differrent formats #[error("{0}")] DecodeError(String), } impl Trust { #[allow(dead_code)] pub fn new( 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<(), TrustError> { if trust.expires_at < cur_time { 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).map_err(SignatureError)?; Ok(()) } fn metadata_bytes(pk: &PublicKey, expires_at: Duration, issued_at: Duration) -> [u8; 48] { let pk_encoded = pk.to_bytes(); 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 { let mut vec = Vec::with_capacity(TRUST_LEN); vec.extend_from_slice(&self.issued_for.to_bytes()); vec.extend_from_slice(&self.signature.to_bytes()); 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 { if arr.len() != TRUST_LEN { return Err(DecodeError( format!("Trust length should be 104: public key(32) + signature(64) + expiration date(8), was: {}", arr.len()) )); } let pk = PublicKey::from_bytes(&arr[0..PK_LEN]).map_err(|err| { DecodeError(format!("Cannot decode a public key: {}", err.to_string())) })?; let signature = &arr[PK_LEN..PK_LEN + SIG_LEN]; let signature = Signature::from_bytes(signature) .map_err(|err| DecodeError(format!("Cannot decode a signature: {}", err)))?; 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, expires_at: expiration_date, issued_at: issued_date, }) } fn bs58_str_to_vec(str: &str, field: &str) -> Result, TrustError> { bs58::decode(str).into_vec().map_err(|e| { DecodeError(format!( "Cannot decode `{}` from base58 format in the trust '{}': {}", field, str, e )) }) } fn str_to_duration(str: &str, field: &str) -> Result { let secs = str.parse().map_err(|e| { DecodeError(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 { // 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| { DecodeError(format!( "Cannot decode the public key: {} in the trust '{}'", issued_for, e )) })?; // 64 bytes signature let signature = Self::bs58_str_to_vec(signature, "signature")?; let signature = Signature::from_bytes(&signature).map_err(DecodeError)?; // 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.to_bytes()).into_string(); let signature = bs58::encode(self.signature.to_bytes()).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); } }