Silk

silk/tls_record

Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc.

Bounded TLS 1.3 record protection and partial-byte transport driving.

When to use

Use TlsRecordSender and TlsRecordReceiver after a TLS handshake supplies a traffic secret, or use their plaintext constructors for the initial handshake record framing.

Details

Each direction owns fixed-capacity storage allocated at construction. Later queue, input, acknowledgment, and consumption operations allocate nothing and retain no caller buffer. Protected epochs support the three TLS 1.3 cipher suites admitted by Silk and stop after RECORDS_PER_EPOCH records. A record view borrows its receiver until it is consumed.

Gotchas

Possession of a traffic secret does not authenticate a server identity. This module does not perform a handshake, validate certificates, acquire trust, drive network resources, reserve a client KeyUpdate, or decide close-notify policy. Secret copies are not guaranteed to be erased.

Import as TlsRecordSender with import silk.tls_record { TlsRecordSender }.

Public declarations: 11.

RECORDS_PER_EPOCH

pub const RECORDS_PER_EPOCH: u64

The conservative maximum number of records protected by one traffic epoch.

CipherSuite

pub enum CipherSuite

One cipher suite in Silk's TLS 1.3 profile.

Aes128GcmSha256

Aes128GcmSha256 = 0

TLS_AES_128_GCM_SHA256.

Aes256GcmSha384

Aes256GcmSha384 = 1

TLS_AES_256_GCM_SHA384.

ChaCha20Poly1305Sha256

ChaCha20Poly1305Sha256 = 2

TLS_CHACHA20_POLY1305_SHA256.

ContentType

pub enum ContentType

A TLS record content type exposed after complete framing and authentication.

ChangeCipherSpec

ChangeCipherSpec = 0

Compatibility change_cipher_spec content. It is admitted only by plaintext framing.

Alert

Alert = 1

Exactly one complete two-byte alert.

Handshake

Handshake = 2

A nonempty handshake fragment.

ApplicationData

ApplicationData = 3

Application data, including an empty fragment.

InputDemand

pub enum InputDemand

The next receive-side action after a successful input call.

NeedInput

NeedInput = 0

Supply more bytes for the current record.

RecordReady

RecordReady = 1

Inspect and consume the completed record.

NeedRecordConsumption

NeedRecordConsumption = 2

Consume the already-ready record before supplying more input.

InputProgress

pub struct InputProgress

Exact input progress and the next receive-side action.

Field consumed

pub consumed: usize

The caller-input prefix copied during this call.

Field demand

pub demand: InputDemand

The resource or action needed next.

OutputDemand

pub enum OutputDemand

The next send-side action after a successful queue call.

RecordQueued

RecordQueued = 0

One record was encoded and is ready for transport.

NeedOutput

NeedOutput = 1

A prior record still has unacknowledged output.

OutputProgress

pub struct OutputProgress

Exact queue progress and the next send-side action.

Field accepted

pub accepted: usize

The content prefix accepted into the queued record.

Field demand

pub demand: OutputDemand

Whether output was queued or an existing record needs acknowledgment.

RecordError

pub union RecordError

A semantic record-construction, caller-use, or peer-record failure.

InvalidSecretLength

RecordError.InvalidSecretLength { requested: usize, expected: usize }: RecordError

A traffic secret did not have the suite hash's exact width.

Field requested

pub requested: usize

The supplied byte length.

Field expected

pub expected: usize

The required byte length.

InvalidState

RecordError.InvalidState: RecordError

The operation is not valid in the direction's current state.

RecordOverflow

RecordError.RecordOverflow { requested: usize, maximum: usize }: RecordError

A declared or supplied record length exceeds the admitted bound.

Field requested

pub requested: usize

The rejected length.

Field maximum

pub maximum: usize

The largest admitted length.

InvalidContent

RecordError.InvalidContent: RecordError

A content type, plaintext version, inner plaintext, or alert shape is invalid.

AuthenticationFailed

RecordError.AuthenticationFailed: RecordError

A protected record's authenticated header, ciphertext, or tag did not verify.

InvalidAcknowledgment

RecordError.InvalidAcknowledgment { requested: usize, pending: usize }: RecordError

An output acknowledgment exceeds the currently pending suffix.

