tls: the peer's certificate from a TlsStream #116

Closed
opened 2026-10-05 21:07:22 +00:00 by orion · 6 comments
Collaborator

Context

Channel binding and certificate pinning. Listed among what std is missing for the first-party packages.

Source

Acceptance criteria

  • Built in the package FIRST-PARTY.md names
  • Tests
  • FIRST-PARTY.md gap 18 marked done

Dependencies

None known.

Moved from the planning lists of the docs; the design record stays the specification.

## Context Channel binding and certificate pinning. Listed among what `std` is missing for the first-party packages. ## Source - [FIRST-PARTY.md gap 18](https://git.torb.dev/torbscript/language/src/commit/393ef2608e4a60b8ff76d8b6b408951b822d887d/docs/design/FIRST-PARTY.md#L278) ## Acceptance criteria - [ ] Built in the package FIRST-PARTY.md names - [ ] Tests - [ ] FIRST-PARTY.md gap 18 marked done ## Dependencies None known. _Moved from the planning lists of the docs; the design record stays the specification._
Author
Collaborator

[loop:torbscript] intake: in scope (author orion, label loop), source FIRST-PARTY.md gap 18, not a duplicate. No dependencies. next: waiting (refine queued, ready buffer full) until 2026-10-10T04:00:00Z

[loop:torbscript] intake: in scope (author orion, label loop), source FIRST-PARTY.md gap 18, not a duplicate. No dependencies. next: waiting (refine queued, ready buffer full) until 2026-10-10T04:00:00Z
Author
Collaborator

[loop:torbscript] claim refine by session:loop-torbscript-20261024 until 2026-10-24T07:39:00Z

[loop:torbscript] claim refine by session:loop-torbscript-20261024 until 2026-10-24T07:39:00Z
Author
Collaborator

Refinement: scope narrowed to step 1 of 2 (the runtime function and its natives row)

