Skip to content

Commit

Permalink
migrate to codec
Browse files Browse the repository at this point in the history
  • Loading branch information
hacker-volodya committed Mar 22, 2024
1 parent 9d3255f commit 55b18c8
Show file tree
Hide file tree
Showing 14 changed files with 298 additions and 338 deletions.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ log = "0.4.14"
rand_core = "0.6.3"
x25519-dalek = { version = "2.0.0-pre.1", optional = true }
curve25519-dalek = { version = "4.0.0-pre.2", optional = true }
tokio = { version = "1.36", features = ["net", "io-util"]}
tokio = { version = "1.36", features = ["net", "io-util"] }
tokio-util = { version = "0.7.10", features = ["codec"] }
thiserror = "1"
rand = "0.8.5"
futures = "0.3.30"
pin-project = "1"
hex = "0.4.3"

[dev-dependencies]
hex = "0.4.3"
Expand Down
14 changes: 7 additions & 7 deletions examples/echo_client.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use adnl::{AdnlPeer, AdnlRawPublicKey};
use futures::{SinkExt, StreamExt};
use std::{env, error::Error};

#[tokio::main]
Expand All @@ -9,20 +10,19 @@ async fn main() -> Result<(), Box<dyn Error>> {

let public_key_hex = env::args()
.nth(2)
.unwrap_or_else(|| "b7d8e88f4033eff806e2f5dff3c785be7dd038c923146e2d9fe80e4fe3cb8805".to_string());
.unwrap_or_else(|| "691a14528fb2911839649c489cb4cbec1f4aa126c244c0ea2ac294eb568a7037".to_string());

let remote_public = AdnlRawPublicKey::try_from(&*hex::decode(public_key_hex)?)?;

// act as a client: connect to ADNL server and perform handshake
let mut client = AdnlPeer::connect(&remote_public, addr).await?;
let mut client = AdnlPeer::connect(&remote_public, addr).await.expect("adnl connect");

// send over ADNL
client.send(&mut "hello".as_bytes().to_vec()).await?;
client.send("hello".as_bytes().into()).await.expect("send");

// receive result into vector
let mut result = Vec::<u8>::new();
client.receive(&mut result).await?;
// receive result
let result = client.next().await.expect("packet must be received")?;

println!("received: {}", String::from_utf8(result).unwrap());
println!("received: {}", String::from_utf8(result.to_vec()).unwrap());
Ok(())
}
18 changes: 5 additions & 13 deletions examples/echo_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::{env, error::Error};

use adnl::{AdnlPeer, AdnlPrivateKey, AdnlPublicKey};
use futures::{SinkExt, StreamExt};
use tokio::net::TcpListener;
use x25519_dalek::StaticSecret;

Expand All @@ -16,7 +17,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
.unwrap_or_else(|| "127.0.0.1:8080".to_string());

// ADNL: get private key from environment variable KEY or use default insecure one
let private_key_hex = env::var("KEY").unwrap_or_else(|_| "69734189c0348245a70eb5335e12bfd75dd4cffc42baf32773e8f994ff5cf7c2".to_string());
let private_key_hex = env::var("KEY").unwrap_or_else(|_| "f0971651aec4bb0d65ec3861c597687fda9c1e7d2ee8a93acb9a131aa9f3aee7".to_string());
let private_key_bytes: [u8; 32] = hex::decode(private_key_hex)?.try_into().unwrap();
let private_key = StaticSecret::from(private_key_bytes);

Expand All @@ -27,7 +28,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
println!("Listening on: {}", addr);

// ADNL: print public key and adnl address associated with given private key
println!("Public key is: {}", hex::encode(private_key.public().as_bytes()));
println!("Public key is: {}", hex::encode(private_key.public().edwards_repr()));
println!("Address is: {}", hex::encode(private_key.public().address().as_bytes()));

loop {
Expand All @@ -47,18 +48,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
// ADNL: handle handshake
let mut adnl_server = AdnlPeer::handle_handshake(socket, &private_key).await.expect("handshake failed");

let mut buf = vec![0; 1024];

// In a loop, read data from the socket and write the data back.
loop {
let n = adnl_server.receive(&mut buf)
.await
.expect("failed to read data from socket");

adnl_server
.send(&mut buf[..n])
.await
.expect("failed to write data to socket");
while let Some(Ok(packet)) = adnl_server.next().await {
let _ = adnl_server.send(packet).await;
}
});
}
Expand Down
15 changes: 7 additions & 8 deletions examples/time.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use adnl::{AdnlPeer, AdnlRawPublicKey};
use futures::{SinkExt, StreamExt};
use std::{error::Error, net::SocketAddrV4};

