1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! Compatibility stub for the upstream `epics-base` "ca-secure"
//! TLS proposal.
//!
//! ## Status
//!
//! Not yet shipped in any released `epics-base`. The upstream
//! design is moving — what's here tracks the public discussion as
//! of `epics-base` 7.x development branches and may need rework
//! when the spec stabilizes.
//!
//! Sources we're tracking:
//!
//! - <https://docs.epics-controls.org/projects/base/en/latest/secure-channel-access.html>
//! (intermittent — may 404)
//! - `epics-base` mailing list ("ca-secure" thread, ongoing)
//! - upstream Slack `#secure-ca`
//!
//! ## What ca-secure intends
//!
//! - **Inline TLS upgrade**: client and server start in plaintext,
//! negotiate TLS via a CA-level capability flag, then upgrade the
//! stream in-place (StartTLS pattern). Requires a wire bit not
//! currently in `CA_PROTO_VERSION`.
//! - **mTLS-driven identity**: subjects from the client cert flow
//! into the rsrv ASG check (replacing host+username matching when
//! present).
//! - **Wire compat**: a server speaking ca-secure must still answer
//! plaintext clients on the same TCP port — TLS is a per-channel
//! capability, not a port-level discriminator.
//!
//! ## What this crate currently ships
//!
//! - `experimental-rust-tls`: an immediate-TLS variant — the entire
//! TCP stream is wrapped in TLS from byte zero. Interoperable only
//! with other Rust clients/servers in the same trust pool.
//!
//! ## Negotiation mode
//!
//! [`TlsMode`] selects which wire format the server speaks.
//! - `RustOnly` — current `experimental-rust-tls` behaviour
//! - `CaSecureDraft` — placeholder; the negotiation hooks below
//! record the intent but defer to RustOnly until the upstream
//! spec stabilizes.
//!
//! Pick the mode via `EPICS_CAS_TLS_MODE`. Unset = `RustOnly`.
//!
//! ## What landing real ca-secure requires
//!
//! 1. New `CA_PROTO_VERSION` capability bit (upstream needs to
//! finalize the value).
//! 2. A handshake state machine in `tcp.rs` that tolerates plaintext
//! framing for the first message, then optionally upgrades.
//! 3. mTLS subject extraction wired into the existing ACF identity
//! path (already done for `experimental-rust-tls`; reuse
//! `crate::tls::identity_from_cert`).
//! 4. Interop test against a real `epics-base` 7 build with the same
//! feature enabled — track-the-spec is fundamentally a moving
//! target so this acceptance gate matters.
/// Wire-level TLS negotiation mode.
/// Stub for the inline-TLS handshake described by the upstream draft.
/// Returns immediately; in the final implementation this would peek
/// at the first `CA_PROTO_VERSION` byte, detect the ca-secure
/// capability flag, and either upgrade the stream or hand back the
/// raw plaintext stream.
///
/// Today: this function exists so the call site in `tcp.rs` has a
/// stable hook to call when the spec lands. With `CaSecureDraft` the
/// caller currently still uses the immediate-TLS path.
pub async