use nettle::{curve25519, ecc, ecdh, ecdsa, ed25519, dsa, rsa, random::Yarrow};
use crate::{Error, Result};
use crate::packet::{self, key, Key};
use crate::crypto::asymmetric::{KeyPair, Decryptor, Signer};
use crate::crypto::mpi::{self, MPI, PublicKey};
use crate::crypto::SessionKey;
use crate::types::{Curve, HashAlgorithm};
impl Signer for KeyPair {
fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
KeyPair::public(self)
}
fn sign(&mut self, hash_algo: HashAlgorithm, digest: &[u8])
-> Result<mpi::Signature>
{
use crate::PublicKeyAlgorithm::*;
let mut rng = Yarrow::default();
self.secret().map(|secret| {
#[allow(deprecated)]
match (self.public().pk_algo(), self.public().mpis(), secret)
{
(RSASign,
&PublicKey::RSA { ref e, ref n },
&mpi::SecretKeyMaterial::RSA { ref p, ref q, ref d, .. }) |
(RSAEncryptSign,
&PublicKey::RSA { ref e, ref n },
&mpi::SecretKeyMaterial::RSA { ref p, ref q, ref d, .. }) => {
let public = rsa::PublicKey::new(n.value(), e.value())?;
let secret = rsa::PrivateKey::new(d.value(), p.value(),
q.value(), Option::None)?;
let mut sig = vec![0u8; n.value().len()];
rsa::sign_digest_pkcs1(&public, &secret, digest,
hash_algo.oid()?,
&mut rng, &mut sig)?;
Ok(mpi::Signature::RSA {
s: MPI::new(&sig),
})
},
(DSA,
&PublicKey::DSA { ref p, ref q, ref g, .. },
&mpi::SecretKeyMaterial::DSA { ref x }) => {
let params = dsa::Params::new(p.value(), q.value(), g.value());
let secret = dsa::PrivateKey::new(x.value());
let sig = dsa::sign(¶ms, &secret, digest, &mut rng)?;
Ok(mpi::Signature::DSA {
r: MPI::new(&sig.r()),
s: MPI::new(&sig.s()),
})
},
(EdDSA,
&PublicKey::EdDSA { ref curve, ref q },
&mpi::SecretKeyMaterial::EdDSA { ref scalar }) => match curve {
Curve::Ed25519 => {
let public = q.decode_point(&Curve::Ed25519)?.0;
let mut sig = vec![0; ed25519::ED25519_SIGNATURE_SIZE];
let missing = ed25519::ED25519_KEY_SIZE
.saturating_sub(scalar.value().len());
let mut sec = [0u8; ed25519::ED25519_KEY_SIZE];
sec[missing..].copy_from_slice(scalar.value());
let res = ed25519::sign(public, &sec[..], digest, &mut sig);
unsafe {
memsec::memzero(sec.as_mut_ptr(),
ed25519::ED25519_KEY_SIZE);
}
res?;
Ok(mpi::Signature::EdDSA {
r: MPI::new(&sig[..32]),
s: MPI::new(&sig[32..]),
})
},
_ => Err(
Error::UnsupportedEllipticCurve(curve.clone()).into()),
},
(ECDSA,
&PublicKey::ECDSA { ref curve, .. },
&mpi::SecretKeyMaterial::ECDSA { ref scalar }) => {
let secret = match curve {
Curve::NistP256 =>
ecc::Scalar::new::<ecc::Secp256r1>(
scalar.value())?,
Curve::NistP384 =>
ecc::Scalar::new::<ecc::Secp384r1>(
scalar.value())?,
Curve::NistP521 =>
ecc::Scalar::new::<ecc::Secp521r1>(
scalar.value())?,
_ =>
return Err(
Error::UnsupportedEllipticCurve(curve.clone())
.into()),
};
let sig = ecdsa::sign(&secret, digest, &mut rng);
Ok(mpi::Signature::ECDSA {
r: MPI::new(&sig.r()),
s: MPI::new(&sig.s()),
})
},
(pk_algo, _, _) => Err(Error::InvalidOperation(format!(
"unsupported combination of algorithm {:?}, key {:?}, \
and secret key {:?}",
pk_algo, self.public(), self.secret())).into()),
}})
}
}
impl Decryptor for KeyPair {
fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
KeyPair::public(self)
}
fn decrypt(&mut self, ciphertext: &mpi::Ciphertext,
plaintext_len: Option<usize>)
-> Result<SessionKey>
{
use crate::PublicKeyAlgorithm::*;
self.secret().map(
|secret| Ok(match (self.public().mpis(), secret, ciphertext)
{
(PublicKey::RSA{ ref e, ref n },
mpi::SecretKeyMaterial::RSA{ ref p, ref q, ref d, .. },
mpi::Ciphertext::RSA{ ref c }) => {
let c_ = if c.value().len() < n.value().len() {
let mut c_ = vec![0; n.value().len() - c.value().len()];
c_.extend_from_slice(c.value());
Some(c_)
} else {
None
};
let c = if let Some(c_) = c_.as_ref() {
&c_[..]
} else {
c.value()
};
let public = rsa::PublicKey::new(n.value(), e.value())?;
let secret = rsa::PrivateKey::new(d.value(), p.value(),
q.value(), Option::None)?;
let mut rand = Yarrow::default();
if let Some(l) = plaintext_len {
let mut plaintext: SessionKey = vec![0; l].into();
rsa::decrypt_pkcs1(&public, &secret, &mut rand,
c, plaintext.as_mut())?;
plaintext
} else {
rsa::decrypt_pkcs1_insecure(&public, &secret,
&mut rand, c)?
.into()
}
}
(PublicKey::ElGamal{ .. },
mpi::SecretKeyMaterial::ElGamal{ .. },
mpi::Ciphertext::ElGamal{ .. }) =>
return Err(
Error::UnsupportedPublicKeyAlgorithm(ElGamalEncrypt).into()),
(PublicKey::ECDH{ .. },
mpi::SecretKeyMaterial::ECDH { .. },
mpi::Ciphertext::ECDH { .. }) =>
crate::crypto::ecdh::decrypt(self.public(), secret, ciphertext)?,
(public, secret, ciphertext) =>
return Err(Error::InvalidOperation(format!(
"unsupported combination of key pair {:?}/{:?} \
and ciphertext {:?}",
public, secret, ciphertext)).into()),
}))
}
}
impl<P: key::KeyParts, R: key::KeyRole> Key<P, R> {
pub fn encrypt(&self, data: &SessionKey) -> Result<mpi::Ciphertext> {
use crate::PublicKeyAlgorithm::*;
#[allow(deprecated)]
match self.pk_algo() {
RSAEncryptSign | RSAEncrypt => {
match self.mpis() {
mpi::PublicKey::RSA { e, n } => {
let ciphertext_len = n.value().len();
if data.len() + 11 > ciphertext_len {
return Err(Error::InvalidArgument(
"Plaintext data too large".into()).into());
}
let mut esk = vec![0u8; ciphertext_len];
let mut rng = Yarrow::default();
let pk = rsa::PublicKey::new(n.value(), e.value())?;
rsa::encrypt_pkcs1(&pk, &mut rng, data,
&mut esk)?;
Ok(mpi::Ciphertext::RSA {
c: MPI::new(&esk),
})
},
pk => {
Err(Error::MalformedPacket(
format!(
"Key: Expected RSA public key, got {:?}",
pk)).into())
},
}
},
ECDH => crate::crypto::ecdh::encrypt(self.parts_as_public(),
data),
algo => Err(Error::UnsupportedPublicKeyAlgorithm(algo).into()),
}
}
pub fn verify(&self, sig: &packet::Signature, digest: &[u8]) -> Result<()>
{
use crate::PublicKeyAlgorithm::*;
use crate::crypto::mpi::Signature;
#[allow(deprecated)]
let ok = match (sig.pk_algo(), self.mpis(), sig.mpis()) {
(RSASign, PublicKey::RSA { e, n }, Signature::RSA { s }) |
(RSAEncryptSign, PublicKey::RSA { e, n }, Signature::RSA { s }) => {
let key = rsa::PublicKey::new(n.value(), e.value())?;
rsa::verify_digest_pkcs1(&key, digest, sig.hash_algo().oid()?,
s.value())?
},
(DSA, PublicKey::DSA{ y, p, q, g }, Signature::DSA { s, r }) => {
let key = dsa::PublicKey::new(y.value());
let params = dsa::Params::new(p.value(), q.value(), g.value());
let signature = dsa::Signature::new(r.value(), s.value());
dsa::verify(¶ms, &key, digest, &signature)
},
(EdDSA, PublicKey::EdDSA{ curve, q }, Signature::EdDSA { r, s }) =>
match curve {
Curve::Ed25519 => {
if q.value().get(0).map(|&b| b != 0x40).unwrap_or(true) {
return Err(Error::MalformedPacket(
"Invalid point encoding".into()).into());
}
let mut signature =
Vec::with_capacity(ed25519::ED25519_SIGNATURE_SIZE);
let half = ed25519::ED25519_SIGNATURE_SIZE / 2;
if r.value().len() < half {
for _ in 0..half - r.value().len() {
signature.push(0);
}
}
signature.extend_from_slice(r.value());
if s.value().len() < half {
for _ in 0..half - s.value().len() {
signature.push(0);
}
}
signature.extend_from_slice(s.value());
if signature.len() != ed25519::ED25519_SIGNATURE_SIZE {
return Err(Error::MalformedPacket(
format!(
"Invalid signature size: {}, r: {:?}, s: {:?}",
signature.len(), r.value(), s.value())).into());
}
ed25519::verify(&q.value()[1..], digest, &signature)?
},
_ => return
Err(Error::UnsupportedEllipticCurve(curve.clone()).into()),
},
(ECDSA, PublicKey::ECDSA{ curve, q }, Signature::ECDSA { s, r }) =>
{
let (x, y) = q.decode_point(curve)?;
let key = match curve {
Curve::NistP256 => ecc::Point::new::<ecc::Secp256r1>(x, y)?,
Curve::NistP384 => ecc::Point::new::<ecc::Secp384r1>(x, y)?,
Curve::NistP521 => ecc::Point::new::<ecc::Secp521r1>(x, y)?,
_ => return Err(
Error::UnsupportedEllipticCurve(curve.clone()).into()),
};
let signature = dsa::Signature::new(r.value(), s.value());
ecdsa::verify(&key, digest, &signature)
},
_ => return Err(Error::MalformedPacket(format!(
"unsupported combination of algorithm {}, key {} and \
signature {:?}.",
sig.pk_algo(), self.pk_algo(), sig.mpis())).into()),
};
if ok {
Ok(())
} else {
Err(Error::ManipulatedMessage.into())
}
}
}
use std::time::SystemTime;
use crate::crypto::mem::Protected;
use crate::packet::key::{Key4, SecretParts};
use crate::types::{PublicKeyAlgorithm, SymmetricAlgorithm};
impl<R> Key4<SecretParts, R>
where R: key::KeyRole,
{
pub fn import_secret_cv25519<H, S, T>(private_key: &[u8],
hash: H, sym: S, ctime: T)
-> Result<Self> where H: Into<Option<HashAlgorithm>>,
S: Into<Option<SymmetricAlgorithm>>,
T: Into<Option<SystemTime>>
{
let mut public_key = [0; curve25519::CURVE25519_SIZE];
curve25519::mul_g(&mut public_key, private_key).unwrap();
let mut private_key = Vec::from(private_key);
private_key.reverse();
Self::with_secret(
ctime.into().unwrap_or_else(SystemTime::now),
PublicKeyAlgorithm::ECDH,
mpi::PublicKey::ECDH {
curve: Curve::Cv25519,
hash: hash.into().unwrap_or(HashAlgorithm::SHA512),
sym: sym.into().unwrap_or(SymmetricAlgorithm::AES256),
q: MPI::new_compressed_point(&public_key),
},
mpi::SecretKeyMaterial::ECDH {
scalar: private_key.into(),
}.into())
}
pub fn import_secret_ed25519<T>(private_key: &[u8], ctime: T)
-> Result<Self> where T: Into<Option<SystemTime>>
{
let mut public_key = [0; ed25519::ED25519_KEY_SIZE];
ed25519::public_key(&mut public_key, private_key).unwrap();
Self::with_secret(
ctime.into().unwrap_or_else(SystemTime::now),
PublicKeyAlgorithm::EdDSA,
mpi::PublicKey::EdDSA {
curve: Curve::Ed25519,
q: MPI::new_compressed_point(&public_key),
},
mpi::SecretKeyMaterial::EdDSA {
scalar: mpi::MPI::new(private_key).into(),
}.into())
}
pub fn import_secret_rsa<T>(d: &[u8], p: &[u8], q: &[u8], ctime: T)
-> Result<Self> where T: Into<Option<SystemTime>>
{
let sec = rsa::PrivateKey::new(d, p, q, None)?;
let key = sec.public_key()?;
let (a, b, c) = sec.as_rfc4880();
Self::with_secret(
ctime.into().unwrap_or_else(SystemTime::now),
PublicKeyAlgorithm::RSAEncryptSign,
mpi::PublicKey::RSA {
e: mpi::MPI::new(&key.e()[..]),
n: mpi::MPI::new(&key.n()[..]),
},
mpi::SecretKeyMaterial::RSA {
d: mpi::MPI::new(d).into(),
p: mpi::MPI::new(&a[..]).into(),
q: mpi::MPI::new(&b[..]).into(),
u: mpi::MPI::new(&c[..]).into(),
}.into())
}
pub fn generate_rsa(bits: usize) -> Result<Self> {
let mut rng = Yarrow::default();
let (public, private) = rsa::generate_keypair(&mut rng, bits as u32)?;
let (p, q, u) = private.as_rfc4880();
let public_mpis = PublicKey::RSA {
e: MPI::new(&*public.e()).into(),
n: MPI::new(&*public.n()).into(),
};
let private_mpis = mpi::SecretKeyMaterial::RSA {
d: MPI::new(&*private.d()).into(),
p: MPI::new(&*p).into(),
q: MPI::new(&*q).into(),
u: MPI::new(&*u).into(),
};
Self::with_secret(
SystemTime::now(),
PublicKeyAlgorithm::RSAEncryptSign,
public_mpis,
private_mpis.into())
}
pub fn generate_ecc(for_signing: bool, curve: Curve) -> Result<Self> {
use crate::PublicKeyAlgorithm::*;
let mut rng = Yarrow::default();
let (mpis, secret, pk_algo) = match (curve.clone(), for_signing) {
(Curve::Ed25519, true) => {
let mut public = [0; ed25519::ED25519_KEY_SIZE];
let private: Protected =
ed25519::private_key(&mut rng).into();
ed25519::public_key(&mut public, &private)?;
let public_mpis = PublicKey::EdDSA {
curve: Curve::Ed25519,
q: MPI::new_compressed_point(&public),
};
let private_mpis = mpi::SecretKeyMaterial::EdDSA {
scalar: private.into(),
};
let sec = private_mpis.into();
(public_mpis, sec, EdDSA)
}
(Curve::Cv25519, false) => {
let mut public = [0; curve25519::CURVE25519_SIZE];
let mut private: Protected =
curve25519::private_key(&mut rng).into();
curve25519::mul_g(&mut public, &private)?;
private.reverse();
let public_mpis = PublicKey::ECDH {
curve: Curve::Cv25519,
q: MPI::new_compressed_point(&public),
hash: HashAlgorithm::SHA256,
sym: SymmetricAlgorithm::AES256,
};
let private_mpis = mpi::SecretKeyMaterial::ECDH {
scalar: private.into(),
};
let sec = private_mpis.into();
(public_mpis, sec, ECDH)
}
(Curve::NistP256, true) | (Curve::NistP384, true)
| (Curve::NistP521, true) => {
let (public, private, field_sz) = match curve {
Curve::NistP256 => {
let (pu, sec) =
ecdsa::generate_keypair::<ecc::Secp256r1, _>(&mut rng)?;
(pu, sec, 256)
}
Curve::NistP384 => {
let (pu, sec) =
ecdsa::generate_keypair::<ecc::Secp384r1, _>(&mut rng)?;
(pu, sec, 384)
}
Curve::NistP521 => {
let (pu, sec) =
ecdsa::generate_keypair::<ecc::Secp521r1, _>(&mut rng)?;
(pu, sec, 521)
}
_ => unreachable!(),
};
let (pub_x, pub_y) = public.as_bytes();
let public_mpis = mpi::PublicKey::ECDSA{
curve,
q: MPI::new_point(&pub_x, &pub_y, field_sz),
};
let private_mpis = mpi::SecretKeyMaterial::ECDSA{
scalar: MPI::new(&private.as_bytes()).into(),
};
let sec = private_mpis.into();
(public_mpis, sec, ECDSA)
}
(Curve::NistP256, false) | (Curve::NistP384, false)
| (Curve::NistP521, false) => {
let (private, hash, field_sz) = match curve {
Curve::NistP256 => {
let pv =
ecc::Scalar::new_random::<ecc::Secp256r1, _>(&mut rng);
(pv, HashAlgorithm::SHA256, 256)
}
Curve::NistP384 => {
let pv =
ecc::Scalar::new_random::<ecc::Secp384r1, _>(&mut rng);
(pv, HashAlgorithm::SHA384, 384)
}
Curve::NistP521 => {
let pv =
ecc::Scalar::new_random::<ecc::Secp521r1, _>(&mut rng);
(pv, HashAlgorithm::SHA512, 521)
}
_ => unreachable!(),
};
let public = ecdh::point_mul_g(&private);
let (pub_x, pub_y) = public.as_bytes();
let public_mpis = mpi::PublicKey::ECDH{
curve,
q: MPI::new_point(&pub_x, &pub_y, field_sz),
hash,
sym: SymmetricAlgorithm::AES256,
};
let private_mpis = mpi::SecretKeyMaterial::ECDH{
scalar: MPI::new(&private.as_bytes()).into(),
};
let sec = private_mpis.into();
(public_mpis, sec, ECDH)
}
(cv, _) => {
return Err(Error::UnsupportedEllipticCurve(cv).into());
}
};
Self::with_secret(
SystemTime::now(),
pk_algo,
mpis,
secret)
}
}