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

Support for DelegationUsage extension #1205

Merged
merged 1 commit into from
Sep 15, 2021
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
5 changes: 3 additions & 2 deletions cli/testdata/csr.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
"1.2.3.4.5": "abc"
}
}
]
}
],
"delegation_enabled": true
}
29 changes: 17 additions & 12 deletions csr/csr.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type Name struct {
L string `json:"L,omitempty" yaml:"L,omitempty"` // Locality
O string `json:"O,omitempty" yaml:"O,omitempty"` // OrganisationName
OU string `json:"OU,omitempty" yaml:"OU,omitempty"` // OrganisationalUnitName
E string `json:"E,omitempty" yaml:"E,omitempty"`
E string `json:"E,omitempty" yaml:"E,omitempty"`
SerialNumber string `json:"SerialNumber,omitempty" yaml:"SerialNumber,omitempty"`
OID map[string]string `json:"OID,omitempty", yaml:"OID,omitempty"`
}
Expand Down Expand Up @@ -136,14 +136,15 @@ type CAConfig struct {
// A CertificateRequest encapsulates the API interface to the
// certificate request functionality.
type CertificateRequest struct {
CN string `json:"CN" yaml:"CN"`
Names []Name `json:"names" yaml:"names"`
Hosts []string `json:"hosts" yaml:"hosts"`
KeyRequest *KeyRequest `json:"key,omitempty" yaml:"key,omitempty"`
CA *CAConfig `json:"ca,omitempty" yaml:"ca,omitempty"`
SerialNumber string `json:"serialnumber,omitempty" yaml:"serialnumber,omitempty"`
Extensions []pkix.Extension `json:"extensions,omitempty" yaml:"extensions,omitempty"`
CRL string `json:"crl_url,omitempty" yaml:"crl_url,omitempty"`
CN string `json:"CN" yaml:"CN"`
Names []Name `json:"names" yaml:"names"`
Hosts []string `json:"hosts" yaml:"hosts"`
KeyRequest *KeyRequest `json:"key,omitempty" yaml:"key,omitempty"`
CA *CAConfig `json:"ca,omitempty" yaml:"ca,omitempty"`
SerialNumber string `json:"serialnumber,omitempty" yaml:"serialnumber,omitempty"`
DelegationEnabled bool `json:"delegation_enabled,omitempty" yaml:"delegation_enabled,omitempty"`
Extensions []pkix.Extension `json:"extensions,omitempty" yaml:"extensions,omitempty"`
CRL string `json:"crl_url,omitempty" yaml:"crl_url,omitempty"`
}

// New returns a new, empty CertificateRequest with a
Expand Down Expand Up @@ -196,9 +197,9 @@ func (cr *CertificateRequest) Name() (pkix.Name, error) {
}
name.ExtraNames = append(name.ExtraNames, pkix.AttributeTypeAndValue{Type: oid, Value: v})
}
if n.E != "" {
name.ExtraNames = append(name.ExtraNames, pkix.AttributeTypeAndValue{Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: n.E})
}
if n.E != "" {
name.ExtraNames = append(name.ExtraNames, pkix.AttributeTypeAndValue{Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: n.E})
}
}
name.SerialNumber = cr.SerialNumber
return name, nil
Expand Down Expand Up @@ -430,6 +431,10 @@ func Generate(priv crypto.Signer, req *CertificateRequest) (csr []byte, err erro
}
}

if req.DelegationEnabled {
tpl.ExtraExtensions = append(tpl.Extensions, helpers.DelegationExtension)
}

if req.Extensions != nil {
err = appendExtensionsToCSR(req.Extensions, &tpl)
if err != nil {
Expand Down
47 changes: 47 additions & 0 deletions csr/csr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -786,3 +786,50 @@ func TestExtractCertificateRequest(t *testing.T) {
t.Fatal("Bad Certificate Request!")
}
}

// TestDelegationCSR tests that we create requests with the DC extension
func TestDelegationCSR(t *testing.T) {
var cr = &CertificateRequest{
CN: "Test Common Name",
Names: []Name{
{
C: "US",
ST: "California",
L: "San Francisco",
O: "CloudFlare, Inc.",
OU: "Systems Engineering",
},
{
C: "GB",
ST: "London",
L: "London",
O: "CloudFlare, Inc",
OU: "Systems Engineering",
},
},
DelegationEnabled: true,
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
KeyRequest: NewKeyRequest(),
}
csr, _, err := ParseRequest(cr)
if err != nil {
t.Fatal("could not generate csr")
}
unPem, _ := pem.Decode(csr)
if unPem == nil {
t.Fatal("Failed to decode pem")
}
res, err := x509.ParseCertificateRequest(unPem.Bytes)
if err != nil {
t.Fatalf("spat out nonsense as a csr: %v", err)
}
found := false
for _, ext := range res.Extensions {
if ext.Id.Equal(helpers.DelegationUsage) {
found = true
}
}
if !found {
t.Fatal("generated csr has no extension")
}
}
10 changes: 10 additions & 0 deletions helpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ const OneYear = 8760 * time.Hour
// OneDay is a time.Duration representing a day's worth of seconds.
const OneDay = 24 * time.Hour

// DelegationUsage is the OID for the DelegationUseage extensions
var DelegationUsage = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 44}

// DelegationExtension
var DelegationExtension = pkix.Extension{
Id: DelegationUsage,
Critical: false,
Value: []byte{0x05, 0x00}, // ASN.1 NULL
}

// InclusiveDate returns the time.Time representation of a date - 1
// nanosecond. This allows time.After to be used inclusively.
func InclusiveDate(year int, month time.Month, day int) time.Time {
Expand Down
5 changes: 4 additions & 1 deletion signer/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/cloudflare/cfssl/config"
"github.com/cloudflare/cfssl/csr"
cferr "github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/info"
)

Expand All @@ -45,7 +46,7 @@ type Extension struct {
// Extensions provided in the signRequest are copied into the certificate, as
// long as they are in the ExtensionWhitelist for the signer's policy.
// Extensions requested in the CSR are ignored, except for those processed by
// ParseCertificateRequest (mainly subjectAltName).
// ParseCertificateRequest (mainly subjectAltName) and DelegationUsage.
type SignRequest struct {
Hosts []string `json:"hosts"`
Request string `json:"certificate_request"`
Expand Down Expand Up @@ -240,6 +241,8 @@ func ParseCertificateRequest(s Signer, p *config.SigningProfile, csrBytes []byte
template.IsCA = constraints.IsCA
template.MaxPathLen = constraints.MaxPathLen
template.MaxPathLenZero = template.MaxPathLen == 0
} else if val.Id.Equal(helpers.DelegationUsage) {
template.ExtraExtensions = append(template.ExtraExtensions, val)
} else {
// If the profile has 'copy_extensions' to true then lets add it
if p.CopyExtensions {
Expand Down