2019-10-28 06:12:15 +00:00
|
|
|
// 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"
|
2022-03-24 02:31:53 +00:00
|
|
|
"crypto/x509"
|
|
|
|
"crypto/x509/pkix"
|
|
|
|
"encoding/asn1"
|
2019-10-28 06:12:15 +00:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"math/big"
|
|
|
|
"time"
|
2020-07-23 05:32:32 +00:00
|
|
|
|
|
|
|
"github.com/google/go-attestation/oid"
|
2019-10-28 06:12:15 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2020-07-23 05:32:32 +00:00
|
|
|
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}
|
2019-10-28 06:12:15 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2020-05-08 19:15:58 +00:00
|
|
|
oidSignatureRSASha1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
|
|
|
|
oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
|
2020-05-06 22:21:05 +00:00
|
|
|
oidSignatureRSASha256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
|
2020-05-08 19:15:58 +00:00
|
|
|
oidSignatureEd25519 = asn1.ObjectIdentifier{1, 3, 101, 112}
|
2019-10-28 06:12:15 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}{
|
2020-05-06 22:21:05 +00:00
|
|
|
{x509.SHA1WithRSA, "SHA1-RSA", oidSignatureRSASha1, x509.RSA, crypto.SHA1},
|
|
|
|
{x509.SHA256WithRSA, "SHA256-RSA", oidSignatureRSASha256, x509.RSA, crypto.SHA256},
|
2019-10-28 06:12:15 +00:00
|
|
|
{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
|
2019-11-06 10:04:38 +00:00
|
|
|
type authKeyID struct {
|
|
|
|
ID []byte `asn1:"optional,tag:0"`
|
2019-10-28 06:12:15 +00:00
|
|
|
IssuerName asn1.RawValue `asn1:"set,optional,tag:1"`
|
|
|
|
SerialNumber *big.Int `asn1:"optional,tag:2"`
|
|
|
|
}
|
|
|
|
|
|
|
|
//RFC 5280 4.2.1.4
|
|
|
|
type cpsPolicy struct {
|
2019-11-06 10:04:38 +00:00
|
|
|
ID asn1.ObjectIdentifier
|
2019-10-28 06:12:15 +00:00
|
|
|
Value string
|
|
|
|
}
|
|
|
|
|
|
|
|
//RFC 5280 4.2.1.4
|
|
|
|
type policyInformation struct {
|
|
|
|
Raw asn1.RawContent
|
2019-11-06 10:04:38 +00:00
|
|
|
ID asn1.ObjectIdentifier
|
2019-10-28 06:12:15 +00:00
|
|
|
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"`
|
2019-11-06 10:04:38 +00:00
|
|
|
BaseCertificateID issuerSerial `asn1:"optional,tag:0"`
|
2019-10-28 06:12:15 +00:00
|
|
|
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 {
|
2019-11-06 10:04:38 +00:00
|
|
|
ID asn1.ObjectIdentifier
|
2019-10-28 06:12:15 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-05-08 19:52:40 +00:00
|
|
|
type Component struct {
|
|
|
|
Manufacturer string
|
|
|
|
Model string
|
|
|
|
Serial string
|
|
|
|
Revision string
|
|
|
|
ManufacturerID int
|
|
|
|
FieldReplaceable bool
|
|
|
|
Addresses []ComponentAddress
|
|
|
|
}
|
|
|
|
|
2019-10-28 06:12:15 +00:00
|
|
|
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
|
2020-05-08 19:52:40 +00:00
|
|
|
Components []Component
|
|
|
|
Properties []Property
|
|
|
|
PropertiesURI string
|
2019-10-28 06:12:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2019-11-06 10:04:38 +00:00
|
|
|
ID asn1.ObjectIdentifier
|
2019-10-28 06:12:15 +00:00
|
|
|
Data string
|
|
|
|
}
|
|
|
|
|
|
|
|
type TCGDirectoryEntry struct {
|
2019-11-06 10:04:38 +00:00
|
|
|
ID asn1.ObjectIdentifier
|
2019-10-28 06:12:15 +00:00
|
|
|
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
|
2020-11-17 20:00:54 +00:00
|
|
|
Plus bool `asn1:"optional,default=false"`
|
2019-10-28 06:12:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type CommonCriteriaMeasures struct {
|
|
|
|
Version string
|
|
|
|
AssuranceLevel asn1.Enumerated
|
|
|
|
EvaluationStatus asn1.Enumerated
|
|
|
|
Plus bool
|
2020-05-08 19:15:58 +00:00
|
|
|
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"`
|
2019-10-28 06:12:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type TBBSecurityAssertions struct {
|
2020-11-17 20:00:54 +00:00
|
|
|
Version int `asn1:"optional,default=0"`
|
2020-05-08 19:15:58 +00:00
|
|
|
CcInfo CommonCriteriaMeasures `asn1:"optional,tag:0"`
|
|
|
|
FipsLevel FipsLevel `asn1:"optional,tag:1"`
|
|
|
|
RtmType asn1.Enumerated `asn1:"optional,tag:2"`
|
2020-11-17 20:00:54 +00:00
|
|
|
Iso9000Certified bool `asn1:"optional,default=false"`
|
2019-11-06 10:04:38 +00:00
|
|
|
Iso9000URI string `asn1:"optional"`
|
2019-10-28 06:12:15 +00:00
|
|
|
}
|
|
|
|
|
2020-05-08 19:15:58 +00:00
|
|
|
// 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
|
2020-11-17 20:00:54 +00:00
|
|
|
Plus bool `asn1:"optional,default=false"`
|
2020-05-08 19:15:58 +00:00
|
|
|
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"`
|
|
|
|
}
|
|
|
|
|
2019-10-28 06:12:15 +00:00
|
|
|
type Property struct {
|
|
|
|
PropertyName string
|
|
|
|
PropertyValue string
|
2020-05-08 19:15:58 +00:00
|
|
|
Status asn1.Enumerated `asn1:"optional,tag:0"`
|
2019-10-28 06:12:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type AttributeCertificateIdentifier struct {
|
|
|
|
HashAlgorithm pkix.AlgorithmIdentifier
|
|
|
|
HashOverSignatureValue string
|
|
|
|
}
|
|
|
|
|
|
|
|
type CertificateIdentifier struct {
|
2020-05-08 19:15:58 +00:00
|
|
|
AttributeCertIdentifier AttributeCertificateIdentifier `asn1:"optional,tag:0"`
|
|
|
|
GenericCertIdientifier issuerSerial `asn1:"optional,tag:1"`
|
2019-10-28 06:12:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type ComponentAddress struct {
|
|
|
|
AddressType asn1.ObjectIdentifier
|
|
|
|
AddressValue string
|
|
|
|
}
|
|
|
|
|
|
|
|
type ComponentClass struct {
|
|
|
|
ComponentClassRegistry asn1.ObjectIdentifier
|
2020-11-13 00:18:30 +00:00
|
|
|
ComponentClassValue []byte
|
2019-10-28 06:12:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type ComponentIdentifierV2 struct {
|
|
|
|
ComponentClass ComponentClass
|
|
|
|
ComponentManufacturer string
|
|
|
|
ComponentModel string
|
2020-11-12 23:31:24 +00:00
|
|
|
ComponentSerial string `asn1:"optional,utf8,tag:0"`
|
|
|
|
ComponentRevision string `asn1:"optional,utf8,tag:1"`
|
2020-05-08 19:15:58 +00:00
|
|
|
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"`
|
2019-10-28 06:12:15 +00:00
|
|
|
}
|
|
|
|
|
2019-11-06 10:04:38 +00:00
|
|
|
type URIReference struct {
|
2019-10-28 06:12:15 +00:00
|
|
|
UniformResourceIdentifier string
|
|
|
|
HashAlgorithm pkix.AlgorithmIdentifier `asn1:"optional"`
|
|
|
|
HashValue string `asn1:"optional"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type PlatformConfigurationV2 struct {
|
2020-05-08 19:15:58 +00:00
|
|
|
ComponentIdentifiers []ComponentIdentifierV2 `asn1:"optional,tag:0"`
|
|
|
|
ComponentIdentifiersURI URIReference `asn1:"optional,tag:1"`
|
|
|
|
PlatformProperties []Property `asn1:"optional,tag:2"`
|
|
|
|
PlatformPropertiesURI URIReference `asn1:"optional,tag:3"`
|
2019-10-28 06:12:15 +00:00
|
|
|
}
|
|
|
|
|
2020-05-30 00:24:06 +00:00
|
|
|
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"`
|
|
|
|
}
|
|
|
|
|
2019-10-28 06:12:15 +00:00
|
|
|
type ComponentIdentifierV1 struct {
|
2020-05-08 19:15:58 +00:00
|
|
|
ComponentClass []byte `asn1:"optional"`
|
2019-10-28 06:12:15 +00:00
|
|
|
ComponentManufacturer string
|
|
|
|
ComponentModel string
|
2020-11-12 23:31:24 +00:00
|
|
|
ComponentSerial string `asn1:"optional,utf8,tag:0"`
|
|
|
|
ComponentRevision string `asn1:"optional,utf8,tag:1"`
|
2020-05-08 19:52:40 +00:00
|
|
|
ComponentManufacturerID int `asn1:"optional,tag:2"`
|
2020-05-08 19:15:58 +00:00
|
|
|
FieldReplaceable bool `asn1:"optional,tag:3"`
|
|
|
|
ComponentAddresses []ComponentAddress `asn1:"optional,tag:4"`
|
2019-10-28 06:12:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type PlatformConfigurationV1 struct {
|
2020-05-08 19:15:58 +00:00
|
|
|
ComponentIdentifiers []ComponentIdentifierV1 `asn1:"optional,tag:0"`
|
|
|
|
PlatformProperties []Property `asn1:"optional,tag:1"`
|
|
|
|
PlatformPropertiesURI URIReference `asn1:"optional,tag:2"`
|
2019-10-28 06:12:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2019-11-06 10:04:38 +00:00
|
|
|
case attribute.ID.Equal(oidAttributeUserNotice):
|
2019-10-28 06:12:15 +00:00
|
|
|
if _, err := asn1.Unmarshal(attribute.RawValues[0].FullBytes, &out.UserNotice); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-07-23 05:32:32 +00:00
|
|
|
case attribute.ID.Equal(oid.TCGPlatformSpecification):
|
2019-10-28 06:12:15 +00:00
|
|
|
if _, err := asn1.Unmarshal(attribute.RawValues[0].FullBytes, &out.TCGPlatformSpecification); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-07-23 05:32:32 +00:00
|
|
|
case attribute.ID.Equal(oid.TBBSecurityAssertions):
|
2019-10-28 06:12:15 +00:00
|
|
|
if _, err := asn1.Unmarshal(attribute.RawValues[0].FullBytes, &out.TBBSecurityAssertions); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-07-23 05:32:32 +00:00
|
|
|
case attribute.ID.Equal(oid.TCGCredentialSpecification):
|
2019-10-28 06:12:15 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2020-07-23 05:32:32 +00:00
|
|
|
case attribute.ID.Equal(oid.TCGCredentialType):
|
2019-10-28 06:12:15 +00:00
|
|
|
var credentialType TCGCredentialType
|
|
|
|
if _, err := asn1.Unmarshal(attribute.RawValues[0].FullBytes, &credentialType); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-07-23 05:32:32 +00:00
|
|
|
case attribute.ID.Equal(oid.PlatformConfigurationV1):
|
2019-10-28 06:12:15 +00:00
|
|
|
var platformConfiguration PlatformConfigurationV1
|
|
|
|
if _, err := asn1.Unmarshal(attribute.RawValues[0].FullBytes, &platformConfiguration); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-05-08 19:52:40 +00:00
|
|
|
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
|
2020-07-23 05:32:32 +00:00
|
|
|
case attribute.ID.Equal(oid.PlatformConfigurationV2):
|
2019-10-28 06:12:15 +00:00
|
|
|
var platformConfiguration PlatformConfigurationV2
|
|
|
|
if _, err := asn1.Unmarshal(attribute.RawValues[0].FullBytes, &platformConfiguration); err != nil {
|
2020-05-30 00:24:06 +00:00
|
|
|
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
|
2019-10-28 06:12:15 +00:00
|
|
|
}
|
2020-05-08 19:52:40 +00:00
|
|
|
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
|
2020-07-23 05:32:32 +00:00
|
|
|
case attribute.ID.Equal(oid.PlatformConfigURI):
|
2019-11-06 10:04:38 +00:00
|
|
|
var platformConfigurationURI URIReference
|
|
|
|
if _, err := asn1.Unmarshal(attribute.RawValues[0].FullBytes, &platformConfigurationURI); err != nil {
|
2019-10-28 06:12:15 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
default:
|
2019-11-06 10:04:38 +00:00
|
|
|
return nil, fmt.Errorf("attributecert: unknown attribute %v", attribute.ID)
|
2019-10-28 06:12:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, extension := range in.TBSAttributeCertificate.Extensions {
|
|
|
|
switch {
|
2020-07-23 05:32:32 +00:00
|
|
|
case extension.Id.Equal(oid.SubjectAltName):
|
2019-10-28 06:12:15 +00:00
|
|
|
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)
|
2020-07-23 05:32:32 +00:00
|
|
|
case e.Type.Equal(oid.TCGCredentialSpecification):
|
2019-10-28 06:12:15 +00:00
|
|
|
// This OID appears to be misused in this context
|
|
|
|
out.PlatformSerial = e.Value.(string)
|
2020-07-23 05:32:32 +00:00
|
|
|
case e.Type.Equal(oid.PlatformManufacturerStr):
|
2019-10-28 06:12:15 +00:00
|
|
|
out.PlatformManufacturer = e.Value.(string)
|
2020-07-23 05:32:32 +00:00
|
|
|
case e.Type.Equal(oid.PlatformManufacturerID):
|
2019-10-28 06:12:15 +00:00
|
|
|
// We can't parse these out at present
|
|
|
|
break
|
2020-07-23 05:32:32 +00:00
|
|
|
case e.Type.Equal(oid.PlatformModel):
|
2019-10-28 06:12:15 +00:00
|
|
|
out.PlatformModel = e.Value.(string)
|
2020-07-23 05:32:32 +00:00
|
|
|
case e.Type.Equal(oid.PlatformVersion):
|
2019-10-28 06:12:15 +00:00
|
|
|
out.PlatformVersion = e.Value.(string)
|
2020-07-23 05:32:32 +00:00
|
|
|
case e.Type.Equal(oid.PlatformSerial):
|
2019-10-28 06:12:15 +00:00
|
|
|
out.PlatformSerial = e.Value.(string)
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("attributecert: unhandled attribute: %v", e.Type)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-23 05:32:32 +00:00
|
|
|
case extension.Id.Equal(oid.SubjectDirectoryAttributes):
|
2019-10-28 06:12:15 +00:00
|
|
|
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 {
|
2020-07-23 05:32:32 +00:00
|
|
|
case e.ID.Equal(oid.TCGPlatformSpecification):
|
2019-10-28 06:12:15 +00:00
|
|
|
var platformSpecification TCGPlatformSpecification
|
|
|
|
_, err := asn1.Unmarshal(e.Data.Bytes, &platformSpecification)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
out.TCGPlatformSpecification = platformSpecification
|
2020-07-23 05:32:32 +00:00
|
|
|
case e.ID.Equal(oid.TBBSecurityAssertions):
|
2020-05-08 19:15:58 +00:00
|
|
|
var securityAssertions TBBSecurityAssertions_sda
|
2019-10-28 06:12:15 +00:00
|
|
|
_, err := asn1.Unmarshal(e.Data.Bytes, &securityAssertions)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-05-08 19:15:58 +00:00
|
|
|
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
|
2019-10-28 06:12:15 +00:00
|
|
|
default:
|
2019-11-06 10:04:38 +00:00
|
|
|
return nil, fmt.Errorf("attributecert: unhandled TCG directory attribute: %v", e.ID)
|
2019-10-28 06:12:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-23 05:32:32 +00:00
|
|
|
case extension.Id.Equal(oid.CertificatePolicies):
|
2019-10-28 06:12:15 +00:00
|
|
|
var policies []policyInformation
|
|
|
|
_, err := asn1.Unmarshal(extension.Value, &policies)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, policy := range policies {
|
2019-11-06 10:04:38 +00:00
|
|
|
if policy.ID.Equal(oidTcgCertificatePolicy) {
|
2019-10-28 06:12:15 +00:00
|
|
|
var subpolicies []policyInformation
|
|
|
|
_, err := asn1.Unmarshal(policy.Policy.FullBytes, &subpolicies)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, subpolicy := range subpolicies {
|
|
|
|
switch {
|
2019-11-06 10:04:38 +00:00
|
|
|
case subpolicy.ID.Equal(oidCpsCertificatePolicy):
|
2019-10-28 06:12:15 +00:00
|
|
|
var cpsPolicy cpsPolicy
|
|
|
|
_, err := asn1.Unmarshal(subpolicy.Raw, &cpsPolicy)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-11-06 10:04:38 +00:00
|
|
|
case subpolicy.ID.Equal(oidAttributeUserNotice):
|
2019-10-28 06:12:15 +00:00
|
|
|
var userNotice string
|
|
|
|
_, err := asn1.Unmarshal(subpolicy.Policy.Bytes, &userNotice)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
default:
|
2019-11-06 10:04:38 +00:00
|
|
|
return nil, fmt.Errorf("attributecert: unhandled certificate policy: %v", subpolicy.ID)
|
2019-10-28 06:12:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case extension.Id.Equal(oidExtensionAuthorityKeyIdentifier):
|
2019-11-06 10:04:38 +00:00
|
|
|
var a authKeyID
|
2019-10-28 06:12:15 +00:00
|
|
|
_, 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)
|
|
|
|
}
|