├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── echo_client.rs ├── echo_server.rs └── time.rs └── src ├── helper_types.rs ├── lib.rs ├── primitives ├── codec.rs ├── handshake.rs └── mod.rs ├── tests.rs └── wrappers ├── builder.rs ├── mod.rs └── peer.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Continuous integration 4 | 5 | jobs: 6 | check: 7 | name: Check 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions-rs/toolchain@v1 12 | with: 13 | profile: minimal 14 | toolchain: stable 15 | override: true 16 | - uses: actions-rs/cargo@v1 17 | with: 18 | command: check 19 | 20 | test: 21 | name: Test Suite 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v2 25 | - uses: actions-rs/toolchain@v1 26 | with: 27 | profile: minimal 28 | toolchain: stable 29 | override: true 30 | - uses: actions-rs/cargo@v1 31 | with: 32 | command: test 33 | 34 | fmt: 35 | name: Rustfmt 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@v2 39 | - uses: actions-rs/toolchain@v1 40 | with: 41 | profile: minimal 42 | toolchain: stable 43 | override: true 44 | - run: rustup component add rustfmt 45 | - uses: actions-rs/cargo@v1 46 | with: 47 | command: fmt 48 | args: --all -- --check 49 | 50 | clippy: 51 | name: Clippy 52 | runs-on: ubuntu-latest 53 | steps: 54 | - uses: actions/checkout@v2 55 | - uses: actions-rs/toolchain@v1 56 | with: 57 | profile: minimal 58 | toolchain: stable 59 | override: true 60 | - run: rustup component add clippy 61 | - uses: actions-rs/cargo@v1 62 | with: 63 | command: clippy 64 | args: -- -D warnings 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea 3 | Cargo.lock -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "adnl" 3 | description = "Implementation of Abstract Datagram Network Layer" 4 | repository = "https://github.com/tonstack/adnl-rs" 5 | keywords = ["ton"] 6 | categories = ["network-programming"] 7 | license = "MIT" 8 | version = "2.0.0" 9 | authors = ["Vladimir Lebedev "] 10 | edition = "2021" 11 | 12 | [dependencies] 13 | sha2 = "0.10.2" 14 | ctr = "0.9.1" 15 | aes = "0.8.1" 16 | log = "0.4.14" 17 | rand_core = "0.6.3" 18 | tokio = { version = "1", features = ["net", "io-util"] } 19 | tokio-util = { version = "0.7.10", features = ["codec"] } 20 | thiserror = "1" 21 | rand = "0.8.5" 22 | futures = "0.3" 23 | pin-project = "1" 24 | hex = "0.4.3" 25 | everscale-crypto = "0.2.1" 26 | 27 | [dev-dependencies] 28 | hex = "0.4.3" 29 | tokio = { version = "1.36", features = ["rt-multi-thread", "macros"]} 30 | base64 = "0.22.1" 31 | 32 | [[example]] 33 | name = "time" 34 | 35 | [[example]] 36 | name = "echo_client" 37 | 38 | [[example]] 39 | name = "echo_server" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Vladimir Lebedev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ADNL 2 | 3 | [![crates.io](https://img.shields.io/crates/v/adnl.svg)](https://crates.io/crates/adnl) 4 | [![documentation](https://docs.rs/adnl/badge.svg)](https://docs.rs/adnl) 5 | 6 | Minimal client-server ADNL implementation in Rust. Specification of ADNL is available [here](https://github.com/tonstack/ton-docs/blob/main/ADNL/README.md). 7 | 8 | ## Quickstart 9 | Run this example: `cargo run --example time` 10 | 11 | ```rust 12 | use adnl::AdnlPeer; 13 | use base64::Engine as _; 14 | use futures::{SinkExt, StreamExt}; 15 | use std::error::Error; 16 | 17 | #[tokio::main] 18 | async fn main() -> Result<(), Box> { 19 | // decode liteserver public key 20 | let remote_public = base64::engine::general_purpose::STANDARD.decode("n4VDnSCUuSpjnCyUk9e3QOOd6o0ItSWYbTnW3Wnn8wk=")?; 21 | 22 | // act as a client: connect to ADNL server and perform handshake 23 | let mut client = AdnlPeer::connect(remote_public, "5.9.10.47:19949").await?; 24 | 25 | // already serialized TL with getTime query 26 | let query = hex::decode("7af98bb435263e6c95d6fecb497dfd0aa5f031e7d412986b5ce720496db512052e8f2d100cdf068c7904345aad16000000000000")?; 27 | 28 | // send over ADNL 29 | client.send(query.into()).await?; 30 | 31 | // receive result 32 | let result = client.next().await.ok_or_else(|| "no result")??; 33 | 34 | // get time from serialized TL answer 35 | println!( 36 | "received: {}", 37 | u32::from_le_bytes(result[result.len() - 7..result.len() - 3].try_into()?) 38 | ); 39 | Ok(()) 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /examples/echo_client.rs: -------------------------------------------------------------------------------- 1 | use adnl::AdnlPeer; 2 | use futures::{SinkExt, StreamExt}; 3 | use std::{env, error::Error}; 4 | 5 | #[tokio::main] 6 | async fn main() -> Result<(), Box> { 7 | let addr = env::args() 8 | .nth(1) 9 | .unwrap_or_else(|| "127.0.0.1:8080".to_string()); 10 | 11 | let public_key_hex = env::args().nth(2).unwrap_or_else(|| { 12 | "691a14528fb2911839649c489cb4cbec1f4aa126c244c0ea2ac294eb568a7037".to_string() 13 | }); 14 | 15 | // act as a client: connect to ADNL server and perform handshake 16 | let mut client = AdnlPeer::connect(hex::decode(public_key_hex)?, addr).await?; 17 | 18 | // send over ADNL 19 | client.send("hello".as_bytes().into()).await?; 20 | 21 | // receive result 22 | let result = client.next().await.ok_or("packet must be received")??; 23 | 24 | println!("received: {}", String::from_utf8(result.to_vec())?); 25 | Ok(()) 26 | } 27 | -------------------------------------------------------------------------------- /examples/echo_server.rs: -------------------------------------------------------------------------------- 1 | //! Adopted from https://github.com/tokio-rs/tokio/blob/b32826bc937a34e4d871c89bb2c3711ed3e20cdc/examples/echo.rs 2 | 3 | use std::{env, error::Error}; 4 | 5 | use adnl::crypto::{KeyPair, SecretKey}; 6 | use adnl::{AdnlAddress, AdnlPeer}; 7 | use futures::{SinkExt, StreamExt}; 8 | use tokio::net::TcpListener; 9 | 10 | #[tokio::main] 11 | async fn main() -> Result<(), Box> { 12 | // Allow passing an address to listen on as the first argument of this 13 | // program, but otherwise we'll just set up our TCP listener on 14 | // 127.0.0.1:8080 for connections. 15 | let addr = env::args() 16 | .nth(1) 17 | .unwrap_or_else(|| "127.0.0.1:8080".to_string()); 18 | 19 | // ADNL: get private key from environment variable KEY or use default insecure one 20 | let private_key_hex = env::var("KEY").unwrap_or_else(|_| { 21 | "f0971651aec4bb0d65ec3861c597687fda9c1e7d2ee8a93acb9a131aa9f3aee7".to_string() 22 | }); 23 | let private_key_bytes: [u8; 32] = hex::decode(private_key_hex)?.try_into().unwrap(); 24 | let keypair = KeyPair::from(&SecretKey::from_bytes(private_key_bytes)); 25 | 26 | // Next up we create a TCP listener which will listen for incoming 27 | // connections. This TCP listener is bound to the address we determined 28 | // above and must be associated with an event loop. 29 | let listener = TcpListener::bind(&addr).await?; 30 | println!("Listening on: {}", addr); 31 | 32 | // ADNL: print public key and adnl address associated with given private key 33 | println!( 34 | "Public key is: {}", 35 | hex::encode(keypair.public_key.as_bytes()) 36 | ); 37 | println!( 38 | "Address is: {}", 39 | hex::encode(AdnlAddress::from(&keypair.public_key).as_bytes()) 40 | ); 41 | 42 | loop { 43 | // Asynchronously wait for an inbound socket. 44 | let (socket, _) = listener.accept().await?; 45 | 46 | // And this is where much of the magic of this server happens. We 47 | // crucially want all clients to make progress concurrently, rather than 48 | // blocking one on completion of another. To achieve this we use the 49 | // `tokio::spawn` function to execute the work in the background. 50 | // 51 | // Essentially here we're executing a new task to run concurrently, 52 | // which will allow all of our clients to be processed concurrently. 53 | 54 | let private_key = keypair.clone(); 55 | tokio::spawn(async move { 56 | // ADNL: handle handshake 57 | let mut adnl_server = AdnlPeer::handle_handshake(socket, |_| Some(private_key.clone())) 58 | .await 59 | .expect("handshake failed"); 60 | 61 | // In a loop, read data from the socket and write the data back. 62 | while let Some(Ok(packet)) = adnl_server.next().await { 63 | let _ = adnl_server.send(packet).await; 64 | } 65 | }); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /examples/time.rs: -------------------------------------------------------------------------------- 1 | use adnl::AdnlPeer; 2 | use base64::Engine as _; 3 | use futures::{SinkExt, StreamExt}; 4 | use std::error::Error; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<(), Box> { 8 | // decode liteserver public key 9 | let remote_public = base64::engine::general_purpose::STANDARD 10 | .decode("n4VDnSCUuSpjnCyUk9e3QOOd6o0ItSWYbTnW3Wnn8wk=")?; 11 | 12 | // act as a client: connect to ADNL server and perform handshake 13 | let mut client = AdnlPeer::connect(remote_public, "5.9.10.47:19949").await?; 14 | 15 | // already serialized TL with getTime query 16 | let query = hex::decode("7af98bb435263e6c95d6fecb497dfd0aa5f031e7d412986b5ce720496db512052e8f2d100cdf068c7904345aad16000000000000")?; 17 | 18 | // send over ADNL 19 | client.send(query.into()).await?; 20 | 21 | // receive result 22 | let result = client.next().await.ok_or_else(|| "no result")??; 23 | 24 | // get time from serialized TL answer 25 | println!( 26 | "received: {}", 27 | u32::from_le_bytes(result[result.len() - 7..result.len() - 3].try_into()?) 28 | ); 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /src/helper_types.rs: -------------------------------------------------------------------------------- 1 | use crate::crypto::PublicKey; 2 | use sha2::{Digest, Sha256}; 3 | use std::{array::TryFromSliceError, io::Error}; 4 | use thiserror::Error; 5 | 6 | pub trait CryptoRandom: rand_core::RngCore + rand_core::CryptoRng {} 7 | 8 | impl CryptoRandom for T where T: rand_core::RngCore + rand_core::CryptoRng {} 9 | 10 | /// Wrapper struct to hold ADNL address, which is a hash of public key 11 | #[derive(PartialEq, Clone)] 12 | pub struct AdnlAddress([u8; 32]); 13 | 14 | impl std::fmt::Debug for AdnlAddress { 15 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 16 | f.debug_tuple("AdnlAddress") 17 | .field(&format!("{:02x?}", &self.0)) 18 | .finish() 19 | } 20 | } 21 | 22 | impl From<[u8; 32]> for AdnlAddress { 23 | fn from(value: [u8; 32]) -> Self { 24 | Self(value) 25 | } 26 | } 27 | 28 | impl TryFrom<&[u8]> for AdnlAddress { 29 | type Error = TryFromSliceError; 30 | 31 | fn try_from(value: &[u8]) -> Result { 32 | Ok(Self(value.try_into()?)) 33 | } 34 | } 35 | 36 | impl From<&PublicKey> for AdnlAddress { 37 | fn from(value: &PublicKey) -> Self { 38 | let mut hasher = Sha256::new(); 39 | hasher.update([0xc6, 0xb4, 0x13, 0x48]); // type id - always ed25519 40 | hasher.update(value.as_bytes()); 41 | AdnlAddress(hasher.finalize().into()) 42 | } 43 | } 44 | 45 | /// Session parameters for AES-CTR encryption of datagrams 46 | #[derive(Clone)] 47 | pub struct AdnlAesParams { 48 | rx_key: [u8; 32], 49 | tx_key: [u8; 32], 50 | rx_nonce: [u8; 16], 51 | tx_nonce: [u8; 16], 52 | padding: [u8; 64], 53 | } 54 | 55 | impl From<[u8; 160]> for AdnlAesParams { 56 | fn from(raw_buffer: [u8; 160]) -> Self { 57 | Self { 58 | rx_key: raw_buffer[..32].try_into().unwrap(), 59 | tx_key: raw_buffer[32..64].try_into().unwrap(), 60 | rx_nonce: raw_buffer[64..80].try_into().unwrap(), 61 | tx_nonce: raw_buffer[80..96].try_into().unwrap(), 62 | padding: raw_buffer[96..160].try_into().unwrap(), 63 | } 64 | } 65 | } 66 | 67 | impl AdnlAesParams { 68 | pub fn rx_key(&self) -> &[u8; 32] { 69 | &self.rx_key 70 | } 71 | 72 | pub fn tx_key(&self) -> &[u8; 32] { 73 | &self.tx_key 74 | } 75 | 76 | pub fn rx_nonce(&self) -> &[u8; 16] { 77 | &self.rx_nonce 78 | } 79 | 80 | pub fn tx_nonce(&self) -> &[u8; 16] { 81 | &self.tx_nonce 82 | } 83 | 84 | /// Serialize this structure into bytes to use in handshake packet 85 | pub fn to_bytes(&self) -> [u8; 160] { 86 | let mut result = [0u8; 160]; 87 | result[..32].copy_from_slice(&self.rx_key); 88 | result[32..64].copy_from_slice(&self.tx_key); 89 | result[64..80].copy_from_slice(&self.rx_nonce); 90 | result[80..96].copy_from_slice(&self.tx_nonce); 91 | result[96..160].copy_from_slice(&self.padding); 92 | result 93 | } 94 | 95 | /// Generate random session parameters 96 | pub fn random(csprng: &mut T) -> Self { 97 | let mut result = [0u8; 160]; 98 | csprng.fill_bytes(&mut result); 99 | Self::from(result) 100 | } 101 | } 102 | 103 | impl Default for AdnlAesParams { 104 | fn default() -> Self { 105 | Self { 106 | rx_key: [0; 32], 107 | tx_key: [0; 32], 108 | rx_nonce: [0; 16], 109 | tx_nonce: [0; 16], 110 | padding: [0; 64], 111 | } 112 | } 113 | } 114 | 115 | impl AdnlAddress { 116 | #[inline] 117 | pub fn to_bytes(&self) -> [u8; 32] { 118 | self.0 119 | } 120 | 121 | #[inline] 122 | pub fn as_bytes(&self) -> &[u8; 32] { 123 | &self.0 124 | } 125 | } 126 | 127 | /// Common error type 128 | #[derive(Debug, Error)] 129 | pub enum AdnlError { 130 | #[error("IO error")] 131 | IoError(#[from] Error), 132 | #[error("Integrity error")] 133 | IntegrityError, 134 | #[error("Too short packet (32 bytes min)")] 135 | TooShortPacket, 136 | #[error("Too long packet (4 MiB max)")] 137 | TooLongPacket, 138 | #[error("Receiver ADNL address mismatch")] 139 | UnknownAddr(AdnlAddress), 140 | #[error("End of stream")] 141 | EndOfStream, 142 | #[error("Invalid public key")] 143 | InvalidPublicKey, 144 | } 145 | 146 | /// Information about connected peers. 147 | pub struct AdnlConnectionInfo { 148 | local_address: AdnlAddress, 149 | remote_address: AdnlAddress, 150 | } 151 | 152 | impl AdnlConnectionInfo { 153 | pub fn new(local_address: AdnlAddress, remote_address: AdnlAddress) -> Self { 154 | Self { 155 | local_address, 156 | remote_address, 157 | } 158 | } 159 | 160 | pub fn local_address(&self) -> &AdnlAddress { 161 | &self.local_address 162 | } 163 | 164 | pub fn remote_address(&self) -> &AdnlAddress { 165 | &self.remote_address 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # ADNL 2 | //! 3 | //! This crate provides a minimal implementation of the Abstract Datagram Network Layer (ADNL) 4 | //! protocol in Rust. ADNL is a network protocol used in The Open Network (TON) blockchain. 5 | //! 6 | //! ## Client example 7 | //! 8 | //! ```rust,no_run 9 | //! use adnl::AdnlPeer; 10 | //! use base64::Engine as _; 11 | //! use futures::{SinkExt, StreamExt}; 12 | //! use std::error::Error; 13 | //! 14 | //! #[tokio::main] 15 | //! async fn main() -> Result<(), Box> { 16 | //! // decode liteserver public key 17 | //! let remote_public = base64::engine::general_purpose::STANDARD.decode("n4VDnSCUuSpjnCyUk9e3QOOd6o0ItSWYbTnW3Wnn8wk=")?; 18 | //! 19 | //! // act as a client: connect to ADNL server and perform handshake 20 | //! let mut client = AdnlPeer::connect(remote_public, "5.9.10.47:19949").await?; 21 | //! 22 | //! // already serialized TL with getTime query 23 | //! let query = hex::decode("7af98bb435263e6c95d6fecb497dfd0aa5f031e7d412986b5ce720496db512052e8f2d100cdf068c7904345aad16000000000000")?; 24 | //! 25 | //! // send over ADNL 26 | //! client.send(query.into()).await?; 27 | //! 28 | //! // receive result 29 | //! let result = client.next().await.ok_or_else(|| "no result")??; 30 | //! 31 | //! // get time from serialized TL answer 32 | //! println!( 33 | //! "received: {}", 34 | //! u32::from_le_bytes(result[result.len() - 7..result.len() - 3].try_into()?) 35 | //! ); 36 | //! Ok(()) 37 | //! } 38 | //! ``` 39 | //! 40 | //! See the `examples/` directory for more usage examples. 41 | 42 | pub use helper_types::{AdnlAddress, AdnlAesParams, AdnlConnectionInfo, AdnlError}; 43 | pub use primitives::codec::AdnlCodec; 44 | pub use primitives::handshake::AdnlHandshake; 45 | pub use wrappers::builder::AdnlBuilder; 46 | pub use wrappers::peer::AdnlPeer; 47 | 48 | pub mod crypto { 49 | pub use everscale_crypto::ed25519::*; 50 | } 51 | 52 | mod helper_types; 53 | mod primitives; 54 | mod wrappers; 55 | 56 | #[cfg(test)] 57 | mod tests; 58 | -------------------------------------------------------------------------------- /src/primitives/codec.rs: -------------------------------------------------------------------------------- 1 | use aes::cipher::{KeyIvInit, StreamCipher}; 2 | use sha2::{Digest, Sha256}; 3 | use tokio_util::{ 4 | bytes::{Buf, Bytes, BytesMut}, 5 | codec::{Decoder, Encoder}, 6 | }; 7 | 8 | use crate::{AdnlAesParams, AdnlError}; 9 | 10 | use super::AdnlAes; 11 | 12 | /// Implementation of ADNL protocol. Connection must be first initialized with [`AdnlHandshake`] to exchange keys. 13 | pub struct AdnlCodec { 14 | aes_rx: AdnlAes, 15 | aes_tx: AdnlAes, 16 | last_readed_length: Option, 17 | } 18 | 19 | impl AdnlCodec { 20 | pub fn client(aes_params: &AdnlAesParams) -> Self { 21 | Self { 22 | aes_rx: AdnlAes::new(aes_params.rx_key().into(), aes_params.rx_nonce().into()), 23 | aes_tx: AdnlAes::new(aes_params.tx_key().into(), aes_params.tx_nonce().into()), 24 | last_readed_length: None, 25 | } 26 | } 27 | 28 | pub fn server(aes_params: &AdnlAesParams) -> Self { 29 | Self { 30 | aes_rx: AdnlAes::new(aes_params.tx_key().into(), aes_params.tx_nonce().into()), 31 | aes_tx: AdnlAes::new(aes_params.rx_key().into(), aes_params.rx_nonce().into()), 32 | last_readed_length: None, 33 | } 34 | } 35 | } 36 | 37 | impl Decoder for AdnlCodec { 38 | type Item = Bytes; 39 | 40 | type Error = AdnlError; 41 | 42 | fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { 43 | let length = if let Some(length) = self.last_readed_length { 44 | length 45 | } else { 46 | if src.len() < 4 { 47 | return Ok(None); 48 | } 49 | self.aes_rx.apply_keystream(&mut src[..4]); 50 | let mut length_bytes = [0u8; 4]; 51 | length_bytes.copy_from_slice(&src[..4]); 52 | let length = u32::from_le_bytes(length_bytes) as usize; 53 | if length < 64 { 54 | return Err(AdnlError::TooShortPacket); 55 | } 56 | if length > (1 << 24) { 57 | return Err(AdnlError::TooLongPacket); 58 | } 59 | src.advance(4); 60 | self.last_readed_length = Some(length); 61 | length 62 | }; 63 | 64 | // not enough bytes, need to wait for more data 65 | if src.len() < length { 66 | if src.capacity() < length { 67 | src.reserve(length - src.capacity()); 68 | } 69 | return Ok(None); 70 | } 71 | 72 | self.last_readed_length = None; 73 | 74 | // decode packet 75 | self.aes_rx.apply_keystream(&mut src[..length]); 76 | let given_hash = &src[length - 32..length]; 77 | 78 | // integrity check 79 | let mut hasher = Sha256::new(); 80 | hasher.update(&src[..length - 32]); 81 | if given_hash != hasher.finalize().as_slice() { 82 | return Err(AdnlError::IntegrityError); 83 | } 84 | 85 | // copy and return buffer 86 | let result = Bytes::copy_from_slice(&src[32..length - 32]); 87 | src.advance(length); 88 | Ok(Some(result)) 89 | } 90 | } 91 | 92 | impl Encoder for AdnlCodec { 93 | type Error = AdnlError; 94 | 95 | fn encode(&mut self, buffer: Bytes, dst: &mut BytesMut) -> Result<(), Self::Error> { 96 | if buffer.len() > ((1 << 24) - 64) { 97 | return Err(AdnlError::TooLongPacket); 98 | } 99 | let length = ((buffer.len() + 64) as u32).to_le_bytes(); 100 | let nonce = rand::random::<[u8; 32]>(); 101 | let mut hash = Sha256::new(); 102 | hash.update(nonce); 103 | hash.update(&buffer); 104 | let hash = hash.finalize(); 105 | dst.reserve(buffer.len() + 68); 106 | dst.extend_from_slice(&length); 107 | dst.extend_from_slice(&nonce); 108 | dst.extend_from_slice(&buffer); 109 | dst.extend_from_slice(&hash); 110 | let start_offset = dst.len() - buffer.len() - 68; 111 | self.aes_tx.apply_keystream(&mut dst[start_offset..]); 112 | Ok(()) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/primitives/handshake.rs: -------------------------------------------------------------------------------- 1 | use crate::crypto::{KeyPair, PublicKey}; 2 | use crate::primitives::AdnlAes; 3 | use crate::{AdnlAddress, AdnlAesParams, AdnlError, AdnlPeer}; 4 | use aes::cipher::KeyIvInit; 5 | use ctr::cipher::StreamCipher; 6 | use sha2::{Digest, Sha256}; 7 | use tokio::io::{AsyncReadExt, AsyncWriteExt}; 8 | 9 | use super::codec::AdnlCodec; 10 | 11 | /// Handshake packet, must be sent from client to server prior to any datagrams 12 | pub struct AdnlHandshake { 13 | receiver: AdnlAddress, 14 | sender: PublicKey, 15 | aes_params: AdnlAesParams, 16 | secret: [u8; 32], 17 | } 18 | 19 | impl AdnlHandshake { 20 | /// Create handshake with given sender and receiver, who already agreed on given secret, also 21 | /// use given session parameters 22 | pub fn new( 23 | receiver: AdnlAddress, 24 | sender: PublicKey, 25 | secret: [u8; 32], 26 | aes_params: AdnlAesParams, 27 | ) -> Self { 28 | Self { 29 | receiver, 30 | sender, 31 | aes_params, 32 | secret, 33 | } 34 | } 35 | 36 | /// Get session AES parameters 37 | pub fn aes_params(&self) -> &AdnlAesParams { 38 | &self.aes_params 39 | } 40 | 41 | /// Get initiator public key of this handshake 42 | pub fn sender(&self) -> &PublicKey { 43 | &self.sender 44 | } 45 | 46 | /// Get destination ADNL address of this handshake 47 | pub fn receiver(&self) -> &AdnlAddress { 48 | &self.receiver 49 | } 50 | 51 | /// Serialize handshake to send it over the transport 52 | pub fn to_bytes(&self) -> [u8; 256] { 53 | let mut raw_params = self.aes_params.to_bytes(); 54 | let hash = Self::sha256(raw_params); 55 | let mut aes = Self::initialize_aes(&self.secret, &hash); 56 | aes.apply_keystream(&mut raw_params); 57 | 58 | let mut packet = [0u8; 256]; 59 | packet[..32].copy_from_slice(self.receiver.as_bytes()); 60 | packet[32..64].copy_from_slice(self.sender.as_bytes()); 61 | packet[64..96].copy_from_slice(&hash); 62 | packet[96..256].copy_from_slice(&raw_params); 63 | packet 64 | } 65 | 66 | pub fn make_client_codec(&self) -> AdnlCodec { 67 | AdnlCodec::client(&self.aes_params) 68 | } 69 | 70 | pub fn make_server_codec(&self) -> AdnlCodec { 71 | AdnlCodec::server(&self.aes_params) 72 | } 73 | 74 | /// Send handshake over the given transport, build [`AdnlClient`] on top of it 75 | pub async fn perform_handshake( 76 | &self, 77 | transport: T, 78 | ) -> Result, AdnlError> { 79 | AdnlPeer::perform_custom_handshake(transport, self).await 80 | } 81 | 82 | fn initialize_aes(secret: &[u8; 32], hash: &[u8]) -> AdnlAes { 83 | let mut key = [0u8; 32]; 84 | key[..16].copy_from_slice(&secret[..16]); 85 | key[16..32].copy_from_slice(&hash[16..32]); 86 | 87 | let mut nonce = [0u8; 16]; 88 | nonce[..4].copy_from_slice(&hash[..4]); 89 | nonce[4..16].copy_from_slice(&secret[20..32]); 90 | 91 | AdnlAes::new(key.as_slice().into(), nonce.as_slice().into()) 92 | } 93 | 94 | fn sha256(data: impl AsRef<[u8]>) -> [u8; 32] { 95 | let mut hasher = Sha256::new(); 96 | hasher.update(data); 97 | hasher.finalize().into() 98 | } 99 | 100 | /// Deserialize and decrypt handshake using keypair from `keypair_selector` function 101 | pub fn decrypt_from_raw Option>( 102 | packet: &[u8; 256], 103 | keypair_selector: F, 104 | ) -> Result { 105 | let receiver = packet[..32].try_into().unwrap(); 106 | let sender = PublicKey::from_bytes(packet[32..64].try_into().unwrap()) 107 | .ok_or_else(|| AdnlError::InvalidPublicKey)?; 108 | let hash: [u8; 32] = packet[64..96].try_into().unwrap(); 109 | let mut raw_params: [u8; 160] = packet[96..256].try_into().unwrap(); 110 | 111 | let keypair = 112 | keypair_selector(&receiver).ok_or_else(|| AdnlError::UnknownAddr(receiver.clone()))?; 113 | 114 | let our_address = AdnlAddress::from(&keypair.public_key); 115 | if our_address != receiver { 116 | log::error!( 117 | "private key selector returned wrong key, expected address: {:?}, got: {:?}", 118 | &receiver, 119 | our_address 120 | ); 121 | return Err(AdnlError::UnknownAddr(receiver)); 122 | } 123 | 124 | let secret = keypair.compute_shared_secret(&sender); 125 | let mut aes = Self::initialize_aes(&secret, &hash); 126 | aes.apply_keystream(&mut raw_params); 127 | 128 | if hash != Self::sha256(raw_params) { 129 | return Err(AdnlError::IntegrityError); 130 | } 131 | 132 | Ok(Self { 133 | receiver, 134 | sender, 135 | aes_params: AdnlAesParams::from(raw_params), 136 | secret, 137 | }) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/primitives/mod.rs: -------------------------------------------------------------------------------- 1 | use aes::Aes256; 2 | use ctr::Ctr128BE; 3 | 4 | pub type AdnlAes = Ctr128BE; 5 | 6 | pub mod codec; 7 | pub mod handshake; 8 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | extern crate alloc; 2 | 3 | use super::*; 4 | use crate::crypto::{KeyPair, PublicKey}; 5 | use alloc::vec::Vec; 6 | use futures::{SinkExt, StreamExt}; 7 | use rand_core::OsRng; 8 | use tokio::net::TcpListener; 9 | use tokio_util::{ 10 | bytes::BytesMut, 11 | codec::{Decoder, Encoder}, 12 | }; 13 | 14 | #[test] 15 | fn test_handshake_1() { 16 | let aes_params = hex::decode("b3d529e34b839a521518447b68343aebaae9314ac95aaacfdb687a2163d1a98638db306b63409ef7bc906b4c9dc115488cf90dfa964f520542c69e1a4a495edf9ae9ee72023203c8b266d552f251e8d724929733428c8e276ab3bd6291367336a6ab8dc3d36243419bd0b742f76691a5dec14edbd50f7c1b58ec961ae45be58cbf6623f3ec9705bd5d227761ec79cee377e2566ff668f863552bddfd6ff3a16b").unwrap(); 17 | let remote_public = 18 | hex::decode("2615edec7d5d6538314132321a2615e1ff5550046e0f1165ff59150632d2301f").unwrap(); 19 | let ecdh = 20 | hex::decode("1f4d11789a5559b238f7ac8213e112184f16a97593b4a059c878af288a784b79").unwrap(); 21 | let expected_handshake = hex::decode("a3fc70bfeff13b04ed4f2581045ff95a385df762eb82ab9902066061c2e6033e67d45a90e775d8f78d9feb9bdd222446e07c3de4a54e29220d18c18c5b340db36c06a61a8eb209b2b4f9d7359d76e3e0722024579d2b8bc920a6506238d6d88d14a880eb99b4996df8a11bb1a7124e39825848c74fc3d7bfab034e71dbc2e2d1606c14db1b04bb25b544a83b47815e9ec0590a9f4dd011b4bae7b01ddb376570d6641919e63933bf297a073b8febfae0c4dd298215e5db929c6764c43502874b7b5af6380fd52d3fd072b7046d6ccadecc771f54b461b5a157fe3e059df9575dc72dfc89e36b26a7cf9a4e7925c96e88d5342c139154c4a6e4e9d683d9373e3a").unwrap(); 22 | let local_public = 23 | hex::decode("67d45a90e775d8f78d9feb9bdd222446e07c3de4a54e29220d18c18c5b340db3").unwrap(); 24 | test_handshake( 25 | remote_public, 26 | local_public, 27 | ecdh, 28 | aes_params, 29 | expected_handshake, 30 | ); 31 | } 32 | 33 | #[test] 34 | fn test_handshake_2() { 35 | let aes_params = hex::decode("7e3c66de7c64d4bee4368e69560101991db4b084430a336cffe676c9ac0a795d8c98367309422a8e927e62ed657ba3eaeeb6acd3bbe5564057dfd1d60609a25a48963cbb7d14acf4fc83ec59254673bc85be22d04e80e7b83c641d37cae6e1d82a400bf159490bbc0048e69234ad89e999d792eefdaa56734202546d9188706e95e1272267206a8e7ee1f7c077f76bd26e494972e34d72e257bf20364dbf39b0").unwrap(); 36 | let remote_public = 37 | hex::decode("2615edec7d5d6538314132321a2615e1ff5550046e0f1165ff59150632d2301f").unwrap(); 38 | let ecdh = 39 | hex::decode("10a28a56cce723b2ab75aeba51039f5f3f72bca49f22b7f8039690811bb0606e").unwrap(); 40 | let expected_handshake = hex::decode("a3fc70bfeff13b04ed4f2581045ff95a385df762eb82ab9902066061c2e6033ed86dac237d94b1b611dcac497f952edb63756910dbf625f5c5806e159d1047270f372a88fd1f76b0a574620cf47202369359bdeff8e709d6c0578cf08d2499cb949ecaaf892f11fc772932182269f9e5f2f44150066ae65fbb5fc9f51dab26825bd6fd4d72de9ccc80bbddcb9d47f9c3cfd00b80a5d9faf15007abb480f9fd85e2f671484e82f3b67f58197c5438dab575062faa9acd821ca6a10e7061c40535112650f1730d03484de0d01aa7912ed64655e672bd077c1f1e50b231556ecfd5e5009f47804c317abec6310165a6618125a2204b0370d40e672e1a640817b894").unwrap(); 41 | let local_public = 42 | hex::decode("d86dac237d94b1b611dcac497f952edb63756910dbf625f5c5806e159d104727").unwrap(); 43 | test_handshake( 44 | remote_public, 45 | local_public, 46 | ecdh, 47 | aes_params, 48 | expected_handshake, 49 | ); 50 | } 51 | 52 | fn test_handshake( 53 | remote_public: Vec, 54 | local_public: Vec, 55 | ecdh: Vec, 56 | aes_params: Vec, 57 | expected_handshake: Vec, 58 | ) { 59 | // test serializing 60 | let aes_params_raw: [u8; 160] = aes_params.try_into().unwrap(); 61 | let aes_params = AdnlAesParams::from(aes_params_raw); 62 | let remote_public = 63 | PublicKey::from_bytes(remote_public.as_slice().try_into().unwrap()).unwrap(); 64 | let local_public = PublicKey::from_bytes(local_public.as_slice().try_into().unwrap()).unwrap(); 65 | let ecdh_raw: [u8; 32] = ecdh.try_into().unwrap(); 66 | let handshake = AdnlHandshake::new( 67 | AdnlAddress::from(&remote_public), 68 | local_public.clone(), 69 | ecdh_raw, 70 | aes_params, 71 | ); 72 | assert_eq!( 73 | handshake.to_bytes(), 74 | expected_handshake.as_slice(), 75 | "handshake is not the same!" 76 | ); 77 | 78 | // test deserializing 79 | // let handshake2 = AdnlHandshake::decrypt_from_raw(expected_handshake.as_slice().try_into().unwrap(), |_| Some(key.clone())).expect("invalid handshake"); 80 | // assert_eq!(handshake2.aes_params().to_bytes(), aes_params_raw, "aes_params mismatch"); 81 | // assert_eq!(handshake2.receiver(), &AdnlAddress::from(&remote_public), "receiver mismatch"); 82 | // assert_eq!(handshake2.sender(), &local_public, "sender mismatch"); 83 | // assert_eq!(&handshake2.to_bytes(), expected_handshake.as_slice(), "reencryption failed"); 84 | } 85 | 86 | #[test] 87 | fn test_send_1() { 88 | let aes_params = hex::decode("b3d529e34b839a521518447b68343aebaae9314ac95aaacfdb687a2163d1a98638db306b63409ef7bc906b4c9dc115488cf90dfa964f520542c69e1a4a495edf9ae9ee72023203c8b266d552f251e8d724929733428c8e276ab3bd6291367336a6ab8dc3d36243419bd0b742f76691a5dec14edbd50f7c1b58ec961ae45be58cbf6623f3ec9705bd5d227761ec79cee377e2566ff668f863552bddfd6ff3a16b").unwrap(); 89 | let _nonce = 90 | hex::decode("9a5ecd5d9afdfff2823e7520fa1c338f2baf1a21f51e6fdab0491d45a50066f7").unwrap(); 91 | let buffer = hex::decode("7af98bb471ff48e9b263959b17a04faae4a23501380d2aa932b09eac6f9846fcbae9bbcb0cdf068c7904345aad16000000000000").unwrap(); 92 | let expected_packet = hex::decode("250d70d08526791bc2b6278ded7bf2b051afb441b309dda06f76e4419d7c31d4d5baafc4ff71e0ebabe246d4ea19e3e579bd15739c8fc916feaf46ea7a6bc562ed1cf87c9bf4220eb037b9a0b58f663f0474b8a8b18fa24db515e41e4b02e509d8ef261a27ba894cbbecc92e59fc44bf5ff7c8281cb5e900").unwrap(); 93 | test_send(aes_params, buffer, expected_packet); 94 | } 95 | 96 | #[test] 97 | fn test_send_2() { 98 | let aes_params = hex::decode("7e3c66de7c64d4bee4368e69560101991db4b084430a336cffe676c9ac0a795d8c98367309422a8e927e62ed657ba3eaeeb6acd3bbe5564057dfd1d60609a25a48963cbb7d14acf4fc83ec59254673bc85be22d04e80e7b83c641d37cae6e1d82a400bf159490bbc0048e69234ad89e999d792eefdaa56734202546d9188706e95e1272267206a8e7ee1f7c077f76bd26e494972e34d72e257bf20364dbf39b0").unwrap(); 99 | let _nonce = 100 | hex::decode("d36d0683da23e62910fa0e8a9331dfc257db4cde0ba8d63893e88ac4de7d8d6c").unwrap(); 101 | let buffer = hex::decode("7af98bb47bcae111ea0e56457826b1aec7f0f59b9b6579678b3db3839d17b63eb60174f20cdf068c7904345aad16000000000000").unwrap(); 102 | let expected_packet = hex::decode("24c709a0f676750ddaeafc8564d84546bfc831af27fb66716de382a347a1c32adef1a27e597c8a07605a09087fff32511d314970cad3983baefff01e7ee51bb672b17f7914a6d3f229a13acb14cdc14d98beae8a1e96510756726913541f558c2ffac63ed6cb076d0e888c3c0bb014d9f229c2a3f62e0847").unwrap(); 103 | test_send(aes_params, buffer, expected_packet); 104 | } 105 | 106 | fn test_send(aes_params: Vec, buffer: Vec, expected_packet: Vec) { 107 | let aes_params: [u8; 160] = aes_params.try_into().unwrap(); 108 | let mut codec = AdnlCodec::client(&aes_params.into()); 109 | let mut packet = BytesMut::new(); 110 | codec 111 | .encode(buffer.clone().into(), &mut packet) 112 | .expect("packet must be encoded correctly"); 113 | 114 | // do not check nonce and hash as it's random 115 | assert_eq!( 116 | &packet[..4], 117 | &expected_packet[..4], 118 | "outcoming packet length is wrong" 119 | ); 120 | assert_eq!( 121 | &packet[36..packet.len() - 32], 122 | &expected_packet[36..expected_packet.len() - 32], 123 | "outcoming packet length is wrong" 124 | ); 125 | 126 | // check packet decoding to original buffer 127 | // swap aes params 128 | let mut new_aes_params = [0u8; 160]; 129 | new_aes_params[..32].copy_from_slice(&aes_params[32..64]); 130 | new_aes_params[32..64].copy_from_slice(&aes_params[..32]); 131 | new_aes_params[64..80].copy_from_slice(&aes_params[80..96]); 132 | new_aes_params[80..96].copy_from_slice(&aes_params[64..80]); 133 | new_aes_params[96..160].copy_from_slice(&aes_params[96..160]); 134 | let mut codec = AdnlCodec::client(&new_aes_params.into()); 135 | test_recv(&mut codec, packet.into(), buffer); 136 | } 137 | 138 | #[test] 139 | fn test_recv_1() { 140 | let encrypted_data = hex::decode("81e95e433c87c9ad2a716637b3a12644fbfb12dbd02996abc40ed2beb352483d6ecf9e2ad181a5abde4d4146ca3a8524739d3acebb2d7599cc6b81967692a62118997e16").unwrap(); 141 | let expected_data = Vec::new(); 142 | let aes_params = hex::decode("b3d529e34b839a521518447b68343aebaae9314ac95aaacfdb687a2163d1a98638db306b63409ef7bc906b4c9dc115488cf90dfa964f520542c69e1a4a495edf9ae9ee72023203c8b266d552f251e8d724929733428c8e276ab3bd6291367336a6ab8dc3d36243419bd0b742f76691a5dec14edbd50f7c1b58ec961ae45be58cbf6623f3ec9705bd5d227761ec79cee377e2566ff668f863552bddfd6ff3a16b").unwrap(); 143 | let aes_params: [u8; 160] = aes_params.as_slice().try_into().unwrap(); 144 | let mut codec = AdnlCodec::client(&aes_params.into()); 145 | test_recv(&mut codec, encrypted_data, expected_data); 146 | let encrypted_data = hex::decode("4b72a32bf31894cce9ceffd2dd97176e502946524e45e62689bd8c5d31ad53603c5fd3b402771f707cd2747747fad9df52e6c23ceec9fa2ee5b0f68b61c33c7790db03d1c593798a29d716505cea75acdf0e031c25447c55c4d29d32caab29bd5a0787644843bafc04160c92140aab0ecc990927").unwrap(); 147 | let expected_data = hex::decode("1684ac0f71ff48e9b263959b17a04faae4a23501380d2aa932b09eac6f9846fcbae9bbcb080d0053e9a3ac3062000000").unwrap(); 148 | test_recv(&mut codec, encrypted_data, expected_data); 149 | } 150 | 151 | #[test] 152 | fn test_recv_2() { 153 | let encrypted_data = hex::decode("b75dcf27582beb4031d6d3700c9b7925bf84a78f2bd16b186484d36427a8824ac86e27cea81eb5bcbac447a37269845c65be51babd11c80627f81b4247f84df16d05c4f1").unwrap(); 154 | let expected_data = Vec::new(); 155 | let aes_params = hex::decode("7e3c66de7c64d4bee4368e69560101991db4b084430a336cffe676c9ac0a795d8c98367309422a8e927e62ed657ba3eaeeb6acd3bbe5564057dfd1d60609a25a48963cbb7d14acf4fc83ec59254673bc85be22d04e80e7b83c641d37cae6e1d82a400bf159490bbc0048e69234ad89e999d792eefdaa56734202546d9188706e95e1272267206a8e7ee1f7c077f76bd26e494972e34d72e257bf20364dbf39b0").unwrap(); 156 | let aes_params: [u8; 160] = aes_params.as_slice().try_into().unwrap(); 157 | let mut codec = AdnlCodec::client(&aes_params.into()); 158 | test_recv(&mut codec, encrypted_data, expected_data); 159 | let encrypted_data = hex::decode("77ebea5a6e6c8758e7703d889abad16e7e3c4e0c10c4e81ca10d0d9abddabb6f008905133a070ff825ad3f4b0ae969e04dbd8b280864d3d2175f3bc7cf3deb31de5497fa43997d8e2acafb9a31de2a22ecb279b5854c00791216e39c2e65863539d82716fc020e9647b2dd99d0f14e4f553b645f").unwrap(); 160 | let expected_data = hex::decode("1684ac0f7bcae111ea0e56457826b1aec7f0f59b9b6579678b3db3839d17b63eb60174f2080d0053e90bb03062000000").unwrap(); 161 | test_recv(&mut codec, encrypted_data, expected_data); 162 | } 163 | 164 | fn test_recv(codec: &mut AdnlCodec, encrypted_packet: Vec, expected_data: Vec) { 165 | let data = codec 166 | .decode(&mut encrypted_packet.as_slice().into()) 167 | .expect("decoding must be correct") 168 | .expect("input must contain full packet"); 169 | assert_eq!(data, expected_data.as_slice(), "incoming packet is wrong"); 170 | } 171 | 172 | #[tokio::test] 173 | async fn integrity_test() { 174 | let keypair = KeyPair::generate(&mut OsRng); 175 | let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); 176 | let port = listener.local_addr().unwrap().port(); 177 | let server_public = keypair.public_key; 178 | tokio::spawn(async move { 179 | loop { 180 | let (socket, _) = listener.accept().await.unwrap(); 181 | let keypair = keypair.clone(); 182 | tokio::spawn(async move { 183 | let mut adnl_server = AdnlPeer::handle_handshake(socket, |_| Some(keypair)) 184 | .await 185 | .expect("handshake failed"); 186 | while let Some(Ok(packet)) = adnl_server.next().await { 187 | let _ = adnl_server.send(packet).await; 188 | } 189 | }); 190 | } 191 | }); 192 | 193 | // act as a client: connect to ADNL server and perform handshake 194 | let mut client = AdnlPeer::connect(server_public.as_bytes(), ("127.0.0.1", port)) 195 | .await 196 | .expect("adnl connect"); 197 | 198 | // send over ADNL 199 | client.send("hello".as_bytes().into()).await.expect("send"); 200 | 201 | // receive result 202 | let result = client 203 | .next() 204 | .await 205 | .expect("packet must be received") 206 | .expect("packet must be decoded properly"); 207 | 208 | assert_eq!(result, "hello".as_bytes()); 209 | } 210 | -------------------------------------------------------------------------------- /src/wrappers/builder.rs: -------------------------------------------------------------------------------- 1 | use crate::crypto::{KeyPair, PublicKey}; 2 | 3 | use crate::{AdnlAddress, AdnlAesParams, AdnlHandshake}; 4 | 5 | use crate::helper_types::CryptoRandom; 6 | 7 | /// Builder of [`AdnlHandshake`] structure, which then can be transformed into [`crate::AdnlClient`] 8 | pub struct AdnlBuilder { 9 | aes_params: AdnlAesParams, 10 | } 11 | 12 | impl AdnlBuilder { 13 | /// Use specified session parameters. It is recommended to use random parameters. 14 | pub fn with_static_aes_params(aes_params: AdnlAesParams) -> Self { 15 | Self { aes_params } 16 | } 17 | 18 | /// Use random session parameters (recommended). 19 | pub fn with_random_aes_params(rng: &mut R) -> Self { 20 | Self { 21 | aes_params: { 22 | let mut buffer = [0u8; 160]; 23 | rng.fill_bytes(&mut buffer); 24 | AdnlAesParams::from(buffer) 25 | }, 26 | } 27 | } 28 | 29 | /// Specify sender, receiver, and secret on which they already agreed. 30 | pub fn use_static_ecdh( 31 | self, 32 | sender_public: PublicKey, 33 | receiver_address: AdnlAddress, 34 | ecdh_secret: [u8; 32], 35 | ) -> AdnlHandshake { 36 | AdnlHandshake::new( 37 | receiver_address, 38 | sender_public, 39 | ecdh_secret, 40 | self.aes_params, 41 | ) 42 | } 43 | 44 | /// Perform key agreement using sender private key and receiver public 45 | pub fn perform_ecdh( 46 | self, 47 | sender_keypair: &KeyPair, 48 | receiver_public: &PublicKey, 49 | ) -> AdnlHandshake { 50 | AdnlHandshake::new( 51 | AdnlAddress::from(receiver_public), 52 | sender_keypair.public_key, 53 | sender_keypair 54 | .secret_key 55 | .compute_shared_secret(receiver_public), 56 | self.aes_params, 57 | ) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/wrappers/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod builder; 2 | pub mod peer; 3 | -------------------------------------------------------------------------------- /src/wrappers/peer.rs: -------------------------------------------------------------------------------- 1 | use std::pin::Pin; 2 | use std::task::{Context, Poll}; 3 | 4 | use crate::crypto::{KeyPair, PublicKey}; 5 | use crate::helper_types::AdnlConnectionInfo; 6 | use crate::{AdnlAddress, AdnlBuilder, AdnlError, AdnlHandshake}; 7 | use futures::{Sink, SinkExt, Stream, StreamExt}; 8 | use pin_project::pin_project; 9 | use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; 10 | use tokio::net::{TcpStream, ToSocketAddrs}; 11 | use tokio_util::bytes::Bytes; 12 | use tokio_util::codec::{Decoder, Framed}; 13 | 14 | use crate::primitives::codec::AdnlCodec; 15 | 16 | /// Abstraction over [`AdnlSender`] and [`AdnlReceiver`] to keep things simple 17 | #[pin_project] 18 | pub struct AdnlPeer 19 | where 20 | T: AsyncRead + AsyncWrite, 21 | { 22 | #[pin] 23 | stream: Framed, 24 | connection_info: AdnlConnectionInfo, 25 | } 26 | 27 | impl AdnlPeer { 28 | /// Connect ADNL client to specified server over [`TcpStream`] 29 | pub async fn connect( 30 | server_public: impl AsRef<[u8]>, 31 | server_address: A, 32 | ) -> Result, AdnlError> { 33 | let transport = TcpStream::connect(server_address).await?; 34 | let client = Self::perform_handshake(transport, server_public).await?; 35 | Ok(client) 36 | } 37 | } 38 | 39 | impl AdnlPeer { 40 | /// Act as a client: perform handshake built from random client keys and `remote_public` over `transport` and check that handshake was successful. 41 | /// That is a simple version of `perform_custom_handshake`, which uses random protocol parameters. 42 | /// Returns client part of ADNL connection 43 | pub async fn perform_handshake( 44 | transport: T, 45 | remote_public: impl AsRef<[u8]>, 46 | ) -> Result { 47 | let local_keypair = KeyPair::generate(&mut rand::rngs::OsRng); 48 | let remote_public = remote_public 49 | .as_ref() 50 | .try_into() 51 | .ok() 52 | .and_then(PublicKey::from_bytes) 53 | .ok_or(AdnlError::InvalidPublicKey)?; 54 | let handshake = AdnlBuilder::with_random_aes_params(&mut rand::rngs::OsRng) 55 | .perform_ecdh(&local_keypair, &remote_public); 56 | Self::perform_custom_handshake(transport, &handshake).await 57 | } 58 | 59 | /// Act as a client: send `handshake` over `transport` and check that handshake was successful 60 | /// Returns client part of ADNL connection 61 | pub async fn perform_custom_handshake( 62 | mut transport: T, 63 | handshake: &AdnlHandshake, 64 | ) -> Result { 65 | // send handshake 66 | transport 67 | .write_all(&handshake.to_bytes()) 68 | .await 69 | .map_err(AdnlError::IoError)?; 70 | 71 | let mut stream = handshake.make_client_codec().framed(transport); 72 | 73 | // receive empty message to ensure that server knows our AES keys 74 | if let Some(x) = stream.next().await { 75 | x?; 76 | let connection_info = 77 | AdnlConnectionInfo::new(handshake.sender().into(), handshake.receiver().clone()); 78 | Ok(Self { 79 | stream, 80 | connection_info, 81 | }) 82 | } else { 83 | Err(AdnlError::EndOfStream) 84 | } 85 | } 86 | 87 | /// Act as a server: receive handshake over transport using [`KeyPair`] provided by `keypair_selector`. 88 | pub async fn handle_handshake Option>( 89 | mut transport: T, 90 | keypair_selector: F, 91 | ) -> Result { 92 | // receive handshake 93 | let mut packet = [0u8; 256]; 94 | transport 95 | .read_exact(&mut packet) 96 | .await 97 | .map_err(AdnlError::IoError)?; 98 | let handshake = AdnlHandshake::decrypt_from_raw(&packet, keypair_selector)?; 99 | let connection_info = 100 | AdnlConnectionInfo::new(handshake.receiver().clone(), handshake.sender().into()); 101 | 102 | let mut server = Self { 103 | stream: handshake.make_server_codec().framed(transport), 104 | connection_info, 105 | }; 106 | 107 | // send empty packet to proof knowledge of AES keys 108 | server.send(Bytes::new()).await?; 109 | 110 | Ok(server) 111 | } 112 | } 113 | 114 | impl Stream for AdnlPeer 115 | where 116 | T: AsyncRead + AsyncWrite, 117 | { 118 | type Item = Result; 119 | 120 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 121 | self.project().stream.poll_next(cx) 122 | } 123 | } 124 | 125 | impl Sink for AdnlPeer 126 | where 127 | T: AsyncWrite + AsyncRead, 128 | { 129 | type Error = AdnlError; 130 | 131 | fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 132 | self.project().stream.poll_ready(cx) 133 | } 134 | 135 | fn start_send(self: Pin<&mut Self>, item: Bytes) -> Result<(), Self::Error> { 136 | self.project().stream.start_send(item) 137 | } 138 | 139 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 140 | self.project().stream.poll_flush(cx) 141 | } 142 | 143 | fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 144 | self.project().stream.poll_close(cx) 145 | } 146 | } 147 | --------------------------------------------------------------------------------