diff --git a/Cargo.toml b/Cargo.toml index a89e448..0bc64ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,25 +14,25 @@ sha2 = "0.10.2" ctr = "0.9.1" aes = "0.8.1" log = "0.4.14" -ciborium-io = "0.2.0" 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"]} +thiserror = "1" +rand = "0.8.5" [dev-dependencies] hex = "0.4.3" x25519-dalek = "= 2.0.0-pre.1" curve25519-dalek = "= 4.0.0-pre.2" -rand = "0.8.5" +tokio = { version = "1.36", features = ["rt-multi-thread", "macros"]} base64 = "0.13.0" -ciborium-io = { version = "0.2.0", features = ["alloc"]} +anyhow = "1" [features] -default = [] +default = ["dalek"] dalek = ["x25519-dalek", "curve25519-dalek"] -alloc = ["ciborium-io/alloc"] -std = ["ciborium-io/std"] [[example]] name = "time" -required-features = ["std", "dalek"] \ No newline at end of file +required-features = ["dalek"] diff --git a/README.md b/README.md index 47ae9e5..35d58c7 100644 --- a/README.md +++ b/README.md @@ -2,69 +2,45 @@ Minimal ADNL implementation in Rust (client-server only, without p2p for now). Specification of ADNL is available [here](https://github.com/tonstack/ton-docs/blob/main/ADNL/README.md). -| Feature | Status | -| ------------- | -------------------------------- | -| ADNL Client | ✅ Implemented | -| ADNL Server | ❌ Not implemented | -| ADNL P2P | ❌ Not implemented | -| async | ❌ Not implemented | -| ed25519 libs | curve25519_dalek + x25519_dalek | +| Feature | Status | +|--------------|---------------------------------| +| ADNL Client | ✅ Implemented | +| ADNL Server | ❌ Not implemented | +| ADNL P2P | ❌ Not implemented | +| async | ✅ Implemented | +| ed25519 libs | curve25519_dalek + x25519_dalek | ## Quickstart -Run this example: `cargo run --example time --features "std dalek"` +Run this example: `cargo run --example time` ```rust -use adnl::{AdnlBuilder, AdnlClient}; -use std::error::Error; -use std::net::{SocketAddrV4, TcpStream}; -use x25519_dalek::StaticSecret; +use adnl::AdnlClient; +use anyhow::{anyhow, Context, Result}; +use std::net::SocketAddrV4; -pub fn connect( - ls_public: &str, - ls_ip: &str, - ls_port: u16, -) -> Result, Box> { - // decode liteserver public key - let remote_public: [u8; 32] = base64::decode(ls_public)? - .try_into() - .map_err(|_| "bad public key length")?; - - // generate private key - let local_secret = StaticSecret::new(rand::rngs::OsRng); - - // use TcpStream as a transport for our ADNL connection - let transport = TcpStream::connect(SocketAddrV4::new(ls_ip.parse()?, ls_port))?; - // build handshake using random session keys, encrypt it with ECDH(local_secret, remote_public) - // then perform handshake over our TcpStream - let client = AdnlBuilder::with_random_aes_params(&mut rand::rngs::OsRng) - .perform_ecdh(local_secret, remote_public) - .perform_handshake(transport) - .map_err(|e| format!("{:?}", e))?; - Ok(client) -} +#[tokio::main] +async fn main() -> Result<()> { + // decode liteserver public key + let remote_public: [u8; 32] = base64::decode("JhXt7H1dZTgxQTIyGiYV4f9VUARuDxFl/1kVBjLSMB8=") + .context("Error decode base64")? + .try_into().map_err(|_| anyhow!("Bad public key length"))?; -fn main() -> Result<(), Box> { + let ls_ip = "65.21.74.140"; + let ls_port = 46427; // create AdnlClient - let mut client = connect( - "JhXt7H1dZTgxQTIyGiYV4f9VUARuDxFl/1kVBjLSMB8=", - "65.21.74.140", - 46427, - )?; + let mut client = + AdnlClient::connect(remote_public, SocketAddrV4::new(ls_ip.parse()?, ls_port)).await?; // already serialized TL with gettime query let mut query = hex::decode("7af98bb435263e6c95d6fecb497dfd0aa5f031e7d412986b5ce720496db512052e8f2d100cdf068c7904345aad16000000000000")?; // send over ADNL, use random nonce - client - .send(&mut query, &mut rand::random()) - .map_err(|e| format!("{:?}", e))?; + client.send(&mut query).await?; // receive result into vector, use 8192 bytes buffer let mut result = Vec::::new(); - client - .receive::<_, 8192>(&mut result) - .map_err(|e| format!("{:?}", e))?; + client.receive(&mut result).await?; // get time from serialized TL answer println!( diff --git a/examples/time.rs b/examples/time.rs index 2296d81..bc044cf 100644 --- a/examples/time.rs +++ b/examples/time.rs @@ -1,54 +1,30 @@ -use adnl::{AdnlBuilder, AdnlClient}; -use std::error::Error; -use std::net::{SocketAddrV4, TcpStream}; -use x25519_dalek::StaticSecret; +use adnl::AdnlClient; +use anyhow::{anyhow, Context, Result}; +use std::net::SocketAddrV4; -pub fn connect( - ls_public: &str, - ls_ip: &str, - ls_port: u16, -) -> Result, Box> { +#[tokio::main] +async fn main() -> Result<()> { // decode liteserver public key - let remote_public: [u8; 32] = base64::decode(ls_public)? + let remote_public: [u8; 32] = base64::decode("JhXt7H1dZTgxQTIyGiYV4f9VUARuDxFl/1kVBjLSMB8=") + .context("Error decode base64")? .try_into() - .map_err(|_| "bad public key length")?; + .map_err(|_| anyhow!("Bad public key length"))?; - // generate private key - let local_secret = StaticSecret::new(rand::rngs::OsRng); - - // use TcpStream as a transport for our ADNL connection - let transport = TcpStream::connect(SocketAddrV4::new(ls_ip.parse()?, ls_port))?; - - // build handshake using random session keys, encrypt it with ECDH(local_secret, remote_public) - // then perform handshake over our TcpStream - let client = AdnlBuilder::with_random_aes_params(&mut rand::rngs::OsRng) - .perform_ecdh(local_secret, remote_public) - .perform_handshake(transport) - .map_err(|e| format!("{:?}", e))?; - Ok(client) -} - -fn main() -> Result<(), Box> { + let ls_ip = "65.21.74.140"; + let ls_port = 46427; // create AdnlClient - let mut client = connect( - "JhXt7H1dZTgxQTIyGiYV4f9VUARuDxFl/1kVBjLSMB8=", - "65.21.74.140", - 46427, - )?; + let mut client = + AdnlClient::connect(remote_public, SocketAddrV4::new(ls_ip.parse()?, ls_port)).await?; // already serialized TL with gettime query let mut query = hex::decode("7af98bb435263e6c95d6fecb497dfd0aa5f031e7d412986b5ce720496db512052e8f2d100cdf068c7904345aad16000000000000")?; // send over ADNL, use random nonce - client - .send(&mut query, &mut rand::random()) - .map_err(|e| format!("{:?}", e))?; + client.send(&mut query).await?; // receive result into vector, use 8192 bytes buffer let mut result = Vec::::new(); - client - .receive::<_, 8192>(&mut result) - .map_err(|e| format!("{:?}", e))?; + client.receive(&mut result).await?; // get time from serialized TL answer println!( diff --git a/src/helper_types.rs b/src/helper_types.rs index bda0524..b025bce 100644 --- a/src/helper_types.rs +++ b/src/helper_types.rs @@ -1,5 +1,7 @@ -use ciborium_io::{Read, Write}; use sha2::{Digest, Sha256}; +use std::io::Error; +use std::net::AddrParseError; +use thiserror::Error; pub trait CryptoRandom: rand_core::RngCore + rand_core::CryptoRng {} @@ -10,7 +12,7 @@ pub trait AdnlPublicKey { let mut hasher = Sha256::new(); hasher.update([0xc6, 0xb4, 0x13, 0x48]); // type id - always ed25519 hasher.update(self.to_bytes()); - AdnlAddress(hasher.finalize().try_into().unwrap()) + AdnlAddress(hasher.finalize().into()) } fn to_bytes(&self) -> [u8; 32]; @@ -144,36 +146,21 @@ impl AdnlSecret { } } -/// Empty transport to use when there is nothing to read or write -#[derive(Debug)] -pub struct Empty; - -impl Write for Empty { - type Error = (); - - fn write_all(&mut self, _data: &[u8]) -> Result<(), Self::Error> { - Ok(()) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } -} - -impl Read for Empty { - type Error = (); - - fn read_exact(&mut self, _data: &mut [u8]) -> Result<(), Self::Error> { - Ok(()) - } -} - /// Common error type -#[derive(Debug)] -pub enum AdnlError { - ReadError(R::Error), - WriteError(W::Error), - ConsumeError(C::Error), +#[derive(Debug, Error)] +pub enum AdnlError { + #[error("Read error")] + ReadError(Error), + #[error("Write error")] + WriteError(Error), + #[error("Consume error")] + ConsumeError(Error), + #[error("Integrity error")] IntegrityError, + #[error("TooShortPacket error")] TooShortPacket, + #[error("Incorrect ip address")] + IncorrectAddr(AddrParseError), + #[error(transparent)] + OtherError(#[from] Error), } diff --git a/src/lib.rs b/src/lib.rs index 88cfd47..9ca6d46 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,5 @@ -#![cfg_attr(not(feature = "std"), no_std)] - pub use helper_types::{ - AdnlAddress, AdnlAesParams, AdnlError, AdnlPrivateKey, AdnlPublicKey, AdnlSecret, Empty, + AdnlAddress, AdnlAesParams, AdnlError, AdnlPrivateKey, AdnlPublicKey, AdnlSecret, }; pub use primitives::handshake::AdnlHandshake; pub use primitives::receive::AdnlReceiver; diff --git a/src/primitives/handshake.rs b/src/primitives/handshake.rs index 56ae215..7696d4c 100644 --- a/src/primitives/handshake.rs +++ b/src/primitives/handshake.rs @@ -1,9 +1,9 @@ use crate::primitives::AdnlAes; -use crate::{AdnlAddress, AdnlAesParams, AdnlClient, AdnlError, AdnlPublicKey, AdnlSecret, Empty}; +use crate::{AdnlAddress, AdnlAesParams, AdnlClient, AdnlError, AdnlPublicKey, AdnlSecret}; use aes::cipher::KeyIvInit; -use ciborium_io::{Read, Write}; use ctr::cipher::StreamCipher; use sha2::{Digest, Sha256}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; /// Handshake packet, must be sent from client to server prior to any datagrams pub struct AdnlHandshake { @@ -44,7 +44,7 @@ impl AdnlHandshake

