mirror of
https://github.com/google/go-attestation.git
synced 2025-01-26 21:59:18 +00:00
x509ext: initial version of package (#279)
This commit is contained in:
parent
50e72a4743
commit
cb976082a3
174
x509/x509ext.go
Normal file
174
x509/x509ext.go
Normal file
@ -0,0 +1,174 @@
|
||||
// Package x509ext provides functions for (un)marshalling X.509 extensions not
|
||||
// supported by the crypto/x509 package.
|
||||
package x509ext
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/go-attestation/oid"
|
||||
)
|
||||
|
||||
// RFC 4043
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc4043
|
||||
var (
|
||||
oidPermanentIdentifier = []int{1, 3, 6, 1, 5, 5, 7, 8, 3}
|
||||
)
|
||||
|
||||
// OtherName ::= SEQUENCE {
|
||||
// type-id OBJECT IDENTIFIER,
|
||||
// value [0] EXPLICIT ANY DEFINED BY type-id }
|
||||
type otherName struct {
|
||||
TypeID asn1.ObjectIdentifier
|
||||
Value asn1.RawValue
|
||||
}
|
||||
|
||||
func marshalOtherName(typeID asn1.ObjectIdentifier, value interface{}) (asn1.RawValue, error) {
|
||||
valueBytes, err := asn1.MarshalWithParams(value, "explicit,tag:0")
|
||||
if err != nil {
|
||||
return asn1.RawValue{}, err
|
||||
}
|
||||
otherName := otherName{
|
||||
TypeID: typeID,
|
||||
Value: asn1.RawValue{FullBytes: valueBytes},
|
||||
}
|
||||
bytes, err := asn1.MarshalWithParams(otherName, "tag:0")
|
||||
if err != nil {
|
||||
return asn1.RawValue{}, err
|
||||
}
|
||||
return asn1.RawValue{FullBytes: bytes}, nil
|
||||
}
|
||||
|
||||
// PermanentIdentifier ::= SEQUENCE {
|
||||
// identifierValue UTF8String OPTIONAL,
|
||||
// assigner OBJECT IDENTIFIER OPTIONAL
|
||||
// }
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc4043
|
||||
type PermanentIdentifier struct {
|
||||
IdentifierValue string `asn1:"utf8,optional"`
|
||||
Assigner asn1.ObjectIdentifier `asn1:"optional"`
|
||||
}
|
||||
|
||||
func parsePermanentIdentifier(der []byte) (PermanentIdentifier, error) {
|
||||
var permID PermanentIdentifier
|
||||
if _, err := asn1.UnmarshalWithParams(der, &permID, "explicit,tag:0"); err != nil {
|
||||
return PermanentIdentifier{}, err
|
||||
}
|
||||
return permID, nil
|
||||
}
|
||||
|
||||
// SubjectAltName contains GeneralName variations not supported by the
|
||||
// crypto/x509 package.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc5280
|
||||
type SubjectAltName struct {
|
||||
DirectoryNames []pkix.Name
|
||||
PermanentIdentifiers []PermanentIdentifier
|
||||
}
|
||||
|
||||
// ParseSubjectAltName parses a pkix.Extension into a SubjectAltName struct.
|
||||
func ParseSubjectAltName(ext pkix.Extension) (*SubjectAltName, error) {
|
||||
var out SubjectAltName
|
||||
dirNames, otherNames, err := parseSubjectAltName(ext)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parseSubjectAltName: %v", err)
|
||||
}
|
||||
out.DirectoryNames = dirNames
|
||||
|
||||
for _, otherName := range otherNames {
|
||||
if otherName.TypeID.Equal(oidPermanentIdentifier) {
|
||||
permID, err := parsePermanentIdentifier(otherName.Value.FullBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsePermanentIdentifier: %v", err)
|
||||
}
|
||||
out.PermanentIdentifiers = append(out.PermanentIdentifiers, permID)
|
||||
}
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc5280#page-35
|
||||
func parseSubjectAltName(ext pkix.Extension) (dirNames []pkix.Name, otherNames []otherName, err error) {
|
||||
err = forEachSAN(ext.Value, func(generalName asn1.RawValue) error {
|
||||
switch generalName.Tag {
|
||||
case 0: // otherName
|
||||
var otherName otherName
|
||||
if _, err := asn1.UnmarshalWithParams(generalName.FullBytes, &otherName, "tag:0"); err != nil {
|
||||
return fmt.Errorf("OtherName: asn1.UnmarshalWithParams: %v", err)
|
||||
}
|
||||
otherNames = append(otherNames, otherName)
|
||||
case 4: // directoryName
|
||||
var rdns pkix.RDNSequence
|
||||
if _, err := asn1.Unmarshal(generalName.Bytes, &rdns); err != nil {
|
||||
return fmt.Errorf("DirectoryName: asn1.Unmarshal: %v", err)
|
||||
}
|
||||
var dirName pkix.Name
|
||||
dirName.FillFromRDNSequence(&rdns)
|
||||
dirNames = append(dirNames, dirName)
|
||||
default:
|
||||
return fmt.Errorf("expected tag %d", generalName.Tag)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Borrowed from the x509 package.
|
||||
func forEachSAN(extension []byte, callback func(ext asn1.RawValue) error) error {
|
||||
var seq asn1.RawValue
|
||||
rest, err := asn1.Unmarshal(extension, &seq)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if len(rest) != 0 {
|
||||
return errors.New("x509: trailing data after X.509 extension")
|
||||
}
|
||||
if !seq.IsCompound || seq.Tag != 16 || seq.Class != 0 {
|
||||
return asn1.StructuralError{Msg: "bad SAN sequence"}
|
||||
}
|
||||
|
||||
rest = seq.Bytes
|
||||
for len(rest) > 0 {
|
||||
var v asn1.RawValue
|
||||
rest, err = asn1.Unmarshal(rest, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := callback(v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalSubjectAltName converts a SubjectAltName struct into a pkix.Extension.
|
||||
func MarshalSubjectAltName(san *SubjectAltName) (pkix.Extension, error) {
|
||||
var generalNames []asn1.RawValue
|
||||
for _, permID := range san.PermanentIdentifiers {
|
||||
val, err := marshalOtherName(oidPermanentIdentifier, permID)
|
||||
if err != nil {
|
||||
return pkix.Extension{}, err
|
||||
}
|
||||
generalNames = append(generalNames, val)
|
||||
}
|
||||
for _, dirName := range san.DirectoryNames {
|
||||
bytes, err := asn1.MarshalWithParams(dirName.ToRDNSequence(), "explicit,tag:4")
|
||||
if err != nil {
|
||||
return pkix.Extension{}, err
|
||||
}
|
||||
generalNames = append(generalNames, asn1.RawValue{FullBytes: bytes})
|
||||
}
|
||||
val, err := asn1.Marshal(generalNames)
|
||||
if err != nil {
|
||||
return pkix.Extension{}, err
|
||||
}
|
||||
return pkix.Extension{
|
||||
Id: oid.SubjectAltName,
|
||||
Value: val,
|
||||
}, nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user