#[cfg(not(feature = "openssl"))]
use std::marker::PhantomData;
#[cfg(feature = "openssl")]
use openssl::bn::BigNumContext;
#[cfg(feature = "openssl")]
use openssl::ec::{EcGroup, EcKey, PointConversionForm};
#[cfg(feature = "openssl")]
use openssl::nid::Nid;
#[cfg(feature = "openssl")]
use openssl::pkey::PKey;
#[cfg(feature = "openssl")]
use openssl::rsa::Rsa as OpenSslRsa;
#[cfg(feature = "openssl")]
use openssl::sign::Signer;
#[cfg(feature = "ring")]
use ring::{
rand,
signature::{
EcdsaKeyPair, Ed25519KeyPair, KeyPair as RingKeyPair, ECDSA_P256_SHA256_FIXED_SIGNING,
ECDSA_P384_SHA384_FIXED_SIGNING,
},
};
#[allow(deprecated)]
use trust_dns_proto::rr::dnssec::rdata::key::{KeyTrust, Protocol, UpdateScope};
use crate::error::*;
#[cfg(any(feature = "openssl", feature = "ring"))]
use crate::rr::dnssec::DigestType;
use crate::rr::dnssec::{Algorithm, PublicKeyBuf};
use crate::rr::dnssec::{HasPrivate, HasPublic, Private, TBS};
use crate::rr::rdata::key::KeyUsage;
#[cfg(any(feature = "openssl", feature = "ring"))]
use crate::rr::rdata::DS;
use crate::rr::rdata::{DNSKEY, KEY};
#[cfg(any(feature = "openssl", feature = "ring"))]
use crate::rr::Name;
#[allow(clippy::large_enum_variant)]
pub enum KeyPair<K> {
#[cfg(feature = "openssl")]
#[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
RSA(PKey<K>),
#[cfg(feature = "openssl")]
#[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
EC(PKey<K>),
#[cfg(not(feature = "openssl"))]
#[doc(hidden)]
Phantom(PhantomData<K>),
#[cfg(feature = "ring")]
#[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
ECDSA(EcdsaKeyPair),
#[cfg(feature = "ring")]
#[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
ED25519(Ed25519KeyPair),
}
impl<K> KeyPair<K> {
#[cfg(feature = "openssl")]
#[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
pub fn from_rsa(rsa: OpenSslRsa<K>) -> DnsSecResult<Self> {
PKey::from_rsa(rsa).map(Self::RSA).map_err(Into::into)
}
#[cfg(feature = "openssl")]
#[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
pub fn from_rsa_pkey(pkey: PKey<K>) -> Self {
Self::RSA(pkey)
}
#[cfg(feature = "openssl")]
#[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
pub fn from_ec_key(ec_key: EcKey<K>) -> DnsSecResult<Self> {
PKey::from_ec_key(ec_key).map(Self::EC).map_err(Into::into)
}
#[cfg(feature = "openssl")]
#[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
pub fn from_ec_pkey(pkey: PKey<K>) -> Self {
Self::EC(pkey)
}
#[cfg(feature = "ring")]
#[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
pub fn from_ecdsa(ec_key: EcdsaKeyPair) -> Self {
Self::ECDSA(ec_key)
}
#[cfg(feature = "ring")]
#[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
pub fn from_ed25519(ed_key: Ed25519KeyPair) -> Self {
Self::ED25519(ed_key)
}
}
impl<K: HasPublic> KeyPair<K> {
pub fn to_public_bytes(&self) -> DnsSecResult<Vec<u8>> {
#[allow(unreachable_patterns)]
match *self {
#[cfg(feature = "openssl")]
Self::RSA(ref pkey) => {
let mut bytes: Vec<u8> = Vec::new();
let rsa: OpenSslRsa<K> = pkey
.rsa()
.expect("pkey should have been initialized with RSA");
let e: Vec<u8> = rsa.e().to_vec();
let n: Vec<u8> = rsa.n().to_vec();
if e.len() > 255 {
bytes.push(0);
bytes.push((e.len() >> 8) as u8);
}
bytes.push(e.len() as u8);
bytes.extend_from_slice(&e);
bytes.extend_from_slice(&n);
Ok(bytes)
}
#[cfg(feature = "openssl")]
Self::EC(ref pkey) => {
let ec_key: EcKey<K> = pkey
.ec_key()
.expect("pkey should have been initialized with EC");
let group = ec_key.group();
let point = ec_key.public_key();
let mut bytes = BigNumContext::new()
.and_then(|mut ctx| {
point.to_bytes(group, PointConversionForm::UNCOMPRESSED, &mut ctx)
})
.map_err(DnsSecError::from)?;
bytes.remove(0);
Ok(bytes)
}
#[cfg(feature = "ring")]
Self::ECDSA(ref ec_key) => {
let mut bytes: Vec<u8> = ec_key.public_key().as_ref().to_vec();
bytes.remove(0);
Ok(bytes)
}
#[cfg(feature = "ring")]
Self::ED25519(ref ed_key) => Ok(ed_key.public_key().as_ref().to_vec()),
#[cfg(not(feature = "openssl"))]
Self::Phantom(..) => panic!("Phantom disallowed"),
#[cfg(not(any(feature = "openssl", feature = "ring")))]
_ => Err(DnsSecErrorKind::Message("openssl or ring feature(s) not enabled").into()),
}
}
pub fn to_public_key(&self) -> DnsSecResult<PublicKeyBuf> {
Ok(PublicKeyBuf::new(self.to_public_bytes()?))
}
pub fn key_tag(&self) -> DnsSecResult<u16> {
let mut ac: usize = 0;
for (i, k) in self.to_public_bytes()?.iter().enumerate() {
ac += if i & 0x0001 == 0x0001 {
*k as usize
} else {
(*k as usize) << 8
};
}
ac += (ac >> 16) & 0xFFFF;
Ok((ac & 0xFFFF) as u16) }
pub fn to_dnskey(&self, algorithm: Algorithm) -> DnsSecResult<DNSKEY> {
self.to_public_bytes()
.map(|bytes| DNSKEY::new(true, true, false, algorithm, bytes))
}
pub fn to_sig0key(&self, algorithm: Algorithm) -> DnsSecResult<KEY> {
self.to_sig0key_with_usage(algorithm, KeyUsage::default())
}
pub fn to_sig0key_with_usage(
&self,
algorithm: Algorithm,
usage: KeyUsage,
) -> DnsSecResult<KEY> {
self.to_public_bytes().map(|bytes| {
KEY::new(
KeyTrust::default(),
usage,
#[allow(deprecated)]
UpdateScope::default(),
Protocol::default(),
algorithm,
bytes,
)
})
}
#[cfg(any(feature = "openssl", feature = "ring"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "openssl", feature = "ring"))))]
pub fn to_ds(
&self,
name: &Name,
algorithm: Algorithm,
digest_type: DigestType,
) -> DnsSecResult<DS> {
self.to_dnskey(algorithm)
.and_then(|dnskey| self.key_tag().map(|key_tag| (key_tag, dnskey)))
.and_then(|(key_tag, dnskey)| {
dnskey
.to_digest(name, digest_type)
.map(|digest| (key_tag, digest))
.map_err(Into::into)
})
.map(|(key_tag, digest)| {
DS::new(key_tag, algorithm, digest_type, digest.as_ref().to_owned())
})
}
}
impl<K: HasPrivate> KeyPair<K> {
#[allow(unused)]
pub fn sign(&self, algorithm: Algorithm, tbs: &TBS) -> DnsSecResult<Vec<u8>> {
use std::iter;
match *self {
#[cfg(feature = "openssl")]
Self::RSA(ref pkey) | Self::EC(ref pkey) => {
let digest_type = DigestType::from(algorithm).to_openssl_digest()?;
let mut signer = Signer::new(digest_type, pkey)?;
signer.update(tbs.as_ref())?;
signer.sign_to_vec().map_err(Into::into).and_then(|bytes| {
if let Self::RSA(_) = *self {
return Ok(bytes);
}
if bytes.len() < 8 {
return Err("unexpected signature format (length too short)".into());
}
let expect = |pos: usize, expected: u8| -> DnsSecResult<()> {
if bytes[pos] != expected {
return Err(format!(
"unexpected signature format ({}, {}))",
pos, expected
)
.into());
}
Ok(())
};
expect(0, 0x30)?;
expect(1, (bytes.len() - 2) as u8)?;
expect(2, 0x02)?;
let p1_len = bytes[3] as usize;
let p2_pos = 4 + p1_len;
expect(p2_pos, 0x02)?;
let p2_len = bytes[p2_pos + 1] as usize;
if p2_pos + 2 + p2_len > bytes.len() {
return Err("unexpected signature format (invalid length)".into());
}
let p1 = &bytes[4..p2_pos];
let p2 = &bytes[p2_pos + 2..p2_pos + 2 + p2_len];
let part_len = match algorithm {
Algorithm::ECDSAP256SHA256 => 32,
Algorithm::ECDSAP384SHA384 => 48,
_ => return Err("unexpected algorithm".into()),
};
let mut ret = Vec::<u8>::new();
{
let mut write_part = |mut part: &[u8]| -> DnsSecResult<()> {
if part.len() > part_len + 1 {
return Err("invalid signature data".into());
}
if part.len() == part_len + 1 {
if part[0] != 0x00 {
return Err("invalid signature data".into());
}
part = &part[1..];
}
ret.extend(iter::repeat(0x00).take(part_len - part.len()));
ret.extend(part);
Ok(())
};
write_part(p1)?;
write_part(p2)?;
}
assert_eq!(ret.len(), part_len * 2);
Ok(ret)
})
}
#[cfg(feature = "ring")]
Self::ECDSA(ref ec_key) => {
let rng = rand::SystemRandom::new();
Ok(ec_key.sign(&rng, tbs.as_ref())?.as_ref().to_vec())
}
#[cfg(feature = "ring")]
Self::ED25519(ref ed_key) => Ok(ed_key.sign(tbs.as_ref()).as_ref().to_vec()),
#[cfg(not(feature = "openssl"))]
Self::Phantom(..) => panic!("Phantom disallowed"),
#[cfg(not(any(feature = "openssl", feature = "ring")))]
_ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
}
}
}
impl KeyPair<Private> {
pub fn generate(algorithm: Algorithm) -> DnsSecResult<Self> {
#[allow(deprecated)]
match algorithm {
Algorithm::Unknown(_) => Err(DnsSecErrorKind::Message("unknown algorithm").into()),
#[cfg(feature = "openssl")]
Algorithm::RSASHA1
| Algorithm::RSASHA1NSEC3SHA1
| Algorithm::RSASHA256
| Algorithm::RSASHA512 => {
OpenSslRsa::generate(2048)
.map_err(Into::into)
.and_then(Self::from_rsa)
}
#[cfg(feature = "openssl")]
Algorithm::ECDSAP256SHA256 => EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)
.and_then(|group| EcKey::generate(&group))
.map_err(Into::into)
.and_then(Self::from_ec_key),
#[cfg(feature = "openssl")]
Algorithm::ECDSAP384SHA384 => EcGroup::from_curve_name(Nid::SECP384R1)
.and_then(|group| EcKey::generate(&group))
.map_err(Into::into)
.and_then(Self::from_ec_key),
#[cfg(feature = "ring")]
Algorithm::ED25519 => Err(DnsSecErrorKind::Message(
"use generate_pkcs8 for generating private key and encoding",
)
.into()),
_ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
}
}
#[cfg(feature = "ring")]
#[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
pub fn generate_pkcs8(algorithm: Algorithm) -> DnsSecResult<Vec<u8>> {
#[allow(deprecated)]
match algorithm {
Algorithm::Unknown(_) => Err(DnsSecErrorKind::Message("unknown algorithm").into()),
#[cfg(feature = "openssl")]
Algorithm::RSASHA1
| Algorithm::RSASHA1NSEC3SHA1
| Algorithm::RSASHA256
| Algorithm::RSASHA512 => {
Err(DnsSecErrorKind::Message("openssl does not yet support pkcs8").into())
}
#[cfg(feature = "ring")]
Algorithm::ECDSAP256SHA256 => {
let rng = rand::SystemRandom::new();
EcdsaKeyPair::generate_pkcs8(&ECDSA_P256_SHA256_FIXED_SIGNING, &rng)
.map_err(Into::into)
.map(|pkcs8_bytes| pkcs8_bytes.as_ref().to_vec())
}
#[cfg(feature = "ring")]
Algorithm::ECDSAP384SHA384 => {
let rng = rand::SystemRandom::new();
EcdsaKeyPair::generate_pkcs8(&ECDSA_P384_SHA384_FIXED_SIGNING, &rng)
.map_err(Into::into)
.map(|pkcs8_bytes| pkcs8_bytes.as_ref().to_vec())
}
#[cfg(feature = "ring")]
Algorithm::ED25519 => {
let rng = rand::SystemRandom::new();
Ed25519KeyPair::generate_pkcs8(&rng)
.map_err(Into::into)
.map(|pkcs8_bytes| pkcs8_bytes.as_ref().to_vec())
}
_ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
}
}
}
#[cfg(any(feature = "openssl", feature = "ring"))]
#[cfg(test)]
mod tests {
use crate::rr::dnssec::TBS;
use crate::rr::dnssec::*;
#[cfg(feature = "openssl")]
#[test]
fn test_rsa() {
public_key_test(Algorithm::RSASHA256, KeyFormat::Der);
hash_test(Algorithm::RSASHA256, KeyFormat::Der);
}
#[cfg(feature = "openssl")]
#[test]
fn test_ec_p256() {
public_key_test(Algorithm::ECDSAP256SHA256, KeyFormat::Der);
hash_test(Algorithm::ECDSAP256SHA256, KeyFormat::Der);
}
#[cfg(feature = "ring")]
#[test]
fn test_ec_p256_pkcs8() {
public_key_test(Algorithm::ECDSAP256SHA256, KeyFormat::Pkcs8);
hash_test(Algorithm::ECDSAP256SHA256, KeyFormat::Pkcs8);
}
#[cfg(feature = "openssl")]
#[test]
fn test_ec_p384() {
public_key_test(Algorithm::ECDSAP384SHA384, KeyFormat::Der);
hash_test(Algorithm::ECDSAP384SHA384, KeyFormat::Der);
}
#[cfg(feature = "ring")]
#[test]
fn test_ec_p384_pkcs8() {
public_key_test(Algorithm::ECDSAP384SHA384, KeyFormat::Pkcs8);
hash_test(Algorithm::ECDSAP384SHA384, KeyFormat::Pkcs8);
}
#[cfg(feature = "ring")]
#[test]
fn test_ed25519() {
public_key_test(Algorithm::ED25519, KeyFormat::Pkcs8);
hash_test(Algorithm::ED25519, KeyFormat::Pkcs8);
}
fn public_key_test(algorithm: Algorithm, key_format: KeyFormat) {
let key = key_format
.decode_key(
&key_format.generate_and_encode(algorithm, None).unwrap(),
None,
algorithm,
)
.unwrap();
let pk = key.to_public_key().unwrap();
let tbs = TBS::from(&b"www.example.com"[..]);
let mut sig = key.sign(algorithm, &tbs).unwrap();
assert!(
pk.verify(algorithm, tbs.as_ref(), &sig).is_ok(),
"algorithm: {:?} (public key)",
algorithm
);
sig[10] = !sig[10];
assert!(
pk.verify(algorithm, tbs.as_ref(), &sig).is_err(),
"algorithm: {:?} (public key, neg)",
algorithm
);
}
fn hash_test(algorithm: Algorithm, key_format: KeyFormat) {
let tbs = TBS::from(&b"www.example.com"[..]);
let key = key_format
.decode_key(
&key_format.generate_and_encode(algorithm, None).unwrap(),
None,
algorithm,
)
.unwrap();
let pub_key = key.to_public_key().unwrap();
let neg = key_format
.decode_key(
&key_format.generate_and_encode(algorithm, None).unwrap(),
None,
algorithm,
)
.unwrap();
let neg_pub_key = neg.to_public_key().unwrap();
let sig = key.sign(algorithm, &tbs).unwrap();
assert!(
pub_key.verify(algorithm, tbs.as_ref(), &sig).is_ok(),
"algorithm: {:?}",
algorithm
);
assert!(
key.to_dnskey(algorithm)
.unwrap()
.verify(tbs.as_ref(), &sig)
.is_ok(),
"algorithm: {:?} (dnskey)",
algorithm
);
assert!(
neg_pub_key.verify(algorithm, tbs.as_ref(), &sig).is_err(),
"algorithm: {:?} (neg)",
algorithm
);
assert!(
neg.to_dnskey(algorithm)
.unwrap()
.verify(tbs.as_ref(), &sig)
.is_err(),
"algorithm: {:?} (dnskey, neg)",
algorithm
);
}
}