Files
yubikey-piv-pkcs11/native-pkcs11-traits/src/lib.rs

222 lines
6.5 KiB
Rust

// 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<T> = std::result::Result<T, Box<dyn std::error::Error>>;
pub type Digest = [u8; 20];
// The Backend is first staged so it can be stored in a Box<dyn Backend>. This
// allows the Backend to be reference with `&'static`.
static STAGED_BACKEND: RwLock<Option<Box<dyn Backend>>> = RwLock::new(None);
static BACKEND: Lazy<Box<dyn Backend>> =
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<dyn Backend>) {
*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<u8>;
fn label(&self) -> String;
fn sign(&self, algorithm: &SignatureAlgorithm, data: &[u8]) -> Result<Vec<u8>>;
fn delete(&self);
fn algorithm(&self) -> KeyAlgorithm;
fn find_public_key(&self, backend: &dyn Backend) -> Result<Option<Box<dyn PublicKey>>> {
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<H: std::hash::Hasher>(&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<u8>;
fn label(&self) -> String;
fn to_der(&self) -> Vec<u8>;
fn verify(&self, algorithm: &SignatureAlgorithm, data: &[u8], signature: &[u8]) -> Result<()>;
fn delete(self: Box<Self>);
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<H: std::hash::Hasher>(&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<u8>;
fn public_key(&self) -> &dyn PublicKey;
fn delete(self: Box<Self>);
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<H: std::hash::Hasher>(&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<u8> {
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<u8> {
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<u8> {
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<T: Certificate + ?Sized> 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<Vec<Box<dyn Certificate>>>;
fn find_private_key(&self, query: KeySearchOptions) -> Result<Option<Arc<dyn PrivateKey>>>;
fn find_public_key(&self, query: KeySearchOptions) -> Result<Option<Box<dyn PublicKey>>>;
fn find_all_private_keys(&self) -> Result<Vec<Arc<dyn PrivateKey>>>;
fn find_all_public_keys(&self) -> Result<Vec<Arc<dyn PublicKey>>>;
fn generate_key(
&self,
algorithm: KeyAlgorithm,
label: Option<&str>,
) -> Result<Arc<dyn PrivateKey>>;
}
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::<String>()
}