1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
|
package core
import (
"bytes"
"crypto"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"sync"
"time"
"github.com/letsencrypt/pebble/acme"
"gopkg.in/square/go-jose.v2"
)
type Order struct {
sync.RWMutex
acme.Order
ID string
AccountID string
Names []string
ParsedCSR *x509.CertificateRequest
ExpiresDate time.Time
AuthorizationObjects []*Authorization
BeganProcessing bool
CertificateObject *Certificate
}
func (o *Order) GetStatus() (string, error) {
// Lock the order for reading
o.RLock()
defer o.RUnlock()
// If the order has an error set, the status is invalid
if o.Error != nil {
return acme.StatusInvalid, nil
}
authzStatuses := make(map[string]int)
for _, authz := range o.AuthorizationObjects {
// Lock the authorization for reading
authz.RLock()
authzStatus := authz.Status
authzExpires := authz.ExpiresDate
authz.RUnlock()
authzStatuses[authzStatus]++
if authzExpires.Before(time.Now()) {
authzStatuses[acme.StatusExpired]++
}
}
// An order is invalid if **any** of its authzs are invalid
if authzStatuses[acme.StatusInvalid] > 0 {
return acme.StatusInvalid, nil
}
// An order is expired if **any** of its authzs are expired
if authzStatuses[acme.StatusExpired] > 0 {
return acme.StatusInvalid, nil
}
// An order is deactivated if **any** of its authzs are deactivated
if authzStatuses[acme.StatusDeactivated] > 0 {
return acme.StatusDeactivated, nil
}
// An order is pending if **any** of its authzs are pending
if authzStatuses[acme.StatusPending] > 0 {
return acme.StatusPending, nil
}
fullyAuthorized := len(o.Identifiers) == authzStatuses[acme.StatusValid]
// If the order isn't fully authorized we've encountered an internal error:
// Above we checked for any invalid or pending authzs and should have returned
// early. Somehow we made it this far but also don't have the correct number
// of valid authzs.
if !fullyAuthorized {
return "", fmt.Errorf(
"Order has the incorrect number of valid authorizations & no pending, " +
"deactivated or invalid authorizations")
}
// If the order is fully authorized and the certificate serial is set then the
// order is valid
if fullyAuthorized && o.CertificateObject != nil {
return acme.StatusValid, nil
}
// If the order is fully authorized, and we have began processing it, then the
// order is processing.
if fullyAuthorized && o.BeganProcessing {
return acme.StatusProcessing, nil
}
// If the order is fully authorized, and we haven't begun processing it, then
// the order is pending finalization and status ready.
if fullyAuthorized && !o.BeganProcessing {
return acme.StatusReady, nil
}
// If none of the above cases match something weird & unexpected has happened.
return "", fmt.Errorf("Order is in an unknown state")
}
type Account struct {
acme.Account
Key *jose.JSONWebKey `json:"key"`
ID string `json:"-"`
}
type Authorization struct {
sync.RWMutex
acme.Authorization
ID string
URL string
ExpiresDate time.Time
Order *Order
Challenges []*Challenge
}
type Challenge struct {
sync.RWMutex
acme.Challenge
ID string
Authz *Authorization
ValidatedDate time.Time
}
func (ch *Challenge) ExpectedKeyAuthorization(key *jose.JSONWebKey) string {
if key == nil {
panic("ExpectedKeyAuthorization called with nil key")
}
thumbprint, err := key.Thumbprint(crypto.SHA256)
if err != nil {
panic("ExpectedKeyAuthorization: " + err.Error())
}
return ch.Token + "." + base64.RawURLEncoding.EncodeToString(thumbprint)
}
type Certificate struct {
ID string
Cert *x509.Certificate
DER []byte
IssuerChains [][]*Certificate
AccountID string
}
func (c Certificate) PEM() []byte {
var buf bytes.Buffer
err := pem.Encode(&buf, &pem.Block{
Type: "CERTIFICATE",
Bytes: c.DER,
})
if err != nil {
panic(fmt.Sprintf("Unable to encode certificate %q to PEM: %s",
c.ID, err.Error()))
}
return buf.Bytes()
}
func (c Certificate) Chain(no int) []byte {
fullchain := make([][]byte, 0)
// Add the leaf certificate
fullchain = append(fullchain, c.PEM())
// Add zero or more intermediates
var chain []*Certificate
if 0 <= no && no < len(c.IssuerChains) {
chain = c.IssuerChains[no]
}
for _, cert := range chain {
fullchain = append(fullchain, cert.PEM())
}
// Return the chain, leaf cert first
return bytes.Join(fullchain, nil)
}
// RevokedCertificate is a certificate together with information about its revocation.
type RevokedCertificate struct {
Certificate *Certificate
RevokedAt time.Time
Reason *uint
}
type ValidationRecord struct {
URL string
Error *acme.ProblemDetails
ValidatedAt time.Time
}
|