Field requested

pub requested: usize

The requested acknowledgment count.

Field pending

pub pending: usize

The number of bytes currently pending.

KeyUsageExhausted

RecordError.KeyUsageExhausted: RecordError

This traffic epoch has already protected its maximum record count.

RecordView

pub struct RecordView<'owner>

A complete record borrowed from its receiver's verified storage.

Field contentType

pub contentType: ContentType

The authenticated or plaintext content type.

Field content

pub content: &'owner [u8]

The complete content without header, tag, inner type, or padding.

TlsRecordSender

pub struct TlsRecordSender

An affine owner of one TLS record send epoch and at most one pending record.

Details

Protected construction validates the traffic-secret width before allocation, derives the AEAD key and IV with TLS HKDF, and starts at sequence zero. Plaintext construction has no key epoch. Queueing accepts at most 16384 content bytes, adds no protected padding, and reserves one protected sequence only after complete encoding. Pending bytes remain stable until acknowledged.

Gotchas

The later authenticated client owns epoch installation and reserves its final send record for KeyUpdate. Standalone users must replace an exhausted sender; no sequence reset is exposed.

Associated function TlsRecordSender.make

pub effect<'life0> fn make<'life0>(suite: CipherSuite, trafficSecret: &'life0 [u8]) -> silk/result.Result<silk/tls_record.TlsRecordSender, silk/tls_record.RecordError> ! OutOfMemoryError ? &mut Allocator

Creates a protected send epoch from one exact-width traffic secret.

Details

SHA-256 suites require 32 secret bytes and the SHA-384 suite requires 48. A wrong width returns InvalidSecretLength before allocation. Success acquires fixed pending and staging storage once; later operations allocate nothing.

Gotchas

A traffic secret proves no peer identity. Install it only after authenticated key agreement.

Associated function TlsRecordSender.makePlaintext

pub effect<'static> fn makePlaintext() -> TlsRecordSender ! OutOfMemoryError ? &mut Allocator

Creates plaintext TLS record framing without a traffic secret or protected epoch.

Method TlsRecordSender.replaceEpoch

pub fn replaceEpoch<'life0, 'life1>(self: &'life0 mut TlsRecordSender, suite: CipherSuite, trafficSecret: &'life1 [u8]) -> silk/result.Result<(), silk/tls_record.RecordError>

Installs a fresh protected traffic epoch on an idle sender without allocation.

Details

The replacement reuses the owned wire and staging storage and starts at sequence zero. The traffic secret must have the selected suite hash width.

Gotchas

Pending output makes the replacement invalid. Every failure leaves the complete old epoch unchanged. A successful replacement is allowed after the old epoch exhausts its record budget.

Method TlsRecordSender.queueRecord

pub fn queueRecord<'life0, 'life1>(self: &'life0 mut TlsRecordSender, contentType: ContentType, content: &'life1 [u8]) -> silk/result.Result<silk/tls_record.OutputProgress, silk/tls_record.RecordError>

Queues at most one content fragment without retaining the caller input.

Details

An existing pending record returns zero accepted bytes with NeedOutput. Otherwise handshake content must be nonempty, alerts exactly two bytes, and application content may be empty. Inputs longer than 16384 bytes queue one exact prefix. A protected record adds its inner type, no padding, an authenticated outer header, and one sequence reservation.

Method TlsRecordSender.pendingOutput

pub fn pendingOutput<'owner>(self: &'owner TlsRecordSender) -> &'owner [u8]

Borrows the exact unacknowledged output suffix. Repeated inspection and inspection before any queued record are idempotent.

Method TlsRecordSender.cancelPending

pub fn cancelPending<'life0>(self: &'life0 mut TlsRecordSender) -> ()

Cancels the complete pending suffix without changing the traffic-key epoch.

Gotchas

Callers must close a transport after canceling a partially written record; its already-written prefix cannot be completed or reused. Authenticated protocols use this only on terminal failure.

Method TlsRecordSender.recordsRemaining

pub fn recordsRemaining<'life0>(self: &'life0 TlsRecordSender) -> u64

Returns the number of records that this protected epoch can still encode. Plaintext framing returns zero because it has no traffic-key epoch.

Method TlsRecordSender.ackWritten

