Enhance error handling and docs
This commit is contained in:
parent
6f1394e4b4
commit
bd80bfc4d7
18 changed files with 295 additions and 206 deletions
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
)?))
|
)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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> {
|
||||||
|
|
|
||||||
|
|
@ -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> {
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue