/* * 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::CertificateError::{ CertificateLengthError, DecodeError, DecodeTrustError, ExpirationError, IncorrectCertificateFormat, KeyInCertificateError, MalformedRoot, NoTrustedRoot, VerificationError, }; use crate::trust::{Trust, TrustError}; use fluence_keypair::key_pair::KeyPair; use fluence_keypair::public_key::PublicKey; use std::str::FromStr; use std::time::Duration; use thiserror::Error as ThisError; /// 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]; const TRUST_NUMBER_LEN: usize = 1; /// Chain of trusts started from self-signed root trust. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Certificate { pub chain: Vec, } #[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) -> Self { Self { chain } } pub fn new_from_root_trust(root_trust: Trust, issued_trust: Trust, cur_time: Duration) -> Result { Trust::verify(&root_trust, &root_trust.issued_for, cur_time).map_err(MalformedRoot)?; Trust::verify(&issued_trust, &root_trust.issued_for, cur_time).map_err(|e| VerificationError(1, e))?; Ok(Self { chain: vec![root_trust, issued_trust] }) } pub fn issue_with_trust(issued_by: PublicKey, trust: Trust, extend_cert: &Certificate, cur_time: Duration) -> Result { if trust.expires_at.lt(&trust.issued_at) { return Err(ExpirationError { expires_at: format!("{:?}", trust.expires_at), issued_at: format!("{:?}", trust.issued_at), }); } Certificate::verify(extend_cert, &[extend_cert.chain[0].issued_for.clone()], cur_time)?; // check if `issued_by` is allowed to issue a certificate (i.e., there’s 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 { previous_trust_num = pk_id as i32; } } if previous_trust_num == -1 { return Err(KeyInCertificateError); }; // 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(); new_chain.push(trust); Ok(Self { chain: new_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(), 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 { if expires_at.lt(&issued_at) { 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)?; let issued_by_pk = issued_by.public(); // check if `issued_by_pk` is allowed to issue a certificate (i.e., there’s 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(KeyInCertificateError); }; // 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<(), CertificateError> { let chain = &cert.chain; if chain.is_empty() { 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(MalformedRoot)?; if !trusted_roots.contains(&root.issued_for) { return Err(NoTrustedRoot); } // 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| VerificationError(trust_id, e))?; } Ok(()) } /// Convert certificate to byte format /// 2 format + 4 version + 1 trusts number + ((1 trust size byte + trust) for each trust) #[allow(dead_code)] pub fn encode(&self) -> Vec { let mut encoded = Vec::new(); encoded.extend_from_slice(FORMAT); encoded.extend_from_slice(VERSION); encoded.push(self.chain.len() as u8); for t in &self.chain { let trust = t.encode(); encoded.push(trust.len() as u8); encoded.extend(trust); } encoded } fn check_arr_len(arr: &[u8], check_len: usize) -> Result<(), CertificateError> { if arr.len() < check_len { Err(CertificateLengthError) } else { Ok(()) } } #[allow(dead_code)] pub fn decode(arr: &[u8]) -> Result { // TODO do match different formats and versions Self::check_arr_len(arr, FORMAT.len() + VERSION.len() + TRUST_NUMBER_LEN)?; let mut offset = 0; let _format = &arr[offset..offset + FORMAT.len()]; offset += FORMAT.len(); let _version = &arr[offset..offset + VERSION.len()]; offset += VERSION.len(); let number_of_trusts = arr[offset] as usize; offset += TRUST_NUMBER_LEN; if number_of_trusts < 2 { return Err(CertificateLengthError); } let mut chain = Vec::with_capacity(number_of_trusts); for _ in 0..number_of_trusts { Self::check_arr_len(arr, offset + 1)?; let trust_len = arr[offset] as usize; let from = offset + 1; let to = from + trust_len; Self::check_arr_len(arr, to)?; let slice = &arr[from..to]; let t = Trust::decode(slice).map_err(DecodeError)?; chain.push(t); offset += 1 + trust_len; } 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 = CertificateError; fn from_str(s: &str) -> Result { 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(IncorrectCertificateFormat(s.to_string())); } 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], ) .map_err(|e| DecodeTrustError(i, e))?; trusts.push(trust); } Ok(Self::new_unverified(trusts)) } } #[cfg(test)] mod tests { use super::*; use crate::misc::current_time; use fluence_keypair::KeyPair; 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_ed25519() { let (_root_kp, second_kp, cert) = generate_root_cert(); let cur_time = current_time(); let third_kp = KeyPair::generate_ed25519(); let new_cert = Certificate::issue( &second_kp, third_kp.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_ed25519() { let (_root_kp, second_kp, cert) = generate_root_cert(); let cur_time = current_time(); let third_kp = KeyPair::generate_ed25519(); let new_cert = Certificate::issue( &second_kp, third_kp.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_ed25519() { 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_ed25519(); let second_kp = KeyPair::generate_ed25519(); let cur_time = current_time(); ( root_kp.clone(), second_kp.clone(), Certificate::issue_root( &root_kp, second_kp.public(), cur_time.checked_add(one_year()).unwrap(), cur_time, ), ) } #[test] fn test_issue_cert_ed25519() { let (root_kp, second_kp, cert) = generate_root_cert(); let trusted_roots = [root_kp.public()]; // 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_ed25519(); let new_cert = Certificate::issue( &second_kp, third_kp.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!("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()); assert_eq!(new_cert.chain[1].issued_for, second_kp.public()); assert_eq!(new_cert.chain[2].issued_for, third_kp.public()); assert!(Certificate::verify(&new_cert, &trusted_roots, cur_time).is_ok()); } #[test] fn test_cert_expiration_ed25519() { let (root_kp, second_kp, cert) = generate_root_cert(); let trusted_roots = [root_kp.public()]; let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); let third_kp = KeyPair::generate_ed25519(); let new_cert = Certificate::issue( &second_kp, third_kp.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_ed25519() { let (root_kp, second_kp, cert) = generate_root_cert(); let trusted_roots = [root_kp.public()]; let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); let third_kp = KeyPair::generate_ed25519(); let fourth_kp = KeyPair::generate_ed25519(); let new_cert = Certificate::issue( &second_kp, third_kp.public(), &cert, cur_time.checked_add(one_second()).unwrap(), cur_time, cur_time, ) .unwrap(); let new_cert = Certificate::issue( &third_kp, fourth_kp.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()); assert_eq!(new_cert.chain[1].issued_for, second_kp.public()); assert_eq!(new_cert.chain[2].issued_for, third_kp.public()); assert_eq!(new_cert.chain[3].issued_for, fourth_kp.public()); assert!(Certificate::verify(&new_cert, &trusted_roots, cur_time).is_ok()); } #[test] fn test_issue_in_chain_body_ed25519() { let (root_kp, second_kp, cert) = generate_root_cert(); let trusted_roots = [root_kp.public()]; let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); let third_kp = KeyPair::generate_ed25519(); let fourth_kp = KeyPair::generate_ed25519(); let new_cert = Certificate::issue( &second_kp, third_kp.public(), &cert, cur_time.checked_add(one_second()).unwrap(), cur_time, cur_time, ) .unwrap(); let new_cert = Certificate::issue( &second_kp, fourth_kp.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()); assert_eq!(new_cert.chain[1].issued_for, second_kp.public()); assert_eq!(new_cert.chain[2].issued_for, fourth_kp.public()); assert!(Certificate::verify(&new_cert, &trusted_roots, cur_time).is_ok()); } #[test] fn test_no_cert_in_chain_ed25519() { let (_root_kp, _second_kp, cert) = generate_root_cert(); let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); let bad_kp = KeyPair::generate_ed25519(); let new_cert_bad = Certificate::issue( &bad_kp, bad_kp.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()]; 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()]; // 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()]; assert_eq!(cert.chain.len(), 2); assert_eq!(cert.chain[0].issued_for, root_kp.public()); assert_eq!(cert.chain[1].issued_for, second_kp.public()); assert!(Certificate::verify(&cert, &trusted_roots, cur_time).is_ok()); } }