Skip to content

Commit

Permalink
Merge pull request #90 from gmacf/cms
Browse files Browse the repository at this point in the history
Return Owner Cert as a CMS structure instead of PEM encoding
  • Loading branch information
gmacf authored Nov 1, 2023
2 parents 1981887 + df0f211 commit cec1e15
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 18 deletions.
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

0 comments on commit cec1e15

Please sign in to comment.