What
- ⚠️ Currently experimental, non-audited / reviewed
- sans-io, no_std, no_alloc, non-monolithic & no arenas Core
- Minimal / no dependency
- De-coupled layers crypto, record, builder, server/client contextes.
- Bring your own crypto or use one of the providers - no defaults
- Ability to build protcol analyzers / your own suite using bits from
- Async optional & does not require Send+Sync
- Trait based callbacks over storing state
- Enum static dispatch over dynamic dispatch
- Atomics free
- TLS 1.3 Only (for now)
- ECDHE no-PSK only (for now)
Todo Plan
- Client Context
- QUIC
- More integrations: Tokio, Yaws blueprint
- More crypto processors
- DTLS
Where
The main monorepo holding all the crates is here: https://github.com/yolotls/yolotls
With this documentation being here: https://github.com/yolotls/book
Rendered into here: https://yolotls.github.io/book/
And discord being here: https://discord.gg/rXVsmzhaZa
Context
Typiclally people either use the integrations which in turn use these crates:
| Crate | no_std | no-alloc | Description |
|---|---|---|---|
| ytls-server | ✅ | ✅ | Server Context |
| ytls-client | WIP | WIP | WIP Client Context |
Protocol
Which then use these crates to tie up the protocol context together:
| Crate | no_std | no-alloc | Description |
|---|---|---|---|
| ytls-record | ✅ | ✅ | Record layer parser & builder |
| ytls-traits | ✅ | ✅ | Traits used to de-couple |
| ytls-extensions | ✅ | ✅ | TLS Extensions |
| ytls-typed | ✅ | ✅ | Rich protocol types |
| ytls-keys | ✅ | ✅ | Keying & schedule operations |
| ytls-util | ✅ | ✅ | Utilities used |
Crypto
And where user brings their own crypto or uses one of the processors:
| Crate | no_std | no-alloc | Description |
|---|---|---|---|
| ytls-rustcrypto | ✅ | ✅ | Rustcrypto primitives |
Traits
Traits are the ❤️ of everything
In order to provide non-monolithic de-coupling, traits are everywhere.
Builder traits
Builder traits are involved in the record generation and provides callbacks to provide data upon construction before they are pushed into the wire
You can find them here: traits/src/t_builder.rs
| Trait | Description |
|---|---|
| HandshakeBuilder | Construct handshakes e.g. ServerHello |
| WrappedApplicationBuilder | For the user (application) traffic |
| WrappedHandshakeBuilder | Wrapped (protected) handshakes |
| Server[..]Builder | Server protected handshake messages |
Cryptography traits
Found here: traits/src/t_crypto.rs
Example impl: crypto/rustcrypto
Context usage example: server/examples/listener.rs
Processor config example: crypto/rustcrypto/src/lib.rs
Validation through trait impl. validation/crypto
| Trait | Description |
|---|---|
| CryptoConfig | Crypto processor implements for end-user to BYOC |
| CryptoSignerP[..]Processor | Signature processors |
| Crypto[..]HmacProcessor | HMAC Processors |
| Crypto[..]HkdfExtractProcessor | HKDF Extract Processors |
| Crypto[..]HkdfGenProcessor | HKDF Expand Processors |
| CryptoX25519Processor | ECDHE X25519 Processor |
| Crypto[..]TranscriptProcessor | Transcript hashing Processors |
| CryptoChaCha20Poly1305Processor | ChaCha20Poly1305 AEAD Processor |
Context
Context is the stateful TLS context that is wound forward with data providing the protocol state machine.
Found here: traits/src/t_ctx.rs
Example impls: server/src
These are implemented in the contextes but they are de-coupled.
| Trait | Description |
|---|---|
| CtxHandshakeProcessor | Implement to provide processing for handshake |
| CtxApplicationProcessor | Implement to provide processing for application traffic |
Input / Output
We have Left and Right sides for I/O where typically the Left is the Ciphertext Network I/O side and the right being side being the Application I/O for Cleartext.
The traits can be found here: traits/src/t_io.rs
Example naive I/O impl found here: server/examples/listener.rs
The I/O reflects how Left/Right also works in yaws.
To "hook" I/O or integration with the given I/O traits the following traits are implemented somewhere the I/O is concerned.
| Trait | Description |
|---|---|
| TlsLeftOut | Left (Ciphertext) or "Network" I/O egress side |
| TlsLeftIn | same as TlsLeftOut but Network ingress |
| TlsRight | Right (Cleartext) or "Application" I/O side |
Keying
Traits for keys provides key / schedule operations.
Found from here: traits/src/t_keys.rs
Implemented here for TLS 1.3: keys/src/tls13_key_schedule.rs
| Trait | Description |
|---|---|
| SecretStore | Used to store derived Application traffic keys |
| Tls13KeySchedule[..] | Key schedule operation states |
Parsing
Found here: [traits/src/t_parser.rs]
| Trait | Description |
|---|---|
| ClientHelloProcessor | Client Hello parsing |
Context
Context is the state machine that I/O "drivers" spin forward, typically with the given application & network I/O.
You might want to use these contextes when there is no driver for your environment e.g. in bare metal embedded environment that doesn't f.ex. use embassy or in a wasm environment with custom runtime etc.
The contextes are split between two depending on which end we are providing a state for: Server and Client.
Another example of these state machines can be found from yaws architecture these contextes closely align to.
Server
Server context is driven with the chosen crypto processor and implementing the required configuration trait.
A combined non-detached example is provided here: server/examples/listener.rs
You can also implement the handshake and application contextes separately providing an opportunity to free the memory from handshake context.
Client
WIP
Crypto
CryptoConfig (trait) is provided for:
- Crypto processor (e.g. rustcrypto) to implement configured initialization
- End-user to bring their desired crypto through the CryptoConfig
No context makes the choice of crypto for the end-user by default.
End-user can even implement their own crypto configuration.
Validation of all the crypto processors are done through trait impl.
RustCrypto
Rustcrypto is an example crypto config & processor: crypto/rustcrypto
It provides no_std & no_alloc cryptography that we provide in our examples through it's implementation of CryptoConfig.
You can find rustcrypto primitives from here: https://github.com/rustcrypto/
In addition we use curve25519-dalek which can be foiund from here: https://github.com/dalek-cryptography/curve25519-dalek
Record
Record layer provides both the parsing and building of TLS record layer.
Instead of using allocating types we use "worst case" heapless static arrays that get initialized to the needed point upon building.
This means that if the configured size of the worst case is lower than what for example other end provides may result into error.
To ensure records are not overflown we can use maximum record size to tell the other side what is the maximum used record size what we use.
Builder uses callbacks to fill the data during encoding and parsing also uses callbacks to extract the required information for the downstream layer typiclaly.