From ea4074574bcba7c4a9d9f2b80deee658e93a296d Mon Sep 17 00:00:00 2001 From: occheung Date: Sun, 4 Oct 2020 22:22:29 +0800 Subject: [PATCH] first commit --- .gitignore | 1 + .idea/codeStyles/Project.xml | 52 ++++ .idea/dbnavigator.xml | 453 +++++++++++++++++++++++++++++++++++ .idea/misc.xml | 9 + .idea/modules.xml | 8 + .idea/no_std_test.iml | 15 ++ .idea/vcs.xml | 6 + .idea/workspace.xml | 356 +++++++++++++++++++++++++++ Cargo.lock | 442 ++++++++++++++++++++++++++++++++++ Cargo.toml | 33 +++ README.md | 0 src/lib.rs | 4 + src/main.rs | 30 +++ src/tls.rs | 449 ++++++++++++++++++++++++++++++++++ src/tls_packet.rs | 162 +++++++++++++ 15 files changed, 2020 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/dbnavigator.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/no_std_test.iml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 src/tls.rs create mode 100644 src/tls_packet.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..e4901c8 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/dbnavigator.xml b/.idea/dbnavigator.xml new file mode 100644 index 0000000..3337791 --- /dev/null +++ b/.idea/dbnavigator.xml @@ -0,0 +1,453 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..1028666 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..8fda3b5 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/no_std_test.iml b/.idea/no_std_test.iml new file mode 100644 index 0000000..cf16088 --- /dev/null +++ b/.idea/no_std_test.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..c8397c9 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..39543ca --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,356 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ant + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1601521481729 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2a310b8 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,442 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aead" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "aes" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd2bc6d3f370b5666245ff421e231cba4353df936e26986d2918e61a8fd6aef6" +dependencies = [ + "aes-soft", + "aesni", + "block-cipher", +] + +[[package]] +name = "aes-gcm" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0301c9e9c443494d970a07885e8cf3e587bae8356a1d5abd0999068413f7205f" +dependencies = [ + "aead", + "aes", + "block-cipher", + "ghash", + "subtle", +] + +[[package]] +name = "aes-soft" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63dd91889c49327ad7ef3b500fd1109dbd3c509a03db0d4a9ce413b79f575cb6" +dependencies = [ + "block-cipher", + "byteorder", + "opaque-debug", +] + +[[package]] +name = "aesni" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6fe808308bb07d393e2ea47780043ec47683fcf19cf5efc8ca51c50cc8c68a" +dependencies = [ + "block-cipher", + "opaque-debug", +] + +[[package]] +name = "as-slice" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb4d1c23475b74e3672afa8c2be22040b8b7783ad9b461021144ed10a46bb0e6" +dependencies = [ + "generic-array 0.12.3", + "generic-array 0.13.2", + "generic-array 0.14.4", + "stable_deref_trait", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "block-cipher" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f337a3e6da609650eb74e02bc9fac7b735049f7623ab12f2e4c719316fcc7e80" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "chacha20" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "244fbce0d47e97e8ef2f63b81d5e05882cb518c68531eb33194990d7b7e85845" +dependencies = [ + "stream-cipher", + "zeroize", +] + +[[package]] +name = "chacha20poly1305" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bf18d374d66df0c05cdddd528a7db98f78c28e2519b120855c4f84c5027b1f5" +dependencies = [ + "aead", + "chacha20", + "poly1305", + "stream-cipher", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "ghash" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6e27f0689a6e15944bdce7e45425efb87eaa8ab0c6e87f11d0987a9133e2531" +dependencies = [ + "polyval", +] + +[[package]] +name = "hash32" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74911a68a1658cfcfb61bc0ccfbd536e3b6e906f8c2f7883ee50157e3e2184f1" +dependencies = [ + "as-slice", + "generic-array 0.13.2", + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "libc" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa7087f49d294270db4e1928fc110c976cd4b9e5a16348e0a1df09afa99e6c98" + +[[package]] +name = "managed" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577" + +[[package]] +name = "num_enum" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066" +dependencies = [ + "derivative", + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "poly1305" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ce46de8e53ee414ca4d02bfefac75d8c12fba948b76622a40b4be34dfce980" +dependencies = [ + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5884790f1ce3553ad55fec37b5aaac5882e0e845a2612df744d6c85c9bf046c" +dependencies = [ + "cfg-if", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "serde" +version = "1.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" + +[[package]] +name = "smoltcp" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a" +dependencies = [ + "bitflags", + "byteorder", + "managed", +] + +[[package]] +name = "smoltcp-tls" +version = "0.1.0" +dependencies = [ + "aes-gcm", + "byteorder", + "chacha20poly1305", + "heapless", + "num_enum", + "rand", + "rand_chacha", + "smoltcp", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stream-cipher" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c80e15f898d8d8f25db24c253ea615cc14acf418ff307822995814e7d42cfa89" +dependencies = [ + "block-cipher", + "generic-array 0.14.4", +] + +[[package]] +name = "subtle" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd" + +[[package]] +name = "syn" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c51d92969d209b54a98397e1b91c8ae82d8c87a7bb87df0b29aa2ad81454228" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "toml" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +dependencies = [ + "serde", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "universal-hash" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" +dependencies = [ + "generic-array 0.14.4", + "subtle", +] + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "zeroize" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f33972566adbd2d3588b0491eb94b98b43695c4ef897903470ede4f3f5a28a" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..67e33b3 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "smoltcp-tls" +version = "0.1.0" +authors = ["occheung "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +aes-gcm = "0.7.0" +chacha20poly1305 = "0.6.0" +byteorder = "1.3.4" +num_enum = "0.5.1" + +[dependencies.smoltcp] +version = "0.6.0" +default-features = false +features = ["proto-ipv4", "proto-ipv6", "socket-tcp"] + +[dependencies.rand_chacha] +version = "0.2.2" +default-features = false +features = [] + +[dependencies.rand] +version = "0.7.3" +default-features = false +features = ["getrandom"] + +[dependencies.heapless] +version = "0.5.6" +default-features = false +features = [] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..41521e7 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,4 @@ +#![no_std] + +pub mod tls; +pub mod tls_packet; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..06686bd --- /dev/null +++ b/src/main.rs @@ -0,0 +1,30 @@ +use smoltcp_tls::tls::TlsSocket; +use smoltcp::socket::TcpSocketBuffer; +use smoltcp::socket::SocketSet; +use smoltcp::wire::Ipv4Address; + +fn main() { + let mut socket_set_entries: [_; 8] = Default::default(); + let mut sockets = SocketSet::new(&mut socket_set_entries[..]); + + let mut tx_storage = [0; 4096]; + let mut rx_storage = [0; 4096]; + + let mut tls_socket = { + let tx_buffer = TcpSocketBuffer::new(&mut tx_storage[..]); + let rx_buffer = TcpSocketBuffer::new(&mut rx_storage[..]); + TlsSocket::new( + &mut sockets, + rx_buffer, + tx_buffer + ) + }; + + tls_socket.tcp_connect( + &mut sockets, + (Ipv4Address::new(192, 168, 1, 125), 1883), + 49600 + ).unwrap(); + + tls_socket.tls_connect(&mut sockets).unwrap(); +} \ No newline at end of file diff --git a/src/tls.rs b/src/tls.rs new file mode 100644 index 0000000..5b197b7 --- /dev/null +++ b/src/tls.rs @@ -0,0 +1,449 @@ +use smoltcp::socket::TcpSocket; +use smoltcp::socket::TcpState; +use smoltcp::socket::Socket; +use smoltcp::socket::AnySocket; +use smoltcp::socket::SocketRef; +use smoltcp::socket::SocketHandle; +use smoltcp::socket::SocketSet; +use smoltcp::socket::TcpSocketBuffer; +use smoltcp::wire::Ipv4Address; +use smoltcp::wire::IpEndpoint; +use smoltcp::Result; +use smoltcp::Error; + +use byteorder::{ByteOrder, NetworkEndian, BigEndian, WriteBytesExt}; + +use rand::prelude::*; +use rand_chacha::ChaCha20Rng; + +use heapless::Vec; +use heapless::consts::*; + +use core::convert::TryInto; +use core::convert::TryFrom; + +use crate::tls_packet::*; +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[allow(non_camel_case_types)] +enum TlsState { + START, + WAIT_SH, + WAIT_EE, + WAIT_CERT_CR, + WAIT_CERT, + WAIT_CV, + WAIT_FINISHED, + CONNECTED, +} + +pub struct TlsSocket +{ + state: TlsState, + tcp_handle: SocketHandle, + random: ChaCha20Rng, +} + +impl TlsSocket { + pub fn new<'a, 'b, 'c>( + sockets: &mut SocketSet<'a, 'b, 'c>, + rx_buffer: TcpSocketBuffer<'b>, + tx_buffer: TcpSocketBuffer<'b>, + ) -> Self + where + 'b: 'c, + { + let tcp_socket = TcpSocket::new(rx_buffer, tx_buffer); + let tcp_handle = sockets.add(tcp_socket); + TlsSocket { + state: TlsState::START, + tcp_handle, + random: ChaCha20Rng::from_entropy(), + } + } + + pub fn tcp_connect( + &mut self, + sockets: &mut SocketSet, + remote_endpoint: T, + local_endpoint: U, + ) -> Result<()> + where + T: Into, + U: Into, + { + let mut tcp_socket = sockets.get::(self.tcp_handle); + tcp_socket.connect(remote_endpoint, local_endpoint) + } + + pub fn tls_connect(&mut self, sockets: &mut SocketSet) -> Result { + // Check tcp_socket connectivity + { + let tcp_socket = sockets.get::(self.tcp_handle); + if tcp_socket.state() != TcpState::Established { + return Ok(false); + } + } + + if self.state == TlsState::START { + // Create TLS representation, length and payload not finalised + let mut random: [u8; 32] = [0; 32]; + let mut session_id: [u8; 32] = [0; 32]; + self.random.fill_bytes(&mut random); + self.random.fill_bytes(&mut session_id); + + let cipher_suites_length = 3; + let cipher_suites = [ + CipherSuite::TLS_AES_128_GCM_SHA256, + CipherSuite::TLS_AES_256_GCM_SHA384, + CipherSuite::TLS_CHACHA20_POLY1305_SHA256, + ]; + + // Length: to be determined + let supported_versions_extension = Extension { + extension_type: ExtensionType::SupportedVersions, + length: 3, + extension_data: &[ + 2, // Number of supported versions * 2 + // Need 2 bytes to contain a version + 0x03, 0x04 // 0x0303: TLS Version 1.3 + ] + }; + + let client_hello = ClientHello { + version: TlsVersion::Tls12, + random, + session_id_length: 32, + session_id, + cipher_suites_length, + cipher_suites: &cipher_suites, + compression_method_length: 1, + compression_methods: 0, + extension_length: supported_versions_extension.get_length(), + extensions: &[supported_versions_extension], + }; + + let handshake_repr = HandshakeRepr { + msg_type: HandshakeType::ClientHello, + length: client_hello.get_length(), + handshake_data: HandshakeData::ClientHello(client_hello), + }; + + let repr = TlsRepr { + content_type: TlsContentType::Handshake, + version: TlsVersion::Tls13, + length: 0, + payload: None, + handshake: Some(handshake_repr), + }; + + self.send_tls_repr(sockets, repr)?; + self.state = TlsState::WAIT_SH; + Ok(true) + } else if self.state == TlsState::WAIT_SH { + Ok(true) + } else { + Ok(true) + } + } + + // Generic inner send method, through TCP socket + fn send_tls_repr(&mut self, sockets: &mut SocketSet, tls_repr: TlsRepr) -> Result<()> { + let mut tcp_socket = sockets.get::(self.tcp_handle); + let mut array = [0; 2048]; + let mut buffer = TlsBuffer::new(&mut array); + buffer.enqueue_tls_repr(tls_repr)?; + let buffer_size = buffer.index.clone(); + tcp_socket.send_slice(buffer.into()) + .and_then( + |size| if size == buffer_size.into_inner() { + Ok(()) + } else { + Err(Error::Truncated) + } + ) + } + + // Generic inner recv method, through TCP socket + fn recv_tls_repr<'a>(&'a mut self, sockets: &mut SocketSet, byte_array: &'a mut [u8]) -> Result> { + let mut tcp_socket = sockets.get::(self.tcp_handle); + let size = tcp_socket.recv_slice(byte_array)?; + let buffer = TlsBuffer::new(&mut byte_array[..size]); + buffer.dequeue_tls_repr() + } +} + +// Only designed to support read or write the entire buffer +pub(crate) struct TlsBuffer<'a> { + buffer: &'a mut [u8], + index: core::cell::RefCell, +} + +impl<'a> Into<&'a [u8]> for TlsBuffer<'a> { + fn into(self) -> &'a [u8] { + &self.buffer[0..self.index.into_inner()] + } +} + +impl<'a> TlsBuffer<'a> { + pub(crate) fn new(buffer: &'a mut [u8]) -> Self { + Self { + buffer, + index: core::cell::RefCell::new(0), + } + } + + pub(crate) fn write(&mut self, data: &[u8]) -> Result<()> { + let mut index = self.index.borrow_mut(); + if (self.buffer.len() - *index) < data.len() { + return Err(Error::Exhausted); + } + let next_index = *index + data.len(); + self.buffer[*index..next_index].copy_from_slice(data); + *index = next_index; + Ok(()) + } + + pub(crate) fn write_u8(&mut self, data: u8) -> Result<()> { + let mut index = self.index.borrow_mut(); + if (self.buffer.len() - *index) < 1 { + return Err(Error::Exhausted); + } + self.buffer[*index] = data; + *index += 1; + Ok(()) + } + + pub(crate) fn read_u8(&mut self) -> Result { + let mut index = self.index.borrow_mut(); + if (self.buffer.len() - *index) < 1 { + return Err(Error::Exhausted); + } + let data = self.buffer[*index]; + *index += 1; + Ok(data) + } + + pub(crate) fn read_all(self) -> &'a [u8] { + &self.buffer[self.index.into_inner()..] + } + + pub(crate) fn read_slice(&self, length: usize) -> Result<&[u8]> { + let mut index = self.index.borrow_mut(); + if (self.buffer.len() - *index) < length { + return Err(Error::Exhausted); + } + let next_index = *index + length; + let slice = &self.buffer[*index..next_index]; + *index = next_index; + Ok(slice) + } + + fn enqueue_tls_repr(&mut self, tls_repr: TlsRepr) -> Result<()> { + self.write_u8(tls_repr.content_type.into())?; + self.write_u16(tls_repr.version.into())?; + self.write_u16(tls_repr.length)?; + if let Some(app_data) = tls_repr.payload { + self.write(app_data)?; + } else if let Some(handshake_repr) = tls_repr.handshake { + // Queue handshake_repr into buffer + self.enqueue_handshake_repr(handshake_repr)?; + } else { + return Err(Error::Malformed); + } + Ok(()) + } + + fn enqueue_handshake_repr(&mut self, handshake_repr: HandshakeRepr) -> Result<()> { + self.write_u8(handshake_repr.msg_type.into())?; + self.write_u24(handshake_repr.length)?; + self.enqueue_handshake_data(handshake_repr.handshake_data) + } + + fn enqueue_handshake_data(&mut self, handshake_data: HandshakeData) -> Result<()> { + match handshake_data { + HandshakeData::ClientHello(client_hello) => { + self.enqueue_client_hello(client_hello) + } + _ => { + Err(Error::Unrecognized) + } + } + } + + fn enqueue_client_hello(&mut self, client_hello: ClientHello) -> Result<()> { + self.write_u16(client_hello.version.into())?; + self.write(&client_hello.random)?; + self.write_u8(client_hello.session_id_length)?; + self.write(&client_hello.session_id)?; + self.write_u16(client_hello.cipher_suites_length)?; + for suite in client_hello.cipher_suites.iter() { + self.write_u16((*suite).into())?; + } + self.write_u8(client_hello.compression_method_length)?; + self.write_u8(client_hello.compression_methods)?; + self.write_u16(client_hello.extension_length)?; + self.enqueue_extensions(client_hello.extensions) + } + + fn enqueue_extensions(&mut self, extensions: &[Extension]) -> Result<()> { + for extension in extensions { + self.write_u16(extension.extension_type.into())?; + self.write_u16(extension.length)?; + self.write(extension.extension_data)?; + } + Ok(()) + } + + fn dequeue_tls_repr<'b>(mut self) -> Result> { + // Create a TLS Representation layer + // Modify the representation along the way + let mut repr = TlsRepr { + content_type: TlsContentType::Invalid, + version: TlsVersion::Tls10, + length: 0, + payload: None, + handshake: None, + }; + + repr.content_type = TlsContentType::try_from(self.read_u8()?) + .map_err(|_| Error::Unrecognized)?; + repr.version = TlsVersion::try_from(self.read_u16()?) + .map_err(|_| Error::Unrecognized)?; + repr.length = self.read_u16()?; + + use TlsContentType::*; + match repr.content_type { + Invalid => Err(Error::Unrecognized), + ChangeCipherSpec | Alert => unimplemented!(), + Handshake => todo!(), + ApplicationData => { + repr.payload = Some(self.read_all()); + Ok(repr) + } + } + } + + fn dequeue_handshake<'b>(mut self) -> Result> { + // Create a Handshake header representation + // Fill in proper value afterwards + let mut repr = HandshakeRepr { + msg_type: HandshakeType::ClientHello, + length: 0, + handshake_data: HandshakeData::Uninitialized, + }; + + repr.msg_type = HandshakeType::try_from(self.read_u8()?) + .map_err(|_| Error::Unrecognized)?; + repr.length = self.read_u24()?; + + use HandshakeType::*; + match repr.msg_type { + ClientHello => unimplemented!(), + ServerHello => todo!(), + _ => unimplemented!(), + } + } + + fn dequeue_server_hello(mut self) -> Result> { + // Create a Server Hello representation + // Fill in proper value afterwards + let mut server_hello = ServerHello { + version: TlsVersion::Tls10, + random: [0; 32], + session_id_echo_length: 0, + session_id_echo: [0; 32], + cipher_suite: CipherSuite::TLS_CHACHA20_POLY1305_SHA256, + compression_method: 0, + extension_length: 0, + extensions: &[], + }; + + server_hello.version = TlsVersion::try_from(self.read_u16()?) + .map_err(|_| Error::Unrecognized)?; + for random_byte in &mut server_hello.random[..] { + *random_byte = self.read_u8()?; + } + server_hello.session_id_echo_length = self.read_u8()?; + for id_byte in &mut server_hello.session_id_echo[ + ..usize::try_from(server_hello.session_id_echo_length) + .map_err(|_| Error::Exhausted)? + ] { + *id_byte = self.read_u8()?; + } + server_hello.cipher_suite = CipherSuite::try_from(self.read_u16()?) + .map_err(|_| Error::Unrecognized)?; + server_hello.compression_method = self.read_u8()?; + server_hello.extension_length = self.read_u16()?; + + let mut remaining_length = server_hello.extension_length; + let mut extension_counter = 0; + let mut extension_vec: Vec = Vec::new(); + while remaining_length != 0 { + extension_vec.push(self.dequeue_extension()?.clone()) + .map_err(|_| Error::Exhausted)?; + // Deduct base length of an extension (ext_type, len) + remaining_length -= 4; + remaining_length -= extension_vec[extension_counter].length; + extension_counter += 1; + } + + Ok(server_hello) + } + + fn dequeue_extension(&self) -> Result> { + // Create an Extension representation + // Fill in proper value afterwards + let mut extension = Extension { + extension_type: ExtensionType::ServerName, + length: 0, + extension_data: &[], + }; + + extension.extension_type = ExtensionType::try_from(self.read_u16()?) + .map_err(|_| Error::Unrecognized)?; + extension.length = self.read_u16()?; + extension.extension_data = self.read_slice( + usize::try_from(extension.length) + .map_err(|_| Error::Exhausted)? + )?; + Ok(extension) + } +} + +macro_rules! export_byte_order_fn { + ($($write_fn_name: ident, $read_fn_name: ident, $data_type: ty, $data_size: literal),+) => { + impl<'a> TlsBuffer<'a> { + $( + pub(crate) fn $write_fn_name(&mut self, data: $data_type) -> Result<()> { + let mut index = self.index.borrow_mut(); + if (self.buffer.len() - *index) < $data_size { + return Err(Error::Exhausted); + } + let next_index = *index + $data_size; + NetworkEndian::$write_fn_name(&mut self.buffer[*index..next_index], data); + *index = next_index; + Ok(()) + } + + pub(crate) fn $read_fn_name(&self) -> Result<$data_type> { + let mut index = self.index.borrow_mut(); + if (self.buffer.len() - *index) < $data_size { + return Err(Error::Exhausted); + } + let next_index = *index + $data_size; + let data = NetworkEndian::$read_fn_name(&self.buffer[*index..next_index]); + *index = next_index; + Ok(data) + } + )+ + } + } +} + +export_byte_order_fn!( + write_u16, read_u16, u16, 2, + write_u24, read_u24, u32, 3, + write_u32, read_u32, u32, 4, + write_u48, read_u48, u64, 6, + write_u64, read_u64, u64, 8 +); diff --git a/src/tls_packet.rs b/src/tls_packet.rs new file mode 100644 index 0000000..fc68776 --- /dev/null +++ b/src/tls_packet.rs @@ -0,0 +1,162 @@ +use byteorder::{ByteOrder, NetworkEndian, BigEndian}; +use num_enum::IntoPrimitive; +use num_enum::TryFromPrimitive; +use core::convert::TryFrom; + +#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)] +#[repr(u8)] +pub(crate) enum TlsContentType { + Invalid = 0, + ChangeCipherSpec = 20, + Alert = 21, + Handshake = 22, + ApplicationData = 23 +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)] +#[repr(u16)] +pub(crate) enum TlsVersion { + Tls10 = 0x0301, + Tls11 = 0x0302, + Tls12 = 0x0303, + Tls13 = 0x0304, +} + +#[derive(Clone, Copy)] +pub(crate) struct TlsRepr<'a, 'b> { + pub(crate) content_type: TlsContentType, + pub(crate) version: TlsVersion, + pub(crate) length: u16, + pub(crate) payload: Option<&'a[u8]>, + pub(crate) handshake: Option> +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)] +#[repr(u8)] +pub(crate) enum HandshakeType { + ClientHello = 1, + ServerHello = 2, + NewSessionTicket = 4, + EndOfEarlyData = 5, + EncryptedExtensions = 8, + Certificate = 11, + CertificateRequest = 13, + CertificateVerify = 15, + Finished = 20, + KeyUpdate = 24, + MessageHash = 254, +} + +#[derive(Clone, Copy)] +pub(crate) struct HandshakeRepr<'a, 'b> { + pub(crate) msg_type: HandshakeType, + pub(crate) length: u32, + pub(crate) handshake_data: HandshakeData<'a, 'b>, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)] +#[repr(u16)] +pub(crate) enum CipherSuite { + TLS_AES_128_GCM_SHA256 = 0x1301, + TLS_AES_256_GCM_SHA384 = 0x1302, + TLS_CHACHA20_POLY1305_SHA256 = 0x1303, + TLS_AES_128_CCM_SHA256 = 0x1304, + TLS_AES_128_CCM_8_SHA256 = 0x1305, +} + +#[derive(Clone, Copy)] +pub(crate) struct ClientHello<'a, 'b> { + pub(crate) version: TlsVersion, // Legacy: Must be Tls12 (0x0303) + pub(crate) random: [u8; 32], + pub(crate) session_id_length: u8, // Legacy: Keep it 32 + pub(crate) session_id: [u8; 32], // Legacy: Fill this with an unpredictable value + pub(crate) cipher_suites_length: u16, + pub(crate) cipher_suites: &'a[CipherSuite], + pub(crate) compression_method_length: u8, // Legacy: Must be 1, to contain a byte + pub(crate) compression_methods: u8, // Legacy: Must be 1 byte of 0 + pub(crate) extension_length: u16, + pub(crate) extensions: &'a[Extension<'b>], +} + +#[derive(Clone, Copy)] +pub(crate) enum HandshakeData<'a, 'b> { + Uninitialized, + ClientHello(ClientHello<'a, 'b>), + ServerHello(ServerHello<'a, 'b>), +} + +impl<'a, 'b> ClientHello<'a, 'b> { + pub(crate) fn get_length(&self) -> u32 { + let mut length :u32 = 2; // TlsVersion size + length += 32; // Random size + length += 1; // Legacy session_id length size + length += 32; // Legacy session_id size + length += 2; // Cipher_suites_length size + length += (self.cipher_suites.len() as u32) * 2; + length += 1; + length += 1; + length += 2; + for extension in self.extensions.iter() { + length += (extension.get_length() as u32); + } + length + } +} + +#[derive(Clone, Copy)] +pub(crate) struct ServerHello<'a, 'b> { + pub(crate) version: TlsVersion, + pub(crate) random: [u8; 32], + pub(crate) session_id_echo_length: u8, + pub(crate) session_id_echo: [u8; 32], + pub(crate) cipher_suite: CipherSuite, + pub(crate) compression_method: u8, // Always 0 + pub(crate) extension_length: u16, + pub(crate) extensions: &'a[Extension<'b>], +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)] +#[repr(u16)] +pub(crate) enum ExtensionType { + ServerName = 0, + MaxFragmentLength = 1, + StatusRequest = 5, + SupportedGroups = 10, + SignatureAlgorithms = 13, + UseSRTP = 14, + Heartbeat = 15, + ApplicationLayerProtocolNegotiation = 16, + SignedCertificateTimestamp = 18, + ClientCertificateType = 19, + ServerCertificateType = 20, + Padding = 21, + PreSharedKey = 41, + EarlyData = 42, + SupportedVersions = 43, + Cookie = 44, + PSKKeyExchangeModes = 45, + CertificateAuthorities = 47, + OIDFilters = 48, + PostHandshakeAuth = 49, + SignatureAlgorithmsCert = 50, + KeyShare = 51, +} + +impl ExtensionType { + pub(crate) fn get_length(&self) -> u16 { + return 2; + } +} + +#[derive(Clone, Copy)] +pub(crate) struct Extension<'a> { + pub(crate) extension_type: ExtensionType, + pub(crate) length: u16, + pub(crate) extension_data: &'a[u8], +} + +impl<'a> Extension<'a> { + pub(crate) fn get_length(&self) -> u16 { + self.extension_type.get_length() + 2 + (self.extension_data.len() as u16) + } +}