Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/semgrep.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
on:
pull_request: {}
workflow_dispatch: {}
push:
push:
branches:
- main
schedule:
- cron: '0 0 * * *'
permissions:
contents: read
name: Semgrep
jobs:
semgrep:
Expand All @@ -19,5 +21,5 @@ jobs:
container:
image: semgrep/semgrep
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- run: semgrep ci --verbose
1 change: 0 additions & 1 deletion commands/_test_store.json

This file was deleted.

46 changes: 13 additions & 33 deletions commands/attester.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package commands

import (
"bytes"
"crypto/elliptic"
"encoding/binary"
"encoding/hex"
"fmt"
Expand All @@ -11,9 +10,8 @@ import (
"net/http/httputil"
"strconv"

"github.com/cloudflare/pat-go/ecdsa"

pat "github.com/cloudflare/pat-go"
"github.com/cloudflare/pat-go/tokens/type2"
"github.com/cloudflare/pat-go/tokens/type3"
log "github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
Expand All @@ -38,7 +36,7 @@ type ClientState struct {

type TestAttester struct {
client *http.Client
attester *pat.RateLimitedAttester
attester *type3.RateLimitedAttester
clientState map[string]ClientState
}

Expand All @@ -51,21 +49,21 @@ func parseStructuredBinaryHeader(req *http.Request, header string) ([]byte, erro
}

type MemoryClientStateCache struct {
cache map[string]*pat.ClientState
cache map[string]*type3.ClientState
}

func NewMemoryClientStateCache() MemoryClientStateCache {
return MemoryClientStateCache{
cache: make(map[string]*pat.ClientState),
cache: make(map[string]*type3.ClientState),
}
}

func (c MemoryClientStateCache) Get(clientID string) (*pat.ClientState, bool) {
func (c MemoryClientStateCache) Get(clientID string) (*type3.ClientState, bool) {
state, ok := c.cache[clientID]
return state, ok
}

func (c MemoryClientStateCache) Put(clientID string, state *pat.ClientState) {
func (c MemoryClientStateCache) Put(clientID string, state *type3.ClientState) {
c.cache[clientID] = state
}

Expand Down Expand Up @@ -120,8 +118,8 @@ func (a TestAttester) handleAttestationRequest(w http.ResponseWriter, req *http.
tokenReq.Header.Set("Content-Type", tokenRequestMediaType)

tokenType := binary.BigEndian.Uint16(requestBody)
if tokenType == pat.RateLimitedTokenType {
var rateLimitedTokenRequest pat.RateLimitedTokenRequest
if tokenType == type3.RateLimitedTokenType {
var rateLimitedTokenRequest type3.RateLimitedTokenRequest
if !rateLimitedTokenRequest.Unmarshal(requestBody) {
log.Println("Failed parsing client TokenRequest", err)
http.Error(w, "Failed parsing client TokenRequest", 400)
Expand Down Expand Up @@ -153,32 +151,14 @@ func (a TestAttester) handleAttestationRequest(w http.ResponseWriter, req *http.
clientID = "default"
}

var tokenRequest pat.RateLimitedTokenRequest
var tokenRequest type3.RateLimitedTokenRequest
if !tokenRequest.Unmarshal(requestBody) {
log.Println("Failed decoding client request body:", err)
http.Error(w, err.Error(), 400)
return
}

// XXX(caw): the pat-go interface for VerifyRequest and FinalizeIndex is wildly inconsistent. One accepts
// encoded byte arrays, whereas the other accepts decoded things of a specific type (public and private keys
// and whatnot). Pick one!

// Deserialize the request key
curve := elliptic.P384()
x, y := elliptic.UnmarshalCompressed(curve, clientKeyEnc)
clientKey := &ecdsa.PublicKey{
curve, x, y,
}

blindKey, err := ecdsa.CreateKey(elliptic.P384(), requestBlind)
if err != nil {
log.Println("Invalid client blind")
http.Error(w, "Invalid client blind", 400)
return
}

err = a.attester.VerifyRequest(tokenRequest, blindKey, clientKey, anonOrigin)
err = a.attester.VerifyRequest(tokenRequest, requestBlind, clientKeyEnc, anonOrigin)
if err != nil {
log.Println("Invalid request (signature verification failed)")
http.Error(w, "Invalid request (signature verification failed)", 400)
Expand Down Expand Up @@ -284,7 +264,7 @@ func (a TestAttester) handleAttestationRequest(w http.ResponseWriter, req *http.

w.Header().Set("content-type", tokenResponseMediaType)
w.Write(blindSignature)
} else if tokenType == pat.BasicPublicTokenType {
} else if tokenType == type2.BasicPublicTokenType {
tokenReqEnc, _ := httputil.DumpRequest(tokenReq, false)
log.Println("Forwarding attestation token request:", string(tokenReqEnc))

Expand Down Expand Up @@ -332,7 +312,7 @@ func startAttester(c *cli.Context) error {

attester := TestAttester{
client: &http.Client{},
attester: pat.NewRateLimitedAttester(NewMemoryClientStateCache()),
attester: type3.NewRateLimitedAttester(NewMemoryClientStateCache()),
clientState: make(map[string]ClientState),
}

Expand Down
78 changes: 40 additions & 38 deletions commands/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import (
"strconv"
"strings"

"github.com/cloudflare/pat-go"
"github.com/cloudflare/pat-go/tokens"
"github.com/cloudflare/pat-go/tokens/type2"
"github.com/cloudflare/pat-go/tokens/type3"
log "github.com/sirupsen/logrus"
"github.com/urfave/cli"
"golang.org/x/crypto/hkdf"
Expand All @@ -45,21 +47,21 @@ func fetchIssuerConfig(issuer string) (IssuerConfig, error) {
return issuerConfig, nil
}

func fetchIssuerNameKey(nameKeyURI string) (pat.EncapKey, error) {
func fetchIssuerNameKey(nameKeyURI string) (type3.EncapKey, error) {
resp, err := http.Get(nameKeyURI)
if err != nil {
return pat.EncapKey{}, err
return type3.EncapKey{}, err
}
defer resp.Body.Close()

nameKeyEnc, err := ioutil.ReadAll(resp.Body)
if err != nil {
return pat.EncapKey{}, err
return type3.EncapKey{}, err
}

nameKey, err := pat.UnmarshalEncapKey(nameKeyEnc)
nameKey, err := type3.UnmarshalEncapKey(nameKeyEnc)
if err != nil {
return pat.EncapKey{}, err
return type3.EncapKey{}, err
}

return nameKey, nil
Expand All @@ -72,40 +74,40 @@ func computeAnonymousOrigin(secret []byte, origin string) ([]byte, error) {
return originID, err
}

func fetchBasicToken(client pat.BasicPublicClient, attester string, challenge []byte, publicKeyEnc []byte) (pat.Token, error) {
func fetchBasicToken(client type2.BasicPublicClient, attester string, challenge []byte, publicKeyEnc []byte) (tokens.Token, error) {
nonce := make([]byte, 32)
rand.Reader.Read(nonce)

tokenKeyID := sha256.Sum256(publicKeyEnc)
publicKey, err := unmarshalTokenKey(publicKeyEnc)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}

tokenChallenge, err := pat.UnmarshalTokenChallenge(challenge)
tokenChallenge, err := tokens.UnmarshalTokenChallenge(challenge)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}

issuerConfig, err := fetchIssuerConfig(tokenChallenge.IssuerName)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}

tokenRequestState, err := client.CreateTokenRequest(challenge, nonce, tokenKeyID[:], publicKey)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}
tokenRequestEnc := tokenRequestState.Request().Marshal()

requestURI, err := composeURL(tokenChallenge.IssuerName, issuerConfig.RequestURI)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}

req, err := http.NewRequest(http.MethodPost, requestURI, bytes.NewBuffer(tokenRequestEnc))
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}
req.Header.Set("Content-Type", tokenRequestMediaType)

Expand All @@ -115,10 +117,10 @@ func fetchBasicToken(client pat.BasicPublicClient, attester string, challenge []
httpClient := &http.Client{}
resp, err := httpClient.Do(req)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}
if resp.StatusCode != 200 {
return pat.Token{}, fmt.Errorf("Request failed with error %d", resp.StatusCode)
return tokens.Token{}, fmt.Errorf("Request failed with error %d", resp.StatusCode)
}
defer resp.Body.Close()

Expand All @@ -127,13 +129,13 @@ func fetchBasicToken(client pat.BasicPublicClient, attester string, challenge []

tokenResponse, err := ioutil.ReadAll(resp.Body)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}

return tokenRequestState.FinalizeToken(tokenResponse)
}

func fetchRateLimitedToken(client pat.RateLimitedClient, clientOriginSecret []byte, clientID string, attester string, origin string, challenge []byte, publicKeyEnc []byte) (pat.Token, error) {
func fetchRateLimitedToken(client type3.RateLimitedClient, clientOriginSecret []byte, clientID string, attester string, origin string, challenge []byte, publicKeyEnc []byte) (tokens.Token, error) {
blind := make([]byte, 32)
rand.Reader.Read(blind)

Expand All @@ -143,56 +145,56 @@ func fetchRateLimitedToken(client pat.RateLimitedClient, clientOriginSecret []by
tokenKeyID := sha256.Sum256(publicKeyEnc)
publicKey, err := unmarshalTokenKey(publicKeyEnc)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}

tokenChallenge, err := pat.UnmarshalTokenChallenge(challenge)
tokenChallenge, err := tokens.UnmarshalTokenChallenge(challenge)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}

issuerConfig, err := fetchIssuerConfig(tokenChallenge.IssuerName)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}

issuerNameKeyURI, err := composeURL(tokenChallenge.IssuerName, issuerConfig.IssuerEncapKeyURI)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}
originNameKey, err := fetchIssuerNameKey(issuerNameKeyURI)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}

tokenRequestState, err := client.CreateTokenRequest(challenge, nonce, blind, tokenKeyID[:], publicKey, origin, originNameKey)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}
tokenRequestEnc := tokenRequestState.Request().Marshal()

anonymousOriginID, err := computeAnonymousOrigin(clientOriginSecret, origin)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}

issuerName, err := composeURL(tokenChallenge.IssuerName, issuerConfig.RequestURI)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}
issuerUrl, err := url.Parse(issuerName)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}

tokenRequestURI, err := composeURL(attester, "/token-request")
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}

req, err := http.NewRequest(http.MethodPost, tokenRequestURI, bytes.NewBuffer(tokenRequestEnc))
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}
q := req.URL.Query()
q.Add("issuer", issuerUrl.Host)
Expand All @@ -212,10 +214,10 @@ func fetchRateLimitedToken(client pat.RateLimitedClient, clientOriginSecret []by
httpClient := &http.Client{}
resp, err := httpClient.Do(req)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}
if resp.StatusCode != 200 {
return pat.Token{}, fmt.Errorf("Request failed with error %d", resp.StatusCode)
return tokens.Token{}, fmt.Errorf("Request failed with error %d", resp.StatusCode)
}
defer resp.Body.Close()

Expand All @@ -224,7 +226,7 @@ func fetchRateLimitedToken(client pat.RateLimitedClient, clientOriginSecret []by

tokenResponse, err := ioutil.ReadAll(resp.Body)
if err != nil {
return pat.Token{}, err
return tokens.Token{}, err
}

return tokenRequestState.FinalizeToken(tokenResponse)
Expand Down Expand Up @@ -289,8 +291,8 @@ func runClientFetch(c *cli.Context) error {
}
}

rateLimitedClient := pat.NewRateLimitedClientFromSecret(clientRequestSecret)
basicClient := pat.NewBasicPublicClient()
rateLimitedClient := type3.NewRateLimitedClientFromSecret(clientRequestSecret)
basicClient := type2.NewBasicPublicClient()

resourceURI, err := composeURL(origin, resource)
if err != nil {
Expand All @@ -309,10 +311,10 @@ func runClientFetch(c *cli.Context) error {
req.Header.Add(headerTokenAttributeCrossOrigin, "true")
}
if tokenType == "basic" {
req.Header.Add(headerTokenType, strconv.Itoa(int(pat.BasicPublicTokenType)))
req.Header.Add(headerTokenType, strconv.Itoa(int(type2.BasicPublicTokenType)))
}
if tokenType == "rate-limited" {
req.Header.Add(headerTokenType, strconv.Itoa(int(pat.RateLimitedTokenType)))
req.Header.Add(headerTokenType, strconv.Itoa(int(type3.RateLimitedTokenType)))
}
req.Header.Add(headerTokenAttributeChallengeCount, strconv.Itoa(tokenCount))
resp, err := httpClient.Do(req)
Expand Down Expand Up @@ -379,7 +381,7 @@ func runClientFetch(c *cli.Context) error {
tokenChallenges = append(tokenChallenges, challengeEnc)

tokenType := binary.BigEndian.Uint16(challengeBlob)
if tokenType == pat.RateLimitedTokenType {
if tokenType == type3.RateLimitedTokenType {
log.Debugln("Fetching rate-limited token...")
token, err := fetchRateLimitedToken(rateLimitedClient, clientOriginSecret, id, attester, origin, challengeBlob, tokenKeyEnc)
if err != nil {
Expand Down
Loading