// 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.

// Based on earlier versions of https://go.dev/src/crypto/x509/x509.go
// LICENSE file from https://go.dev/LICENSE follows:

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:

//    * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//    * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// 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 (
	oidSignatureSHA1WithRSA     = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
	oidSignatureSHA256WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
	oidSignatureSHA384WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
	oidSignatureSHA512WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
	oidSignatureRSAPSS          = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
	oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
	oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
	oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
	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}
)

type signatureAlgorithmDetail struct {
	algo       x509.SignatureAlgorithm
	name       string
	oid        asn1.ObjectIdentifier
	params     asn1.RawValue
	pubKeyAlgo x509.PublicKeyAlgorithm
	hash       crypto.Hash
	isRSAPSS   bool
}

// Shamelessly copied from stdlib x509.go
var signatureAlgorithmDetails = []signatureAlgorithmDetail{
	// SHA1WithRSA is deprecated, but we still support it for legacy reasons.
	// See testdata/Intel_nuc_pc2.cer for example.
	// TODO: Remove SHA1WithRSA once we no longer need to support it.
	{x509.SHA1WithRSA, "SHA1-RSA", oidSignatureSHA1WithRSA, asn1.NullRawValue, x509.RSA, crypto.SHA1, false},
	{x509.SHA256WithRSA, "SHA256-RSA", oidSignatureSHA256WithRSA, asn1.NullRawValue, x509.RSA, crypto.SHA256, false},
	{x509.SHA384WithRSA, "SHA384-RSA", oidSignatureSHA384WithRSA, asn1.NullRawValue, x509.RSA, crypto.SHA384, false},
	{x509.SHA512WithRSA, "SHA512-RSA", oidSignatureSHA512WithRSA, asn1.NullRawValue, x509.RSA, crypto.SHA512, false},
	{x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, pssParametersSHA256, x509.RSA, crypto.SHA256, true},
	{x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, pssParametersSHA384, x509.RSA, crypto.SHA384, true},
	{x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, pssParametersSHA512, x509.RSA, crypto.SHA512, true},
	{x509.ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, emptyRawValue, x509.ECDSA, crypto.SHA256, false},
	{x509.ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, emptyRawValue, x509.ECDSA, crypto.SHA384, false},
	{x509.ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, emptyRawValue, x509.ECDSA, crypto.SHA512, false},
	{x509.PureEd25519, "Ed25519", oidSignatureEd25519, emptyRawValue, x509.Ed25519, crypto.Hash(0) /* no pre-hashing */, false},
}

var emptyRawValue = asn1.RawValue{}

// DER encoded RSA PSS parameters for the
// SHA256, SHA384, and SHA512 hashes as defined in RFC 3447, Appendix A.2.3.
// The parameters contain the following values:
//   - hashAlgorithm contains the associated hash identifier with NULL parameters
//   - maskGenAlgorithm always contains the default mgf1SHA1 identifier
//   - saltLength contains the length of the associated hash
//   - trailerField always contains the default trailerFieldBC value
var (
	pssParametersSHA256 = asn1.RawValue{FullBytes: []byte{48, 52, 160, 15, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 5, 0, 161, 28, 48, 26, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 8, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 5, 0, 162, 3, 2, 1, 32}}
	pssParametersSHA384 = asn1.RawValue{FullBytes: []byte{48, 52, 160, 15, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 2, 5, 0, 161, 28, 48, 26, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 8, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 2, 5, 0, 162, 3, 2, 1, 48}}
	pssParametersSHA512 = asn1.RawValue{FullBytes: []byte{48, 52, 160, 15, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 3, 5, 0, 161, 28, 48, 26, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 8, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 3, 5, 0, 162, 3, 2, 1, 64}}
)

// 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, &params); 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 `asn1:"optional"`
	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   int64
	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
	}
	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  int64                 `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 int64              `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) {
	switch v.Tag {
	case asn1.TagSet:
		var e pkix.AttributeTypeAndValue
		if _, err := asn1.Unmarshal(v.Bytes, &e); err != nil {
			return nil, err
		}
		return []pkix.AttributeTypeAndValue{e}, nil
	case asn1.TagOctetString:
		var attributes []pkix.AttributeTypeAndValue
		var platformData PlatformDataSequence
		rest, err := asn1.Unmarshal(v.Bytes, &platformData)
		if err != nil {
			return nil, err
		}
		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
	default:
		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
			}
			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
					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
			}
			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)
}
