moved prf back to tls-core

This commit is contained in:
sinuio
2022-03-23 13:26:51 -07:00
parent 90b12e2610
commit 7d14b34421
7 changed files with 594 additions and 2 deletions

View File

@@ -1,12 +1,18 @@
[package]
name = "pop-tls"
name = "pop-tls-core"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["prf"]
prf = []
[dependencies]
pop-mpc-core = { path = "../pop-mpc-core" }
sha2 = { version = "0.10.1", features = ["compress"] }
digest = { version = "0.10.3" }
hmac = { version = "0.12.1" }
[dev-dependencies]
criterion = "0.3.5"

View File

@@ -1 +1,2 @@
#[cfg(feature = "prf")]
pub mod prf;

View File

@@ -0,0 +1,225 @@
use super::sha::finalize_sha256_digest;
use super::slave::{SlaveKe1, SlaveKe2, SlaveMs1, SlaveMs2};
use super::utils::{seed_ke, seed_ms};
pub struct Initialized;
pub struct Ms1 {
/// master secret || client_random || server_random
seed: [u8; 77],
/// H(pms xor ipad)
inner_hash_state: [u32; 8],
}
pub struct Ms2 {
/// master secret || client_random || server_random
seed: [u8; 77],
/// H(pms xor ipad)
inner_hash_state: [u32; 8],
}
pub struct Ke1;
pub struct Ke2 {
/// key expansion || client_random || server_random
seed: [u8; 77],
/// H(ms xor ipad)
inner_hash_state: [u32; 8],
}
pub struct Ke3 {
/// key expansion || client_random || server_random
seed: [u8; 77],
/// H(ms xor ipad)
inner_hash_state: [u32; 8],
/// H((ms xor opad) || H((ms xor ipad) || seed))
a1: [u8; 32],
}
pub struct Cf;
pub trait State {}
impl State for Initialized {}
impl State for Ms1 {}
impl State for Ms2 {}
impl State for Ke1 {}
impl State for Ke2 {}
impl State for Ke3 {}
impl State for Cf {}
pub struct PrfMaster<S>
where
S: State,
{
/// State of 2PC PRF Protocol
state: S,
client_random: [u8; 32],
server_random: [u8; 32],
}
pub struct MasterMs1 {
/// H((pms xor ipad) || seed)
pub inner_hash: [u8; 32],
}
pub struct MasterMs2 {
/// H((pms xor ipad) || a1)
pub inner_hash: [u8; 32],
}
pub struct MasterMs3 {
/// H((pms xor ipad) || a2)
pub inner_hash: [u8; 32],
}
pub struct MasterKe1 {
/// H((ms xor ipad) || seed)
pub inner_hash: [u8; 32],
}
pub struct MasterKe2 {
/// H((ms xor ipad) || a1)
pub inner_hash: [u8; 32],
}
pub struct MasterKe3 {
/// H((ms xor ipad) || a1 || seed)
pub inner_hash_p1: [u8; 32],
/// H((ms xor ipad) || a2 || seed)
pub inner_hash_p2: [u8; 32],
}
impl PrfMaster<Initialized> {
pub fn new(client_random: [u8; 32], server_random: [u8; 32]) -> Self {
Self {
state: Initialized,
client_random,
server_random,
}
}
pub fn next(self, inner_hash_state: [u32; 8]) -> (MasterMs1, PrfMaster<Ms1>) {
let seed = seed_ms(&self.client_random, &self.server_random);
// H((pms xor ipad) || seed)
let inner_hash = finalize_sha256_digest(inner_hash_state.clone(), 64, &seed);
(
MasterMs1 { inner_hash },
PrfMaster {
state: Ms1 {
seed,
inner_hash_state,
},
client_random: self.client_random,
server_random: self.server_random,
},
)
}
}
impl PrfMaster<Ms1> {
pub fn next(self, m: SlaveMs1) -> (MasterMs2, PrfMaster<Ms2>) {
// H((pms xor ipad) || a1)
let inner_hash = finalize_sha256_digest(self.state.inner_hash_state.clone(), 64, &m.a1);
(
MasterMs2 { inner_hash },
PrfMaster {
state: Ms2 {
seed: self.state.seed,
inner_hash_state: self.state.inner_hash_state,
},
client_random: self.client_random,
server_random: self.server_random,
},
)
}
}
impl PrfMaster<Ms2> {
pub fn next(self, m: SlaveMs2) -> (MasterMs3, PrfMaster<Ke1>) {
let mut a2_seed = [0u8; 109];
a2_seed[..32].copy_from_slice(&m.a2);
a2_seed[32..].copy_from_slice(&self.state.seed);
// H((pms xor ipad) || a2 || seed)
let inner_hash = finalize_sha256_digest(self.state.inner_hash_state, 64, &a2_seed);
(
MasterMs3 { inner_hash },
PrfMaster {
state: Ke1,
client_random: self.client_random,
server_random: self.server_random,
},
)
}
}
impl PrfMaster<Ke1> {
pub fn next(self, inner_hash_state: [u32; 8]) -> (MasterKe1, PrfMaster<Ke2>) {
let seed = seed_ke(&self.client_random, &self.server_random);
// H((ms xor ipad) || seed)
let inner_hash = finalize_sha256_digest(inner_hash_state.clone(), 64, &seed);
(
MasterKe1 { inner_hash },
PrfMaster {
state: Ke2 {
seed,
inner_hash_state,
},
client_random: self.client_random,
server_random: self.server_random,
},
)
}
}
impl PrfMaster<Ke2> {
pub fn next(self, m: SlaveKe1) -> (MasterKe2, PrfMaster<Ke3>) {
// H((pms xor ipad) || a1)
let inner_hash = finalize_sha256_digest(self.state.inner_hash_state.clone(), 64, &m.a1);
(
MasterKe2 { inner_hash },
PrfMaster {
state: Ke3 {
a1: m.a1,
seed: self.state.seed,
inner_hash_state: self.state.inner_hash_state,
},
client_random: self.client_random,
server_random: self.server_random,
},
)
}
}
impl PrfMaster<Ke3> {
pub fn next(self, m: SlaveKe2) -> (MasterKe3, PrfMaster<Cf>) {
let mut a1_seed = [0u8; 109];
a1_seed[..32].copy_from_slice(&self.state.a1);
a1_seed[32..].copy_from_slice(&self.state.seed);
let mut a2_seed = [0u8; 109];
a2_seed[..32].copy_from_slice(&m.a2);
a2_seed[32..].copy_from_slice(&self.state.seed);
// H((pms xor ipad) || a1 || seed)
let inner_hash_p1 =
finalize_sha256_digest(self.state.inner_hash_state.clone(), 64, &a1_seed);
// H((pms xor ipad) || a2 || seed)
let inner_hash_p2 = finalize_sha256_digest(self.state.inner_hash_state, 64, &a2_seed);
(
MasterKe3 {
inner_hash_p1,
inner_hash_p2,
},
PrfMaster {
state: Cf,
client_random: self.client_random,
server_random: self.server_random,
},
)
}
}

103
pop-tls-core/src/prf/mod.rs Normal file
View File

@@ -0,0 +1,103 @@
pub mod master;
mod sha;
pub mod slave;
mod utils;
pub use master::PrfMaster;
pub use slave::PrfSlave;
#[cfg(test)]
mod tests {
use super::*;
use sha::{finalize_sha256_digest, partial_sha256_digest};
use utils::*;
#[test]
fn test_prf() {
let client_random = b"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC";
let server_random = b"SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS";
let pms = [0x99_u8; 32];
let (ipad, opad) = generate_hmac_pads(&pms);
let master = PrfMaster::new(*client_random, *server_random);
let slave = PrfSlave::new();
// H(pms xor ipad)
let inner_hash_state = partial_sha256_digest(&ipad);
// H(pms xor opad)
let outer_hash_state = partial_sha256_digest(&opad);
let (message, master) = master.next(inner_hash_state.clone());
let (message, slave) = slave.next(outer_hash_state.clone(), message);
// H((pms xor opad) || H((pms xor ipad) || seed))
let a1 = message.a1.clone();
assert_eq!(
&a1,
&hmac_sha256(&pms, &seed_ms(client_random, server_random))
);
let (message, master) = master.next(message);
let (message, slave) = slave.next(message);
// H((pms xor opad) || H((pms xor ipad) || a1))
let a2 = message.a2.clone();
assert_eq!(&a2, &hmac_sha256(&pms, &a1));
let (message, master) = master.next(message);
let (message, slave) = slave.next(message);
// H((pms xor opad) || H((pms xor ipad) || a2 || seed))
let p2 = message.p2.clone();
// a1 || seed
let mut a1_seed = [0u8; 109];
a1_seed[..32].copy_from_slice(&a1);
a1_seed[32..].copy_from_slice(&seed_ms(client_random, server_random));
// H((pms xor opad) || H((pms xor ipad) || a1 || seed))
let p1 = finalize_sha256_digest(outer_hash_state, 64, &a1_seed);
let mut ms = [0u8; 48];
ms[..32].copy_from_slice(&p1);
ms[32..48].copy_from_slice(&p2[..16]);
let (ipad, opad) = generate_hmac_pads(&ms);
// H(ms xor ipad)
let inner_hash_state = partial_sha256_digest(&ipad);
// H(ms xor opad)
let outer_hash_state = partial_sha256_digest(&opad);
let (message, master) = master.next(inner_hash_state.clone());
let (message, slave) = slave.next(outer_hash_state.clone(), message);
// H((ms xor opad) || H((ms xor ipad) || seed))
let a1 = message.a1.clone();
assert_eq!(
&a1,
&hmac_sha256(&ms, &seed_ke(&client_random, &server_random))
);
let (message, master) = master.next(message);
let (message, slave) = slave.next(message);
// H((ms xor opad) || H((ms xor ipad) || a1))
let a2 = message.a2.clone();
assert_eq!(&a2, &hmac_sha256(&ms, &a1));
let (inner_hashes, master) = master.next(message);
let p1 = finalize_sha256_digest(outer_hash_state.clone(), 64, &inner_hashes.inner_hash_p1);
let p2 = finalize_sha256_digest(outer_hash_state, 64, &inner_hashes.inner_hash_p2);
let mut ek = [0u8; 40];
ek[..32].copy_from_slice(&p1);
ek[32..].copy_from_slice(&p2[..8]);
let client_write_key = &ek[..16];
let server_write_key = &ek[16..32];
let client_write_iv = &ek[32..36];
let server_write_iv = &ek[36..];
}
}

View File

@@ -0,0 +1,81 @@
use core::slice::from_ref;
use digest::{
block_buffer::{BlockBuffer, Eager},
generic_array::GenericArray,
typenum::U64,
};
use sha2::compress256;
#[allow(dead_code)]
#[inline]
pub fn partial_sha256_digest(input: &[u8]) -> [u32; 8] {
if input.len() % 64 != 0 {
panic!("input length must be a multiple of 64");
}
let mut state = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
0x5be0cd19,
];
for b in input.chunks_exact(64) {
let mut block = GenericArray::<u8, U64>::default();
block[..].copy_from_slice(b);
compress256(&mut state, &[block]);
}
state
}
/// Takes existing state from SHA2 hash and finishes it with additional data
#[inline]
pub fn finalize_sha256_digest(mut state: [u32; 8], pos: usize, input: &[u8]) -> [u8; 32] {
let mut buffer = BlockBuffer::<U64, Eager>::default();
buffer.digest_blocks(input, |b| compress256(&mut state, b));
buffer.digest_pad(
0x80,
&(((input.len() + pos) * 8) as u64).to_be_bytes(),
|b| compress256(&mut state, from_ref(b)),
);
let mut out: [u8; 32] = [0; 32];
for (chunk, v) in out.chunks_exact_mut(4).zip(state.iter()) {
chunk.copy_from_slice(&v.to_be_bytes());
}
out
}
#[cfg(test)]
mod tests {
use super::*;
use sha2::{Digest, Sha256};
#[test]
fn test_sha2_initial_state() {
let s = b"test string";
// initial state for sha2
let state = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
0x5be0cd19,
];
let digest = finalize_sha256_digest(state, 0, s);
let mut hasher = Sha256::new();
hasher.update(s);
assert_eq!(digest, hasher.finalize().as_slice());
}
#[test]
fn test_sha2_resume_state() {
let s = b"test string test string test string test string test string test";
let state = partial_sha256_digest(s);
let s2 = b"additional data";
let digest = finalize_sha256_digest(state, s.len(), s2);
let mut hasher = Sha256::new();
hasher.update(s);
hasher.update(s2);
assert_eq!(digest, hasher.finalize().as_slice());
}
}

View File

@@ -0,0 +1,131 @@
use super::master::{MasterKe1, MasterKe2, MasterMs1, MasterMs2, MasterMs3};
use super::sha::finalize_sha256_digest;
pub struct Initialized;
pub struct Ms1 {
/// H(pms xor opad)
outer_hash_state: [u32; 8],
}
pub struct Ms2 {
/// H(pms xor opad)
outer_hash_state: [u32; 8],
}
pub struct Ke1;
pub struct Ke2 {
/// H(ms xor opad)
outer_hash_state: [u32; 8],
}
pub struct Cf;
pub trait State {}
impl State for Initialized {}
impl State for Ms1 {}
impl State for Ms2 {}
impl State for Ke1 {}
impl State for Ke2 {}
impl State for Cf {}
pub struct PrfSlave<S>
where
S: State,
{
/// State of 2PC PRF Protocol
state: S,
}
pub struct SlaveMs1 {
/// H((pms xor opad) || H((pms xor ipad) || seed))
pub a1: [u8; 32],
}
pub struct SlaveMs2 {
/// H((pms xor opad) || H((pms xor ipad) || a1))
pub a2: [u8; 32],
}
pub struct SlaveMs3 {
/// H((pms xor opad) || H((pms xor ipad) || a2 || seed))
pub p2: [u8; 32],
}
pub struct SlaveKe1 {
/// H((ms xor opad) || H((ms xor ipad) || seed))
pub a1: [u8; 32],
}
pub struct SlaveKe2 {
/// H((ms xor opad) || H((ms xor ipad) || a1))
pub a2: [u8; 32],
}
pub struct SlaveCf {}
impl PrfSlave<Initialized> {
pub fn new() -> Self {
Self { state: Initialized }
}
pub fn next(self, outer_hash_state: [u32; 8], m: MasterMs1) -> (SlaveMs1, PrfSlave<Ms1>) {
// H((pms xor opad) || H((pms xor ipad) || seed))
let a1 = finalize_sha256_digest(outer_hash_state.clone(), 64, &m.inner_hash);
(
SlaveMs1 { a1 },
PrfSlave {
state: Ms1 { outer_hash_state },
},
)
}
}
impl PrfSlave<Ms1> {
pub fn next(self, m: MasterMs2) -> (SlaveMs2, PrfSlave<Ms2>) {
// H((pms xor opad) || H((pms xor ipad) || a1))
let a2 = finalize_sha256_digest(self.state.outer_hash_state.clone(), 64, &m.inner_hash);
(
SlaveMs2 { a2 },
PrfSlave {
state: Ms2 {
outer_hash_state: self.state.outer_hash_state,
},
},
)
}
}
impl PrfSlave<Ms2> {
pub fn next(self, m: MasterMs3) -> (SlaveMs3, PrfSlave<Ke1>) {
// H((pms xor opad) || H((pms xor ipad) || a2 || seed))
let p2 = finalize_sha256_digest(self.state.outer_hash_state, 64, &m.inner_hash);
(SlaveMs3 { p2 }, PrfSlave { state: Ke1 })
}
}
impl PrfSlave<Ke1> {
pub fn next(self, outer_hash_state: [u32; 8], m: MasterKe1) -> (SlaveKe1, PrfSlave<Ke2>) {
// H((pms xor opad) || H((pms xor ipad) || seed))
let a1 = finalize_sha256_digest(outer_hash_state.clone(), 64, &m.inner_hash);
(
SlaveKe1 { a1 },
PrfSlave {
state: Ke2 { outer_hash_state },
},
)
}
}
impl PrfSlave<Ke2> {
pub fn next(self, m: MasterKe2) -> (SlaveKe2, PrfSlave<Cf>) {
// H((pms xor opad) || H((pms xor ipad) || a1))
let a2 = finalize_sha256_digest(self.state.outer_hash_state.clone(), 64, &m.inner_hash);
(SlaveKe2 { a2 }, PrfSlave { state: Cf })
}
}

View File

@@ -0,0 +1,45 @@
#![allow(dead_code)]
use hmac::{Hmac, Mac};
use sha2::Sha256;
use std::convert::TryInto;
type HmacSha256 = Hmac<Sha256>;
pub(crate) fn hmac_sha256(key: &[u8], input: &[u8]) -> [u8; 32] {
let mut mac = HmacSha256::new_from_slice(key).unwrap();
mac.update(input);
let out = mac.finalize().into_bytes();
out[..32]
.try_into()
.expect("expected output to be 32 bytes")
}
pub(crate) fn generate_hmac_pads(input: &[u8]) -> ([u8; 64], [u8; 64]) {
let mut ipad = [0x36_u8; 64];
let mut opad = [0x5c_u8; 64];
for (ipad, input) in ipad.iter_mut().zip(input.iter()) {
*ipad = *ipad ^ *input;
}
for (opad, input) in opad.iter_mut().zip(input.iter()) {
*opad = *opad ^ *input;
}
(ipad, opad)
}
pub(crate) fn seed_ms(client_random: &[u8; 32], server_random: &[u8; 32]) -> [u8; 77] {
let mut seed = [0u8; 77];
seed[..13].copy_from_slice(b"master secret");
seed[13..45].copy_from_slice(client_random);
seed[45..].copy_from_slice(server_random);
seed
}
pub(crate) fn seed_ke(client_random: &[u8; 32], server_random: &[u8; 32]) -> [u8; 77] {
let mut seed = [0u8; 77];
seed[..13].copy_from_slice(b"key expansion");
seed[13..45].copy_from_slice(server_random);
seed[45..].copy_from_slice(client_random);
seed
}