Skip to content
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
1 change: 1 addition & 0 deletions wolfx509/certgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func CreateCertificate(template, parent *Certificate, pubKey, signer KeyHandle)
DNSNames: template.DNSNames,
IPAddresses: template.IPAddresses,
AcmeKeyAuth: template.AcmeKeyAuth,
BasicConstraintsValid: template.BasicConstraintsValid,
}

if template == parent && pubKey == signer {
Expand Down
73 changes: 73 additions & 0 deletions wolfx509/certgen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ package wolfx509

import (
"bytes"
"encoding/asn1"
"encoding/hex"
"math/big"
"net"
"testing"
Expand Down Expand Up @@ -448,3 +450,74 @@ func TestCreateCertificateValidDays(t *testing.T) {
t.Errorf("NotAfter = %s, want within [%s, %s]", parsed.NotAfter, naLo, naHi)
}
}

// oidBasicConstraints is id-ce-basicConstraints (RFC 5280 4.2.1.9).
var oidBasicConstraints = asn1.ObjectIdentifier{2, 5, 29, 19}

// TestBasicConstraintsMatrix pins the BasicConstraints extension that is
// emitted for a given template:
// - emitted only when BasicConstraintsValid is set,
// - always critical,
// - present but with the cA boolean omitted (an empty SEQUENCE) when IsCA
// is false.
func TestBasicConstraintsMatrix(t *testing.T) {
for _, tc := range []struct {
name string
bcValid bool
isCA bool
wantPresent bool
wantValue string // hex of the extension value
}{
{"valid+CA", true, true, true, "30030101ff"},
{"valid+notCA", true, false, true, "3000"},
{"notValid+CA", false, true, false, ""},
{"notValid+notCA", false, false, false, ""},
} {
t.Run(tc.name, func(t *testing.T) {
k, err := GenerateP256Key()
if err != nil {
t.Fatalf("GenerateP256Key: %v", err)
}
defer k.Free()

tmpl := &Certificate{
SerialNumber: big.NewInt(1),
Subject: Name{CommonName: "bc test"},
NotBefore: time.Now().Add(-time.Hour),
NotAfter: time.Now().Add(24 * time.Hour),
BasicConstraintsValid: tc.bcValid,
IsCA: tc.isCA,
}
der, err := CreateCertificate(tmpl, tmpl, k, k)
if err != nil {
t.Fatalf("CreateCertificate: %v", err)
}
parsed, err := stdlibParseCert(der)
if err != nil {
t.Fatalf("stdlibParseCert: %v", err)
}

for _, ext := range parsed.Extensions {
if !ext.Id.Equal(oidBasicConstraints) {
continue
}
if !tc.wantPresent {
t.Fatalf("BasicConstraints emitted for BasicConstraintsValid=false")
}
if !ext.Critical {
t.Error("BasicConstraints is not critical; RFC 5280 4.2.1.9 requires it of CA certs and crypto/x509 always marks it")
}
if got := hex.EncodeToString(ext.Value); got != tc.wantValue {
t.Errorf("BasicConstraints value = %s, want %s", got, tc.wantValue)
}
if parsed.IsCA != tc.isCA {
t.Errorf("parsed IsCA = %v, want %v", parsed.IsCA, tc.isCA)
}
return
}
if tc.wantPresent {
t.Fatal("BasicConstraints extension missing")
}
})
}
}
21 changes: 19 additions & 2 deletions wolfx509/certgen_wolfcrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ package wolfx509
// return -174;
// }
// #endif
//
// /* Cert.basicConstSet is a C bitfield, which cgo cannot address
// * without a setter. */
// static void wolfx509_set_basic_const(Cert* cert, int toggle) {
// cert->basicConstSet = (toggle != 0);
// }
import "C"
import (
"encoding/asn1"
Expand Down Expand Up @@ -120,6 +126,9 @@ type certBuildOpts struct {
DNSNames []string // SubjectAltName dNSName entries
IPAddresses []net.IP // SubjectAltName iPAddress entries

// BasicConstraintsValid requests the BasicConstraints extension.
BasicConstraintsValid bool

// AcmeKeyAuth, if non-empty, sets the RFC 8737 id-pe-acmeIdentifier
// (1.3.6.1.5.5.7.1.31) extension on the cert. Pass the raw keyAuth
// bytes (token "." JWK_thumbprint per RFC 8555 §8.1); wolfCrypt
Expand Down Expand Up @@ -182,8 +191,16 @@ func buildAndSignCert(opts certBuildOpts, parentDER []byte, pubKey, signerKey Ke
}
cert.version = 2
cert.sigType = C.int(sigType)
if opts.IsCA {
cert.isCA = 1
// BasicConstraints: emitted only when BasicConstraintsValid is set, has to
// be marked CRITICAL if set.
// When IsCA is TRUE, wolfSSL will automatically encode the extension.
if opts.BasicConstraintsValid {
cert.basicConstCrit = 1
if opts.IsCA {
cert.isCA = 1
} else {
C.wolfx509_set_basic_const(&cert, 1)
}
}
cert.keyUsage = C.word16(opts.KeyUsage)
cert.extKeyUsage = C.byte(opts.ExtKeyUsage)
Expand Down
Loading