Skip to content

Commit

Permalink
feat: add secure HTTP client
Browse files Browse the repository at this point in the history
  • Loading branch information
natesales committed Jan 16, 2025
1 parent 500a0d7 commit 81be56d
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 0 deletions.
42 changes: 42 additions & 0 deletions cmd/httpclient/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"bytes"
"io"
"log"

"github.com/tinfoilanalytics/verifier/pkg/client"
)

func main() {
client := client.NewSecureClient(
"inference-enclave.tinfoil.sh",
"tinfoilanalytics/nitro-enclave-build-demo",
)
if err := client.Verify(); err != nil {
log.Fatal(err)
}

vs := client.VerificationState()
log.Printf("Cert fingerprint: %x\n", vs.CertFingerprint)
log.Printf("EIF hash: %s\n", vs.EIFHash)

resp, err := client.HTTPClient().Post("https://inference-enclave.tinfoil.sh/api/chat", "application/json", bytes.NewBufferString(`{
"model": "llama3.2:1b",
"stream": false,
"messages": [
{"role": "user","content": "What is 1+1?"}
]
}`))
if err != nil {
log.Fatal(err)
}

body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()

log.Println(string(body))
}
84 changes: 84 additions & 0 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package client

import (
"fmt"
"net/http"

"github.com/tinfoilanalytics/verifier/pkg/attestation"
"github.com/tinfoilanalytics/verifier/pkg/github"
"github.com/tinfoilanalytics/verifier/pkg/sigstore"
)

type EnclaveState struct {
CertFingerprint []byte
EIFHash string
}

type SecureClient struct {
enclave, repo string

verifiedState *EnclaveState
}

func NewSecureClient(enclave, repo string) *SecureClient {
return &SecureClient{
enclave: enclave,
repo: repo,
}
}

// Verify verifies the enclave against the latest code release
func (s *SecureClient) Verify() error {
_, eifHash, err := github.FetchLatestRelease(s.repo)
if err != nil {
return fmt.Errorf("failed to fetch latest release: %v", err)
}

sigstoreBundle, err := github.FetchAttestationBundle(s.repo, eifHash)
if err != nil {
return fmt.Errorf("failed to fetch attestation bundle: %v", err)
}

sigstoreTrustRoot, err := sigstore.FetchTrustRoot()
if err != nil {
return fmt.Errorf("failed to fetch trust root: %v", err)
}

codeMeasurements, err := sigstore.VerifyMeasurementAttestation(
sigstoreTrustRoot, sigstoreBundle,
eifHash, s.repo,
)
if err != nil {
return fmt.Errorf("failed to verify attested measurements: %v", err)
}

enclaveAttestation, err := attestation.Fetch(s.enclave)
if err != nil {
return fmt.Errorf("failed to fetch enclave measurements: %v", err)
}
enclaveMeasurements, certFP, err := enclaveAttestation.Verify()
if err != nil {
return fmt.Errorf("failed to verify enclave measurements: %v", err)
}

err = codeMeasurements.Equals(enclaveMeasurements)
if err == nil {
s.verifiedState = &EnclaveState{
CertFingerprint: certFP,
EIFHash: eifHash,
}
}
return err
}

// VerificationState returns the last verified enclave state
func (s *SecureClient) VerificationState() *EnclaveState {
return s.verifiedState
}

// HTTPClient returns a HTTP client that only accepts TLS connections to the verified enclave
func (s *SecureClient) HTTPClient() *http.Client {
return &http.Client{
Transport: &TLSBoundRoundTripper{s.verifiedState.CertFingerprint},
}
}
37 changes: 37 additions & 0 deletions pkg/client/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package client

import (
"crypto/sha256"
"crypto/subtle"
"errors"
"net/http"
)

var (
ErrNoTLS = errors.New("no TLS connection")
ErrCertMismatch = errors.New("certificate fingerprint mismatch")
)

type TLSBoundRoundTripper struct {
ExpectedCertFP []byte
}

var _ http.RoundTripper = &TLSBoundRoundTripper{}

func (t *TLSBoundRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
resp, err := http.DefaultTransport.RoundTrip(r)
if err != nil {
return nil, err
}

if resp.TLS == nil {
return nil, ErrNoTLS
}

certFP := sha256.Sum256(resp.TLS.PeerCertificates[0].Raw)
if subtle.ConstantTimeCompare(t.ExpectedCertFP, certFP[:]) != 1 {
return nil, ErrCertMismatch
}

return resp, err
}

0 comments on commit 81be56d

Please sign in to comment.