use std::fs::File;
use pgp::{
composed::{
CleartextSignedMessage, EncryptionCaps, KeyType, Message, MessageBuilder,
SecretKeyParamsBuilder, SignedPublicKey, SignedSecretKey,
},
crypto::{
aead::{AeadAlgorithm, ChunkSize},
ecc_curve::ECCCurve,
sym::SymmetricKeyAlgorithm,
},
types::{KeyDetails, KeyVersion},
};
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
const MSG: &str = "hello world\n";
const CASES_9580: &[&str] = &[
("tests/rfc9580/v6-25519-annex-a-4"), ("tests/rfc9580/v6-ed448-x448"), ("tests/rfc9580/v6-ed25519-x448"), ("tests/rfc9580/v6-rsa"), ("tests/rfc9580/v6-nistp"), ("tests/rfc9580/v4-ed25519-x25519"), ];
const CASES_PRE_9580: &[&str] = &[
("tests/rfc9580/v4-rsa"), ("tests/rfc9580/v4-nistp"), ("tests/rfc9580/v4-legacy"), ];
fn load_ssk(filename: &str) -> SignedSecretKey {
let (mut iter, _) =
pgp::composed::PublicOrSecret::from_reader_many(File::open(filename).unwrap()).expect("ok");
let pos = iter.next().expect("some").expect("ok");
pos.try_into().unwrap()
}
fn try_decrypt(keyfile: &str, msg_file: &str) {
let ssk = load_ssk(keyfile);
let (message, _) = Message::from_armor_file(msg_file).expect("ok");
let mut dec = message.decrypt(&"".into(), &ssk).expect("decrypt");
let decrypted = dec.as_data_string().unwrap();
assert_eq!(&decrypted, MSG);
}
#[test]
fn rfc9580_decrypt_seipdv1_msg() {
for case in CASES_9580 {
try_decrypt(
&format!("{case}/tsk.asc"),
&format!("{case}/enc-seipdv1.msg"),
);
}
}
#[test]
fn rfc9580_decrypt_seipdv2_msg() {
for case in CASES_9580.iter().chain(CASES_PRE_9580.iter()) {
try_decrypt(
&format!("{case}/tsk.asc"),
&format!("{case}/enc-seipdv2.msg"),
);
}
}
#[test]
fn rfc9580_verify_csf() {
for case in CASES_9580 {
let keyfile = format!("{case}/tsk.asc");
let csffile = format!("{case}/csf.msg");
let ssk = load_ssk(&keyfile);
let spk = SignedPublicKey::from(ssk.clone());
spk.verify_bindings().expect("SignedPublicKey::verify");
let (csf, _) =
CleartextSignedMessage::from_armor(File::open(csffile).unwrap()).expect("csf loaded");
csf.verify(&spk).expect("verify ok");
}
}
#[test]
fn rfc9580_seipdv1_roundtrip() {
let mut rng = ChaCha8Rng::seed_from_u64(0);
for case in CASES_9580 {
let keyfile = format!("{case}/tsk.asc");
let ssk = load_ssk(&keyfile);
let spk = SignedPublicKey::from(ssk.clone());
let enc_subkey = &spk.public_subkeys.first().unwrap().key;
let mut builder = MessageBuilder::from_bytes("", MSG.as_bytes())
.seipd_v1(&mut rng, SymmetricKeyAlgorithm::AES256);
builder.encrypt_to_key(&mut rng, &enc_subkey).unwrap();
let enc = builder.to_vec(&mut rng).unwrap();
let msg = Message::from_bytes(&enc[..]).unwrap();
let mut dec = msg.decrypt(&"".into(), &ssk).expect("decrypt");
let data = dec.as_data_string().unwrap();
assert_eq!(data, MSG);
}
}
#[test]
fn rfc9580_seipdv2_roundtrip() {
let mut rng = ChaCha8Rng::seed_from_u64(0);
for case in CASES_9580.iter().chain(CASES_PRE_9580.iter()) {
let keyfile = format!("{case}/tsk.asc");
let ssk = load_ssk(&keyfile);
let spk = SignedPublicKey::from(ssk.clone());
let enc_subkey = &spk.public_subkeys.first().unwrap().key;
let mut builder = MessageBuilder::from_bytes("", MSG.as_bytes()).seipd_v2(
&mut rng,
SymmetricKeyAlgorithm::AES256,
AeadAlgorithm::Ocb,
ChunkSize::default(),
);
builder.encrypt_to_key(&mut rng, &enc_subkey).unwrap();
let enc = builder.to_vec(&mut rng).unwrap();
let msg = Message::from_bytes(&enc[..]).unwrap();
let mut dec = msg.decrypt(&"".into(), &ssk).expect("decrypt");
let data = dec.as_data_string().unwrap();
assert_eq!(data, MSG);
}
}
#[test]
fn rfc9580_roundtrip_csf() {
let mut rng = ChaCha8Rng::seed_from_u64(0);
for case in CASES_9580 {
let keyfile = format!("{case}/tsk.asc");
let ssk = load_ssk(&keyfile);
let spk = SignedPublicKey::from(ssk.clone());
let csf = CleartextSignedMessage::sign(&mut rng, MSG, &*ssk, &"".into()).expect("sign");
csf.verify(&spk).expect("verify");
}
}
#[test]
fn rfc9580_roundtrip_sign_verify_inline_msg() {
let mut rng = ChaCha8Rng::seed_from_u64(0);
for case in CASES_9580 {
let keyfile = format!("{case}/tsk.asc");
let ssk = load_ssk(&keyfile);
let spk = SignedPublicKey::from(ssk.clone());
let mut builder = MessageBuilder::from_bytes("", MSG.as_bytes());
builder.sign(
&*ssk,
"".into(),
ssk.public_key().public_params().hash_alg(),
);
let msg = builder.to_vec(&mut rng).unwrap();
let mut msg = Message::from_bytes(&msg[..]).unwrap();
msg.verify_read(&spk).expect("verify");
}
}
#[test]
fn rfc9580_legacy_25519_illegal_in_v6() {
let mut rng = ChaCha8Rng::seed_from_u64(0);
let key_file = File::open("tests/rfc9580/v6-legacy_illegal/tsk.asc").unwrap();
let (mut iter, _) = pgp::composed::PublicOrSecret::from_reader_many(key_file).expect("ok");
let res = iter.next().expect("result");
assert!(res.is_err());
let mut key_params = SecretKeyParamsBuilder::default();
key_params
.key_type(KeyType::Ed25519Legacy)
.version(KeyVersion::V6)
.can_sign(true)
.primary_user_id("Me <me@example.com>".into());
let secret_key_params = key_params
.build()
.expect("Must be able to create secret key params");
let res = secret_key_params.generate(&mut rng);
assert!(res.is_err());
let mut key_params = SecretKeyParamsBuilder::default();
key_params
.key_type(KeyType::ECDH(ECCCurve::Curve25519))
.version(KeyVersion::V6)
.can_encrypt(EncryptionCaps::All)
.primary_user_id("Me <me@example.com>".into());
let secret_key_params = key_params
.build()
.expect("Must be able to create secret key params");
let res = secret_key_params.generate(&mut rng);
assert!(res.is_err());
}