Skip to content

4.8.13 Ensemble TLS Helpers

Import :std/ensemble/network/tls for DID-named self-signed TLS contexts and certificate identity extraction. The current helpers support Ed25519 keys, matching the DID module.

4.8.13.1 Procedures

Procedure Argument Result
make-tls-context PrivKey Native SSL_CTX object
did->hostname DID string Internal hostname string
tls-certificate->hostname+did Native X509 certificate Two values: hostname, canonical DID

make-tls-context derives the host name from the supplied key and installs a self-signed certificate. It uses the shared SSL context builder, which requires TLS 1.3 and a certificate from each peer. The supplied private key remains owned by the caller; the context retains its own native reference. Keep the context alive while listeners or connections use it.

4.8.13.2 Hostname Encoding

did->hostname decodes the DID before encoding its complete multicodec-plus-key payload as lowercase, unpadded RFC 4648 Base32, followed by .internal. There is no extra multibase prefix. Equivalent accepted DID spellings produce the same name.

For Ed25519, the payload consists of codec bytes #xed #x01 and the 32 public-key bytes. It yields a 55-character DNS label and a 64-character complete hostname, within both the DNS label limit and the X509 common-name limit.

Base64url DID suffixes are not copied directly: they can contain underscores and use case distinctions that DNS hostnames cannot preserve. The internal domain is a certificate naming convention, not a requirement to resolve those names in DNS.

4.8.13.3 Peer Identity

tls-certificate->hostname+did returns the certificate’s claimed common name first and a canonical DID derived from its actual public key second. It releases the temporary public-key reference after extraction. Missing public keys or common names raise errors; the supplied certificate remains owned by its existing holder. Missing common name or unsupported key type raises TLSPeerIdentityError, an IOError subclass. The handshake constructor uses the same type for missing peer certificates and expected-DID mismatch. Native key extraction/allocation, crypto, and contract errors propagate without being relabeled as peer policy failures.

SSL client/server negotiation failures use SSLHandshakeError (an SSLError subclass), retaining native results. This describes the failure stage, not a guarantee of peer fault. Known native allocation, internal and syscall failures remain terminal SSLError, as do local SSL setup errors. Timeout and Closed remain distinct.

The name is not proof of the DID. The self-signed TLS policy permits peers to prove possession of their own keys without a common CA. A peer can nevertheless put another host’s name into a certificate signed with its own key. Network code must compare the extracted DID against the expected peer DID before accepting the connection, and should check that the claimed name equals did->hostname of that extracted DID. Hostname checking alone is insufficient.

Successful TLS setup is also distinct from UCAN authorization. Admission and stream capability checks belong to the network and capability-context layers.

4.8.13.4 Upgrade Ownership

Both SSL close paths use one internal with-ssl-socket-close macro. It catches device-close failure inside the shared write lock, releases native SSL there, then rethrows the identical exception after unlocking. An unwind-protect alone is insufficient: the rwlock exception handler unlocks before dynamic-wind cleanup, and a plain failing thread may terminate without that unwind. The regression closes a real device and raises at the close-expression boundary, exercising the shared macro without corrupting a descriptor or adding a production injection hook.

Raw and upgraded sockets share the underlying device and synchronization lock. An owner may close its retained raw view while TLS setup finishes. Closing the upgraded view must still release its independently owned native SSL state; both abortive close and writer/reader shutdown now do so even when the device is closed. Repeated/concurrent close is serialized, and native release disables the foreign object’s finalizer. Peer-certificate acquisition also holds the shared lock and checks device liveness before touching SSL, preventing lookup after release.

The connector attaches raw transport before TLS and the upgraded view before constructing Handshake. Rejection of a late upgrade closes the upgraded view. Tests cover silent TLS interruption, this late-attachment ordering, explicit native release after raw closure, and repeated/concurrent SSL closure. These tests do not claim full Network shutdown or Connection publication integration.