#![doc = include_str!("../README.md")]
mod constants;
use blake2::Digest;
use std::io::Read;
use constants::*;
mod errors;
pub use errors::*;
pub use public_key::PublicKeyBox;
use public_key::{PublicKey, RawPk};
pub use secret_key::SecretKeyBox;
use signature::Signature;
mod keypair;
pub use keypair::KeyPairBox;
mod public_key;
mod secret_key;
mod signature;
pub use signature::SignatureBox;
use crate::public_key::verify_prehashed;
use crate::util::validate_comment;
mod util;
fn prehash<R>(data_reader: &mut R) -> Result<Vec<u8>>
where
R: Read,
{
let mut hash = blake2::Blake2b512::new();
let mut buf = [0; 2048];
loop {
let n = data_reader.read(&mut buf)?;
if n == 0 {
break;
}
hash.update(&buf[..n]);
}
Ok(hash.finalize().to_vec())
}
pub fn pub_key_from_str(s: &str) -> Result<PublicKeyBox<'_>> {
PublicKeyBox::from_str(s)
}
pub fn sec_key_from_str(s: &str) -> Result<SecretKeyBox<'_>> {
SecretKeyBox::from_str(s)
}
pub fn pub_key_from_sec_key<'s>(
sec_key: &SecretKeyBox<'s>,
password: Option<&[u8]>,
) -> Result<PublicKeyBox<'s>> {
let keynum_sk = sec_key.xor_keynum_sk(password)?;
PublicKeyBox::new(
None,
PublicKey::new(
sec_key.sig_alg(),
keynum_sk.key_id,
RawPk(keynum_sk.pub_key),
),
)
}
pub fn sign<'a, R>(
pk: Option<&PublicKeyBox>,
sk: &SecretKeyBox,
password: Option<&[u8]>,
mut data_reader: R,
trusted_comment: Option<&'a str>,
untrusted_comment: Option<&'a str>,
) -> Result<SignatureBox<'a>>
where
R: Read,
{
validate_comment(trusted_comment, ErrorKind::SignatureError)?;
validate_comment(untrusted_comment, ErrorKind::SignatureError)?;
let prehashed = prehash(&mut data_reader)?;
let sig = sk.sign(&prehashed, password)?;
let mut global_data = sig.to_bytes().to_vec();
global_data.extend_from_slice(trusted_comment.unwrap_or("").as_bytes());
let global_sig = sk.sign(&global_data, password)?;
let keynum_sk = sk.xor_keynum_sk(password)?;
let signature = Signature::new(SIGALG_PREHASHED, keynum_sk.key_id, sig, global_sig);
let sig_box = SignatureBox::new(untrusted_comment, trusted_comment, signature)?;
if let Some(pk) = pk {
verify_prehashed(pk, &sig_box, &prehashed)?;
}
Ok(sig_box)
}
pub fn verify<R>(
pk: &PublicKeyBox,
signature_box: &SignatureBox,
mut data_reader: R,
) -> Result<bool>
where
R: Read,
{
let prehashed = prehash(&mut data_reader)?;
verify_prehashed(pk, signature_box, &prehashed)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let KeyPairBox {
public_key_box,
secret_key_box,
} = KeyPairBox::generate(
Some(b"password"),
Some("pk untrusted comment"),
Some("sk untrusted comment"),
)
.unwrap();
let msg = "test";
let sig_box = sign(
Some(&public_key_box),
&secret_key_box,
Some(b"password"),
msg.as_bytes(),
Some("trusted comment"),
Some("untrusted comment"),
)
.unwrap();
let v = verify(&public_key_box, &sig_box, msg.as_bytes()).unwrap();
assert!(v);
}
#[test]
fn test_sign_rejects_comment_control_characters() {
let KeyPairBox {
public_key_box,
secret_key_box,
} = KeyPairBox::generate(Some(b"password"), None, None).unwrap();
assert!(sign(
Some(&public_key_box),
&secret_key_box,
Some(b"password"),
"test".as_bytes(),
Some("trusted\ncomment"),
Some("untrusted comment"),
)
.is_err());
assert!(sign(
Some(&public_key_box),
&secret_key_box,
Some(b"password"),
"test".as_bytes(),
Some("trusted comment"),
Some("untrusted\0comment"),
)
.is_err());
}
}