Use boringssl prf for tls1.2

This commit is contained in:
Jan Rüth 2023-11-25 15:20:07 +01:00 committed by Jan
commit d38412a3bd
9 changed files with 285 additions and 7 deletions

View file

@ -2,6 +2,8 @@
members = [ members = [
# things that should probably be in boring crate # things that should probably be in boring crate
"boring-additions", "boring-additions",
# things that should probably be in boring-sys crate
"boring-sys-additions",
# the main library and tests # the main library and tests
"boring-rustls-provider", "boring-rustls-provider",
# tests and example code # tests and example code

View file

@ -17,6 +17,18 @@ AES_256_GCM_SHA384
CHACHA20_POLY1305_SHA256 CHACHA20_POLY1305_SHA256
``` ```
TLS 1.2 prepared for (doesn't work yet):
```
ECDHE_ECDSA_AES128_GCM_SHA256
ECDHE_RSA_AES128_GCM_SHA256
ECDHE_ECDSA_AES256_GCM_SHA384
ECDHE_RSA_AES256_GCM_SHA384
ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
```
### Key Exchange Algorithms ### Key Exchange Algorithms
`ECDHE` with curves: `ECDHE` with curves:
@ -37,10 +49,10 @@ ffdhe2048
### Signature Generation / Verification ### Signature Generation / Verification
``` ```
RSA_PKCS1_SHA256, RSA_PKCS1_SHA256
RSA_PKCS1_SHA384, RSA_PKCS1_SHA384
RSA_PKCS1_SHA512, RSA_PKCS1_SHA512
RSA_PSS_SHA256, RSA_PSS_SHA256
RSA_PSS_SHA384 RSA_PSS_SHA384
RSA_PSS_SHA512 RSA_PSS_SHA512
ECDSA_NISTP256_SHA256 ECDSA_NISTP256_SHA256

View file

@ -21,6 +21,7 @@ aead = {version = "0.5", default_features = false, features = ["alloc"] }
boring = { workspace = true } boring = { workspace = true }
boring-additions = { path = "../boring-additions" } boring-additions = { path = "../boring-additions" }
boring-sys = { workspace = true } boring-sys = { workspace = true }
boring-sys-additions = { path = "../boring-sys-additions" }
foreign-types = "0.5" foreign-types = "0.5"
lazy_static = "1.4" lazy_static = "1.4"
log = { version = "0.4.4", optional = true } log = { version = "0.4.4", optional = true }

View file

@ -10,7 +10,9 @@ use crate::helper::{cvt, cvt_p};
/// A SHA256-based Hmac /// 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 /// A SHA384-based Hmac
#[allow(unused)]
pub const SHA384: &dyn crypto::hmac::Hmac = &BoringHmac(boring::nid::Nid::SHA384); pub const SHA384: &dyn crypto::hmac::Hmac = &BoringHmac(boring::nid::Nid::SHA384);
struct BoringHmac(pub boring::nid::Nid); struct BoringHmac(pub boring::nid::Nid);

View file

@ -13,6 +13,8 @@ mod helper;
mod hkdf; mod hkdf;
mod hmac; mod hmac;
mod kx; mod kx;
#[cfg(feature = "tls12")]
mod prf;
mod sign; mod sign;
#[cfg(feature = "tls12")] #[cfg(feature = "tls12")]
mod tls12; mod tls12;

View file

@ -0,0 +1,222 @@
use std::ptr;
use boring::error::ErrorStack;
use rustls::crypto;
use crate::helper::{cvt, log_and_map};
pub struct PrfTls1WithDigest(pub boring::nid::Nid);
pub struct MySecret(Vec<u8>);
impl crypto::tls12::Prf for PrfTls1WithDigest {
fn for_key_exchange(
&self,
output: &mut [u8],
kx: Box<dyn crypto::ActiveKeyExchange>,
peer_pub_key: &[u8],
label: &[u8],
seed: &[u8],
) -> Result<(), rustls::Error> {
let digest = boring::hash::MessageDigest::from_nid(self.0)
.ok_or(rustls::Error::General("invalid digest for prf".into()))?;
let secret = kx.complete(peer_pub_key)?;
let secret: MySecret = unsafe {
// I don't see another way to get to the secret...
std::mem::transmute(secret)
};
prf(digest, output, &secret.0, label, seed)
.map_err(|e| log_and_map("prf", e, rustls::Error::General("failed on prf".into())))
}
fn for_secret(&self, output: &mut [u8], secret: &[u8], label: &[u8], seed: &[u8]) {
let digest = boring::hash::MessageDigest::from_nid(self.0).expect("failed getting digest");
prf(digest, output, secret, label, seed).expect("failed calculating prf")
}
}
fn prf(
digest: boring::hash::MessageDigest,
output: &mut [u8],
secret: &[u8],
label: &[u8],
seed: &[u8],
) -> Result<(), ErrorStack> {
unsafe {
// seed is already concatenated from the randoms, so we
// can simply pass it in as a single seed
cvt(boring_sys_additions::CRYPTO_tls1_prf(
digest.as_ptr(),
output.as_mut_ptr(),
output.len(),
secret.as_ptr(),
secret.len(),
label.as_ptr(),
label.len(),
seed.as_ptr(),
seed.len(),
ptr::null(),
0,
))
}
.map(|_| ())
}
#[cfg(test)]
mod tests {
use hex_literal::hex;
use crate::prf::prf;
#[test]
fn test_tls12_prf() {
struct TestVector {
test_name: &'static str,
digest: boring::nid::Nid,
secret: &'static [u8],
label: &'static [u8],
seed: &'static [u8],
expected_output: &'static [u8],
}
// test vectors taken from https://mailarchive.ietf.org/arch/msg/tls/fzVCzk-z3FShgGJ6DOXqM1ydxms/
let inputs = &[
TestVector {
test_name: "88 bytes of pseudo-randomness using TLS1.2PRF-SHA224",
digest: boring::nid::Nid::SHA224,
secret: &hex!("e1 88 28 74 03 52 b5 30 d6 9b 34 c6 59 7d ea 2e"),
label: b"test label",
seed: &hex!("f5 a3 fe 6d 34 e2 e2 85 60 fd ca f6 82 3f 90 91"),
expected_output: &hex!(
"
22 4d 8a f3 c0 45 33 93
a9 77 97 89 d2 1c f7 da
5e e6 2a e6 b6 17 87 3d
48 94 28 ef c8 dd 58 d1
56 6e 70 29 e2 ca 3a 5e
cd 35 5d c6 4d 4d 92 7e
2f bd 78 c4 23 3e 86 04
b1 47 49 a7 7a 92 a7 0f
dd f6 14 bc 0d f6 23 d7
98 60 4e 4c a5 51 27 94
d8 02 a2 58 e8 2f 86 cf
"
),
},
TestVector {
test_name: "100 bytes of pseudo-randomness using TLS1.2PRF-SHA256",
digest: boring::nid::Nid::SHA256,
secret: &hex!("9b be 43 6b a9 40 f0 17 b1 76 52 84 9a 71 db 35"),
label: b"test label",
seed: &hex!("a0 ba 9f 93 6c da 31 18 27 a6 f7 96 ff d5 19 8c"),
expected_output: &hex!(
"
e3 f2 29 ba 72 7b e1 7b
8d 12 26 20 55 7c d4 53
c2 aa b2 1d 07 c3 d4 95
32 9b 52 d4 e6 1e db 5a
6b 30 17 91 e9 0d 35 c9
c9 a4 6b 4e 14 ba f9 af
0f a0 22 f7 07 7d ef 17
ab fd 37 97 c0 56 4b ab
4f bc 91 66 6e 9d ef 9b
97 fc e3 4f 79 67 89 ba
a4 80 82 d1 22 ee 42 c5
a7 2e 5a 51 10 ff f7 01
87 34 7b 66
"
),
},
TestVector {
test_name: "196 bytes of pseudo-randomness using TLS1.2PRF-SHA512",
digest: boring::nid::Nid::SHA512,
secret: &hex!("b0 32 35 23 c1 85 35 99 58 4d 88 56 8b bb 05 eb"),
label: b"test label",
seed: &hex!("d4 64 0e 12 e4 bc db fb 43 7f 03 e6 ae 41 8e e5"),
expected_output: &hex!(
"
12 61 f5 88 c7 98 c5 c2
01 ff 03 6e 7a 9c b5 ed
cd 7f e3 f9 4c 66 9a 12
2a 46 38 d7 d5 08 b2 83
04 2d f6 78 98 75 c7 14
7e 90 6d 86 8b c7 5c 45
e2 0e b4 0c 1c f4 a1 71
3b 27 37 1f 68 43 25 92
f7 dc 8e a8 ef 22 3e 12
ea 85 07 84 13 11 bf 68
65 3d 0c fc 40 56 d8 11
f0 25 c4 5d df a6 e6 fe
c7 02 f0 54 b4 09 d6 f2
8d d0 a3 23 3e 49 8d a4
1a 3e 75 c5 63 0e ed be
22 fe 25 4e 33 a1 b0 e9
f6 b9 82 66 75 be c7 d0
1a 84 56 58 dc 9c 39 75
45 40 1d 40 b9 f4 6c 7a
40 0e e1 b8 f8 1c a0 a6
0d 1a 39 7a 10 28 bf f5
d2 ef 50 66 12 68 42 fb
8d a4 19 76 32 bd b5 4f
f6 63 3f 86 bb c8 36 e6
40 d4 d8 98
"
),
},
TestVector {
test_name: "148 bytes of pseudo-randomness using TLS1.2PRF-SHA384",
digest: boring::nid::Nid::SHA384,
secret: &hex!("b8 0b 73 3d 6c ee fc dc 71 56 6e a4 8e 55 67 df"),
label: b"test label",
seed: &hex!("cd 66 5c f6 a8 44 7d d6 ff 8b 27 55 5e db 74 65"),
expected_output: &hex!(
"
7b 0c 18 e9 ce d4 10 ed
18 04 f2 cf a3 4a 33 6a
1c 14 df fb 49 00 bb 5f
d7 94 21 07 e8 1c 83 cd
e9 ca 0f aa 60 be 9f e3
4f 82 b1 23 3c 91 46 a0
e5 34 cb 40 0f ed 27 00
88 4f 9d c2 36 f8 0e dd
8b fa 96 11 44 c9 e8 d7
92 ec a7 22 a7 b3 2f c3
d4 16 d4 73 eb c2 c5 fd
4a bf da d0 5d 91 84 25
9b 5b f8 cd 4d 90 fa 0d
31 e2 de c4 79 e4 f1 a2
60 66 f2 ee a9 a6 92 36
a3 e5 26 55 c9 e9 ae e6
91 c8 f3 a2 68 54 30 8d
5e aa 3b e8 5e 09 90 70
3d 73 e5 6f
"
),
},
];
for input in inputs {
let digest = boring::hash::MessageDigest::from_nid(input.digest).unwrap();
let mut calculated_output = vec![0u8; input.expected_output.len()];
prf(
digest,
calculated_output.as_mut(),
input.secret,
input.label,
input.seed,
)
.unwrap();
assert_eq!(
calculated_output.as_slice(),
input.expected_output,
"test: {} failed",
input.test_name
);
}
}
}

View file

@ -1,6 +1,6 @@
use rustls::{crypto, SignatureScheme, Tls12CipherSuite}; use rustls::{crypto, SignatureScheme, Tls12CipherSuite};
use crate::{aead, hash, hmac}; use crate::{aead, hash, prf};
static ALL_ECDSA_SCHEMES: &[SignatureScheme] = &[ static ALL_ECDSA_SCHEMES: &[SignatureScheme] = &[
SignatureScheme::ECDSA_NISTP256_SHA256, SignatureScheme::ECDSA_NISTP256_SHA256,
@ -19,8 +19,8 @@ static ALL_RSA_SCHEMES: &[SignatureScheme] = &[
SignatureScheme::RSA_PSS_SHA512, SignatureScheme::RSA_PSS_SHA512,
]; ];
const PRF_SHA256: crypto::tls12::PrfUsingHmac<'_> = crypto::tls12::PrfUsingHmac(hmac::SHA256); const PRF_SHA256: prf::PrfTls1WithDigest = prf::PrfTls1WithDigest(boring::nid::Nid::SHA256);
const PRF_SHA384: crypto::tls12::PrfUsingHmac<'_> = crypto::tls12::PrfUsingHmac(hmac::SHA384); const PRF_SHA384: prf::PrfTls1WithDigest = prf::PrfTls1WithDigest(boring::nid::Nid::SHA384);
pub static ECDHE_ECDSA_AES128_GCM_SHA256: Tls12CipherSuite = Tls12CipherSuite { pub static ECDHE_ECDSA_AES128_GCM_SHA256: Tls12CipherSuite = Tls12CipherSuite {
common: rustls::CipherSuiteCommon { common: rustls::CipherSuiteCommon {

View file

@ -0,0 +1,15 @@
[package]
name = "boring-sys-additions"
version = "0.0.1"
authors = ["Jan Rüth <boring-rustls-provider@djiehmail.com>"]
edition = "2021"
license = "MIT"
description = "Boring-sys additions"
publish = false
[dependencies]
boring-sys = { workspace = true }

View file

@ -0,0 +1,22 @@
use std::ffi;
extern "C" {
/// Calculates `out_len` bytes of the TLS PDF, using `digest`, and
/// writes them to `out`. It returns one on success and zero on error.
///
/// This isn't part of the public headers in `BoringSSL` but it is exported
/// in `crypto/fipsmodule/tls/internal.h` :)
pub fn CRYPTO_tls1_prf(
digest: *const boring_sys::EVP_MD,
out: *mut u8,
out_len: usize,
secret: *const u8,
secret_len: usize,
label: *const u8,
label_len: usize,
seed1: *const u8,
seed1_len: usize,
seed2: *const u8,
seed2_len: usize,
) -> ffi::c_int;
}