Add attestation certificate validation for Apple devices

This commit is contained in:
Mariano Cano 2022-07-14 17:10:03 -07:00
parent 9b9c5551f6
commit 66356cff43
4 changed files with 164 additions and 204 deletions

View file

@ -33,7 +33,7 @@ func (n *NewOrderRequest) Validate() error {
return acme.NewError(acme.ErrorMalformedType, "identifiers list cannot be empty") return acme.NewError(acme.ErrorMalformedType, "identifiers list cannot be empty")
} }
for _, id := range n.Identifiers { for _, id := range n.Identifiers {
if !(id.Type == acme.DNS || id.Type == acme.IP || id.Type == acme.PermanentIdentifier || id.Type == acme.CA) { if !(id.Type == acme.DNS || id.Type == acme.IP || id.Type == acme.PermanentIdentifier) {
return acme.NewError(acme.ErrorMalformedType, "identifier type unsupported: %s", id.Type) return acme.NewError(acme.ErrorMalformedType, "identifier type unsupported: %s", id.Type)
} }
if id.Type == acme.IP && net.ParseIP(id.Value) == nil { if id.Type == acme.IP && net.ParseIP(id.Value) == nil {
@ -375,8 +375,6 @@ func challengeTypes(az *acme.Authorization) []acme.ChallengeType {
} }
case acme.PermanentIdentifier: case acme.PermanentIdentifier:
chTypes = []acme.ChallengeType{acme.DEVICEATTEST01} chTypes = []acme.ChallengeType{acme.DEVICEATTEST01}
case acme.CA:
chTypes = []acme.ChallengeType{acme.APPLEATTEST01}
default: default:
chTypes = []acme.ChallengeType{} chTypes = []acme.ChallengeType{}
} }

View file

@ -1,14 +1,12 @@
package acme package acme
import ( import (
"bytes"
"context" "context"
"crypto" "crypto"
"crypto/sha256" "crypto/sha256"
"crypto/subtle" "crypto/subtle"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix"
"encoding/asn1" "encoding/asn1"
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
@ -25,11 +23,8 @@ import (
"time" "time"
"github.com/fxamacker/cbor/v2" "github.com/fxamacker/cbor/v2"
"github.com/google/go-attestation/attest"
"github.com/google/go-attestation/oid"
x509ext "github.com/google/go-attestation/x509"
"github.com/google/go-tpm/tpm2"
"go.step.sm/crypto/jose" "go.step.sm/crypto/jose"
"go.step.sm/crypto/pemutil"
) )
type ChallengeType string type ChallengeType string
@ -43,7 +38,6 @@ const (
TLSALPN01 ChallengeType = "tls-alpn-01" TLSALPN01 ChallengeType = "tls-alpn-01"
// DEVICEATTEST01 is the device-attest-01 ACME challenge type // DEVICEATTEST01 is the device-attest-01 ACME challenge type
DEVICEATTEST01 ChallengeType = "device-attest-01" DEVICEATTEST01 ChallengeType = "device-attest-01"
APPLEATTEST01 ChallengeType = "client-01"
) )
// Challenge represents an ACME response Challenge type. // Challenge represents an ACME response Challenge type.
@ -87,8 +81,6 @@ func (ch *Challenge) Validate(ctx context.Context, db DB, jwk *jose.JSONWebKey,
return tlsalpn01Validate(ctx, ch, db, jwk) return tlsalpn01Validate(ctx, ch, db, jwk)
case DEVICEATTEST01: case DEVICEATTEST01:
return deviceAttest01Validate(ctx, ch, db, jwk, payload) return deviceAttest01Validate(ctx, ch, db, jwk, payload)
case APPLEATTEST01:
return appleAttest01Validate(ctx, ch, db, jwk, payload)
default: default:
return NewErrorISE("unexpected challenge type '%s'", ch.Type) return NewErrorISE("unexpected challenge type '%s'", ch.Type)
} }
@ -315,7 +307,8 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK
} }
type Payload struct { type Payload struct {
AttStmt []byte `json:"attStmt"` AttObj string `json:"attObj"`
Error string `json:"error"`
} }
type AttestationObject struct { type AttestationObject struct {
@ -326,121 +319,11 @@ type AttestationObject struct {
// TODO(bweeks): move attestation verification to a shared package. // TODO(bweeks): move attestation verification to a shared package.
// TODO(bweeks): define new error type for failed attestation validation. // TODO(bweeks): define new error type for failed attestation validation.
func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, payload []byte) error { func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, payload []byte) error {
// TODO(bweeks): investigate if the iOS implementation allows for proper
// platform detection.
{
var p ApplePayload
if err := json.Unmarshal(payload, &p); err == nil {
return appleAttest01Validate(ctx, ch, db, jwk, payload)
}
}
var p Payload var p Payload
if err := json.Unmarshal(payload, &p); err != nil { if err := json.Unmarshal(payload, &p); err != nil {
return WrapErrorISE(err, "error unmarshalling JSON") return WrapErrorISE(err, "error unmarshalling JSON")
} }
att := AttestationObject{}
if err := cbor.Unmarshal(p.AttStmt, &att); err != nil {
return WrapErrorISE(err, "error unmarshalling CBOR")
}
// TODO(bweeks): move verification code to a shared package.
// begin TPM key certification verification
params := &attest.CertificationParameters{
Public: att.AttStatement["pubArea"].([]byte),
CreateAttestation: att.AttStatement["certInfo"].([]byte),
CreateSignature: att.AttStatement["sig"].([]byte),
}
// end TPM key certification verification
x5c, x509present := att.AttStatement["x5c"].([]interface{})
if !x509present {
return errors.New("x5c not present")
}
akCertBytes, valid := x5c[0].([]byte)
if !valid {
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
"error getting certificate from x5c cert chain"))
}
akCert, err := x509.ParseCertificate(akCertBytes)
if err != nil {
return WrapErrorISE(err, "error parsing AK certificate")
}
if err := params.Verify(attest.VerifyOpts{Public: akCert.PublicKey, Hash: crypto.SHA256}); err != nil {
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
"params.Verify failed: %v", err))
}
attData, err := tpm2.DecodeAttestationData(params.CreateAttestation)
if err != nil {
return WrapErrorISE(err, "error decoding attestation data")
}
keyAuth, err := KeyAuthorization(ch.Token, jwk)
if err != nil {
return err
}
hashedKeyAuth := sha256.Sum256([]byte(keyAuth))
if !bytes.Equal(attData.ExtraData, hashedKeyAuth[:]) {
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
"key authorization mismatch"))
}
var sanExt pkix.Extension
for _, ext := range akCert.Extensions {
if ext.Id.Equal(oid.SubjectAltName) {
sanExt = ext
}
}
if sanExt.Value == nil {
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
"akCert missing subjectAltName"))
}
san, err := x509ext.ParseSubjectAltName(sanExt)
if err != nil {
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
"failed to parse subjectAltName"))
}
if len(san.PermanentIdentifiers) != 1 {
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
"subjectAltName doesn't contain a PermanentIdentifier"))
}
wantPermID := san.PermanentIdentifiers[0]
if wantPermID.IdentifierValue != ch.Value {
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
"identifier from certificate and challenge do not match"))
}
// Update and store the challenge.
ch.Status = StatusValid
ch.Error = nil
ch.ValidatedAt = clock.Now().Format(time.RFC3339)
if err := db.UpdateChallenge(ctx, ch); err != nil {
return WrapErrorISE(err, "error updating challenge")
}
return nil
}
type ApplePayload struct {
AttObj string `json:"attObj"`
Error string `json:"error"`
}
func appleAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, payload []byte) error {
var p ApplePayload
if err := json.Unmarshal(payload, &p); err != nil {
return WrapErrorISE(err, "error unmarshalling JSON")
}
if p.Error != "" { if p.Error != "" {
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
"payload contained error: %v", p.Error)) "payload contained error: %v", p.Error))
@ -456,39 +339,39 @@ func appleAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.
return WrapErrorISE(err, "error unmarshalling CBOR") return WrapErrorISE(err, "error unmarshalling CBOR")
} }
if att.Format != "apple" { switch att.Format {
return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatement, case "apple":
"unexpected attestation object format")) data, err := doAppleAttestationFormat(ctx, ch, db, &att)
} if err != nil {
return err
}
x5c, x509present := att.AttStatement["x5c"].([]interface{}) // Validate nonce with SHA-256 of the token
if !x509present { //
return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatement, // TODO(mariano): validate this
"x5c not present")) if data.Nonce != "" {
} sum := sha256.Sum256([]byte(ch.Token))
if data.Nonce != hex.EncodeToString(sum[:]) {
return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatement, "challenge token does not match"))
}
}
if len(x5c) == 0 { // Validate Apple's ClientIdentifier (Identifier.Value) with device
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, // identifiers.
"x5c is empty")) //
} // Note: We might want to use an external service for this.
if data.UDID != ch.Value && data.SerialNumber != ch.Value {
return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatement, "permanent identifier does not match"))
}
attCertBytes, valid := x5c[0].([]byte) // TODO(mariano): debug - remove me
if !valid { pem.Encode(os.Stderr, &pem.Block{
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, Type: "CERTIFICATE", Bytes: data.Certificate.Raw,
"error getting certificate from x5c cert chain")) })
default:
return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatement, "unexpected attestation object format"))
} }
attCert, err := x509.ParseCertificate(attCertBytes)
if err != nil {
return WrapErrorISE(err, "error parsing AK certificate")
}
b := &pem.Block{
Type: "CERTIFICATE",
Bytes: attCert.Raw,
}
pem.Encode(os.Stderr, b)
// Update and store the challenge. // Update and store the challenge.
ch.Status = StatusValid ch.Status = StatusValid
ch.Error = nil ch.Error = nil
@ -500,6 +383,104 @@ func appleAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.
return nil return nil
} }
// Apple Enterprise Attestation Root CA from
// https://www.apple.com/certificateauthority/private/
const appleEnterpriseAttestationRootCA = `-----BEGIN CERTIFICATE-----
MIICJDCCAamgAwIBAgIUQsDCuyxyfFxeq/bxpm8frF15hzcwCgYIKoZIzj0EAwMw
UTEtMCsGA1UEAwwkQXBwbGUgRW50ZXJwcmlzZSBBdHRlc3RhdGlvbiBSb290IENB
MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzAeFw0yMjAyMTYxOTAx
MjRaFw00NzAyMjAwMDAwMDBaMFExLTArBgNVBAMMJEFwcGxlIEVudGVycHJpc2Ug
QXR0ZXN0YXRpb24gUm9vdCBDQTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UE
BhMCVVMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT6Jigq+Ps9Q4CoT8t8q+UnOe2p
oT9nRaUfGhBTbgvqSGXPjVkbYlIWYO+1zPk2Sz9hQ5ozzmLrPmTBgEWRcHjA2/y7
7GEicps9wn2tj+G89l3INNDKETdxSPPIZpPj8VmjQjBAMA8GA1UdEwEB/wQFMAMB
Af8wHQYDVR0OBBYEFPNqTQGd8muBpV5du+UIbVbi+d66MA4GA1UdDwEB/wQEAwIB
BjAKBggqhkjOPQQDAwNpADBmAjEA1xpWmTLSpr1VH4f8Ypk8f3jMUKYz4QPG8mL5
8m9sX/b2+eXpTv2pH4RZgJjucnbcAjEA4ZSB6S45FlPuS/u4pTnzoz632rA+xW/T
ZwFEh9bhKjJ+5VQ9/Do1os0u3LEkgN/r
-----END CERTIFICATE-----`
var (
oidAppleSerialNumber = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 8, 9, 1}
oidAppleUniqueDeviceIdentifier = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 8, 9, 2}
oidAppleSecureEnclaveProcessorOSVersion = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 8, 10, 2}
oidAppleNonce = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 8, 11, 1}
)
type appleAttestationData struct {
Nonce string
SerialNumber string
UDID string
SEPVersion string
Certificate *x509.Certificate
}
func doAppleAttestationFormat(ctx context.Context, ch *Challenge, db DB, att *AttestationObject) (*appleAttestationData, error) {
root, err := pemutil.ParseCertificate([]byte(appleEnterpriseAttestationRootCA))
if err != nil {
return nil, WrapErrorISE(err, "error parsing apple enterprise ca")
}
roots := x509.NewCertPool()
roots.AddCert(root)
x5c, ok := att.AttStatement["x5c"].([]interface{})
if !ok {
return nil, storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatement, "x5c not present"))
}
if len(x5c) == 0 {
return nil, storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, "x5c is empty"))
}
der, ok := x5c[0].([]byte)
if !ok {
return nil, storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatement, "x5c is malformed"))
}
leaf, err := x509.ParseCertificate(der)
if err != nil {
return nil, storeError(ctx, db, ch, true, WrapError(ErrorBadAttestationStatement, err, "x5c is malformed"))
}
intermediates := x509.NewCertPool()
for _, v := range x5c[1:] {
der, ok = v.([]byte)
if !ok {
return nil, storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatement, "x5c is malformed"))
}
cert, err := x509.ParseCertificate(der)
if err != nil {
return nil, storeError(ctx, db, ch, true, WrapError(ErrorBadAttestationStatement, err, "x5c is malformed"))
}
intermediates.AddCert(cert)
}
if _, err := leaf.Verify(x509.VerifyOptions{
Intermediates: intermediates,
Roots: roots,
CurrentTime: time.Now().Truncate(time.Second),
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
}); err != nil {
return nil, storeError(ctx, db, ch, true, WrapError(ErrorBadAttestationStatement, err, "x5c is not valid"))
}
data := &appleAttestationData{
Certificate: leaf,
}
for _, ext := range leaf.Extensions {
switch {
case ext.Id.Equal(oidAppleSerialNumber):
data.SerialNumber = string(ext.Value)
case ext.Id.Equal(oidAppleUniqueDeviceIdentifier):
data.UDID = string(ext.Value)
case ext.Id.Equal(oidAppleSecureEnclaveProcessorOSVersion):
data.SEPVersion = string(ext.Value)
case ext.Id.Equal(oidAppleNonce):
data.Nonce = string(ext.Value)
}
}
return data, nil
}
// serverName determines the SNI HostName to set based on an acme.Challenge // serverName determines the SNI HostName to set based on an acme.Challenge
// for TLS-ALPN-01 challenges RFC8738 states that, if HostName is an IP, it // for TLS-ALPN-01 challenges RFC8738 states that, if HostName is an IP, it
// should be the ARPA address https://datatracker.ietf.org/doc/html/rfc8738#section-6. // should be the ARPA address https://datatracker.ietf.org/doc/html/rfc8738#section-6.

View file

@ -4,8 +4,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"os"
"runtime/debug"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/api/render" "github.com/smallstep/certificates/api/render"
@ -277,8 +275,6 @@ type Error struct {
// NewError creates a new Error type. // NewError creates a new Error type.
func NewError(pt ProblemType, msg string, args ...interface{}) *Error { func NewError(pt ProblemType, msg string, args ...interface{}) *Error {
fmt.Fprintf(os.Stderr, msg+"\n", args...)
debug.PrintStack()
return newError(pt, errors.Errorf(msg, args...)) return newError(pt, errors.Errorf(msg, args...))
} }
@ -304,8 +300,6 @@ func newError(pt ProblemType, err error) *Error {
// NewErrorISE creates a new ErrorServerInternalType Error. // NewErrorISE creates a new ErrorServerInternalType Error.
func NewErrorISE(msg string, args ...interface{}) *Error { func NewErrorISE(msg string, args ...interface{}) *Error {
fmt.Fprintf(os.Stderr, msg+"\n", args...)
debug.PrintStack()
return NewError(ErrorServerInternalType, msg, args...) return NewError(ErrorServerInternalType, msg, args...)
} }
@ -328,8 +322,6 @@ func WrapError(typ ProblemType, err error, msg string, args ...interface{}) *Err
// WrapErrorISE shortcut to wrap an internal server error type. // WrapErrorISE shortcut to wrap an internal server error type.
func WrapErrorISE(err error, msg string, args ...interface{}) *Error { func WrapErrorISE(err error, msg string, args ...interface{}) *Error {
fmt.Fprintf(os.Stderr, msg+"\n", args...)
debug.PrintStack()
return WrapError(ErrorServerInternalType, err, msg, args...) return WrapError(ErrorServerInternalType, err, msg, args...)
} }

View file

@ -12,8 +12,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/google/go-attestation/oid"
attest_x509 "github.com/google/go-attestation/x509"
"github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/authority/provisioner"
"go.step.sm/crypto/x509util" "go.step.sm/crypto/x509util"
) )
@ -25,9 +23,9 @@ const (
IP IdentifierType = "ip" IP IdentifierType = "ip"
// DNS is the ACME dns identifier type // DNS is the ACME dns identifier type
DNS IdentifierType = "dns" DNS IdentifierType = "dns"
// DNS is the ACME dns identifier type // PermanentIdentifier is the ACME permanent-identifier identifier type
// defined in https://datatracker.ietf.org/doc/html/draft-bweeks-acme-device-attest-00
PermanentIdentifier IdentifierType = "permanent-identifier" PermanentIdentifier IdentifierType = "permanent-identifier"
CA IdentifierType = "ca"
) )
// Identifier encodes the type that an order pertains to. // Identifier encodes the type that an order pertains to.
@ -131,6 +129,11 @@ func (o *Order) UpdateStatus(ctx context.Context, db DB) error {
// Finalize signs a certificate if the necessary conditions for Order completion // Finalize signs a certificate if the necessary conditions for Order completion
// have been met. // have been met.
//
// TODO(mariano): Here or in the challenge validation we should perform some
// external validation using the identifier value and the attestation data. From
// a validation service we can get the list of SANs to set in the final
// certificate.
func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateRequest, auth CertificateAuthority, p Provisioner) error { func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateRequest, auth CertificateAuthority, p Provisioner) error {
if err := o.UpdateStatus(ctx, db); err != nil { if err := o.UpdateStatus(ctx, db); err != nil {
return err return err
@ -149,24 +152,33 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques
return NewErrorISE("unexpected status %s for order %s", o.Status, o.ID) return NewErrorISE("unexpected status %s for order %s", o.Status, o.ID)
} }
b := &pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: csr.Raw,
}
pem.Encode(os.Stderr, b)
// canonicalize the CSR to allow for comparison // canonicalize the CSR to allow for comparison
csr = canonicalize(csr) csr = canonicalize(csr)
// retrieve the requested SANs for the Order // Template data
sans, err := o.sans(csr) data := x509util.NewTemplateData()
if err != nil { data.SetCommonName(csr.Subject.CommonName)
return err
// TODO: support for multiple identifiers?
var permanentIdentifier string
for i := range o.Identifiers {
if o.Identifiers[i].Type == PermanentIdentifier {
permanentIdentifier = o.Identifiers[i].Value
break
}
} }
deviceIDs, err := o.deviceIDs(csr) if permanentIdentifier != "" {
if err != nil { data.SetPermanentIdentifiers([]x509util.PermanentIdentifier{
return err {Value: permanentIdentifier},
})
} else {
// retrieve the requested SANs for the Order
sans, err := o.sans(csr)
if err != nil {
return err
}
data.Set(x509util.SANsKey, sans)
} }
// Get authorizations from the ACME provisioner. // Get authorizations from the ACME provisioner.
@ -176,12 +188,6 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques
return WrapErrorISE(err, "error retrieving authorization options from ACME provisioner") return WrapErrorISE(err, "error retrieving authorization options from ACME provisioner")
} }
// Template data
data := x509util.NewTemplateData()
data.SetCommonName(csr.Subject.CommonName)
data.Set(x509util.SANsKey, sans)
data.SetPermanentIdentifiers(deviceIDs)
templateOptions, err := provisioner.TemplateOptions(p.GetOptions(), data) templateOptions, err := provisioner.TemplateOptions(p.GetOptions(), data)
if err != nil { if err != nil {
return WrapErrorISE(err, "error creating template options from ACME provisioner") return WrapErrorISE(err, "error creating template options from ACME provisioner")
@ -206,6 +212,11 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques
return WrapErrorISE(err, "error creating certificate for order %s", o.ID) return WrapErrorISE(err, "error creating certificate for order %s", o.ID)
} }
// TODO(mariano): debug - remove me
pem.Encode(os.Stderr, &pem.Block{
Type: "CERTIFICATE", Bytes: cert.Leaf.Raw,
})
o.CertificateID = cert.ID o.CertificateID = cert.ID
o.Status = StatusValid o.Status = StatusValid
if err = db.UpdateOrder(ctx, o); err != nil { if err = db.UpdateOrder(ctx, o); err != nil {
@ -215,9 +226,7 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques
} }
func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativeName, error) { func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativeName, error) {
var sans []x509util.SubjectAlternativeName var sans []x509util.SubjectAlternativeName
if len(csr.EmailAddresses) > 0 || len(csr.URIs) > 0 { if len(csr.EmailAddresses) > 0 || len(csr.URIs) > 0 {
return sans, NewError(ErrorBadCSRType, "Only DNS names and IP addresses are allowed") return sans, NewError(ErrorBadCSRType, "Only DNS names and IP addresses are allowed")
} }
@ -238,7 +247,6 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ
case PermanentIdentifier: case PermanentIdentifier:
orderPIDs[indexPID] = n.Value orderPIDs[indexPID] = n.Value
indexPID++ indexPID++
case CA:
default: default:
return sans, NewErrorISE("unsupported identifier type in order: %s", n.Type) return sans, NewErrorISE("unsupported identifier type in order: %s", n.Type)
} }
@ -292,25 +300,6 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ
return sans, nil return sans, nil
} }
func (o *Order) deviceIDs(csr *x509.CertificateRequest) ([]x509util.PermanentIdentifier, error) {
var permIDs []x509util.PermanentIdentifier
for _, ext := range csr.Extensions {
if ext.Id.Equal(oid.SubjectAltName) {
san, err := attest_x509.ParseSubjectAltName(ext)
if err != nil {
return nil, err
}
for _, pi := range san.PermanentIdentifiers {
permIDs = append(permIDs, x509util.PermanentIdentifier{
Value: pi.IdentifierValue,
Assigner: pi.Assigner,
})
}
}
}
return permIDs, nil
}
// numberOfIdentifierType returns the number of Identifiers that // numberOfIdentifierType returns the number of Identifiers that
// are of type typ. // are of type typ.
func numberOfIdentifierType(typ IdentifierType, ids []Identifier) int { func numberOfIdentifierType(typ IdentifierType, ids []Identifier) int {