Use boringssl prf for tls1.2
This commit is contained in:
parent
bd80bfc4d7
commit
d38412a3bd
9 changed files with 285 additions and 7 deletions
|
|
@ -2,6 +2,8 @@
|
|||
members = [
|
||||
# things that should probably be in boring crate
|
||||
"boring-additions",
|
||||
# things that should probably be in boring-sys crate
|
||||
"boring-sys-additions",
|
||||
# the main library and tests
|
||||
"boring-rustls-provider",
|
||||
# tests and example code
|
||||
|
|
|
|||
20
Readme.md
20
Readme.md
|
|
@ -17,6 +17,18 @@ AES_256_GCM_SHA384
|
|||
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
|
||||
|
||||
`ECDHE` with curves:
|
||||
|
|
@ -37,10 +49,10 @@ ffdhe2048
|
|||
### Signature Generation / Verification
|
||||
|
||||
```
|
||||
RSA_PKCS1_SHA256,
|
||||
RSA_PKCS1_SHA384,
|
||||
RSA_PKCS1_SHA512,
|
||||
RSA_PSS_SHA256,
|
||||
RSA_PKCS1_SHA256
|
||||
RSA_PKCS1_SHA384
|
||||
RSA_PKCS1_SHA512
|
||||
RSA_PSS_SHA256
|
||||
RSA_PSS_SHA384
|
||||
RSA_PSS_SHA512
|
||||
ECDSA_NISTP256_SHA256
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ aead = {version = "0.5", default_features = false, features = ["alloc"] }
|
|||
boring = { workspace = true }
|
||||
boring-additions = { path = "../boring-additions" }
|
||||
boring-sys = { workspace = true }
|
||||
boring-sys-additions = { path = "../boring-sys-additions" }
|
||||
foreign-types = "0.5"
|
||||
lazy_static = "1.4"
|
||||
log = { version = "0.4.4", optional = true }
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ use crate::helper::{cvt, cvt_p};
|
|||
/// A SHA256-based Hmac
|
||||
#[allow(unused)]
|
||||
pub const SHA256: &dyn crypto::hmac::Hmac = &BoringHmac(boring::nid::Nid::SHA256);
|
||||
|
||||
/// A SHA384-based Hmac
|
||||
#[allow(unused)]
|
||||
pub const SHA384: &dyn crypto::hmac::Hmac = &BoringHmac(boring::nid::Nid::SHA384);
|
||||
|
||||
struct BoringHmac(pub boring::nid::Nid);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ mod helper;
|
|||
mod hkdf;
|
||||
mod hmac;
|
||||
mod kx;
|
||||
#[cfg(feature = "tls12")]
|
||||
mod prf;
|
||||
mod sign;
|
||||
#[cfg(feature = "tls12")]
|
||||
mod tls12;
|
||||
|
|
|
|||
222
boring-rustls-provider/src/prf.rs
Normal file
222
boring-rustls-provider/src/prf.rs
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
use rustls::{crypto, SignatureScheme, Tls12CipherSuite};
|
||||
|
||||
use crate::{aead, hash, hmac};
|
||||
use crate::{aead, hash, prf};
|
||||
|
||||
static ALL_ECDSA_SCHEMES: &[SignatureScheme] = &[
|
||||
SignatureScheme::ECDSA_NISTP256_SHA256,
|
||||
|
|
@ -19,8 +19,8 @@ static ALL_RSA_SCHEMES: &[SignatureScheme] = &[
|
|||
SignatureScheme::RSA_PSS_SHA512,
|
||||
];
|
||||
|
||||
const PRF_SHA256: crypto::tls12::PrfUsingHmac<'_> = crypto::tls12::PrfUsingHmac(hmac::SHA256);
|
||||
const PRF_SHA384: crypto::tls12::PrfUsingHmac<'_> = crypto::tls12::PrfUsingHmac(hmac::SHA384);
|
||||
const PRF_SHA256: prf::PrfTls1WithDigest = prf::PrfTls1WithDigest(boring::nid::Nid::SHA256);
|
||||
const PRF_SHA384: prf::PrfTls1WithDigest = prf::PrfTls1WithDigest(boring::nid::Nid::SHA384);
|
||||
|
||||
pub static ECDHE_ECDSA_AES128_GCM_SHA256: Tls12CipherSuite = Tls12CipherSuite {
|
||||
common: rustls::CipherSuiteCommon {
|
||||
|
|
|
|||
15
boring-sys-additions/Cargo.toml
Normal file
15
boring-sys-additions/Cargo.toml
Normal 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 }
|
||||
|
||||
|
||||
|
||||
|
||||
22
boring-sys-additions/src/lib.rs
Normal file
22
boring-sys-additions/src/lib.rs
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue