mirror of
https://github.com/google/go-attestation.git
synced 2025-02-12 13:25:18 +00:00
Some fields have default values, which also means they should be treated as optional. Fix up the tagging to handle that. The natural defaults here will work, so no need to make them explicit.
764 lines
26 KiB
Go
764 lines
26 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Copyright 2019 Google, LLC.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package attributecert parses X.509-encoded attribute certificates.
|
|
package attributecert
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/asn1"
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
"time"
|
|
|
|
"github.com/google/go-attestation/oid"
|
|
)
|
|
|
|
var (
|
|
oidExtensionAuthorityKeyIdentifier = []int{2, 5, 29, 35}
|
|
oidAuthorityInfoAccess = []int{1, 3, 6, 1, 5, 5, 7, 1, 1}
|
|
oidCpsCertificatePolicy = []int{1, 3, 6, 1, 5, 5, 7, 2, 1}
|
|
oidAuthorityInfoAccessOcsp = []int{1, 3, 6, 1, 5, 5, 7, 48, 1}
|
|
oidAuthorityInfoAccessIssuers = []int{1, 3, 6, 1, 5, 5, 7, 48, 2}
|
|
oidTcgCertificatePolicy = []int{1, 2, 840, 113741, 1, 5, 2, 4}
|
|
oidAttributeUserNotice = []int{1, 3, 6, 1, 5, 5, 7, 2, 2}
|
|
oidTcgPlatformManufacturerStrV1 = []int{2, 23, 133, 2, 4}
|
|
oidTcgPlatformModelV1 = []int{2, 23, 133, 2, 5}
|
|
oidTcgPlatformVersionV1 = []int{2, 23, 133, 2, 6}
|
|
)
|
|
|
|
var (
|
|
oidSignatureRSASha1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
|
|
oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
|
|
oidSignatureRSASha256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
|
|
oidSignatureEd25519 = asn1.ObjectIdentifier{1, 3, 101, 112}
|
|
|
|
oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
|
|
oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2}
|
|
oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}
|
|
|
|
oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8}
|
|
)
|
|
|
|
var signatureAlgorithmDetails = []struct {
|
|
algo x509.SignatureAlgorithm
|
|
name string
|
|
oid asn1.ObjectIdentifier
|
|
pubKeyAlgo x509.PublicKeyAlgorithm
|
|
hash crypto.Hash
|
|
}{
|
|
{x509.SHA1WithRSA, "SHA1-RSA", oidSignatureRSASha1, x509.RSA, crypto.SHA1},
|
|
{x509.SHA256WithRSA, "SHA256-RSA", oidSignatureRSASha256, x509.RSA, crypto.SHA256},
|
|
{x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA256},
|
|
{x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384},
|
|
{x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512},
|
|
{x509.PureEd25519, "Ed25519", oidSignatureEd25519, x509.Ed25519, crypto.Hash(0) /* no pre-hashing */},
|
|
}
|
|
|
|
// pssParameters reflects the parameters in an AlgorithmIdentifier that
|
|
// specifies RSA PSS. See RFC 3447, Appendix A.2.3.
|
|
type pssParameters struct {
|
|
// The following three fields are not marked as
|
|
// optional because the default values specify SHA-1,
|
|
// which is no longer suitable for use in signatures.
|
|
Hash pkix.AlgorithmIdentifier `asn1:"explicit,tag:0"`
|
|
MGF pkix.AlgorithmIdentifier `asn1:"explicit,tag:1"`
|
|
SaltLength int `asn1:"explicit,tag:2"`
|
|
TrailerField int `asn1:"optional,explicit,tag:3,default:1"`
|
|
}
|
|
|
|
func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgorithm {
|
|
if ai.Algorithm.Equal(oidSignatureEd25519) {
|
|
// RFC 8410, Section 3
|
|
// > For all of the OIDs, the parameters MUST be absent.
|
|
if len(ai.Parameters.FullBytes) != 0 {
|
|
return x509.UnknownSignatureAlgorithm
|
|
}
|
|
}
|
|
|
|
if !ai.Algorithm.Equal(oidSignatureRSAPSS) {
|
|
for _, details := range signatureAlgorithmDetails {
|
|
if ai.Algorithm.Equal(details.oid) {
|
|
return details.algo
|
|
}
|
|
}
|
|
return x509.UnknownSignatureAlgorithm
|
|
}
|
|
|
|
// RSA PSS is special because it encodes important parameters
|
|
// in the Parameters.
|
|
|
|
var params pssParameters
|
|
if _, err := asn1.Unmarshal(ai.Parameters.FullBytes, ¶ms); err != nil {
|
|
return x509.UnknownSignatureAlgorithm
|
|
}
|
|
|
|
var mgf1HashFunc pkix.AlgorithmIdentifier
|
|
if _, err := asn1.Unmarshal(params.MGF.Parameters.FullBytes, &mgf1HashFunc); err != nil {
|
|
return x509.UnknownSignatureAlgorithm
|
|
}
|
|
|
|
// PSS is greatly overburdened with options. This code forces them into
|
|
// three buckets by requiring that the MGF1 hash function always match the
|
|
// message hash function (as recommended in RFC 3447, Section 8.1), that the
|
|
// salt length matches the hash length, and that the trailer field has the
|
|
// default value.
|
|
if (len(params.Hash.Parameters.FullBytes) != 0 && !bytes.Equal(params.Hash.Parameters.FullBytes, asn1.NullBytes)) ||
|
|
!params.MGF.Algorithm.Equal(oidMGF1) ||
|
|
!mgf1HashFunc.Algorithm.Equal(params.Hash.Algorithm) ||
|
|
(len(mgf1HashFunc.Parameters.FullBytes) != 0 && !bytes.Equal(mgf1HashFunc.Parameters.FullBytes, asn1.NullBytes)) ||
|
|
params.TrailerField != 1 {
|
|
return x509.UnknownSignatureAlgorithm
|
|
}
|
|
|
|
switch {
|
|
case params.Hash.Algorithm.Equal(oidSHA256) && params.SaltLength == 32:
|
|
return x509.SHA256WithRSAPSS
|
|
case params.Hash.Algorithm.Equal(oidSHA384) && params.SaltLength == 48:
|
|
return x509.SHA384WithRSAPSS
|
|
case params.Hash.Algorithm.Equal(oidSHA512) && params.SaltLength == 64:
|
|
return x509.SHA512WithRSAPSS
|
|
}
|
|
|
|
return x509.UnknownSignatureAlgorithm
|
|
}
|
|
|
|
//RFC 5280 4.2.2.1
|
|
type authorityInfoAccess struct {
|
|
Method asn1.ObjectIdentifier
|
|
Location asn1.RawValue
|
|
}
|
|
|
|
//RFC 5280 4.2.1.1
|
|
type authKeyID struct {
|
|
ID []byte `asn1:"optional,tag:0"`
|
|
IssuerName asn1.RawValue `asn1:"set,optional,tag:1"`
|
|
SerialNumber *big.Int `asn1:"optional,tag:2"`
|
|
}
|
|
|
|
//RFC 5280 4.2.1.4
|
|
type cpsPolicy struct {
|
|
ID asn1.ObjectIdentifier
|
|
Value string
|
|
}
|
|
|
|
//RFC 5280 4.2.1.4
|
|
type policyInformation struct {
|
|
Raw asn1.RawContent
|
|
ID asn1.ObjectIdentifier
|
|
Policy asn1.RawValue
|
|
}
|
|
|
|
//RFC 5280 4.1.2.5
|
|
type validity struct {
|
|
NotBefore, NotAfter time.Time
|
|
}
|
|
|
|
//RFC 5280 4.2.1.4
|
|
type NoticeReference struct {
|
|
Organization string
|
|
NoticeNumbers []int
|
|
}
|
|
|
|
//RFC 5280 4.2.1.4
|
|
type userNotice struct {
|
|
NoticeRef NoticeReference `asn1:"optional"`
|
|
ExplicitText string `asn1:"optional"`
|
|
}
|
|
|
|
//RFC 5755 4.1
|
|
type objectDigestInfo struct {
|
|
DigestedObjectType asn1.Enumerated
|
|
OtherObjectTypeID asn1.ObjectIdentifier
|
|
DigestAlgorithm pkix.AlgorithmIdentifier
|
|
ObjectDigest asn1.BitString
|
|
}
|
|
|
|
//RFC 5755 4.1
|
|
type attCertIssuer struct {
|
|
IssuerName asn1.RawValue `asn1:"set,optional"`
|
|
BaseCertificateID issuerSerial `asn1:"optional,tag:0"`
|
|
ObjectDigestInfo objectDigestInfo `asn1:"optional,tag:1"`
|
|
}
|
|
|
|
//RFC 5755 4.1
|
|
type issuerSerial struct {
|
|
Raw asn1.RawContent
|
|
Issuer asn1.RawValue
|
|
Serial *big.Int
|
|
IssuerUID asn1.BitString `asn1:"optional"`
|
|
}
|
|
|
|
//RFC 5755 4.1
|
|
type holder struct {
|
|
Raw asn1.RawContent
|
|
BaseCertificateID issuerSerial `asn1:"optional,tag:0"`
|
|
EntityName pkix.Extension `asn1:"optional,tag:1"`
|
|
ObjectDigestInfo objectDigestInfo `asn1:"optional,tag:2"`
|
|
}
|
|
|
|
//RFC 5755 4.1
|
|
type attribute struct {
|
|
ID asn1.ObjectIdentifier
|
|
RawValues []asn1.RawValue `asn1:"set"`
|
|
}
|
|
|
|
//RFC 5755 4.1
|
|
type tbsAttributeCertificate struct {
|
|
Raw asn1.RawContent
|
|
Version int
|
|
Holder holder
|
|
Issuer attCertIssuer `asn1:"tag:0"`
|
|
SignatureAlgorithm pkix.AlgorithmIdentifier
|
|
SerialNumber *big.Int
|
|
Validity validity
|
|
Attributes []attribute
|
|
IssuerUniqueID asn1.BitString `asn1:"optional"`
|
|
Extensions []pkix.Extension `asn1:"optional"`
|
|
}
|
|
|
|
type attributeCertificate struct {
|
|
Raw asn1.RawContent
|
|
TBSAttributeCertificate tbsAttributeCertificate
|
|
SignatureAlgorithm pkix.AlgorithmIdentifier
|
|
SignatureValue asn1.BitString
|
|
}
|
|
|
|
type Certholder struct {
|
|
Issuer pkix.Name
|
|
Serial *big.Int
|
|
}
|
|
|
|
type Component struct {
|
|
Manufacturer string
|
|
Model string
|
|
Serial string
|
|
Revision string
|
|
ManufacturerID int
|
|
FieldReplaceable bool
|
|
Addresses []ComponentAddress
|
|
}
|
|
|
|
type AttributeCertificate struct {
|
|
Raw []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature).
|
|
RawTBSAttributeCertificate []byte // Certificate part of raw ASN.1 DER content.
|
|
|
|
Signature []byte
|
|
SignatureAlgorithm x509.SignatureAlgorithm
|
|
|
|
Version int
|
|
SerialNumber *big.Int
|
|
Holder Certholder
|
|
Issuer pkix.Name
|
|
Subject pkix.Name
|
|
NotBefore, NotAfter time.Time // Validity bounds.
|
|
TCGPlatformSpecification TCGPlatformSpecification
|
|
TBBSecurityAssertions TBBSecurityAssertions
|
|
PlatformManufacturer string
|
|
PlatformModel string
|
|
PlatformVersion string
|
|
PlatformSerial string
|
|
CredentialSpecification string
|
|
UserNotice userNotice
|
|
Components []Component
|
|
Properties []Property
|
|
PropertiesURI string
|
|
}
|
|
|
|
// ParseAttributeCertificate parses a single attribute certificate from the
|
|
// given ASN.1 DER data.
|
|
func ParseAttributeCertificate(asn1Data []byte) (*AttributeCertificate, error) {
|
|
var cert attributeCertificate
|
|
|
|
rest, err := asn1.Unmarshal(asn1Data, &cert)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if len(rest) != 0 {
|
|
return nil, asn1.SyntaxError{Msg: "attributecert: trailing data"}
|
|
}
|
|
|
|
return parseAttributeCertificate(&cert)
|
|
}
|
|
|
|
type PlatformDataSequence []PlatformDataSET
|
|
type PlatformDataSET []pkix.AttributeTypeAndValue
|
|
|
|
type TCGData struct {
|
|
ID asn1.ObjectIdentifier
|
|
Data string
|
|
}
|
|
|
|
type TCGDirectoryEntry struct {
|
|
ID asn1.ObjectIdentifier
|
|
Data asn1.RawValue
|
|
}
|
|
|
|
type TCGSpecificationVersion struct {
|
|
MajorVersion int
|
|
MinorVersion int
|
|
Revision int
|
|
}
|
|
|
|
type TCGPlatformSpecification struct {
|
|
Version TCGSpecificationVersion
|
|
}
|
|
|
|
type TCGCredentialSpecification struct {
|
|
Version TCGSpecificationVersion
|
|
}
|
|
|
|
type TCGCredentialType struct {
|
|
CertificateType asn1.ObjectIdentifier
|
|
}
|
|
|
|
type FipsLevel struct {
|
|
Version string
|
|
Level asn1.Enumerated
|
|
Plus bool `asn1:"optional,default=false"`
|
|
}
|
|
|
|
type CommonCriteriaMeasures struct {
|
|
Version string
|
|
AssuranceLevel asn1.Enumerated
|
|
EvaluationStatus asn1.Enumerated
|
|
Plus bool
|
|
StrengthOfFunction asn1.Enumerated `asn1:"optional,tag:0"`
|
|
ProfileOid asn1.ObjectIdentifier `asn1:"optional,tag:1"`
|
|
ProfileURI string `asn1:"optional,tag:2"`
|
|
TargetOid asn1.ObjectIdentifier `asn1:"optional,tag:3"`
|
|
TargetURI asn1.ObjectIdentifier `asn1:"optional,tag:4"`
|
|
}
|
|
|
|
type TBBSecurityAssertions struct {
|
|
Version int `asn1:"optional,default=0"`
|
|
CcInfo CommonCriteriaMeasures `asn1:"optional,tag:0"`
|
|
FipsLevel FipsLevel `asn1:"optional,tag:1"`
|
|
RtmType asn1.Enumerated `asn1:"optional,tag:2"`
|
|
Iso9000Certified bool `asn1:"optional,default=false"`
|
|
Iso9000URI string `asn1:"optional"`
|
|
}
|
|
|
|
// Certificates with this information in the SDA region appear to fail to
|
|
// tag the optional fields
|
|
type CommonCriteriaMeasures_sda struct {
|
|
Version string
|
|
AssuranceLevel asn1.Enumerated
|
|
EvaluationStatus asn1.Enumerated
|
|
Plus bool `asn1:"optional,default=false"`
|
|
StrengthOfFunction asn1.Enumerated `asn1:"optional"`
|
|
ProfileOid asn1.ObjectIdentifier `asn1:"optional"`
|
|
ProfileURI string `asn1:"optional"`
|
|
TargetOid asn1.ObjectIdentifier `asn1:"optional"`
|
|
TargetURI asn1.ObjectIdentifier `asn1:"optional"`
|
|
}
|
|
|
|
type TBBSecurityAssertions_sda struct {
|
|
Version int
|
|
CcInfo CommonCriteriaMeasures_sda `asn1:"optional"`
|
|
FipsLevel FipsLevel `asn1:"optional"`
|
|
RtmType asn1.Enumerated `asn1:"optional"`
|
|
Iso9000Certified bool `asn1:"optional"`
|
|
Iso9000URI string `asn1:"optional"`
|
|
}
|
|
|
|
type Property struct {
|
|
PropertyName string
|
|
PropertyValue string
|
|
Status asn1.Enumerated `asn1:"optional,tag:0"`
|
|
}
|
|
|
|
type AttributeCertificateIdentifier struct {
|
|
HashAlgorithm pkix.AlgorithmIdentifier
|
|
HashOverSignatureValue string
|
|
}
|
|
|
|
type CertificateIdentifier struct {
|
|
AttributeCertIdentifier AttributeCertificateIdentifier `asn1:"optional,tag:0"`
|
|
GenericCertIdientifier issuerSerial `asn1:"optional,tag:1"`
|
|
}
|
|
|
|
type ComponentAddress struct {
|
|
AddressType asn1.ObjectIdentifier
|
|
AddressValue string
|
|
}
|
|
|
|
type ComponentClass struct {
|
|
ComponentClassRegistry asn1.ObjectIdentifier
|
|
ComponentClassValue []byte
|
|
}
|
|
|
|
type ComponentIdentifierV2 struct {
|
|
ComponentClass ComponentClass
|
|
ComponentManufacturer string
|
|
ComponentModel string
|
|
ComponentSerial string `asn1:"optional,utf8,tag:0"`
|
|
ComponentRevision string `asn1:"optional,utf8,tag:1"`
|
|
ComponentManufacturerID int `asn1:"optional,tag:2"`
|
|
FieldReplaceable bool `asn1:"optional,tag:3"`
|
|
ComponentAddresses []ComponentAddress `asn1:"optional,tag:4"`
|
|
ComponentPlatformCert CertificateIdentifier `asn1:"optional,tag:5"`
|
|
ComponentPlatformCertURI string `asn1:"optional,tag:6"`
|
|
Status asn1.Enumerated `asn1:"optional,tag:7"`
|
|
}
|
|
|
|
type URIReference struct {
|
|
UniformResourceIdentifier string
|
|
HashAlgorithm pkix.AlgorithmIdentifier `asn1:"optional"`
|
|
HashValue string `asn1:"optional"`
|
|
}
|
|
|
|
type PlatformConfigurationV2 struct {
|
|
ComponentIdentifiers []ComponentIdentifierV2 `asn1:"optional,tag:0"`
|
|
ComponentIdentifiersURI URIReference `asn1:"optional,tag:1"`
|
|
PlatformProperties []Property `asn1:"optional,tag:2"`
|
|
PlatformPropertiesURI URIReference `asn1:"optional,tag:3"`
|
|
}
|
|
|
|
type PlatformConfigurationV2Workaround struct {
|
|
ComponentIdentifiers []ComponentIdentifierV2 `asn1:"optional,tag:0"`
|
|
ComponentIdentifiersURI URIReference `asn1:"optional,tag:1"`
|
|
PlatformProperty Property `asn1:"optional,tag:2"`
|
|
PlatformPropertiesURI URIReference `asn1:"optional,tag:3"`
|
|
}
|
|
|
|
type ComponentIdentifierV1 struct {
|
|
ComponentClass []byte `asn1:"optional"`
|
|
ComponentManufacturer string
|
|
ComponentModel string
|
|
ComponentSerial string `asn1:"optional,utf8,tag:0"`
|
|
ComponentRevision string `asn1:"optional,utf8,tag:1"`
|
|
ComponentManufacturerID int `asn1:"optional,tag:2"`
|
|
FieldReplaceable bool `asn1:"optional,tag:3"`
|
|
ComponentAddresses []ComponentAddress `asn1:"optional,tag:4"`
|
|
}
|
|
|
|
type PlatformConfigurationV1 struct {
|
|
ComponentIdentifiers []ComponentIdentifierV1 `asn1:"optional,tag:0"`
|
|
PlatformProperties []Property `asn1:"optional,tag:1"`
|
|
PlatformPropertiesURI URIReference `asn1:"optional,tag:2"`
|
|
}
|
|
|
|
func unmarshalSAN(v asn1.RawValue) ([]pkix.AttributeTypeAndValue, error) {
|
|
if v.Tag == asn1.TagSet {
|
|
var e pkix.AttributeTypeAndValue
|
|
if _, err := asn1.Unmarshal(v.Bytes, &e); err != nil {
|
|
return nil, err
|
|
}
|
|
return []pkix.AttributeTypeAndValue{e}, nil
|
|
} else if v.Tag == asn1.TagOctetString {
|
|
var attributes []pkix.AttributeTypeAndValue
|
|
var platformData PlatformDataSequence
|
|
rest, err := asn1.Unmarshal(v.Bytes, &platformData)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if len(rest) != 0 {
|
|
return nil, errors.New("attributecert: trailing data after X.509 subject")
|
|
}
|
|
for _, e := range platformData {
|
|
for _, e2 := range e {
|
|
attributes = append(attributes, e2)
|
|
}
|
|
}
|
|
return attributes, nil
|
|
}
|
|
return nil, fmt.Errorf("attributecert: unexpected SAN type %v", v.Tag)
|
|
}
|
|
|
|
func parseAttributeCertificate(in *attributeCertificate) (*AttributeCertificate, error) {
|
|
out := &AttributeCertificate{
|
|
Raw: in.Raw,
|
|
RawTBSAttributeCertificate: in.TBSAttributeCertificate.Raw,
|
|
Signature: in.SignatureValue.RightAlign(),
|
|
SignatureAlgorithm: getSignatureAlgorithmFromAI(in.TBSAttributeCertificate.SignatureAlgorithm),
|
|
Version: in.TBSAttributeCertificate.Version + 1,
|
|
SerialNumber: in.TBSAttributeCertificate.SerialNumber,
|
|
}
|
|
|
|
var v asn1.RawValue
|
|
if _, err := asn1.Unmarshal(in.TBSAttributeCertificate.Issuer.IssuerName.Bytes, &v); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var issuer pkix.RDNSequence
|
|
if rest, err := asn1.Unmarshal(v.Bytes, &issuer); err != nil {
|
|
return nil, err
|
|
} else if len(rest) != 0 {
|
|
return nil, errors.New("attributecert: trailing data after X.509 subject")
|
|
}
|
|
|
|
out.Issuer.FillFromRDNSequence(&issuer)
|
|
if _, err := asn1.Unmarshal(in.TBSAttributeCertificate.Holder.BaseCertificateID.Issuer.Bytes, &v); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var holder pkix.RDNSequence
|
|
if rest, err := asn1.Unmarshal(v.Bytes, &holder); err != nil {
|
|
return nil, err
|
|
} else if len(rest) != 0 {
|
|
return nil, errors.New("attributecert: trailing data after X.509 subject")
|
|
}
|
|
|
|
out.Holder.Issuer.FillFromRDNSequence(&holder)
|
|
out.Holder.Serial = in.TBSAttributeCertificate.Holder.BaseCertificateID.Serial
|
|
|
|
out.NotBefore = in.TBSAttributeCertificate.Validity.NotBefore
|
|
out.NotAfter = in.TBSAttributeCertificate.Validity.NotAfter
|
|
|
|
for _, attribute := range in.TBSAttributeCertificate.Attributes {
|
|
switch {
|
|
case attribute.ID.Equal(oidAttributeUserNotice):
|
|
if _, err := asn1.Unmarshal(attribute.RawValues[0].FullBytes, &out.UserNotice); err != nil {
|
|
return nil, err
|
|
}
|
|
case attribute.ID.Equal(oid.TCGPlatformSpecification):
|
|
if _, err := asn1.Unmarshal(attribute.RawValues[0].FullBytes, &out.TCGPlatformSpecification); err != nil {
|
|
return nil, err
|
|
}
|
|
case attribute.ID.Equal(oid.TBBSecurityAssertions):
|
|
if _, err := asn1.Unmarshal(attribute.RawValues[0].FullBytes, &out.TBBSecurityAssertions); err != nil {
|
|
return nil, err
|
|
}
|
|
case attribute.ID.Equal(oid.TCGCredentialSpecification):
|
|
var credentialSpecification TCGCredentialSpecification
|
|
if _, err := asn1.Unmarshal(attribute.RawValues[0].FullBytes, &credentialSpecification); err != nil {
|
|
var credentialSpecification TCGSpecificationVersion
|
|
if _, err := asn1.Unmarshal(attribute.RawValues[0].FullBytes, &credentialSpecification); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
case attribute.ID.Equal(oid.TCGCredentialType):
|
|
var credentialType TCGCredentialType
|
|
if _, err := asn1.Unmarshal(attribute.RawValues[0].FullBytes, &credentialType); err != nil {
|
|
return nil, err
|
|
}
|
|
case attribute.ID.Equal(oid.PlatformConfigurationV1):
|
|
var platformConfiguration PlatformConfigurationV1
|
|
if _, err := asn1.Unmarshal(attribute.RawValues[0].FullBytes, &platformConfiguration); err != nil {
|
|
return nil, err
|
|
}
|
|
for _, component := range platformConfiguration.ComponentIdentifiers {
|
|
t := Component{
|
|
Manufacturer: component.ComponentManufacturer,
|
|
Model: component.ComponentModel,
|
|
Serial: component.ComponentSerial,
|
|
Revision: component.ComponentRevision,
|
|
ManufacturerID: component.ComponentManufacturerID,
|
|
FieldReplaceable: component.FieldReplaceable,
|
|
Addresses: component.ComponentAddresses,
|
|
}
|
|
out.Components = append(out.Components, t)
|
|
}
|
|
out.Properties = platformConfiguration.PlatformProperties
|
|
out.PropertiesURI = platformConfiguration.PlatformPropertiesURI.UniformResourceIdentifier
|
|
case attribute.ID.Equal(oid.PlatformConfigurationV2):
|
|
var platformConfiguration PlatformConfigurationV2
|
|
if _, err := asn1.Unmarshal(attribute.RawValues[0].FullBytes, &platformConfiguration); err != nil {
|
|
var workaround PlatformConfigurationV2Workaround
|
|
if _, err := asn1.Unmarshal(attribute.RawValues[0].FullBytes, &workaround); err != nil {
|
|
return nil, err
|
|
}
|
|
platformConfiguration.ComponentIdentifiers = workaround.ComponentIdentifiers
|
|
platformConfiguration.ComponentIdentifiersURI = workaround.ComponentIdentifiersURI
|
|
platformConfiguration.PlatformProperties = append(platformConfiguration.PlatformProperties, workaround.PlatformProperty)
|
|
platformConfiguration.PlatformPropertiesURI = workaround.PlatformPropertiesURI
|
|
}
|
|
for _, component := range platformConfiguration.ComponentIdentifiers {
|
|
t := Component{
|
|
Manufacturer: component.ComponentManufacturer,
|
|
Model: component.ComponentModel,
|
|
Serial: component.ComponentSerial,
|
|
Revision: component.ComponentRevision,
|
|
ManufacturerID: component.ComponentManufacturerID,
|
|
FieldReplaceable: component.FieldReplaceable,
|
|
Addresses: component.ComponentAddresses,
|
|
}
|
|
out.Components = append(out.Components, t)
|
|
}
|
|
out.Properties = platformConfiguration.PlatformProperties
|
|
out.PropertiesURI = platformConfiguration.PlatformPropertiesURI.UniformResourceIdentifier
|
|
case attribute.ID.Equal(oid.PlatformConfigURI):
|
|
var platformConfigurationURI URIReference
|
|
if _, err := asn1.Unmarshal(attribute.RawValues[0].FullBytes, &platformConfigurationURI); err != nil {
|
|
return nil, err
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("attributecert: unknown attribute %v", attribute.ID)
|
|
}
|
|
}
|
|
|
|
for _, extension := range in.TBSAttributeCertificate.Extensions {
|
|
switch {
|
|
case extension.Id.Equal(oid.SubjectAltName):
|
|
var seq asn1.RawValue
|
|
rest, err := asn1.Unmarshal(extension.Value, &seq)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if len(rest) != 0 {
|
|
return nil, errors.New("attributecert: trailing data after X.509 extension")
|
|
}
|
|
rest = seq.Bytes
|
|
for len(rest) > 0 {
|
|
var v asn1.RawValue
|
|
rest, err = asn1.Unmarshal(rest, &v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tcgdata, err := unmarshalSAN(v)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("attributecert: failed to unmarshal SAN: %v", err)
|
|
}
|
|
for _, e := range tcgdata {
|
|
switch {
|
|
case e.Type.Equal(oidTcgPlatformManufacturerStrV1):
|
|
out.PlatformManufacturer = e.Value.(string)
|
|
case e.Type.Equal(oidTcgPlatformModelV1):
|
|
out.PlatformModel = e.Value.(string)
|
|
case e.Type.Equal(oidTcgPlatformVersionV1):
|
|
out.PlatformVersion = e.Value.(string)
|
|
case e.Type.Equal(oid.TCGCredentialSpecification):
|
|
// This OID appears to be misused in this context
|
|
out.PlatformSerial = e.Value.(string)
|
|
case e.Type.Equal(oid.PlatformManufacturerStr):
|
|
out.PlatformManufacturer = e.Value.(string)
|
|
case e.Type.Equal(oid.PlatformManufacturerID):
|
|
// We can't parse these out at present
|
|
break
|
|
case e.Type.Equal(oid.PlatformModel):
|
|
out.PlatformModel = e.Value.(string)
|
|
case e.Type.Equal(oid.PlatformVersion):
|
|
out.PlatformVersion = e.Value.(string)
|
|
case e.Type.Equal(oid.PlatformSerial):
|
|
out.PlatformSerial = e.Value.(string)
|
|
default:
|
|
return nil, fmt.Errorf("attributecert: unhandled attribute: %v", e.Type)
|
|
}
|
|
}
|
|
}
|
|
|
|
case extension.Id.Equal(oid.SubjectDirectoryAttributes):
|
|
var seq asn1.RawValue
|
|
rest, err := asn1.Unmarshal(extension.Value, &seq)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if len(rest) != 0 {
|
|
return nil, errors.New("attributecert: trailing data after X.509 extension")
|
|
}
|
|
rest = seq.Bytes
|
|
for len(rest) > 0 {
|
|
var e TCGDirectoryEntry
|
|
rest, err = asn1.Unmarshal(rest, &e)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch {
|
|
case e.ID.Equal(oid.TCGPlatformSpecification):
|
|
var platformSpecification TCGPlatformSpecification
|
|
_, err := asn1.Unmarshal(e.Data.Bytes, &platformSpecification)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out.TCGPlatformSpecification = platformSpecification
|
|
case e.ID.Equal(oid.TBBSecurityAssertions):
|
|
var securityAssertions TBBSecurityAssertions_sda
|
|
_, err := asn1.Unmarshal(e.Data.Bytes, &securityAssertions)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out.TBBSecurityAssertions.Version = securityAssertions.Version
|
|
out.TBBSecurityAssertions.CcInfo = CommonCriteriaMeasures(securityAssertions.CcInfo)
|
|
out.TBBSecurityAssertions.FipsLevel = securityAssertions.FipsLevel
|
|
out.TBBSecurityAssertions.RtmType = securityAssertions.RtmType
|
|
out.TBBSecurityAssertions.Iso9000Certified = securityAssertions.Iso9000Certified
|
|
out.TBBSecurityAssertions.Iso9000URI = securityAssertions.Iso9000URI
|
|
default:
|
|
return nil, fmt.Errorf("attributecert: unhandled TCG directory attribute: %v", e.ID)
|
|
}
|
|
}
|
|
|
|
case extension.Id.Equal(oid.CertificatePolicies):
|
|
var policies []policyInformation
|
|
_, err := asn1.Unmarshal(extension.Value, &policies)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, policy := range policies {
|
|
if policy.ID.Equal(oidTcgCertificatePolicy) {
|
|
var subpolicies []policyInformation
|
|
_, err := asn1.Unmarshal(policy.Policy.FullBytes, &subpolicies)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, subpolicy := range subpolicies {
|
|
switch {
|
|
case subpolicy.ID.Equal(oidCpsCertificatePolicy):
|
|
var cpsPolicy cpsPolicy
|
|
_, err := asn1.Unmarshal(subpolicy.Raw, &cpsPolicy)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
case subpolicy.ID.Equal(oidAttributeUserNotice):
|
|
var userNotice string
|
|
_, err := asn1.Unmarshal(subpolicy.Policy.Bytes, &userNotice)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("attributecert: unhandled certificate policy: %v", subpolicy.ID)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
case extension.Id.Equal(oidExtensionAuthorityKeyIdentifier):
|
|
var a authKeyID
|
|
_, err := asn1.Unmarshal(extension.Value, &a)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
case extension.Id.Equal(oidAuthorityInfoAccess):
|
|
var aia []authorityInfoAccess
|
|
_, err := asn1.Unmarshal(extension.Value, &aia)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, v := range aia {
|
|
if v.Method.Equal(oidAuthorityInfoAccessOcsp) {
|
|
//TODO
|
|
} else if v.Method.Equal(oidAuthorityInfoAccessIssuers) {
|
|
//TODO
|
|
} else {
|
|
return nil, fmt.Errorf("attributecert: unhandled Authority Info Access type %v", v.Method)
|
|
}
|
|
}
|
|
|
|
default:
|
|
return nil, fmt.Errorf("attributecert: unknown extension ID %v", extension.Id)
|
|
}
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
// CheckSignatureFrom verifies that the signature on c is a valid signature
|
|
// from parent.
|
|
func (c *AttributeCertificate) CheckSignatureFrom(parent *x509.Certificate) error {
|
|
if parent.KeyUsage != 0 && parent.KeyUsage&x509.KeyUsageCertSign == 0 {
|
|
return x509.ConstraintViolationError{}
|
|
}
|
|
|
|
if parent.PublicKeyAlgorithm == x509.UnknownPublicKeyAlgorithm {
|
|
return x509.ErrUnsupportedAlgorithm
|
|
}
|
|
|
|
// TODO(agl): don't ignore the path length constraint.
|
|
|
|
return parent.CheckSignature(c.SignatureAlgorithm, c.RawTBSAttributeCertificate, c.Signature)
|
|
}
|