std/tls/lib
std/tls/src/lib.trb
TLS over a TcpStream: TlsStream.connect for a client, TlsStream.accept with a ServerIdentity for a server,
and a stream with the same two directions a TcpStream has (docs/design/NETWORK.md section 5).
const tcp = TcpStream.connectTo("example.test", 443).await()?
var stream = TlsStream.connect(tcp, "example.test").await()?
stream.send("GET / HTTP/1.1\r\nHost: example.test\r\n\r\n".bytes().toList()).await()?
The protocol is mbedTLS 3, TLS 1.2 and 1.3. A client checks the server's certificate for the name it asks for: on
Windows the platform decides - its roots, its enterprise roots, its policies - and elsewhere the system's bundle of
roots does; TlsSettings.trusted replaces both with roots of the program's own. There is no switch that turns the
check off.
TLS is a state machine over bytes here: every wait is a wait of the TcpStream under it, so a TLS read is cancelled,
timed out and paced exactly as a TCP read is.
type TlsSettings
type TlsSettings
What a client trusts. The default is the platform's judgement, which is what every other program of the machine trusts.
field trusted
trusted: List<String> = []
PEM certificates to trust instead of the platform: a private root of a company, the root of a test. Empty is the platform's roots, and a list that names any is only these.
type ServerIdentity
shared type ServerIdentity with Close
What a server proves who it is with: its certificate chain and the private key of the first certificate, both PEM.
Parsed once and shared by every connection of a server; a shared type, because the key it holds is the runtime's.
field handle
protected var handle: Int
The runtime's handle: what a server session is started with. Read by TlsStream.accept, written by nothing.
fn of
static fn of(certificates: String, privateKey: String): Result<ServerIdentity, NetworkError>
The identity of a PEM chain - the server's own certificate first, then the intermediates - and the PEM private key that belongs to the first certificate.
Errors
NetworkErrorthatisTlsFailure()where a certificate or the key cannot be read, or the key is not the certificate's.
fn close
var fn close()
Not documented.
type TlsStream
shared type TlsStream with Close
One TLS connection over a TcpStream: bytes in both directions, encrypted and authenticated. A shared type, like the
stream it holds; it closes when the last reference to it goes, and the TCP stream with it.
fn connect
static fn connect(stream: TcpStream, serverName: String, settings: TlsSettings = TlsSettings()): Task<Result<TlsStream, NetworkError>>
The client's side: a handshake over stream with a server that must prove it is serverName.
Errors
NetworkErrorthatisCertificateRejected()where the certificate is not trusted for that name, andisTlsFailure()where the handshake fails otherwise.
fn accept
static fn accept(stream: TcpStream, identity: ServerIdentity): Task<Result<TlsStream, NetworkError>>
The server's side: a handshake over an accepted stream, proving identity.
fn protocol
fn protocol(): String
The version the two sides agreed on: "TLSv1.3" or "TLSv1.2".
fn localAddress
fn localAddress(): Result<SocketAddress, NetworkError>
The address of this end.
fn remoteAddress
fn remoteAddress(): Result<SocketAddress, NetworkError>
The address of the other end.
fn receive
var fn receive(maximum: Int = 65536): Task<Result<Bytes?, NetworkError>>
At most maximum bytes, as soon as any were decrypted: Some(bytes), or None once the peer ended the session.
fn receiveBuffer
var fn receiveBuffer(maximum: Int = 65536): Task<Result<ArrayList<UInt8>?, NetworkError>>
TlsStream.receive as the ArrayList the bytes arrive in.
fn send
var fn send(bytes: Bytes): Task<Result<Void, NetworkError>>
Writes all of bytes, encrypted; finishes once the operating system took the ciphertext.
fn sendBuffer
var fn sendBuffer(bytes: ArrayList<UInt8>): Task<Result<Void, NetworkError>>
TlsStream.send for bytes that are an ArrayList already.
fn shutdown
var fn shutdown(): Task<Result<Void, NetworkError>>
Ends this half: a close_notify for the peer, then the end of the TCP stream's writing half.
fn source
fn source(chunk: Int = 65536): TlsSource
The reading direction as a Source: at most chunk bytes per next(), and None once the peer ended.
fn sink
fn sink(): TlsSink
The writing direction as a Sink: end() ends this half with close_notify.
fn close
var fn close()
Frees the session; the TCP stream closes with its own release.
type TlsSource
shared type TlsSource with Source<Bytes, NetworkError>
The reading direction of a TlsStream: a Source<Bytes, NetworkError>.
fn of
static fn of(stream: TlsStream, chunk: Int): TlsSource
The source of stream: what TlsStream.source answers.
fn next
var fn next(): Task<Result<Bytes?, NetworkError>>
Not documented.
fn close
var fn close()
Nothing of its own: the stream closes once its last holder is gone.
type TlsSink
shared type TlsSink with Sink<Bytes, NetworkError>
The writing direction of a TlsStream: a Sink<Bytes, NetworkError>.
fn of
static fn of(stream: TlsStream): TlsSink
The sink of stream: what TlsStream.sink answers.
fn add
var fn add(item: Bytes): Task<Result<Void, NetworkError>>
Not documented.
fn end
var fn end(): Task<Result<Void, NetworkError>>
Ends this half of the session with close_notify.
fn close
var fn close()
Nothing of its own: the stream closes once its last holder is gone.
type TlsResolver
type TlsResolver
DNS over TLS (RFC 7858): the lookups of std/network's Resolver, asked of one name server over a TLS connection to
its port 853 - the question and the answer each after their length in two bytes, as over TCP (RFC 7766) - with the
server authenticated by the name its certificate has to carry (RFC 8310's strict profile: a server that cannot prove
serverName is never asked).
const resolver = TlsResolver(SocketAddress(IpAddress.tryFrom("192.0.2.53")?, 853), "dns.example.test")
const records = resolver.lookup(DomainName.tryFrom("example.test")?, RecordType.Aaaa).await()?
Every lookup opens a connection of its own and closes it with the answer: keeping one open for the next question is a
later refinement. Times are milliseconds, because a field's default is a constant and a Duration is none.
field server
server: SocketAddress
The name server, usually on port 853.
field serverName
serverName: String
The name its certificate has to carry.
field settings
settings: TlsSettings = TlsSettings()
What the client trusts: the platform's roots by default.
field attemptMilliseconds
attemptMilliseconds: Int = 5000
How long connecting, the handshake and the answer may take together.
fn lookup
fn lookup(name: DomainName, recordType: RecordType): Task<Result<List<Record>, NetworkError>>
The records of recordType that name has, CNAME chains followed within the answer.
Errors
NetworkErrorthatisHostNotFound()where the name does not exist (NXDOMAIN), andisNameServerFailure()where the server answeredSERVFAIL,REFUSEDorNOTIMP.- Every failure of
TlsResolver.exchange.
fn exchange
fn exchange(name: DomainName, recordType: RecordType): Task<Result<Message, NetworkError>>
The whole response to a query for recordType at name, with a random identifier (RFC 5452).
Errors
NetworkErrorthatisTimedOut()where the attempt's time passed,isCertificateRejected()where the server could not prove its name, andisNameServerFailure()where it answered something that is not the response.