Skip to content

Commit

Permalink
Move from complex traits to direct everscale-crypto usage
Browse files Browse the repository at this point in the history
  • Loading branch information
hacker-volodya committed Aug 21, 2024
1 parent 5295e90 commit eb8fc80
Show file tree
Hide file tree
Showing 15 changed files with 307 additions and 329 deletions.
15 changes: 8 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,25 @@ ctr = "0.9.1"
aes = "0.8.1"
log = "0.4.14"
rand_core = "0.6.3"
x25519-dalek = { version = "2", features = ["static_secrets"], optional = true }
curve25519-dalek = { version = "4", optional = true }
tokio = { version = "1", features = ["net", "io-util"] }
tokio-util = { version = "0.7.10", features = ["codec"] }
thiserror = "1"
rand = "0.8.5"
futures = "0.3"
pin-project = "1"
hex = "0.4.3"
everscale-crypto = "0.2.1"

[dev-dependencies]
hex = "0.4.3"
tokio = { version = "1.36", features = ["rt-multi-thread", "macros"]}
base64 = "0.13.0"

[features]
default = ["dalek"]
dalek = ["x25519-dalek", "curve25519-dalek"]
base64 = "0.22.1"

[[example]]
name = "time"

[[example]]
name = "echo_client"

[[example]]
name = "echo_server"
37 changes: 12 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,31 @@

Minimal client-server ADNL implementation in Rust. 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 |
| async | ✅ Implemented |
| ed25519 libs | curve25519_dalek + x25519_dalek |

## Quickstart
Run this example: `cargo run --example time`

