Documentation
¶
Overview ¶
Package ratls implements a Caddy TLS issuance module ("ra_tls") that produces RA-TLS certificates for Confidential VMs.
It generates ECDSA P-256 key pairs and issues X.509 certificates signed by a private-PKI intermediary CA. Each certificate embeds hardware attestation evidence (a quote/report) in a custom X.509 extension whose OID is determined by the selected backend.
Backends ¶
Hardware-specific logic is abstracted behind the Attester interface (see attester.go). The backend is selected via the "backend" configuration field. Currently supported:
- "tdx" -- Intel TDX via Linux configfs-tsm (attester_tdx.go)
- "sgx" -- Intel SGX via Gramine DCAP (attester_sgx.go)
Planned:
- "sev-snp" -- AMD SEV-SNP
Report Data ¶
The quote's 64-byte ReportData field is:
SHA-512( SHA-256(DER public key) || creation_time )
where creation_time is the certificate's NotBefore value truncated to 1-minute precision, formatted as the UTC string "2006-01-02T15:04Z". This allows a verifier to reproduce the ReportData from the certificate alone: read the public key and NotBefore, apply the same formula, and compare against the quote.
Trust Model ¶
Certificates are signed by a user-provided intermediary CA (private PKI). The attestation evidence embedded in the certificate provides hardware- rooted proof that the public key was generated inside a genuine Confidential VM. A relying party should:
- Validate the certificate chain back to the trusted root CA.
- Extract and verify the attestation evidence against the hardware vendor's attestation infrastructure.
- Recompute SHA-512(SHA-256(pub key) || NotBefore as "2006-01-02T15:04Z") and confirm it matches the quote's ReportData.
Attestation Paths ¶
The module supports two attestation modes:
Deterministic (Issue path): ReportData = SHA-512(SHA-256(pubkey) || time). Certificates are cached and auto-renewed by certmagic. A verifier reproduces the ReportData from the certificate's public key and NotBefore.
Challenge-Response (GetCertificate path): When the client's TLS ClientHello contains a RA-TLS challenge extension (0xffbb), a fresh ephemeral certificate is generated with ReportData = SHA-512(SHA-256(pubkey) || nonce). This certificate is not cached. To read the challenge payload, build with the Privasys/go fork (https://github.com/Privasys/go/tree/ratls) and the "ratls" build tag. With standard Go the extension is detected but the payload cannot be read, so the module falls back to the deterministic certificate.
Private Key Sensitivity ¶
The ECDSA private key is generated inside the TEE and protected by hardware memory encryption. It should be treated as highly sensitive:
- It is held in an in-memory sync.Map only between GenerateKey and Issue, then immediately deleted from the map after use.
- certmagic will still PEM-encode and persist the key via its Storage backend. To avoid writing it to unencrypted disk, configure Caddy with an encrypted or in-memory storage backend.
Caddyfile Example ¶
example.com {
tls {
issuer ra_tls {
backend tdx
ca_cert /path/to/intermediate-ca.crt
ca_key /path/to/intermediate-ca.key
}
}
respond "Hello from a Confidential VM!"
}
Build ¶
xcaddy build --with github.com/Privasys/ra-tls-caddy=.
Index ¶
- func RegisterAttester(name string, factory func() Attester)
- type Attester
- type RATLSIssuer
- func (RATLSIssuer) CaddyModule() caddy.ModuleInfo
- func (iss *RATLSIssuer) GenerateKey() (crypto.PrivateKey, error)
- func (iss *RATLSIssuer) GetCertificate(ctx context.Context, hello *tls.ClientHelloInfo) (*tls.Certificate, error)
- func (iss *RATLSIssuer) Issue(ctx context.Context, csr *x509.CertificateRequest) (*certmagic.IssuedCertificate, error)
- func (iss *RATLSIssuer) IssuerKey() string
- func (iss *RATLSIssuer) Provision(ctx caddy.Context) error
- func (iss *RATLSIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error
- type TDXAttester
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RegisterAttester ¶
RegisterAttester registers an Attester factory under the given name. Call this from an init() function in the backend's source file.
Types ¶
type Attester ¶
type Attester interface {
// Name returns the short identifier for this attester (e.g. "tdx",
// "sev-snp"). Used in log messages and issuer keys.
Name() string
// Provision initialises the attester, validating hardware availability
// and setting up any providers. Called once during Caddy provisioning.
Provision(logger *zap.Logger) error
// Quote generates raw attestation evidence for the given 64-byte
// report data.
Quote(reportData [64]byte) ([]byte, error)
// CertExtension returns the X.509 extension (OID + value) used to
// embed the attestation evidence in an RA-TLS certificate. The raw
// quote bytes are passed in as the extension value.
CertExtension(quote []byte) pkix.Extension
}
Attester abstracts hardware-specific confidential computing attestation. Implementations produce attestation evidence (quotes/reports) for a given 64-byte ReportData value.
To add a new backend, implement this interface and register a factory via RegisterAttester in an init() function — typically in a file named attester_<backend>.go.
type RATLSIssuer ¶
type RATLSIssuer struct {
// Backend selects the confidential computing hardware backend.
// Supported values: "tdx". Planned: "sev-snp", "sgx".
Backend string `json:"backend"`
// CACertPath is the path to the PEM-encoded intermediary CA certificate
// used to sign issued RA-TLS certificates.
CACertPath string `json:"ca_cert_path"`
// CAKeyPath is the path to the PEM-encoded private key of the
// intermediary CA.
CAKeyPath string `json:"ca_key_path"`
// contains filtered or unexported fields
}
RATLSIssuer is a Caddy TLS issuance module that produces RA-TLS certificates for Confidential VMs.
It implements:
- certmagic.KeyGenerator -- generates ECDSA P-256 key pairs.
- certmagic.Issuer -- issues CA-signed certificates with embedded attestation evidence.
- certmagic.Manager -- serves challenge-response certs for RA-TLS clients.
- caddy.Provisioner -- verifies hardware availability and loads the CA.
- caddyfile.Unmarshaler -- parses the "ra_tls" Caddyfile directive.
func (RATLSIssuer) CaddyModule ¶
func (RATLSIssuer) CaddyModule() caddy.ModuleInfo
CaddyModule returns the Caddy module information.
func (*RATLSIssuer) GenerateKey ¶
func (iss *RATLSIssuer) GenerateKey() (crypto.PrivateKey, error)
GenerateKey generates an ECDSA P-256 key pair and stores it temporarily so that the subsequent call to Issue can retrieve it for signing by the intermediary CA.
The returned crypto.PrivateKey is an *ecdsa.PrivateKey (which also implements crypto.Signer). Because the key is generated inside a TEE, it should be treated as sensitive -- configure an encrypted or in-memory storage backend in Caddy to avoid persisting it in plaintext.
func (*RATLSIssuer) GetCertificate ¶
func (iss *RATLSIssuer) GetCertificate(ctx context.Context, hello *tls.ClientHelloInfo) (*tls.Certificate, error)
GetCertificate implements certmagic.Manager. It inspects the TLS ClientHello for a RA-TLS challenge extension (0xffbb). If present and the challenge payload is available (requires the Privasys/go fork + "ratls" build tag), it generates a fresh ephemeral certificate with attestation evidence bound to the client's nonce — providing interactive, challenge-response attestation.
If no RA-TLS challenge is found, or if the extension is detected but the payload is unavailable (standard Go), it returns (nil, nil) to let certmagic serve a pre-issued certificate from the Issue() path.
func (*RATLSIssuer) Issue ¶
func (iss *RATLSIssuer) Issue(ctx context.Context, csr *x509.CertificateRequest) (*certmagic.IssuedCertificate, error)
Issue creates a CA-signed X.509 certificate with embedded attestation evidence from the configured hardware backend.
Steps performed:
- Validate the CSR signature.
- Determine the creation time (NotBefore), truncated to 1-minute precision.
- Compute ReportData = SHA-512( SHA-256(DER public key) || creation_time ).
- Request attestation evidence from the backend with the ReportData.
- Build a certificate template carrying the evidence in a backend- specific extension and sign it with the intermediary CA key.
func (*RATLSIssuer) IssuerKey ¶
func (iss *RATLSIssuer) IssuerKey() string
IssuerKey returns a string that uniquely identifies this issuer configuration, used by certmagic to namespace stored certificates.
func (*RATLSIssuer) Provision ¶
func (iss *RATLSIssuer) Provision(ctx caddy.Context) error
Provision validates configuration, loads the intermediary CA, and initialises the hardware-specific attestation backend.
func (*RATLSIssuer) UnmarshalCaddyfile ¶
func (iss *RATLSIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error
UnmarshalCaddyfile parses the "ra_tls" issuer directive:
tls {
issuer ra_tls {
backend tdx
ca_cert /path/to/intermediate-ca.crt
ca_key /path/to/intermediate-ca.key
}
}
type TDXAttester ¶
type TDXAttester struct {
// contains filtered or unexported fields
}
TDXAttester implements Attester for Intel TDX Confidential VMs using the Linux configfs-tsm interface.
func (*TDXAttester) CertExtension ¶
func (a *TDXAttester) CertExtension(quote []byte) pkix.Extension
CertExtension returns a non-critical X.509 extension with the TDX quote under OID 1.2.840.113741.1.5.5.1.6.