// Copyright 2022 Google LLC // // 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 std::{ any::Any, hash::Hash, sync::{Arc, RwLock}, }; pub use once_cell; use once_cell::sync::Lazy; use x509_cert::der::Decode; pub type Result = std::result::Result>; pub type Digest = [u8; 20]; // The Backend is first staged so it can be stored in a Box. This // allows the Backend to be reference with `&'static`. static STAGED_BACKEND: RwLock>> = RwLock::new(None); static BACKEND: Lazy> = Lazy::new(|| STAGED_BACKEND.write().unwrap().take().unwrap()); /// Stores a backend to later be returned by all calls `crate::backend()`. pub fn register_backend(backend: Box) { *STAGED_BACKEND.write().unwrap() = Some(backend); } pub fn backend() -> &'static dyn Backend { BACKEND.as_ref() } #[derive(Debug, PartialEq, Eq, Clone)] pub enum DigestType { Sha1, Sha224, Sha256, Sha384, Sha512, } impl DigestType { pub fn digest_len(&self) -> usize { match self { DigestType::Sha1 => 20, DigestType::Sha224 => 28, DigestType::Sha256 => 32, DigestType::Sha384 => 48, DigestType::Sha512 => 64, } } } #[derive(Debug, Clone)] pub enum SignatureAlgorithm { Ecdsa, RsaRaw, RsaPkcs1v15Raw, RsaPkcs1v15Sha1, RsaPkcs1v15Sha384, RsaPkcs1v15Sha256, RsaPkcs1v15Sha512, RsaPss { digest: DigestType, mask_generation_function: DigestType, salt_length: u64, }, } pub trait PrivateKey: Send + Sync { fn public_key_hash(&self) -> Vec; fn label(&self) -> String; fn sign(&self, algorithm: &SignatureAlgorithm, data: &[u8]) -> Result>; fn delete(&self); fn algorithm(&self) -> KeyAlgorithm; fn find_public_key(&self, backend: &dyn Backend) -> Result>> { let pubkey_hash: Digest = self.public_key_hash().as_slice().try_into()?; backend.find_public_key(KeySearchOptions::PublicKeyHash(pubkey_hash)) } } impl std::fmt::Debug for dyn PrivateKey { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("PrivateKey") .field("label", &self.label()) .finish_non_exhaustive() } } impl PartialEq for dyn PrivateKey { fn eq(&self, other: &Self) -> bool { self.public_key_hash() == other.public_key_hash() && self.label() == other.label() } } impl Eq for dyn PrivateKey {} impl Hash for dyn PrivateKey { fn hash(&self, state: &mut H) { self.type_id().hash(state); self.public_key_hash().hash(state); self.label().hash(state); } } pub trait PublicKey: Send + Sync + std::fmt::Debug { fn public_key_hash(&self) -> Vec; fn label(&self) -> String; fn to_der(&self) -> Vec; fn verify(&self, algorithm: &SignatureAlgorithm, data: &[u8], signature: &[u8]) -> Result<()>; fn delete(self: Box); fn algorithm(&self) -> KeyAlgorithm; } impl PartialEq for dyn PublicKey { fn eq(&self, other: &Self) -> bool { self.public_key_hash() == other.public_key_hash() && self.label() == other.label() } } impl Eq for dyn PublicKey {} impl Hash for dyn PublicKey { fn hash(&self, state: &mut H) { self.type_id().hash(state); self.public_key_hash().hash(state); self.label().hash(state); } } pub trait Certificate: Send + Sync + std::fmt::Debug { fn label(&self) -> String; fn to_der(&self) -> Vec; fn public_key(&self) -> &dyn PublicKey; fn delete(self: Box); fn algorithm(&self) -> KeyAlgorithm { self.public_key().algorithm() } } impl PartialEq for dyn Certificate { fn eq(&self, other: &Self) -> bool { self.to_der() == other.to_der() && self.label() == other.label() } } impl Eq for dyn Certificate {} impl Hash for dyn Certificate { fn hash(&self, state: &mut H) { self.type_id().hash(state); self.to_der().hash(state); self.label().hash(state); } } pub trait CertificateExt: Certificate { fn issuer(&self) -> Vec { let der = self.to_der(); let c = x509_cert::Certificate::from_der(&der).unwrap(); x509_cert::der::Encode::to_der(&c.tbs_certificate.issuer).unwrap() } fn serial_number(&self) -> Vec { let der = self.to_der(); let c = x509_cert::Certificate::from_der(&der).unwrap(); x509_cert::der::Encode::to_der(&c.tbs_certificate.serial_number).unwrap() } fn subject(&self) -> Vec { let der = self.to_der(); let c = x509_cert::Certificate::from_der(&der).unwrap(); x509_cert::der::Encode::to_der(&c.tbs_certificate.subject).unwrap() } } impl CertificateExt for T {} #[derive(Debug)] pub enum KeySearchOptions { // TODO(kcking): search keys by _both_ label and public key hash as that is how // they are de-duped and referenced. Label(String), PublicKeyHash(Digest), } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum KeyAlgorithm { Rsa, Ecc, } pub trait Backend: Send + Sync { fn name(&self) -> String; fn find_all_certificates(&self) -> Result>>; fn find_private_key(&self, query: KeySearchOptions) -> Result>>; fn find_public_key(&self, query: KeySearchOptions) -> Result>>; fn find_all_private_keys(&self) -> Result>>; fn find_all_public_keys(&self) -> Result>>; fn generate_key( &self, algorithm: KeyAlgorithm, label: Option<&str>, ) -> Result>; } pub fn random_label() -> String { use rand::{distributions::Alphanumeric, Rng}; String::from("bumpkey ") + &rand::thread_rng() .sample_iter(&Alphanumeric) .take(32) .map(char::from) .collect::() }