218 lines
6.6 KiB
Go
218 lines
6.6 KiB
Go
// Copyright 2014 ISRG. All rights reserved
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package core
|
|
|
|
import (
|
|
"crypto/x509"
|
|
"encoding/json"
|
|
"github.com/letsencrypt/boulder/jose"
|
|
"time"
|
|
)
|
|
|
|
type IdentifierType string
|
|
type AcmeStatus string
|
|
type Buffer []byte
|
|
|
|
const (
|
|
StatusUnknown = AcmeStatus("unknown") // Unknown status; the default
|
|
StatusPending = AcmeStatus("pending") // In process; client has next action
|
|
StatusProcessing = AcmeStatus("processing") // In process; server has next action
|
|
StatusValid = AcmeStatus("valid") // Validation succeeded
|
|
StatusInvalid = AcmeStatus("invalid") // Validation failed
|
|
StatusRevoked = AcmeStatus("revoked") // Object no longer valid
|
|
)
|
|
|
|
const (
|
|
ChallengeTypeSimpleHTTPS = "simpleHTTPS"
|
|
ChallengeTypeDVSNI = "dvsni"
|
|
ChallengeTypeDNS = "dns"
|
|
ChallengeTypeRecoveryToken = "recoveryToken"
|
|
)
|
|
|
|
const (
|
|
IdentifierDNS = IdentifierType("dns")
|
|
)
|
|
|
|
// An AcmeIdentifier encodes an identifier that can
|
|
// be validated by ACME. The protocol allows for different
|
|
// types of identifier to be supported (DNS names, IP
|
|
// addresses, etc.), but currently we only support
|
|
// domain names.
|
|
type AcmeIdentifier struct {
|
|
Type IdentifierType `json:"type"` // The type of identifier being encoded
|
|
Value string `json:"value"` // The identifier itself
|
|
}
|
|
|
|
// An ACME certificate request is just a CSR together with
|
|
// URIs pointing to authorizations that should collectively
|
|
// authorize the certificate being requsted.
|
|
//
|
|
// This type is never marshaled, since we only ever receive
|
|
// it from the client. So it carries some additional information
|
|
// that is useful internally. (We rely on Go's case-insensitive
|
|
// JSON unmarshal to properly unmarshal client requests.)
|
|
type CertificateRequest struct {
|
|
CSR *x509.CertificateRequest // The CSR
|
|
Authorizations []AcmeURL // Links to Authorization over the account key
|
|
}
|
|
|
|
type rawCertificateRequest struct {
|
|
CSR jose.JsonBuffer `json:"csr"` // The encoded CSR
|
|
Authorizations []AcmeURL `json:"authorizations"` // Authorizations
|
|
}
|
|
|
|
func (cr *CertificateRequest) UnmarshalJSON(data []byte) error {
|
|
var raw rawCertificateRequest
|
|
if err := json.Unmarshal(data, &raw); err != nil {
|
|
return err
|
|
}
|
|
|
|
csr, err := x509.ParseCertificateRequest(raw.CSR)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
cr.CSR = csr
|
|
cr.Authorizations = raw.Authorizations
|
|
return nil
|
|
}
|
|
|
|
func (cr CertificateRequest) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(rawCertificateRequest{
|
|
CSR: cr.CSR.Raw,
|
|
Authorizations: cr.Authorizations,
|
|
})
|
|
}
|
|
|
|
// Registration objects represent non-public metadata attached
|
|
// to account keys.
|
|
type Registration struct {
|
|
// Unique identifier
|
|
ID string `json:"-"`
|
|
|
|
// Account key to which the details are attached
|
|
Key jose.JsonWebKey `json:"key"`
|
|
|
|
// Recovery Token is used to prove connection to an earlier transaction
|
|
RecoveryToken string `json:"recoveryToken"`
|
|
|
|
// Contact URIs
|
|
Contact []AcmeURL `json:"contact,omitempty"`
|
|
|
|
// Agreement with terms of service
|
|
Agreement string `json:"agreement,omitempty"`
|
|
}
|
|
|
|
func (r *Registration) MergeUpdate(input Registration) {
|
|
if len(input.Contact) > 0 {
|
|
r.Contact = input.Contact
|
|
}
|
|
|
|
// TODO: Test to make sure this has the proper value
|
|
if len(input.Agreement) > 0 {
|
|
r.Agreement = input.Agreement
|
|
}
|
|
}
|
|
|
|
// Rather than define individual types for different types of
|
|
// challenge, we just throw all the elements into one bucket,
|
|
// together with the common metadata elements.
|
|
type Challenge struct {
|
|
// The type of challenge
|
|
Type string `json:"type"`
|
|
|
|
// The status of this challenge
|
|
Status AcmeStatus `json:"status,omitempty"`
|
|
|
|
// If successful, the time at which this challenge
|
|
// was completed by the server.
|
|
Validated *time.Time `json:"validated,omitempty"`
|
|
|
|
// A URI to which a response can be POSTed
|
|
URI AcmeURL `json:"uri"`
|
|
|
|
// Used by simpleHTTPS, recoveryToken, and dns challenges
|
|
Token string `json:"token,omitempty"`
|
|
|
|
// Used by simpleHTTPS challenges
|
|
Path string `json:"path,omitempty"`
|
|
|
|
// Used by dvsni challenges
|
|
R string `json:"r,omitempty"`
|
|
S string `json:"s,omitempty"`
|
|
Nonce string `json:"nonce,omitempty"`
|
|
}
|
|
|
|
// Merge a client-provide response to a challenge with the issued challenge
|
|
// TODO: Remove return type from this method
|
|
func (ch Challenge) MergeResponse(resp Challenge) Challenge {
|
|
// Only override fields that are supposed to be client-provided
|
|
if len(ch.Path) == 0 {
|
|
ch.Path = resp.Path
|
|
}
|
|
|
|
if len(ch.S) == 0 {
|
|
ch.S = resp.S
|
|
}
|
|
|
|
return ch
|
|
}
|
|
|
|
// An ACME authorization object represents the authorization
|
|
// of an account key holder to act on behalf of a domain. This
|
|
// struct is intended to be used both internally and for JSON
|
|
// marshaling on the wire. Any fields that should be suppressed
|
|
// on the wire (e.g., ID) must be made empty before marshaling.
|
|
type Authorization struct {
|
|
// An identifier for this authorization, unique across
|
|
// authorizations and certificates within this instance.
|
|
ID string `json:"id,omitempty"`
|
|
|
|
// The identifier for which authorization is being given
|
|
Identifier AcmeIdentifier `json:"identifier,omitempty"`
|
|
|
|
// The account key that is authorized for the identifier
|
|
Key jose.JsonWebKey `json:"key,omitempty"`
|
|
|
|
// The status of the validation of this authorization
|
|
Status AcmeStatus `json:"status,omitempty"`
|
|
|
|
// The date after which this authorization will be no
|
|
// longer be considered valid
|
|
Expires time.Time `json:"expires,omitempty"`
|
|
|
|
// An array of challenges objects used to validate the
|
|
// applicant's control of the identifier. For authorizations
|
|
// in process, these are challenges to be fulfilled; for
|
|
// final authorizations, they describe the evidence that
|
|
// the server used in support of granting the authorization.
|
|
Challenges []Challenge `json:"challenges,omitempty"`
|
|
|
|
// The server may suggest combinations of challenges if it
|
|
// requires more than one challenge to be completed.
|
|
Combinations [][]int `json:"combinations,omitempty"`
|
|
|
|
// The client may provide contact URIs to allow the server
|
|
// to push information to it.
|
|
Contact []AcmeURL `json:"contact,omitempty"`
|
|
}
|
|
|
|
// Certificate objects are entirely internal to the server. The only
|
|
// thing exposed on the wire is the certificate itself.
|
|
type Certificate struct {
|
|
// An identifier for this authorization, unique across
|
|
// authorizations and certificates within this instance.
|
|
ID string
|
|
|
|
// The certificate itself
|
|
DER jose.JsonBuffer
|
|
|
|
// The revocation status of the certificate.
|
|
// * "valid" - not revoked
|
|
// * "revoked" - revoked
|
|
Status AcmeStatus
|
|
}
|