```rust
use adnl::AdnlPeer;
use anyhow::{anyhow, Context, Result};
use std::net::SocketAddrV4;
use base64::Engine as _;
use futures::{SinkExt, StreamExt};
use std::error::Error;

#[tokio::main]
async fn main() -> Result<()> {
async fn main() -> Result<(), Box<dyn Error>> {
// 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"))?;
let remote_public = base64::engine::general_purpose::STANDARD.decode("n4VDnSCUuSpjnCyUk9e3QOOd6o0ItSWYbTnW3Wnn8wk=")?;

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, "5.9.10.47:19949").await?;

// already serialized TL with gettime query
let mut query = hex::decode("7af98bb435263e6c95d6fecb497dfd0aa5f031e7d412986b5ce720496db512052e8f2d100cdf068c7904345aad16000000000000")?;
// already serialized TL with getTime query
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
18 changes: 8 additions & 10 deletions examples/echo_client.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use adnl::{AdnlPeer, AdnlRawPublicKey};
use adnl::AdnlPeer;
use futures::{SinkExt, StreamExt};
use std::{env, error::Error};

Expand All @@ -8,21 +8,19 @@ async fn main() -> Result<(), Box<dyn Error>> {
.nth(1)
.unwrap_or_else(|| "127.0.0.1:8080".to_string());

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

let remote_public = AdnlRawPublicKey::try_from(&*hex::decode(public_key_hex)?)?;
let public_key_hex = env::args().nth(2).unwrap_or_else(|| {
"691a14528fb2911839649c489cb4cbec1f4aa126c244c0ea2ac294eb568a7037".to_string()
});

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

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

// receive result
let result = client.next().await.expect("packet must be received")?;
let result = client.next().await.ok_or("packet must be received")??;

println!("received: {}", String::from_utf8(result.to_vec()).unwrap());
println!("received: {}", String::from_utf8(result.to_vec())?);
Ok(())
}
28 changes: 19 additions & 9 deletions examples/echo_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

use std::{env, error::Error};

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

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
Expand All @@ -17,9 +17,11 @@ 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(|_| "f0971651aec4bb0d65ec3861c597687fda9c1e7d2ee8a93acb9a131aa9f3aee7".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);
let keypair = KeyPair::from(&SecretKey::from_bytes(private_key_bytes));

// Next up we create a TCP listener which will listen for incoming
// connections. This TCP listener is bound to the address we determined
Expand All @@ -28,8 +30,14 @@ 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().edwards_repr()));
println!("Address is: {}", hex::encode(private_key.public().address().as_bytes()));
println!(
"Public key is: {}",
hex::encode(keypair.public_key.as_bytes())
);
println!(
"Address is: {}",
hex::encode(AdnlAddress::from(&keypair.public_key).as_bytes())
);

loop {
// Asynchronously wait for an inbound socket.
Expand All @@ -43,15 +51,17 @@ async fn main() -> Result<(), Box<dyn Error>> {
// Essentially here we're executing a new task to run concurrently,
// which will allow all of our clients to be processed concurrently.

let private_key = private_key.clone();
let private_key = keypair.clone();
tokio::spawn(async move {
// ADNL: handle handshake
let mut adnl_server = AdnlPeer::handle_handshake(socket, |_| Some(private_key.clone())).await.expect("handshake failed");
let mut adnl_server = AdnlPeer::handle_handshake(socket, |_| Some(private_key.clone()))
.await
.expect("handshake failed");

// In a loop, read data from the socket and write the data back.
while let Some(Ok(packet)) = adnl_server.next().await {
let _ = adnl_server.send(packet).await;
}
});
}
}
}
14 changes: 7 additions & 7 deletions examples/time.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
use adnl::{AdnlPeer, AdnlRawPublicKey};
use adnl::AdnlPeer;
use base64::Engine as _;
use futures::{SinkExt, StreamExt};
use std::{error::Error, net::SocketAddrV4};
use std::error::Error;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// decode liteserver public key
let remote_public = AdnlRawPublicKey::try_from(&*base64::decode("JhXt7H1dZTgxQTIyGiYV4f9VUARuDxFl/1kVBjLSMB8=")?)?;
let remote_public = base64::engine::general_purpose::STANDARD
.decode("n4VDnSCUuSpjnCyUk9e3QOOd6o0ItSWYbTnW3Wnn8wk=")?;

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, "5.9.10.47:19949").await?;

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

// send over ADNL
Expand Down
104 changes: 23 additions & 81 deletions src/helper_types.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::crypto::PublicKey;
use sha2::{Digest, Sha256};
use std::{array::TryFromSliceError, io::Error};
use thiserror::Error;
Expand All @@ -6,66 +7,15 @@ 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.edwards_repr());
AdnlAddress(hasher.finalize().into())
}

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

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

impl AdnlPublicKey for AdnlRawPublicKey {
fn edwards_repr(&self) -> [u8; 32] {
self.0
}
}

impl From<[u8; 32]> for AdnlRawPublicKey {
fn from(value: [u8; 32]) -> Self {
Self(value)
}
}

impl TryFrom<&[u8]> for AdnlRawPublicKey {
type Error = TryFromSliceError;

fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
Ok(Self(value.try_into()?))
}
}

/// Trait which must be implemented to perform key agreement inside [`AdnlHandshake`]
pub trait AdnlPrivateKey {
type PublicKey: AdnlPublicKey;

/// Perform key agreement protocol (usually x25519) between our private key
/// and their public
fn key_agreement<P: AdnlPublicKey>(&self, their_public: &P) -> AdnlSecret;

/// Get public key corresponding to this private
fn public(&self) -> Self::PublicKey;
}

/// Wrapper struct to hold the secret, result of ECDH between peers
#[derive(Clone)]
pub struct AdnlSecret([u8; 32]);

/// Wrapper struct to hold ADNL address, which is a hash of public key
#[derive(PartialEq, Clone)]
pub struct AdnlAddress([u8; 32]);

impl std::fmt::Debug for AdnlAddress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("AdnlAddress").field(&format!("{:02x?}", &self.0)).finish()
f.debug_tuple("AdnlAddress")
.field(&format!("{:02x?}", &self.0))
.finish()
}
}

Expand All @@ -83,6 +33,15 @@ impl TryFrom<&[u8]> for AdnlAddress {
}
}

impl From<&PublicKey> for AdnlAddress {
fn from(value: &PublicKey) -> Self {
let mut hasher = Sha256::new();
hasher.update([0xc6, 0xb4, 0x13, 0x48]); // type id - always ed25519
hasher.update(value.as_bytes());
AdnlAddress(hasher.finalize().into())
}
}

/// Session parameters for AES-CTR encryption of datagrams
#[derive(Clone)]
pub struct AdnlAesParams {
Expand Down Expand Up @@ -135,13 +94,9 @@ impl AdnlAesParams {

/// Generate random session parameters
pub fn random<T: CryptoRandom>(csprng: &mut T) -> Self {
let mut result: AdnlAesParams = Default::default();
csprng.fill_bytes(&mut result.rx_key);
csprng.fill_bytes(&mut result.tx_key);
csprng.fill_bytes(&mut result.rx_nonce);
csprng.fill_bytes(&mut result.tx_nonce);
csprng.fill_bytes(&mut result.padding);
result
let mut result = [0u8; 160];
csprng.fill_bytes(&mut result);
Self::from(result)
}
}

Expand All @@ -157,12 +112,6 @@ impl Default for AdnlAesParams {
}
}

impl From<[u8; 32]> for AdnlSecret {
fn from(secret: [u8; 32]) -> Self {
Self(secret)
}
}

impl AdnlAddress {
#[inline]
pub fn to_bytes(&self) -> [u8; 32] {
Expand All @@ -175,18 +124,6 @@ impl AdnlAddress {
}
}

impl AdnlSecret {
#[inline]
pub fn to_bytes(&self) -> [u8; 32] {
self.0
}

#[inline]
pub fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
}

/// Common error type
#[derive(Debug, Error)]
pub enum AdnlError {
Expand All @@ -202,6 +139,8 @@ pub enum AdnlError {
UnknownAddr(AdnlAddress),
#[error("End of stream")]
EndOfStream,
#[error("Invalid public key")]
InvalidPublicKey,
}

/// Information about connected peers.
Expand All @@ -212,7 +151,10 @@ pub struct AdnlConnectionInfo {

impl AdnlConnectionInfo {
pub fn new(local_address: AdnlAddress, remote_address: AdnlAddress) -> Self {
Self { local_address, remote_address }
Self {
local_address,
remote_address,
}
}

pub fn local_address(&self) -> &AdnlAddress {
Expand All @@ -222,4 +164,4 @@ impl AdnlConnectionInfo {
pub fn remote_address(&self) -> &AdnlAddress {
&self.remote_address
}
}
}
Loading

0 comments on commit eb8fc80

Please sign in to comment.