Skip to content
Open
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
1 change: 0 additions & 1 deletion common/owner_certificate/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,4 @@ go_test(
name = "owner_certificate_test",
srcs = ["owner_certificate_test.go"],
embed = [":owner_certificate"],
deps = ["//testdata:testdata_lib"],
)
61 changes: 52 additions & 9 deletions common/owner_certificate/owner_certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,40 +17,43 @@ package ownercertificate

import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"time"

"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.
// Verify checks that the provided CMS message is signed by a signer in the provided certPool and returns the signer certificate.
func Verify(in []byte, certPool *x509.CertPool) (*x509.Certificate, error) {
if len(in) == 0 {
return nil, fmt.Errorf("owner certificate is empty")
return nil, fmt.Errorf("input CMS message 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")
}
if err = p7.VerifyWithChain(certPool); err != nil {
return nil, fmt.Errorf("failed to verify the chain of trust: %v", err)
}
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.
// GenerateCMS takes an owner certificate keypair and converts it to a CMS message.
// The CMS message contains the owner 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
}
// Override the default SHA1 digest with SHA256.
signedMessage.SetDigestAlgorithm(pkcs7.OIDDigestAlgorithmSHA256)
signedMessage.SetEncryptionAlgorithm(pkcs7.OIDEncryptionAlgorithmRSA)
signedMessage.AddCertificate(cert)

err = signedMessage.AddSigner(cert, priv, pkcs7.SignerInfoConfig{})
Expand All @@ -60,3 +63,43 @@ func GenerateCMS(cert *x509.Certificate, priv crypto.PrivateKey) ([]byte, error)

return signedMessage.Finish()
}

// NewRSACertificate creates a new RSA certificate and its private key, signed by the given certificate authority.
// If certificate authority is not provided, this new certificate will be created as a certificate authority instead.
func NewRSACertificate(commonName, deviceSerial string, caCert *x509.Certificate, caKey crypto.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) {
isCA := false
if caCert == nil || caKey == nil {
isCA = true
}
template := &x509.Certificate{
Subject: pkix.Name{
CommonName: commonName,
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(999, 0, 0),
IsCA: isCA,
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
if deviceSerial != "" {
template.Subject.SerialNumber = deviceSerial
}
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, err
}
if isCA {
caCert = template
caKey = key
}
der, err := x509.CreateCertificate(rand.Reader, template, caCert, &key.PublicKey, caKey)
if err != nil {
return nil, nil, err
}
cert, err := x509.ParseCertificate(der)
if err != nil {
return nil, nil, err
}
return cert, key, nil
}
16 changes: 6 additions & 10 deletions common/owner_certificate/owner_certificate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,26 @@ 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")
pdc, pdcPrivateKey, err := NewRSACertificate("Pinned Domain Cert", "", nil, nil)
if err != nil {
t.Fatalf("NewCertificateAuthority(): %v", err)
t.Fatalf("failed to create PDC: %v", err)
}
oc, ocPrivateKey, err := artifacts.NewSignedCertificate("Owner Certificate", "Google", "localhost", pdc, pdcPrivateKey)
oc, ocPrivateKey, err := NewRSACertificate("Owner Certificate", "", pdc, pdcPrivateKey)
if err != nil {
t.Fatalf("NewSignedCertificate(): %v", err)
t.Fatalf("failed to create owner certificate: %v", err)
}
cms, err := GenerateCMS(oc, ocPrivateKey)
if err != nil {
t.Fatalf("GenerateCMS(): %v", err)
t.Fatalf("failed to create CMS: %v", err)
}
pdcPool := x509.NewCertPool()
pdcPool.AddCert(pdc)
_, err = Verify(cms, pdcPool)
if err != nil {
t.Fatalf("Verify(): %v", err)
t.Fatalf("failed to verify owner certificate: %v", err)
}
}
7 changes: 2 additions & 5 deletions common/ownership_voucher/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@ go_library(
srcs = ["ownership_voucher.go"],
importpath = "github.com/openconfig/bootz/common/ownership_voucher",
visibility = ["//visibility:public"],
deps = [
"//testdata:testdata_lib",
"@org_mozilla_go_pkcs7//:pkcs7",
],
deps = ["@org_mozilla_go_pkcs7//:pkcs7"],
)

go_test(
name = "ownership_voucher_test",
srcs = ["ownership_voucher_test.go"],
embed = [":ownership_voucher"],
deps = ["//testdata:testdata_lib"],
deps = ["//common/owner_certificate"],
)
69 changes: 60 additions & 9 deletions common/ownership_voucher/ownership_voucher.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,43 @@
package ownershipvoucher

