forked from TrueCloudLab/certificates
iOS 16 beta 1 support
This commit is contained in:
parent
77c6d10fd6
commit
7e1b0bebd9
4 changed files with 82 additions and 1 deletions
|
@ -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) {
|
if !(id.Type == acme.DNS || id.Type == acme.IP || id.Type == acme.PermanentIdentifier || id.Type == acme.CA) {
|
||||||
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,6 +375,8 @@ 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{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,11 +13,13 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -41,6 +43,7 @@ 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.
|
||||||
|
@ -84,6 +87,8 @@ 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)
|
||||||
}
|
}
|
||||||
|
@ -416,6 +421,68 @@ func deviceAttest01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose
|
||||||
return nil
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(os.Stderr, "p.AttObj: %v\n", p.AttObj)
|
||||||
|
|
||||||
|
attObj, err := base64.RawURLEncoding.DecodeString(p.AttObj)
|
||||||
|
if err != nil {
|
||||||
|
return WrapErrorISE(err, "error base64 decoding attObj")
|
||||||
|
}
|
||||||
|
|
||||||
|
att := AttestationObject{}
|
||||||
|
if err := cbor.Unmarshal(attObj, &att); err != nil {
|
||||||
|
return WrapErrorISE(err, "error unmarshalling CBOR")
|
||||||
|
}
|
||||||
|
|
||||||
|
if att.Format != "apple" {
|
||||||
|
return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatement,
|
||||||
|
"unexpected attestation object format"))
|
||||||
|
}
|
||||||
|
|
||||||
|
x5c, x509present := att.AttStatement["x5c"].([]interface{})
|
||||||
|
if !x509present {
|
||||||
|
return storeError(ctx, db, ch, true, NewError(ErrorBadAttestationStatement,
|
||||||
|
"x5c not present"))
|
||||||
|
}
|
||||||
|
|
||||||
|
attCertBytes, valid := x5c[0].([]byte)
|
||||||
|
if !valid {
|
||||||
|
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
|
||||||
|
"error getting certificate from x5c cert chain"))
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
|
|
|
@ -19,6 +19,8 @@ const (
|
||||||
ErrorAccountDoesNotExistType ProblemType = iota
|
ErrorAccountDoesNotExistType ProblemType = iota
|
||||||
// ErrorAlreadyRevokedType request specified a certificate to be revoked that has already been revoked
|
// ErrorAlreadyRevokedType request specified a certificate to be revoked that has already been revoked
|
||||||
ErrorAlreadyRevokedType
|
ErrorAlreadyRevokedType
|
||||||
|
// ErrorBadAttestation WebAuthn attestation statement could not be verified
|
||||||
|
ErrorBadAttestationStatement
|
||||||
// ErrorBadCSRType CSR is unacceptable (e.g., due to a short key)
|
// ErrorBadCSRType CSR is unacceptable (e.g., due to a short key)
|
||||||
ErrorBadCSRType
|
ErrorBadCSRType
|
||||||
// ErrorBadNonceType client sent an unacceptable anti-replay nonce
|
// ErrorBadNonceType client sent an unacceptable anti-replay nonce
|
||||||
|
|
|
@ -5,7 +5,9 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -25,6 +27,7 @@ const (
|
||||||
DNS IdentifierType = "dns"
|
DNS IdentifierType = "dns"
|
||||||
// DNS is the ACME dns identifier type
|
// DNS is the ACME dns identifier type
|
||||||
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.
|
||||||
|
@ -146,6 +149,12 @@ 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)
|
||||||
|
|
||||||
|
@ -229,6 +238,7 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue