Skip to content

Commit

Permalink
Extend signers to set CRL Distribution Points
Browse files Browse the repository at this point in the history
  • Loading branch information
Danielius1922 committed Oct 30, 2024
1 parent 488551e commit fcd987e
Show file tree
Hide file tree
Showing 24 changed files with 831 additions and 118 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ unit-test: certificates
go test -race -parallel 1 -v ./bridge/... -coverpkg=./... -covermode=atomic -coverprofile=$(TMP_PATH)/bridge.unit.coverage.txt
go test -race -v ./schema/... -covermode=atomic -coverprofile=$(TMP_PATH)/schema.unit.coverage.txt
ROOT_CA_CRT="$(ROOT_CA_CRT)" ROOT_CA_KEY="$(ROOT_CA_KEY)" \
INTERMEDIATE_CA_CRT="$(INTERMEDIATE_CA_CRT)" INTERMEDIATE_CA_KEY=$(INTERMEDIATE_CA_KEY) \
go test -race -v ./pkg/... -covermode=atomic -coverprofile=$(TMP_PATH)/pkg.unit.coverage.txt

test: env build-testcontainer
Expand Down
21 changes: 11 additions & 10 deletions client/deviceOwnershipSDK.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,14 @@ type Signer = interface {
}

type DeviceOwnershipSDKConfig struct {
ID string
Cert string
CertKey string
ValidFrom string // RFC3339, or now-1m, empty means now-1m
CertExpiry *string

CreateSignerFunc func(caCert []*x509.Certificate, caKey crypto.PrivateKey, validNotBefore time.Time, validNotAfter time.Time) core.CertificateSigner
ID string
Cert string
CertKey string
ValidFrom string // RFC3339, or now-1m, empty means now-1m
CertExpiry *string
CRLDistributionPoints []string

CreateSignerFunc func(caCert []*x509.Certificate, caKey crypto.PrivateKey, validNotBefore, validNotAfter time.Time, crlDistributionPoints []string) (core.CertificateSigner, error)
}

type deviceOwnershipSDK struct {
Expand Down Expand Up @@ -78,11 +79,11 @@ func newDeviceOwnershipSDKFromConfig(app ApplicationCallback, dialTLS core.DialT
return nil, fmt.Errorf("invalid ID for device ownership SDK: %w", err)
}

return newDeviceOwnershipSDK(app, uid.String(), dialTLS, dialDLTS, &signerCert, cfg.ValidFrom, certExpiry, cfg.CreateSignerFunc)
return newDeviceOwnershipSDK(app, uid.String(), dialTLS, dialDLTS, &signerCert, cfg.ValidFrom, certExpiry, cfg.CRLDistributionPoints, cfg.CreateSignerFunc)
}

func newDeviceOwnershipSDK(app ApplicationCallback, sdkDeviceID string, dialTLS core.DialTLS,
dialDTLS core.DialDTLS, signerCert *tls.Certificate, validFrom string, certExpiry time.Duration, createSigner func(caCert []*x509.Certificate, caKey crypto.PrivateKey, validNotBefore time.Time, validNotAfter time.Time) core.CertificateSigner,
dialDTLS core.DialDTLS, signerCert *tls.Certificate, validFrom string, certExpiry time.Duration, crlDistributionPoints []string, createSigner func(caCert []*x509.Certificate, caKey crypto.PrivateKey, validNotBefore, validNotAfter time.Time, crlDistributionPoints []string) (core.CertificateSigner, error),
) (*deviceOwnershipSDK, error) {
if validFrom == "" {
validFrom = "now-1m"
Expand All @@ -107,7 +108,7 @@ func newDeviceOwnershipSDK(app ApplicationCallback, sdkDeviceID string, dialTLS
return nil, fmt.Errorf("invalid validFrom(%v): %w", validFrom, err)
}
notAfter := notBefore.Add(certExpiry)
return createSigner(signerCAs, signerCert.PrivateKey, notBefore, notAfter), nil
return createSigner(signerCAs, signerCert.PrivateKey, notBefore, notAfter, crlDistributionPoints)
},
app: app,
dialTLS: dialTLS,
Expand Down
4 changes: 2 additions & 2 deletions cmd/bridge-device/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ WORKDIR $GOPATH/src/github.com/plgd-dev/device
RUN CGO_ENABLED=0 go build -o /go/bin/bridge-device ./cmd/bridge-device

FROM alpine:3.20 AS security-provider
RUN apk add -U --no-cache ca-certificates
RUN addgroup -S nonroot \
RUN apk add -U --no-cache ca-certificates \
&& addgroup -S nonroot \
&& adduser -S nonroot -G nonroot

FROM scratch AS service
Expand Down
4 changes: 2 additions & 2 deletions cmd/ocfclient/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -523,8 +523,8 @@ func NewSecureClient() (*local.Client, error) {
ID: CertIdentity,
Cert: string(IdentityIntermediateCA),
CertKey: string(IdentityIntermediateCAKey),
CreateSignerFunc: func(caCert []*x509.Certificate, caKey crypto.PrivateKey, validNotBefore time.Time, validNotAfter time.Time) core.CertificateSigner {
return signer.NewOCFIdentityCertificate(caCert, caKey, validNotBefore, validNotAfter)
CreateSignerFunc: func(caCert []*x509.Certificate, caKey crypto.PrivateKey, validNotBefore, validNotAfter time.Time, crlDistributionPoints []string) (core.CertificateSigner, error) {
return signer.NewOCFIdentityCertificate(caCert, caKey, validNotBefore, validNotAfter, crlDistributionPoints)
},
},
}
Expand Down
19 changes: 16 additions & 3 deletions pkg/security/generateCertificate/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ import (
"encoding/asn1"
"fmt"
"net"
"slices"
"strconv"
"strings"
"time"

pkgX509 "github.com/plgd-dev/device/v2/pkg/security/x509"
)

type (
Expand Down Expand Up @@ -56,9 +59,10 @@ type Configuration struct {
//nolint:staticcheck
KeyUsages []string `yaml:"keyUsages" long:"ku" default:"digitalSignature" default:"keyAgreement" description:"to set more values repeat option with parameter"`
//nolint:staticcheck
ExtensionKeyUsages []string `yaml:"extensionKeyUsages" long:"eku" default:"client" default:"server" description:"to set more values repeat option with parameter"`
EllipticCurve EllipticCurve `yaml:"ellipticCurve" long:"ellipticCurve" default:"P256" description:"supported values:P256, P384, P521"`
SignatureAlgorithm SignatureAlgorithm `yaml:"signatureAlgorithm" long:"signatureAlgorithm" default:"ECDSA-SHA256" description:"supported values:ECDSA-SHA256, ECDSA-SHA384, ECDSA-SHA512"`
ExtensionKeyUsages []string `yaml:"extensionKeyUsages" long:"eku" default:"client" default:"server" description:"to set more values repeat option with parameter"`
EllipticCurve EllipticCurve `yaml:"ellipticCurve" long:"ellipticCurve" default:"P256" description:"supported values:P256, P384, P521"`
SignatureAlgorithm SignatureAlgorithm `yaml:"signatureAlgorithm" long:"signatureAlgorithm" default:"ECDSA-SHA256" description:"supported values:ECDSA-SHA256, ECDSA-SHA384, ECDSA-SHA512"`
CRLDistributionPoints []string `yaml:"crlDistributionPoints" long:"crl" description:"to set more values repeat option with parameter"`
}

func (cfg Configuration) ToPkixName() pkix.Name {
Expand Down Expand Up @@ -303,3 +307,12 @@ func (cfg Configuration) ToIPAddresses() ([]net.IP, error) {
}
return ips, nil
}

func (cfg Configuration) ToCRLDistributionPoints() ([]string, error) {
if err := pkgX509.ValidateCRLDistributionPoints(cfg.CRLDistributionPoints); err != nil {
return nil, err
}
cdp := slices.Clone(cfg.CRLDistributionPoints)
slices.Sort(cdp)
return slices.Compact(cdp), nil
}
57 changes: 57 additions & 0 deletions pkg/security/generateCertificate/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,60 @@ func TestToIPAddresses(t *testing.T) {
expected := []net.IP{net.ParseIP("192.168.0.1"), net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334")}
require.Equal(t, expected, ips)
}

func TestToCRLDistributionPoints(t *testing.T) {
tests := []struct {
name string
cfg generateCertificate.Configuration
want []string
wantErr bool
}{
{
name: "Valid CRL URLs",
cfg: generateCertificate.Configuration{
CRLDistributionPoints: []string{
"http://example.com/crl1",
"http://example.com/crl2",
},
},
want: []string{"http://example.com/crl1", "http://example.com/crl2"},
},
{
name: "Duplicate CRL URLs",
cfg: generateCertificate.Configuration{
CRLDistributionPoints: []string{
"http://example.com/crl1",
"http://example.com/crl1", // duplicate
},
},
want: []string{"http://example.com/crl1"},
},
{
name: "Invalid CRL URL",
cfg: generateCertificate.Configuration{
CRLDistributionPoints: []string{
"invalid-url",
},
},
wantErr: true,
},
{
name: "Empty CRL list",
cfg: generateCertificate.Configuration{
CRLDistributionPoints: []string{},
},
want: []string{},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
crls, err := tt.cfg.ToCRLDistributionPoints()
if tt.wantErr {
require.Error(t, err)
return
}
require.ElementsMatch(t, tt.want, crls)
})
}
}
23 changes: 16 additions & 7 deletions pkg/security/generateCertificate/generateCertificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ import (
"encoding/asn1"
"encoding/pem"

ocfSigner "github.com/plgd-dev/kit/v2/security/signer"
ocfSigner "github.com/plgd-dev/device/v2/pkg/security/signer"
)

var (
ASN1KeyUsage = asn1.ObjectIdentifier{2, 5, 29, 15}
ASN1BasicConstraints = asn1.ObjectIdentifier{2, 5, 29, 19}
ASN1ExtKeyUsage = asn1.ObjectIdentifier{2, 5, 29, 37}
)

// GenerateCSR creates CSR according to configuration.
Expand All @@ -28,12 +34,12 @@ func GenerateCSR(cfg Configuration, privateKey *ecdsa.PrivateKey) ([]byte, error

extraExtensions := make([]pkix.Extension, 0, 3)
if !cfg.BasicConstraints.Ignore {
bcVal, errM := asn1.Marshal(basicConstraints{false})
bcVal, errM := asn1.Marshal(BasicConstraints{false})
if errM != nil {
return nil, errM
}
extraExtensions = append(extraExtensions, pkix.Extension{
Id: asn1.ObjectIdentifier{2, 5, 29, 19}, // basic constraints
Id: ASN1BasicConstraints,
Value: bcVal,
Critical: false,
})
Expand All @@ -49,7 +55,7 @@ func GenerateCSR(cfg Configuration, privateKey *ecdsa.PrivateKey) ([]byte, error
return nil, errM
}
extraExtensions = append(extraExtensions, pkix.Extension{
Id: asn1.ObjectIdentifier{2, 5, 29, 15}, // key usage
Id: ASN1KeyUsage,
Value: val,
Critical: false,
})
Expand All @@ -65,7 +71,7 @@ func GenerateCSR(cfg Configuration, privateKey *ecdsa.PrivateKey) ([]byte, error
return nil, errM
}
extraExtensions = append(extraExtensions, pkix.Extension{
Id: asn1.ObjectIdentifier{2, 5, 29, 37}, // EKU
Id: ASN1ExtKeyUsage,
Value: val,
Critical: false,
})
Expand Down Expand Up @@ -100,8 +106,11 @@ func GenerateCert(cfg Configuration, privateKey *ecdsa.PrivateKey, signerCA []*x
if err != nil {
return nil, err
}

notAfter := notBefore.Add(cfg.ValidFor)
s := ocfSigner.NewBasicCertificateSigner(signerCA, signerCAKey, notBefore, notAfter)
crlDistributionPoints, err := cfg.ToCRLDistributionPoints()
if err != nil {
return nil, err
}
s := ocfSigner.NewBasicCertificateSigner(signerCA, signerCAKey, notBefore, notAfter, crlDistributionPoints)
return s.Sign(context.Background(), csr)
}
Loading

0 comments on commit fcd987e

Please sign in to comment.