FIRST-PARTY.md gap 18 needs a new native, tlsPeerCertificate. CLAUDE.md "Seed and breaking changes": a new native takes two commits, the second only after a published seed knows the first (the pattern of #362 / #363). So this issue is commit 1: the runtime function, the header, the natives row. The std/tls declaration, the TlsStream method, its tests and the docs are #463, ordered after this issue and the next published seed.

[loop:torbscript] decided by the loop: native shape int64_t torb_tls_peer_certificate(int64_t session, torb_list *into) appends the DER and answers the length (0 = none), the same shape as torb_tls_take_outgoing / torb_tls_read (the list is the caller's, no new ownership rule). The bytes come from mbedtls_ssl_get_peer_cert; MBEDTLS_SSL_KEEP_PEER_CERTIFICATE is on in the vendored mbedtls_config.h, so no config change. Leaf certificate only (what tls-server-end-point and pinning use); the chain is not needed. Naming follows the tls* natives. No owner question: internal technical design, no published contract changes.

Requirements

  • runtime/include/torb_tls.h: int64_t torb_tls_peer_certificate(int64_t session, torb_list *into); with a doc comment (DER of the certificate the peer sent, leaf, appended to *into a list of UInt8; the number of bytes, 0 where the session is unknown, the handshake is not done, or the peer sent none).
  • runtime/tls/tls.c: the body under torb_tls_lock like torb_tls_protocol: look up the session, mbedtls_ssl_is_handshake_over, mbedtls_ssl_get_peer_cert, torb_list_add_plain(into, cert->raw.p, cert->raw.len). Works with the platform verifier on Windows too (the certificate is parsed by mbedTLS either way).
  • compiler/src/backend/c/natives.trb: a tlsRuntimeOf("tlsPeerCertificate", ...) entry in tlsEntries with the prototype of the header; the VM thunk comes from the same manifest.
  • docs/design/NATIVES.md: a row tlsPeerCertificate | Runtime | keep | tls | - | - | - and the counts of the table updated (the audit test checks that every manifest entry has a row and the counts match).
  • Nothing under std/ (a declaration there would break the seed's bootstrap), nothing under seed/, tests/conformance/.

Acceptance criteria

  • The function, the header declaration and the manifest entry exist; compiler/tests/natives.test.trb (the audit) passes with the new row and counts.
  • A C test in runtime/tests/tls_test.c (it has tls_certificates.h): after a loopback handshake of a client and a server session, the client's torb_tls_peer_certificate returns the length of the server's leaf and the bytes equal the DER of the first block of the test chain (decode in the test); the server's returns 0 (no client certificate); a session before the handshake and an unknown handle return 0.
  • Tier A green; tier B green (runtime/ touched).
  • After the merge the seed with this commit is published (forge seed workflow or the nightly). Not part of this PR; #463 stays not ready until seeds.txt lists a seed at or after the merge commit.

Seed impact

Yes: a new native. This is commit 1 of the two-commit rule; the declaration is #463.

Size

One PR, about 1 h: one Conventional Commit feat(tls): the peer certificate of a session, and its natives row (two if the test file is split out). Source: gap 18; design record NETWORK.md section 5 (TLS as a state machine over bytes), ROADMAP std/tls. No conflict with #117 (client certificates) or #118 (ALPN): different natives.

## Refinement: scope narrowed to step 1 of 2 (the runtime function and its natives row) [FIRST-PARTY.md gap 18](https://git.torb.dev/torbscript/language/src/commit/393ef2608e4a60b8ff76d8b6b408951b822d887d/docs/design/FIRST-PARTY.md#L278) needs a new native, `tlsPeerCertificate`. `CLAUDE.md` "Seed and breaking changes": a new native takes two commits, the second only after a published seed knows the first (the pattern of #362 / #363). So this issue is **commit 1**: the runtime function, the header, the natives row. The `std/tls` declaration, the `TlsStream` method, its tests and the docs are #463, ordered after this issue and the next published seed. [loop:torbscript] decided by the loop: native shape `int64_t torb_tls_peer_certificate(int64_t session, torb_list *into)` appends the DER and answers the length (0 = none), the same shape as `torb_tls_take_outgoing` / `torb_tls_read` (the list is the caller's, no new ownership rule). The bytes come from `mbedtls_ssl_get_peer_cert`; `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` is on in the vendored `mbedtls_config.h`, so no config change. Leaf certificate only (what `tls-server-end-point` and pinning use); the chain is not needed. Naming follows the `tls*` natives. No owner question: internal technical design, no published contract changes. ### Requirements - `runtime/include/torb_tls.h`: `int64_t torb_tls_peer_certificate(int64_t session, torb_list *into);` with a doc comment (DER of the certificate the peer sent, leaf, appended to `*into` a list of `UInt8`; the number of bytes, 0 where the session is unknown, the handshake is not done, or the peer sent none). - `runtime/tls/tls.c`: the body under `torb_tls_lock` like `torb_tls_protocol`: look up the session, `mbedtls_ssl_is_handshake_over`, `mbedtls_ssl_get_peer_cert`, `torb_list_add_plain(into, cert->raw.p, cert->raw.len)`. Works with the platform verifier on Windows too (the certificate is parsed by mbedTLS either way). - `compiler/src/backend/c/natives.trb`: a `tlsRuntimeOf("tlsPeerCertificate", ...)` entry in `tlsEntries` with the prototype of the header; the VM thunk comes from the same manifest. - `docs/design/NATIVES.md`: a row `tlsPeerCertificate | Runtime | keep | tls | - | - | -` and the counts of the table updated (the audit test checks that every manifest entry has a row and the counts match). - **Nothing under `std/`** (a declaration there would break the seed's bootstrap), nothing under `seed/`, `tests/conformance/`. ### Acceptance criteria - [ ] The function, the header declaration and the manifest entry exist; `compiler/tests/natives.test.trb` (the audit) passes with the new row and counts. - [ ] A C test in `runtime/tests/tls_test.c` (it has `tls_certificates.h`): after a loopback handshake of a client and a server session, the client's `torb_tls_peer_certificate` returns the length of the server's leaf and the bytes equal the DER of the first block of the test chain (decode in the test); the server's returns 0 (no client certificate); a session before the handshake and an unknown handle return 0. - [ ] Tier A green; tier B green (`runtime/` touched). - [ ] After the merge the seed with this commit is published (forge `seed` workflow or the nightly). **Not part of this PR**; #463 stays not `ready` until `seeds.txt` lists a seed at or after the merge commit. ### Seed impact Yes: a new native. This is commit 1 of the two-commit rule; the declaration is #463. ### Size One PR, about 1 h: one Conventional Commit `feat(tls): the peer certificate of a session, and its natives row` (two if the test file is split out). Source: gap 18; design record NETWORK.md section 5 (TLS as a state machine over bytes), ROADMAP std/tls. No conflict with #117 (client certificates) or #118 (ALPN): different natives.
Author
Collaborator

[loop:torbscript] refine: split by the two-commit rule: #116 is step 1 (runtime function, header, natives row; requirements and acceptance above), step 2 is #463 (std/tls declaration, TlsStream.peerCertificate(), tests, docs, gap 18 done) and waits for a published seed after #116's merge; label ready set on #116. Tokens: ~0.2M. next: implement

[loop:torbscript] refine: split by the two-commit rule: #116 is step 1 (runtime function, header, natives row; requirements and acceptance above), step 2 is #463 (std/tls declaration, `TlsStream.peerCertificate()`, tests, docs, gap 18 done) and waits for a published seed after #116's merge; label `ready` set on #116. Tokens: ~0.2M. next: implement
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261024 until 2026-10-24T07:48:27Z

[loop:torbscript] claim implement by session:loop-torbscript-20261024 until 2026-10-24T07:48:27Z
Author
Collaborator

[loop:torbscript] implement: PR 464 (57ea45b2), next: waiting (forge CI of 57ea45b2)

[loop:torbscript] implement: PR 464 (57ea45b2), next: waiting (forge CI of 57ea45b2)
argus closed this issue 2026-10-24 07:38:43 +00:00
Sign in to join this conversation.
No labels
loop
ready
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
torbscript/language#116
No description provided.