Skip to content

Commit

Permalink
Default/reference impl of IAK and IDevID cert verifier and parser (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
jenia-grunin authored Feb 14, 2024
1 parent dba4318 commit a79c277
Show file tree
Hide file tree
Showing 5 changed files with 422 additions and 55 deletions.
22 changes: 22 additions & 0 deletions service/biz/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "enrollz_biz",
srcs = ["enrollz_biz.go"],
embed = [":tpm_cert_verifier"],
importpath = "github.com/openconfig/attestz/service/biz",
visibility = ["//visibility:public"],
deps = [
Expand All @@ -41,3 +42,24 @@ go_test(
"@org_golang_google_protobuf//testing/protocmp",
],
)

go_library(
name = "tpm_cert_verifier",
srcs = ["tpm_cert_verifier.go"],
importpath = "github.com/openconfig/attestz/service/biz",
visibility = ["//visibility:public"],
deps = [
"@com_github_golang_glog//:glog",
"@org_golang_google_grpc//:go_default_library",
"@com_github_google_go_cmp//cmp",
],
)

go_test(
name = "tpm_cert_verifier_test",
srcs = ["tpm_cert_verifier_test.go"],
embed = [":tpm_cert_verifier"],
deps = [
"@com_github_google_go_cmp//cmp",
],
)
46 changes: 23 additions & 23 deletions service/biz/enrollz_biz.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ type EnrollzInfraDeps interface {

// Client to communicate with the switch's enrollz endpoints.
EnrollzDeviceClient

// Parser and verifier of IAK and IDevID certs.
TpmCertVerifier
}

// This is a "client"/switch-owner side implementation of Enrollz service. This client-side logic
Expand All @@ -85,40 +88,37 @@ func EnrollControlCard(controlCardSelection *cpb.ControlCardSelection, deps Enro
log.Infof("Successfully received from device GetIakCert() resp=%s for req=%s",
prototext.Format(getIakCertResp), prototext.Format(getIakCertReq))

// 3. Validate IDevID TLS cert if control card is standby (for active card IDevID would
// be validated during the TLS handshake).

// 4. Validate IAK cert.

// 5. Make sure IAK and IDevID serials match.

// 6. Extract IAK pub from IAK cert and validate it (accepted crypto algo and key length).

// 7. Extract IDevID pub from IDevID cert and validate it (accepted crypto algo and key length).

// 8. Call Switch Owner CA to issue oIAK and oIDevID certs.
// 3. Validate and parse IDevID and IAK certs.
tpmCertVerifierReq := &TpmCertVerifierReq{
iakCertPem: getIakCertResp.IakCert,
iDevIdCertPem: getIakCertResp.IdevidCert,
}
tpmCertVerifierResp, err := deps.VerifyAndParseIakAndIDevIdCerts(tpmCertVerifierReq)
if err != nil {
return fmt.Errorf("failed to verify IAK_cert_pem=%s and IDevID_cert_pem=%s: %w",
tpmCertVerifierReq.iakCertPem, tpmCertVerifierReq.iDevIdCertPem, err)
}
log.Infof("Successfully verified IAK and IDevID certs and parsed IAK_pub_pem=%s and IDevID_pub_pem=%s",
tpmCertVerifierResp.iakPubPem, tpmCertVerifierResp.iDevIdPubPem)

// TODO(jenia-grunin): Pass IAK cert PEM as is for now, but use IAK pub key PEM instead once x509 cert parser
// logic is implemented.
oIakCertPem, err := deps.IssueOwnerIakCert(getIakCertResp.ControlCardId, getIakCertResp.IakCert)
// 4. Call Switch Owner CA to issue oIAK and oIDevID certs.
oIakCertPem, err := deps.IssueOwnerIakCert(getIakCertResp.ControlCardId, tpmCertVerifierResp.iakPubPem)
if err != nil {
return fmt.Errorf("failed to execute Switch Owner CA IssueOwnerIakCert() with control_card_id=%s IAK_pub_pem=%s: %w",
prototext.Format(getIakCertResp.ControlCardId), getIakCertResp.IakCert, err)
prototext.Format(getIakCertResp.ControlCardId), tpmCertVerifierResp.iakPubPem, err)
}
log.Infof("Successfully received Switch Owner CA IssueOwnerIakCert() resp=%s for control_card_id=%s IAK_pub_pem=%s",
oIakCertPem, prototext.Format(getIakCertResp.ControlCardId), getIakCertResp.IakCert)
oIakCertPem, prototext.Format(getIakCertResp.ControlCardId), tpmCertVerifierResp.iakPubPem)

// TODO(jenia-grunin): Pass IDevID cert PEM as is for now, but use IDevID pub key PEM instead once x509 cert parser
// logic is implemented.
oIDevIdCertPem, err := deps.IssueOwnerIDevIdCert(getIakCertResp.ControlCardId, getIakCertResp.IdevidCert)
oIDevIdCertPem, err := deps.IssueOwnerIDevIdCert(getIakCertResp.ControlCardId, tpmCertVerifierResp.iDevIdPubPem)
if err != nil {
return fmt.Errorf("failed to execute Switch Owner CA IssueOwnerIDevIdCert() with control_card_id=%s IDevID_pub_pem=%s: %w",
prototext.Format(getIakCertResp.ControlCardId), getIakCertResp.IdevidCert, err)
prototext.Format(getIakCertResp.ControlCardId), tpmCertVerifierResp.iDevIdPubPem, err)
}
log.Infof("Successfully received Switch Owner CA IssueOwnerIDevIdCert() resp=%s for control_card_id=%s IDevID_pub_pem=%s",
oIDevIdCertPem, prototext.Format(getIakCertResp.ControlCardId), getIakCertResp.IdevidCert)
oIDevIdCertPem, prototext.Format(getIakCertResp.ControlCardId), tpmCertVerifierResp.iDevIdPubPem)

