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:

Crateno_stdno-allocDescription
ytls-server✅✅Server Context
ytls-clientWIPWIPWIP Client Context

Protocol

Which then use these crates to tie up the protocol context together:

Crateno_stdno-allocDescription
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:

Crateno_stdno-allocDescription
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

TraitDescription
HandshakeBuilderConstruct handshakes e.g. ServerHello
WrappedApplicationBuilderFor the user (application) traffic
WrappedHandshakeBuilderWrapped (protected) handshakes
Server[..]BuilderServer 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

TraitDescription
CryptoConfigCrypto processor implements for end-user to BYOC
CryptoSignerP[..]ProcessorSignature processors
Crypto[..]HmacProcessorHMAC Processors
Crypto[..]HkdfExtractProcessorHKDF Extract Processors
Crypto[..]HkdfGenProcessorHKDF Expand Processors
CryptoX25519ProcessorECDHE X25519 Processor
Crypto[..]TranscriptProcessorTranscript hashing Processors
CryptoChaCha20Poly1305ProcessorChaCha20Poly1305 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.

TraitDescription
CtxHandshakeProcessorImplement to provide processing for handshake
CtxApplicationProcessorImplement 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.

TraitDescription
TlsLeftOutLeft (Ciphertext) or "Network" I/O egress side
TlsLeftInsame as TlsLeftOut but Network ingress
TlsRightRight (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

TraitDescription
SecretStoreUsed to store derived Application traffic keys
Tls13KeySchedule[..]Key schedule operation states

Parsing

Found here: [traits/src/t_parser.rs]

TraitDescription
ClientHelloProcessorClient 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.