Skip to content

Commit

Permalink
Merge pull request #135 from THS-on/tpm2-quote-parsing
Browse files Browse the repository at this point in the history
Implement parsing of the tpm2/quote in ga10
  • Loading branch information
iolivergithub authored Nov 12, 2023
2 parents 204d9dc + 08f6ef9 commit bc64f80
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 101 deletions.
132 changes: 86 additions & 46 deletions ga10/protocols/a10httprestv2/public.go
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
package a10httprestv2

import(
"fmt"
"net/http"
"encoding/json"
"io/ioutil"
import (
"bytes"
"crypto/rand"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"

"a10/structures"
"a10/utilities"
)

const nonceSize int = 24

func Registration() (structures.Protocol) {
intents := []string{"tpm2/pcrs","tpm2/quote","uefi/eventlog","ima/asciilog","txt/log","sys/info"}
func Registration() structures.Protocol {
intents := []string{"tpm2/pcrs", "tpm2/quote", "uefi/eventlog", "ima/asciilog", "txt/log", "sys/info"}

return structures.Protocol{"A10HTTPRESTv2","HTTP protcol for Go based trust agents",Call,intents}
return structures.Protocol{"A10HTTPRESTv2", "HTTP protcol for Go based trust agents", Call, intents}
}


// THis is the function that is called by operations.attestation --- this is the entry point to the actual protocol part.
// It returns a "json" structure and a string with the body type.
// If requestFromTA returns and error, then it is encoded here and returned.
// The body type is *ERROR in these situations and the body should have a field "error": <some value>
func Call(e structures.Element, p structures.Policy, s structures.Session, aps map[string]interface{}) (map[string]interface{}, string) {
rtn, err :=requestFromTA(e,p,s,aps)
rtn, err := requestFromTA(e, p, s, aps)

if err != nil {
//rtn["error"] = structures.ClaimError{ "error", err.Error() }
rtn["error"] = err.Error()
return rtn,structures.CLAIMERROR

return rtn, structures.CLAIMERROR
} else {
return rtn,p.Intent
return rtn, p.Intent
}
}

Expand All @@ -48,89 +49,128 @@ func mergeMaps(m1 map[string]interface{}, m2 map[string]interface{}) map[string]
return merged
}

//This function performs the actual interaction with the TA
//This will be highly specific to the actual protocol and its implemented intents
// This function performs the actual interaction with the TA
// This will be highly specific to the actual protocol and its implemented intents
func requestFromTA(e structures.Element, p structures.Policy, s structures.Session, aps map[string]interface{}) (map[string]interface{}, error) {
var empty map[string]interface{} = make(map[string]interface{}) // this is an *instantiated* empty map used for error situations
var bodymap map[string]interface{} // this is used to store the result of the final unmarshalling of the body received from the TA

var empty map[string]interface{} = make(map[string]interface{}) // this is an *instantiated* empty map used for error situations
var bodymap map[string]interface{} // this is used to store the result of the final unmarshalling of the body received from the TA

// Parameters
//
// Some come from the element itself, eg: UEFI.eventlog
// Then those supplied by the policy and finally the additional parameters
// Only certain intents supply parameters and these are dealt with on a case by case basis here
//
//
// First we construct "ips" which is the intial set of parameters
//
// For sanity reasons (and Go's strong typing, the parameters is a plain key,value list)
var ips map[string]interface{} = make(map[string]interface{})

// always supply which device to use

// for specific intents for the a10httprestv2
if p.Intent=="tpm2/pcrs" {
// for specific intents for the a10httprestv2
if p.Intent == "tpm2/pcrs" {
ips["tpm2/device"] = (e.TPM2).Device
}

if p.Intent=="tpm2/quote" {
if p.Intent == "tpm2/quote" {
ips["tpm2/device"] = (e.TPM2).Device
ips["tpm2/akhandle"] = (e.TPM2).AK.Handle
nce := make([]byte,nonceSize)
nce := make([]byte, nonceSize)
_, _ = rand.Read(nce)
ips["tpm2/nonce"] = nce
}

if p.Intent=="uefi/eventlog" {
if p.Intent == "uefi/eventlog" {
ips["uefi/eventlog"] = (e.UEFI).Eventlog
}

if p.Intent=="ima/asciilog" {
if p.Intent == "ima/asciilog" {
ips["ima/ASCIIlog"] = (e.IMA).ASCIILog
}

if p.Intent=="txt/log" {
if p.Intent == "txt/log" {
ips["ima/log"] = (e.TXT).Log
}

// merge ips with policy parameters. The policy parameters take precidence

pps := mergeMaps(ips,p.Parameters)
cps := mergeMaps(pps,aps)
pps := mergeMaps(ips, p.Parameters)
cps := mergeMaps(pps, aps)

// Construct the call

postbody,err := json.Marshal(cps)
postbody, err := json.Marshal(cps)
if err != nil {
return empty,fmt.Errorf("JSON Marshalling failed: %w",err)
return empty, fmt.Errorf("JSON Marshalling failed: %w", err)
}

url := e.Endpoint+"/"+p.Intent
req,err := http.NewRequest("POST", url, bytes.NewBuffer(postbody))
req.Header.Set("Content-Type","application/json")
url := e.Endpoint + "/" + p.Intent
req, err := http.NewRequest("POST", url, bytes.NewBuffer(postbody))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp,err := client.Do(req)
resp, err := client.Do(req)

if err!=nil {
return empty,err // err will be the error from http.client.Do
if err != nil {
return empty, err // err will be the error from http.client.Do
}
defer resp.Body.Close()

taResponse, _ := ioutil.ReadAll(resp.Body)
taResponse, _ := io.ReadAll(resp.Body)
fmt.Println("*****************")
fmt.Printf("taReponse is %v",taResponse)
err = json.Unmarshal(taResponse,&bodymap)
fmt.Printf("taReponse is %v", taResponse)
err = json.Unmarshal(taResponse, &bodymap)
fmt.Println("bodymap")
fmt.Printf("%v",bodymap)
fmt.Printf("%v", bodymap)
fmt.Println("*****************")

if err != nil {
return empty,fmt.Errorf("JSON Unmarshalling reponse from TA: %w",err)
return empty, fmt.Errorf("JSON Unmarshalling reponse from TA: %w", err)
}

if resp.Status != "200 OK" { // is it always 200 ? This might cause issues later if the TA reponds otherwise!
return bodymap,fmt.Errorf("TA reports error %v with response %v",resp.Status,taResponse)
}
if resp.Status != "200 OK" { // is it always 200 ? This might cause issues later if the TA reponds otherwise!
return bodymap, fmt.Errorf("TA reports error %v with response %v", resp.Status, taResponse)
}

if p.Intent == "tpm2/quote" {
quoteValue, ok := bodymap["quote"]
if !ok {
return bodymap, fmt.Errorf("missing quote data in response")
}
quoteStr, ok := quoteValue.(string)
if !ok {
return bodymap, fmt.Errorf("quote value is not a string")
}
quoteBytes, err := base64.StdEncoding.DecodeString(quoteStr)
if err != nil {
return nil, fmt.Errorf("could not base64 decode quote")
}

signatureValue, ok := bodymap["signature"]
if !ok {
return bodymap, fmt.Errorf("missing signature data in response")
}
signatureStr, ok := signatureValue.(string)
if !ok {
return bodymap, fmt.Errorf("signature value is not a string")
}
signatureBytes, err := base64.StdEncoding.DecodeString(signatureStr)
if err != nil {
return nil, fmt.Errorf("could not base64 decode signature")
}

var attestableData utilities.AttestableData
attestableData.Decode(quoteBytes, signatureBytes)

// Try to parse the quote into a map representation for display purposes
parsed, err := attestableData.Parse()
if err != nil {
return bodymap, err
}
bodymap["parsed"] = parsed

}

return bodymap,nil
return bodymap, nil
}
19 changes: 10 additions & 9 deletions ga10/rules/tpm2rules/public.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,10 @@ func ValidSignature(claim structures.Claim, rule string, ev structures.ExpectedV
return structures.Success, "Quote was validated successfully", nil
}

// Constructs AttestableData struct with optional signature
// Constructs AttestableData struct with signature
// TODO find way to cache this in the session object
func getQuote(claim structures.Claim) (*utilities.AttestableData, error) {
quoteData, ok := (claim.Body)["quote_bytes"]
quoteData, ok := (claim.Body)["quote"]
if !ok {
return nil, fmt.Errorf("claim does not contain quote")

Expand All @@ -123,13 +123,14 @@ func getQuote(claim structures.Claim) (*utilities.AttestableData, error) {
return nil, fmt.Errorf("could not base64 decode quote")
}
var signatureBytes []byte
signatureData, ok := (claim.Body)["signature_bytes"]
if ok {
signatureStr := signatureData.(string)
signatureBytes, err = base64.StdEncoding.DecodeString(signatureStr)
if err != nil {
return nil, fmt.Errorf("could not base64 decode signature")
}
signatureData, ok := (claim.Body)["signature"]
if !ok {
return nil, fmt.Errorf("claim does not contain a signature")
}
signatureStr := signatureData.(string)
signatureBytes, err = base64.StdEncoding.DecodeString(signatureStr)
if err != nil {
return nil, fmt.Errorf("could not base64 decode signature")
}

var quote utilities.AttestableData
Expand Down
2 changes: 1 addition & 1 deletion ga10/services/webui/initwebapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func StartWebUI() {
// It is done this way so we can have different templates for each operation...a bit ugly, but html/template is not jinja
//dev.to/ykyuen/setup-nested-html-template-in-go-echo-web-framework-d9b

functions := template.FuncMap{"defaultMessage": DefaultMessage, "epochToUTC": EpochToUTC, "base64decode": Base64decode, "encodeAsHexString": EncodeAsHexString, "tcgHash": TCGHash, "tcgAlg": TCGAlg}
functions := template.FuncMap{"defaultMessage": DefaultMessage, "epochToUTC": EpochToUTC, "base64decode": Base64decode, "encodeAsHexString": EncodeAsHexString, "tcgAlg": TCGAlg}

templates["home.html"] = template.Must(template.ParseFS(WPFS, T+"home.html", T+"base.html"))
templates["help.html"] = template.Must(template.ParseFS(WPFS, T+"help.html", T+"base.html"))
Expand Down
44 changes: 13 additions & 31 deletions ga10/services/webui/templatefunctions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,42 @@ package webui

import (
"fmt"
"time"
"strconv"
"time"

"encoding/base64"
"encoding/hex"

"a10/structures"
"a10/utilities"

"github.com/google/go-tpm/legacy/tpm2"
)

// No idea if this works but it is supposed to be in the html files
func EpochToUTC(epoch structures.Timestamp) string {
sec,err := strconv.ParseInt(fmt.Sprintf("%v",epoch),10,64)
func EpochToUTC(epoch structures.Timestamp) string {
sec, err := strconv.ParseInt(fmt.Sprintf("%v", epoch), 10, 64)
if err != nil {
t := time.Unix(0,0)
return fmt.Sprintf("%v",t.UTC())
t := time.Unix(0, 0)
return fmt.Sprintf("%v", t.UTC())
}
t := time.Unix(0,sec)
return fmt.Sprintf("%v",t.UTC())
t := time.Unix(0, sec)
return fmt.Sprintf("%v", t.UTC())
}

func DefaultMessage() string {
return "Single invocation from WebUI at "+EpochToUTC(utilities.MakeTimestamp())
return "Single invocation from WebUI at " + EpochToUTC(utilities.MakeTimestamp())
}

func Base64decode(u string) string {
d,_ := base64.StdEncoding.DecodeString(u)
d, _ := base64.StdEncoding.DecodeString(u)
return string(d)
}

func EncodeAsHexString(b []byte) string {
return hex.EncodeToString(b)
}


//These two functions could be combined
//See: TCG Algorithm Registry Revision 1.34

func TCGHash(h float64) string { //Don't you just love JSON...the value is an int, but JSON interprets it as a float64 ... seriously!
if h==11 {
return "sha256"
} else if h==3 {
return "sha1"
} else {
return "Unknown hash function"
}
func TCGAlg(h int32) string {
return tpm2.Algorithm(h).String()
}



func TCGAlg(h float64) string { //Don't you just love JSON...the value is an int, but JSON interprets it as a float64 ... seriously!
if h==20 {
return "RSASSA"
} else {
return "Unknown or undefined. Might be ECC?"
}
}
2 changes: 1 addition & 1 deletion ga10/services/webui/templates/claim.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ <h5>Body: </h5>

{{ if eq .BodyType "tpm2/quote" }}
<p><span class="badge bg-primary"> {{ .BodyType }}</span></p>
{{ template "claim_quote.html" .Body }}
{{ template "claim_quote.html" .Body.parsed }}
{{ end }}

<hr/>
Expand Down
16 changes: 8 additions & 8 deletions ga10/services/webui/templates/claim_quote.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,35 @@
<tbody>

<tr><td><b>PCR Digest</b></td><td>{{ .quote.AttestedQuoteInfo.PCRDigest }}</td></tr>
<tr><td><b>PCR Selection</b></td><td><span class="badge bg-primary">{{ tcgHash .quote.AttestedQuoteInfo.PCRSelection.Hash }}</span> :
<tr><td><b>PCR Selection</b></td><td><span class="badge bg-primary">{{ tcgAlg .quote.AttestedQuoteInfo.PCRSelection.Hash }}</span> :
{{ range .quote.AttestedQuoteInfo.PCRSelection.PCRs }}
<span class="badge bg-secondary">{{ . }}</span>
{{ end }}
</td></tr>
<tr><td><b>Firmware</b></td><td>{{ .quote.FirmwareVersion }}</td></tr>

<tr><td><b>Magic & Type</b></td><td>{{ .quote.Magic }}, {{ .Quote.Type }}</td></tr>
<tr><td><b>Magic & Type</b></td><td>{{ .quote.Magic }}, {{ .quote.Type }}</td></tr>
<tr><td><b>Extra Data</b></td><td>{{ .quote.ExtraData }}</td></tr>

<tr><td><b>Clock</b></td><td>{{ .quote.ClockInfo.Clock }}</td></tr>
<tr><td><b>Reset Count</b></td><td>{{ .quote.ClockInfo.ResetCount }}</td></tr>
<tr><td><b>Restart Count</b></td><td>{{ .quote.ClockInfo.RestartCount }}</td></tr>
<tr><td><b>Safe</b></td><td>
{{ if eq .quote.ClockInfo.Safe 1.0 }}
{{ if .quote.ClockInfo.Safe}}
<span class="badge bg-success">Safe ({{ .quote.ClockInfo.Safe }})</span>
{{ else }}
<span class="badge bg-danger">Unsafe ({{ .quote.ClockInfo.Safe }})</span>
{{ end }}
</td></tr>

<tr><td><b>Qualified Signer</b></td><td><span class="badge bg-primary">{{ tcgHash .quote.QualifiedSigner.Digest.Alg }}</span> {{ .quote.QualifiedSigner.Digest.Value }}</td></tr>
<tr><td><b>Qualified Signer</b></td><td><span class="badge bg-primary">{{ .quote.QualifiedSigner.Digest.Alg }}</span> {{ .quote.QualifiedSigner.Digest.Value }}</td></tr>


<tr><td><b>Signing Algorithm</b></td><td>{{ tcgAlg .signature.Alg }}</td></tr>
{{ if eq .signature.Alg 20.0 }}
<tr><td><b>RSA</b></td><td><span class="badge bg-primary">{{ tcgHash .signature.RSA.HashAlg }}</span> {{ .signature.RSA.Signature }}</td></tr>
<tr><td><b>Signing Algorithm</b></td><td>{{ tcgAlg .signature.Alg }}</td></tr>
{{ if eq .signature.Alg 20 }}
<tr><td><b>RSA</b></td><td><span class="badge bg-primary">{{ tcgAlg .signature.RSA.HashAlg }}</span> {{ .signature.RSA.Signature }}</td></tr>
{{ else }}
<tr><td><b>ECC</b></td><td><span class="badge bg-primary">{{ tcgHash .signature.ECC.HashAlg }}</span> {{ .signature.ECC.Sígnature }}</td></tr>
<tr><td><b>ECC</b></td><td><span class="badge bg-primary">{{ tcgAlg .signature.ECC.HashAlg }}</span> {{ .signature.ECC.Signature }}</td></tr>
{{ end }}


Expand Down
Loading

0 comments on commit bc64f80

Please sign in to comment.