use std::time::Duration;
use std::time::SystemTime;
use sequoia_openpgp::cert::CertBuilder;
use sequoia_openpgp::packet::UserID;
use nettle::curve25519::private_key as cv25519_private_key;
use nettle::ed25519::private_key as ed25519_private_key;
use nettle::random::Yarrow;
use nettle::rsa::generate_keypair as rsa_generate_keypair;
use sequoia_openpgp::cert::CertRevocationBuilder;
use sequoia_openpgp::crypto::KeyPair;
use sequoia_openpgp::packet::key::{Key4, PrimaryRole, SecretParts, SubordinateRole};
use sequoia_openpgp::packet::signature::SignatureBuilder;
use sequoia_openpgp::packet::{signature, Key, Signature};
use sequoia_openpgp::types::RevocationKey;
use sequoia_openpgp::types::{
Features, HashAlgorithm, KeyFlags, ReasonForRevocation, SignatureType, SymmetricAlgorithm,
};
use sequoia_openpgp::{Cert, Packet};
use sha2::{Digest, Sha512};
pub fn get_secret_phrase(secret_phrase: &Option<String>) -> String {
if let Some(val) = secret_phrase {
val.to_owned()
} else {
"".to_string()
}
}
pub fn get_key_creation_time() -> SystemTime {
SystemTime::now()
}
pub fn get_key_expiration_time(weeks_subkeys_exp_in: &Option<u64>) -> Option<SystemTime> {
weeks_subkeys_exp_in.map(|val| SystemTime::now() + Duration::new(val * 7 * 24 * 60 * 60, 0))
}
pub fn cert_and_revoc(
_user_id: &str,
email: &str,
secret_phrase: &Option<String>,
weeks_subkeys_exp_in: &Option<u64>,
) -> (Cert, Signature) {
let (mut pgp_packets, sig) = signed_primary_key(secret_phrase);
let user_id = UserID::from_address(_user_id, "unlimited", email).unwrap();
pgp_packets.push(sig.clone().into());
pgp_packets.push(user_id.clone().into());
let mut pgp_cert = Cert::from_packets(pgp_packets.into_iter()).unwrap();
let (bob, _) = CertBuilder::new().add_userid(_user_id).generate().unwrap();
let pk_algo = bob.primary_key().key().pk_algo(); let fingerprint = bob.fingerprint();
let revocation_key = RevocationKey::new(pk_algo, fingerprint, false);
let sig = signature::SignatureBuilder::from(sig)
.set_revocation_key(revocation_key)
.unwrap();
pgp_cert = signed_subkeys(pgp_cert, secret_phrase, weeks_subkeys_exp_in);
pgp_cert = sign_cert_uid(pgp_cert, sig, &user_id);
let revocation = CertRevocationBuilder::new()
.set_reason_for_revocation(ReasonForRevocation::Unspecified, b"Unspecified")
.unwrap()
.build(
&mut pgp_cert
.primary_key()
.key()
.clone()
.parts_into_secret()
.unwrap()
.into_keypair()
.unwrap(),
&pgp_cert,
None,
)
.unwrap();
(pgp_cert, revocation)
}
fn signed_subkeys(
mut cert: Cert,
secret_phrase: &Option<String>,
weeks_subkeys_exp_in: &Option<u64>,
) -> Cert {
let primary_key: Key<SecretParts, PrimaryRole> = cert
.primary_key()
.key()
.clone()
.parts_into_secret()
.unwrap();
let mut primary_keypair: KeyPair = primary_key.clone().into_keypair().unwrap();
let salts = ["sign", "authenticate", "encrypt_ed", "encrypt_rsa"];
for (i, salt) in salts.iter().enumerate() {
let mut subkey = ed25519_key_from_seed(salt.to_owned(), secret_phrase);
let key_flags: KeyFlags;
match i {
0 => key_flags = KeyFlags::empty().set_signing(),
1 => key_flags = KeyFlags::empty().set_authentication(),
2 => {
subkey = cv25519_key_from_seed(salt.to_owned(), secret_phrase);
key_flags = KeyFlags::empty()
.set_transport_encryption()
.set_storage_encryption();
}
3 => {
subkey = rsa_key_from_seed(salt.to_owned(), secret_phrase);
key_flags = KeyFlags::empty()
.set_transport_encryption()
.set_storage_encryption();
}
_ => key_flags = KeyFlags::empty(),
}
let mut subkey_kp = subkey
.clone()
.parts_into_secret()
.unwrap()
.into_keypair()
.unwrap();
let mut subkey_sb = signature::SignatureBuilder::new(SignatureType::SubkeyBinding)
.set_hash_algo(HashAlgorithm::SHA512)
.set_features(Features::sequoia())
.unwrap()
.set_key_flags(key_flags.clone())
.unwrap()
.set_key_expiration_time(&subkey, get_key_expiration_time(weeks_subkeys_exp_in))
.unwrap();
if key_flags == KeyFlags::empty().set_signing() {
let backsig = signature::SignatureBuilder::new(SignatureType::PrimaryKeyBinding)
.set_hash_algo(HashAlgorithm::SHA512)
.sign_primary_key_binding(&mut subkey_kp, &primary_key, &subkey)
.unwrap();
subkey_sb = subkey_sb.set_embedded_signature(backsig).unwrap();
}
let subkey_s = subkey_sb
.sign_subkey_binding(&mut primary_keypair, None, &subkey)
.unwrap();
let (c, _) = cert
.insert_packets(vec![Packet::from(subkey), Packet::from(subkey_s)])
.unwrap();
cert = c
}
cert
}
fn ed25519_key_from_seed(
salt: &str,
secret_phrase: &Option<String>,
) -> Key<SecretParts, SubordinateRole> {
let mut secret_phrase: String = get_secret_phrase(secret_phrase);
secret_phrase.push_str(salt);
let key_from_seed = ed25519_private_key(&mut Yarrow::from_seed(&Sha512::digest(
secret_phrase.as_bytes(),
)));
Key::from(
Key4::<SecretParts, SubordinateRole>::import_secret_ed25519(
&key_from_seed,
get_key_creation_time(),
)
.unwrap(),
)
}
fn cv25519_key_from_seed(
salt: &str,
secret_phrase: &Option<String>,
) -> Key<SecretParts, SubordinateRole> {
let mut secret_phrase: String = get_secret_phrase(secret_phrase);
secret_phrase.push_str(salt);
let key_from_seed = cv25519_private_key(&mut Yarrow::from_seed(&Sha512::digest(
secret_phrase.as_bytes(),
)));
Key::from(
Key4::<SecretParts, SubordinateRole>::import_secret_cv25519(
&key_from_seed,
None,
None,
get_key_creation_time(),
)
.unwrap(),
)
}
fn rsa_key_from_seed(
salt: &str,
secret_phrase: &Option<String>,
) -> Key<SecretParts, SubordinateRole> {
let mut secret_phrase: String = get_secret_phrase(secret_phrase);
secret_phrase.push_str(salt);
let (_rsa_public, rsa_secret) = rsa_generate_keypair(
&mut Yarrow::from_seed(&Sha512::digest(secret_phrase.as_bytes())),
4096,
)
.unwrap();
Key::from(
Key4::<SecretParts, SubordinateRole>::import_secret_rsa(
&rsa_secret.d(),
&rsa_secret.primes().0,
&rsa_secret.primes().1,
get_key_creation_time(),
)
.unwrap(),
)
}
fn signed_primary_key(secret_phrase: &Option<String>) -> (Vec<Packet>, Signature) {
let mut sequoia_packets: Vec<Packet> = vec![];
let secret_phrase: String = get_secret_phrase(secret_phrase);
let key_from_seed = ed25519_private_key(&mut Yarrow::from_seed(&Sha512::digest(
secret_phrase.as_bytes(),
)));
let primary_key = Key::from(
Key4::<SecretParts, PrimaryRole>::import_secret_ed25519(
&key_from_seed,
get_key_creation_time(),
)
.unwrap(),
);
let mut primary_keypair = primary_key.clone().into_keypair().unwrap();
let primary_key_sb = signature::SignatureBuilder::new(SignatureType::DirectKey)
.set_hash_algo(HashAlgorithm::SHA512)
.set_features(Features::sequoia())
.unwrap()
.set_key_flags(KeyFlags::empty().set_certification().set_signing())
.unwrap()
.set_preferred_hash_algorithms(vec![HashAlgorithm::SHA512, HashAlgorithm::SHA256])
.unwrap()
.set_preferred_symmetric_algorithms(vec![
SymmetricAlgorithm::AES256,
SymmetricAlgorithm::AES128,
])
.unwrap();
sequoia_packets.push(Packet::from(primary_key.clone()));
(
sequoia_packets,
primary_key_sb
.sign_direct_key(&mut primary_keypair, primary_key.parts_as_public())
.unwrap(),
)
}
fn sign_cert_uid(cert: Cert, cert_sb: SignatureBuilder, user_id: &UserID) -> Cert {
let current_time = SystemTime::now();
let mut primary_keypair: KeyPair = cert
.primary_key()
.key()
.clone()
.parts_into_secret()
.unwrap()
.into_keypair()
.unwrap();
let uid_sb = cert_sb
.set_signature_creation_time(current_time)
.unwrap()
.set_type(SignatureType::PositiveCertification)
.set_hash_algo(HashAlgorithm::SHA512)
.set_primary_userid(true)
.unwrap();
let uid_s = user_id.bind(&mut primary_keypair, &cert, uid_sb).unwrap();
let (c, _) = cert.insert_packets(vec![Packet::Signature(uid_s)]).unwrap();
c
}