package acme import ( "errors" "fmt" "net/http" ) // ACME server response statuses used to describe Authorization and Challenge states. const ( StatusUnknown = "unknown" StatusPending = "pending" StatusProcessing = "processing" StatusValid = "valid" StatusInvalid = "invalid" StatusRevoked = "revoked" ) // CRLReasonCode identifies the reason for a certificate revocation. type CRLReasonCode int // CRL reason codes as defined in RFC 5280. const ( CRLReasonUnspecified CRLReasonCode = 0 CRLReasonKeyCompromise CRLReasonCode = 1 CRLReasonCACompromise CRLReasonCode = 2 CRLReasonAffiliationChanged CRLReasonCode = 3 CRLReasonSuperseded CRLReasonCode = 4 CRLReasonCessationOfOperation CRLReasonCode = 5 CRLReasonCertificateHold CRLReasonCode = 6 CRLReasonRemoveFromCRL CRLReasonCode = 8 CRLReasonPrivilegeWithdrawn CRLReasonCode = 9 CRLReasonAACompromise CRLReasonCode = 10 ) var ( // ErrAuthorizationFailed indicates that an authorization for an identifier // did not succeed. ErrAuthorizationFailed = errors.New("acme: identifier authorization failed") // ErrUnsupportedKey is returned when an unsupported key type is encountered. ErrUnsupportedKey = errors.New("acme: unknown key type; only RSA and ECDSA are supported") ) // Error is an ACME error, defined in Problem Details for HTTP APIs doc // http://tools.ietf.org/html/draft-ietf-appsawg-http-problem. type Error struct { // StatusCode is The HTTP status code generated by the origin server. StatusCode int // ProblemType is a URI reference that identifies the problem type, // typically in a "urn:acme:error:xxx" form. ProblemType string // Detail is a human-readable explanation specific to this occurrence of the problem. Detail string // Header is the original server error response headers. Header http.Header } func (e *Error) Error() string { return fmt.Sprintf("%d %s: %s", e.StatusCode, e.ProblemType, e.Detail) } // Account is a user account. It is associated with a private key. type Account struct { // URI is the account unique ID, which is also a URL used to retrieve // account data from the CA. URI string // Contact is a slice of contact info used during registration. Contact []string // The terms user has agreed to. // A value not matching CurrentTerms indicates that the user hasn't agreed // to the actual Terms of Service of the CA. AgreedTerms string // Actual terms of a CA. CurrentTerms string // Authz is the authorization URL used to initiate a new authz flow. Authz string // Authorizations is a URI from which a list of authorizations // granted to this account can be fetched via a GET request. Authorizations string // Certificates is a URI from which a list of certificates // issued for this account can be fetched via a GET request. Certificates string } // Directory is ACME server discovery data. type Directory struct { // RegURL is an account endpoint URL, allowing for creating new // and modifying existing accounts. RegURL string // AuthzURL is used to initiate Identifier Authorization flow. AuthzURL string // CertURL is a new certificate issuance endpoint URL. CertURL string // RevokeURL is used to initiate a certificate revocation flow. RevokeURL string // Term is a URI identifying the current terms of service. Terms string // Website is an HTTP or HTTPS URL locating a website // providing more information about the ACME server. Website string // CAA consists of lowercase hostname elements, which the ACME server // recognises as referring to itself for the purposes of CAA record validation // as defined in RFC6844. CAA []string } // Challenge encodes a returned CA challenge. type Challenge struct { // Type is the challenge type, e.g. "http-01", "tls-sni-02", "dns-01". Type string // URI is where a challenge response can be posted to. URI string // Token is a random value that uniquely identifies the challenge. Token string // Status identifies the status of this challenge. Status string } // Authorization encodes an authorization response. type Authorization struct { // URI uniquely identifies a authorization. URI string // Status identifies the status of an authorization. Status string // Identifier is what the account is authorized to represent. Identifier AuthzID // Challenges that the client needs to fulfill in order to prove possession // of the identifier (for pending authorizations). // For final authorizations, the challenges that were used. Challenges []*Challenge // A collection of sets of challenges, each of which would be sufficient // to prove possession of the identifier. // Clients must complete a set of challenges that covers at least one set. // Challenges are identified by their indices in the challenges array. // If this field is empty, the client needs to complete all challenges. Combinations [][]int } // AuthzID is an identifier that an account is authorized to represent. type AuthzID struct { Type string // The type of identifier, e.g. "dns". Value string // The identifier itself, e.g. "example.org". } // wireAuthz is ACME JSON representation of Authorization objects. type wireAuthz struct { Status string Challenges []wireChallenge Combinations [][]int Identifier struct { Type string Value string } } func (z *wireAuthz) authorization(uri string) *Authorization { a := &Authorization{ URI: uri, Status: z.Status, Identifier: AuthzID{Type: z.Identifier.Type, Value: z.Identifier.Value}, Combinations: z.Combinations, // shallow copy Challenges: make([]*Challenge, len(z.Challenges)), } for i, v := range z.Challenges { a.Challenges[i] = v.challenge() } return a } // wireChallenge is ACME JSON challenge representation. type wireChallenge struct { URI string `json:"uri"` Type string Token string Status string } func (c *wireChallenge) challenge() *Challenge { v := &Challenge{ URI: c.URI, Type: c.Type, Token: c.Token, Status: c.Status, } if v.Status == "" { v.Status = StatusPending } return v }