Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return Owner Cert as a CMS structure instead of PEM encoding #90

Merged
merged 6 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 2 additions & 16 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"time"

log "github.com/golang/glog"
ownercertificate "github.com/openconfig/bootz/common/owner_certificate"
ownershipvoucher "github.com/openconfig/bootz/common/ownership_voucher"
"github.com/openconfig/bootz/common/signature"

Expand Down Expand Up @@ -72,11 +73,6 @@ func validateArtifacts(serialNumber string, resp *bpb.GetBootstrapDataResponse)
log.Infof("Validated ownership voucher signed by vendor")
log.Infof("=============================================================================")

oc := resp.GetOwnershipCertificate()
if len(oc) == 0 {
return fmt.Errorf("received empty ownership certificate from server")
}

// Verify the serial number for this OV
log.Infof("Verifying the serial number for this OV")
if parsedOV.OV.SerialNumber != serialNumber {
Expand All @@ -95,18 +91,8 @@ func validateArtifacts(serialNumber string, resp *bpb.GetBootstrapDataResponse)

// Parse the Ownership Certificate.
log.Infof("Parsing the OC")
ocCert, err := x509.ParseCertificate(oc)
ocCert, err := ownercertificate.Verify(resp.GetOwnershipCertificate(), pdcPool)
if err != nil {
return fmt.Errorf("failed to parse certificate: %v", err)
}

// Verify that the OC is signed by the PDC.
log.Infof("Verifying that the OC is signed by the PDC")
opts := x509.VerifyOptions{
Roots: pdcPool,
Intermediates: x509.NewCertPool(),
}
if _, err := ocCert.Verify(opts); err != nil {
return err
}
log.Infof("Validated ownership certificate with OV PDC")
Expand Down
62 changes: 62 additions & 0 deletions common/owner_certificate/owner_certificate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package ownercertificate provides helper functions for generating, parsing and verifying owner certs.
package ownercertificate

import (
"crypto"
"crypto/x509"
"fmt"

"go.mozilla.org/pkcs7"
)

// Verify checks that the provided CMS value is signed by a signer in the provided
// certPool and returns the Ownership Certificate.
func Verify(in []byte, certPool *x509.CertPool) (*x509.Certificate, error) {
if len(in) == 0 {
return nil, fmt.Errorf("owner certificate is empty")
}
p7, err := pkcs7.Parse(in)
if err != nil {
return nil, fmt.Errorf("unable to parse into pkcs7 format: %v", err)
}
if err = p7.VerifyWithChain(certPool); err != nil {
return nil, fmt.Errorf("failed to verify OC: %v", err)
}
if len(p7.Certificates) == 0 {
return nil, fmt.Errorf("no certificates found in pkcs7 message")
}
return p7.Certificates[0], nil
}

// GenerateCMS takes an Ownership Certificate keypair and converts it to a CMS structure.
// The CMS structure contains the Ownership Certificate in its list of certificates.
func GenerateCMS(cert *x509.Certificate, priv crypto.PrivateKey) ([]byte, error) {
signedMessage, err := pkcs7.NewSignedData(nil)
if err != nil {
return nil, err
}
signedMessage.SetDigestAlgorithm(pkcs7.OIDDigestAlgorithmSHA256)
signedMessage.SetEncryptionAlgorithm(pkcs7.OIDEncryptionAlgorithmRSA)
signedMessage.AddCertificate(cert)

err = signedMessage.AddSigner(cert, priv, pkcs7.SignerInfoConfig{})
if err != nil {
return nil, err
}

return signedMessage.Finish()
}
46 changes: 46 additions & 0 deletions common/owner_certificate/owner_certificate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ownercertificate

import (
"crypto/x509"
"testing"

_ "embed"

artifacts "github.com/openconfig/bootz/testdata"
)

// Tests that the CMS structure can be created and that it can be verified with a PDC.
func TestGenerateAndVerify(t *testing.T) {
pdc, pdcPrivateKey, err := artifacts.NewCertificateAuthority("Pinned Domain Cert", "Google", "localhost")
if err != nil {
t.Fatalf("NewCertificateAuthority(): %v", err)
}
oc, ocPrivateKey, err := artifacts.NewSignedCertificate("Owner Certificate", "Google", "localhost", pdc, pdcPrivateKey)
if err != nil {
t.Fatalf("NewSignedCertificate(): %v", err)
}
cms, err := GenerateCMS(oc, ocPrivateKey)
if err != nil {
t.Fatalf("GenerateCMS(): %v", err)
}
pdcPool := x509.NewCertPool()
pdcPool.AddCert(pdc)
_, err = Verify(cms, pdcPool)
if err != nil {
t.Fatalf("Verify(): %v", err)
}
}
7 changes: 6 additions & 1 deletion server/entitymanager/entitymanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"os"
"sync"

ownercertificate "github.com/openconfig/bootz/common/owner_certificate"
"github.com/openconfig/bootz/common/signature"
"github.com/openconfig/bootz/server/service"
"google.golang.org/grpc/codes"
Expand Down Expand Up @@ -272,7 +273,11 @@ func (m *InMemoryEntityManager) Sign(resp *bpb.GetBootstrapDataResponse, chassis
log.Infof("OV populated")

// Populate the OC
resp.OwnershipCertificate = m.secArtifacts.OwnerCert.Raw
ocCMS, err := ownercertificate.GenerateCMS(m.secArtifacts.OwnerCert, m.secArtifacts.OwnerCertPrivateKey)
if err != nil {
return err
}
resp.OwnershipCertificate = ocCMS
log.Infof("OC populated")
return nil
}
Expand Down
7 changes: 6 additions & 1 deletion server/entitymanager/entitymanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/h-fam/errdiff"
ownercertificate "github.com/openconfig/bootz/common/owner_certificate"
"github.com/openconfig/bootz/common/signature"
"github.com/openconfig/bootz/server/service"
artifacts "github.com/openconfig/bootz/testdata"
Expand Down Expand Up @@ -314,8 +315,12 @@ func TestSign(t *testing.T) {
if !bytes.Equal(test.resp.GetOwnershipVoucher(), a.OV[test.serial]) {
t.Errorf("Sign() ov = %v, want %v", test.resp.GetOwnershipVoucher(), a.OV[test.serial])
}
wantOC, err := ownercertificate.GenerateCMS(a.OwnerCert, a.OwnerCertPrivateKey)
if err != nil {
t.Fatalf("unable to generate OC CMS: %v", err)
}
if test.wantOC {
if !bytes.Equal(test.resp.GetOwnershipCertificate(), a.OwnerCert.Raw) {
if !bytes.Equal(test.resp.GetOwnershipCertificate(), wantOC) {
t.Errorf("Sign() oc = %v, want %v", test.resp.GetOwnershipCertificate(), a.OwnerCert.Raw)
}
}
Expand Down