Enhance error handling and docs

This commit is contained in:
Jan Rüth 2023-11-24 19:38:18 +01:00 committed by Jan
commit bd80bfc4d7
18 changed files with 295 additions and 206 deletions

View file

@ -15,6 +15,7 @@ impl Algorithm {
/// AES-128 in Galois Counter Mode. /// AES-128 in Galois Counter Mode.
/// ///
/// Note: AES-GCM should only be used with 12-byte (96-bit) nonces. Although it is specified to take a variable-length nonce, nonces with other lengths are effectively randomized, which means one must consider collisions. Unless implementing an existing protocol which has already specified incorrect parameters, only use 12-byte nonces. /// Note: AES-GCM should only be used with 12-byte (96-bit) nonces. Although it is specified to take a variable-length nonce, nonces with other lengths are effectively randomized, which means one must consider collisions. Unless implementing an existing protocol which has already specified incorrect parameters, only use 12-byte nonces.
#[must_use]
pub fn aes_128_gcm() -> Self { pub fn aes_128_gcm() -> Self {
Self(unsafe { boring_sys::EVP_aead_aes_128_gcm() }) Self(unsafe { boring_sys::EVP_aead_aes_128_gcm() })
} }
@ -22,38 +23,45 @@ impl Algorithm {
/// AES-256 in Galois Counter Mode. /// AES-256 in Galois Counter Mode.
/// ///
/// Note: AES-GCM should only be used with 12-byte (96-bit) nonces. Although it is specified to take a variable-length nonce, nonces with other lengths are effectively randomized, which means one must consider collisions. Unless implementing an existing protocol which has already specified incorrect parameters, only use 12-byte nonces. /// Note: AES-GCM should only be used with 12-byte (96-bit) nonces. Although it is specified to take a variable-length nonce, nonces with other lengths are effectively randomized, which means one must consider collisions. Unless implementing an existing protocol which has already specified incorrect parameters, only use 12-byte nonces.
#[must_use]
pub fn aes_256_gcm() -> Self { pub fn aes_256_gcm() -> Self {
Self(unsafe { boring_sys::EVP_aead_aes_256_gcm() }) Self(unsafe { boring_sys::EVP_aead_aes_256_gcm() })
} }
/// ChaCha20 and Poly1305 as described in RFC 8439. /// `ChaCha20` with `Poly1305` as described in RFC 8439.
#[must_use]
pub fn chacha20_poly1305() -> Self { pub fn chacha20_poly1305() -> Self {
Self(unsafe { boring_sys::EVP_aead_chacha20_poly1305() }) Self(unsafe { boring_sys::EVP_aead_chacha20_poly1305() })
} }
/// ChaCha20-Poly1305 with an extended nonce that makes random generation of nonces safe. /// ChaCha20-Poly1305 with an extended nonce that makes random generation of nonces safe.
#[allow(unused)] #[allow(unused)]
#[must_use]
pub fn xchacha20_poly1305() -> Self { pub fn xchacha20_poly1305() -> Self {
Self(unsafe { boring_sys::EVP_aead_xchacha20_poly1305() }) Self(unsafe { boring_sys::EVP_aead_xchacha20_poly1305() })
} }
/// Returns the length, in bytes, of the keys used by aead /// Returns the length, in bytes, of the keys used by aead
#[must_use]
pub fn key_length(&self) -> usize { pub fn key_length(&self) -> usize {
unsafe { boring_sys::EVP_AEAD_key_length(self.0) } unsafe { boring_sys::EVP_AEAD_key_length(self.0) }
} }
/// Returns the maximum number of additional bytes added by the act of sealing data with aead. /// Returns the maximum number of additional bytes added by the act of sealing data with aead.
#[must_use]
pub fn max_overhead(&self) -> usize { pub fn max_overhead(&self) -> usize {
unsafe { boring_sys::EVP_AEAD_max_overhead(self.0) } unsafe { boring_sys::EVP_AEAD_max_overhead(self.0) }
} }
/// Returns the maximum tag length when using aead. /// Returns the maximum tag length when using aead.
#[allow(unused)] #[allow(unused)]
#[must_use]
pub fn max_tag_len(&self) -> usize { pub fn max_tag_len(&self) -> usize {
unsafe { boring_sys::EVP_AEAD_max_tag_len(self.0) } unsafe { boring_sys::EVP_AEAD_max_tag_len(self.0) }
} }
/// Returns the length, in bytes, of the per-message nonce for aead. /// Returns the length, in bytes, of the per-message nonce for aead.
#[must_use]
pub fn nonce_len(&self) -> usize { pub fn nonce_len(&self) -> usize {
unsafe { boring_sys::EVP_AEAD_nonce_length(self.0) } unsafe { boring_sys::EVP_AEAD_nonce_length(self.0) }
} }
@ -66,7 +74,14 @@ pub struct Crypter {
} }
impl Crypter { impl Crypter {
pub fn new(aead_alg: Algorithm, key: &[u8]) -> Result<Self, ErrorStack> { /// Constructs a new AEAD crypter with the given algorithm and key
///
/// # Errors
/// Returns the `BoringSSL` error in case of an internal error
///
/// # Panics
/// * If the key length mismatches the `aead_alg` required key length
pub fn new(aead_alg: &Algorithm, key: &[u8]) -> Result<Self, ErrorStack> {
assert_eq!(aead_alg.key_length(), key.len()); assert_eq!(aead_alg.key_length(), key.len());
boring_sys::init(); boring_sys::init();
@ -86,13 +101,25 @@ impl Crypter {
Ok(this) Ok(this)
} }
/// Returns the maximum required overhead in bytes
/// that will be added to a ciphertext, e.g.,
/// to hold an authentication tag.
#[must_use]
pub fn max_overhead(&self) -> usize { pub fn max_overhead(&self) -> usize {
self.max_overhead self.max_overhead
} }
/// Encrypts and authenticates buffer and authenticates associated_data. /// Encrypts and authenticates `buffer` and authenticates `associated_data`.
/// It writes the ciphertext to buffer and the authentication tag to tag. /// It writes the ciphertext to `buffer` and the authentication tag to `tag`.
/// On success, it returns the actual length of the tag /// `tag` needs to have sufficient space, see [`Self::max_overhead()`](fn@Self::max_overhead())
/// On success, it returns the actual length of the `tag`
///
/// # Errors
/// In case of an error, returns the `BoringSSL` error
///
/// # Panics
/// * If the `nonce` is not the expected lenght
/// * If the `tag` has not enough space
pub fn seal_in_place( pub fn seal_in_place(
&self, &self,
nonce: &[u8], nonce: &[u8],
@ -124,6 +151,14 @@ impl Crypter {
Ok(tag_len) Ok(tag_len)
} }
/// Decrypts and authenticates `buffer` and authenticates `associated_data`.
/// It writes the cleartext to `buffer` and validates using `tag`.
///
/// # Errors
/// In case of an error, returns the `BoringSSL` error
///
/// # Panics
/// * if the nonce has the wrong lenght
pub fn open_in_place( pub fn open_in_place(
&self, &self,
nonce: &[u8], nonce: &[u8],
@ -157,25 +192,19 @@ mod tests {
#[test] #[test]
fn in_out() { fn in_out() {
let key = Crypter::new(super::Algorithm::aes_128_gcm(), &[0u8; 16]).unwrap(); let key = Crypter::new(&super::Algorithm::aes_128_gcm(), &[0u8; 16]).unwrap();
let nonce = [0u8; 12]; let nonce = [0u8; 12];
let associated_data = b"this is signed"; let associated_data = b"this is authenticated";
let mut buffer = Vec::with_capacity(26); let mut buffer = Vec::with_capacity(26);
buffer.push(b'A'); buffer.extend_from_slice(b"ABCDE");
buffer.push(b'B');
buffer.push(b'C');
buffer.push(b'D');
buffer.push(b'E');
let mut tag = [0u8; 16]; let mut tag = [0u8; 16];
key.seal_in_place(&nonce, associated_data, buffer.as_mut_slice(), &mut tag) key.seal_in_place(&nonce, associated_data, buffer.as_mut_slice(), &mut tag)
.unwrap(); .unwrap();
println!("Encrypted: {:02X?}, Tag: {:02X?}", buffer, tag);
key.open_in_place(&nonce, associated_data, buffer.as_mut_slice(), &tag[..]) key.open_in_place(&nonce, associated_data, buffer.as_mut_slice(), &tag[..])
.unwrap(); .unwrap();
println!("Plaintext: {}", String::from_utf8(buffer).unwrap()); assert_eq!(b"ABCDE", buffer.as_slice());
} }
} }

View file

@ -2,11 +2,11 @@ use std::os::raw::c_int;
use boring::error::ErrorStack; use boring::error::ErrorStack;
/// Check the value returned from a BoringSSL ffi call /// Check the value returned from a `BoringSSL` ffi call
/// that returns a pointer. /// that returns a pointer.
/// ///
/// If the pointer is null, this method returns the BoringSSL /// If the pointer is null, this method returns the
/// ErrorStack as Err, the pointer otherwise. /// [`boring::error::ErrorStack`] as Err, the pointer otherwise.
pub(crate) fn cvt_p<T>(r: *mut T) -> Result<*mut T, ErrorStack> { pub(crate) fn cvt_p<T>(r: *mut T) -> Result<*mut T, ErrorStack> {
if r.is_null() { if r.is_null() {
Err(ErrorStack::get()) Err(ErrorStack::get())
@ -15,10 +15,10 @@ pub(crate) fn cvt_p<T>(r: *mut T) -> Result<*mut T, ErrorStack> {
} }
} }
/// Check the value returned from a BoringSSL ffi call that /// Check the value returned from a `BoringSSL` ffi call that
/// returns a integer. /// returns a integer.
/// ///
/// Returns the BoringSSL Errorstack when the result is <= 0. /// Returns the [`boring::error::ErrorStack`] when the result is <= 0.
/// And forwards the return code otherwise /// And forwards the return code otherwise
pub(crate) fn cvt(r: c_int) -> Result<i32, ErrorStack> { pub(crate) fn cvt(r: c_int) -> Result<i32, ErrorStack> {
if r <= 0 { if r <= 0 {

View file

@ -38,11 +38,14 @@ unsafe impl ForeignType for HmacCtx {
impl Clone for HmacCtx { impl Clone for HmacCtx {
fn clone(&self) -> Self { fn clone(&self) -> Self {
unsafe { unsafe {
let ctx = HmacCtx::from_ptr(cvt_p(boring_sys::HMAC_CTX_new()).unwrap()); cvt_p(boring_sys::HMAC_CTX_new())
.map(|ctx| HmacCtx::from_ptr(ctx))
cvt(boring_sys::HMAC_CTX_copy(ctx.as_ptr(), self.0.as_ptr())).unwrap(); .and_then(|ctx| {
ctx cvt(boring_sys::HMAC_CTX_copy(ctx.as_ptr(), self.0.as_ptr()))?;
Ok(ctx)
})
} }
.expect("failed cloning hmac ctx")
} }
} }

View file

@ -6,7 +6,7 @@ use boring_additions::aead::Algorithm;
use rustls::crypto::cipher::{self, make_tls12_aad, make_tls13_aad, Iv}; use rustls::crypto::cipher::{self, make_tls12_aad, make_tls13_aad, Iv};
use rustls::{ConnectionTrafficSecrets, ContentType, ProtocolVersion}; use rustls::{ConnectionTrafficSecrets, ContentType, ProtocolVersion};
use crate::helper::error_stack_to_aead_error; use crate::helper::log_and_map;
pub(crate) mod aes; pub(crate) mod aes;
pub(crate) mod chacha20; pub(crate) mod chacha20;
@ -47,6 +47,7 @@ impl<T: BoringAead> AeadCore for BoringAeadCrypter<T> {
} }
impl<T: BoringAead> BoringAeadCrypter<T> { impl<T: BoringAead> BoringAeadCrypter<T> {
/// Creates a new aead crypter
pub fn new(iv: Iv, key: &[u8], tls_version: ProtocolVersion) -> Result<Self, ErrorStack> { pub fn new(iv: Iv, key: &[u8], tls_version: ProtocolVersion) -> Result<Self, ErrorStack> {
assert!(match tls_version { assert!(match tls_version {
#[cfg(feature = "tls12")] #[cfg(feature = "tls12")]
@ -63,7 +64,7 @@ impl<T: BoringAead> BoringAeadCrypter<T> {
); );
let crypter = BoringAeadCrypter { let crypter = BoringAeadCrypter {
crypter: boring_additions::aead::Crypter::new(cipher, key)?, crypter: boring_additions::aead::Crypter::new(&cipher, key)?,
iv, iv,
tls_version, tls_version,
phantom: PhantomData, phantom: PhantomData,
@ -82,7 +83,7 @@ impl<T: BoringAead> aead::AeadInPlace for BoringAeadCrypter<T> {
let mut tag = Tag::<Self>::default(); let mut tag = Tag::<Self>::default();
self.crypter self.crypter
.seal_in_place(nonce, associated_data, buffer, &mut tag) .seal_in_place(nonce, associated_data, buffer, &mut tag)
.map_err(|e| error_stack_to_aead_error("seal_in_place", e))?; .map_err(|e| log_and_map("seal_in_place", e, aead::Error))?;
Ok(tag) Ok(tag)
} }
@ -96,7 +97,7 @@ impl<T: BoringAead> aead::AeadInPlace for BoringAeadCrypter<T> {
) -> aead::Result<()> { ) -> aead::Result<()> {
self.crypter self.crypter
.open_in_place(nonce, associated_data, buffer, tag) .open_in_place(nonce, associated_data, buffer, tag)
.map_err(|e| error_stack_to_aead_error("open_in_place", e))?; .map_err(|e| log_and_map("open_in_place", e, aead::Error))?;
Ok(()) Ok(())
} }
} }

View file

@ -3,11 +3,10 @@ use aead::consts::{U12, U16};
use boring_additions::aead::Algorithm; use boring_additions::aead::Algorithm;
use rustls::{crypto::cipher, ConnectionTrafficSecrets}; use rustls::{crypto::cipher, ConnectionTrafficSecrets};
/// Aes128 AEAD cipher
pub struct Aes128 {} pub struct Aes128 {}
impl BoringAead for Aes128 {} impl BoringAead for Aes128 {}
unsafe impl Send for Aes128 {}
unsafe impl Sync for Aes128 {}
impl BoringCipher for Aes128 { impl BoringCipher for Aes128 {
fn new_cipher() -> Algorithm { fn new_cipher() -> Algorithm {
@ -37,11 +36,10 @@ impl aead::AeadCore for Aes128 {
type CiphertextOverhead = U16; type CiphertextOverhead = U16;
} }
/// Aes256 AEAD cipher
pub struct Aes256 {} pub struct Aes256 {}
impl BoringAead for Aes256 {} impl BoringAead for Aes256 {}
unsafe impl Send for Aes256 {}
unsafe impl Sync for Aes256 {}
impl BoringCipher for Aes256 { impl BoringCipher for Aes256 {
fn new_cipher() -> Algorithm { fn new_cipher() -> Algorithm {

View file

@ -6,11 +6,10 @@ use aead::{
use boring_additions::aead::Algorithm; use boring_additions::aead::Algorithm;
use rustls::{crypto::cipher, ConnectionTrafficSecrets}; use rustls::{crypto::cipher, ConnectionTrafficSecrets};
/// `ChaCha20` with `Poly1305` cipher
pub struct ChaCha20Poly1305 {} pub struct ChaCha20Poly1305 {}
impl BoringAead for ChaCha20Poly1305 {} impl BoringAead for ChaCha20Poly1305 {}
unsafe impl Send for ChaCha20Poly1305 {}
unsafe impl Sync for ChaCha20Poly1305 {}
impl BoringCipher for ChaCha20Poly1305 { impl BoringCipher for ChaCha20Poly1305 {
fn new_cipher() -> Algorithm { fn new_cipher() -> Algorithm {

View file

@ -1,3 +1,4 @@
use boring::hash::{Hasher, MessageDigest};
use rustls::crypto::hash; use rustls::crypto::hash;
pub const SHA256: &dyn hash::Hash = &Hash(boring::nid::Nid::SHA256); pub const SHA256: &dyn hash::Hash = &Hash(boring::nid::Nid::SHA256);
@ -7,8 +8,8 @@ pub struct Hash(pub boring::nid::Nid);
impl hash::Hash for Hash { impl hash::Hash for Hash {
fn start(&self) -> Box<dyn hash::Context> { fn start(&self) -> Box<dyn hash::Context> {
let digest = boring::hash::MessageDigest::from_nid(self.0).unwrap(); let digest = MessageDigest::from_nid(self.0).expect("failed getting hash digest");
let hasher = boring::hash::Hasher::new(digest).unwrap(); let hasher = Hasher::new(digest).expect("failed getting hasher");
Box::new(HasherContext(hasher)) Box::new(HasherContext(hasher))
} }
@ -21,24 +22,26 @@ impl hash::Hash for Hash {
fn algorithm(&self) -> hash::HashAlgorithm { fn algorithm(&self) -> hash::HashAlgorithm {
match self.0 { match self.0 {
boring::nid::Nid::SHA256 => hash::HashAlgorithm::SHA256, boring::nid::Nid::SHA256 => hash::HashAlgorithm::SHA256,
boring::nid::Nid::SHA384 => hash::HashAlgorithm::SHA384,
boring::nid::Nid::SHA512 => hash::HashAlgorithm::SHA512,
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
fn output_len(&self) -> usize { fn output_len(&self) -> usize {
boring::hash::MessageDigest::from_nid(self.0) MessageDigest::from_nid(self.0)
.unwrap() .expect("failed getting digest")
.size() .size()
} }
} }
struct HasherContext(boring::hash::Hasher); struct HasherContext(Hasher);
impl hash::Context for HasherContext { impl hash::Context for HasherContext {
fn fork_finish(&self) -> hash::Output { fn fork_finish(&self) -> hash::Output {
let mut cloned = self.0.clone(); let mut cloned = self.0.clone();
hash::Output::new(&cloned.finish().unwrap()[..]) hash::Output::new(&cloned.finish().expect("failed finishing hash")[..])
} }
fn fork(&self) -> Box<dyn hash::Context> { fn fork(&self) -> Box<dyn hash::Context> {
@ -46,11 +49,11 @@ impl hash::Context for HasherContext {
} }
fn finish(mut self: Box<Self>) -> hash::Output { fn finish(mut self: Box<Self>) -> hash::Output {
hash::Output::new(&self.0.finish().unwrap()[..]) hash::Output::new(&self.0.finish().expect("failed finishing hash")[..])
} }
fn update(&mut self, data: &[u8]) { fn update(&mut self, data: &[u8]) {
self.0.update(data).unwrap(); self.0.update(data).expect("failed adding data to hash");
} }
} }

View file

@ -4,11 +4,11 @@ use boring::error::ErrorStack;
#[cfg(feature = "log")] #[cfg(feature = "log")]
use log::trace; use log::trace;
/// Check the value returned from a BoringSSL ffi call /// Check the value returned from a `BoringSSL` ffi call
/// that returns a pointer. /// that returns a pointer.
/// ///
/// If the pointer is null, this method returns the BoringSSL /// If the pointer is null, this method returns the
/// ErrorStack as Err, the pointer otherwise. /// [`boring::error::ErrorStack`] as Err, the pointer otherwise.
pub(crate) fn cvt_p<T>(r: *mut T) -> Result<*mut T, ErrorStack> { pub(crate) fn cvt_p<T>(r: *mut T) -> Result<*mut T, ErrorStack> {
if r.is_null() { if r.is_null() {
Err(ErrorStack::get()) Err(ErrorStack::get())
@ -17,10 +17,10 @@ pub(crate) fn cvt_p<T>(r: *mut T) -> Result<*mut T, ErrorStack> {
} }
} }
/// Check the value returned from a BoringSSL ffi call that /// Check the value returned from a `BoringSSL` ffi call that
/// returns a integer. /// returns a integer.
/// ///
/// Returns the BoringSSL Errorstack when the result is <= 0. /// Returns the [`boring::error::ErrorStack`] when the result is <= 0.
/// And forwards the return code otherwise /// And forwards the return code otherwise
pub(crate) fn cvt(r: c_int) -> Result<i32, ErrorStack> { pub(crate) fn cvt(r: c_int) -> Result<i32, ErrorStack> {
if r <= 0 { if r <= 0 {
@ -30,17 +30,13 @@ pub(crate) fn cvt(r: c_int) -> Result<i32, ErrorStack> {
} }
} }
pub(crate) fn error_stack_to_aead_error(func: &'static str, e: ErrorStack) -> aead::Error {
map_error_stack(func, e, aead::Error)
}
#[cfg(feature = "log")] #[cfg(feature = "log")]
pub(crate) fn map_error_stack<T>(func: &'static str, e: ErrorStack, mapped: T) -> T { pub(crate) fn log_and_map<E: core::fmt::Display, T>(func: &'static str, e: E, mapped: T) -> T {
trace!("failed {}, error: {}", func, e); trace!("failed {}, error: {}", func, e);
mapped mapped
} }
#[cfg(not(feature = "log"))] #[cfg(not(feature = "log"))]
pub(crate) fn map_error_stack<T>(func: &'static str, e: ErrorStack, mapped: T) -> T { pub(crate) fn log_and_map<E: core::fmt::Display, T>(func: &'static str, e: E, mapped: T) -> T {
mapped mapped
} }

View file

@ -5,10 +5,15 @@ use rustls::crypto::tls13::{self, Hkdf as RustlsHkdf};
use crate::helper::{cvt, cvt_p}; use crate::helper::{cvt, cvt_p};
/// A trait that is required for a Hkdf function
pub trait BoringHash: Send + Sync { pub trait BoringHash: Send + Sync {
/// Instantiate a new digest using
/// the hash function that this trait
/// is implemented for.
fn new_hash() -> MessageDigest; fn new_hash() -> MessageDigest;
} }
/// SHA256-based for Hkdf
pub struct Sha256(); pub struct Sha256();
impl BoringHash for Sha256 { impl BoringHash for Sha256 {
fn new_hash() -> MessageDigest { fn new_hash() -> MessageDigest {
@ -16,6 +21,7 @@ impl BoringHash for Sha256 {
} }
} }
/// SHA384-based for Hkdf
pub struct Sha384(); pub struct Sha384();
impl BoringHash for Sha384 { impl BoringHash for Sha384 {
fn new_hash() -> MessageDigest { fn new_hash() -> MessageDigest {
@ -23,9 +29,12 @@ impl BoringHash for Sha384 {
} }
} }
/// A Hmac-based key derivation function
/// using T as the hash function
pub struct Hkdf<T: BoringHash>(PhantomData<T>); pub struct Hkdf<T: BoringHash>(PhantomData<T>);
impl<T: BoringHash> Hkdf<T> { impl<T: BoringHash> Hkdf<T> {
/// A default Hkdf implementation
pub const DEFAULT: Self = Self(PhantomData); pub const DEFAULT: Self = Self(PhantomData);
} }
@ -130,7 +139,7 @@ impl<T: BoringHash> RustlsHkdf for Hkdf<T> {
} }
} }
pub struct HkdfExpander { struct HkdfExpander {
prk: [u8; boring_sys::EVP_MAX_MD_SIZE as usize], prk: [u8; boring_sys::EVP_MAX_MD_SIZE as usize],
prk_len: usize, prk_len: usize,
digest: MessageDigest, digest: MessageDigest,
@ -181,7 +190,8 @@ impl tls13::HkdfExpander for HkdfExpander {
let mut output = [0u8; boring_sys::EVP_MAX_MD_SIZE as usize]; let mut output = [0u8; boring_sys::EVP_MAX_MD_SIZE as usize];
let output_len = self.hash_len(); let output_len = self.hash_len();
self.expand_slice(info, &mut output[..output_len]).unwrap(); self.expand_slice(info, &mut output[..output_len])
.expect("failed hkdf expand");
tls13::OkmBlock::new(&output[..output_len]) tls13::OkmBlock::new(&output[..output_len])
} }

View file

@ -7,30 +7,34 @@ use rustls::crypto;
use crate::helper::{cvt, cvt_p}; use crate::helper::{cvt, cvt_p};
/// A SHA256-based Hmac
#[allow(unused)] #[allow(unused)]
pub const SHA256: &dyn crypto::hmac::Hmac = &BoringHmac(boring::nid::Nid::SHA256); pub const SHA256: &dyn crypto::hmac::Hmac = &BoringHmac(boring::nid::Nid::SHA256);
/// A SHA384-based Hmac
pub const SHA384: &dyn crypto::hmac::Hmac = &BoringHmac(boring::nid::Nid::SHA384); pub const SHA384: &dyn crypto::hmac::Hmac = &BoringHmac(boring::nid::Nid::SHA384);
pub struct BoringHmac(pub boring::nid::Nid); struct BoringHmac(pub boring::nid::Nid);
impl crypto::hmac::Hmac for BoringHmac { impl crypto::hmac::Hmac for BoringHmac {
fn with_key(&self, key: &[u8]) -> Box<dyn crypto::hmac::Key> { fn with_key(&self, key: &[u8]) -> Box<dyn crypto::hmac::Key> {
Box::new(unsafe { let ctx = unsafe {
let ctx = HmacCtx::from_ptr(cvt_p(boring_sys::HMAC_CTX_new()).unwrap()); HmacCtx::from_ptr(
cvt_p(boring_sys::HMAC_CTX_new()).expect("failed getting hmac context"),
)
};
let md = boring::hash::MessageDigest::from_nid(self.0).unwrap(); let md = MessageDigest::from_nid(self.0).expect("failed getting digest");
BoringHmacKey { Box::new(BoringHmacKey {
ctx, ctx,
md, md,
key: key.to_vec(), key: key.to_vec(),
}
}) })
} }
fn hash_output_len(&self) -> usize { fn hash_output_len(&self) -> usize {
boring::hash::MessageDigest::from_nid(self.0) MessageDigest::from_nid(self.0)
.unwrap() .expect("failed getting digest")
.size() .size()
} }
} }
@ -42,11 +46,9 @@ struct BoringHmacKey {
key: Vec<u8>, key: Vec<u8>,
} }
impl crypto::hmac::Key for BoringHmacKey { impl BoringHmacKey {
fn sign_concat(&self, first: &[u8], middle: &[&[u8]], last: &[u8]) -> crypto::hmac::Tag { fn init(&self) {
let mut out = [0u8; 32]; unsafe {
crypto::hmac::Tag::new(unsafe {
// initialize a new hmac // initialize a new hmac
cvt(boring_sys::HMAC_Init_ex( cvt(boring_sys::HMAC_Init_ex(
self.ctx.as_ptr(), self.ctx.as_ptr(),
@ -55,41 +57,50 @@ impl crypto::hmac::Key for BoringHmacKey {
self.md.as_ptr(), self.md.as_ptr(),
ptr::null_mut(), ptr::null_mut(),
)) ))
.unwrap(); }
.expect("failed initializing hmac");
}
fn update(&self, bytes: &[u8]) {
unsafe {
cvt(boring_sys::HMAC_Update( cvt(boring_sys::HMAC_Update(
self.ctx.as_ptr(), self.ctx.as_ptr(),
first.as_ptr(), bytes.as_ptr(),
first.len(), bytes.len(),
)) ))
.unwrap(); }
.expect("failed updating hmac");
}
for m in middle { fn finish(&self, out: &mut [u8]) -> usize {
cvt(boring_sys::HMAC_Update( let mut out_len = 0;
self.ctx.as_ptr(), unsafe {
m.as_ptr(),
m.len(),
))
.unwrap();
}
cvt(boring_sys::HMAC_Update(
self.ctx.as_ptr(),
last.as_ptr(),
last.len(),
))
.unwrap();
let mut out_len = 0;
cvt(boring_sys::HMAC_Final( cvt(boring_sys::HMAC_Final(
self.ctx.as_ptr(), self.ctx.as_ptr(),
out.as_mut_ptr(), out.as_mut_ptr(),
&mut out_len, &mut out_len,
)) ))
.unwrap(); }
.expect("failed hmac final");
out_len as usize
}
}
&out[..out_len as usize] impl crypto::hmac::Key for BoringHmacKey {
}) fn sign_concat(&self, first: &[u8], middle: &[&[u8]], last: &[u8]) -> crypto::hmac::Tag {
self.init();
self.update(first);
for m in middle {
self.update(m);
}
self.update(last);
let mut out = [0u8; 32];
let out_len = self.finish(&mut out);
crypto::hmac::Tag::new(&out[..out_len])
} }
fn tag_len(&self) -> usize { fn tag_len(&self) -> usize {

View file

@ -1,6 +1,6 @@
use rustls::crypto::{self, ActiveKeyExchange}; use rustls::crypto::{self, ActiveKeyExchange};
use crate::helper::map_error_stack; use crate::helper::log_and_map;
mod dh; mod dh;
mod ex; mod ex;
@ -11,13 +11,14 @@ enum DhKeyType {
FFDHE2048, FFDHE2048,
} }
/// A X25519-based key exchange
#[derive(Debug)] #[derive(Debug)]
pub struct X25519; pub struct X25519;
impl crypto::SupportedKxGroup for X25519 { impl crypto::SupportedKxGroup for X25519 {
fn start(&self) -> Result<Box<(dyn ActiveKeyExchange + 'static)>, rustls::Error> { fn start(&self) -> Result<Box<(dyn ActiveKeyExchange + 'static)>, rustls::Error> {
Ok(Box::new(ex::ExKeyExchange::with_x25519().map_err(|e| { Ok(Box::new(ex::KeyExchange::with_x25519().map_err(|e| {
map_error_stack("X25519.start", e, crypto::GetRandomFailed) log_and_map("X25519.start", e, crypto::GetRandomFailed)
})?)) })?))
} }
@ -26,13 +27,14 @@ impl crypto::SupportedKxGroup for X25519 {
} }
} }
/// A X448-based key exchange
#[derive(Debug)] #[derive(Debug)]
pub struct X448; pub struct X448;
impl crypto::SupportedKxGroup for X448 { impl crypto::SupportedKxGroup for X448 {
fn start(&self) -> Result<Box<(dyn ActiveKeyExchange + 'static)>, rustls::Error> { fn start(&self) -> Result<Box<(dyn ActiveKeyExchange + 'static)>, rustls::Error> {
Ok(Box::new(ex::ExKeyExchange::with_x448().map_err(|e| { Ok(Box::new(ex::KeyExchange::with_x448().map_err(|e| {
map_error_stack("X448.start", e, crypto::GetRandomFailed) log_and_map("X448.start", e, crypto::GetRandomFailed)
})?)) })?))
} }
@ -41,13 +43,14 @@ impl crypto::SupportedKxGroup for X448 {
} }
} }
/// A secp256r1-based key exchange
#[derive(Debug)] #[derive(Debug)]
pub struct Secp256r1; pub struct Secp256r1;
impl crypto::SupportedKxGroup for Secp256r1 { impl crypto::SupportedKxGroup for Secp256r1 {
fn start(&self) -> Result<Box<(dyn ActiveKeyExchange + 'static)>, rustls::Error> { fn start(&self) -> Result<Box<(dyn ActiveKeyExchange + 'static)>, rustls::Error> {
Ok(Box::new(ex::ExKeyExchange::with_secp256r1().map_err( Ok(Box::new(ex::KeyExchange::with_secp256r1().map_err(
|e| map_error_stack("Secp256r1.start", e, crypto::GetRandomFailed), |e| log_and_map("Secp256r1.start", e, crypto::GetRandomFailed),
)?)) )?))
} }
@ -56,13 +59,14 @@ impl crypto::SupportedKxGroup for Secp256r1 {
} }
} }
/// A secp384r1-based key exchange
#[derive(Debug)] #[derive(Debug)]
pub struct Secp384r1; pub struct Secp384r1;
impl crypto::SupportedKxGroup for Secp384r1 { impl crypto::SupportedKxGroup for Secp384r1 {
fn start(&self) -> Result<Box<(dyn ActiveKeyExchange + 'static)>, rustls::Error> { fn start(&self) -> Result<Box<(dyn ActiveKeyExchange + 'static)>, rustls::Error> {
Ok(Box::new(ex::ExKeyExchange::with_secp384r1().map_err( Ok(Box::new(ex::KeyExchange::with_secp384r1().map_err(
|e| map_error_stack("Secp384r1.start", e, crypto::GetRandomFailed), |e| log_and_map("Secp384r1.start", e, crypto::GetRandomFailed),
)?)) )?))
} }
@ -71,13 +75,14 @@ impl crypto::SupportedKxGroup for Secp384r1 {
} }
} }
/// A secp521r1-based key exchange
#[derive(Debug)] #[derive(Debug)]
pub struct Secp521r1; pub struct Secp521r1;
impl crypto::SupportedKxGroup for Secp521r1 { impl crypto::SupportedKxGroup for Secp521r1 {
fn start(&self) -> Result<Box<(dyn ActiveKeyExchange + 'static)>, rustls::Error> { fn start(&self) -> Result<Box<(dyn ActiveKeyExchange + 'static)>, rustls::Error> {
Ok(Box::new(ex::ExKeyExchange::with_secp521r1().map_err( Ok(Box::new(ex::KeyExchange::with_secp521r1().map_err(
|e| map_error_stack("Secp521r1.start", e, crypto::GetRandomFailed), |e| log_and_map("Secp521r1.start", e, crypto::GetRandomFailed),
)?)) )?))
} }
@ -86,13 +91,14 @@ impl crypto::SupportedKxGroup for Secp521r1 {
} }
} }
/// A ffedhe2048-based key exchange
#[derive(Debug)] #[derive(Debug)]
pub struct FfDHe2048; pub struct FfDHe2048;
impl crypto::SupportedKxGroup for FfDHe2048 { impl crypto::SupportedKxGroup for FfDHe2048 {
fn start(&self) -> Result<Box<(dyn ActiveKeyExchange + 'static)>, rustls::Error> { fn start(&self) -> Result<Box<(dyn ActiveKeyExchange + 'static)>, rustls::Error> {
Ok(Box::new(dh::DhKeyExchange::generate_ffdhe_2048().map_err( Ok(Box::new(dh::KeyExchange::generate_ffdhe_2048().map_err(
|e| map_error_stack("FfDHe2048.start", e, crypto::GetRandomFailed), |e| log_and_map("FfDHe2048.start", e, crypto::GetRandomFailed),
)?)) )?))
} }

View file

@ -2,17 +2,19 @@ use boring::{dh::Dh, error::ErrorStack, pkey::Private};
use foreign_types::ForeignType; use foreign_types::ForeignType;
use rustls::crypto; use rustls::crypto;
use crate::helper::{cvt, cvt_p, map_error_stack}; use crate::helper::{cvt, cvt_p, log_and_map};
use super::DhKeyType; use super::DhKeyType;
pub struct DhKeyExchange { /// This type can be used to perform a
/// Diffie-Hellman key exchange.
pub struct KeyExchange {
dh: Dh<Private>, dh: Dh<Private>,
pub_bytes: Vec<u8>, pub_bytes: Vec<u8>,
key_type: DhKeyType, key_type: DhKeyType,
} }
impl DhKeyExchange { impl KeyExchange {
// Generate a new KeyExchange with a random FFDHE_2048 private key // Generate a new KeyExchange with a random FFDHE_2048 private key
pub fn generate_ffdhe_2048() -> Result<Self, ErrorStack> { pub fn generate_ffdhe_2048() -> Result<Self, ErrorStack> {
let mut me = Self { let mut me = Self {
@ -49,9 +51,11 @@ impl DhKeyExchange {
/// Generate a shared secret with the other's raw public key /// Generate a shared secret with the other's raw public key
fn diffie_hellman(&self, raw_public_key: &[u8]) -> Result<Vec<u8>, ErrorStack> { fn diffie_hellman(&self, raw_public_key: &[u8]) -> Result<Vec<u8>, ErrorStack> {
let peer = boring::bn::BigNum::from_slice(raw_public_key).unwrap(); let peer = boring::bn::BigNum::from_slice(raw_public_key)?;
let secret_len = unsafe { cvt(boring_sys::DH_size(self.dh.as_ptr()))? } as usize; let secret_len = unsafe { cvt(boring_sys::DH_size(self.dh.as_ptr()))? } as usize;
let mut secret = vec![0u8; secret_len]; let mut secret = vec![0u8; secret_len];
let secret_len = unsafe { let secret_len = unsafe {
cvt(boring_sys::DH_compute_key_padded( cvt(boring_sys::DH_compute_key_padded(
secret.as_mut_ptr(), secret.as_mut_ptr(),
@ -59,12 +63,13 @@ impl DhKeyExchange {
self.dh.as_ptr(), self.dh.as_ptr(),
))? ))?
} as usize; } as usize;
secret.truncate(secret_len); secret.truncate(secret_len);
Ok(secret) Ok(secret)
} }
} }
impl crypto::ActiveKeyExchange for DhKeyExchange { impl crypto::ActiveKeyExchange for KeyExchange {
fn complete( fn complete(
self: Box<Self>, self: Box<Self>,
peer_pub_key: &[u8], peer_pub_key: &[u8],
@ -78,8 +83,8 @@ impl crypto::ActiveKeyExchange for DhKeyExchange {
Ok(crypto::SharedSecret::from( Ok(crypto::SharedSecret::from(
self.diffie_hellman(peer_pub_key) self.diffie_hellman(peer_pub_key)
.map_err(|e| { .map_err(|e| {
map_error_stack( log_and_map(
"dh.diffie_hellman", "dh::KeyExchange::diffie_hellman",
e, e,
rustls::PeerMisbehaved::InvalidKeyShare, rustls::PeerMisbehaved::InvalidKeyShare,
) )
@ -102,13 +107,13 @@ impl crypto::ActiveKeyExchange for DhKeyExchange {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::kx::dh::DhKeyExchange; use crate::kx::dh::KeyExchange;
use rustls::crypto::ActiveKeyExchange; use rustls::crypto::ActiveKeyExchange;
#[test] #[test]
fn test_derive_dh() { fn test_derive_dh() {
let alice = DhKeyExchange::generate_ffdhe_2048().unwrap(); let alice = KeyExchange::generate_ffdhe_2048().unwrap();
let bob = DhKeyExchange::generate_ffdhe_2048().unwrap(); let bob = KeyExchange::generate_ffdhe_2048().unwrap();
let shared_secret1 = alice.diffie_hellman(bob.pub_key()).unwrap(); let shared_secret1 = alice.diffie_hellman(bob.pub_key()).unwrap();
let shared_secret2 = bob.diffie_hellman(alice.pub_key()).unwrap(); let shared_secret2 = bob.diffie_hellman(alice.pub_key()).unwrap();

View file

@ -14,56 +14,59 @@ use foreign_types::ForeignType;
use rustls::crypto; use rustls::crypto;
use spki::der::Decode; use spki::der::Decode;
use crate::helper::{cvt, cvt_p, map_error_stack}; use crate::helper::{cvt, cvt_p, log_and_map};
use super::DhKeyType; use super::DhKeyType;
pub struct ExKeyExchange { /// This type can be used to perform an
/// Eliptic Curve or Edwards Curve key
/// exchange.
pub struct KeyExchange {
own_key: PKey<Private>, own_key: PKey<Private>,
pub_bytes: Vec<u8>, pub_bytes: Vec<u8>,
key_type: DhKeyType, key_type: DhKeyType,
} }
impl ExKeyExchange { impl KeyExchange {
/// Creates a new KeyExchange using a random /// Creates a new `KeyExchange` using a random
/// private key for the X25519 Edwards curve /// private key for the `X25519` Edwards curve
pub fn with_x25519() -> Result<Self, ErrorStack> { pub fn with_x25519() -> Result<Self, ErrorStack> {
Self::ed_from_curve(Nid::from_raw(boring_sys::NID_X25519)) Self::ed_from_curve(Nid::from_raw(boring_sys::NID_X25519))
} }
/// Creates a new KeyExchange using a random /// Creates a new `KeyExchange` using a random
/// private key for the X448 Edwards curve /// private key for the `X448` Edwards curve
pub fn with_x448() -> Result<Self, ErrorStack> { pub fn with_x448() -> Result<Self, ErrorStack> {
Self::ed_from_curve(Nid::from_raw(boring_sys::NID_X448)) Self::ed_from_curve(Nid::from_raw(boring_sys::NID_X448))
} }
/// Creates a new KeyExchange using a random /// Creates a new `KeyExchange` using a random
/// private key for sepc256r1 curve /// private key for `sepc256r1` curve
/// Also known as X9_62_PRIME256V1 /// Also known as `X9_62_PRIME256V1`
pub fn with_secp256r1() -> Result<Self, ErrorStack> { pub fn with_secp256r1() -> Result<Self, ErrorStack> {
Self::ec_from_curve(Nid::X9_62_PRIME256V1) Self::ec_from_curve(Nid::X9_62_PRIME256V1)
} }
/// Creates a new KeyExchange using a random /// Creates a new `KeyExchange` using a random
/// private key for sepc384r1 curve /// private key for `sepc384r1` curve
pub fn with_secp384r1() -> Result<Self, ErrorStack> { pub fn with_secp384r1() -> Result<Self, ErrorStack> {
Self::ec_from_curve(Nid::SECP384R1) Self::ec_from_curve(Nid::SECP384R1)
} }
/// Creates a new KeyExchange using a random /// Creates a new `KeyExchange` using a random
/// private key for sep521r1 curve /// private key for `sep521r1` curve
pub fn with_secp521r1() -> Result<Self, ErrorStack> { pub fn with_secp521r1() -> Result<Self, ErrorStack> {
Self::ec_from_curve(Nid::SECP521R1) Self::ec_from_curve(Nid::SECP521R1)
} }
/// Allows getting a new KeyExchange using Eliptic Curves /// Allows getting a new `KeyExchange` using Eliptic Curves
/// on the specified curve /// on the specified curve
fn ec_from_curve(nid: Nid) -> Result<Self, ErrorStack> { fn ec_from_curve(nid: Nid) -> Result<Self, ErrorStack> {
let ec_group = EcGroup::from_curve_name(nid)?; let ec_group = EcGroup::from_curve_name(nid)?;
let ec_key = EcKey::generate(&ec_group)?; let ec_key = EcKey::generate(&ec_group)?;
let own_key = PKey::from_ec_key(ec_key)?; let own_key = PKey::from_ec_key(ec_key)?;
let pub_bytes = Self::raw_public_key(&own_key); let pub_bytes = Self::raw_public_key(&own_key)?;
Ok(Self { Ok(Self {
own_key, own_key,
pub_bytes, pub_bytes,
@ -71,7 +74,7 @@ impl ExKeyExchange {
}) })
} }
/// Allows getting a new KeyExchange using Edwards Curves /// Allows getting a new `KeyExchange` using Edwards Curves
/// on the specified curve /// on the specified curve
fn ed_from_curve(nid: Nid) -> Result<Self, ErrorStack> { fn ed_from_curve(nid: Nid) -> Result<Self, ErrorStack> {
let pkey_ctx = unsafe { let pkey_ctx = unsafe {
@ -91,7 +94,7 @@ impl ExKeyExchange {
PKey::from_ptr(pkey) PKey::from_ptr(pkey)
}; };
let pub_bytes = Self::raw_public_key(&own_key); let pub_bytes = Self::raw_public_key(&own_key)?;
Ok(Self { Ok(Self {
own_key, own_key,
@ -101,14 +104,19 @@ impl ExKeyExchange {
} }
/// Decodes a SPKI public key to it's raw public key component /// Decodes a SPKI public key to it's raw public key component
fn raw_public_key(pkey: &PKeyRef<Private>) -> Vec<u8> { fn raw_public_key(pkey: &PKeyRef<Private>) -> Result<Vec<u8>, ErrorStack> {
let spki = pkey.public_key_to_der().unwrap(); let spki = pkey.public_key_to_der()?;
// parse the key // parse the key
let key = spki::SubjectPublicKeyInfoRef::from_der(spki.as_ref()).unwrap(); let pkey = spki::SubjectPublicKeyInfoRef::from_der(spki.as_ref())
.expect("failed parsing spki bytes");
// return the raw public key as a new vec // return the raw public key as a new vec
Vec::from(key.subject_public_key.as_bytes().unwrap()) Ok(Vec::from(
pkey.subject_public_key
.as_bytes()
.expect("failed getting raw spki bytes"),
))
} }
/// Derives a shared secret using the peer's raw public key /// Derives a shared secret using the peer's raw public key
@ -117,13 +125,11 @@ impl ExKeyExchange {
DhKeyType::EC((group, _)) => { DhKeyType::EC((group, _)) => {
let mut bn_ctx = boring::bn::BigNumContext::new()?; let mut bn_ctx = boring::bn::BigNumContext::new()?;
let point = crate::verify::ec::ec_point(group, &mut bn_ctx, peer_pub_key)?; let point = crate::verify::ec::get_ec_point(group, &mut bn_ctx, peer_pub_key)?;
crate::verify::ec::ec_public_key(group, point.as_ref())? crate::verify::ec::create_public_key(group, point.as_ref())?
}
DhKeyType::ED(nid) => {
crate::verify::ed::ed_public_key(peer_pub_key, Nid::from_raw(*nid))?
} }
DhKeyType::ED(nid) => crate::verify::ed::public_key(peer_pub_key, Nid::from_raw(*nid))?,
_ => unimplemented!(), _ => unimplemented!(),
}; };
@ -135,7 +141,7 @@ impl ExKeyExchange {
} }
} }
impl crypto::ActiveKeyExchange for ExKeyExchange { impl crypto::ActiveKeyExchange for KeyExchange {
fn complete( fn complete(
self: Box<Self>, self: Box<Self>,
peer_pub_key: &[u8], peer_pub_key: &[u8],
@ -143,8 +149,8 @@ impl crypto::ActiveKeyExchange for ExKeyExchange {
self.diffie_hellman(peer_pub_key) self.diffie_hellman(peer_pub_key)
.map(|x| crypto::SharedSecret::from(x.as_slice())) .map(|x| crypto::SharedSecret::from(x.as_slice()))
.map_err(|e| { .map_err(|e| {
map_error_stack( log_and_map(
"ex.diffie_hellman", "ex::KeyExchange::diffie_hellman",
e, e,
rustls::Error::PeerMisbehaved(rustls::PeerMisbehaved::InvalidKeyShare), rustls::Error::PeerMisbehaved(rustls::PeerMisbehaved::InvalidKeyShare),
) )
@ -169,13 +175,13 @@ impl crypto::ActiveKeyExchange for ExKeyExchange {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::ExKeyExchange; use super::KeyExchange;
use rustls::crypto::ActiveKeyExchange; use rustls::crypto::ActiveKeyExchange;
#[test] #[test]
fn test_derive_ec() { fn test_derive_ec() {
let alice = Box::new(ExKeyExchange::with_secp256r1().unwrap()); let alice = Box::new(KeyExchange::with_secp256r1().unwrap());
let bob = ExKeyExchange::with_secp256r1().unwrap(); let bob = KeyExchange::with_secp256r1().unwrap();
assert_eq!( assert_eq!(
alice.diffie_hellman(bob.pub_key()).unwrap(), alice.diffie_hellman(bob.pub_key()).unwrap(),
@ -185,8 +191,8 @@ mod tests {
#[test] #[test]
fn test_derive_ed() { fn test_derive_ed() {
let alice = Box::new(ExKeyExchange::with_x25519().unwrap()); let alice = Box::new(KeyExchange::with_x25519().unwrap());
let bob = ExKeyExchange::with_x25519().unwrap(); let bob = KeyExchange::with_x25519().unwrap();
assert_eq!( assert_eq!(
alice.diffie_hellman(bob.pub_key()).unwrap(), alice.diffie_hellman(bob.pub_key()).unwrap(),

View file

@ -1,5 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use helper::log_and_map;
use rustls::{ use rustls::{
crypto::{CryptoProvider, GetRandomFailed, SupportedKxGroup}, crypto::{CryptoProvider, GetRandomFailed, SupportedKxGroup},
SupportedCipherSuite, SupportedCipherSuite,
@ -18,6 +19,7 @@ mod tls12;
mod tls13; mod tls13;
mod verify; mod verify;
/// The boringssl-based Rustls Crypto provider
pub static PROVIDER: &'static dyn CryptoProvider = &Provider; pub static PROVIDER: &'static dyn CryptoProvider = &Provider;
#[derive(Debug)] #[derive(Debug)]
@ -25,7 +27,7 @@ struct Provider;
impl CryptoProvider for Provider { impl CryptoProvider for Provider {
fn fill_random(&self, bytes: &mut [u8]) -> Result<(), GetRandomFailed> { fn fill_random(&self, bytes: &mut [u8]) -> Result<(), GetRandomFailed> {
boring::rand::rand_bytes(bytes).map_err(|_| GetRandomFailed) boring::rand::rand_bytes(bytes).map_err(|e| log_and_map("rand_bytes", e, GetRandomFailed))
} }
fn default_cipher_suites(&self) -> &'static [SupportedCipherSuite] { fn default_cipher_suites(&self) -> &'static [SupportedCipherSuite] {
@ -54,9 +56,7 @@ impl CryptoProvider for Provider {
&self, &self,
key_der: PrivateKeyDer<'static>, key_der: PrivateKeyDer<'static>,
) -> Result<std::sync::Arc<dyn rustls::sign::SigningKey>, rustls::Error> { ) -> Result<std::sync::Arc<dyn rustls::sign::SigningKey>, rustls::Error> {
sign::BoringPrivateKey::try_from(key_der) sign::BoringPrivateKey::try_from(key_der).map(|x| Arc::new(x) as _)
.map(|x| Arc::new(x) as _)
.map_err(|_| rustls::Error::General("invalid private key".into()))
} }
fn signature_verification_algorithms(&self) -> rustls::WebPkiSupportedAlgorithms { fn signature_verification_algorithms(&self) -> rustls::WebPkiSupportedAlgorithms {

View file

@ -9,6 +9,8 @@ use boring::{
use rustls::{sign::SigningKey, SignatureScheme}; use rustls::{sign::SigningKey, SignatureScheme};
use rustls_pki_types::PrivateKeyDer; use rustls_pki_types::PrivateKeyDer;
use crate::helper::log_and_map;
const ALL_RSA_SCHEMES: &[SignatureScheme] = &[ const ALL_RSA_SCHEMES: &[SignatureScheme] = &[
SignatureScheme::RSA_PSS_SHA512, SignatureScheme::RSA_PSS_SHA512,
SignatureScheme::RSA_PSS_SHA384, SignatureScheme::RSA_PSS_SHA384,
@ -24,6 +26,8 @@ const ALL_EC_SCHEMES: &[SignatureScheme] = &[
SignatureScheme::ECDSA_NISTP521_SHA512, SignatureScheme::ECDSA_NISTP521_SHA512,
]; ];
/// An abstraction over a boringssl private key
/// used for signing
#[derive(Debug)] #[derive(Debug)]
pub struct BoringPrivateKey(Arc<boring::pkey::PKey<Private>>, rustls::SignatureAlgorithm); pub struct BoringPrivateKey(Arc<boring::pkey::PKey<Private>>, rustls::SignatureAlgorithm);
@ -33,10 +37,12 @@ impl TryFrom<PrivateKeyDer<'static>> for BoringPrivateKey {
fn try_from(value: PrivateKeyDer<'static>) -> Result<Self, Self::Error> { fn try_from(value: PrivateKeyDer<'static>) -> Result<Self, Self::Error> {
let pkey = match value { let pkey = match value {
PrivateKeyDer::Pkcs8(der) => { PrivateKeyDer::Pkcs8(der) => {
boring::pkey::PKey::private_key_from_pkcs8(der.secret_pkcs8_der()).map_err(|_| ()) boring::pkey::PKey::private_key_from_pkcs8(der.secret_pkcs8_der())
.map_err(|e| log_and_map("private_key_from_pkcs8", e, ()))
} }
PrivateKeyDer::Pkcs1(der) => { PrivateKeyDer::Pkcs1(der) => {
boring::pkey::PKey::private_key_from_der(der.secret_pkcs1_der()).map_err(|_| ()) boring::pkey::PKey::private_key_from_der(der.secret_pkcs1_der())
.map_err(|e| log_and_map("private_key_from_der", e, ()))
} }
_ => Err(()), _ => Err(()),
} }
@ -120,6 +126,7 @@ impl SigningKey for BoringPrivateKey {
} }
} }
/// A boringssl-based Signer
#[derive(Debug)] #[derive(Debug)]
pub struct BoringSigner(Arc<boring::pkey::PKey<Private>>, rustls::SignatureScheme); pub struct BoringSigner(Arc<boring::pkey::PKey<Private>>, rustls::SignatureScheme);
@ -156,10 +163,7 @@ impl BoringSigner {
ec_signer_from_params(self.0.as_ref(), MessageDigest::sha512()) ec_signer_from_params(self.0.as_ref(), MessageDigest::sha512())
} }
SignatureScheme::ED25519 => { SignatureScheme::ED25519 | SignatureScheme::ED448 => {
Signer::new_without_digest(self.0.as_ref()).expect("failed getting signer")
}
SignatureScheme::ED448 => {
Signer::new_without_digest(self.0.as_ref()).expect("failed getting signer") Signer::new_without_digest(self.0.as_ref()).expect("failed getting signer")
} }
@ -178,7 +182,7 @@ impl rustls::sign::Signer for BoringSigner {
let toatl_len = signer let toatl_len = signer
.sign(&mut msg_with_sig[..]) .sign(&mut msg_with_sig[..])
.map_err(|_| rustls::Error::General("failed signing".into()))?; .map_err(|e| log_and_map("sign", e, rustls::Error::General("failed signing".into())))?;
msg_with_sig.truncate(toatl_len); msg_with_sig.truncate(toatl_len);
Ok(msg_with_sig) Ok(msg_with_sig)
} }

View file

@ -2,6 +2,8 @@ use boring::{error::ErrorStack, hash::MessageDigest};
use rustls::SignatureScheme; use rustls::SignatureScheme;
use rustls_pki_types::{InvalidSignature, SignatureVerificationAlgorithm}; use rustls_pki_types::{InvalidSignature, SignatureVerificationAlgorithm};
use crate::helper;
pub struct BoringEcVerifier(SignatureScheme); pub struct BoringEcVerifier(SignatureScheme);
impl BoringEcVerifier { impl BoringEcVerifier {
@ -17,11 +19,15 @@ impl SignatureVerificationAlgorithm for BoringEcVerifier {
message: &[u8], message: &[u8],
signature: &[u8], signature: &[u8],
) -> Result<(), rustls_pki_types::InvalidSignature> { ) -> Result<(), rustls_pki_types::InvalidSignature> {
let (group, mut bn_ctx) = setup_ec_key(self.0); let (group, mut bn_ctx) = setup_ec_key(self.0)
let ec_point = .map_err(|e| helper::log_and_map("setup_ec_key", e, InvalidSignature))?;
ec_point(group.as_ref(), bn_ctx.as_mut(), public_key).map_err(|_| InvalidSignature)?;
let public_key = let ec_point = get_ec_point(group.as_ref(), bn_ctx.as_mut(), public_key)
ec_public_key(group.as_ref(), ec_point.as_ref()).map_err(|_| InvalidSignature)?; .map_err(|e| helper::log_and_map("ec_point", e, InvalidSignature))?;
let public_key = create_public_key(group.as_ref(), ec_point.as_ref())
.map_err(|e| helper::log_and_map("ec_public_key", e, InvalidSignature))?;
let mut verifier = match self.0 { let mut verifier = match self.0 {
SignatureScheme::ECDSA_NISTP256_SHA256 => { SignatureScheme::ECDSA_NISTP256_SHA256 => {
ec_verifier_from_params(public_key.as_ref(), MessageDigest::sha256()) ec_verifier_from_params(public_key.as_ref(), MessageDigest::sha256())
@ -34,9 +40,11 @@ impl SignatureVerificationAlgorithm for BoringEcVerifier {
} }
_ => unimplemented!(), _ => unimplemented!(),
}; }
.map_err(|e| helper::log_and_map("ec_verifier_from_params", e, InvalidSignature))?;
verifier.verify_oneshot(signature, message).map_or_else( verifier.verify_oneshot(signature, message).map_or_else(
|_| Err(InvalidSignature), |e| Err(helper::log_and_map("verify_oneshot", e, InvalidSignature)),
|res| if res { Ok(()) } else { Err(InvalidSignature) }, |res| if res { Ok(()) } else { Err(InvalidSignature) },
) )
} }
@ -74,30 +82,27 @@ impl SignatureVerificationAlgorithm for BoringEcVerifier {
fn ec_verifier_from_params( fn ec_verifier_from_params(
key: &boring::pkey::PKeyRef<boring::pkey::Public>, key: &boring::pkey::PKeyRef<boring::pkey::Public>,
digest: MessageDigest, digest: MessageDigest,
) -> boring::sign::Verifier { ) -> Result<boring::sign::Verifier, ErrorStack> {
let verifier = boring::sign::Verifier::new(digest, key).expect("failed getting verifier"); boring::sign::Verifier::new(digest, key)
verifier
} }
fn group_for_scheme(scheme: SignatureScheme) -> boring::ec::EcGroup { fn group_for_scheme(scheme: SignatureScheme) -> Result<boring::ec::EcGroup, ErrorStack> {
let nid = match scheme { let nid = match scheme {
SignatureScheme::ECDSA_NISTP256_SHA256 => boring::nid::Nid::X9_62_PRIME256V1, SignatureScheme::ECDSA_NISTP256_SHA256 => boring::nid::Nid::X9_62_PRIME256V1,
SignatureScheme::ECDSA_NISTP384_SHA384 => boring::nid::Nid::SECP384R1, SignatureScheme::ECDSA_NISTP384_SHA384 => boring::nid::Nid::SECP384R1,
SignatureScheme::ECDSA_NISTP521_SHA512 => boring::nid::Nid::SECP521R1, SignatureScheme::ECDSA_NISTP521_SHA512 => boring::nid::Nid::SECP521R1,
_ => unimplemented!(), _ => unimplemented!(),
}; };
boring::ec::EcGroup::from_curve_name(nid).expect("failed getting verify curve") boring::ec::EcGroup::from_curve_name(nid)
} }
fn setup_ec_key(scheme: SignatureScheme) -> (boring::ec::EcGroup, boring::bn::BigNumContext) { fn setup_ec_key(
( scheme: SignatureScheme,
group_for_scheme(scheme), ) -> Result<(boring::ec::EcGroup, boring::bn::BigNumContext), ErrorStack> {
boring::bn::BigNumContext::new().unwrap(), Ok((group_for_scheme(scheme)?, boring::bn::BigNumContext::new()?))
)
} }
pub(crate) fn ec_point( pub(crate) fn get_ec_point(
group: &boring::ec::EcGroupRef, group: &boring::ec::EcGroupRef,
bignum_ctx: &mut boring::bn::BigNumContextRef, bignum_ctx: &mut boring::bn::BigNumContextRef,
spki_spk: &[u8], spki_spk: &[u8],
@ -105,7 +110,7 @@ pub(crate) fn ec_point(
boring::ec::EcPoint::from_bytes(group, spki_spk, bignum_ctx) boring::ec::EcPoint::from_bytes(group, spki_spk, bignum_ctx)
} }
pub(crate) fn ec_public_key( pub(crate) fn create_public_key(
group: &boring::ec::EcGroupRef, group: &boring::ec::EcGroupRef,
ec_point: &boring::ec::EcPointRef, ec_point: &boring::ec::EcPointRef,
) -> Result<boring::pkey::PKey<boring::pkey::Public>, ErrorStack> { ) -> Result<boring::pkey::PKey<boring::pkey::Public>, ErrorStack> {

View file

@ -5,7 +5,7 @@ use foreign_types::ForeignType;
use rustls::SignatureScheme; use rustls::SignatureScheme;
use rustls_pki_types::{InvalidSignature, SignatureVerificationAlgorithm}; use rustls_pki_types::{InvalidSignature, SignatureVerificationAlgorithm};
use crate::helper::cvt_p; use crate::helper::{cvt_p, log_and_map};
pub struct BoringEdVerifier(SignatureScheme); pub struct BoringEdVerifier(SignatureScheme);
@ -21,9 +21,11 @@ impl SignatureVerificationAlgorithm for BoringEdVerifier {
message: &[u8], message: &[u8],
signature: &[u8], signature: &[u8],
) -> Result<(), rustls_pki_types::InvalidSignature> { ) -> Result<(), rustls_pki_types::InvalidSignature> {
let public_key = let public_key = ed_public_key_for_scheme(public_key, self.0)
ed_public_key_for_scheme(public_key, self.0).map_err(|_| InvalidSignature)?; .map_err(|e| log_and_map("ed_public_key_for_scheme", e, InvalidSignature))?;
let mut verifier = ed_verifier_from_params(public_key.as_ref());
let mut verifier = ed_verifier_from_params(public_key.as_ref())
.map_err(|e| log_and_map("ed_verifier_from_params", e, InvalidSignature))?;
verifier.verify_oneshot(signature, message).map_or_else( verifier.verify_oneshot(signature, message).map_or_else(
|_| Err(InvalidSignature), |_| Err(InvalidSignature),
@ -50,11 +52,8 @@ impl SignatureVerificationAlgorithm for BoringEdVerifier {
fn ed_verifier_from_params( fn ed_verifier_from_params(
key: &boring::pkey::PKeyRef<boring::pkey::Public>, key: &boring::pkey::PKeyRef<boring::pkey::Public>,
) -> boring::sign::Verifier { ) -> Result<boring::sign::Verifier, ErrorStack> {
let verifier = boring::sign::Verifier::new_without_digest(key)
boring::sign::Verifier::new_without_digest(key).expect("failed getting verifier");
verifier
} }
fn ed_public_key_for_scheme( fn ed_public_key_for_scheme(
@ -66,10 +65,10 @@ fn ed_public_key_for_scheme(
SignatureScheme::ED448 => boring_sys::EVP_PKEY_ED448, SignatureScheme::ED448 => boring_sys::EVP_PKEY_ED448,
_ => unimplemented!(), _ => unimplemented!(),
}); });
ed_public_key(spki_spk, nid) public_key(spki_spk, nid)
} }
pub fn ed_public_key( pub fn public_key(
spki_spk: &[u8], spki_spk: &[u8],
nid: boring::nid::Nid, nid: boring::nid::Nid,
) -> Result<boring::pkey::PKey<boring::pkey::Public>, ErrorStack> { ) -> Result<boring::pkey::PKey<boring::pkey::Public>, ErrorStack> {

View file

@ -1,8 +1,16 @@
use boring::{hash::MessageDigest, rsa::Padding, sign::RsaPssSaltlen}; use boring::{
bn::BigNum,
hash::MessageDigest,
pkey::PKey,
rsa::{Padding, Rsa},
sign::RsaPssSaltlen,
};
use rustls::SignatureScheme; use rustls::SignatureScheme;
use rustls_pki_types::{InvalidSignature, SignatureVerificationAlgorithm}; use rustls_pki_types::{InvalidSignature, SignatureVerificationAlgorithm};
use spki::der::Reader; use spki::der::Reader;
use crate::helper::log_and_map;
pub struct BoringRsaVerifier(SignatureScheme); pub struct BoringRsaVerifier(SignatureScheme);
impl BoringRsaVerifier { impl BoringRsaVerifier {
@ -105,18 +113,24 @@ fn rsa_verifier_from_params(
pub(crate) fn decode_spki_spk( pub(crate) fn decode_spki_spk(
spki_spk: &[u8], spki_spk: &[u8],
) -> Result<boring::pkey::PKey<boring::pkey::Public>, InvalidSignature> { ) -> Result<PKey<boring::pkey::Public>, InvalidSignature> {
// public_key: unfortunately this is not a whole SPKI, but just the key material. // public_key: unfortunately this is not a whole SPKI, but just the key material.
// decode the two integers manually. // decode the two integers manually.
let mut reader = spki::der::SliceReader::new(spki_spk).map_err(|_| InvalidSignature)?; let mut reader = spki::der::SliceReader::new(spki_spk)
let ne: [spki::der::asn1::UintRef; 2] = reader.decode().map_err(|_| InvalidSignature)?; .map_err(|e| log_and_map("SliceReader::new", e, InvalidSignature))?;
let ne: [spki::der::asn1::UintRef; 2] = reader
.decode()
.map_err(|e| log_and_map("SliceReader::decode", e, InvalidSignature))?;
let n = boring::bn::BigNum::from_slice(ne[0].as_bytes()).map_err(|_| InvalidSignature)?; let n = BigNum::from_slice(ne[0].as_bytes())
let e = boring::bn::BigNum::from_slice(ne[1].as_bytes()).map_err(|_| InvalidSignature)?; .map_err(|e| log_and_map("BigNum::from_slice", e, InvalidSignature))?;
let e = BigNum::from_slice(ne[1].as_bytes())
.map_err(|e| log_and_map("BigNum::from_slice", e, InvalidSignature))?;
boring::pkey::PKey::from_rsa( PKey::from_rsa(
boring::rsa::Rsa::from_public_components(n, e).map_err(|_| InvalidSignature)?, Rsa::from_public_components(n, e)
.map_err(|e| log_and_map("Rsa::from_public_components", e, InvalidSignature))?,
) )
.map_err(|_| InvalidSignature) .map_err(|e| log_and_map("Pkey::from_rsa", e, InvalidSignature))
} }