issuance: add new IncludeCRLDistributionPoints bool (#7985)
To achieve this without breaking hashes of deployed configs, create a ProfileConfigNew containing the new field (and removing some deprecated fields). Move the CA's profile-hashing logic into the `issuance` package, and gate it on the presence of IncludeCRLDistributionPoints. If that field is false (the default), create an instance of the old `ProfileConfig` with the appropriate values and encode/hash that instead. Note: the IncludeCRLDistributionPoints field does not yet control any behavior. That will be part of #7974. Part of #7094
This commit is contained in:
parent
c7da1201db
commit
d93f0c316a
21
ca/ca.go
21
ca/ca.go
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"encoding/gob"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -195,7 +194,7 @@ func makeIssuerMaps(issuers []*issuance.Issuer) (issuerMaps, error) {
|
||||||
// - CA1 returns the precertificate DER bytes and profile hash to the RA
|
// - CA1 returns the precertificate DER bytes and profile hash to the RA
|
||||||
// - RA instructs CA2 to issue a final certificate, but CA2 does not contain a
|
// - RA instructs CA2 to issue a final certificate, but CA2 does not contain a
|
||||||
// profile corresponding to that hash and an issuance is prevented.
|
// profile corresponding to that hash and an issuance is prevented.
|
||||||
func makeCertificateProfilesMap(defaultName string, profiles map[string]*issuance.ProfileConfig) (certProfilesMaps, error) {
|
func makeCertificateProfilesMap(defaultName string, profiles map[string]*issuance.ProfileConfigNew) (certProfilesMaps, error) {
|
||||||
if len(profiles) <= 0 {
|
if len(profiles) <= 0 {
|
||||||
return certProfilesMaps{}, fmt.Errorf("must pass at least one certificate profile")
|
return certProfilesMaps{}, fmt.Errorf("must pass at least one certificate profile")
|
||||||
}
|
}
|
||||||
|
|
@ -215,20 +214,7 @@ func makeCertificateProfilesMap(defaultName string, profiles map[string]*issuanc
|
||||||
return certProfilesMaps{}, err
|
return certProfilesMaps{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// gob can only encode exported fields, of which an issuance.Profile has
|
hash := profile.Hash()
|
||||||
// none. However, since we're already in a loop iteration having access
|
|
||||||
// to the issuance.ProfileConfig used to generate the issuance.Profile,
|
|
||||||
// we'll generate the hash from that.
|
|
||||||
var encodedProfile bytes.Buffer
|
|
||||||
enc := gob.NewEncoder(&encodedProfile)
|
|
||||||
err = enc.Encode(profileConfig)
|
|
||||||
if err != nil {
|
|
||||||
return certProfilesMaps{}, err
|
|
||||||
}
|
|
||||||
if len(encodedProfile.Bytes()) <= 0 {
|
|
||||||
return certProfilesMaps{}, fmt.Errorf("certificate profile encoding returned 0 bytes")
|
|
||||||
}
|
|
||||||
hash := sha256.Sum256(encodedProfile.Bytes())
|
|
||||||
|
|
||||||
withID := certProfileWithID{
|
withID := certProfileWithID{
|
||||||
name: name,
|
name: name,
|
||||||
|
|
@ -237,7 +223,6 @@ func makeCertificateProfilesMap(defaultName string, profiles map[string]*issuanc
|
||||||
}
|
}
|
||||||
|
|
||||||
profilesByName[name] = &withID
|
profilesByName[name] = &withID
|
||||||
|
|
||||||
_, found := profilesByHash[hash]
|
_, found := profilesByHash[hash]
|
||||||
if found {
|
if found {
|
||||||
return certProfilesMaps{}, fmt.Errorf("duplicate certificate profile hash %d", hash)
|
return certProfilesMaps{}, fmt.Errorf("duplicate certificate profile hash %d", hash)
|
||||||
|
|
@ -256,7 +241,7 @@ func NewCertificateAuthorityImpl(
|
||||||
pa core.PolicyAuthority,
|
pa core.PolicyAuthority,
|
||||||
boulderIssuers []*issuance.Issuer,
|
boulderIssuers []*issuance.Issuer,
|
||||||
defaultCertProfileName string,
|
defaultCertProfileName string,
|
||||||
certificateProfiles map[string]*issuance.ProfileConfig,
|
certificateProfiles map[string]*issuance.ProfileConfigNew,
|
||||||
serialPrefix byte,
|
serialPrefix byte,
|
||||||
maxNames int,
|
maxNames int,
|
||||||
keyPolicy goodkey.KeyPolicy,
|
keyPolicy goodkey.KeyPolicy,
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ type testCtx struct {
|
||||||
ocsp *ocspImpl
|
ocsp *ocspImpl
|
||||||
crl *crlImpl
|
crl *crlImpl
|
||||||
defaultCertProfileName string
|
defaultCertProfileName string
|
||||||
certProfiles map[string]*issuance.ProfileConfig
|
certProfiles map[string]*issuance.ProfileConfigNew
|
||||||
serialPrefix byte
|
serialPrefix byte
|
||||||
maxNames int
|
maxNames int
|
||||||
boulderIssuers []*issuance.Issuer
|
boulderIssuers []*issuance.Issuer
|
||||||
|
|
@ -153,14 +153,14 @@ func setup(t *testing.T) *testCtx {
|
||||||
err = pa.LoadHostnamePolicyFile("../test/hostname-policy.yaml")
|
err = pa.LoadHostnamePolicyFile("../test/hostname-policy.yaml")
|
||||||
test.AssertNotError(t, err, "Couldn't set hostname policy")
|
test.AssertNotError(t, err, "Couldn't set hostname policy")
|
||||||
|
|
||||||
certProfiles := make(map[string]*issuance.ProfileConfig, 0)
|
certProfiles := make(map[string]*issuance.ProfileConfigNew, 0)
|
||||||
certProfiles["legacy"] = &issuance.ProfileConfig{
|
certProfiles["legacy"] = &issuance.ProfileConfigNew{
|
||||||
AllowMustStaple: true,
|
AllowMustStaple: true,
|
||||||
MaxValidityPeriod: config.Duration{Duration: time.Hour * 24 * 90},
|
MaxValidityPeriod: config.Duration{Duration: time.Hour * 24 * 90},
|
||||||
MaxValidityBackdate: config.Duration{Duration: time.Hour},
|
MaxValidityBackdate: config.Duration{Duration: time.Hour},
|
||||||
IgnoredLints: []string{"w_subject_common_name_included"},
|
IgnoredLints: []string{"w_subject_common_name_included"},
|
||||||
}
|
}
|
||||||
certProfiles["modern"] = &issuance.ProfileConfig{
|
certProfiles["modern"] = &issuance.ProfileConfigNew{
|
||||||
AllowMustStaple: true,
|
AllowMustStaple: true,
|
||||||
OmitCommonName: true,
|
OmitCommonName: true,
|
||||||
OmitKeyEncipherment: true,
|
OmitKeyEncipherment: true,
|
||||||
|
|
@ -546,7 +546,7 @@ func TestMakeCertificateProfilesMap(t *testing.T) {
|
||||||
testCtx := setup(t)
|
testCtx := setup(t)
|
||||||
test.AssertEquals(t, len(testCtx.certProfiles), 2)
|
test.AssertEquals(t, len(testCtx.certProfiles), 2)
|
||||||
|
|
||||||
testProfile := issuance.ProfileConfig{
|
testProfile := issuance.ProfileConfigNew{
|
||||||
AllowMustStaple: false,
|
AllowMustStaple: false,
|
||||||
MaxValidityPeriod: config.Duration{Duration: time.Hour * 24 * 90},
|
MaxValidityPeriod: config.Duration{Duration: time.Hour * 24 * 90},
|
||||||
MaxValidityBackdate: config.Duration{Duration: time.Hour},
|
MaxValidityBackdate: config.Duration{Duration: time.Hour},
|
||||||
|
|
@ -560,7 +560,7 @@ func TestMakeCertificateProfilesMap(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
defaultName string
|
defaultName string
|
||||||
profileConfigs map[string]*issuance.ProfileConfig
|
profileConfigs map[string]*issuance.ProfileConfigNew
|
||||||
expectedErrSubstr string
|
expectedErrSubstr string
|
||||||
expectedProfiles []nameToHash
|
expectedProfiles []nameToHash
|
||||||
}{
|
}{
|
||||||
|
|
@ -571,13 +571,13 @@ func TestMakeCertificateProfilesMap(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no profiles",
|
name: "no profiles",
|
||||||
profileConfigs: map[string]*issuance.ProfileConfig{},
|
profileConfigs: map[string]*issuance.ProfileConfigNew{},
|
||||||
expectedErrSubstr: "at least one certificate profile",
|
expectedErrSubstr: "at least one certificate profile",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no profile matching default name",
|
name: "no profile matching default name",
|
||||||
defaultName: "default",
|
defaultName: "default",
|
||||||
profileConfigs: map[string]*issuance.ProfileConfig{
|
profileConfigs: map[string]*issuance.ProfileConfigNew{
|
||||||
"notDefault": &testProfile,
|
"notDefault": &testProfile,
|
||||||
},
|
},
|
||||||
expectedErrSubstr: "profile object was not found for that name",
|
expectedErrSubstr: "profile object was not found for that name",
|
||||||
|
|
@ -585,7 +585,7 @@ func TestMakeCertificateProfilesMap(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "duplicate hash",
|
name: "duplicate hash",
|
||||||
defaultName: "default",
|
defaultName: "default",
|
||||||
profileConfigs: map[string]*issuance.ProfileConfig{
|
profileConfigs: map[string]*issuance.ProfileConfigNew{
|
||||||
"default": &testProfile,
|
"default": &testProfile,
|
||||||
"default2": &testProfile,
|
"default2": &testProfile,
|
||||||
},
|
},
|
||||||
|
|
@ -594,7 +594,7 @@ func TestMakeCertificateProfilesMap(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "empty profile config",
|
name: "empty profile config",
|
||||||
defaultName: "empty",
|
defaultName: "empty",
|
||||||
profileConfigs: map[string]*issuance.ProfileConfig{
|
profileConfigs: map[string]*issuance.ProfileConfigNew{
|
||||||
"empty": {},
|
"empty": {},
|
||||||
},
|
},
|
||||||
expectedProfiles: []nameToHash{
|
expectedProfiles: []nameToHash{
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ type Config struct {
|
||||||
|
|
||||||
// One of the profile names must match the value of
|
// One of the profile names must match the value of
|
||||||
// DefaultCertificateProfileName or boulder-ca will fail to start.
|
// DefaultCertificateProfileName or boulder-ca will fail to start.
|
||||||
CertProfiles map[string]*issuance.ProfileConfig `validate:"dive,keys,alphanum,min=1,max=32,endkeys,required_without=Profile,structonly"`
|
CertProfiles map[string]*issuance.ProfileConfigNew `validate:"dive,keys,alphanum,min=1,max=32,endkeys,required_without=Profile,structonly"`
|
||||||
|
|
||||||
// TODO(#7159): Make this required once all live configs are using it.
|
// TODO(#7159): Make this required once all live configs are using it.
|
||||||
CRLProfile issuance.CRLProfileConfig `validate:"-"`
|
CRLProfile issuance.CRLProfileConfig `validate:"-"`
|
||||||
|
|
|
||||||
113
issuance/cert.go
113
issuance/cert.go
|
|
@ -6,9 +6,11 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
|
"encoding/gob"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -28,7 +30,26 @@ import (
|
||||||
"github.com/letsencrypt/boulder/precert"
|
"github.com/letsencrypt/boulder/precert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProfileConfig describes the certificate issuance constraints for all issuers.
|
// ProfileConfig is a subset of ProfileConfigNew used for hashing.
|
||||||
|
//
|
||||||
|
// Deprecated: Use ProfileConfigNew instead.
|
||||||
|
//
|
||||||
|
// This struct exists for backwards-compatibility purposes when generating hashes
|
||||||
|
// of profile configs.
|
||||||
|
//
|
||||||
|
// The CA uses a hash of the gob encoding of ProfileConfig to ensure precert
|
||||||
|
// and final cert issuance use the exact same profile settings. Gob encodes all
|
||||||
|
// fields, including zero values, which means adding fields immediately changes all
|
||||||
|
// hashes, causing a deployability problem. It also encodes the struct name.
|
||||||
|
//
|
||||||
|
// To solve the deployability problem, we're switching to ASN.1 encoding. However,
|
||||||
|
// while deploying that we still need the ability to hash old configs the same way
|
||||||
|
// they've always been hashed. So this struct (with the same name it always had)
|
||||||
|
// gets hashed, only when `ProfileConfigNew.IncludeCRLDistributionPoints` (the
|
||||||
|
// newly added field) is false.
|
||||||
|
//
|
||||||
|
// Note that gob encodes the names of structs, not just their fields, so we needed
|
||||||
|
// to retain the name as well.
|
||||||
type ProfileConfig struct {
|
type ProfileConfig struct {
|
||||||
// AllowMustStaple, when false, causes all IssuanceRequests which specify the
|
// AllowMustStaple, when false, causes all IssuanceRequests which specify the
|
||||||
// OCSP Must Staple extension to be rejected.
|
// OCSP Must Staple extension to be rejected.
|
||||||
|
|
@ -72,6 +93,82 @@ type ProfileConfig struct {
|
||||||
Policies []PolicyConfig `validate:"-"`
|
Policies []PolicyConfig `validate:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProfileConfigNew describes the certificate issuance constraints for all issuers.
|
||||||
|
//
|
||||||
|
// See ProfileConfig for why this is called "New".
|
||||||
|
//
|
||||||
|
// This struct gets hashed in the CA to allow matching up precert and final cert
|
||||||
|
// issuance by the exact profile config. We compute the hash over an ASN.1 encoding
|
||||||
|
// because ASN.1 encoding has a canonical form and can omit optional fields (which
|
||||||
|
// allows for gracefully adding new fields without changing the hash of existing
|
||||||
|
// profile configs). This struct does not get embedded into any certs, CRLs, or
|
||||||
|
// other objects, and does not get signed; it's only used internally.
|
||||||
|
//
|
||||||
|
// Note: even though these fields have encoding instructions (tag:N), they will
|
||||||
|
// be encoded in the order they appear in the struct, so do not reorder them.
|
||||||
|
type ProfileConfigNew struct {
|
||||||
|
// AllowMustStaple, when false, causes all IssuanceRequests which specify the
|
||||||
|
// OCSP Must Staple extension to be rejected.
|
||||||
|
AllowMustStaple bool `asn1:"tag:1,optional"`
|
||||||
|
|
||||||
|
// OmitCommonName causes the CN field to be excluded from the resulting
|
||||||
|
// certificate, regardless of its inclusion in the IssuanceRequest.
|
||||||
|
OmitCommonName bool `asn1:"tag:2,optional"`
|
||||||
|
// OmitKeyEncipherment causes the keyEncipherment bit to be omitted from the
|
||||||
|
// Key Usage field of all certificates (instead of only from ECDSA certs).
|
||||||
|
OmitKeyEncipherment bool `asn1:"tag:3,optional"`
|
||||||
|
// OmitClientAuth causes the id-kp-clientAuth OID (TLS Client Authentication)
|
||||||
|
// to be omitted from the EKU extension.
|
||||||
|
OmitClientAuth bool `asn1:"tag:4,optional"`
|
||||||
|
// OmitSKID causes the Subject Key Identifier extension to be omitted.
|
||||||
|
OmitSKID bool `asn1:"tag:5,optional"`
|
||||||
|
// IncludeCRLDistributionPoints causes the CRLDistributionPoints extension to
|
||||||
|
// be added to all certificates issued by this profile.
|
||||||
|
IncludeCRLDistributionPoints bool `asn1:"tag:6,optional"`
|
||||||
|
|
||||||
|
MaxValidityPeriod config.Duration `asn1:"tag:7,optional"`
|
||||||
|
MaxValidityBackdate config.Duration `asn1:"tag:8,optional"`
|
||||||
|
|
||||||
|
// LintConfig is a path to a zlint config file, which can be used to control
|
||||||
|
// the behavior of zlint's "customizable lints".
|
||||||
|
LintConfig string `asn1:"tag:9,optional"`
|
||||||
|
// IgnoredLints is a list of lint names that we know will fail for this
|
||||||
|
// profile, and which we know it is safe to ignore.
|
||||||
|
IgnoredLints []string `asn1:"tag:10,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pcn ProfileConfigNew) Hash() ([32]byte, error) {
|
||||||
|
var encodedBytes []byte
|
||||||
|
var err error
|
||||||
|
if !pcn.IncludeCRLDistributionPoints {
|
||||||
|
old := ProfileConfig{
|
||||||
|
AllowMustStaple: pcn.AllowMustStaple,
|
||||||
|
AllowCTPoison: false,
|
||||||
|
AllowSCTList: false,
|
||||||
|
AllowCommonName: false,
|
||||||
|
OmitCommonName: pcn.OmitCommonName,
|
||||||
|
OmitKeyEncipherment: pcn.OmitKeyEncipherment,
|
||||||
|
OmitClientAuth: pcn.OmitClientAuth,
|
||||||
|
OmitSKID: pcn.OmitSKID,
|
||||||
|
MaxValidityPeriod: pcn.MaxValidityPeriod,
|
||||||
|
MaxValidityBackdate: pcn.MaxValidityBackdate,
|
||||||
|
LintConfig: pcn.LintConfig,
|
||||||
|
IgnoredLints: pcn.IgnoredLints,
|
||||||
|
Policies: nil,
|
||||||
|
}
|
||||||
|
var encoded bytes.Buffer
|
||||||
|
enc := gob.NewEncoder(&encoded)
|
||||||
|
err = enc.Encode(old)
|
||||||
|
encodedBytes = encoded.Bytes()
|
||||||
|
} else {
|
||||||
|
encodedBytes, err = asn1.Marshal(pcn)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return [32]byte{}, err
|
||||||
|
}
|
||||||
|
return sha256.Sum256(encodedBytes), nil
|
||||||
|
}
|
||||||
|
|
||||||
// PolicyConfig describes a policy
|
// PolicyConfig describes a policy
|
||||||
type PolicyConfig struct {
|
type PolicyConfig struct {
|
||||||
OID string `validate:"required"`
|
OID string `validate:"required"`
|
||||||
|
|
@ -89,10 +186,12 @@ type Profile struct {
|
||||||
maxValidity time.Duration
|
maxValidity time.Duration
|
||||||
|
|
||||||
lints lint.Registry
|
lints lint.Registry
|
||||||
|
|
||||||
|
hash [32]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProfile converts the profile config into a usable profile.
|
// NewProfile converts the profile config into a usable profile.
|
||||||
func NewProfile(profileConfig *ProfileConfig) (*Profile, error) {
|
func NewProfile(profileConfig *ProfileConfigNew) (*Profile, error) {
|
||||||
// The Baseline Requirements, Section 7.1.2.7, says that the notBefore time
|
// The Baseline Requirements, Section 7.1.2.7, says that the notBefore time
|
||||||
// must be "within 48 hours of the time of signing". We can be even stricter.
|
// must be "within 48 hours of the time of signing". We can be even stricter.
|
||||||
if profileConfig.MaxValidityBackdate.Duration >= 24*time.Hour {
|
if profileConfig.MaxValidityBackdate.Duration >= 24*time.Hour {
|
||||||
|
|
@ -113,6 +212,11 @@ func NewProfile(profileConfig *ProfileConfig) (*Profile, error) {
|
||||||
lints.SetConfiguration(lintconfig)
|
lints.SetConfiguration(lintconfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hash, err := profileConfig.Hash()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
sp := &Profile{
|
sp := &Profile{
|
||||||
allowMustStaple: profileConfig.AllowMustStaple,
|
allowMustStaple: profileConfig.AllowMustStaple,
|
||||||
omitCommonName: profileConfig.OmitCommonName,
|
omitCommonName: profileConfig.OmitCommonName,
|
||||||
|
|
@ -122,11 +226,16 @@ func NewProfile(profileConfig *ProfileConfig) (*Profile, error) {
|
||||||
maxBackdate: profileConfig.MaxValidityBackdate.Duration,
|
maxBackdate: profileConfig.MaxValidityBackdate.Duration,
|
||||||
maxValidity: profileConfig.MaxValidityPeriod.Duration,
|
maxValidity: profileConfig.MaxValidityPeriod.Duration,
|
||||||
lints: lints,
|
lints: lints,
|
||||||
|
hash: hash,
|
||||||
}
|
}
|
||||||
|
|
||||||
return sp, nil
|
return sp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Profile) Hash() [32]byte {
|
||||||
|
return p.hash
|
||||||
|
}
|
||||||
|
|
||||||
// GenerateValidity returns a notBefore/notAfter pair bracketing the input time,
|
// GenerateValidity returns a notBefore/notAfter pair bracketing the input time,
|
||||||
// based on the profile's configured backdate and validity.
|
// based on the profile's configured backdate and validity.
|
||||||
func (p *Profile) GenerateValidity(now time.Time) (time.Time, time.Time) {
|
func (p *Profile) GenerateValidity(now time.Time) (time.Time, time.Time) {
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,14 @@ import (
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
ct "github.com/google/certificate-transparency-go"
|
ct "github.com/google/certificate-transparency-go"
|
||||||
"github.com/jmhodges/clock"
|
"github.com/jmhodges/clock"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/config"
|
||||||
"github.com/letsencrypt/boulder/ctpolicy/loglist"
|
"github.com/letsencrypt/boulder/ctpolicy/loglist"
|
||||||
"github.com/letsencrypt/boulder/linter"
|
"github.com/letsencrypt/boulder/linter"
|
||||||
"github.com/letsencrypt/boulder/test"
|
"github.com/letsencrypt/boulder/test"
|
||||||
|
|
@ -779,7 +781,7 @@ func TestMismatchedProfiles(t *testing.T) {
|
||||||
|
|
||||||
// Create a new profile that differs slightly (no common name)
|
// Create a new profile that differs slightly (no common name)
|
||||||
pc = defaultProfileConfig()
|
pc = defaultProfileConfig()
|
||||||
pc.AllowCommonName = false
|
pc.OmitCommonName = false
|
||||||
test.AssertNotError(t, err, "building test lint registry")
|
test.AssertNotError(t, err, "building test lint registry")
|
||||||
noCNProfile, err := NewProfile(pc)
|
noCNProfile, err := NewProfile(pc)
|
||||||
test.AssertNotError(t, err, "NewProfile failed")
|
test.AssertNotError(t, err, "NewProfile failed")
|
||||||
|
|
@ -809,3 +811,51 @@ func TestMismatchedProfiles(t *testing.T) {
|
||||||
test.AssertError(t, err, "preparing final cert issuance")
|
test.AssertError(t, err, "preparing final cert issuance")
|
||||||
test.AssertContains(t, err.Error(), "precert does not correspond to linted final cert")
|
test.AssertContains(t, err.Error(), "precert does not correspond to linted final cert")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProfileHash(t *testing.T) {
|
||||||
|
// A profile without IncludeCRLDistributionPoints.
|
||||||
|
// Hash calculated over the gob encoding of the old `ProfileConfig`.
|
||||||
|
profile := ProfileConfigNew{
|
||||||
|
IncludeCRLDistributionPoints: false,
|
||||||
|
AllowMustStaple: true,
|
||||||
|
OmitCommonName: true,
|
||||||
|
OmitKeyEncipherment: false,
|
||||||
|
OmitClientAuth: false,
|
||||||
|
OmitSKID: true,
|
||||||
|
MaxValidityPeriod: config.Duration{Duration: time.Hour},
|
||||||
|
MaxValidityBackdate: config.Duration{Duration: time.Second},
|
||||||
|
LintConfig: "example/config.toml",
|
||||||
|
IgnoredLints: []string{"one", "two"},
|
||||||
|
}
|
||||||
|
hash, err := profile.Hash()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("hashing %+v: %s", profile, err)
|
||||||
|
}
|
||||||
|
expectedHash := "f6b5766141fdc066824e781347095ffb3c86fa97a174e21123a323a93b078f46"
|
||||||
|
if expectedHash != fmt.Sprintf("%x", hash) {
|
||||||
|
t.Errorf("%+v.Hash()=%x, want %s", profile, hash, expectedHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A profile _with_ IncludeCRLDistributionPoints.
|
||||||
|
// Hash calculated over the ASN.1 encoding of the `ProfileConfigNew`.
|
||||||
|
profile = ProfileConfigNew{
|
||||||
|
IncludeCRLDistributionPoints: true,
|
||||||
|
AllowMustStaple: true,
|
||||||
|
OmitCommonName: true,
|
||||||
|
OmitKeyEncipherment: false,
|
||||||
|
OmitClientAuth: false,
|
||||||
|
OmitSKID: true,
|
||||||
|
MaxValidityPeriod: config.Duration{Duration: time.Hour},
|
||||||
|
MaxValidityBackdate: config.Duration{Duration: time.Second},
|
||||||
|
LintConfig: "example/config.toml",
|
||||||
|
IgnoredLints: []string{"one", "two"},
|
||||||
|
}
|
||||||
|
hash, err = profile.Hash()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("hashing %+v: %s", profile, err)
|
||||||
|
}
|
||||||
|
expectedHash = "d2a6c9f0aa37d2ac0b15476cb6e0ae9b98ba59b1321d8d6da26efc620581c53d"
|
||||||
|
if expectedHash != fmt.Sprintf("%x", hash) {
|
||||||
|
t.Errorf("%+v.Hash()=%x, want %s", profile, hash, expectedHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ import (
|
||||||
"github.com/letsencrypt/boulder/test"
|
"github.com/letsencrypt/boulder/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
func defaultProfileConfig() *ProfileConfig {
|
func defaultProfileConfig() *ProfileConfigNew {
|
||||||
return &ProfileConfig{
|
return &ProfileConfigNew{
|
||||||
AllowMustStaple: true,
|
AllowMustStaple: true,
|
||||||
MaxValidityPeriod: config.Duration{Duration: time.Hour},
|
MaxValidityPeriod: config.Duration{Duration: time.Hour},
|
||||||
MaxValidityBackdate: config.Duration{Duration: time.Hour},
|
MaxValidityBackdate: config.Duration{Duration: time.Hour},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue