103 lines
3.1 KiB
Go
103 lines
3.1 KiB
Go
package idp
|
|
|
|
import (
|
|
"crypto/x509/pkix"
|
|
"encoding/asn1"
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
var idpOID = asn1.ObjectIdentifier{2, 5, 29, 28} // id-ce-issuingDistributionPoint
|
|
|
|
// issuingDistributionPoint represents the ASN.1 IssuingDistributionPoint
|
|
// SEQUENCE as defined in RFC 5280 Section 5.2.5. We only use three of the
|
|
// fields, so the others are omitted.
|
|
type issuingDistributionPoint struct {
|
|
DistributionPoint distributionPointName `asn1:"optional,tag:0"`
|
|
OnlyContainsUserCerts bool `asn1:"optional,tag:1"`
|
|
OnlyContainsCACerts bool `asn1:"optional,tag:2"`
|
|
}
|
|
|
|
// distributionPointName represents the ASN.1 DistributionPointName CHOICE as
|
|
// defined in RFC 5280 Section 4.2.1.13. We only use one of the fields, so the
|
|
// others are omitted.
|
|
type distributionPointName struct {
|
|
// Technically, FullName is of type GeneralNames, which is of type SEQUENCE OF
|
|
// GeneralName. But GeneralName itself is of type CHOICE, and the asn1.Marshal
|
|
// function doesn't support marshalling structs to CHOICEs, so we have to use
|
|
// asn1.RawValue and encode the GeneralName ourselves.
|
|
FullName []asn1.RawValue `asn1:"optional,tag:0"`
|
|
}
|
|
|
|
// MakeUserCertsExt returns a critical IssuingDistributionPoint extension
|
|
// containing the given URLs and with the OnlyContainsUserCerts boolean set to
|
|
// true.
|
|
func MakeUserCertsExt(urls []string) (pkix.Extension, error) {
|
|
var gns []asn1.RawValue
|
|
for _, url := range urls {
|
|
gns = append(gns, asn1.RawValue{ // GeneralName
|
|
Class: 2, // context-specific
|
|
Tag: 6, // uniformResourceIdentifier, IA5String
|
|
Bytes: []byte(url),
|
|
})
|
|
}
|
|
|
|
val := issuingDistributionPoint{
|
|
DistributionPoint: distributionPointName{FullName: gns},
|
|
OnlyContainsUserCerts: true,
|
|
}
|
|
|
|
valBytes, err := asn1.Marshal(val)
|
|
if err != nil {
|
|
return pkix.Extension{}, err
|
|
}
|
|
|
|
return pkix.Extension{
|
|
Id: idpOID,
|
|
Value: valBytes,
|
|
Critical: true,
|
|
}, nil
|
|
}
|
|
|
|
// MakeCACertsExt returns a critical IssuingDistributionPoint extension
|
|
// asserting the OnlyContainsCACerts boolean.
|
|
func MakeCACertsExt() (*pkix.Extension, error) {
|
|
val := issuingDistributionPoint{
|
|
OnlyContainsCACerts: true,
|
|
}
|
|
|
|
valBytes, err := asn1.Marshal(val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &pkix.Extension{
|
|
Id: idpOID,
|
|
Value: valBytes,
|
|
Critical: true,
|
|
}, nil
|
|
}
|
|
|
|
// GetIDPURIs returns the URIs contained within the issuingDistributionPoint
|
|
// extension, if present, or an error otherwise.
|
|
func GetIDPURIs(exts []pkix.Extension) ([]string, error) {
|
|
for _, ext := range exts {
|
|
if ext.Id.Equal(idpOID) {
|
|
val := issuingDistributionPoint{}
|
|
rest, err := asn1.Unmarshal(ext.Value, &val)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parsing IssuingDistributionPoint extension: %w", err)
|
|
}
|
|
if len(rest) != 0 {
|
|
return nil, fmt.Errorf("parsing IssuingDistributionPoint extension: got %d unexpected trailing bytes", len(rest))
|
|
}
|
|
var uris []string
|
|
for _, generalName := range val.DistributionPoint.FullName {
|
|
uris = append(uris, string(generalName.Bytes))
|
|
}
|
|
return uris, nil
|
|
}
|
|
}
|
|
return nil, errors.New("no IssuingDistributionPoint extension found")
|
|
}
|