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
features = [ "u64_backend" ]
[dependencies.x25519-dalek]
version = "1.1.0"
default-features = false
features = [ "u64_backend" ]
# Fetch from master, for "no_std" + "alloc" combination
[dependencies.rsa]
git = "https://github.com/RustCrypto/RSA.git"

View File

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

View File

@ -126,12 +126,13 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
// Prepare field that is randomised,
// Supply it to the TLS repr builder.
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 session_id: [u8; 32] = [0; 32];
self.rng.fill_bytes(&mut random);
self.rng.fill_bytes(&mut session_id);
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
let mut array = [0; 512];
@ -145,6 +146,7 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
// Update TLS session
self.session.borrow_mut().client_update_for_ch(
ecdh_secret,
x25519_secret,
session_id,
&slice[5..]
);
@ -313,7 +315,6 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
if repr.is_server_hello() {
// Check SH content:
// random: Cannot represent HelloRequestRetry
// (TODO: Support other key shares, e.g. X25519)
// session_id_echo: should be same as the one sent by client
// cipher_suite: Store
// (TODO: Check if such suite was offered)
@ -321,11 +322,11 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
//
// Check extensions:
// supported_version: Must be TLS 1.3
// key_share: Store key, must be in secp256r1
// (TODO: Support other key shares ^)
// key_share: Store key, must be in secp256r1 or x25519
// "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;
// Process the handshake data within ServerHello
@ -377,22 +378,25 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
server_share
}
) = &extension.extension_data {
// TODO: Use legitimate checking to ensure the chosen
// group is indeed acceptable, when allowing more (EC)DHE
// key sharing
if server_share.group != NamedGroup::secp256r1 {
// Abort for wrong key sharing
todo!()
match server_share.group {
NamedGroup::secp256r1 => {
p256_public.replace(
EncodedPoint::from_untagged_bytes(
GenericArray::from_slice(&server_share.key_exchange[1..])
)
);
},
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!()
}
// Check that both selected_cipher and server_public were received
if selected_cipher.is_none() || server_public.is_none() {
// Check that both selected_cipher and (p256_public XNOR x25519_public) were received
if selected_cipher.is_none() || (p256_public.is_none() && x25519_public.is_none()) {
// Abort communication
todo!()
}
@ -412,7 +416,8 @@ impl<R: 'static + RngCore + CryptoRng> TlsSocket<R> {
let mut session = self.session.borrow_mut();
session.client_update_for_sh(
selected_cipher.unwrap(),
server_public.unwrap(),
p256_public,
x25519_public,
handshake_slice
);
// 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.version = TlsVersion::Tls10;
let handshake_repr = {
let mut repr = HandshakeRepr::new();
repr.msg_type = HandshakeType::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
@ -252,7 +252,7 @@ impl<'a> HandshakeData<'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 {
version: TlsVersion::Tls12,
random,
@ -274,7 +274,7 @@ impl<'a> ClientHello<'a> {
client_hello.add_ch_supported_versions()
.add_sig_algs()
.add_client_groups_with_key_shares(secret)
.add_client_groups_with_key_shares(p256_secret, x25519_secret)
.finalise()
}
@ -354,9 +354,10 @@ impl<'a> ClientHello<'a> {
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
let mut list = Vec::new();
list.push(NamedGroup::x25519);
list.push(NamedGroup::secp256r1);
let length = list.len()*2;
@ -384,6 +385,19 @@ impl<'a> ClientHello<'a> {
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!(),
};