use std::io::Cursor;
use std::path::Path;
use pgp::composed::{
CleartextSignedMessage, Deserializable, DetachedSignature, Message, SignedPublicKey,
};
use crate::error::{Error, Result};
use crate::internal::{is_primary_key_valid_for_verification, is_subkey_revoked, parse_public_key};
pub fn verify_bytes(signer_cert: &[u8], signed_message: &[u8]) -> Result<bool> {
let public_key = parse_public_key(signer_cert)?;
if let Ok(result) = verify_cleartext(&public_key, signed_message) {
return Ok(result);
}
verify_inline_signed(&public_key, signed_message)
}
pub fn verify_and_extract_bytes(signer_cert: &[u8], signed_message: &[u8]) -> Result<Vec<u8>> {
let public_key = parse_public_key(signer_cert)?;
if let Some(content) = extract_cleartext(&public_key, signed_message)? {
return Ok(content);
}
extract_inline_signed(&public_key, signed_message)
}
pub fn verify_bytes_detached(signer_cert: &[u8], data: &[u8], signature: &[u8]) -> Result<bool> {
let public_key = parse_public_key(signer_cert)?;
let sig = match DetachedSignature::from_armor_single(Cursor::new(signature)) {
Ok((result, _headers)) => result,
Err(_) => {
match DetachedSignature::from_bytes(Cursor::new(signature)) {
Ok(result) => result,
Err(_) => return Ok(false),
}
}
};
if is_primary_key_valid_for_verification(&public_key)
&& sig.verify(&public_key.primary_key, data).is_ok()
{
return Ok(true);
}
for subkey in &public_key.public_subkeys {
if !is_subkey_revoked(subkey) && sig.verify(&subkey.key, data).is_ok() {
return Ok(true);
}
}
Ok(false)
}
pub fn verify_file(signer_cert: &[u8], signed_file: impl AsRef<Path>) -> Result<bool> {
let signed_message = std::fs::read(signed_file.as_ref())?;
verify_bytes(signer_cert, &signed_message)
}
pub fn verify_and_extract_file(
signer_cert: &[u8],
signed_file: impl AsRef<Path>,
output: impl AsRef<Path>,
) -> Result<()> {
let signed_message = std::fs::read(signed_file.as_ref())?;
let content = verify_and_extract_bytes(signer_cert, &signed_message)?;
std::fs::write(output.as_ref(), content)?;
Ok(())
}
pub fn verify_file_detached(
signer_cert: &[u8],
file: impl AsRef<Path>,
signature: &[u8],
) -> Result<bool> {
let data = std::fs::read(file.as_ref())?;
verify_bytes_detached(signer_cert, &data, signature)
}
fn verify_cleartext(public_key: &SignedPublicKey, signed_message: &[u8]) -> Result<bool> {
let text = String::from_utf8_lossy(signed_message);
let (msg, _) =
CleartextSignedMessage::from_string(&text).map_err(|e| Error::Parse(e.to_string()))?;
if is_primary_key_valid_for_verification(public_key)
&& msg.verify(&public_key.primary_key).is_ok()
{
return Ok(true);
}
for subkey in &public_key.public_subkeys {
if !is_subkey_revoked(subkey) && msg.verify(&subkey.key).is_ok() {
return Ok(true);
}
}
Ok(false)
}
fn extract_cleartext(
public_key: &SignedPublicKey,
signed_message: &[u8],
) -> Result<Option<Vec<u8>>> {
let text = match String::from_utf8(signed_message.to_vec()) {
Ok(t) => t,
Err(_) => return Ok(None),
};
let (msg, _) = match CleartextSignedMessage::from_string(&text) {
Ok(result) => result,
Err(_) => return Ok(None),
};
if is_primary_key_valid_for_verification(public_key)
&& msg.verify(&public_key.primary_key).is_ok()
{
let content = normalize_line_endings(&msg.signed_text());
return Ok(Some(content));
}
for subkey in &public_key.public_subkeys {
if !is_subkey_revoked(subkey) && msg.verify(&subkey.key).is_ok() {
let content = normalize_line_endings(&msg.signed_text());
return Ok(Some(content));
}
}
Ok(None)
}
fn normalize_line_endings(text: &str) -> Vec<u8> {
text.replace("\r\n", "\n").into_bytes()
}
fn verify_inline_signed(public_key: &SignedPublicKey, signed_message: &[u8]) -> Result<bool> {
let mut message = match Message::from_armor(Cursor::new(signed_message)) {
Ok((msg, _headers)) => msg,
Err(_) => Message::from_bytes(signed_message).map_err(|e| Error::Parse(e.to_string()))?,
};
if message.is_compressed() {
message = message
.decompress()
.map_err(|e| Error::Parse(e.to_string()))?;
}
let _ = message
.as_data_vec()
.map_err(|e| Error::Parse(e.to_string()))?;
if is_primary_key_valid_for_verification(public_key)
&& message.verify(&public_key.primary_key).is_ok()
{
return Ok(true);
}
for subkey in &public_key.public_subkeys {
if !is_subkey_revoked(subkey) && message.verify(&subkey.key).is_ok() {
return Ok(true);
}
}
Ok(false)
}
fn extract_inline_signed(public_key: &SignedPublicKey, signed_message: &[u8]) -> Result<Vec<u8>> {
let mut message = match Message::from_armor(Cursor::new(signed_message)) {
Ok((msg, _headers)) => msg,
Err(_) => Message::from_bytes(signed_message).map_err(|e| Error::Parse(e.to_string()))?,
};
if message.is_compressed() {
message = message
.decompress()
.map_err(|e| Error::Parse(e.to_string()))?;
}
let content = message
.as_data_vec()
.map_err(|e| Error::Parse(e.to_string()))?;
if is_primary_key_valid_for_verification(public_key)
&& message.verify(&public_key.primary_key).is_ok()
{
return Ok(content);
}
for subkey in &public_key.public_subkeys {
if !is_subkey_revoked(subkey) && message.verify(&subkey.key).is_ok() {
return Ok(content);
}
}
Err(Error::VerificationFailed)
}
#[cfg(test)]
mod tests {
}