use std::io::Cursor;
use std::path::Path;
use pgp::composed::{CleartextSignedMessage, DetachedSignature, MessageBuilder, SignedSecretKey};
use pgp::crypto::hash::HashAlgorithm;
use pgp::types::{KeyDetails, Password, PublicParams};
use rand::thread_rng;
use crate::error::{Error, Result};
use crate::internal::parse_secret_key;
fn select_hash_for_key(secret_key: &SignedSecretKey) -> HashAlgorithm {
let params = secret_key.primary_key.public_params();
match params {
PublicParams::ECDSA(ecdsa) => {
use pgp::types::EcdsaPublicParams;
match ecdsa {
EcdsaPublicParams::P256 { .. } => HashAlgorithm::Sha256,
EcdsaPublicParams::P384 { .. } => HashAlgorithm::Sha384,
EcdsaPublicParams::P521 { .. } => HashAlgorithm::Sha512,
_ => HashAlgorithm::Sha256,
}
}
PublicParams::EdDSALegacy(_) | PublicParams::Ed25519(_) => HashAlgorithm::Sha256,
PublicParams::RSA(_) => HashAlgorithm::Sha256,
_ => HashAlgorithm::Sha256,
}
}
pub fn sign_bytes(secret_cert: &[u8], data: &[u8], password: &str) -> Result<Vec<u8>> {
sign_bytes_internal(secret_cert, data, password, false)
}
pub fn sign_bytes_cleartext(secret_cert: &[u8], data: &[u8], password: &str) -> Result<Vec<u8>> {
sign_bytes_internal(secret_cert, data, password, true)
}
pub fn sign_bytes_detached(secret_cert: &[u8], data: &[u8], password: &str) -> Result<String> {
let secret_key = parse_secret_key(secret_cert)?;
let password: Password = password.into();
let mut rng = thread_rng();
let hash_alg = select_hash_for_key(&secret_key);
let signature = DetachedSignature::sign_binary_data(
&mut rng,
&secret_key.primary_key,
&password,
hash_alg,
Cursor::new(data),
)
.map_err(|e| Error::Crypto(e.to_string()))?;
signature
.to_armored_string(None.into())
.map_err(|e| Error::Crypto(e.to_string()))
}
pub fn sign_file(
secret_cert: &[u8],
input: impl AsRef<Path>,
output: impl AsRef<Path>,
password: &str,
) -> Result<()> {
let data = std::fs::read(input.as_ref())?;
let signed = sign_bytes(secret_cert, &data, password)?;
std::fs::write(output.as_ref(), signed)?;
Ok(())
}
pub fn sign_file_cleartext(
secret_cert: &[u8],
input: impl AsRef<Path>,
output: impl AsRef<Path>,
password: &str,
) -> Result<()> {
let data = std::fs::read(input.as_ref())?;
let signed = sign_bytes_cleartext(secret_cert, &data, password)?;
std::fs::write(output.as_ref(), signed)?;
Ok(())
}
pub fn sign_file_detached(
secret_cert: &[u8],
input: impl AsRef<Path>,
password: &str,
) -> Result<String> {
let data = std::fs::read(input.as_ref())?;
sign_bytes_detached(secret_cert, &data, password)
}
fn sign_bytes_internal(
secret_cert: &[u8],
data: &[u8],
password: &str,
cleartext: bool,
) -> Result<Vec<u8>> {
let secret_key = parse_secret_key(secret_cert)?;
let password_obj: Password = password.into();
let hash_alg = select_hash_for_key(&secret_key);
let mut rng = thread_rng();
if cleartext {
let text = String::from_utf8_lossy(data);
let csf = CleartextSignedMessage::sign(&mut rng, &text, &secret_key.primary_key, &password_obj)
.map_err(|e| Error::Crypto(e.to_string()))?;
csf.to_armored_string(None.into())
.map(|s| s.into_bytes())
.map_err(|e| Error::Crypto(e.to_string()))
} else {
let mut builder = MessageBuilder::from_bytes("", data.to_vec());
builder.sign(&secret_key.primary_key, password_obj, hash_alg);
builder
.to_armored_string(&mut rng, None.into())
.map(|s| s.into_bytes())
.map_err(|e| Error::Crypto(e.to_string()))
}
}
#[cfg(test)]
mod tests {
}