// 9. Call device's RotateOIakCert for the specified card card to persist oIAK and oIDevID certs.
// 5. Call device's RotateOIakCert for the specified card card to persist oIAK and oIDevID certs.
rotateOIakCertReq := &epb.RotateOIakCertRequest{
ControlCardSelection: controlCardSelection,
OiakCert: oIakCertPem,
Expand Down
156 changes: 124 additions & 32 deletions service/biz/enrollz_biz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
type stubEnrollzInfraDeps struct {
SwitchOwnerCaClient
EnrollzDeviceClient
TpmCertVerifier

// Request params that would be captured in stubbed deps' function calls.
cardIdIssueIakReq *cpb.ControlCardVendorId
Expand All @@ -38,18 +39,34 @@ type stubEnrollzInfraDeps struct {
iDevIdPubPemReq string
getIakCertReq *epb.GetIakCertRequest
rotateOIakCertReq *epb.RotateOIakCertRequest
tpmCertVerifierReq *TpmCertVerifierReq

// Stubbed responses to simulate behavior of deps without implementing them.
oIakCertResp string
oIDevIdCertPemResp string
getIakCertResp *epb.GetIakCertResponse
rotateOIakCertResp *epb.RotateOIakCertResponse
oIakCertResp string
oIDevIdCertPemResp string
getIakCertResp *epb.GetIakCertResponse
rotateOIakCertResp *epb.RotateOIakCertResponse
tpmCertVerifierResp *TpmCertVerifierResp

// If we need to simulate an error response from any of the deps, then set
// the dep's response to nil and populate this error field.
errorResp error
}

func (s *stubEnrollzInfraDeps) VerifyAndParseIakAndIDevIdCerts(req *TpmCertVerifierReq) (*TpmCertVerifierResp, error) {
// Validate that no stub (captured) request params were set prior to execution.
if s.tpmCertVerifierReq != nil {
return nil, fmt.Errorf("VerifyAndParseIakAndIDevIdCerts unexpected req %+v", req)
}
s.tpmCertVerifierReq = req

// If a stubbed response is not set, then return error, otherwise return the response.
if s.tpmCertVerifierResp == nil {
return nil, s.errorResp
}
return s.tpmCertVerifierResp, nil
}

func (s *stubEnrollzInfraDeps) IssueOwnerIakCert(cardId *cpb.ControlCardVendorId, iakPubPem string) (string, error) {
// Validate that no stub (captured) request params were set prior to execution.
if s.cardIdIssueIakReq != nil {
Expand Down Expand Up @@ -132,7 +149,9 @@ func TestEnrollControlCard(t *testing.T) {
ChassisSerialNumber: "Some chassis serial",
}
iakCert := "Some IAK cert PEM"
iakPub := "Some IAK pub PEM"
iDevIdCert := "Some IDevID cert PEM"
iDevIdPub := "Some IDevID pub PEM"
oIakCert := "Some Owner IAK cert PEM"
oIdevIdCert := "Some Owner IDevID cert PEM"
errorResp := errors.New("Some error")
Expand All @@ -149,29 +168,44 @@ func TestEnrollControlCard(t *testing.T) {
wantCardIdIssueIDevIdReq *cpb.ControlCardVendorId
wantIDevIdPubPemReq string
wantRotateOIakCertReq *epb.RotateOIakCertRequest
wantTpmCertVerifierReq *TpmCertVerifierReq
// Stubbed responses to EnrollzInfraDeps deps.
oIakCertResp string
oIDevIdCertPemResp string
getIakCertResp *epb.GetIakCertResponse
rotateOIakCertResp *epb.RotateOIakCertResponse
oIakCertResp string
oIDevIdCertPemResp string
getIakCertResp *epb.GetIakCertResponse
rotateOIakCertResp *epb.RotateOIakCertResponse
tpmCertVerifierResp *TpmCertVerifierResp
}{
{
desc: "Successful control card enrollment",
// Stubbed deps called: GetIakCert (Success), IssueOwnerIakCert (Success), IssueOwnerIDevIdCert (Success), RotateOIakCert(Success)
// Stubbed deps called:
// * GetIakCert => Success
// * VerifyAndParseIakAndIDevIdCerts => Success
// * IssueOwnerIakCert => Success
// * IssueOwnerIDevIdCert => Success
// * RotateOIakCert => Success
getIakCertResp: &epb.GetIakCertResponse{
ControlCardId: vendorId,
IakCert: iakCert,
IdevidCert: iDevIdCert,
},
tpmCertVerifierResp: &TpmCertVerifierResp{
iakPubPem: iakPub,
iDevIdPubPem: iDevIdPub,
},
oIakCertResp: oIakCert,
oIDevIdCertPemResp: oIdevIdCert,
rotateOIakCertResp: &epb.RotateOIakCertResponse{},
// Expected params to all deps functions calls.
wantGetIakCertReq: &epb.GetIakCertRequest{ControlCardSelection: controlCardSelection},
wantGetIakCertReq: &epb.GetIakCertRequest{ControlCardSelection: controlCardSelection},
wantTpmCertVerifierReq: &TpmCertVerifierReq{
iakCertPem: iakCert,
iDevIdCertPem: iDevIdCert,
},
wantCardIdIssueIakReq: vendorId,
wantIakPubPemReq: iakCert,
wantIakPubPemReq: iakPub,
wantCardIdIssueIDevIdReq: vendorId,
wantIDevIdPubPemReq: iDevIdCert,
wantIDevIdPubPemReq: iDevIdPub,
wantRotateOIakCertReq: &epb.RotateOIakCertRequest{
ControlCardSelection: controlCardSelection,
OiakCert: oIakCert,
Expand All @@ -181,54 +215,108 @@ func TestEnrollControlCard(t *testing.T) {
{
desc: "GetIakCert failure causes EnrollControlCard failure",
wantErrResp: errorResp,
// Stubbed deps called: GetIakCert (Fail)
// Stubbed deps called:
// * GetIakCert => Fail
wantGetIakCertReq: &epb.GetIakCertRequest{ControlCardSelection: controlCardSelection},
},
{
desc: "VerifyAndParseIakAndIDevIdCerts failure causes EnrollControlCard failure",
wantErrResp: errorResp,
// Stubbed deps called:
// * GetIakCert => Success
// * VerifyAndParseIakAndIDevIdCerts => Fail
getIakCertResp: &epb.GetIakCertResponse{
ControlCardId: vendorId,
IakCert: iakCert,
IdevidCert: iDevIdCert,
},
wantGetIakCertReq: &epb.GetIakCertRequest{ControlCardSelection: controlCardSelection},
wantTpmCertVerifierReq: &TpmCertVerifierReq{
iakCertPem: iakCert,
iDevIdCertPem: iDevIdCert,
},
},
{
desc: "IssueOwnerIakCert failure causes EnrollControlCard failure",
wantErrResp: errorResp,
// Stubbed deps called: GetIakCert (Success), IssueOwnerIakCert (Fail)
// Stubbed deps called:
// * GetIakCert => Success
// * VerifyAndParseIakAndIDevIdCerts => Success
// * IssueOwnerIakCert => Fail
getIakCertResp: &epb.GetIakCertResponse{
ControlCardId: vendorId,
IakCert: iakCert,
IdevidCert: iDevIdCert,
},
wantGetIakCertReq: &epb.GetIakCertRequest{ControlCardSelection: controlCardSelection},
tpmCertVerifierResp: &TpmCertVerifierResp{
iakPubPem: iakPub,
iDevIdPubPem: iDevIdPub,
},
wantGetIakCertReq: &epb.GetIakCertRequest{ControlCardSelection: controlCardSelection},
wantTpmCertVerifierReq: &TpmCertVerifierReq{
iakCertPem: iakCert,
iDevIdCertPem: iDevIdCert,
},
wantCardIdIssueIakReq: vendorId,
wantIakPubPemReq: iakCert,
wantIakPubPemReq: iakPub,
},
{
desc: "IssueOwnerIDevIdCert failure causes EnrollControlCard failure",
wantErrResp: errorResp,
// Stubbed deps called: GetIakCert (Success), IssueOwnerIakCert (Success), IssueOwnerIDevIdCert (Fail)
// Stubbed deps called:
// * GetIakCert => Success
// * VerifyAndParseIakAndIDevIdCerts => Success
// * IssueOwnerIakCert => Success
// * IssueOwnerIDevIdCert => Fail
getIakCertResp: &epb.GetIakCertResponse{
ControlCardId: vendorId,
IakCert: iakCert,
IdevidCert: iDevIdCert,
},
oIakCertResp: oIakCert,
wantGetIakCertReq: &epb.GetIakCertRequest{ControlCardSelection: controlCardSelection},
tpmCertVerifierResp: &TpmCertVerifierResp{
iakPubPem: iakPub,
iDevIdPubPem: iDevIdPub,
},
oIakCertResp: oIakCert,
wantGetIakCertReq: &epb.GetIakCertRequest{ControlCardSelection: controlCardSelection},
wantTpmCertVerifierReq: &TpmCertVerifierReq{
iakCertPem: iakCert,
iDevIdCertPem: iDevIdCert,
},
wantCardIdIssueIakReq: vendorId,
wantIakPubPemReq: iakCert,
wantIakPubPemReq: iakPub,
wantCardIdIssueIDevIdReq: vendorId,
wantIDevIdPubPemReq: iDevIdCert,
wantIDevIdPubPemReq: iDevIdPub,
},
{
desc: "RotateOIakCert failure causes EnrollControlCard failure",
wantErrResp: errorResp,
// Stubbed deps called: GetIakCert (Success), IssueOwnerIakCert (Success), IssueOwnerIDevIdCert (Success), RotateOIakCert(Fail)
// Stubbed deps called:
// * GetIakCert => Success
// * VerifyAndParseIakAndIDevIdCerts => Success
// * IssueOwnerIakCert => Success
// * IssueOwnerIDevIdCert => Success
// * RotateOIakCert => Fail
getIakCertResp: &epb.GetIakCertResponse{
ControlCardId: vendorId,
IakCert: iakCert,
IdevidCert: iDevIdCert,
},
oIakCertResp: oIakCert,
oIDevIdCertPemResp: oIdevIdCert,
wantGetIakCertReq: &epb.GetIakCertRequest{ControlCardSelection: controlCardSelection},
tpmCertVerifierResp: &TpmCertVerifierResp{
iakPubPem: iakPub,
iDevIdPubPem: iDevIdPub,
},
oIakCertResp: oIakCert,
oIDevIdCertPemResp: oIdevIdCert,
wantGetIakCertReq: &epb.GetIakCertRequest{ControlCardSelection: controlCardSelection},
wantTpmCertVerifierReq: &TpmCertVerifierReq{
iakCertPem: iakCert,
iDevIdCertPem: iDevIdCert,
},
wantCardIdIssueIakReq: vendorId,
wantIakPubPemReq: iakCert,
wantIakPubPemReq: iakPub,
wantCardIdIssueIDevIdReq: vendorId,
wantIDevIdPubPemReq: iDevIdCert,
wantIDevIdPubPemReq: iDevIdPub,
wantRotateOIakCertReq: &epb.RotateOIakCertRequest{
ControlCardSelection: controlCardSelection,
OiakCert: oIakCert,
Expand All @@ -240,11 +328,12 @@ func TestEnrollControlCard(t *testing.T) {
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
stub := &stubEnrollzInfraDeps{
getIakCertResp: test.getIakCertResp,
oIakCertResp: test.oIakCertResp,
oIDevIdCertPemResp: test.oIDevIdCertPemResp,
rotateOIakCertResp: test.rotateOIakCertResp,
errorResp: test.wantErrResp,
getIakCertResp: test.getIakCertResp,
tpmCertVerifierResp: test.tpmCertVerifierResp,
oIakCertResp: test.oIakCertResp,
oIDevIdCertPemResp: test.oIDevIdCertPemResp,
rotateOIakCertResp: test.rotateOIakCertResp,
errorResp: test.wantErrResp,
}
got := EnrollControlCard(controlCardSelection, stub)

Expand All @@ -259,6 +348,9 @@ func TestEnrollControlCard(t *testing.T) {
if diff := cmp.Diff(stub.getIakCertReq, test.wantGetIakCertReq, protocmp.Transform()); diff != "" {
t.Errorf("GetIakCertRequest request param to stubbed GetIakCert dep does not match expectations: diff = %v", diff)
}
if diff := cmp.Diff(stub.tpmCertVerifierReq, test.wantTpmCertVerifierReq, cmp.AllowUnexported(TpmCertVerifierReq{})); diff != "" {
t.Errorf("TpmCertVerifierReq request param to stubbed VerifyAndParseIakAndIDevIdCerts dep does not match expectations: diff = %v", diff)
}
if diff := cmp.Diff(stub.cardIdIssueIakReq, test.wantCardIdIssueIakReq, protocmp.Transform()); diff != "" {
t.Errorf("ControlCardVendorId request param to stubbed IssueOwnerIakCert dep does not match expectations: diff = %v", diff)
}
Expand Down
Loading

0 comments on commit a79c277

Please sign in to comment.