use crate::{
basic_crypto_functions::{sign as openssl_sign, verify, BasisCryptoError},
byte_array::ByteArray,
direct_trust::{DirectTrustError, Keystore},
hashing::{HashError, HashableMessage, RecursiveHashTrait},
};
use thiserror::Error;
#[derive(Error, Debug)]
#[error(transparent)]
pub struct SignatureError(#[from] SignatureErrorRepr);
#[derive(Error, Debug)]
enum SignatureErrorRepr {
#[error("Error verifiying the signature")]
VerifySignatureError { source: SignatureInternalError },
#[error("Error signing")]
SignError { source: SignatureInternalError },
}
#[derive(Error, Debug)]
enum SignatureInternalError {
#[error("Error getting the direct_trust certificate from the keystore")]
DTCertificate { source: DirectTrustError },
#[error("Error getting the valid time of the direct_trust certificate")]
GetTime { source: BasisCryptoError },
#[error("Time is not valide for certificate: {0}")]
Time(String),
#[error("Error getting the public key from the direct_trust certificate")]
PublicKey { source: BasisCryptoError },
#[error("Error hashing message")]
HashMessage { source: HashError },
#[error("Error hashing additional content")]
HashAddContent { source: HashError },
#[error("Error hashing h")]
HashH { source: HashError },
#[error("Error verifying signature")]
VerifySignature { source: BasisCryptoError },
#[error("Secret key is missing")]
MissingSecretKey,
#[error("Error signing the message")]
Sign { source: BasisCryptoError },
}
pub fn verify_signature(
keystore: &Keystore,
ca_id: &str,
message: &HashableMessage,
additional_context: &HashableMessage,
signature: &ByteArray,
) -> Result<bool, SignatureError> {
verify_signature_impl(keystore, ca_id, message, additional_context, signature)
.map_err(|e| SignatureErrorRepr::VerifySignatureError { source: e })
.map_err(SignatureError::from)
}
fn verify_signature_impl(
keystore: &Keystore,
ca_id: &str,
message: &HashableMessage,
additional_context: &HashableMessage,
signature: &ByteArray,
) -> Result<bool, SignatureInternalError> {
let direct_trust_certificate = keystore
.public_certificate(ca_id)
.map_err(|e| SignatureInternalError::DTCertificate { source: e })?;
let cert = direct_trust_certificate.signing_certificate();
let time_ok = cert
.is_valid_time()
.map_err(|e| SignatureInternalError::GetTime { source: e })?;
if !time_ok {
return Err(SignatureInternalError::Time(ca_id.to_string()));
}
let pkey = cert
.public_key()
.map_err(|e| SignatureInternalError::PublicKey { source: e })?;
let h_vec = vec![
HashableMessage::Hashed(
message
.recursive_hash()
.map_err(|e| SignatureInternalError::HashMessage { source: e })?,
),
HashableMessage::Hashed(
additional_context
.recursive_hash()
.map_err(|e| SignatureInternalError::HashAddContent { source: e })?,
),
];
let h = HashableMessage::from(h_vec)
.recursive_hash()
.map_err(|e| SignatureInternalError::HashH { source: e })?;
verify(&pkey, &h, signature).map_err(|e| SignatureInternalError::VerifySignature { source: e })
}
pub fn sign(
keystore: &Keystore,
message: &HashableMessage,
additional_context: &HashableMessage,
) -> Result<ByteArray, SignatureError> {
sign_impl(keystore, message, additional_context)
.map_err(|e| SignatureErrorRepr::SignError { source: e })
.map_err(SignatureError::from)
}
fn sign_impl(
keystore: &Keystore,
message: &HashableMessage,
additional_context: &HashableMessage,
) -> Result<ByteArray, SignatureInternalError> {
let direct_trust_certificate = keystore
.secret_key_certificate()
.map_err(|e| SignatureInternalError::DTCertificate { source: e })?;
let cert = direct_trust_certificate.signing_certificate();
let time_ok = cert
.is_valid_time()
.map_err(|e| SignatureInternalError::GetTime { source: e })?;
if !time_ok {
return Err(SignatureInternalError::Time(cert.authority().to_owned()));
}
let pk_private = cert
.secret_key()
.as_ref()
.ok_or(SignatureInternalError::MissingSecretKey)?;
let h_vec = vec![
HashableMessage::Hashed(
message
.recursive_hash()
.map_err(|e| SignatureInternalError::HashMessage { source: e })?,
),
HashableMessage::Hashed(
additional_context
.recursive_hash()
.map_err(|e| SignatureInternalError::HashAddContent { source: e })?,
),
];
let h = HashableMessage::from(h_vec)
.recursive_hash()
.map_err(|e| SignatureInternalError::HashH { source: e })?;
openssl_sign(pk_private, &h).map_err(|e| SignatureInternalError::Sign { source: e })
}
#[cfg(test)]
mod test {
use super::*;
use std::path::{Path, PathBuf};
const VERIFIER_KEYSTORE_FILE_NAME: &str = "public_keys_keystore_verifier.p12";
const VERIFIER_PASSWORD_FILE_NAME: &str = "public_keys_keystore_verifier_pw.txt";
const CANTON_KEYSTORE_FILE_NAME: &str = "signing_keystore_canton.p12";
const CANTON_PASSWORD_FILE_NAME: &str = "signing_pw_canton.txt";
fn get_location() -> PathBuf {
Path::new("./").join("test_data").join("direct-trust")
}
fn get_verifier_keystore() -> Keystore {
Keystore::from_pkcs12(
&get_location().join(Path::new(VERIFIER_KEYSTORE_FILE_NAME)),
&get_location().join(Path::new(VERIFIER_PASSWORD_FILE_NAME)),
)
.unwrap()
}
fn get_canton_keystore() -> Keystore {
Keystore::from_pkcs12(
&get_location().join(Path::new(CANTON_KEYSTORE_FILE_NAME)),
&get_location().join(Path::new(CANTON_PASSWORD_FILE_NAME)),
)
.unwrap()
}
#[test]
fn test_create_pkcs12() {
let m = HashableMessage::from("test");
let s = HashableMessage::from("addtional context");
let v_ks = get_verifier_keystore();
let c_ks = get_canton_keystore();
let signature_res = sign(&c_ks, &m, &s);
assert!(signature_res.is_ok());
let signature = signature_res.unwrap();
let verify_res = verify_signature(&v_ks, "canton", &m, &s, &signature);
assert!(verify_res.is_ok());
assert!(verify_res.unwrap());
}
}