Feat/pcr yaml output (#222)

* remove extra output and provide yaml option
* Add some explanation on how yaml format could be used.
This commit is contained in:
Fabian Kammel 2022-06-20 13:57:25 +02:00 committed by GitHub
parent d856b0cd86
commit a1103b6da6
4 changed files with 93 additions and 16 deletions

1
hack/pcr-reader/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
pcr-reader

View File

@ -88,6 +88,17 @@ PCRs:
}
```
### Extend Config
To set measurement values in Constellation config, use `yaml` format option.
Optionally filter down results measurements per cloud provider:
Azure
```bash
./pcr-reader --coord-ip ${COORD_IP} --format yaml | yq e 'del(.[0,6,10,11,12,13,14,15,16,17,18,19,20,21,22,23])' -
```
## Meaning of PCR values
An overview about what data is measured into the different registers can be found [in the TPM spec](https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClient_PFP_r1p05_v23_pub.pdf#%5B%7B%22num%22%3A157%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C33%2C400%2C0%5D).

View File

@ -4,6 +4,7 @@ import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
"flag"
@ -25,12 +26,14 @@ import (
"github.com/spf13/afero"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"gopkg.in/yaml.v3"
)
var (
coordIP = flag.String("coord-ip", "", "IP of the VM the Coordinator is running on")
coordinatorPort = flag.String("coord-port", "9000", "Port of the Coordinator's pub API")
export = flag.String("o", "", "Write PCRs, formatted as Go code, to file")
format = flag.String("format", "json", "Output format: json, yaml (default json)")
quiet = flag.Bool("q", false, "Set to disable output")
timeout = flag.Duration("timeout", 2*time.Minute, "Wait this duration for the Coordinator to become available")
)
@ -38,7 +41,6 @@ var (
func main() {
flag.Parse()
fmt.Printf("connecting to Coordinator at %s:%s\n", *coordIP, *coordinatorPort)
addr := net.JoinHostPort(*coordIP, *coordinatorPort)
ctx, cancel := context.WithTimeout(context.Background(), *timeout)
defer cancel()
@ -71,7 +73,7 @@ func main() {
}
if !*quiet {
if err := printPCRs(os.Stdout, pcrs); err != nil {
if err := printPCRs(os.Stdout, pcrs, *format); err != nil {
log.Fatal(err)
}
}
@ -82,6 +84,20 @@ func main() {
}
}
type Measurements map[uint32][]byte
// MarshalYAML forces that measurements are written as base64. Default would
// be to print list of bytes.
func (m Measurements) MarshalYAML() (interface{}, error) {
base64Map := make(map[uint32]string)
for key, value := range m {
base64Map[key] = base64.StdEncoding.EncodeToString(value[:])
}
return base64Map, nil
}
// connectToCoordinator connects to the Constellation Coordinator and returns its attestation document.
func connectToCoordinator(ctx context.Context, addr string, tlsConfig *tls.Config) error {
conn, err := grpc.DialContext(
@ -145,12 +161,33 @@ func validatePCRAttDoc(attDocRaw []byte) (map[uint32][]byte, error) {
}
// printPCRs formates and prints PCRs to the given writer.
func printPCRs(w io.Writer, pcrs map[uint32][]byte) error {
// format can be one of 'json' or 'yaml'. If it doesnt match defaults to 'json'.
func printPCRs(w io.Writer, pcrs map[uint32][]byte, format string) error {
switch format {
case "json":
return printPCRsJSON(w, pcrs)
case "yaml":
return printPCRsYAML(w, pcrs)
default:
return printPCRsJSON(w, pcrs)
}
}
func printPCRsYAML(w io.Writer, pcrs Measurements) error {
pcrYAML, err := yaml.Marshal(pcrs)
if err != nil {
return err
}
fmt.Fprintf(w, "%s", string(pcrYAML))
return nil
}
func printPCRsJSON(w io.Writer, pcrs map[uint32][]byte) error {
pcrJSON, err := json.MarshalIndent(pcrs, "", " ")
if err != nil {
return err
}
fmt.Fprintf(w, "PCRs:\n%s\n", string(pcrJSON))
fmt.Fprintf(w, "%s", string(pcrJSON))
return nil
}

View File

@ -227,20 +227,48 @@ func mustMarshalAttDoc(t *testing.T, attDoc vtpm.AttestationDocument) []byte {
}
func TestPrintPCRs(t *testing.T) {
assert := assert.New(t)
pcrs := map[uint32][]byte{
0: {0x1, 0x2, 0x3},
1: {0x1, 0x2, 0x3},
2: {0x1, 0x2, 0x3},
testCases := map[string]struct {
pcrs map[uint32][]byte
format string
}{
"json": {
pcrs: map[uint32][]byte{
0: {0x1, 0x2, 0x3},
1: {0x1, 0x2, 0x3},
2: {0x1, 0x2, 0x3},
},
format: "json",
},
"empty format": {
pcrs: map[uint32][]byte{
0: {0x1, 0x2, 0x3},
1: {0x1, 0x2, 0x3},
2: {0x1, 0x2, 0x3},
},
format: "",
},
"yaml": {
pcrs: map[uint32][]byte{
0: {0x1, 0x2, 0x3},
1: {0x1, 0x2, 0x3},
2: {0x1, 0x2, 0x3},
},
format: "yaml",
},
}
var out bytes.Buffer
err := printPCRs(&out, pcrs)
assert.NoError(err)
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
for idx, pcr := range pcrs {
assert.Contains(out.String(), fmt.Sprintf("\"%d\": ", idx))
assert.Contains(out.String(), fmt.Sprintf(": \"%s\"", base64.StdEncoding.EncodeToString(pcr)))
var out bytes.Buffer
err := printPCRs(&out, tc.pcrs, tc.format)
assert.NoError(err)
for idx, pcr := range tc.pcrs {
assert.Contains(out.String(), fmt.Sprintf("%d", idx))
assert.Contains(out.String(), base64.StdEncoding.EncodeToString(pcr))
}
})
}
}