smallstep-certificates/acme/errors.go
David Cowden a26b5f322d acme/api: Brush up documentation on key-change
Add more specific wording describing what a 501 means and add more color
explaining how official vs unofficial error types should be handled.
2020-05-28 11:22:37 -07:00

482 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package acme
import (
"github.com/pkg/errors"
)
// AccountDoesNotExistErr returns a new acme error.
func AccountDoesNotExistErr(err error) *Error {
return &Error{
Type: accountDoesNotExistErr,
Detail: "Account does not exist",
Status: 400,
Err: err,
}
}
// AlreadyRevokedErr returns a new acme error.
func AlreadyRevokedErr(err error) *Error {
return &Error{
Type: alreadyRevokedErr,
Detail: "Certificate already revoked",
Status: 400,
Err: err,
}
}
// BadCSRErr returns a new acme error.
func BadCSRErr(err error) *Error {
return &Error{
Type: badCSRErr,
Detail: "The CSR is unacceptable",
Status: 400,
Err: err,
}
}
// BadNonceErr returns a new acme error.
func BadNonceErr(err error) *Error {
return &Error{
Type: badNonceErr,
Detail: "Unacceptable anti-replay nonce",
Status: 400,
Err: err,
}
}
// BadPublicKeyErr returns a new acme error.
func BadPublicKeyErr(err error) *Error {
return &Error{
Type: badPublicKeyErr,
Detail: "The jws was signed by a public key the server does not support",
Status: 400,
Err: err,
}
}
// BadRevocationReasonErr returns a new acme error.
func BadRevocationReasonErr(err error) *Error {
return &Error{
Type: badRevocationReasonErr,
Detail: "The revocation reason provided is not allowed by the server",
Status: 400,
Err: err,
}
}
// BadSignatureAlgorithmErr returns a new acme error.
func BadSignatureAlgorithmErr(err error) *Error {
return &Error{
Type: badSignatureAlgorithmErr,
Detail: "The JWS was signed with an algorithm the server does not support",
Status: 400,
Err: err,
}
}
// CaaErr returns a new acme error.
func CaaErr(err error) *Error {
return &Error{
Type: caaErr,
Detail: "Certification Authority Authorization (CAA) records forbid the CA from issuing a certificate",
Status: 400,
Err: err,
}
}
// CompoundErr returns a new acme error.
func CompoundErr(err error) *Error {
return &Error{
Type: compoundErr,
Detail: "Specific error conditions are indicated in the “subproblems” array",
Status: 400,
Err: err,
}
}
// ConnectionErr returns a new acme error.
func ConnectionErr(err error) *Error {
return &Error{
Type: connectionErr,
Detail: "The server could not connect to validation target",
Status: 400,
Err: err,
}
}
// DNSErr returns a new acme error.
func DNSErr(err error) *Error {
return &Error{
Type: dnsErr,
Detail: "There was a problem with a DNS query during identifier validation",
Status: 400,
Err: err,
}
}
// ExternalAccountRequiredErr returns a new acme error.
func ExternalAccountRequiredErr(err error) *Error {
return &Error{
Type: externalAccountRequiredErr,
Detail: "The request must include a value for the \"externalAccountBinding\" field",
Status: 400,
Err: err,
}
}
// IncorrectResponseErr returns a new acme error.
func IncorrectResponseErr(err error) *Error {
return &Error{
Type: incorrectResponseErr,
Detail: "Response received didn't match the challenge's requirements",
Status: 400,
Err: err,
}
}
// InvalidContactErr returns a new acme error.
func InvalidContactErr(err error) *Error {
return &Error{
Type: invalidContactErr,
Detail: "A contact URL for an account was invalid",
Status: 400,
Err: err,
}
}
// MalformedErr returns a new acme error.
func MalformedErr(err error) *Error {
return &Error{
Type: malformedErr,
Detail: "The request message was malformed",
Status: 400,
Err: err,
}
}
// OrderNotReadyErr returns a new acme error.
func OrderNotReadyErr(err error) *Error {
return &Error{
Type: orderNotReadyErr,
Detail: "The request attempted to finalize an order that is not ready to be finalized",
Status: 400,
Err: err,
}
}
// RateLimitedErr returns a new acme error.
func RateLimitedErr(err error) *Error {
return &Error{
Type: rateLimitedErr,
Detail: "The request exceeds a rate limit",
Status: 400,
Err: err,
}
}
// RejectedIdentifierErr returns a new acme error.
func RejectedIdentifierErr(err error) *Error {
return &Error{
Type: rejectedIdentifierErr,
Detail: "The server will not issue certificates for the identifier",
Status: 400,
Err: err,
}
}
// ServerInternalErr returns a new acme error.
func ServerInternalErr(err error) *Error {
return &Error{
Type: serverInternalErr,
Detail: "The server experienced an internal error",
Status: 500,
Err: err,
}
}
// NotImplemented returns a new acme error.
func NotImplemented(err error) *Error {
return &Error{
Type: notImplemented,
Detail: "The requested operation is not implemented",
Status: 501,
Err: err,
}
}
// TLSErr returns a new acme error.
func TLSErr(err error) *Error {
return &Error{
Type: tlsErr,
Detail: "The server received a TLS error during validation",
Status: 400,
Err: err,
}
}
// UnauthorizedErr returns a new acme error.
func UnauthorizedErr(err error) *Error {
return &Error{
Type: unauthorizedErr,
Detail: "The client lacks sufficient authorization",
Status: 401,
Err: err,
}
}
// UnsupportedContactErr returns a new acme error.
func UnsupportedContactErr(err error) *Error {
return &Error{
Type: unsupportedContactErr,
Detail: "A contact URL for an account used an unsupported protocol scheme",
Status: 400,
Err: err,
}
}
// UnsupportedIdentifierErr returns a new acme error.
func UnsupportedIdentifierErr(err error) *Error {
return &Error{
Type: unsupportedIdentifierErr,
Detail: "An identifier is of an unsupported type",
Status: 400,
Err: err,
}
}
// UserActionRequiredErr returns a new acme error.
func UserActionRequiredErr(err error) *Error {
return &Error{
Type: userActionRequiredErr,
Detail: "Visit the “instance” URL and take actions specified there",
Status: 400,
Err: err,
}
}
// ProbType is the type of the ACME problem.
type ProbType int
const (
// The request specified an account that does not exist
accountDoesNotExistErr ProbType = iota
// The request specified a certificate to be revoked that has already been revoked
alreadyRevokedErr
// The CSR is unacceptable (e.g., due to a short key)
badCSRErr
// The client sent an unacceptable anti-replay nonce
badNonceErr
// The JWS was signed by a public key the server does not support
badPublicKeyErr
// The revocation reason provided is not allowed by the server
badRevocationReasonErr
// The JWS was signed with an algorithm the server does not support
badSignatureAlgorithmErr
// Certification Authority Authorization (CAA) records forbid the CA from issuing a certificate
caaErr
// Specific error conditions are indicated in the “subproblems” array.
compoundErr
// The server could not connect to validation target
connectionErr
// There was a problem with a DNS query during identifier validation
dnsErr
// The request must include a value for the “externalAccountBinding” field
externalAccountRequiredErr
// Response received didnt match the challenges requirements
incorrectResponseErr
// A contact URL for an account was invalid
invalidContactErr
// The request message was malformed
malformedErr
// The request attempted to finalize an order that is not ready to be finalized
orderNotReadyErr
// The request exceeds a rate limit
rateLimitedErr
// The server will not issue certificates for the identifier
rejectedIdentifierErr
// The server experienced an internal error
serverInternalErr
// The server received a TLS error during validation
tlsErr
// The client lacks sufficient authorization
unauthorizedErr
// A contact URL for an account used an unsupported protocol scheme
unsupportedContactErr
// An identifier is of an unsupported type
unsupportedIdentifierErr
// Visit the “instance” URL and take actions specified there
userActionRequiredErr
// The operation is not implemented
notImplemented
)
// String returns the string representation of the acme problem type,
// fulfilling the Stringer interface.
func (ap ProbType) String() string {
switch ap {
case accountDoesNotExistErr:
return "accountDoesNotExist"
case alreadyRevokedErr:
return "alreadyRevoked"
case badCSRErr:
return "badCSR"
case badNonceErr:
return "badNonce"
case badPublicKeyErr:
return "badPublicKey"
case badRevocationReasonErr:
return "badRevocationReason"
case badSignatureAlgorithmErr:
return "badSignatureAlgorithm"
case caaErr:
return "caa"
case compoundErr:
return "compound"
case connectionErr:
return "connection"
case dnsErr:
return "dns"
case externalAccountRequiredErr:
return "externalAccountRequired"
case incorrectResponseErr:
return "incorrectResponse"
case invalidContactErr:
return "invalidContact"
case malformedErr:
return "malformed"
case orderNotReadyErr:
return "orderNotReady"
case rateLimitedErr:
return "rateLimited"
case rejectedIdentifierErr:
return "rejectedIdentifier"
case serverInternalErr:
return "serverInternal"
case tlsErr:
return "tls"
case unauthorizedErr:
return "unauthorized"
case unsupportedContactErr:
return "unsupportedContact"
case unsupportedIdentifierErr:
return "unsupportedIdentifier"
case userActionRequiredErr:
return "userActionRequired"
case notImplemented:
return "notImplemented"
default:
return "unsupported type"
}
}
// Error is an ACME error type complete with problem document.
type Error struct {
Type ProbType
Detail string
Err error
Status int
Sub []*Error
Identifier *Identifier
}
// Wrap attempts to wrap the internal error.
func Wrap(err error, wrap string) *Error {
switch e := err.(type) {
case nil:
return nil
case *Error:
if e.Err == nil {
e.Err = errors.New(wrap + "; " + e.Detail)
} else {
e.Err = errors.Wrap(e.Err, wrap)
}
return e
default:
return ServerInternalErr(errors.Wrap(err, wrap))
}
}
// Error implements the error interface.
func (e *Error) Error() string {
if e.Err == nil {
return e.Detail
}
return e.Err.Error()
}
// Cause returns the internal error and implements the Causer interface.
func (e *Error) Cause() error {
if e.Err == nil {
return errors.New(e.Detail)
}
return e.Err
}
// Official returns true if this error's type is listed in §6.7 of RFC 8555.
// Error types in §6.7 are registered under IETF urn namespace:
//
// "urn:ietf:params:acme:error:"
//
// and should include the namespace as a prefix when appearing as a problem
// document.
//
// RFC 8555 also says:
//
// This list is not exhaustive. The server MAY return errors whose
// "type" field is set to a URI other than those defined above. Servers
// MUST NOT use the ACME URN namespace for errors not listed in the
// appropriate IANA registry (see Section 9.6). Clients SHOULD display
// the "detail" field of all errors.
//
// In this case Official returns `false` so that a different namespace can
// be used.
func (e *Error) Official() bool {
return e.Type != notImplemented
}
// ToACME returns an acme representation of the problem type.
// For official errors, the IETF ACME namespace is prepended to the error type.
// For our own errors, we use an (yet) unregistered smallstep acme namespace.
func (e *Error) ToACME() *AError {
prefix := "urn:step:acme:error"
if e.Official() {
prefix = "urn:ietf:params:acme:error:"
}
ae := &AError{
Type: prefix + e.Type.String(),
Detail: e.Error(),
Status: e.Status,
}
if e.Identifier != nil {
ae.Identifier = *e.Identifier
}
for _, p := range e.Sub {
ae.Subproblems = append(ae.Subproblems, p.ToACME())
}
return ae
}
// StatusCode returns the status code and implements the StatusCode interface.
func (e *Error) StatusCode() int {
return e.Status
}
// AError is the error type as seen in acme request/responses.
type AError struct {
Type string `json:"type"`
Detail string `json:"detail"`
Identifier interface{} `json:"identifier,omitempty"`
Subproblems []interface{} `json:"subproblems,omitempty"`
Status int `json:"-"`
}
// Error allows AError to implement the error interface.
func (ae *AError) Error() string {
return ae.Detail
}
// StatusCode returns the status code and implements the StatusCode interface.
func (ae *AError) StatusCode() int {
return ae.Status
}