#[tokio::main]
Expand All @@ -9,18 +10,16 @@ async fn main() -> Result<(), Box<dyn Error>> {
let ls_ip = "65.21.74.140";
let ls_port = 46427;
// act as a client: connect to ADNL server and perform handshake
let mut client =
AdnlPeer::connect(&remote_public, SocketAddrV4::new(ls_ip.parse()?, ls_port)).await?;
let mut client = AdnlPeer::connect(&remote_public, SocketAddrV4::new(ls_ip.parse()?, ls_port)).await?;

// already serialized TL with gettime query
let mut query = hex::decode("7af98bb435263e6c95d6fecb497dfd0aa5f031e7d412986b5ce720496db512052e8f2d100cdf068c7904345aad16000000000000")?;
let query = hex::decode("7af98bb435263e6c95d6fecb497dfd0aa5f031e7d412986b5ce720496db512052e8f2d100cdf068c7904345aad16000000000000")?;

// send over ADNL, use random nonce
client.send(&mut query).await?;
// send over ADNL
client.send(query.into()).await?;

// receive result into vector, use 8192 bytes buffer
let mut result = Vec::<u8>::new();
client.receive(&mut result).await?;
// receive result
let result = client.next().await.ok_or_else(|| "no result")??;

// get time from serialized TL answer
println!(
Expand Down
37 changes: 24 additions & 13 deletions src/helper_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,24 @@ pub trait CryptoRandom: rand_core::RngCore + rand_core::CryptoRng {}
impl<T> CryptoRandom for T where T: rand_core::RngCore + rand_core::CryptoRng {}

pub trait AdnlPublicKey {
/// Derives address from a public key
fn address(&self) -> AdnlAddress {
let mut hasher = Sha256::new();
hasher.update([0xc6, 0xb4, 0x13, 0x48]); // type id - always ed25519
hasher.update(self.to_bytes());
hasher.update(self.edwards_repr());
AdnlAddress(hasher.finalize().into())
}

fn to_bytes(&self) -> [u8; 32];
/// Gets ed25519 representation of a public key
fn edwards_repr(&self) -> [u8; 32];
}

/// Public key can be provided using raw slice
/// Public key can be provided in a ed25519 form using raw slice
#[derive(Clone)]
pub struct AdnlRawPublicKey([u8; 32]);

impl AdnlPublicKey for AdnlRawPublicKey {
fn to_bytes(&self) -> [u8; 32] {
fn edwards_repr(&self) -> [u8; 32] {
self.0
}
}
Expand Down Expand Up @@ -104,6 +106,17 @@ impl From<[u8; 160]> for AdnlAesParams {
}

impl AdnlAesParams {
/// Swap receiver and transciever keys
pub fn swap(self) -> Self {
Self {
rx_key: self.tx_key,
tx_key: self.rx_key,
rx_nonce: self.tx_nonce,
tx_nonce: self.rx_nonce,
padding: self.padding,
}
}

pub fn rx_key(&self) -> &[u8; 32] {
&self.rx_key
}
Expand Down Expand Up @@ -188,18 +201,16 @@ impl AdnlSecret {
/// Common error type
#[derive(Debug, Error)]
pub enum AdnlError {
#[error("Read error")]
ReadError(Error),
#[error("Write error")]
WriteError(Error),
#[error("Consume error")]
ConsumeError(Error),
#[error("IO error")]
IoError(#[from] Error),
#[error("Integrity error")]
IntegrityError,
#[error("TooShortPacket error")]
#[error("Too short packet (32 bytes min)")]
TooShortPacket,
#[error("Too long packet (4 MiB max)")]
TooLongPacket,
#[error("Receiver ADNL address mismatch")]
UnknownAddr(AdnlAddress),
#[error(transparent)]
OtherError(#[from] Error),
#[error("End of stream")]
EndOfStream,
}
4 changes: 2 additions & 2 deletions src/integrations/dalek.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use curve25519_dalek::montgomery::MontgomeryPoint;
use x25519_dalek::{PublicKey, StaticSecret};

impl AdnlPublicKey for PublicKey {
fn to_bytes(&self) -> [u8; 32] {
fn edwards_repr(&self) -> [u8; 32] {
MontgomeryPoint(self.to_bytes())
.to_edwards(0)
.unwrap()
Expand All @@ -18,7 +18,7 @@ impl AdnlPublicKey for PublicKey {

fn edwards_to_montgomery<P: AdnlPublicKey>(public_key: &P) -> PublicKey {
PublicKey::from(
CompressedEdwardsY::from_slice(&public_key.to_bytes())
CompressedEdwardsY::from_slice(&public_key.edwards_repr())
.decompress()
.unwrap()
.to_montgomery()
Expand Down
3 changes: 1 addition & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ pub use helper_types::{
AdnlAddress, AdnlAesParams, AdnlError, AdnlPrivateKey, AdnlPublicKey, AdnlSecret, AdnlRawPublicKey,
};
pub use primitives::handshake::AdnlHandshake;
pub use primitives::receive::AdnlReceiver;
pub use primitives::send::AdnlSender;
pub use primitives::codec::AdnlCodec;
pub use wrappers::builder::AdnlBuilder;
pub use wrappers::peer::AdnlPeer;

Expand Down
102 changes: 102 additions & 0 deletions src/primitives/codec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use aes::cipher::{KeyIvInit, StreamCipher};
use sha2::{Digest, Sha256};
use tokio_util::{bytes::{Buf, Bytes, BytesMut}, codec::{Decoder, Encoder}};

use crate::{AdnlAesParams, AdnlError};

use super::AdnlAes;

pub struct AdnlCodec {
aes_rx: AdnlAes,
aes_tx: AdnlAes,
last_readed_length: Option<usize>,
}

impl AdnlCodec {
pub fn new(aes_params: &AdnlAesParams) -> Self {
Self {
aes_rx: AdnlAes::new(aes_params.rx_key().into(), aes_params.rx_nonce().into()),
aes_tx: AdnlAes::new(aes_params.tx_key().into(), aes_params.tx_nonce().into()),
last_readed_length: None,
}
}
}

impl Decoder for AdnlCodec {
type Item = Bytes;

type Error = AdnlError;

fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
let length = if let Some(length) = self.last_readed_length {
length
} else {
if src.len() < 4 {
return Ok(None)
}
self.aes_rx.apply_keystream(&mut src[..4]);
let mut length_bytes = [0u8; 4];
length_bytes.copy_from_slice(&src[..4]);
let length = u32::from_le_bytes(length_bytes) as usize;
if length < 64 {
return Err(AdnlError::TooShortPacket);
}
if length > (1 << 24) {
return Err(AdnlError::TooLongPacket);
}
src.advance(4);
self.last_readed_length = Some(length);
length
};

// not enough bytes, need to wait for more data
if src.len() < length {
if src.capacity() < length {
src.reserve(length - src.capacity());
}
return Ok(None)
}

self.last_readed_length = None;

// decode packet
self.aes_rx.apply_keystream(&mut src[..length]);
let given_hash = &src[length-32..length];

// integrity check
let mut hasher = Sha256::new();
hasher.update(&src[..length-32]);
if given_hash != hasher.finalize().as_slice() {
return Err(AdnlError::IntegrityError)
}

// copy and return buffer
let result = Bytes::copy_from_slice(&src[32..length-32]);
src.advance(length);
Ok(Some(result))
}
}

impl Encoder<Bytes> for AdnlCodec {
type Error = AdnlError;

fn encode(&mut self, buffer: Bytes, dst: &mut BytesMut) -> Result<(), Self::Error> {
if buffer.len() > ((1 << 24) - 64) {
return Err(AdnlError::TooLongPacket);
}
let length = ((buffer.len() + 64) as u32).to_le_bytes();
let nonce = rand::random::<[u8; 32]>();
let mut hash = Sha256::new();
hash.update(&nonce);
hash.update(&buffer);
let hash = hash.finalize();
dst.reserve(buffer.len() + 68);
dst.extend_from_slice(&length);
dst.extend_from_slice(&nonce);
dst.extend_from_slice(&buffer);
dst.extend_from_slice(&hash);
let start_offset = dst.len() - buffer.len() - 68;
self.aes_tx.apply_keystream(&mut dst[start_offset..]);
Ok(())
}
}
10 changes: 8 additions & 2 deletions src/primitives/handshake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use ctr::cipher::StreamCipher;
use sha2::{Digest, Sha256};
use tokio::io::{AsyncReadExt, AsyncWriteExt};

use super::codec::AdnlCodec;

/// Handshake packet, must be sent from client to server prior to any datagrams
pub struct AdnlHandshake<P: AdnlPublicKey> {
receiver: AdnlAddress,
Expand Down Expand Up @@ -55,12 +57,16 @@ impl<P: AdnlPublicKey> AdnlHandshake<P> {

let mut packet = [0u8; 256];
packet[..32].copy_from_slice(self.receiver.as_bytes());
packet[32..64].copy_from_slice(&self.sender.to_bytes());
packet[32..64].copy_from_slice(&self.sender.edwards_repr());
packet[64..96].copy_from_slice(&hash);
packet[96..256].copy_from_slice(&raw_params);
packet
}

pub fn make_codec(&self) -> AdnlCodec {
AdnlCodec::new(&self.aes_params)
}

/// Send handshake over the given transport, build [`AdnlClient`] on top of it
pub async fn perform_handshake<T: AsyncReadExt + AsyncWriteExt + Unpin>(
&self,
Expand Down Expand Up @@ -111,7 +117,7 @@ impl AdnlHandshake<AdnlRawPublicKey> {
Ok(Self {
receiver,
sender,
aes_params: AdnlAesParams::from(raw_params),
aes_params: AdnlAesParams::from(raw_params).swap(),
secret,
})
}
Expand Down
3 changes: 1 addition & 2 deletions src/primitives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@ use ctr::Ctr128BE;

pub type AdnlAes = Ctr128BE<Aes256>;

pub mod codec;
pub mod handshake;
pub mod receive;
pub mod send;
Loading

0 comments on commit 55b18c8

Please sign in to comment.