use nettle::{curve25519, ecc, ecdh, ecdsa, ed25519, dsa, rsa, random::Yarrow};
use crate::{Error, Result};
use crate::packet::{key, Key};
use crate::crypto::asymmetric::KeyPair;
use crate::crypto::backend::interface::Asymmetric;
use crate::crypto::mpi::{self, MPI, ProtectedMPI, PublicKey};
use crate::crypto::SessionKey;
use crate::types::{Curve, HashAlgorithm};
impl Asymmetric for super::Backend {
fn supports_algo(algo: PublicKeyAlgorithm) -> bool {
use PublicKeyAlgorithm::*;
#[allow(deprecated)]
match algo {
RSAEncryptSign | RSAEncrypt | RSASign | DSA | ECDH | ECDSA | EdDSA
=> true,
ElGamalEncrypt | ElGamalEncryptSign | Private(_) | Unknown(_)
=> false,
}
}
fn supports_curve(curve: &Curve) -> bool {
use Curve::*;
match curve {
NistP256 | NistP384 | NistP521 | Ed25519 | Cv25519
=> true,
BrainpoolP256 | BrainpoolP512 | Unknown(_)
=> false,
}
}
fn x25519_generate_key() -> Result<(Protected, [u8; 32])> {
debug_assert_eq!(curve25519::CURVE25519_SIZE, 32);
let mut rng = Yarrow::default();
let secret = curve25519::private_key(&mut rng);
let mut public = [0; 32];
curve25519::mul_g(&mut public, &secret)?;
Ok((secret.into(), public))
}
fn x25519_derive_public(secret: &Protected) -> Result<[u8; 32]> {
debug_assert_eq!(curve25519::CURVE25519_SIZE, 32);
let mut public = [0; 32];
curve25519::mul_g(&mut public, secret)?;
Ok(public)
}
fn x25519_shared_point(secret: &Protected, public: &[u8; 32])
-> Result<Protected> {
debug_assert_eq!(curve25519::CURVE25519_SIZE, 32);
let mut s: Protected = vec![0; 32].into();
curve25519::mul(&mut s, secret, public)?;
Ok(s)
}
fn ed25519_generate_key() -> Result<(Protected, [u8; 32])> {
debug_assert_eq!(ed25519::ED25519_KEY_SIZE, 32);
let mut rng = Yarrow::default();
let mut public = [0; 32];
let secret: Protected =
ed25519::private_key(&mut rng).into();
ed25519::public_key(&mut public, &secret)?;
Ok((secret, public))
}
fn ed25519_derive_public(secret: &Protected) -> Result<[u8; 32]> {
debug_assert_eq!(ed25519::ED25519_KEY_SIZE, 32);
let mut public = [0; 32];
ed25519::public_key(&mut public, secret)?;
Ok(public)
}
fn ed25519_sign(secret: &Protected, public: &[u8; 32], digest: &[u8])
-> Result<[u8; 64]> {
debug_assert_eq!(ed25519::ED25519_KEY_SIZE, 32);
debug_assert_eq!(ed25519::ED25519_SIGNATURE_SIZE, 64);
let mut sig = [0u8; 64];
ed25519::sign(public, secret, digest, &mut sig)?;
Ok(sig)
}
fn ed25519_verify(public: &[u8; 32], digest: &[u8], signature: &[u8; 64])
-> Result<bool> {
debug_assert_eq!(ed25519::ED25519_KEY_SIZE, 32);
debug_assert_eq!(ed25519::ED25519_SIGNATURE_SIZE, 64);
Ok(ed25519::verify(public, digest, signature)?)
}
fn dsa_generate_key(p_bits: usize)
-> Result<(MPI, MPI, MPI, MPI, ProtectedMPI)>
{
let mut rng = Yarrow::default();
let q_bits = if p_bits <= 1024 { 160 } else { 256 };
let params = dsa::Params::generate(&mut rng, p_bits, q_bits)?;
let (p, q) = params.primes();
let g = params.g();
let (y, x) = dsa::generate_keypair(¶ms, &mut rng);
Ok((p.into(), q.into(), g.into(), y.as_bytes().into(),
x.as_bytes().into()))
}
}
impl KeyPair {
pub(crate) fn sign_backend(&self,
secret: &mpi::SecretKeyMaterial,
hash_algo: HashAlgorithm,
digest: &[u8])
-> Result<mpi::Signature>
{
use crate::PublicKeyAlgorithm::*;
let mut rng = Yarrow::default();
#[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()),
})
},
(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 KeyPair {
pub(crate) fn decrypt_backend(&self, secret: &mpi::SecretKeyMaterial, ciphertext: &mpi::Ciphertext,
plaintext_len: Option<usize>)
-> Result<SessionKey>
{
use crate::PublicKeyAlgorithm::*;
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 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.value(), plaintext.as_mut())?;
plaintext
} else {
rsa::decrypt_pkcs1_insecure(&public, &secret,
&mut rand, c.value())?
.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,
plaintext_len)?,
(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(crate) fn encrypt_backend(&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),
RSASign | DSA | ECDSA | EdDSA =>
Err(Error::InvalidOperation(
format!("{} is not an encryption algorithm", self.pk_algo())
).into()),
ElGamalEncrypt | ElGamalEncryptSign |
Private(_) | Unknown(_) =>
Err(Error::UnsupportedPublicKeyAlgorithm(self.pk_algo()).into()),
}
}
pub(crate) fn verify_backend(&self, sig: &mpi::Signature, hash_algo: HashAlgorithm,
digest: &[u8]) -> Result<()>
{
use crate::crypto::mpi::Signature;
let ok = match (self.mpis(), sig) {
(PublicKey::RSA { e, n }, Signature::RSA { s }) => {
let key = rsa::PublicKey::new(n.value(), e.value())?;
rsa::verify_digest_pkcs1(&key, digest, hash_algo.oid()?,
s.value())?
},
(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)
},
(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 key {} and signature {:?}.",
self.pk_algo(), sig)).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;
impl<R> Key4<SecretParts, R>
where R: key::KeyRole,
{
#[allow(clippy::many_single_char_names)]
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(crate::now),
PublicKeyAlgorithm::RSAEncryptSign,
mpi::PublicKey::RSA {
e: mpi::MPI::new(&key.e()[..]),
n: mpi::MPI::new(&key.n()[..]),
},
mpi::SecretKeyMaterial::RSA {
d: d.into(),
p: a.into(),
q: b.into(),
u: 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()),
n: MPI::new(&*public.n()),
};
let private_mpis = mpi::SecretKeyMaterial::RSA {
d: private.d().into(),
p: p.into(),
q: q.into(),
u: u.into(),
};
Self::with_secret(
crate::now(),
PublicKeyAlgorithm::RSAEncryptSign,
public_mpis,
private_mpis.into())
}
pub(crate) fn generate_ecc_backend(for_signing: bool, curve: Curve)
-> Result<(PublicKeyAlgorithm,
mpi::PublicKey,
mpi::SecretKeyMaterial)>
{
let mut rng = Yarrow::default();
match (curve.clone(), for_signing) {
(Curve::Ed25519, true) =>
unreachable!("handled in Key4::generate_ecc"),
(Curve::Cv25519, false) =>
unreachable!("handled in Key4::generate_ecc"),
(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: private.as_bytes().into(),
};
Ok((PublicKeyAlgorithm::ECDSA, public_mpis, private_mpis))
}
(Curve::NistP256, false) | (Curve::NistP384, false)
| (Curve::NistP521, false) => {
let (private, field_sz) = match curve {
Curve::NistP256 => {
let pv =
ecc::Scalar::new_random::<ecc::Secp256r1, _>(&mut rng);
(pv, 256)
}
Curve::NistP384 => {
let pv =
ecc::Scalar::new_random::<ecc::Secp384r1, _>(&mut rng);
(pv, 384)
}
Curve::NistP521 => {
let pv =
ecc::Scalar::new_random::<ecc::Secp521r1, _>(&mut rng);
(pv, 521)
}
_ => unreachable!(),
};
let public = ecdh::point_mul_g(&private);
let (pub_x, pub_y) = public.as_bytes();
let public_mpis = mpi::PublicKey::ECDH{
q: MPI::new_point(&pub_x, &pub_y, field_sz),
hash:
crate::crypto::ecdh::default_ecdh_kdf_hash(&curve),
sym:
crate::crypto::ecdh::default_ecdh_kek_cipher(&curve),
curve,
};
let private_mpis = mpi::SecretKeyMaterial::ECDH{
scalar: private.as_bytes().into(),
};
Ok((PublicKeyAlgorithm::ECDH, public_mpis, private_mpis))
}
_ => Err(Error::UnsupportedEllipticCurve(curve).into()),
}
}
}