osslsigncode 0.1.0

In-process Rust bindings for the vendored osslsigncode Authenticode implementation
//! Credentials and zeroizing secrets for signing jobs.
//!
//! ```
//! use osslsigncode::{Credential, Secret};
//! use std::path::PathBuf;
//!
//! let secret = Secret::value("hunter2");
//! assert!(format!("{secret:?}").contains("redacted"));
//!
//! let pkcs12 = Credential::pkcs12("publisher.p12", Secret::file("pass.txt"));
//! let chain = Credential::CertificateKey {
//!     certificates: PathBuf::from("spc.pem"),
//!     key: PathBuf::from("key.pem"),
//!     additional: Some(PathBuf::from("extra.pem")),
//!     secret: Secret::Prompt,
//! };
//! let _ = (pkcs12, chain);
//! ```

use std::path::{Path, PathBuf};

use zeroize::Zeroizing;

/// A private-key secret that is copied into OpenSSL-owned memory.
#[derive(Clone)]
pub enum Secret {
    /// Prompt on the TTY.
    Prompt,
    /// Read from a file, or `-` for stdin.
    File(PathBuf),
    /// Read from stdin.
    Stdin,
    /// In-memory password. Zeroized on drop; also wiped by native `free_options`.
    Value(Zeroizing<String>),
}

impl std::fmt::Debug for Secret {
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Prompt => formatter.write_str("Prompt"),
            Self::File(path) => formatter.debug_tuple("File").field(path).finish(),
            Self::Stdin => formatter.write_str("Stdin"),
            Self::Value(_) => formatter.write_str("Value([redacted])"),
        }
    }
}

impl Secret {
    /// In-memory password.
    pub fn value(value: impl Into<String>) -> Self {
        Self::Value(Zeroizing::new(value.into()))
    }

    /// Password file, or `-` for stdin.
    pub fn file(path: impl Into<PathBuf>) -> Self {
        Self::File(path.into())
    }
}

/// How the signer authenticates.
#[derive(Clone, Debug)]
pub enum Credential {
    /// PKCS#12 container.
    Pkcs12 { path: PathBuf, secret: Secret },
    /// SPC/PEM certificates plus a key file or URI.
    CertificateKey {
        certificates: PathBuf,
        key: PathBuf,
        additional: Option<PathBuf>,
        secret: Secret,
    },
    /// PKCS#11 module / engine / provider.
    Pkcs11(Pkcs11),
}

impl Credential {
    /// PKCS#12 credential.
    pub fn pkcs12(path: impl AsRef<Path>, secret: Secret) -> Self {
        Self::Pkcs12 {
            path: path.as_ref().to_path_buf(),
            secret,
        }
    }

    /// Certificate chain plus private key.
    pub fn certificate_key(
        certificates: impl AsRef<Path>,
        key: impl AsRef<Path>,
        secret: Secret,
    ) -> Self {
        Self::CertificateKey {
            certificates: certificates.as_ref().to_path_buf(),
            key: key.as_ref().to_path_buf(),
            additional: None,
            secret,
        }
    }
}

/// PKCS#11 / engine / provider settings.
#[derive(Clone, Debug)]
pub struct Pkcs11 {
    pub module: PathBuf,
    pub cert: Option<String>,
    pub engine: Option<String>,
    pub provider: Option<String>,
    pub login: bool,
    pub engine_ctrls: Vec<String>,
    pub secret: Secret,
}

impl Pkcs11 {
    /// PKCS#11 module with no extra URIs.
    pub fn module(path: impl AsRef<Path>, secret: Secret) -> Self {
        Self {
            module: path.as_ref().to_path_buf(),
            cert: None,
            engine: None,
            provider: None,
            login: false,
            engine_ctrls: Vec::new(),
            secret,
        }
    }
}