ecdhe: add x25519
This commit is contained in:
parent
5638a660c2
commit
3bd54d682a
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
51
src/tls.rs
51
src/tls.rs
|
@ -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
|
||||
|
|
|
@ -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!(),
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue