ecdhe: add x25519

This commit is contained in:
occheung 2020-11-05 16:25:14 +08:00
parent 5638a660c2
commit 3bd54d682a
4 changed files with 87 additions and 39 deletions

View File

@ -50,6 +50,11 @@ version = "1.0.1"
default-features = false default-features = false
features = [ "u64_backend" ] features = [ "u64_backend" ]
[dependencies.x25519-dalek]
version = "1.1.0"
default-features = false
features = [ "u64_backend" ]
# Fetch from master, for "no_std" + "alloc" combination # Fetch from master, for "no_std" + "alloc" combination
[dependencies.rsa] [dependencies.rsa]
git = "https://github.com/RustCrypto/RSA.git" git = "https://github.com/RustCrypto/RSA.git"

View File

@ -34,7 +34,7 @@ pub(crate) struct Session {
// Hash functions needed // Hash functions needed
hash: Hash, hash: Hash,
// Ephemeral secret for ECDHE key exchange // Ephemeral secret for ECDHE key exchange
ecdhe_secret: Option<EphemeralSecret>, ecdhe_secret: Option<(EphemeralSecret, x25519_dalek::EphemeralSecret)>,
// Block ciphers for client & server // Block ciphers for client & server
client_handshake_cipher: Option<Cipher>, client_handshake_cipher: Option<Cipher>,
server_handshake_cipher: Option<Cipher>, server_handshake_cipher: Option<Cipher>,
@ -101,6 +101,7 @@ impl Session {
pub(crate) fn client_update_for_ch( pub(crate) fn client_update_for_ch(
&mut self, &mut self,
ecdhe_secret: EphemeralSecret, ecdhe_secret: EphemeralSecret,
x25519_secret: x25519_dalek::EphemeralSecret,
session_id: [u8; 32], session_id: [u8; 32],
ch_slice: &[u8] ch_slice: &[u8]
) { ) {
@ -108,7 +109,7 @@ impl Session {
if self.state != TlsState::START || self.role != TlsRole::Client { if self.state != TlsState::START || self.role != TlsRole::Client {
todo!() todo!()
} }
self.ecdhe_secret = Some(ecdhe_secret); self.ecdhe_secret = Some((ecdhe_secret, x25519_secret));
self.session_id = Some(session_id); self.session_id = Some(session_id);
self.hash.update(ch_slice); self.hash.update(ch_slice);
self.state = TlsState::WAIT_SH; self.state = TlsState::WAIT_SH;
@ -120,7 +121,8 @@ impl Session {
pub(crate) fn client_update_for_sh( pub(crate) fn client_update_for_sh(
&mut self, &mut self,
cipher_suite: CipherSuite, cipher_suite: CipherSuite,
encoded_point: EncodedPoint, encoded_point: Option<EncodedPoint>,
x25519_shared: Option<x25519_dalek::PublicKey>,
sh_slice: &[u8] sh_slice: &[u8]
) { ) {
// Handle inappropriate call to move state // Handle inappropriate call to move state
@ -129,12 +131,34 @@ impl Session {
} }
// Generate ECDHE shared secret // Generate ECDHE shared secret
// Remove private secret // Remove private secret
let ecdhe_shared_secret = // let ecdhe_shared_secret =
self.ecdhe_secret // self.ecdhe_secret
.take() // .take()
.unwrap() // .unwrap()
.diffie_hellman(&encoded_point) // .diffie_hellman(&encoded_point)
.unwrap(); // .unwrap();
let mut shared_secret_bytes: [u8; 32] = [0; 32];
if encoded_point.is_some() {
let p256_shared_secret =
self.ecdhe_secret
.take()
.unwrap()
.0
.diffie_hellman(&encoded_point.unwrap())
.unwrap();
shared_secret_bytes.clone_from_slice(p256_shared_secret.as_bytes());
} else if x25519_shared.is_some() {
let x25519_shared_secret =
self.ecdhe_secret
.take()
.unwrap()
.1
.diffie_hellman(&x25519_shared.unwrap());
shared_secret_bytes.clone_from_slice(x25519_shared_secret.as_bytes());
} else {
todo!()
}
// Generate Handshake secret // Generate Handshake secret
match cipher_suite { match cipher_suite {
@ -161,7 +185,7 @@ impl Session {
let (handshake_secret, handshake_secret_hkdf) = let (handshake_secret, handshake_secret_hkdf) =
Hkdf::<Sha256>::extract( Hkdf::<Sha256>::extract(
Some(&derived_secret), Some(&derived_secret),
ecdhe_shared_secret.as_bytes() &shared_secret_bytes
); );
// Store the handshake secret // Store the handshake secret
@ -349,7 +373,7 @@ impl Session {
let (handshake_secret, handshake_secret_hkdf) = let (handshake_secret, handshake_secret_hkdf) =
Hkdf::<Sha384>::extract( Hkdf::<Sha384>::extract(
Some(&derived_secret), Some(&derived_secret),
ecdhe_shared_secret.as_bytes() &shared_secret_bytes
); );
// Store the handshake secret // Store the handshake secret

View File

@ -126,12 +126,13 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
// Prepare field that is randomised, // Prepare field that is randomised,
// Supply it to the TLS repr builder. // Supply it to the TLS repr builder.
let ecdh_secret = EphemeralSecret::random(&mut self.rng); let ecdh_secret = EphemeralSecret::random(&mut self.rng);
let x25519_secret = x25519_dalek::EphemeralSecret::new(&mut self.rng);
let mut random: [u8; 32] = [0; 32]; let mut random: [u8; 32] = [0; 32];
let mut session_id: [u8; 32] = [0; 32]; let mut session_id: [u8; 32] = [0; 32];
self.rng.fill_bytes(&mut random); self.rng.fill_bytes(&mut random);
self.rng.fill_bytes(&mut session_id); self.rng.fill_bytes(&mut session_id);
let repr = TlsRepr::new() let repr = TlsRepr::new()
.client_hello(&ecdh_secret, random, session_id.clone()); .client_hello(&ecdh_secret, &x25519_secret, random, session_id.clone());
// Update hash function with client hello handshake // Update hash function with client hello handshake
let mut array = [0; 512]; let mut array = [0; 512];
@ -145,6 +146,7 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
// Update TLS session // Update TLS session
self.session.borrow_mut().client_update_for_ch( self.session.borrow_mut().client_update_for_ch(
ecdh_secret, ecdh_secret,
x25519_secret,
session_id, session_id,
&slice[5..] &slice[5..]
); );
@ -313,7 +315,6 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
if repr.is_server_hello() { if repr.is_server_hello() {
// Check SH content: // Check SH content:
// random: Cannot represent HelloRequestRetry // random: Cannot represent HelloRequestRetry
// (TODO: Support other key shares, e.g. X25519)
// session_id_echo: should be same as the one sent by client // session_id_echo: should be same as the one sent by client
// cipher_suite: Store // cipher_suite: Store
// (TODO: Check if such suite was offered) // (TODO: Check if such suite was offered)
@ -321,11 +322,11 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
// //
// Check extensions: // Check extensions:
// supported_version: Must be TLS 1.3 // supported_version: Must be TLS 1.3
// key_share: Store key, must be in secp256r1 // key_share: Store key, must be in secp256r1 or x25519
// (TODO: Support other key shares ^)
// "Cache" for ECDHE server public info // "Cache" for ECDHE server public info
let mut server_public: Option<EncodedPoint> = None; let mut p256_public: Option<EncodedPoint> = None;
let mut x25519_public: Option<x25519_dalek::PublicKey> = None;
let mut selected_cipher: Option<CipherSuite> = None; let mut selected_cipher: Option<CipherSuite> = None;
// Process the handshake data within ServerHello // Process the handshake data within ServerHello
@ -377,22 +378,25 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
server_share server_share
} }
) = &extension.extension_data { ) = &extension.extension_data {
// TODO: Use legitimate checking to ensure the chosen match server_share.group {
// group is indeed acceptable, when allowing more (EC)DHE NamedGroup::secp256r1 => {
// key sharing p256_public.replace(
if server_share.group != NamedGroup::secp256r1 { EncodedPoint::from_untagged_bytes(
// Abort for wrong key sharing GenericArray::from_slice(&server_share.key_exchange[1..])
todo!() )
);
},
NamedGroup::x25519 => {
let mut x25519_server_key: [u8; 32] = [0; 32];
x25519_server_key.clone_from_slice(&server_share.key_exchange);
x25519_public.replace(
x25519_dalek::PublicKey::from(
x25519_server_key
)
);
}
_ => todo!()
} }
// Store key
// It is surely from secp256r1, no other groups are permitted
// Convert untagged bytes into encoded point on p256 eliptic curve
// Slice the first byte out of the bytes
server_public.replace(
EncodedPoint::from_untagged_bytes(
GenericArray::from_slice(&server_share.key_exchange[1..])
)
);
} }
} }
} }
@ -402,8 +406,8 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
todo!() todo!()
} }
// Check that both selected_cipher and server_public were received // Check that both selected_cipher and (p256_public XNOR x25519_public) were received
if selected_cipher.is_none() || server_public.is_none() { if selected_cipher.is_none() || (p256_public.is_none() && x25519_public.is_none()) {
// Abort communication // Abort communication
todo!() todo!()
} }
@ -412,7 +416,8 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
let mut session = self.session.borrow_mut(); let mut session = self.session.borrow_mut();
session.client_update_for_sh( session.client_update_for_sh(
selected_cipher.unwrap(), selected_cipher.unwrap(),
server_public.unwrap(), p256_public,
x25519_public,
handshake_slice handshake_slice
); );
// Key exchange occurred, seq_num is set to 0 // Key exchange occurred, seq_num is set to 0

View File

@ -59,14 +59,14 @@ impl<'a> TlsRepr<'a> {
} }
} }
pub(crate) fn client_hello(mut self, secret: &EphemeralSecret, random: [u8; 32], session_id: [u8; 32]) -> Self { pub(crate) fn client_hello(mut self, p256_secret: &EphemeralSecret, x25519_secret: &x25519_dalek::EphemeralSecret, random: [u8; 32], session_id: [u8; 32]) -> Self {
self.content_type = TlsContentType::Handshake; self.content_type = TlsContentType::Handshake;
self.version = TlsVersion::Tls10; self.version = TlsVersion::Tls10;
let handshake_repr = { let handshake_repr = {
let mut repr = HandshakeRepr::new(); let mut repr = HandshakeRepr::new();
repr.msg_type = HandshakeType::ClientHello; repr.msg_type = HandshakeType::ClientHello;
repr.handshake_data = HandshakeData::ClientHello({ repr.handshake_data = HandshakeData::ClientHello({
ClientHello::new(secret, random, session_id) ClientHello::new(p256_secret, x25519_secret, random, session_id)
}); });
repr.length = repr.handshake_data.get_length().try_into().unwrap(); repr.length = repr.handshake_data.get_length().try_into().unwrap();
repr repr
@ -252,7 +252,7 @@ impl<'a> HandshakeData<'a> {
} }
impl<'a> ClientHello<'a> { impl<'a> ClientHello<'a> {
pub(self) fn new(secret: &EphemeralSecret, random: [u8; 32], session_id: [u8; 32]) -> Self { pub(self) fn new(p256_secret: &EphemeralSecret, x25519_secret: &x25519_dalek::EphemeralSecret, random: [u8; 32], session_id: [u8; 32]) -> Self {
let mut client_hello = ClientHello { let mut client_hello = ClientHello {
version: TlsVersion::Tls12, version: TlsVersion::Tls12,
random, random,
@ -274,7 +274,7 @@ impl<'a> ClientHello<'a> {
client_hello.add_ch_supported_versions() client_hello.add_ch_supported_versions()
.add_sig_algs() .add_sig_algs()
.add_client_groups_with_key_shares(secret) .add_client_groups_with_key_shares(p256_secret, x25519_secret)
.finalise() .finalise()
} }
@ -354,9 +354,10 @@ impl<'a> ClientHello<'a> {
self self
} }
pub(crate) fn add_client_groups_with_key_shares(mut self, ecdh_secret: &EphemeralSecret) -> Self { pub(crate) fn add_client_groups_with_key_shares(mut self, ecdh_secret: &EphemeralSecret, x25519_secret: &x25519_dalek::EphemeralSecret) -> Self {
// List out all supported groups // List out all supported groups
let mut list = Vec::new(); let mut list = Vec::new();
list.push(NamedGroup::x25519);
list.push(NamedGroup::secp256r1); list.push(NamedGroup::secp256r1);
let length = list.len()*2; let length = list.len()*2;
@ -384,6 +385,19 @@ impl<'a> ClientHello<'a> {
key_exchange key_exchange
} }
}, },
NamedGroup::x25519 => {
let x25519_public = x25519_dalek::PublicKey::from(x25519_secret);
key_exchange.extend_from_slice(x25519_public.as_bytes());
let key_exchange_length = key_exchange.len();
KeyShareEntry {
group: *named_group,
length: key_exchange_length.try_into().unwrap(),
key_exchange
}
}
// TODO: Implement keygen for other named groups // TODO: Implement keygen for other named groups
_ => todo!(), _ => todo!(),
}; };