import (
"crypto"
"crypto/x509"
"encoding/json"
"encoding/xml"
"fmt"
"time"

"go.mozilla.org/pkcs7"

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

// Unmarshal unmarshals the contents of an Ownership Voucher. If a certPool is provided,
// it is used to verify the contents.
func Unmarshal(in []byte, certPool *x509.CertPool) (*artifacts.OwnershipVoucher, error) {
// OwnershipVoucher defines the ownership voucher. See https://www.rfc-editor.org/rfc/rfc8366.html.
type OwnershipVoucher struct {
OV OVInner `json:"ietf-voucher:voucher"`
}

// OVInner defines the ownership voucher inner structure.
type OVInner struct {
XMLName xml.Name `xml:"voucher"`
CreatedOn string `json:"created-on" xml:"created-on"`
ExpiresOn string `json:"expires-on" xml:"expires-on"`
SerialNumber string `json:"serial-number" xml:"serial-number"`
Assertion string `json:"assertion" xml:"assertion"`
PinnedDomainCert []byte `json:"pinned-domain-cert" xml:"pinned-domain-cert"`
DomainCertRevocationChecks bool `json:"domain-cert-revocation-checks" xml:"domain-cert-revocation-checks"`
}

// Unmarshal unmarshals the contents of an ownership voucher. If a certPool is provided, it is used to verify the contents.
func Unmarshal(in []byte, certPool *x509.CertPool) (*OwnershipVoucher, error) {
if len(in) == 0 {
return nil, fmt.Errorf("ownership voucher is empty")
}
p7, err := pkcs7.Parse(in)
if err != nil {
return nil, fmt.Errorf("unable to parse into pkcs7 format: %v", err)
}
ov := artifacts.OwnershipVoucher{}
jsonErr := json.Unmarshal(p7.Content, &ov)
ov := &OwnershipVoucher{}
jsonErr := json.Unmarshal(p7.Content, ov)
if jsonErr != nil {
xmlErr := xml.Unmarshal(p7.Content, &ov.OV)
if xmlErr != nil {
Expand All @@ -46,8 +61,44 @@ func Unmarshal(in []byte, certPool *x509.CertPool) (*artifacts.OwnershipVoucher,
}
if certPool != nil {
if err = p7.VerifyWithChain(certPool); err != nil {
return nil, fmt.Errorf("failed to verify OV: %v", err)
return nil, fmt.Errorf("failed to verify ownership voucher: %v", err)
}
}
return ov, nil
}

// NewOwnershipVoucher generates an ownership voucher signed by the vendor certificate.
func NewOwnershipVoucher(encoding string, deviceSerial string, pdc, vendorCert *x509.Certificate, vendorKey crypto.PrivateKey) ([]byte, error) {
ov := &OwnershipVoucher{
OV: OVInner{
CreatedOn: time.Now().Format(time.RFC3339),
ExpiresOn: time.Now().AddDate(999, 0, 0).Format(time.RFC3339),
SerialNumber: deviceSerial,
PinnedDomainCert: pdc.Raw,
},
}
var ovBytes []byte
var err error
switch encoding {
case "json":
if ovBytes, err = json.Marshal(ov); err != nil {
return nil, err
}
case "xml":
if ovBytes, err = xml.Marshal(ov.OV); err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unsupported encoding: %v", encoding)
}
signedMessage, err := pkcs7.NewSignedData(ovBytes)
if err != nil {
return nil, err
}
// Override the default SHA1 digest with SHA256.
signedMessage.SetDigestAlgorithm(pkcs7.OIDDigestAlgorithmSHA256)
if err = signedMessage.AddSigner(vendorCert, vendorKey, pkcs7.SignerInfoConfig{}); err != nil {
return nil, err
}
return &ov, nil
return signedMessage.Finish()
}
18 changes: 9 additions & 9 deletions common/ownership_voucher/ownership_voucher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
"crypto/x509"
"testing"

artifacts "github.com/openconfig/bootz/testdata"
ownercertificate "github.com/openconfig/bootz/common/owner_certificate"
)

var (
Expand All @@ -28,18 +28,18 @@ var (

// Tests that a new OV can be created and it can be unpacked and verified.
func TestEndToEndJSON(t *testing.T) {
pdc, _, err := artifacts.NewCertificateAuthority("Pinned Domain Cert", "Google", "localhost")
pdc, _, err := ownercertificate.NewRSACertificate("Pinned Domain Cert", "", nil, nil)
if err != nil {
t.Fatalf("unable to generate PDC: %v", err)
}
vendorca, vendorcaPrivateKey, err := artifacts.NewCertificateAuthority("Cisco Certificate Authority", "Cisco", "localhost")
vendorca, vendorcaPrivateKey, err := ownercertificate.NewRSACertificate("Vendor Certificate Authority", "", nil, nil)
if err != nil {
t.Fatalf("unable to generate Vendor CA: %v", err)
}

ov, err := artifacts.NewOwnershipVoucher("json", wantSerial, pdc, vendorca, vendorcaPrivateKey)
ov, err := NewOwnershipVoucher("json", wantSerial, pdc, vendorca, vendorcaPrivateKey)
if err != nil {
t.Errorf("New err = %v, want nil", err)
t.Errorf("unable to generate ownership voucher: err = %v, want nil", err)
}

vendorCAPool := x509.NewCertPool()
Expand All @@ -59,18 +59,18 @@ func TestEndToEndJSON(t *testing.T) {
}

func TestEndToEndXML(t *testing.T) {
pdc, _, err := artifacts.NewCertificateAuthority("Pinned Domain Cert", "Google", "localhost")
pdc, _, err := ownercertificate.NewRSACertificate("Pinned Domain Cert", "", nil, nil)
if err != nil {
t.Fatalf("unable to generate PDC: %v", err)
}
vendorca, vendorcaPrivateKey, err := artifacts.NewCertificateAuthority("Cisco Certificate Authority", "Cisco", "localhost")
vendorca, vendorcaPrivateKey, err := ownercertificate.NewRSACertificate("Vendor Certificate Authority", "", nil, nil)
if err != nil {
t.Fatalf("unable to generate Vendor CA: %v", err)
}

ov, err := artifacts.NewOwnershipVoucher("xml", wantSerial, pdc, vendorca, vendorcaPrivateKey)
ov, err := NewOwnershipVoucher("xml", wantSerial, pdc, vendorca, vendorcaPrivateKey)
if err != nil {
t.Errorf("New err = %v, want nil", err)
t.Errorf("unable to generate ownership voucher: err = %v, want nil", err)
}

vendorCAPool := x509.NewCertPool()
Expand Down
1 change: 0 additions & 1 deletion common/signature/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ go_test(
name = "signature_test",
srcs = ["signature_test.go"],
embed = [":signature"],
deps = ["//testdata:testdata_lib"],
)
2 changes: 1 addition & 1 deletion common/tls/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"@com_github_golang_glog//:glog",
]
],
)

go_test(
Expand Down
29 changes: 10 additions & 19 deletions common/tls/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"math/big"
"net"
"time"

Expand Down Expand Up @@ -69,26 +67,19 @@ func TLSConfiguration(opts *Opts) (*tls.Config, error) {
if opts.ServerCertSubject == nil {
return nil, fmt.Errorf("ServerCertSubject is nil")
}
// 1. Generate a private key for the server.
// Generate a private key for the server.
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return nil, fmt.Errorf("unable to generate private key: %v", err)
}
// 2. Calculate SubjectKeyId
pubKeyBytes := x509.MarshalPKCS1PublicKey(&privateKey.PublicKey)
keyHash := sha256.Sum256(pubKeyBytes)

// 3. Create the template and cert.
// Create the template and cert.
template := x509.Certificate{
SerialNumber: big.NewInt(int64(time.Now().Year())),
Subject: *opts.ServerCertSubject,
IPAddresses: []net.IP{opts.IPAddress},
NotBefore: time.Now().AddDate(0, 0, -1), // One day before server start-up.
NotAfter: time.Now().AddDate(11, 0, 0), // 11 years after server start-up.
SubjectKeyId: keyHash[:],
AuthorityKeyId: opts.CACert.SubjectKeyId,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
Subject: *opts.ServerCertSubject,
IPAddresses: []net.IP{opts.IPAddress},
NotBefore: time.Now().AddDate(0, 0, -1), // One day before server start-up.
NotAfter: time.Now().AddDate(11, 0, 0), // 11 years after server start-up.
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
}

cert, err := x509.CreateCertificate(
Expand All @@ -107,11 +98,11 @@ func TLSConfiguration(opts *Opts) (*tls.Config, error) {
Certificate: [][]byte{cert},
}

// 4. Create the Root CAs trust bundle.
// Create the Root CAs trust bundle.
rootCAs := x509.NewCertPool()
rootCAs.AddCert(opts.CACert)

// 5. Create the final TLS server config.
// Create the final TLS server config.
return &tls.Config{
Certificates: []tls.Certificate{*tlsCert},
RootCAs: rootCAs,
Expand Down
Loading
Loading