rust_ev_crypto_primitives 0.8.4

Crypto Primitives necessary for E-Voting Applications.
Documentation
// Copyright © 2023 Denis Morel

// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License and
// a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.

//! Module implementing the direct trust

use super::basic_crypto_functions::{
    BasisCryptoError, CertificateExtension, Keystore as SslKeystore, SigningCertificate,
};
use std::{
    fs, io,
    path::{Path, PathBuf},
};
use thiserror::Error;

#[derive(Error, Debug)]
#[error(transparent)]
/// Error in direct_trust
pub struct DirectTrustError(#[from] DirectTrustErrorRepr);

#[derive(Error, Debug)]
enum DirectTrustErrorRepr {
    #[error("Error reading password file {path}")]
    ReadPassword { path: PathBuf, source: io::Error },
    #[error("Error creating the keystore from the file {path}")]
    KeystoreFromFile {
        path: PathBuf,
        source: BasisCryptoError,
    },
    #[error("Error creating the keystore from the directory {path}")]
    KeystoreFromDir {
        path: PathBuf,
        source: BasisCryptoError,
    },
    #[error("Error getting the public certificate {authority} from the keystore")]
    PublicCertificate {
        authority: String,
        source: BasisCryptoError,
    },
    #[error("Error getting the private certificate from the keystore")]
    PrivateCertificate { source: BasisCryptoError },
}

/// Struct representing a direct trust
pub struct Keystore {
    keystore: SslKeystore,
}

impl Keystore {
    /// Read a direct trust keystore from pkcs12
    pub fn from_pkcs12(
        keystore_path: &Path,
        password_file_path: &Path,
    ) -> Result<Self, DirectTrustError> {
        let pwd = fs::read_to_string(password_file_path).map_err(|e| {
            DirectTrustErrorRepr::ReadPassword {
                path: password_file_path.to_path_buf(),
                source: e,
            }
        })?;
        Ok(Keystore {
            keystore: SslKeystore::from_pkcs12(keystore_path, &pwd).map_err(|e| {
                DirectTrustErrorRepr::KeystoreFromFile {
                    path: keystore_path.to_path_buf(),
                    source: e,
                }
            })?,
        })
    }

    /// Read a direct trust keystore from a directory, where the public key certificates are stored
    pub fn from_directory(
        keystore_path: &Path,
        extension: &CertificateExtension,
    ) -> Result<Self, DirectTrustError> {
        let mut ks = SslKeystore::from_directory(keystore_path).map_err(|e| {
            DirectTrustErrorRepr::KeystoreFromDir {
                path: keystore_path.to_path_buf(),
                source: e,
            }
        })?;
        ks.set_certificate_extension(extension);
        Ok(Self { keystore: ks })
    }

    /// Read the public certificate with the authority given as parameter
    pub fn public_certificate(
        &self,
        authority: &str,
    ) -> Result<DirectTrustCertificate, DirectTrustError> {
        let cert = self
            .keystore
            .get_public_certificate(&String::from(authority))
            .map_err(|e| DirectTrustErrorRepr::PublicCertificate {
                authority: authority.to_string(),
                source: e,
            })?;
        Ok(DirectTrustCertificate { cert })
    }

    /// Read the secret key and associated certificate
    ///
    /// Return an error if no secret key found in the keystore
    pub fn secret_key_certificate(&self) -> Result<DirectTrustCertificate, DirectTrustError> {
        let cert = self
            .keystore
            .get_secret_certificate()
            .map_err(|e| DirectTrustErrorRepr::PrivateCertificate { source: e })?;
        Ok(DirectTrustCertificate { cert })
    }
}

/// Struct representing a direct trust certificate
#[derive(Clone)]
pub struct DirectTrustCertificate {
    cert: SigningCertificate,
}

impl DirectTrustCertificate {
    /// Get authority of the certificate
    pub fn authority(&self) -> &str {
        self.cert.authority()
    }

    /// Get the certificate of the authority
    pub fn signing_certificate(&self) -> &SigningCertificate {
        &self.cert
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::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")
    }

    #[test]
    fn test_create_pkcs12() {
        let dt = Keystore::from_pkcs12(
            &get_location().join(Path::new(VERIFIER_KEYSTORE_FILE_NAME)),
            &get_location().join(Path::new(VERIFIER_PASSWORD_FILE_NAME)),
        )
        .unwrap();
        assert!(dt.public_certificate("canton").is_ok());
        assert!(dt.public_certificate("toto").is_err());
        assert!(dt.secret_key_certificate().is_err());
        let dt = Keystore::from_pkcs12(
            &get_location().join(Path::new(CANTON_KEYSTORE_FILE_NAME)),
            &get_location().join(Path::new(CANTON_PASSWORD_FILE_NAME)),
        )
        .unwrap();
        assert!(dt.secret_key_certificate().is_ok());
        let dt_err = Keystore::from_pkcs12(
            Path::new("./toto"),
            &get_location().join(Path::new(VERIFIER_PASSWORD_FILE_NAME)),
        );
        assert!(dt_err.is_err());
    }

    #[test]
    fn test_create_dir() {
        let dt =
            Keystore::from_directory(&get_location(), &CertificateExtension::default()).unwrap();
        //let dt = DirectTrustCertificate::new(, &CertificateAuthority::Canton);
        assert!(dt.public_certificate("canton").is_ok());
        assert!(dt.public_certificate("toto").is_err());
        let dt_err =
            Keystore::from_directory(Path::new("./toto"), &CertificateExtension::default());
        assert!(dt_err.is_err());
    }
}