#![cfg(feature = "unstable-crypto-backend")]
#![cfg_attr(docsrs, doc(cfg(any(feature = "ring", feature = "openssl"))))]
const _: () = {
assert!(
cfg!(any(feature = "ring", feature = "openssl")),
"Do not enable the 'unstable-crypto-backend' feature directly, enable 'ring' or 'openssl' instead",
);
};
use core::fmt;
use std::error;
use std::vec::Vec;
use crate::rdata::Dnskey;
#[cfg(feature = "openssl")]
use super::openssl;
#[cfg(feature = "ring")]
use super::ring;
pub enum DigestType {
Sha1,
Sha256,
Sha384,
}
pub enum DigestBuilder {
#[cfg(feature = "ring")]
Ring(ring::DigestBuilder),
#[cfg(feature = "openssl")]
Openssl(openssl::DigestBuilder),
}
impl DigestBuilder {
#[allow(unreachable_code)]
pub fn new(digest_type: DigestType) -> Self {
#[cfg(feature = "ring")]
return Self::Ring(ring::DigestBuilder::new(digest_type));
#[cfg(feature = "openssl")]
return Self::Openssl(openssl::DigestBuilder::new(digest_type));
}
pub fn update(&mut self, data: &[u8]) {
match self {
#[cfg(feature = "ring")]
DigestBuilder::Ring(digest_context) => {
digest_context.update(data)
}
#[cfg(feature = "openssl")]
DigestBuilder::Openssl(digest_context) => {
digest_context.update(data)
}
}
}
pub fn finish(self) -> Digest {
match self {
#[cfg(feature = "ring")]
DigestBuilder::Ring(digest_context) => {
Digest::Ring(digest_context.finish())
}
#[cfg(feature = "openssl")]
DigestBuilder::Openssl(digest_context) => {
Digest::Openssl(digest_context.finish())
}
}
}
}
pub enum Digest {
#[cfg(feature = "ring")]
Ring(ring::Digest),
#[cfg(feature = "openssl")]
Openssl(openssl::Digest),
}
impl AsRef<[u8]> for Digest {
fn as_ref(&self) -> &[u8] {
match self {
#[cfg(feature = "ring")]
Digest::Ring(digest) => digest.as_ref(),
#[cfg(feature = "openssl")]
Digest::Openssl(digest) => digest.as_ref(),
}
}
}
pub enum PublicKey {
#[cfg(feature = "ring")]
Ring(ring::PublicKey),
#[cfg(feature = "openssl")]
Openssl(openssl::PublicKey),
}
impl PublicKey {
#[allow(unreachable_code)]
pub fn from_dnskey(
dnskey: &Dnskey<impl AsRef<[u8]>>,
) -> Result<Self, AlgorithmError> {
#[cfg(feature = "ring")]
return Ok(Self::Ring(ring::PublicKey::from_dnskey(dnskey)?));
#[cfg(all(feature = "openssl", not(feature = "ring")))]
return Ok(Self::Openssl(openssl::PublicKey::from_dnskey(dnskey)?));
#[cfg(not(any(feature = "ring", feature = "openssl")))]
compile_error!("Either feature \"ring\" or \"openssl\" must be enabled for this crate.");
}
pub fn verify(
&self,
signed_data: &[u8],
signature: &[u8],
) -> Result<(), AlgorithmError> {
match self {
#[cfg(feature = "ring")]
PublicKey::Ring(public_key) => {
public_key.verify(signed_data, signature)
}
#[cfg(feature = "openssl")]
PublicKey::Openssl(public_key) => {
public_key.verify(signed_data, signature)
}
}
}
}
pub fn rsa_exponent_modulus(
dnskey: &Dnskey<impl AsRef<[u8]>>,
min_len: usize,
) -> Result<(Vec<u8>, Vec<u8>), AlgorithmError> {
let public_key: &[u8] = dnskey.public_key().as_ref();
let (exp_len, rest) = match *public_key {
[exp_len @ 1..=255, ref rest @ ..] => (exp_len as usize, rest),
[0, hi @ 1..=255, lo, ref rest @ ..] => {
let exp_len = u16::from_be_bytes([hi, lo]);
(exp_len as usize, rest)
}
_ => return Err(AlgorithmError::InvalidData),
};
if rest.len() < exp_len {
return Err(AlgorithmError::InvalidData);
}
let (exp, num) = rest.split_at(exp_len);
for i in [exp, num] {
if !(1..=512).contains(&i.len()) || i[0] == 0 {
return Err(AlgorithmError::InvalidData);
}
}
if num.len() < min_len {
return Err(AlgorithmError::Unsupported);
}
Ok((exp.to_vec(), num.to_vec()))
}
pub fn rsa_encode(mut e: &[u8], mut n: &[u8]) -> Vec<u8> {
fn trim_leading_zeroes(bytes: &[u8]) -> &[u8] {
bytes
.iter()
.position(|&v| v != 0)
.map(|idx| &bytes[idx..])
.unwrap_or_default()
}
let mut key = Vec::new();
e = trim_leading_zeroes(e);
n = trim_leading_zeroes(n);
if let Ok(exp_len) = u8::try_from(e.len()) {
key.reserve_exact(1 + e.len() + n.len());
key.push(exp_len);
} else if let Ok(exp_len) = u16::try_from(e.len()) {
key.reserve_exact(3 + e.len() + n.len());
key.push(0u8);
key.extend(&exp_len.to_be_bytes());
} else {
unreachable!("RSA exponents are (much) shorter than 64KiB")
}
key.extend(e);
key.extend(n);
key
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AlgorithmError {
Unsupported,
BadSig,
InvalidData,
}
impl fmt::Display for AlgorithmError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
AlgorithmError::Unsupported => "unsupported algorithm",
AlgorithmError::BadSig => "bad signature",
AlgorithmError::InvalidData => "invalid data",
})
}
}
impl error::Error for AlgorithmError {}
#[derive(Clone, Debug)]
pub enum FromDnskeyError {
UnsupportedAlgorithm,
UnsupportedProtocol,
InvalidKey,
}
impl fmt::Display for FromDnskeyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::UnsupportedAlgorithm => "unsupported algorithm",
Self::UnsupportedProtocol => "unsupported protocol",
Self::InvalidKey => "malformed key",
})
}
}
impl error::Error for FromDnskeyError {}
#[cfg(test)]
mod tests {
use crate::crypto::common::rsa_encode;
#[test]
fn test_rsa_encode() {
assert_eq!(rsa_encode(&[], &[]), &[0]);
assert_eq!(rsa_encode(&[1], &[]), &[1, 1]);
assert_eq!(rsa_encode(&[], &[1]), &[0, 1]);
assert_eq!(rsa_encode(&[1], &[1]), &[1, 1, 1]);
assert_eq!(rsa_encode(&[0, 1], &[1]), &[1, 1, 1]);
assert_eq!(rsa_encode(&[1], &[0, 1]), &[1, 1, 1]);
assert_eq!(rsa_encode(&[0, 1], &[0, 1]), &[1, 1, 1]);
assert_eq!(rsa_encode(&[0, 0, 1], &[0, 1]), &[1, 1, 1]);
assert_eq!(rsa_encode(&[0, 1], &[0, 0, 1]), &[1, 1, 1]);
assert_eq!(rsa_encode(&[0, 1, 1], &[0, 0, 1]), &[2, 1, 1, 1]);
assert_eq!(rsa_encode(&[1, 2], &[]), &[2, 1, 2]);
assert_eq!(rsa_encode(&[], &[1, 2]), &[0, 1, 2]);
assert_eq!(rsa_encode(&[1, 2], &[1, 2]), &[2, 1, 2, 1, 2]);
assert_eq!(rsa_encode(&[0, 1, 2], &[1]), &[2, 1, 2, 1]);
assert_eq!(rsa_encode(&[1], &[0, 1, 2]), &[1, 1, 1, 2]);
assert_eq!(rsa_encode(&[0, 1, 2], &[0, 1, 2]), &[2, 1, 2, 1, 2]);
}
}