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 didn’t match the challenge’s 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 }