pub fn ackWritten<'life0>(self: &'life0 mut TlsRecordSender, count: usize) -> silk/result.Result<(), silk/tls_record.RecordError>

Acknowledges a written prefix, or leaves all state unchanged when the count is excessive.

TlsRecordReceiver

pub struct TlsRecordReceiver

An affine owner of one TLS record receive epoch and one verified record view.

Details

Input is copied only into fixed storage, headers are admitted before body bytes, and protected plaintext is published only after tag, type, padding, and content validation. A complete record remains ready until consumed, and its view structurally borrows this owner.

Gotchas

Every peer-record failure is terminal for this receiver. Construct a fresh epoch after a valid TLS key transition; never attempt to reuse a failed or exhausted receiver.

Associated function TlsRecordReceiver.make

pub effect<'life0> fn make<'life0>(suite: CipherSuite, trafficSecret: &'life0 [u8]) -> silk/result.Result<silk/tls_record.TlsRecordReceiver, silk/tls_record.RecordError> ! OutOfMemoryError ? &mut Allocator

Creates a protected receive epoch from one exact-width traffic secret.

Associated function TlsRecordReceiver.makePlaintext

pub effect<'static> fn makePlaintext() -> TlsRecordReceiver ! OutOfMemoryError ? &mut Allocator

Creates plaintext TLS record framing without a traffic secret or protected epoch.

Method TlsRecordReceiver.replaceEpoch

pub fn replaceEpoch<'life0, 'life1>(self: &'life0 mut TlsRecordReceiver, suite: CipherSuite, trafficSecret: &'life1 [u8]) -> silk/result.Result<(), silk/tls_record.RecordError>

Installs a fresh protected traffic epoch on an idle receiver without allocation.

Details

The replacement reuses the owned wire and plaintext storage and starts at sequence zero. The traffic secret must have the selected suite hash width.

Gotchas

A partial, ready, or terminal receiver makes the replacement invalid. Every failure leaves the complete old epoch unchanged. A successful replacement is allowed after exhaustion.

Method TlsRecordReceiver.feedInput

pub fn feedInput<'life0, 'life1>(self: &'life0 mut TlsRecordReceiver, input: &'life1 [u8]) -> silk/result.Result<silk/tls_record.InputProgress, silk/tls_record.RecordError>

Copies a prefix for the current record and reports the exact next receive action.

Details

Empty input reports zero-consumption NeedInput. Header admission occurs after exactly five bytes and before any body byte is copied. Completion stops before a coalesced following record. A ready record causes zero-consumption NeedRecordConsumption until consumeRecord or consumeRecordInto succeeds. During a protected epoch, exact plaintext compatibility CCS {1} is admitted without consuming a protected sequence; every other record remains protected.

Gotchas

Header, authentication, content, and key-use failures terminally invalidate this receiver.

Method TlsRecordReceiver.record

pub fn record<'owner>(self: &'owner TlsRecordReceiver) -> silk/option.Option<silk/tls_record.RecordView<'owner>>

Borrows the complete current record, or returns None while more input is needed.

Method TlsRecordReceiver.readyContentType

pub fn readyContentType<'life0>(self: &'life0 TlsRecordReceiver) -> silk/option.Option<silk/tls_record.ContentType>

Returns the ready record's authenticated content type without borrowing its content.

Method TlsRecordReceiver.readyLength

pub fn readyLength<'life0>(self: &'life0 TlsRecordReceiver) -> usize

Returns the ready record's authenticated content length, or zero when no record is ready.

Method TlsRecordReceiver.consumeRecordInto

pub fn consumeRecordInto<'life0, 'life1>(self: &'life0 mut TlsRecordReceiver, destination: &'life1 mut [u8]) -> silk/result.Result<usize, silk/tls_record.RecordError>

Copies and consumes one complete authenticated record without retaining an owner-tied view.

Gotchas

A destination smaller than the ready content returns RecordOverflow and leaves the record ready and unchanged. No ready record returns InvalidState.

Method TlsRecordReceiver.consumeRecord

pub fn consumeRecord<'life0>(self: &'life0 mut TlsRecordReceiver) -> silk/result.Result<(), silk/tls_record.RecordError>

Releases the current record and permits input for the next record.

On this page