Skip to content

Commit

Permalink
Sign support (#251)
Browse files Browse the repository at this point in the history
Add verification tests for the Sign command
  • Loading branch information
hpya93 authored Nov 21, 2023
1 parent 1b68c8a commit d0c06e8
Show file tree
Hide file tree
Showing 4 changed files with 289 additions and 27 deletions.
130 changes: 105 additions & 25 deletions verification/certifyKey.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ package verification

import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/x509"
"encoding/asn1"
"encoding/binary"
"encoding/pem"
"fmt"
"math/big"
"reflect"
"testing"
"time"
Expand Down Expand Up @@ -117,18 +120,23 @@ const (

type TcgMultiTcbInfo = []DiceTcbInfo

type CertifyKeyParams struct {
Label []byte
Flags CertifyKeyFlags
}

func TestCertifyKey(d TestDPEInstance, c DPEClient, t *testing.T) {
testCertifyKey(d, c, t, false)
}

func TestCertifyKey_SimulationMode(d TestDPEInstance, c DPEClient, t *testing.T) {
func TestCertifyKeySimulation(d TestDPEInstance, c DPEClient, t *testing.T) {
testCertifyKey(d, c, t, true)
}

// Ignores critical extensions that are unknown to x509 package
// but atleast defined in DPE certificate profile specification.
// UnhandledCriticalExtensions may have only custom extensions mentioned in spec
// unknownExtnMap collects extensions unknown to both x59 and the DICE certificate profiles spec.
// unknownExtnMap collects extensions unknown to both x509 and the DICE certificate profiles spec.
// positive case expects the unknownExtnMap to be empty.
func removeTcgDiceCriticalExtensions(t *testing.T, certs []*x509.Certificate) {
t.Helper()
Expand Down Expand Up @@ -158,6 +166,11 @@ func removeTcgDiceCriticalExtensions(t *testing.T, certs []*x509.Certificate) {
}
}

// Ignores extended key usages that are unknown to x509 package
// but atleast defined in DPE certificate profile specification.
// UnhandledExtendedKeyUsages may have only custom key usages mentioned in spec
// unknownKeyUsagesMap collects keyusages unknown to both x509 and the DICE certificate profiles spec.
// positive case expects the unknownKeyUsagesMap to be empty.
func removeTcgDiceExtendedKeyUsages(t *testing.T, certs []*x509.Certificate) {
t.Helper()
unknownKeyUsagesMap := map[string][]string{}
Expand Down Expand Up @@ -218,7 +231,7 @@ func checkCertifyKeyTcgUeidExtension(t *testing.T, c *x509.Certificate, label []
}
}

// Check whether certificate extended key usage is as per spec
// Checks whether certificate extended key usage is as per spec
// OID for ExtendedKeyUsage Extension: 2.5.29.37
// The ExtendedKeyUsage extension SHOULD be marked as critical
// If IsCA = true, the extension SHOULD contain tcg-dice-kp-eca
Expand Down Expand Up @@ -274,7 +287,7 @@ func checkCertifyKeyExtendedKeyUsages(t *testing.T, c *x509.Certificate) (*TcgMu
return multiTcbInfo, err
}

// Check for KeyUsage Extension as per spec
// Checks for KeyUsage Extension as per spec
// If IsCA = true, KeyUsage extension MUST contain DigitalSignature and KeyCertSign
// If IsCA = false, KeyUsage extension MUST contain only DigitalSignature
func checkCertifyKeyExtensions(t *testing.T, c *x509.Certificate) {
Expand All @@ -296,25 +309,25 @@ func checkCertifyKeyExtensions(t *testing.T, c *x509.Certificate) {

}

// Validate basic constraints in certificate returned by CertifyKey command
// Validates basic constraints in certificate returned by CertifyKey command
// against the flag set for input parameter.
// The BasicConstraints extension MUST be included
// If CertifyKey AddIsCA is set, IsCA MUST be set to true.
// If CertifyKey AddIsCA is NOT set, IsCA MUST be set to false
func checkCertifyKeyBasicConstraints(t *testing.T, c *x509.Certificate, flags uint32) {
func checkCertifyKeyBasicConstraints(t *testing.T, c *x509.Certificate, flags CertifyKeyFlags) {
t.Helper()

flagsBuf := &bytes.Buffer{}
binary.Write(flagsBuf, binary.LittleEndian, flags)

flagIsCA := uint32(CertifyAddIsCA)&flags != 0
flagIsCA := CertifyAddIsCA&flags != 0
if flagIsCA != c.IsCA {
t.Errorf("[ERROR]: ADD_IS_CA is set to %v but the basic constraint IsCA is set to %v", flagIsCA, c.IsCA)
}
}

// Validate X509 fields in certificate returned by CertifyKey command.
func validateCertifyKeyCert(t *testing.T, c *x509.Certificate, flags uint32, label []byte) {
// Validates X509 fields in certificate returned by CertifyKey command.
func validateCertifyKeyCert(t *testing.T, c *x509.Certificate, flags CertifyKeyFlags, label []byte) {
t.Helper()

// Check for basic constraints extension
Expand All @@ -337,6 +350,7 @@ func validateCertifyKeyCert(t *testing.T, c *x509.Certificate, flags uint32, lab
}
}

// Parses X509 certificate
func checkCertificateStructure(t *testing.T, certBytes []byte) *x509.Certificate {
t.Helper()
failed := false
Expand Down Expand Up @@ -411,44 +425,48 @@ func checkCertificateStructure(t *testing.T, certBytes []byte) *x509.Certificate
return x509Cert
}

func testCertifyKey(d TestDPEInstance, client DPEClient, t *testing.T, simulation bool) {
ctx := getInitialContextHandle(d, client, t, simulation)
func testCertifyKey(d TestDPEInstance, c DPEClient, t *testing.T, simulation bool) {
handle := getInitialContextHandle(d, c, t, simulation)
defer func() {
if simulation {
client.DestroyContext(ctx, DestroyDescendants)
c.DestroyContext(handle, DestroyDescendants)
}
}()

type Params struct {
Label []byte
Flags CertifyKeyFlags
}

profile, err := GetTransportProfile(d)
if err != nil {
t.Fatalf("Could not get profile: %v", err)
}
digestLen := profile.GetDigestSize()

var hashAlg asn1.ObjectIdentifier
if digestLen == 32 {
hashAlg = OidSHA256
} else if digestLen == 48 {
hashAlg = OidSHA384
} else {
t.Fatal("Unknown Hash Algorithm")
}

seqLabel := make([]byte, digestLen)
for i := range seqLabel {
seqLabel[i] = byte(i)
}

certifyKeyParams := []Params{
certifyKeyParams := []CertifyKeyParams{
{Label: make([]byte, digestLen), Flags: CertifyKeyFlags(0)},
{Label: seqLabel, Flags: CertifyKeyFlags(0)},
}

for _, params := range certifyKeyParams {
// Get DPE leaf certificate from CertifyKey
certifyKeyResp, err := client.CertifyKey(ctx, params.Label, CertifyKeyX509, params.Flags)
certifyKeyResp, err := c.CertifyKey(handle, params.Label, CertifyKeyX509, params.Flags)
if err != nil {
t.Fatalf("[FATAL]: Could not certify key: %v", err)
}

// Get root and intermediate certificates to validate certificate chain of leaf cert
certChainBytes, err := client.GetCertificateChain()
certChainBytes, err := c.GetCertificateChain()
if err != nil {
t.Fatalf("[FATAL]: Could not get Certificate Chain: %v", err)
}
Expand All @@ -459,8 +477,14 @@ func testCertifyKey(d TestDPEInstance, client DPEClient, t *testing.T, simulatio
leafCert := checkCertificateStructure(t, leafCertBytes)
certChain := checkCertificateChain(t, certChainBytes)

// Check default context handle is unchanged
checkCertifyKeyRespHandle(*certifyKeyResp, t, handle)

// Check key returned in command response against certificate
checkCertifyKeyResponse(t, leafCert, *certifyKeyResp, hashAlg)

// Validate that all X.509 fields conform with the format defined in the DPE iRoT profile
validateCertifyKeyCert(t, leafCert, uint32(params.Flags), params.Label)
validateCertifyKeyCert(t, leafCert, params.Flags, params.Label)

// Ensure full certificate chain has valid signatures
// This also checks certificate lifetime, signatures as part of cert chain validation
Expand All @@ -469,13 +493,12 @@ func testCertifyKey(d TestDPEInstance, client DPEClient, t *testing.T, simulatio
// Reassign handle for simulation mode.
// However, this does not impact in default mode because
// same default context handle is returned in default mode.
ctx = &certifyKeyResp.Handle

// TODO: When DeriveChild is implemented, call it here to add more TCIs and call CertifyKey again.
handle = &certifyKeyResp.Handle
}
// TODO: When DeriveChild is implemented, call it here to add more TCIs and call CertifyKey again.
}

// Build certificate chain and calls to validateSignature on each chain.
// Builds and verifies certificate chain.
func validateLeafCertChain(t *testing.T, certChain []*x509.Certificate, leafCert *x509.Certificate) {
t.Helper()
certsToProcess := []*x509.Certificate{leafCert}
Expand All @@ -502,6 +525,7 @@ func validateLeafCertChain(t *testing.T, certChain []*x509.Certificate, leafCert
}
}

// Builds Certificate chain verifier parameters.
func buildVerifyOptions(t *testing.T, certChain []*x509.Certificate) x509.VerifyOptions {
roots := x509.NewCertPool()
intermediates := x509.NewCertPool()
Expand All @@ -526,6 +550,7 @@ func buildVerifyOptions(t *testing.T, certChain []*x509.Certificate) x509.Verify
return opts
}

// Gets KeyUsage bitmap and returns as list of KeyUsage name strings.
func getKeyUsageNames(keyUsage x509.KeyUsage) []string {
keyUsageNames := []string{}

Expand Down Expand Up @@ -567,3 +592,58 @@ func getKeyUsageNames(keyUsage x509.KeyUsage) []string {

return keyUsageNames
}

// Checks CertifyKey command response against public key extracted from certificate returned in response
func checkCertifyKeyResponse(t *testing.T, x509Cert *x509.Certificate, response CertifiedKey, hashAlg asn1.ObjectIdentifier) {
var err error

publicKeyDer, err := x509.MarshalPKIXPublicKey(x509Cert.PublicKey)
if err != nil {
t.Fatalf("[FATAL]: Could not marshal pub key: %v", err)
}

// Parse the DER-encoded public key
pubKeyInCert, err := x509.ParsePKIXPublicKey(publicKeyDer)
if err != nil {
t.Fatalf("[FATAL]: Failed to parse DER-encoded public key: %v", err)
}

if _, ok := pubKeyInCert.(*ecdsa.PublicKey); !ok {
t.Fatal("[FATAL]: Public key is not a ecdsa key")
}

var pubKeyInResponse ecdsa.PublicKey

if hashAlg.Equal(OidSHA384) {
pubKeyInResponse = ecdsa.PublicKey{
Curve: elliptic.P384(),
X: new(big.Int).SetBytes(response.Pub.X),
Y: new(big.Int).SetBytes(response.Pub.Y),
}
} else if hashAlg.Equal(OidSHA256) {
pubKeyInResponse = ecdsa.PublicKey{
Curve: elliptic.P256(),
X: new(big.Int).SetBytes(response.Pub.X),
Y: new(big.Int).SetBytes(response.Pub.Y),
}
} else {
t.Errorf("[ERROR]: Unsupported hash algorithm.")
return
}

if !(pubKeyInResponse.Equal(pubKeyInCert)) {
t.Errorf("[ERROR]: Public key returned in response must match the Public Key Info in the certificate.")
}
}

// Checks whether the context handle is unchanged after certifyKey command when default context handle is used.
func checkCertifyKeyRespHandle(res CertifiedKey, t *testing.T, handle *ContextHandle) {
if *handle != DefaultContextHandle {
t.Logf("[LOG]: Handle is not default context, skipping check...")
return
}

if res.Handle != *handle {
t.Errorf("[ERROR]: Handle must be unchanged by CertifyKey, want original handle %v but got %v", handle, res.Handle)
}
}
Loading

0 comments on commit d0c06e8

Please sign in to comment.