Open eltorio opened 7 months ago
this is my key encrypting et decrypting in a test program
use hbb_common::{
bytes::Bytes,
sodiumoxide::{
crypto::{box_, secretbox},
hex,
},
};
use std::error::Error;
pub fn create_symmetric_key_msg(their_pk_b: [u8; 32]) -> ([u8; 32], [u8; 32], Bytes, secretbox::Key) {
let their_pk_b = box_::PublicKey(their_pk_b);
let (our_pk_b, our_sk_b) = box_::gen_keypair();
let key = secretbox::gen_key();
let nonce = box_::Nonce([0u8; box_::NONCEBYTES]);
let sealed_key = box_::seal(&key.0, &nonce, &their_pk_b, &our_sk_b);
(our_pk_b.0, our_sk_b.0, sealed_key.into(), key)
}
pub fn get_symetric_key_from_msg(
our_sk_b: [u8; 32],
their_pk_b: [u8; 32],
sealed_value: &[u8; 48],
) -> [u8; 32] {
let their_pk_b = box_::PublicKey(their_pk_b);
let nonce = box_::Nonce([0u8; box_::NONCEBYTES]);
let sk = box_::SecretKey(our_sk_b);
let key = box_::open(sealed_value, &nonce, &their_pk_b, &sk);
match key {
Ok(key) => {
let mut key_array = [0u8; 32];
key_array.copy_from_slice(&key);
key_array
}
Err(e) => panic!("Error while opening the seal key{:?}", e),
}
}
fn main() -> Result<(), Box<dyn Error>> {
let (theirpk, theirsk) = box_::gen_keypair();
println!("theirpk {:?}", hex::encode(&theirpk.0));
println!("theirsk {:?}", hex::encode(&theirsk.0));
let (ourpk,oursk, sealed_value, key) = create_symmetric_key_msg(theirpk.0);
println!("ourpk {:?}", hex::encode(&ourpk));
println!("oursk {:?}", hex::encode(&oursk));
println!(
"symmetric_value {:?} [u8;{:?}]",
hex::encode(&sealed_value),
&sealed_value.len()
);
println!("key {:?} [u8;{:?}]", hex::encode(key.0), key.0.len());
let vec: Vec<u8> = sealed_value.to_vec();
let sealed_value_48: [u8; 48] = vec.try_into().unwrap();
let clear = get_symetric_key_from_msg(oursk, theirpk.0, &sealed_value_48);
println!("clear {:?} [u8;{:?}]", hex::encode(&clear),clear.len());
Ok(())
}
this is my server side handshake Phase 1, Server to client after nat test answer
async fn key_exchange_phase1(
&mut self,
key: &str,
addr: SocketAddr,
connection: &mut FramedStream,
) {
let mut msg_out = RendezvousMessage::new();
let (_, sk) = Self::get_server_sk(&key);
match sk {
Some(sk) => {
let our_pk_b = self.our_pk_b.clone();
let sm = sign::sign(&our_pk_b.0, &sk);
let bytes_sm = Bytes::from(sm);
msg_out.set_key_exchange(KeyExchange {
keys: vec![bytes_sm],
..Default::default()
});
log::debug!(
"KeyExchange {:?} -> bytes: {:?}",
addr,
hex::encode(Bytes::from(msg_out.write_to_bytes().unwrap()))
);
//TODO handle return
let _ = connection.send(&msg_out).await;
}
None => {}
}
}
Phase1b: client generates a symetric key and use hbb_common::tcp::FramedStream::set_key([u8; 32]) Phase 2: decrypt the symetric key received from the client
async fn key_exchange_phase2(
&mut self,
addr: SocketAddr,
connection: &mut FramedStream,
bytes: &BytesMut,
) {
if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(bytes) {
match msg_in.union {
Some(rendezvous_message::Union::KeyExchange(ex)) => {
log::debug!("KeyExchange {:?} <- bytes: {:?}", addr, hex::encode(&bytes));
if ex.keys.len() != 2 {
log::error!("Handshake failed: invalid phase 2 key exchange message");
return;
}
log::debug!("KeyExchange their_pk: {:?}", hex::encode(&ex.keys[0]));
log::debug!("KeyExchange box: {:?}", hex::encode(&ex.keys[1]));
let their_pk: [u8; 32] = ex.keys[0].to_vec().try_into().unwrap();
let cryptobox: [u8; 48] = ex.keys[1].to_vec().try_into().unwrap();
let symetric_key =
get_symetric_key_from_msg(self.our_sk_b.0, their_pk, &cryptobox);
log::debug!("KeyExchange symetric key: {:?}", hex::encode(&symetric_key));
let key = secretbox::Key::from_slice(&symetric_key);
match key {
Some(key) => {
connection.set_key(key);
log::debug!("KeyExchange symetric key set");
return;
}
None => {
log::error!("KeyExchange symetric key NOT set");
return;
}
}
}
_ => {}
}
}
}
Voilà !
The complete proof of concept is divided into two parts:
Please note that this is purely a proof of concept. It consists of a significant amount of copied and pasted code, and some parts are currently disabled. Despite these limitations, the client successfully connects, a symmetric key exchange occurs, the TCP connection is encrypted, and the public key registers.
Moving forward, I believe that Rustdesk will likely share its code rather than creating a separate TCP fork.
Rustdesk made a wonderfull opensource code…
The code isn't fully optimized, as that isn't my primary goal. My ultimate aim is to demonstrate the viability of hosting hbbs in my Kubernetes cluster, using HAProxy as the ingress controller.
Like many others, I'd like to host hbbs behind HAProxy, but this requires several modifications:
1 - RustDesk needs to be compiled with TEST_TCP
.
2 - The TCP handshake must be implemented in the open-source RustDesk server (the pro version already has this feature).
3 - The real peer address must be detected. I've tested the HAProxy v2 protocol here.
@rustdesk, are there any plans to include the TCP server in the open-source server?
Another, and potentially better, option is to use WebSocket encapsulation, similar to the web client. However, this would require additions to both the client and the server. The server would need to handle RegisterPeer and RegisterPK. WebSocket offers several advantages:
@rustdesk, are there any plans to develop WebSocket support?
Thanks @eltorio for sharing your code how do you enable HAProxy v2 protocol ?
Thanks @eltorio for sharing your code. How do you enable the HAProxy v2 protocol?
Firstly, my code is not production-ready! It lacks testing and cleaning. If @rustdesk has no plans to publish their code, I'll consider writing clean code. However, to be honest, I would prefer the WebSocket solution.
To answer your question, you simply need to have a backend like this one:
backend hbbs_hbbs-route
mode tcp
default-server check send-proxy-v2
server SRV_1 172.28.5.131:21116 enabled
eltorio published a PR with his code
Describe the solution you'd like Please publish the server side related to client [ start_tcp(server: ServerPtr, host: String)] (https://github.com/rustdesk/rustdesk/blob/0d75f71d16b9712b959423f6ae8fe5de7502e8f2/src/rendezvous_mediator.rs#L334)
Describe alternatives you've considered Forking the rustdesk-server for allowing tcp only handshake.
RustDesk already have a option for allowing a tcp only handshake while it is compiled with
TEST_TCP
.It works perfectly with hbbs / hbbr from rustdesk-server-pro docker image but not with oss server.
After updating libs/hbb_common on rustdesk_server we can refactor the
handle_tcp
Option<Encrypt>
as a proof of concept I modified the RustDesk client for adding an option to choose between UDP and TCP mode.
Next I quickly modified an oss rustdesk-server for working with this tcp enabled RustDesk client. I added this when the tcp connection
it sends a correct message to the client, the client answers also a KeyExchange message but with with two keys generated with create_symmetric_key_msg(their_pk_b: [u8; 32]) . Basically it creates a symetric key with sodiumoxide, encrypt it with the server ed25519 public key a null nonce and our ed25519 private key.
The server receive the KeyExchange, because it has 2 keys, it decrypts the sodiumoxide secret box with client pk and server sk , get the symetric key and issue
stream.set_key(pk);
. Now the 21116 tcp port must be secured and can handle RegisterPeer…The protocol can be hardened by using a random nonce and transmit it back to the server.
Additional context Add any other context about the feature request here.
Notes
id_ed25519
file