#![deny(missing_docs)]
use openssl::{
error::ErrorStack,
hash::MessageDigest,
pkey::{PKey, Private, Public},
sha::Sha256,
sign::{Signer, Verifier},
};
use std::fmt::Debug;
#[derive(Clone)]
pub struct OpenSslDigest {
digest: Sha256,
}
pub struct OpenSslSigner {
private_key: PKey<Private>,
}
pub struct OpenSslVerifier {
public_key: PKey<Public>,
}
#[derive(Clone, serde::Deserialize, serde::Serialize)]
pub struct OpenSsl {
key_id: String,
#[serde(with = "openssl_private_key")]
private_key: PKey<Private>,
}
impl OpenSsl {
pub fn new(key_id: String, private_key: PKey<Private>) -> Self {
Self {
key_id,
private_key,
}
}
}
impl apub_core::digest::Digest for OpenSslDigest {
const NAME: &'static str = "SHA-256";
fn digest(&mut self, input: &[u8]) -> String {
self.digest.update(input);
let bytes = self.digest.clone().finish();
openssl::base64::encode_block(&bytes)
}
fn update(&mut self, input: &[u8]) {
self.digest.update(input);
}
fn verify(&mut self, encoded: &str) -> bool {
let bytes = self.digest.clone().finish();
openssl::base64::encode_block(&bytes) == encoded
}
}
impl apub_core::digest::DigestBuilder for OpenSslDigest {
fn build() -> Self {
OpenSslDigest {
digest: Sha256::new(),
}
}
}
impl apub_core::signature::Sign for OpenSslSigner {
type Error = ErrorStack;
fn sign(&self, signing_string: &str) -> Result<String, Self::Error> {
let mut signer = Signer::new(MessageDigest::sha256(), &self.private_key)?;
signer.update(signing_string.as_bytes())?;
Ok(openssl::base64::encode_block(&signer.sign_to_vec()?))
}
}
impl apub_core::signature::Verify for OpenSslVerifier {
type Error = ErrorStack;
fn verify(&self, signing_string: &str, signature: &str) -> Result<bool, Self::Error> {
let mut verifier = Verifier::new(MessageDigest::sha256(), &self.public_key)?;
verifier.update(signing_string.as_bytes())?;
verifier.verify(&openssl::base64::decode_block(signature)?)
}
}
impl apub_core::signature::VerifyBuilder for OpenSslVerifier {
fn build(public_key_pem: &str) -> Result<Self, Self::Error>
where
Self: Sized,
{
Ok(OpenSslVerifier {
public_key: PKey::public_key_from_pem(public_key_pem.as_bytes())?,
})
}
}
impl apub_core::digest::DigestFactory for OpenSsl {
type Digest = OpenSslDigest;
}
impl apub_core::signature::PrivateKey for OpenSsl {
type Signer = OpenSslSigner;
fn key_id(&self) -> String {
self.key_id.clone()
}
fn signer(&self) -> Self::Signer {
OpenSslSigner {
private_key: self.private_key.clone(),
}
}
}
impl apub_core::signature::PrivateKeyBuilder for OpenSsl {
type Error = ErrorStack;
fn build(key_id: String, private_key_pem: &str) -> Result<Self, Self::Error>
where
Self: Sized,
{
Ok(Self {
key_id,
private_key: PKey::private_key_from_pem(private_key_pem.as_bytes())?,
})
}
fn private_key_pem(&self) -> Result<String, Self::Error> {
self.private_key
.private_key_to_pem_pkcs8()
.map(|v| String::from_utf8_lossy(&v).to_string())
}
}
impl Debug for OpenSslDigest {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OpenSslDigest")
.field("digest", &"Sha256")
.finish()
}
}
impl Debug for OpenSslSigner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OpenSslSigner")
.field("private_key", &"hidden")
.finish()
}
}
impl Debug for OpenSsl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OpenSsl")
.field("key_id", &self.key_id)
.field("private_key", &"hidden")
.finish()
}
}
mod openssl_private_key {
use openssl::pkey::{PKey, Private};
use serde::{
de::{Deserialize, Deserializer},
ser::{Serialize, Serializer},
};
pub(super) fn serialize<S: Serializer>(
private_key: &PKey<Private>,
serializer: S,
) -> Result<S::Ok, S::Error> {
use serde::ser::Error;
let der = private_key.private_key_to_der().map_err(S::Error::custom)?;
let der_string = openssl::base64::encode_block(&der);
String::serialize(&der_string, serializer)
}
pub(super) fn deserialize<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<PKey<Private>, D::Error> {
use serde::de::Error;
let der_string = String::deserialize(deserializer)?;
let der = openssl::base64::decode_block(&der_string).map_err(D::Error::custom)?;
PKey::<Private>::private_key_from_der(&der).map_err(D::Error::custom)
}
}
#[cfg(test)]
mod tests {
use super::OpenSsl;
use apub_core::signature::{PrivateKey, Sign};
use openssl::{pkey::PKey, rsa::Rsa};
#[test]
fn round_trip() {
let private_key = PKey::from_rsa(Rsa::generate(1024).unwrap()).unwrap();
let crypto = OpenSsl::new("key-id".into(), private_key);
let signer = crypto.signer();
let first_sign = signer.sign("hello").unwrap();
let s = serde_json::to_string(&crypto).unwrap();
let crypto2: OpenSsl = serde_json::from_str(&s).unwrap();
let signer2 = crypto2.signer();
let second_sign = signer2.sign("hello").unwrap();
let s2 = serde_json::to_string(&crypto2).unwrap();
assert_eq!(s, s2);
assert_eq!(first_sign, second_sign);
}
}