mirror of
https://github.com/google/go-attestation.git
synced 2024-12-30 09:48:59 +00:00
a801f7333b
* Upstream the verifier sub-library. * Rename proto package to go_attestation
243 lines
8.9 KiB
Go
243 lines
8.9 KiB
Go
package verifier
|
|
|
|
import (
|
|
"crypto/rsa"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
"testing"
|
|
|
|
tpb "github.com/google/go-attestation/proto"
|
|
"github.com/google/go-tpm-tools/simulator"
|
|
"github.com/google/go-tpm/tpm2"
|
|
"github.com/google/go-tpm/tpmutil"
|
|
)
|
|
|
|
func decodeBase64(in string, t *testing.T) []byte {
|
|
out, err := base64.StdEncoding.DecodeString(in)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func TestVerifyAIK(t *testing.T) {
|
|
pub := decodeBase64("AAEACwAFBHIAIJ3/y/NsODrmmfuYaNxty4nXFTiEvigDkiwSQVi/rSKuABAAFAAECAAAAAAAAQC/08gj/04z4xGMIVTmr02lzhI5epufXgU831xEpf2qpXfvtNGUfqTcgWF2EUux2HDPqgcj59dtXRobQdlr4uCGNzfZIGAej4JusLa4MjpG6W2DtJPot6F1Mry63talzJ36U47niy9Iesd34CO2p9Xk3+86ZmBnQ6PQ2roUNK3l7bKz6cFLM9drOLwCqU0AUl6pHvzYPPz+xXsPl3iaA2cM97oneUiJNmJM7wtR9OcaKyIA4wVlX5TndB9NwWq5Iuj8q2Sp40Dg0noXXGSPliAtVD8flkXtAcuI9UHkQbzu9cGPRdSJPMn743GONg3bYalFtcgh2VpACXkPbXB32J7B", t)
|
|
creation := decodeBase64("AAAAAAAg47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFUBAAsAIgALWI9hwDRB3zYSkannqM5z0J1coQNA1Jz/oCRxJQwTaNwAIgALmyFYBhHeIU3FUKIAPgXFD3NXyasP3siQviDEyH7avu4AAA==", t)
|
|
attest := decodeBase64("/1RDR4AaACIAC41+jhmEOue1MZhJjIk79ENar6i15rBvamXLpQnGTBCOAAAAAAAAD3GRNfU4syzJ1jQGATDCDteFC5C4ACIAC3ToMYGy9GXxcf8A0HvOuLOHbU7HPEppM47C7CMcU8TtACBDmJFUFO1f5+BYevaYdd3VtfMCsxIuHhoTZJczzLP2BA==", t)
|
|
sig := decodeBase64("ABQABAEALVzJSnKRJU39gHjETaI89/sM1L6HwBPGNekw6NojSW8bwD5/W1cLRDakCsYKUQu68mmbjs8xaIVBRvVM2YWP10tbTWNB0iJc9b8rERhkk3QIIFm/XsiVZsb0mysTxfeh8zygaAKQ/50sYyzp+raD0Ho0mYIRKJOEdQ6chsBflM3eB8mCXGTugUfrET80q3iu0gncaKWbfxQaQUb9ZTPSJrTN64HQ9tlOfnGT+8++WA3hV0NqKMnoAqiI9GZnI5MPXs6XxEncu/GJLJpAYZakBiS74Jvlr34Pur32B4xjm1M25AUGHEIgb6r49S0sV+hzaKu45858lQRMXj01GcyBhw==", t)
|
|
|
|
verificationResults, err := VerifyAIK(2, &tpb.AikInfo{
|
|
TpmAikInfo: &tpb.AikInfo_Tpm20{
|
|
Tpm20: &tpb.Tpm20AikInfo{
|
|
PublicBlob: pub,
|
|
CreationData: creation,
|
|
AttestationData: attest,
|
|
SignatureData: sig,
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("VerifyAIK() returned err: %v", err)
|
|
}
|
|
if !verificationResults.GetSucceeded() {
|
|
t.Errorf("verification.Succeeded = %v, want true", verificationResults.GetSucceeded())
|
|
}
|
|
}
|
|
|
|
func setupSimulatedTPM(t *testing.T) *simulator.Simulator {
|
|
t.Helper()
|
|
tpm, err := simulator.Get()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return tpm
|
|
}
|
|
|
|
func allPCRs(tpm io.ReadWriter, hash tpm2.Algorithm) (map[uint32][]byte, error) {
|
|
numPCRs := 24
|
|
out := map[uint32][]byte{}
|
|
|
|
// The TPM 2.0 spec says that the TPM can partially fulfill the
|
|
// request. As such, we repeat the command up to 8 times to get all
|
|
// 24 PCRs.
|
|
for i := 0; i < numPCRs; i++ {
|
|
// Build a selection structure, specifying all PCRs we do
|
|
// not have the value for.
|
|
sel := tpm2.PCRSelection{Hash: hash}
|
|
for pcr := 0; pcr < numPCRs; pcr++ {
|
|
if _, present := out[uint32(pcr)]; !present {
|
|
sel.PCRs = append(sel.PCRs, pcr)
|
|
}
|
|
}
|
|
|
|
// Ask the TPM for those PCR values.
|
|
ret, err := tpm2.ReadPCRs(tpm, sel)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("tpm2.ReadPCRs(%+v) failed with err: %v", sel, err)
|
|
}
|
|
// Keep track of the PCRs we were actually given.
|
|
for pcr, digest := range ret {
|
|
out[uint32(pcr)] = digest
|
|
}
|
|
if len(out) == numPCRs {
|
|
break
|
|
}
|
|
}
|
|
|
|
if len(out) != numPCRs {
|
|
return nil, fmt.Errorf("failed to read all PCRs, only read %d", len(out))
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
func TestVerifyQuoteTPM20(t *testing.T) {
|
|
tpm := setupSimulatedTPM(t)
|
|
defer tpm.Close()
|
|
if err := tpm.ManufactureReset(); err != nil {
|
|
t.Fatalf("Failed to reset TPM: %v", err)
|
|
}
|
|
|
|
// Create the attestation key.
|
|
keyHandle, pub, _, _, _, _, err := tpm2.CreatePrimaryEx(tpm, tpm2.HandleEndorsement, tpm2.PCRSelection{}, "", "", tpm2.Public{
|
|
Type: tpm2.AlgRSA,
|
|
NameAlg: tpm2.AlgSHA256,
|
|
Attributes: tpm2.FlagSignerDefault | tpm2.FlagNoDA,
|
|
RSAParameters: &tpm2.RSAParams{
|
|
Sign: &tpm2.SigScheme{
|
|
Alg: tpm2.AlgRSASSA,
|
|
Hash: tpm2.AlgSHA256,
|
|
},
|
|
KeyBits: 2048,
|
|
Modulus: big.NewInt(0),
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("CreatePrimaryEx() failed: %v", err)
|
|
}
|
|
defer tpm2.FlushContext(tpm, keyHandle)
|
|
|
|
for _, alg := range []tpm2.Algorithm{tpm2.AlgSHA1, tpm2.AlgSHA256} {
|
|
t.Run(fmt.Sprintf("Alg %x", alg), func(t *testing.T) {
|
|
// Generate the quote.
|
|
sel := tpm2.PCRSelection{Hash: alg}
|
|
numPCRs := 24
|
|
for pcr := 0; pcr < numPCRs; pcr++ {
|
|
sel.PCRs = append(sel.PCRs, pcr)
|
|
}
|
|
nonce := []byte{1, 2, 3, 4}
|
|
quote, qSig, err := tpm2.Quote(tpm, keyHandle, "", "", nonce, sel, tpm2.AlgNull)
|
|
if err != nil {
|
|
t.Fatalf("tpm2.Quote() failed: %v", err)
|
|
}
|
|
sig, err := tpmutil.Pack(qSig.Alg, qSig.RSA.HashAlg, qSig.RSA.Signature)
|
|
if err != nil {
|
|
t.Fatalf("tpmutil.Pack() failed: %v", err)
|
|
}
|
|
|
|
PCRs, err := allPCRs(tpm, alg)
|
|
if err != nil {
|
|
t.Fatalf("allPCRs() failed: %v", err)
|
|
}
|
|
|
|
verificationResults, err := VerifyQuote(2, pub, quote, sig, PCRs, nonce)
|
|
if err != nil {
|
|
t.Errorf("VerifyQuote failed: %v", err)
|
|
}
|
|
if !verificationResults.Succeeded {
|
|
t.Logf("Verification results: %+v", verificationResults)
|
|
t.Errorf("verificationResults.succeeded = %v, expected true", verificationResults.Succeeded)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRoca(t *testing.T) {
|
|
key := &rsa.PublicKey{N: &big.Int{}}
|
|
key.N.SetString("944e13208a280c37efc31c3114485e590192adbb8e11c87cad60cdef0037ce99278330d3f471a2538fa667802ed2a3c44a8b7dea826e888d0aa341fd664f7fa7", 16)
|
|
|
|
if !ROCAVulnerableKey(key) {
|
|
t.Errorf("ROCAVulnerableKey() = %v, wanted true", ROCAVulnerableKey(key))
|
|
}
|
|
}
|
|
|
|
func TestVerifyQuoteTPM12(t *testing.T) {
|
|
tcs := []struct {
|
|
PublicHex string
|
|
QuoteHex string
|
|
SignatureHex string
|
|
Nonce []byte
|
|
PCRs map[int]string
|
|
}{
|
|
{
|
|
PublicHex: "00000001000100020000000c00000800000000020000000000000100be855eadb504443ec1a85f5894cf9ae6b97fe75c39debe2376d13e49632ea34dc917c99f0ea29c52349eba9b1abfd2a92e814057568338ea68a32a45f92ae23944d0765805489414f9c588778220a3f384b7b2c4be8132515e276eefde7cb807303f7a7d57900f94dda27e6abe5e411026b8be7637483747073fa731643807e4c3d7e6fdad0ea297beaaeb208465aa4906447fcddf1955f5ac0a439295f7b43fbe38d018009456c17426e4ebf1581c99e3a97ff151a0c649335a46ec8189849b4efe932cb3a7d57e2ee45e67a7fcb64da5041604f24fd6153898fbe5d8432d95b2ad5d4b89088f6306f6b1a7d8c55c748838a96d106efc39ce119b11ac51211b",
|
|
QuoteHex: "0101000051554f54d45a9b15807eac8d85cd467ec0060815cc687c18a8b93257fea90bba13ede78f0db2d81ae4173c19",
|
|
SignatureHex: "33a4260f049c64f7539ab5a5f5adf1c87fc31d9ae5165340636ba96c88b82eb402b46902315c65d0d8b7a861ec8cd3f0d1bb7d264420cb7dca8e3d5862c5ecf5114f3bde50890cbf8c05b95fb0f2c70c816f9e86f247c4377aa58e84f24e6a910ea414664b9cdd1ff4a3522994ec1e1f419a2e1e3f503689e4eb0606c0b3e9a42f4f7b74c937d4d061161390e1790b6561b0d288e3534fd1b62af6a8c232174e1cb1586b863bd20e95e73e52e27a0781c7160672257831eb9b1d5192098495ad2170490c5e52693385e43aeab95069eecdd3f80529fdcc7ff2ef6086c24c06576e53b77e2f88eafb3e9b9fd40d954a7e0ab4c01e5b9a73d5d1841c49924beb64",
|
|
Nonce: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
|
PCRs: map[int]string{
|
|
0: "b777654263752ed0bfe13b369cf512b4661eee04",
|
|
1: "c2b93db7e6f705f98419a35cc6e46deb639c6a28",
|
|
2: "b2a83b0ebf2f8374299a5b2bdfc31ea955ad7236",
|
|
3: "b2a83b0ebf2f8374299a5b2bdfc31ea955ad7236",
|
|
4: "8f3fe5e128e3f2186bfca0b804900ec9ded96e39",
|
|
5: "45a323382bd933f08e7f0e256bc8249e4095b1ec",
|
|
6: "ee1b0f997d7517b286bc9d73a4cf742c65a769be",
|
|
7: "9d42f8fe8bfd73fba0274c7db891d91fb7861562",
|
|
8: "0000000000000000000000000000000000000000",
|
|
9: "0000000000000000000000000000000000000000",
|
|
10: "0000000000000000000000000000000000000000",
|
|
11: "ebb98df76613280f20dc38221143a9e727399486",
|
|
12: "575e12d9ec16d6512648f25b65d38b4eb27a2d6d",
|
|
13: "a9f1781a95d2e37970d1d73c463157fa016d9858",
|
|
14: "fc76feaf714c844cc888ea454ddf97c0ed220b61",
|
|
15: "0000000000000000000000000000000000000000",
|
|
16: "0000000000000000000000000000000000000000",
|
|
17: "ffffffffffffffffffffffffffffffffffffffff",
|
|
18: "ffffffffffffffffffffffffffffffffffffffff",
|
|
19: "ffffffffffffffffffffffffffffffffffffffff",
|
|
20: "ffffffffffffffffffffffffffffffffffffffff",
|
|
21: "ffffffffffffffffffffffffffffffffffffffff",
|
|
22: "ffffffffffffffffffffffffffffffffffffffff",
|
|
23: "0000000000000000000000000000000000000000",
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, testcase := range tcs {
|
|
tc := testcase
|
|
t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
|
|
pub, err := hex.DecodeString(tc.PublicHex)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
quote, err := hex.DecodeString(tc.QuoteHex)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sig, err := hex.DecodeString(tc.SignatureHex)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
pcrs := make(map[uint32][]byte)
|
|
for idx, h := range tc.PCRs {
|
|
pcrs[uint32(idx)], err = hex.DecodeString(h)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
verificationResults, err := VerifyQuote(1, pub, quote, sig, pcrs, tc.Nonce)
|
|
if err != nil {
|
|
t.Errorf("VerifyQuote failed: %v", err)
|
|
}
|
|
if !verificationResults.Succeeded {
|
|
t.Errorf("verificationResults.Succeeded = %v, want %v", verificationResults.Succeeded, true)
|
|
}
|
|
})
|
|
}
|
|
}
|