clawft_kernel/workload_runtime/
seed_tls.rs1use std::sync::Arc;
20
21use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
22use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
23use rustls::{DigitallySignedStruct, SignatureScheme};
24use sha2::{Digest, Sha256};
25
26use super::types::RuntimeError;
27
28#[derive(Debug, Clone, PartialEq, Eq)]
30pub enum SeedTls {
31 WebPki,
33 PinnedSha256([u8; 32]),
35 PinnedSpki([u8; 32]),
38}
39
40impl SeedTls {
41 pub fn pinned(fingerprint: &str) -> Result<Self, RuntimeError> {
44 let bad = || {
45 RuntimeError::InvalidConfig(
46 "seed certificate pin must be sha256:<64 hex digits>".into(),
47 )
48 };
49 let hex: String = fingerprint
50 .strip_prefix("sha256:")
51 .ok_or_else(bad)?
52 .chars()
53 .filter(|c| *c != ':')
54 .collect();
55 if hex.len() != 64 || !hex.bytes().all(|b| b.is_ascii_hexdigit()) {
56 return Err(bad());
57 }
58 let mut out = [0u8; 32];
59 for (i, byte) in out.iter_mut().enumerate() {
60 *byte = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16).map_err(|_| bad())?;
61 }
62 Ok(Self::PinnedSha256(out))
63 }
64
65 pub fn pinned_spki(fingerprint: &str) -> Result<Self, RuntimeError> {
67 let rest = fingerprint.strip_prefix("spki-sha256:").ok_or_else(|| {
68 RuntimeError::InvalidConfig("seed key pin must be spki-sha256:<64 hex digits>".into())
69 })?;
70 match Self::pinned(&format!("sha256:{rest}"))? {
71 Self::PinnedSha256(h) => Ok(Self::PinnedSpki(h)),
72 other => Ok(other),
73 }
74 }
75
76 pub fn spki_fingerprint(der: &[u8]) -> Option<String> {
79 let d = Sha256::digest(spki_der(der)?);
80 let hex: String = d.iter().map(|b| format!("{b:02x}")).collect();
81 Some(format!("spki-sha256:{hex}"))
82 }
83
84 pub fn fingerprint(der: &[u8]) -> String {
86 let d = Sha256::digest(der);
87 let hex: String = d.iter().map(|b| format!("{b:02x}")).collect();
88 format!("sha256:{hex}")
89 }
90
91 pub(crate) fn client_config(&self) -> Option<rustls::ClientConfig> {
94 let pin = match self {
95 Self::PinnedSha256(p) => Pin::Cert(*p),
96 Self::PinnedSpki(p) => Pin::Spki(*p),
97 Self::WebPki => return None,
98 };
99 let provider = Arc::new(rustls::crypto::ring::default_provider());
100 let verifier = Arc::new(PinnedCert {
101 pin,
102 provider: provider.clone(),
103 });
104 let mut cfg = rustls::ClientConfig::builder_with_provider(provider)
105 .with_safe_default_protocol_versions()
106 .ok()?
107 .dangerous()
108 .with_custom_certificate_verifier(verifier)
109 .with_no_client_auth();
110 cfg.alpn_protocols = vec![b"http/1.1".to_vec()];
111 Some(cfg)
112 }
113}
114
115#[derive(Debug, Clone, Copy)]
116enum Pin {
117 Cert([u8; 32]),
118 Spki([u8; 32]),
119}
120
121fn der_take(b: &[u8], tag: u8) -> Option<(&[u8], &[u8])> {
124 let (&t, b) = b.split_first()?;
125 if t != tag {
126 return None;
127 }
128 let (&l0, b) = b.split_first()?;
129 let (len, b) = if l0 < 0x80 {
130 (l0 as usize, b)
131 } else {
132 let n = (l0 & 0x7f) as usize;
133 if n == 0 || n > 4 || b.len() < n {
134 return None;
135 }
136 let len = b[..n].iter().fold(0usize, |a, x| (a << 8) | *x as usize);
137 (len, &b[n..])
138 };
139 (b.len() >= len).then(|| b.split_at(len))
140}
141
142fn spki_der(cert: &[u8]) -> Option<&[u8]> {
145 let (cert, _) = der_take(cert, 0x30)?;
146 let (tbs, _) = der_take(cert, 0x30)?;
147 let mut rest = tbs;
148 if rest.first() == Some(&0xa0) {
149 rest = der_take(rest, 0xa0)?.1; }
151 let (_, r) = der_take(rest, 0x02)?;
153 rest = r;
154 for _ in 0..4 {
155 rest = der_take(rest, 0x30)?.1;
156 }
157 let (inner, _) = der_take(rest, 0x30)?;
158 let head = rest.len() - der_take(rest, 0x30)?.1.len() - inner.len();
160 Some(&rest[..head + inner.len()])
161}
162
163#[derive(Debug)]
166struct PinnedCert {
167 pin: Pin,
168 provider: Arc<rustls::crypto::CryptoProvider>,
169}
170
171impl ServerCertVerifier for PinnedCert {
172 fn verify_server_cert(
173 &self,
174 end_entity: &CertificateDer<'_>,
175 _intermediates: &[CertificateDer<'_>],
176 _server_name: &ServerName<'_>,
177 _ocsp_response: &[u8],
178 _now: UnixTime,
179 ) -> Result<ServerCertVerified, rustls::Error> {
180 let ok = match self.pin {
181 Pin::Cert(p) => <[u8; 32]>::from(Sha256::digest(end_entity.as_ref())) == p,
182 Pin::Spki(p) => spki_der(end_entity.as_ref())
183 .is_some_and(|k| <[u8; 32]>::from(Sha256::digest(k)) == p),
184 };
185 if ok {
186 Ok(ServerCertVerified::assertion())
187 } else {
188 Err(rustls::Error::General(
189 "seed certificate does not match the pinned fingerprint".into(),
190 ))
191 }
192 }
193
194 fn verify_tls12_signature(
195 &self,
196 message: &[u8],
197 cert: &CertificateDer<'_>,
198 dss: &DigitallySignedStruct,
199 ) -> Result<HandshakeSignatureValid, rustls::Error> {
200 rustls::crypto::verify_tls12_signature(
201 message,
202 cert,
203 dss,
204 &self.provider.signature_verification_algorithms,
205 )
206 }
207
208 fn verify_tls13_signature(
209 &self,
210 message: &[u8],
211 cert: &CertificateDer<'_>,
212 dss: &DigitallySignedStruct,
213 ) -> Result<HandshakeSignatureValid, rustls::Error> {
214 rustls::crypto::verify_tls13_signature(
215 message,
216 cert,
217 dss,
218 &self.provider.signature_verification_algorithms,
219 )
220 }
221
222 fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
223 self.provider
224 .signature_verification_algorithms
225 .supported_schemes()
226 }
227}