{ let mut raw_params = self.aes_params.to_bytes(); let mut hasher = Sha256::new(); hasher.update(raw_params); - let hash: [u8; 32] = hasher.finalize().try_into().unwrap(); + let hash: [u8; 32] = hasher.finalize().into(); let mut key = [0u8; 32]; key[..16].copy_from_slice(&self.secret.as_bytes()[..16]); @@ -62,10 +62,10 @@ impl AdnlHandshake

{ } /// Send handshake over the given transport, build [`AdnlClient`] on top of it - pub fn perform_handshake( + pub async fn perform_handshake( &self, transport: T, - ) -> Result, AdnlError> { - AdnlClient::perform_handshake(transport, self) + ) -> Result, AdnlError> { + AdnlClient::perform_handshake(transport, self).await } } diff --git a/src/primitives/receive.rs b/src/primitives/receive.rs index 653468a..a4cf14f 100644 --- a/src/primitives/receive.rs +++ b/src/primitives/receive.rs @@ -1,9 +1,9 @@ use crate::primitives::AdnlAes; -use crate::{AdnlAesParams, AdnlError, Empty}; +use crate::{AdnlAesParams, AdnlError}; use aes::cipher::KeyIvInit; -use ciborium_io::{Read, Write}; use ctr::cipher::StreamCipher; use sha2::{Digest, Sha256}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; /// Low-level incoming datagram processor pub struct AdnlReceiver { @@ -25,17 +25,18 @@ impl AdnlReceiver { /// /// You can adjust `BUFFER` according to your memory requirements. /// Recommended size is 8192 bytes. - pub fn receive( + pub async fn receive( &mut self, transport: &mut R, consumer: &mut C, - ) -> Result<(), AdnlError> { + ) -> Result<(), AdnlError> { // read length let mut length = [0u8; 4]; log::debug!("reading length"); transport .read_exact(&mut length) - .map_err(|e| AdnlError::ReadError(e))?; + .await + .map_err(AdnlError::ReadError)?; self.aes.apply_keystream(&mut length); let length = u32::from_le_bytes(length); log::debug!("length = {}", length); @@ -50,7 +51,8 @@ impl AdnlReceiver { log::debug!("reading nonce"); transport .read_exact(&mut nonce) - .map_err(|e| AdnlError::ReadError(e))?; + .await + .map_err(AdnlError::ReadError)?; self.aes.apply_keystream(&mut nonce); hasher.update(nonce); @@ -66,12 +68,14 @@ impl AdnlReceiver { ); transport .read_exact(&mut buffer) - .map_err(|e| AdnlError::ReadError(e))?; + .await + .map_err(AdnlError::ReadError)?; self.aes.apply_keystream(&mut buffer); hasher.update(buffer); consumer .write_all(&buffer) - .map_err(|e| AdnlError::ConsumeError(e))?; + .await + .map_err(AdnlError::WriteError)?; bytes_to_read -= BUFFER; } @@ -81,12 +85,14 @@ impl AdnlReceiver { let buffer = &mut buffer[..bytes_to_read]; transport .read_exact(buffer) - .map_err(|e| AdnlError::ReadError(e))?; + .await + .map_err(AdnlError::ReadError)?; self.aes.apply_keystream(buffer); hasher.update(&buffer); consumer .write_all(buffer) - .map_err(|e| AdnlError::ConsumeError(e))?; + .await + .map_err(AdnlError::WriteError)?; } } @@ -94,7 +100,8 @@ impl AdnlReceiver { log::debug!("reading hash"); transport .read_exact(&mut given_hash) - .map_err(|e| AdnlError::ReadError(e))?; + .await + .map_err(AdnlError::ReadError)?; self.aes.apply_keystream(&mut given_hash); let real_hash = hasher.finalize(); diff --git a/src/primitives/send.rs b/src/primitives/send.rs index 25d6f9e..41a7e93 100644 --- a/src/primitives/send.rs +++ b/src/primitives/send.rs @@ -1,10 +1,10 @@ use aes::cipher::KeyIvInit; -use ciborium_io::Write; use ctr::cipher::StreamCipher; use sha2::{Digest, Sha256}; +use tokio::io::AsyncWriteExt; use crate::primitives::AdnlAes; -use crate::{AdnlAesParams, AdnlError, Empty}; +use crate::{AdnlAesParams, AdnlError}; /// Low-level outgoing datagram generator pub struct AdnlSender { @@ -26,12 +26,12 @@ impl AdnlSender { /// Send `buffer` over `transport` with `nonce`. Note that `nonce` must be random /// in order to prevent bit-flipping attacks when an attacker knows whole plaintext in datagram. - pub fn send( + pub async fn send( &mut self, transport: &mut W, nonce: &mut [u8; 32], buffer: &mut [u8], - ) -> Result<(), AdnlError> { + ) -> Result<(), AdnlError> { // remember not to send more than 4 GiB in a single packet let mut length = ((buffer.len() + 64) as u32).to_le_bytes(); @@ -39,7 +39,7 @@ impl AdnlSender { let mut hasher = Sha256::new(); hasher.update(*nonce); hasher.update(&*buffer); - let mut hash: [u8; 32] = hasher.finalize().try_into().unwrap(); + let mut hash: [u8; 32] = hasher.finalize().into(); // encrypt packet self.aes.apply_keystream(&mut length); @@ -50,17 +50,21 @@ impl AdnlSender { // write to transport transport .write_all(&length) - .map_err(|e| AdnlError::WriteError(e))?; + .await + .map_err(AdnlError::WriteError)?; transport .write_all(nonce) - .map_err(|e| AdnlError::WriteError(e))?; + .await + .map_err(AdnlError::WriteError)?; transport .write_all(buffer) - .map_err(|e| AdnlError::WriteError(e))?; + .await + .map_err(AdnlError::WriteError)?; transport .write_all(&hash) - .map_err(|e| AdnlError::WriteError(e))?; - transport.flush().map_err(|e| AdnlError::WriteError(e))?; + .await + .map_err(AdnlError::WriteError)?; + transport.flush().await.map_err(AdnlError::WriteError)?; Ok(()) } diff --git a/src/tests.rs b/src/tests.rs index d55ed7a..9474b04 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -62,34 +62,36 @@ fn test_handshake( ); } -#[test] -fn test_send_1() { +#[tokio::test] +async fn test_send_1() { let aes_params = hex::decode("b3d529e34b839a521518447b68343aebaae9314ac95aaacfdb687a2163d1a98638db306b63409ef7bc906b4c9dc115488cf90dfa964f520542c69e1a4a495edf9ae9ee72023203c8b266d552f251e8d724929733428c8e276ab3bd6291367336a6ab8dc3d36243419bd0b742f76691a5dec14edbd50f7c1b58ec961ae45be58cbf6623f3ec9705bd5d227761ec79cee377e2566ff668f863552bddfd6ff3a16b").unwrap(); let nonce = hex::decode("9a5ecd5d9afdfff2823e7520fa1c338f2baf1a21f51e6fdab0491d45a50066f7").unwrap(); let buffer = hex::decode("7af98bb471ff48e9b263959b17a04faae4a23501380d2aa932b09eac6f9846fcbae9bbcb0cdf068c7904345aad16000000000000").unwrap(); let expected_packet = hex::decode("250d70d08526791bc2b6278ded7bf2b051afb441b309dda06f76e4419d7c31d4d5baafc4ff71e0ebabe246d4ea19e3e579bd15739c8fc916feaf46ea7a6bc562ed1cf87c9bf4220eb037b9a0b58f663f0474b8a8b18fa24db515e41e4b02e509d8ef261a27ba894cbbecc92e59fc44bf5ff7c8281cb5e900").unwrap(); - test_send(aes_params, nonce, buffer, expected_packet); + test_send(aes_params, nonce, buffer, expected_packet).await; } -#[test] -fn test_send_2() { +#[tokio::test] +async fn test_send_2() { let aes_params = hex::decode("7e3c66de7c64d4bee4368e69560101991db4b084430a336cffe676c9ac0a795d8c98367309422a8e927e62ed657ba3eaeeb6acd3bbe5564057dfd1d60609a25a48963cbb7d14acf4fc83ec59254673bc85be22d04e80e7b83c641d37cae6e1d82a400bf159490bbc0048e69234ad89e999d792eefdaa56734202546d9188706e95e1272267206a8e7ee1f7c077f76bd26e494972e34d72e257bf20364dbf39b0").unwrap(); let nonce = hex::decode("d36d0683da23e62910fa0e8a9331dfc257db4cde0ba8d63893e88ac4de7d8d6c").unwrap(); let buffer = hex::decode("7af98bb47bcae111ea0e56457826b1aec7f0f59b9b6579678b3db3839d17b63eb60174f20cdf068c7904345aad16000000000000").unwrap(); let expected_packet = hex::decode("24c709a0f676750ddaeafc8564d84546bfc831af27fb66716de382a347a1c32adef1a27e597c8a07605a09087fff32511d314970cad3983baefff01e7ee51bb672b17f7914a6d3f229a13acb14cdc14d98beae8a1e96510756726913541f558c2ffac63ed6cb076d0e888c3c0bb014d9f229c2a3f62e0847").unwrap(); - test_send(aes_params, nonce, buffer, expected_packet); + test_send(aes_params, nonce, buffer, expected_packet).await; } -fn test_send(aes_params: Vec, nonce: Vec, buffer: Vec, expected_packet: Vec) { +async fn test_send(aes_params: Vec, nonce: Vec, buffer: Vec, expected_packet: Vec) { let mut nonce = nonce.try_into().unwrap(); let mut buffer = buffer; let aes_params: [u8; 160] = aes_params.try_into().unwrap(); let aes_params = AdnlAesParams::from(aes_params); let mut protocol_client = AdnlSender::new(&aes_params); let mut packet = Vec::::new(); - let _result = protocol_client.send(&mut packet, &mut nonce, &mut buffer); + let _result = protocol_client + .send(&mut packet, &mut nonce, &mut buffer) + .await; assert_eq!( packet.as_slice(), &expected_packet, @@ -97,36 +99,37 @@ fn test_send(aes_params: Vec, nonce: Vec, buffer: Vec, expected_pack ); } -#[test] -fn test_recv_1() { +#[tokio::test] +async fn test_recv_1() { let encrypted_data = hex::decode("81e95e433c87c9ad2a716637b3a12644fbfb12dbd02996abc40ed2beb352483d6ecf9e2ad181a5abde4d4146ca3a8524739d3acebb2d7599cc6b81967692a62118997e16").unwrap(); let expected_data = Vec::new(); let aes_params = hex::decode("b3d529e34b839a521518447b68343aebaae9314ac95aaacfdb687a2163d1a98638db306b63409ef7bc906b4c9dc115488cf90dfa964f520542c69e1a4a495edf9ae9ee72023203c8b266d552f251e8d724929733428c8e276ab3bd6291367336a6ab8dc3d36243419bd0b742f76691a5dec14edbd50f7c1b58ec961ae45be58cbf6623f3ec9705bd5d227761ec79cee377e2566ff668f863552bddfd6ff3a16b").unwrap(); let aes_params: [u8; 160] = aes_params.try_into().unwrap(); let aes_params = AdnlAesParams::from(aes_params); let mut protocol_client = AdnlReceiver::new(&aes_params); - test_recv(&mut protocol_client, encrypted_data, expected_data); + test_recv(&mut protocol_client, encrypted_data, expected_data).await; let encrypted_data = hex::decode("4b72a32bf31894cce9ceffd2dd97176e502946524e45e62689bd8c5d31ad53603c5fd3b402771f707cd2747747fad9df52e6c23ceec9fa2ee5b0f68b61c33c7790db03d1c593798a29d716505cea75acdf0e031c25447c55c4d29d32caab29bd5a0787644843bafc04160c92140aab0ecc990927").unwrap(); let expected_data = hex::decode("1684ac0f71ff48e9b263959b17a04faae4a23501380d2aa932b09eac6f9846fcbae9bbcb080d0053e9a3ac3062000000").unwrap(); - test_recv(&mut protocol_client, encrypted_data, expected_data); + test_recv(&mut protocol_client, encrypted_data, expected_data).await; } -#[test] -fn test_recv_2() { +#[tokio::test] +async fn test_recv_2() { let encrypted_data = hex::decode("b75dcf27582beb4031d6d3700c9b7925bf84a78f2bd16b186484d36427a8824ac86e27cea81eb5bcbac447a37269845c65be51babd11c80627f81b4247f84df16d05c4f1").unwrap(); let expected_data = Vec::new(); let aes_params = hex::decode("7e3c66de7c64d4bee4368e69560101991db4b084430a336cffe676c9ac0a795d8c98367309422a8e927e62ed657ba3eaeeb6acd3bbe5564057dfd1d60609a25a48963cbb7d14acf4fc83ec59254673bc85be22d04e80e7b83c641d37cae6e1d82a400bf159490bbc0048e69234ad89e999d792eefdaa56734202546d9188706e95e1272267206a8e7ee1f7c077f76bd26e494972e34d72e257bf20364dbf39b0").unwrap(); let aes_params: [u8; 160] = aes_params.try_into().unwrap(); let aes_params = AdnlAesParams::from(aes_params); let mut protocol_client = AdnlReceiver::new(&aes_params); - test_recv(&mut protocol_client, encrypted_data, expected_data); + test_recv(&mut protocol_client, encrypted_data, expected_data).await; let encrypted_data = hex::decode("77ebea5a6e6c8758e7703d889abad16e7e3c4e0c10c4e81ca10d0d9abddabb6f008905133a070ff825ad3f4b0ae969e04dbd8b280864d3d2175f3bc7cf3deb31de5497fa43997d8e2acafb9a31de2a22ecb279b5854c00791216e39c2e65863539d82716fc020e9647b2dd99d0f14e4f553b645f").unwrap(); let expected_data = hex::decode("1684ac0f7bcae111ea0e56457826b1aec7f0f59b9b6579678b3db3839d17b63eb60174f2080d0053e90bb03062000000").unwrap(); - test_recv(&mut protocol_client, encrypted_data, expected_data); + test_recv(&mut protocol_client, encrypted_data, expected_data).await; } -fn test_recv(client: &mut AdnlReceiver, encrypted_packet: Vec, expected_data: Vec) { +async fn test_recv(client: &mut AdnlReceiver, encrypted_packet: Vec, expected_data: Vec) { let mut data = Vec::::new(); - let _r = client.receive::<_, _, 8192>(&mut encrypted_packet.as_slice(), &mut data); + let mut binding = encrypted_packet.as_slice(); + let _r = client.receive::<_, _, 8192>(&mut binding, &mut data).await; assert_eq!(data, expected_data.as_slice(), "incoming packet is wrong"); } diff --git a/src/wrappers/client.rs b/src/wrappers/client.rs index 8ab5933..72c5049 100644 --- a/src/wrappers/client.rs +++ b/src/wrappers/client.rs @@ -1,23 +1,49 @@ -use crate::{AdnlError, AdnlHandshake, AdnlPublicKey, AdnlReceiver, AdnlSender, Empty}; -use ciborium_io::{Read, Write}; +use crate::{AdnlBuilder, AdnlError, AdnlHandshake, AdnlPublicKey, AdnlReceiver, AdnlSender}; +use tokio::io::{empty, AsyncReadExt, AsyncWriteExt}; +use tokio::net::{TcpStream, ToSocketAddrs}; +use x25519_dalek::StaticSecret; /// Abstraction over [`AdnlSender`] and [`AdnlReceiver`] to keep things simple -pub struct AdnlClient { +pub struct AdnlClient { sender: AdnlSender, receiver: AdnlReceiver, transport: T, } -impl AdnlClient { +impl AdnlClient { + /// Create ADNL client use random private key and random AES params + #[cfg(feature = "dalek")] + pub async fn connect( + ls_public: P, + ls_addr: A, + ) -> Result, AdnlError> { + // generate private key + let local_secret = StaticSecret::new(rand::rngs::OsRng); + + // use TcpStream as transport for our ADNL connection + let transport = TcpStream::connect(ls_addr).await?; + + // build handshake using random session keys, encrypt it with ECDH(local_secret, remote_public) + // then perform handshake over our TcpStream + let client = AdnlBuilder::with_random_aes_params(&mut rand::rngs::OsRng) + .perform_ecdh(local_secret, ls_public) + .perform_handshake(transport) + .await?; + Ok(client) + } +} + +impl AdnlClient { /// Send `handshake` over `transport` and check that handshake was successful - pub fn perform_handshake( + pub async fn perform_handshake( mut transport: T, handshake: &AdnlHandshake

, - ) -> Result> { + ) -> Result { // send handshake transport .write_all(&handshake.to_bytes()) - .map_err(|e| AdnlError::WriteError(e))?; + .await + .map_err(AdnlError::WriteError)?; // receive empty message to ensure that server knows our AES keys let mut client = Self { @@ -25,32 +51,49 @@ impl AdnlClient { receiver: AdnlReceiver::new(handshake.aes_params()), transport, }; - client.receive::<_, 0>(&mut Empty).map_err(|e| match e { - AdnlError::ReadError(err) => AdnlError::ReadError(err), - AdnlError::WriteError(_) => unreachable!(), - AdnlError::ConsumeError(err) => AdnlError::ConsumeError(err), - AdnlError::IntegrityError => AdnlError::IntegrityError, - AdnlError::TooShortPacket => AdnlError::TooShortPacket, - })?; + let mut empty = empty(); + client + .receiver + .receive::<_, _, 0>(&mut client.transport, &mut empty) + .await?; Ok(client) } + /// Send `data` to another peer with random nonce + pub async fn send(&mut self, data: &mut [u8]) -> Result<(), AdnlError> { + self.sender + .send(&mut self.transport, &mut rand::random(), data) + .await + } + /// Send `data` to another peer. Random `nonce` must be provided to eliminate bit-flipping attacks. - pub fn send( + pub async fn send_with_nonce( &mut self, data: &mut [u8], nonce: &mut [u8; 32], - ) -> Result<(), AdnlError> { - self.sender.send(&mut self.transport, nonce, data) + ) -> Result<(), AdnlError> { + self.sender.send(&mut self.transport, nonce, data).await + } + + /// Receive data from another peer into `consumer` which will process the data with + /// a `BUFFER` size of 8192 bytes. + pub async fn receive( + &mut self, + consumer: &mut C, + ) -> Result<(), AdnlError> { + self.receiver + .receive::<_, _, 8192>(&mut self.transport, consumer) + .await } /// Receive data from another peer into `consumer` which will process the data. Set `BUFFER` /// according to your memory requirements, recommended size is 8192 bytes. - pub fn receive( + pub async fn receive_with_buffer( &mut self, consumer: &mut C, - ) -> Result<(), AdnlError> { + ) -> Result<(), AdnlError> { self.receiver .receive::<_, _, BUFFER>(&mut self.transport, consumer) + .await } }