use std::fmt;
use std::convert::TryInto;
use std::hash::Hasher;
#[cfg(test)]
use quickcheck::{Arbitrary, Gen};
use crate::Error;
use crate::cert::prelude::*;
use crate::crypto::{self, mem, mpi, KeyPair};
use crate::packet::prelude::*;
use crate::policy::HashAlgoSecurity;
use crate::PublicKeyAlgorithm;
use crate::seal;
use crate::SymmetricAlgorithm;
use crate::HashAlgorithm;
use crate::types::{
AEADAlgorithm,
Curve,
};
use crate::crypto::S2K;
use crate::Result;
use crate::crypto::Password;
use crate::crypto::SessionKey;
mod conversions;
mod v6;
pub use v6::Key6;
mod v4;
pub use v4::Key4;
#[non_exhaustive]
#[derive(PartialEq, Eq, Hash, Debug)]
pub enum Key<P: key::KeyParts, R: key::KeyRole> {
V4(Key4<P, R>),
V6(Key6<P, R>),
}
assert_send_and_sync!(Key<P, R> where P: key::KeyParts, R: key::KeyRole);
impl<P, R> Clone for Key<P, R>
where P: key::KeyParts, R: key::KeyRole
{
fn clone(&self) -> Self {
match self {
Key::V4(key) => Key::V4(key.clone()),
Key::V6(key) => Key::V6(key.clone()),
}
}
}
impl<P: key::KeyParts, R: key::KeyRole> fmt::Display for Key<P, R> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Key::V4(k) => k.fmt(f),
Key::V6(k) => k.fmt(f),
}
}
}
impl From<Key<key::PublicParts, key::PrimaryRole>> for Packet {
fn from(k: Key<key::PublicParts, key::PrimaryRole>) -> Self {
Packet::PublicKey(k)
}
}
impl From<Key<key::PublicParts, key::SubordinateRole>> for Packet {
fn from(k: Key<key::PublicParts, key::SubordinateRole>) -> Self {
Packet::PublicSubkey(k)
}
}
impl From<Key<key::SecretParts, key::PrimaryRole>> for Packet {
fn from(k: Key<key::SecretParts, key::PrimaryRole>) -> Self {
Packet::SecretKey(k)
}
}
impl From<Key<key::SecretParts, key::SubordinateRole>> for Packet {
fn from(k: Key<key::SecretParts, key::SubordinateRole>) -> Self {
Packet::SecretSubkey(k)
}
}
impl<R: key::KeyRole> Key<key::SecretParts, R> {
pub fn secret(&self) -> &SecretKeyMaterial {
match self {
Key::V4(k) => k.secret(),
Key::V6(k) => k.secret(),
}
}
pub fn secret_mut(&mut self) -> &mut SecretKeyMaterial {
match self {
Key::V4(k) => k.secret_mut(),
Key::V6(k) => k.secret_mut(),
}
}
pub fn into_keypair(self) -> Result<KeyPair> {
match self {
Key::V4(k) => k.into_keypair(),
Key::V6(k) => k.into_keypair(),
}
}
pub fn decrypt_secret(self, password: &Password) -> Result<Self>
{
match self {
Key::V4(k) => Ok(Key::V4(k.decrypt_secret(password)?)),
Key::V6(k) => Ok(Key::V6(k.decrypt_secret(password)?)),
}
}
pub fn encrypt_secret(self, password: &Password) -> Result<Self>
{
match self {
Key::V4(k) => Ok(Key::V4(k.encrypt_secret(password)?)),
Key::V6(k) => Ok(Key::V6(k.encrypt_secret(password)?)),
}
}
}
macro_rules! impl_common_secret_functions {
($t: path) => {
impl<R: key::KeyRole> Key<$t, R> {
pub fn take_secret(self)
-> (Key<key::PublicParts, R>,
Option<key::SecretKeyMaterial>)
{
match self {
Key::V4(k) => {
let (k, s) = k.take_secret();
(k.into(), s)
},
Key::V6(k) => {
let (k, s) = k.take_secret();
(k.into(), s)
},
}
}
pub fn add_secret(self, secret: key::SecretKeyMaterial)
-> (Key<key::SecretParts, R>,
Option<key::SecretKeyMaterial>)
{
match self {
Key::V4(k) => {
let (k, s) = k.add_secret(secret);
(k.into(), s)
},
Key::V6(k) => {
let (k, s) = k.add_secret(secret);
(k.into(), s)
},
}
}
pub fn steal_secret(&mut self) -> Option<key::SecretKeyMaterial>
{
match self {
Key::V4(k) => k.steal_secret(),
Key::V6(k) => k.steal_secret(),
}
}
}
}
}
impl_common_secret_functions!(key::PublicParts);
impl_common_secret_functions!(key::UnspecifiedParts);
impl<R: key::KeyRole> Key<key::SecretParts, R> {
pub fn take_secret(self)
-> (Key<key::PublicParts, R>, key::SecretKeyMaterial)
{
match self {
Key::V4(k) => {
let (k, s) = k.take_secret();
(k.into(), s)
},
Key::V6(k) => {
let (k, s) = k.take_secret();
(k.into(), s)
},
}
}
pub fn add_secret(self, secret: key::SecretKeyMaterial)
-> (Key<key::SecretParts, R>, key::SecretKeyMaterial)
{
match self {
Key::V4(k) => {
let (k, s) = k.add_secret(secret);
(k.into(), s)
},
Key::V6(k) => {
let (k, s) = k.add_secret(secret);
(k.into(), s)
},
}
}
}
impl<P: key::KeyParts, R: key::KeyRole> Key<P, R> {
pub fn public_cmp<PB, RB>(&self, b: &Key<PB, RB>)
-> std::cmp::Ordering
where
PB: key::KeyParts,
RB: key::KeyRole,
{
match (self, b) {
(Key::V4(a), Key::V4(b)) => a.public_cmp(b),
(Key::V6(a), Key::V6(b)) => a.public_cmp(b),
(Key::V4(_), Key::V6(_)) => std::cmp::Ordering::Less,
(Key::V6(_), Key::V4(_)) => std::cmp::Ordering::Greater,
}
}
pub fn public_eq<PB, RB>(&self, b: &Key<PB, RB>)
-> bool
where
PB: key::KeyParts,
RB: key::KeyRole,
{
self.public_cmp(b) == std::cmp::Ordering::Equal
}
pub fn public_hash<H>(&self, state: &mut H)
where
H: Hasher,
{
use std::hash::Hash;
match self {
Key::V4(k) => k.common.hash(state),
Key::V6(k) => k.common.common.hash(state),
}
self.creation_time().hash(state);
self.pk_algo().hash(state);
Hash::hash(&self.mpis(), state);
}
}
impl<P: key::KeyParts, R: key::KeyRole> Key<P, R> {
pub fn version(&self) -> u8 {
match self {
Key::V4(_) => 4,
Key::V6(_) => 6,
}
}
pub fn creation_time(&self) -> std::time::SystemTime {
match self {
Key::V4(k) => k.creation_time(),
Key::V6(k) => k.creation_time(),
}
}
pub fn set_creation_time<T>(&mut self, timestamp: T)
-> Result<std::time::SystemTime>
where
T: Into<std::time::SystemTime>,
{
match self {
Key::V4(k) => k.set_creation_time(timestamp.into()),
Key::V6(k) => k.set_creation_time(timestamp.into()),
}
}
pub fn pk_algo(&self) -> PublicKeyAlgorithm {
match self {
Key::V4(k) => k.pk_algo(),
Key::V6(k) => k.pk_algo(),
}
}
pub fn set_pk_algo(&mut self, pk_algo: PublicKeyAlgorithm)
-> PublicKeyAlgorithm
{
match self {
Key::V4(k) => k.set_pk_algo(pk_algo),
Key::V6(k) => k.set_pk_algo(pk_algo),
}
}
pub fn mpis(&self) -> &mpi::PublicKey {
match self {
Key::V4(k) => k.mpis(),
Key::V6(k) => k.mpis(),
}
}
pub fn mpis_mut(&mut self) -> &mut mpi::PublicKey {
match self {
Key::V4(k) => k.mpis_mut(),
Key::V6(k) => k.mpis_mut(),
}
}
pub fn set_mpis(&mut self, mpis: mpi::PublicKey) -> mpi::PublicKey {
match self {
Key::V4(k) => k.set_mpis(mpis),
Key::V6(k) => k.set_mpis(mpis),
}
}
pub fn has_secret(&self) -> bool {
match self {
Key::V4(k) => k.has_secret(),
Key::V6(k) => k.has_secret(),
}
}
pub fn has_unencrypted_secret(&self) -> bool {
match self {
Key::V4(k) => k.has_unencrypted_secret(),
Key::V6(k) => k.has_unencrypted_secret(),
}
}
pub fn optional_secret(&self) -> Option<&SecretKeyMaterial> {
match self {
Key::V4(k) => k.optional_secret(),
Key::V6(k) => k.optional_secret(),
}
}
pub fn key_handle(&self) -> crate::KeyHandle {
match self {
Key::V4(k) => k.key_handle(),
Key::V6(k) => k.key_handle(),
}
}
pub fn fingerprint(&self) -> crate::Fingerprint {
match self {
Key::V4(k) => k.fingerprint(),
Key::V6(k) => k.fingerprint(),
}
}
pub fn keyid(&self) -> crate::KeyID {
match self {
Key::V4(k) => k.keyid(),
Key::V6(k) => k.keyid(),
}
}
pub fn hash_algo_security(&self) -> HashAlgoSecurity {
HashAlgoSecurity::SecondPreImageResistance
}
pub(crate) fn role(&self) -> key::KeyRoleRT {
match self {
Key::V4(k) => k.role(),
Key::V6(k) => k.role(),
}
}
pub(crate) fn set_role(&mut self, role: key::KeyRoleRT) {
match self {
Key::V4(k) => k.set_role(role),
Key::V6(k) => k.set_role(role),
}
}
}
#[cfg(test)]
impl<P, R> Arbitrary for Key<P, R>
where
P: KeyParts,
R: KeyRole,
Key4<P, R>: Arbitrary,
Key6<P, R>: Arbitrary,
{
fn arbitrary(g: &mut Gen) -> Self {
if <bool>::arbitrary(g) {
Key4::arbitrary(g).into()
} else {
Key6::arbitrary(g).into()
}
}
}
pub trait KeyParts: fmt::Debug + seal::Sealed {
fn convert_key<R: KeyRole>(key: Key<UnspecifiedParts, R>)
-> Result<Key<Self, R>>
where Self: Sized;
fn convert_key_ref<R: KeyRole>(key: &Key<UnspecifiedParts, R>)
-> Result<&Key<Self, R>>
where Self: Sized;
fn convert_bundle<R: KeyRole>(bundle: KeyBundle<UnspecifiedParts, R>)
-> Result<KeyBundle<Self, R>>
where Self: Sized;
fn convert_bundle_ref<R: KeyRole>(bundle: &KeyBundle<UnspecifiedParts, R>)
-> Result<&KeyBundle<Self, R>>
where Self: Sized;
fn convert_key_amalgamation<R: KeyRole>(
ka: ComponentAmalgamation<Key<UnspecifiedParts, R>>)
-> Result<ComponentAmalgamation<Key<Self, R>>>
where Self: Sized;
fn convert_key_amalgamation_ref<'a, R: KeyRole>(
ka: &'a ComponentAmalgamation<'a, Key<UnspecifiedParts, R>>)
-> Result<&'a ComponentAmalgamation<'a, Key<Self, R>>>
where Self: Sized;
fn significant_secrets() -> bool;
}
pub trait KeyRole: fmt::Debug + seal::Sealed {
fn convert_key<P: KeyParts>(key: Key<P, UnspecifiedRole>)
-> Key<P, Self>
where Self: Sized;
fn convert_key_ref<P: KeyParts>(key: &Key<P, UnspecifiedRole>)
-> &Key<P, Self>
where Self: Sized;
fn convert_bundle<P: KeyParts>(bundle: KeyBundle<P, UnspecifiedRole>)
-> KeyBundle<P, Self>
where Self: Sized;
fn convert_bundle_ref<P: KeyParts>(bundle: &KeyBundle<P, UnspecifiedRole>)
-> &KeyBundle<P, Self>
where Self: Sized;
fn role() -> KeyRoleRT;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PublicParts;
assert_send_and_sync!(PublicParts);
impl seal::Sealed for PublicParts {}
impl KeyParts for PublicParts {
fn convert_key<R: KeyRole>(key: Key<UnspecifiedParts, R>)
-> Result<Key<Self, R>> {
Ok(key.into())
}
fn convert_key_ref<R: KeyRole>(key: &Key<UnspecifiedParts, R>)
-> Result<&Key<Self, R>> {
Ok(key.into())
}
fn convert_bundle<R: KeyRole>(bundle: KeyBundle<UnspecifiedParts, R>)
-> Result<KeyBundle<Self, R>> {
Ok(bundle.into())
}
fn convert_bundle_ref<R: KeyRole>(bundle: &KeyBundle<UnspecifiedParts, R>)
-> Result<&KeyBundle<Self, R>> {
Ok(bundle.into())
}
fn convert_key_amalgamation<R: KeyRole>(
ka: ComponentAmalgamation<Key<UnspecifiedParts, R>>)
-> Result<ComponentAmalgamation<Key<Self, R>>> {
Ok(ka.into())
}
fn convert_key_amalgamation_ref<'a, R: KeyRole>(
ka: &'a ComponentAmalgamation<'a, Key<UnspecifiedParts, R>>)
-> Result<&'a ComponentAmalgamation<'a, Key<Self, R>>> {
Ok(ka.into())
}
fn significant_secrets() -> bool {
false
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct SecretParts;
assert_send_and_sync!(SecretParts);
impl seal::Sealed for SecretParts {}
impl KeyParts for SecretParts {
fn convert_key<R: KeyRole>(key: Key<UnspecifiedParts, R>)
-> Result<Key<Self, R>>{
key.try_into()
}
fn convert_key_ref<R: KeyRole>(key: &Key<UnspecifiedParts, R>)
-> Result<&Key<Self, R>> {
key.try_into()
}
fn convert_bundle<R: KeyRole>(bundle: KeyBundle<UnspecifiedParts, R>)
-> Result<KeyBundle<Self, R>> {
bundle.try_into()
}
fn convert_bundle_ref<R: KeyRole>(bundle: &KeyBundle<UnspecifiedParts, R>)
-> Result<&KeyBundle<Self, R>> {
bundle.try_into()
}
fn convert_key_amalgamation<R: KeyRole>(
ka: ComponentAmalgamation<Key<UnspecifiedParts, R>>)
-> Result<ComponentAmalgamation<Key<Self, R>>> {
ka.try_into()
}
fn convert_key_amalgamation_ref<'a, R: KeyRole>(
ka: &'a ComponentAmalgamation<'a, Key<UnspecifiedParts, R>>)
-> Result<&'a ComponentAmalgamation<'a, Key<Self, R>>> {
ka.try_into()
}
fn significant_secrets() -> bool {
true
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct UnspecifiedParts;
assert_send_and_sync!(UnspecifiedParts);
impl seal::Sealed for UnspecifiedParts {}
impl KeyParts for UnspecifiedParts {
fn convert_key<R: KeyRole>(key: Key<UnspecifiedParts, R>)
-> Result<Key<Self, R>> {
Ok(key)
}
fn convert_key_ref<R: KeyRole>(key: &Key<UnspecifiedParts, R>)
-> Result<&Key<Self, R>> {
Ok(key)
}
fn convert_bundle<R: KeyRole>(bundle: KeyBundle<UnspecifiedParts, R>)
-> Result<KeyBundle<Self, R>> {
Ok(bundle)
}
fn convert_bundle_ref<R: KeyRole>(bundle: &KeyBundle<UnspecifiedParts, R>)
-> Result<&KeyBundle<Self, R>> {
Ok(bundle)
}
fn convert_key_amalgamation<R: KeyRole>(
ka: ComponentAmalgamation<Key<UnspecifiedParts, R>>)
-> Result<ComponentAmalgamation<Key<UnspecifiedParts, R>>> {
Ok(ka)
}
fn convert_key_amalgamation_ref<'a, R: KeyRole>(
ka: &'a ComponentAmalgamation<'a, Key<UnspecifiedParts, R>>)
-> Result<&'a ComponentAmalgamation<'a, Key<Self, R>>> {
Ok(ka)
}
fn significant_secrets() -> bool {
true
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PrimaryRole;
assert_send_and_sync!(PrimaryRole);
impl seal::Sealed for PrimaryRole {}
impl KeyRole for PrimaryRole {
fn convert_key<P: KeyParts>(key: Key<P, UnspecifiedRole>)
-> Key<P, Self> {
key.into()
}
fn convert_key_ref<P: KeyParts>(key: &Key<P, UnspecifiedRole>)
-> &Key<P, Self> {
key.into()
}
fn convert_bundle<P: KeyParts>(bundle: KeyBundle<P, UnspecifiedRole>)
-> KeyBundle<P, Self> {
bundle.into()
}
fn convert_bundle_ref<P: KeyParts>(bundle: &KeyBundle<P, UnspecifiedRole>)
-> &KeyBundle<P, Self> {
bundle.into()
}
fn role() -> KeyRoleRT {
KeyRoleRT::Primary
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct SubordinateRole;
assert_send_and_sync!(SubordinateRole);
impl seal::Sealed for SubordinateRole {}
impl KeyRole for SubordinateRole {
fn convert_key<P: KeyParts>(key: Key<P, UnspecifiedRole>)
-> Key<P, Self> {
key.into()
}
fn convert_key_ref<P: KeyParts>(key: &Key<P, UnspecifiedRole>)
-> &Key<P, Self> {
key.into()
}
fn convert_bundle<P: KeyParts>(bundle: KeyBundle<P, UnspecifiedRole>)
-> KeyBundle<P, Self> {
bundle.into()
}
fn convert_bundle_ref<P: KeyParts>(bundle: &KeyBundle<P, UnspecifiedRole>)
-> &KeyBundle<P, Self> {
bundle.into()
}
fn role() -> KeyRoleRT {
KeyRoleRT::Subordinate
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct UnspecifiedRole;
assert_send_and_sync!(UnspecifiedRole);
impl seal::Sealed for UnspecifiedRole {}
impl KeyRole for UnspecifiedRole {
fn convert_key<P: KeyParts>(key: Key<P, UnspecifiedRole>)
-> Key<P, Self> {
key
}
fn convert_key_ref<P: KeyParts>(key: &Key<P, UnspecifiedRole>)
-> &Key<P, Self> {
key
}
fn convert_bundle<P: KeyParts>(bundle: KeyBundle<P, UnspecifiedRole>)
-> KeyBundle<P, Self> {
bundle
}
fn convert_bundle_ref<P: KeyParts>(bundle: &KeyBundle<P, UnspecifiedRole>)
-> &KeyBundle<P, Self> {
bundle
}
fn role() -> KeyRoleRT {
KeyRoleRT::Unspecified
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyRoleRT {
Primary,
Subordinate,
Unspecified,
}
pub(crate) type PublicKey = Key<PublicParts, PrimaryRole>;
pub(crate) type PublicSubkey = Key<PublicParts, SubordinateRole>;
pub(crate) type SecretKey = Key<SecretParts, PrimaryRole>;
pub(crate) type SecretSubkey = Key<SecretParts, SubordinateRole>;
#[allow(dead_code)]
pub(crate) type UnspecifiedPublic = Key<PublicParts, UnspecifiedRole>;
pub(crate) type UnspecifiedSecret = Key<SecretParts, UnspecifiedRole>;
#[allow(dead_code)]
pub(crate) type UnspecifiedPrimary = Key<UnspecifiedParts, PrimaryRole>;
#[allow(dead_code)]
pub(crate) type UnspecifiedSecondary = Key<UnspecifiedParts, SubordinateRole>;
#[allow(dead_code)]
pub(crate) type UnspecifiedKey = Key<UnspecifiedParts, UnspecifiedRole>;
impl<P, R> Key<P, R>
where P: key::KeyParts,
R: key::KeyRole,
{
pub fn encrypt(&self, data: &SessionKey) -> Result<mpi::Ciphertext> {
use crate::crypto::ecdh::aes_key_wrap;
use crate::crypto::backend::{Backend, interface::{Asymmetric, Kdf}};
use crate::crypto::mpi::PublicKey;
use PublicKeyAlgorithm::*;
#[allow(deprecated, non_snake_case)]
#[allow(clippy::erasing_op, clippy::identity_op)]
match self.pk_algo() {
X25519 =>
if let mpi::PublicKey::X25519 { u: U } = self.mpis()
{
let (v, V) = Backend::x25519_generate_key()?;
let S = Backend::x25519_shared_point(&v, U)?;
let wrap_algo = SymmetricAlgorithm::AES128;
let mut ikm: SessionKey = vec![0; 32 + 32 + 32].into();
ikm[0 * 32..1 * 32].copy_from_slice(&V[..]);
ikm[1 * 32..2 * 32].copy_from_slice(&U[..]);
ikm[2 * 32..3 * 32].copy_from_slice(&S[..]);
let mut kek = vec![0; wrap_algo.key_size()?].into();
Backend::hkdf_sha256(&ikm, None, b"OpenPGP X25519", &mut kek)?;
let esk = aes_key_wrap(wrap_algo, kek.as_protected(),
data.as_protected())?;
Ok(mpi::Ciphertext::X25519 {
e: Box::new(V),
key: esk.into(),
})
} else {
Err(Error::MalformedPacket(format!(
"Key: Expected X25519 public key, got {:?}", self.mpis())).into())
},
X448 =>
if let mpi::PublicKey::X448 { u: U } = self.mpis()
{
let (v, V) = Backend::x448_generate_key()?;
let S = Backend::x448_shared_point(&v, U)?;
let wrap_algo = SymmetricAlgorithm::AES256;
let mut ikm: SessionKey = vec![0; 56 + 56 + 56].into();
ikm[0 * 56..1 * 56].copy_from_slice(&V[..]);
ikm[1 * 56..2 * 56].copy_from_slice(&U[..]);
ikm[2 * 56..3 * 56].copy_from_slice(&S[..]);
let mut kek = vec![0; wrap_algo.key_size()?].into();
Backend::hkdf_sha512(&ikm, None, b"OpenPGP X448", &mut kek)?;
let esk = aes_key_wrap(wrap_algo, kek.as_protected(),
data.as_protected())?;
Ok(mpi::Ciphertext::X448 {
e: Box::new(V),
key: esk.into(),
})
} else {
Err(Error::MalformedPacket(format!(
"Key: Expected X448 public key, got {:?}", self.mpis())).into())
},
RSASign | DSA | ECDSA | EdDSA | Ed25519 | Ed448 =>
Err(Error::InvalidOperation(
format!("{} is not an encryption algorithm", self.pk_algo())
).into()),
ECDH if matches!(self.mpis(),
PublicKey::ECDH { curve: Curve::Cv25519, ..}) =>
{
let q = match self.mpis() {
PublicKey::ECDH { q, .. } => q,
_ => unreachable!(),
};
let R = q.decode_point(&Curve::Cv25519)?.0;
let (v, VB) = Backend::x25519_generate_key()?;
let VB = mpi::MPI::new_compressed_point(&VB);
let S = Backend::x25519_shared_point(&v, R.try_into()?)?;
crate::crypto::ecdh::encrypt_wrap(
self.parts_as_public().role_as_subordinate(), data, VB, &S)
},
RSAEncryptSign | RSAEncrypt |
ElGamalEncrypt | ElGamalEncryptSign |
ECDH |
Private(_) | Unknown(_) => self.encrypt_backend(data),
}
}
pub fn verify(&self, sig: &mpi::Signature, hash_algo: HashAlgorithm,
digest: &[u8]) -> Result<()> {
use crate::crypto::backend::{Backend, interface::Asymmetric};
use crate::crypto::mpi::{PublicKey, Signature};
fn bad(e: impl ToString) -> anyhow::Error {
Error::BadSignature(e.to_string()).into()
}
let ok = match (self.mpis(), sig) {
(PublicKey::Ed25519 { a }, Signature::Ed25519 { s }) =>
Backend::ed25519_verify(a, digest, s)?,
(PublicKey::Ed448 { a }, Signature::Ed448 { s }) =>
Backend::ed448_verify(a, digest, s)?,
(PublicKey::EdDSA { curve, q }, Signature::EdDSA { r, s }) =>
match curve {
Curve::Ed25519 => {
let (public, ..) = q.decode_point(&Curve::Ed25519)?;
assert_eq!(public.len(), 32);
let mut signature = Vec::with_capacity(64);
signature.extend_from_slice(
&r.value_padded(32).map_err(bad)?);
signature.extend_from_slice(
&s.value_padded(32).map_err(bad)?);
debug_assert_eq!(signature.len(), 64);
Backend::ed25519_verify(public.try_into()?,
digest,
&signature.as_slice().try_into()?)?
},
_ => return
Err(Error::UnsupportedEllipticCurve(curve.clone()).into()),
},
(PublicKey::DSA { p, q, g, y }, Signature::DSA { r, s }) =>
Backend::dsa_verify(p, q, g, y, digest, r, s)?,
(PublicKey::RSA { .. }, Signature::RSA { .. }) |
(PublicKey::ECDSA { .. }, Signature::ECDSA { .. }) =>
return self.verify_backend(sig, hash_algo, digest),
_ => return Err(Error::MalformedPacket(format!(
"unsupported combination of key {} and signature {:?}.",
self.pk_algo(), sig)).into()),
};
if ok {
Ok(())
} else {
Err(Error::ManipulatedMessage.into())
}
}
}
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub enum SecretKeyMaterial {
Unencrypted(Unencrypted),
Encrypted(Encrypted),
}
assert_send_and_sync!(SecretKeyMaterial);
impl From<mpi::SecretKeyMaterial> for SecretKeyMaterial {
fn from(mpis: mpi::SecretKeyMaterial) -> Self {
SecretKeyMaterial::Unencrypted(mpis.into())
}
}
impl From<Unencrypted> for SecretKeyMaterial {
fn from(key: Unencrypted) -> Self {
SecretKeyMaterial::Unencrypted(key)
}
}
impl From<Encrypted> for SecretKeyMaterial {
fn from(key: Encrypted) -> Self {
SecretKeyMaterial::Encrypted(key)
}
}
impl SecretKeyMaterial {
pub fn decrypt<P, R>(mut self,
key: &Key<P, R>,
password: &Password)
-> Result<Self>
where
P: KeyParts,
R: KeyRole,
{
self.decrypt_in_place(key, password)?;
Ok(self)
}
pub fn decrypt_in_place<P, R>(&mut self,
key: &Key<P, R>,
password: &Password)
-> Result<()>
where
P: KeyParts,
R: KeyRole,
{
match self {
SecretKeyMaterial::Encrypted(e) => {
*self = e.decrypt(key, password)?.into();
Ok(())
}
SecretKeyMaterial::Unencrypted(_) =>
Err(Error::InvalidArgument(
"secret key is not encrypted".into()).into()),
}
}
pub fn encrypt<P, R>(mut self,
key: &Key<P, R>,
password: &Password)
-> Result<Self>
where
P: KeyParts,
R: KeyRole,
{
self.encrypt_in_place(key, password)?;
Ok(self)
}
pub fn encrypt_with<P, R>(mut self,
key: &Key<P, R>,
s2k: S2K,
symm: SymmetricAlgorithm,
aead: Option<AEADAlgorithm>,
password: &Password)
-> Result<Self>
where
P: KeyParts,
R: KeyRole,
{
self.encrypt_in_place_with(key, s2k, symm, aead, password)?;
Ok(self)
}
pub fn encrypt_in_place<P, R>(&mut self,
key: &Key<P, R>,
password: &Password)
-> Result<()>
where
P: KeyParts,
R: KeyRole,
{
match self {
SecretKeyMaterial::Unencrypted(ref u) => {
*self = SecretKeyMaterial::Encrypted(
u.encrypt(key, password)?);
Ok(())
}
SecretKeyMaterial::Encrypted(_) =>
Err(Error::InvalidArgument(
"secret key is encrypted".into()).into()),
}
}
pub fn encrypt_in_place_with<P, R>(&mut self,
key: &Key<P, R>,
s2k: S2K,
symm: SymmetricAlgorithm,
aead: Option<AEADAlgorithm>,
password: &Password)
-> Result<()>
where
P: KeyParts,
R: KeyRole,
{
match self {
SecretKeyMaterial::Unencrypted(ref u) => {
*self = SecretKeyMaterial::Encrypted(
u.encrypt_with(key, s2k, symm, aead, password)?);
Ok(())
}
SecretKeyMaterial::Encrypted(_) =>
Err(Error::InvalidArgument(
"secret key is encrypted".into()).into()),
}
}
pub fn is_encrypted(&self) -> bool {
match self {
SecretKeyMaterial::Encrypted(_) => true,
SecretKeyMaterial::Unencrypted(_) => false,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Unencrypted {
mpis: mem::Encrypted,
}
assert_send_and_sync!(Unencrypted);
impl From<mpi::SecretKeyMaterial> for Unencrypted {
fn from(mpis: mpi::SecretKeyMaterial) -> Self {
use crate::serialize::MarshalInto;
let mut plaintext = mem::Protected::new(1 + mpis.serialized_len());
plaintext[0] =
mpis.algo().unwrap_or(PublicKeyAlgorithm::Unknown(0)).into();
mpis.serialize_into(&mut plaintext[1..])
.expect("MPI serialization to vec failed");
Unencrypted {
mpis: mem::Encrypted::new(plaintext)
.expect("encrypting memory failed"),
}
}
}
impl Unencrypted {
pub fn map<F, T>(&self, mut fun: F) -> T
where F: FnMut(&mpi::SecretKeyMaterial) -> T
{
self.mpis.map(|plaintext| {
let algo: PublicKeyAlgorithm = plaintext[0].into();
let mpis = mpi::SecretKeyMaterial::from_bytes(algo, &plaintext[1..])
.expect("Decrypted secret key is malformed");
fun(&mpis)
})
}
pub fn encrypt<P, R>(&self,
key: &Key<P, R>,
password: &Password)
-> Result<Encrypted>
where
P: KeyParts,
R: KeyRole,
{
let (s2k, symm, aead) = match key.version() {
6 => (
S2K::default(),
SymmetricAlgorithm::AES128,
Some(AEADAlgorithm::OCB),
),
_ => (
S2K::default(),
SymmetricAlgorithm::default(),
None,
),
};
self.encrypt_with(key, s2k, symm, aead, password)
}
pub fn encrypt_with<P, R>(&self,
key: &Key<P, R>,
s2k: S2K,
symm: SymmetricAlgorithm,
aead: Option<AEADAlgorithm>,
password: &Password)
-> Result<Encrypted>
where
P: KeyParts,
R: KeyRole,
{
use std::io::Write;
use crate::crypto::symmetric::Encryptor;
let derived_key = s2k.derive_key(password, symm.key_size()?)?;
let checksum = Default::default();
constrain_encryption_methods(key, &s2k, symm, aead, Some(checksum))?;
if matches!(s2k, S2K::Argon2 { .. }) && aead.is_none() {
return Err(Error::InvalidOperation(
"Argon2 MUST be used with an AEAD mode".into()).into());
}
if let Some(aead) = aead {
use crate::serialize::MarshalInto;
let mut iv = vec![0; aead.nonce_size()?];
crypto::random(&mut iv)?;
let schedule = Key253Schedule::new(
match key.role() {
KeyRoleRT::Primary => Tag::SecretKey,
KeyRoleRT::Subordinate => Tag::SecretSubkey,
KeyRoleRT::Unspecified =>
return Err(Error::InvalidOperation(
"cannot encrypt key with unspecified role".into()).into()),
},
key.parts_as_public(), derived_key, symm, aead, &iv)?;
let mut enc = schedule.encryptor()?;
let esk = self.map(|mpis| -> Result<Vec<u8>> {
let mut esk =
vec![0; mpis.serialized_len() + aead.digest_size()?];
let secret = mpis.to_vec()?;
enc.encrypt_seal(&mut esk, &secret)?;
Ok(esk)
})?;
Ok(Encrypted::new_aead(s2k, symm, aead, iv.into_boxed_slice(),
esk.into_boxed_slice()))
} else {
use crypto::symmetric::{
BlockCipherMode,
PaddingMode,
};
let mut trash = vec![0u8; symm.block_size()?];
crypto::random(&mut trash)?;
let mut esk = Vec::new();
let mut encryptor =
Encryptor::new(symm, BlockCipherMode::CFB, PaddingMode::None,
&derived_key, None, &mut esk)?;
encryptor.write_all(&trash)?;
self.map(|mpis| mpis.serialize_with_checksum(&mut encryptor,
checksum))?;
drop(encryptor);
Ok(Encrypted::new(s2k, symm, Some(checksum),
esk.into_boxed_slice()))
}
}
}
#[derive(Clone, Debug)]
pub struct Encrypted {
s2k: S2K,
algo: SymmetricAlgorithm,
aead: Option<(AEADAlgorithm, Box<[u8]>)>,
checksum: Option<mpi::SecretKeyChecksum>,
ciphertext: std::result::Result<(usize, // IV length
Box<[u8]>), Box<[u8]>>, }
assert_send_and_sync!(Encrypted);
impl PartialEq for Encrypted {
fn eq(&self, other: &Encrypted) -> bool {
self.algo == other.algo
&& self.aead == other.aead
&& self.checksum == other.checksum
&& match (&self.ciphertext, &other.ciphertext) {
(Ok(a), Ok(b)) =>
self.s2k == other.s2k && a == b,
(Err(a_raw), Err(b_raw)) => {
use crate::serialize::MarshalInto;
let mut a = self.s2k.to_vec().unwrap();
let mut b = other.s2k.to_vec().unwrap();
a.extend_from_slice(a_raw);
b.extend_from_slice(b_raw);
a == b
},
_ => false,
}
}
}
impl Eq for Encrypted {}
impl std::hash::Hash for Encrypted {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.algo.hash(state);
self.aead.hash(state);
self.checksum.hash(state);
match &self.ciphertext {
Ok(c) => {
self.s2k.hash(state);
c.hash(state);
},
Err(c) => {
use crate::serialize::MarshalInto;
let mut a = self.s2k.to_vec().unwrap();
a.extend_from_slice(c);
a.hash(state);
},
}
}
}
impl Encrypted {
pub fn new(s2k: S2K, algo: SymmetricAlgorithm,
checksum: Option<mpi::SecretKeyChecksum>, ciphertext: Box<[u8]>)
-> Self
{
Self::new_raw(s2k, algo, checksum, Ok((0, ciphertext)))
}
pub fn new_aead(s2k: S2K,
sym_algo: SymmetricAlgorithm,
aead_algo: AEADAlgorithm,
aead_iv: Box<[u8]>,
ciphertext: Box<[u8]>)
-> Self
{
Encrypted {
s2k,
algo: sym_algo,
aead: Some((aead_algo, aead_iv)),
checksum: None,
ciphertext: Ok((0, ciphertext)),
}
}
pub(crate) fn new_raw(s2k: S2K, algo: SymmetricAlgorithm,
checksum: Option<mpi::SecretKeyChecksum>,
ciphertext: std::result::Result<(usize, Box<[u8]>),
Box<[u8]>>)
-> Self
{
Encrypted { s2k, algo, aead: None, checksum, ciphertext }
}
pub fn s2k(&self) -> &S2K {
&self.s2k
}
pub fn algo(&self) -> SymmetricAlgorithm {
self.algo
}
pub fn aead_algo(&self) -> Option<AEADAlgorithm> {
self.aead.as_ref().map(|(a, _iv)| *a)
}
pub fn aead_iv(&self) -> Option<&[u8]> {
self.aead.as_ref().map(|(_a, iv)| &iv[..])
}
pub fn checksum(&self) -> Option<mpi::SecretKeyChecksum> {
self.checksum
}
pub fn ciphertext(&self) -> Result<&[u8]> {
self.ciphertext
.as_ref()
.map(|(_cfb_iv_len, ciphertext)| &ciphertext[..])
.map_err(|_| Error::MalformedPacket(
format!("Unknown S2K: {:?}", self.s2k)).into())
}
pub(crate) fn raw_ciphertext(&self) -> &[u8] {
match self.ciphertext.as_ref() {
Ok((_cfb_iv_len, ciphertext)) => &ciphertext[..],
Err(s2k_ciphertext) => &s2k_ciphertext[..],
}
}
pub(crate) fn cfb_iv_len(&self) -> usize {
self.ciphertext.as_ref().ok()
.map(|(cfb_iv_len, _)| *cfb_iv_len)
.unwrap_or(0)
}
pub fn decrypt<P, R>(&self, key: &Key<P, R>, password: &Password)
-> Result<Unencrypted>
where
P: KeyParts,
R: KeyRole,
{
use std::io::Read;
use crate::crypto;
constrain_encryption_methods(
key, &self.s2k, self.algo,self.aead.as_ref().map(|(a, _)| *a),
self.checksum)?;
let derived_key = self.s2k.derive_key(password, self.algo.key_size()?)?;
let ciphertext = self.ciphertext()?;
if let Some((aead, iv)) = &self.aead {
let schedule = Key253Schedule::new(
match key.role() {
KeyRoleRT::Primary => Tag::SecretKey,
KeyRoleRT::Subordinate => Tag::SecretSubkey,
KeyRoleRT::Unspecified =>
return Err(Error::InvalidOperation(
"cannot decrypt key with unspecified role".into()).into()),
},
key.parts_as_public(), derived_key, self.algo, *aead, iv)?;
let mut dec = schedule.decryptor()?;
let mut secret = mem::Protected::new(
ciphertext.len().saturating_sub(aead.digest_size()?));
dec.decrypt_verify(&mut secret, ciphertext)?;
mpi::SecretKeyMaterial::from_bytes(
key.pk_algo(), &secret).map(|m| m.into())
} else {
use crypto::symmetric::{
BlockCipherMode,
UnpaddingMode,
};
let cur = buffered_reader::Memory::with_cookie(
ciphertext, Default::default());
let mut dec =
crypto::symmetric::InternalDecryptor::new(
self.algo,
BlockCipherMode::CFB,
UnpaddingMode::None,
&derived_key,
None,
cur)?;
let block_size = self.algo.block_size()?;
let mut trash = mem::Protected::new(block_size);
dec.read_exact(&mut trash)?;
let mut secret = mem::Protected::new(ciphertext.len() - block_size);
dec.read_exact(&mut secret)?;
mpi::SecretKeyMaterial::from_bytes_with_checksum(
key.pk_algo(), &secret, self.checksum.unwrap_or_default())
.map(|m| m.into())
}
}
}
fn constrain_encryption_methods<P, R>(key: &Key<P, R>,
s2k: &S2K,
_symm: SymmetricAlgorithm,
aead: Option<AEADAlgorithm>,
checksum: Option<mpi::SecretKeyChecksum>)
-> Result<()>
where
P: KeyParts,
R: KeyRole,
{
#[allow(deprecated)]
match s2k {
S2K::Argon2 { .. } if aead.is_none() =>
Err(Error::InvalidOperation(
"Argon2 MUST be used with an AEAD mode".into()).into()),
S2K::Implicit if key.version() == 6 =>
Err(Error::InvalidOperation(
"Implicit S2K MUST NOT be used with v6 keys".into()).into()),
S2K::Simple { .. } if key.version() == 6 =>
Err(Error::InvalidOperation(
"Simple S2K SHOULD NOT be used with v6 keys".into()).into()),
_ if key.version() == 6 && aead.is_none()
&& checksum != Some(mpi::SecretKeyChecksum::SHA1) =>
Err(Error::InvalidOperation(
"Malleable CFB MUST NOT be used with v6 keys".into()).into()),
_ => Ok(()),
}
}
pub(crate) struct Key253Schedule<'a> {
symm: SymmetricAlgorithm,
aead: AEADAlgorithm,
nonce: &'a [u8],
kek: SessionKey,
ad: Vec<u8>
}
impl<'a> Key253Schedule<'a> {
fn new<R>(tag: Tag,
key: &Key<PublicParts, R>,
derived_key: SessionKey,
symm: SymmetricAlgorithm,
aead: AEADAlgorithm,
nonce: &'a [u8])
-> Result<Self>
where
R: KeyRole,
{
use crate::serialize::{Marshal, MarshalInto};
use crate::crypto::backend::{Backend, interface::Kdf};
let info = [
0b1100_0000 | u8::from(tag), key.version(),
symm.into(),
aead.into(),
];
let mut kek = vec![0; symm.key_size()?].into();
Backend::hkdf_sha256(&derived_key, None, &info, &mut kek)?;
let mut ad = Vec::with_capacity(key.serialized_len());
ad.push(0b1100_0000 | u8::from(tag)); key.serialize(&mut ad)?;
Ok(Self {
symm,
aead,
nonce,
kek,
ad,
})
}
fn decryptor(&self) -> Result<crypto::aead::DecryptionContext> {
self.aead.context(self.symm, &self.kek, &self.ad, self.nonce)?
.for_decryption()
}
fn encryptor(&self) -> Result<crypto::aead::EncryptionContext> {
self.aead.context(self.symm, &self.kek, &self.ad, self.nonce)?
.for_encryption()
}
}
#[cfg(test)]
mod tests {
use crate::packet::Key;
use crate::Cert;
use crate::packet::key::SecretKeyMaterial;
use crate::packet::Packet;
use super::*;
use crate::parse::Parse;
use crate::SignatureType;
use crate::crypto::mpi::PublicKey;
#[test]
fn encrypted_rsa_key() {
let cert = Cert::from_bytes(
crate::tests::key("testy-new-encrypted-with-123.pgp")).unwrap();
let key = cert.primary_key().key().clone();
let (key, secret) = key.take_secret();
let mut secret = secret.unwrap();
assert!(secret.is_encrypted());
secret.decrypt_in_place(&key, &"123".into()).unwrap();
assert!(!secret.is_encrypted());
let (pair, _) = key.add_secret(secret);
assert!(pair.has_unencrypted_secret());
match pair.secret() {
SecretKeyMaterial::Unencrypted(ref u) => u.map(|mpis| match mpis {
mpi::SecretKeyMaterial::RSA { .. } => (),
_ => panic!(),
}),
_ => panic!(),
}
}
#[test]
fn signature_roundtrip() {
let gen_v4_rsa = |bits: usize| -> Key<key::SecretParts, key::PrimaryRole> {
Key4::generate_rsa(bits)
.expect("Can generate a v4 RSA key")
.into()
};
let gen_v6_rsa = |bits: usize| -> Key<_, _> {
Key6::generate_rsa(bits)
.expect("Can generate a v6 RSA key")
.into()
};
let gen_v4_curve = |curve: Curve| -> Key<_, _> {
Key4::generate_ecc(true, curve.clone())
.expect(&format!("Can generate a v4 {:?}", curve))
.into()
};
let gen_v6_curve = |curve: Curve| -> Key<_, _> {
Key6::generate_ecc(true, curve.clone())
.expect(&format!("Can generate a v6 {:?}", curve))
.into()
};
let gen_v4_ed25519 = || -> Key<_, _> {
Key4::generate_ed25519()
.expect("Can generate a v4 Ed25519 key")
.into()
};
let gen_v6_ed25519 = || -> Key<_, _> {
Key6::generate_ed25519()
.expect("Can generate a v6 Ed25519 key")
.into()
};
let gen_v4_ed448 = || -> Key<_, _> {
Key4::generate_ed448()
.expect("Can generate a v4 Ed448 key")
.into()
};
let gen_v6_ed448 = || -> Key<_, _> {
Key6::generate_ed448()
.expect("Can generate a v6 Ed448 key")
.into()
};
#[allow(deprecated)]
for (algo, curve, profile, gen) in [
(PublicKeyAlgorithm::RSAEncryptSign, None, 4,
Box::new(|| gen_v4_rsa(2048)) as Box<dyn Fn () -> _>),
(PublicKeyAlgorithm::RSAEncryptSign, None, 4,
Box::new(|| gen_v4_rsa(3072)) as Box<dyn Fn () -> _>),
(PublicKeyAlgorithm::RSAEncryptSign, None, 4,
Box::new(|| gen_v4_rsa(4096)) as Box<dyn Fn () -> _>),
(PublicKeyAlgorithm::RSAEncryptSign, None, 6,
Box::new(|| gen_v6_rsa(2048))),
(PublicKeyAlgorithm::RSAEncryptSign, None, 6,
Box::new(|| gen_v6_rsa(3072))),
(PublicKeyAlgorithm::RSAEncryptSign, None, 6,
Box::new(|| gen_v6_rsa(4096))),
(PublicKeyAlgorithm::EdDSA, Some(Curve::Ed25519), 4,
Box::new(|| gen_v4_curve(Curve::Ed25519))),
(PublicKeyAlgorithm::Ed25519, None, 4,
Box::new(gen_v4_ed25519)),
(PublicKeyAlgorithm::Ed25519, None, 6,
Box::new(gen_v6_ed25519)),
(PublicKeyAlgorithm::Ed448, None, 4,
Box::new(gen_v4_ed448)),
(PublicKeyAlgorithm::Ed448, None, 6,
Box::new(gen_v6_ed448)),
(PublicKeyAlgorithm::ECDSA, Some(Curve::NistP256), 4,
Box::new(|| gen_v4_curve(Curve::NistP256))),
(PublicKeyAlgorithm::ECDSA, Some(Curve::NistP256), 6,
Box::new(|| gen_v6_curve(Curve::NistP256))),
(PublicKeyAlgorithm::ECDSA, Some(Curve::NistP384), 4,
Box::new(|| gen_v4_curve(Curve::NistP384))),
(PublicKeyAlgorithm::ECDSA, Some(Curve::NistP384), 6,
Box::new(|| gen_v6_curve(Curve::NistP384))),
(PublicKeyAlgorithm::ECDSA, Some(Curve::NistP521), 4,
Box::new(|| gen_v4_curve(Curve::NistP521))),
(PublicKeyAlgorithm::ECDSA, Some(Curve::NistP521), 6,
Box::new(|| gen_v6_curve(Curve::NistP521))),
(PublicKeyAlgorithm::ECDSA, Some(Curve::BrainpoolP256), 4,
Box::new(|| gen_v4_curve(Curve::BrainpoolP256))),
(PublicKeyAlgorithm::ECDSA, Some(Curve::BrainpoolP256), 6,
Box::new(|| gen_v6_curve(Curve::BrainpoolP256))),
(PublicKeyAlgorithm::ECDSA, Some(Curve::BrainpoolP384), 4,
Box::new(|| gen_v4_curve(Curve::BrainpoolP384))),
(PublicKeyAlgorithm::ECDSA, Some(Curve::BrainpoolP384), 6,
Box::new(|| gen_v6_curve(Curve::BrainpoolP384))),
(PublicKeyAlgorithm::ECDSA, Some(Curve::BrainpoolP512), 4,
Box::new(|| gen_v4_curve(Curve::BrainpoolP512))),
(PublicKeyAlgorithm::ECDSA, Some(Curve::BrainpoolP512), 6,
Box::new(|| gen_v6_curve(Curve::BrainpoolP512))),
]
{
eprintln!("Checking algo: {}, curve: {:?}, profile: {}.",
algo, curve, profile);
if ! algo.is_supported() {
eprintln!("Algorithm {} not supported, skipping test.",
algo);
continue;
}
if let Some(curve) = curve.as_ref() {
if ! curve.is_supported() {
eprintln!("Curve {:?} not supported, skipping test.",
curve);
continue;
}
}
let key = gen();
assert_eq!(algo, key.pk_algo(),
"\n\
algo expected: {:?} ({})\n\
algo got: {:?} ({})",
algo, u8::from(algo),
key.pk_algo(), u8::from(key.pk_algo()));
let got_curve = match key.mpis() {
PublicKey::EdDSA { curve, .. }
| PublicKey::ECDSA { curve, .. }
| PublicKey::ECDH { curve, .. } =>
{
Some(curve.clone())
}
_ => None,
};
assert_eq!(curve, got_curve,
"\n\
curve expected: {:?}\n\
curve got: {:?}",
curve, got_curve);
assert_eq!(profile, key.version(),
"\n\
profile expected: {:?}\n\
profile got: {:?}",
profile, key.version());
let mut pair = key.clone().into_keypair().unwrap();
let hash = HashAlgorithm::default();
let ctx = hash.context().unwrap().for_signature(profile);
let sig = SignatureBuilder::new(SignatureType::Binary)
.sign_hash(&mut pair, ctx).unwrap();
let ctx = hash.context().unwrap().for_signature(profile);
sig.verify_hash(&key, ctx).unwrap();
}
}
#[test]
fn primary_key_encrypt_decrypt() -> Result<()> {
key_encrypt_decrypt::<PrimaryRole>()
}
#[test]
fn subkey_encrypt_decrypt() -> Result<()> {
key_encrypt_decrypt::<SubordinateRole>()
}
fn key_encrypt_decrypt<R>() -> Result<()>
where
R: KeyRole + PartialEq,
{
let mut g = quickcheck::Gen::new(256);
let p: Password = Vec::<u8>::arbitrary(&mut g).into();
let check = |key: Key<SecretParts, R>| -> Result<()> {
let encrypted = key.clone().encrypt_secret(&p)?;
let decrypted = encrypted.decrypt_secret(&p)?;
assert_eq!(key, decrypted);
Ok(())
};
use crate::types::Curve::*;
for curve in vec![NistP256, NistP384, NistP521, Ed25519] {
if ! curve.is_supported() {
eprintln!("Skipping unsupported {}", curve);
continue;
}
let key: Key4<_, R>
= Key4::generate_ecc(true, curve.clone())?;
check(key.into())?;
let key: Key6<_, R>
= Key6::generate_ecc(true, curve.clone())?;
check(key.into())?;
}
for bits in vec![2048, 3072] {
if ! PublicKeyAlgorithm::RSAEncryptSign.is_supported() {
eprintln!("Skipping unsupported RSA");
continue;
}
let key: Key4<_, R>
= Key4::generate_rsa(bits)?;
check(key.into())?;
let key: Key6<_, R>
= Key6::generate_rsa(bits)?;
check(key.into())?;
}
Ok(())
}
quickcheck! {
fn roundtrip_public(p: Key<PublicParts, UnspecifiedRole>) -> bool {
use crate::parse::Parse;
use crate::serialize::MarshalInto;
let buf = p.to_vec().expect("Failed to serialize key");
let q = Key::from_bytes(&buf).expect("Failed to parse key").into();
assert_eq!(p, q);
true
}
}
quickcheck! {
fn roundtrip_secret(p: Key<SecretParts, PrimaryRole>) -> bool {
use crate::parse::Parse;
use crate::serialize::MarshalInto;
let buf = p.to_vec().expect("Failed to serialize key");
let q = Key::from_bytes(&buf).expect("Failed to parse key")
.parts_into_secret().expect("No secret material")
.role_into_primary();
assert_eq!(p, q);
true
}
}
fn mutate_eq_discriminates_key<P, R>(key: Key<P, R>, i: usize) -> bool
where P: KeyParts,
R: KeyRole,
Key<P, R>: Into<Packet>,
{
use crate::serialize::MarshalInto;
let p: Packet = key.into();
let mut buf = p.to_vec().unwrap();
if buf.len() < 3 { return true; }
let bit = i % ((buf.len() - 2) * 8) + 16;
buf[bit / 8] ^= 1 << (bit % 8);
let ok = match Packet::from_bytes(&buf) {
Ok(q) => p != q,
Err(_) => true, };
if ! ok {
eprintln!("mutate_eq_discriminates_key for ({:?}, {})", p, i);
}
ok
}
quickcheck! {
fn mutate_eq_discriminates_pp(key: Key<PublicParts, PrimaryRole>,
i: usize) -> bool {
mutate_eq_discriminates_key(key, i)
}
}
quickcheck! {
fn mutate_eq_discriminates_ps(key: Key<PublicParts, SubordinateRole>,
i: usize) -> bool {
mutate_eq_discriminates_key(key, i)
}
}
quickcheck! {
fn mutate_eq_discriminates_sp(key: Key<SecretParts, PrimaryRole>,
i: usize) -> bool {
mutate_eq_discriminates_key(key, i)
}
}
quickcheck! {
fn mutate_eq_discriminates_ss(key: Key<SecretParts, SubordinateRole>,
i: usize) -> bool {
mutate_eq_discriminates_key(key, i)
}
}
}