mirror of
https://github.com/containers/skopeo.git
synced 2025-09-03 07:35:02 +00:00
Vendor in c/image with sigstore support
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
This commit is contained in:
375
vendor/github.com/letsencrypt/boulder/LICENSE.txt
generated
vendored
Normal file
375
vendor/github.com/letsencrypt/boulder/LICENSE.txt
generated
vendored
Normal file
@@ -0,0 +1,375 @@
|
||||
Copyright 2016 ISRG. All rights reserved.
|
||||
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
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/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
27
vendor/github.com/letsencrypt/boulder/core/challenges.go
generated
vendored
Normal file
27
vendor/github.com/letsencrypt/boulder/core/challenges.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package core
|
||||
|
||||
func newChallenge(challengeType AcmeChallenge, token string) Challenge {
|
||||
return Challenge{
|
||||
Type: challengeType,
|
||||
Status: StatusPending,
|
||||
Token: token,
|
||||
}
|
||||
}
|
||||
|
||||
// HTTPChallenge01 constructs a random http-01 challenge. If token is empty a random token
|
||||
// will be generated, otherwise the provided token is used.
|
||||
func HTTPChallenge01(token string) Challenge {
|
||||
return newChallenge(ChallengeTypeHTTP01, token)
|
||||
}
|
||||
|
||||
// DNSChallenge01 constructs a random dns-01 challenge. If token is empty a random token
|
||||
// will be generated, otherwise the provided token is used.
|
||||
func DNSChallenge01(token string) Challenge {
|
||||
return newChallenge(ChallengeTypeDNS01, token)
|
||||
}
|
||||
|
||||
// TLSALPNChallenge01 constructs a random tls-alpn-01 challenge. If token is empty a random token
|
||||
// will be generated, otherwise the provided token is used.
|
||||
func TLSALPNChallenge01(token string) Challenge {
|
||||
return newChallenge(ChallengeTypeTLSALPN01, token)
|
||||
}
|
14
vendor/github.com/letsencrypt/boulder/core/interfaces.go
generated
vendored
Normal file
14
vendor/github.com/letsencrypt/boulder/core/interfaces.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"github.com/letsencrypt/boulder/identifier"
|
||||
)
|
||||
|
||||
// PolicyAuthority defines the public interface for the Boulder PA
|
||||
// TODO(#5891): Move this interface to a more appropriate location.
|
||||
type PolicyAuthority interface {
|
||||
WillingToIssue(domain identifier.ACMEIdentifier) error
|
||||
WillingToIssueWildcards(identifiers []identifier.ACMEIdentifier) error
|
||||
ChallengesFor(domain identifier.ACMEIdentifier) ([]Challenge, error)
|
||||
ChallengeTypeEnabled(t AcmeChallenge) bool
|
||||
}
|
536
vendor/github.com/letsencrypt/boulder/core/objects.go
generated
vendored
Normal file
536
vendor/github.com/letsencrypt/boulder/core/objects.go
generated
vendored
Normal file
@@ -0,0 +1,536 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"github.com/letsencrypt/boulder/identifier"
|
||||
"github.com/letsencrypt/boulder/probs"
|
||||
"github.com/letsencrypt/boulder/revocation"
|
||||
)
|
||||
|
||||
// AcmeStatus defines the state of a given authorization
|
||||
type AcmeStatus string
|
||||
|
||||
// These statuses are the states of authorizations, challenges, and registrations
|
||||
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
|
||||
StatusReady = AcmeStatus("ready") // Order is ready for finalization
|
||||
StatusValid = AcmeStatus("valid") // Object is valid
|
||||
StatusInvalid = AcmeStatus("invalid") // Validation failed
|
||||
StatusRevoked = AcmeStatus("revoked") // Object no longer valid
|
||||
StatusDeactivated = AcmeStatus("deactivated") // Object has been deactivated
|
||||
)
|
||||
|
||||
// AcmeResource values identify different types of ACME resources
|
||||
type AcmeResource string
|
||||
|
||||
// The types of ACME resources
|
||||
const (
|
||||
ResourceNewReg = AcmeResource("new-reg")
|
||||
ResourceNewAuthz = AcmeResource("new-authz")
|
||||
ResourceNewCert = AcmeResource("new-cert")
|
||||
ResourceRevokeCert = AcmeResource("revoke-cert")
|
||||
ResourceRegistration = AcmeResource("reg")
|
||||
ResourceChallenge = AcmeResource("challenge")
|
||||
ResourceAuthz = AcmeResource("authz")
|
||||
ResourceKeyChange = AcmeResource("key-change")
|
||||
)
|
||||
|
||||
// AcmeChallenge values identify different types of ACME challenges
|
||||
type AcmeChallenge string
|
||||
|
||||
// These types are the available challenges
|
||||
// TODO(#5009): Make this a custom type as well.
|
||||
const (
|
||||
ChallengeTypeHTTP01 = AcmeChallenge("http-01")
|
||||
ChallengeTypeDNS01 = AcmeChallenge("dns-01")
|
||||
ChallengeTypeTLSALPN01 = AcmeChallenge("tls-alpn-01")
|
||||
)
|
||||
|
||||
// IsValid tests whether the challenge is a known challenge
|
||||
func (c AcmeChallenge) IsValid() bool {
|
||||
switch c {
|
||||
case ChallengeTypeHTTP01, ChallengeTypeDNS01, ChallengeTypeTLSALPN01:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// OCSPStatus defines the state of OCSP for a domain
|
||||
type OCSPStatus string
|
||||
|
||||
// These status are the states of OCSP
|
||||
const (
|
||||
OCSPStatusGood = OCSPStatus("good")
|
||||
OCSPStatusRevoked = OCSPStatus("revoked")
|
||||
)
|
||||
|
||||
// DNSPrefix is attached to DNS names in DNS challenges
|
||||
const DNSPrefix = "_acme-challenge"
|
||||
|
||||
// CertificateRequest is just a CSR
|
||||
//
|
||||
// This data is unmarshalled from JSON by way of RawCertificateRequest, which
|
||||
// represents the actual structure received from the client.
|
||||
type CertificateRequest struct {
|
||||
CSR *x509.CertificateRequest // The CSR
|
||||
Bytes []byte // The original bytes of the CSR, for logging.
|
||||
}
|
||||
|
||||
type RawCertificateRequest struct {
|
||||
CSR JSONBuffer `json:"csr"` // The encoded CSR
|
||||
}
|
||||
|
||||
// UnmarshalJSON provides an implementation for decoding CertificateRequest objects.
|
||||
func (cr *CertificateRequest) UnmarshalJSON(data []byte) error {
|
||||
var raw RawCertificateRequest
|
||||
err := json.Unmarshal(data, &raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
csr, err := x509.ParseCertificateRequest(raw.CSR)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cr.CSR = csr
|
||||
cr.Bytes = raw.CSR
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON provides an implementation for encoding CertificateRequest objects.
|
||||
func (cr CertificateRequest) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(RawCertificateRequest{
|
||||
CSR: cr.CSR.Raw,
|
||||
})
|
||||
}
|
||||
|
||||
// Registration objects represent non-public metadata attached
|
||||
// to account keys.
|
||||
type Registration struct {
|
||||
// Unique identifier
|
||||
ID int64 `json:"id,omitempty" db:"id"`
|
||||
|
||||
// Account key to which the details are attached
|
||||
Key *jose.JSONWebKey `json:"key"`
|
||||
|
||||
// Contact URIs
|
||||
Contact *[]string `json:"contact,omitempty"`
|
||||
|
||||
// Agreement with terms of service
|
||||
Agreement string `json:"agreement,omitempty"`
|
||||
|
||||
// InitialIP is the IP address from which the registration was created
|
||||
InitialIP net.IP `json:"initialIp"`
|
||||
|
||||
// CreatedAt is the time the registration was created.
|
||||
CreatedAt *time.Time `json:"createdAt,omitempty"`
|
||||
|
||||
Status AcmeStatus `json:"status"`
|
||||
}
|
||||
|
||||
// ValidationRecord represents a validation attempt against a specific URL/hostname
|
||||
// and the IP addresses that were resolved and used
|
||||
type ValidationRecord struct {
|
||||
// SimpleHTTP only
|
||||
URL string `json:"url,omitempty"`
|
||||
|
||||
// Shared
|
||||
Hostname string `json:"hostname"`
|
||||
Port string `json:"port,omitempty"`
|
||||
AddressesResolved []net.IP `json:"addressesResolved,omitempty"`
|
||||
AddressUsed net.IP `json:"addressUsed,omitempty"`
|
||||
// AddressesTried contains a list of addresses tried before the `AddressUsed`.
|
||||
// Presently this will only ever be one IP from `AddressesResolved` since the
|
||||
// only retry is in the case of a v6 failure with one v4 fallback. E.g. if
|
||||
// a record with `AddressesResolved: { 127.0.0.1, ::1 }` were processed for
|
||||
// a challenge validation with the IPv6 first flag on and the ::1 address
|
||||
// failed but the 127.0.0.1 retry succeeded then the record would end up
|
||||
// being:
|
||||
// {
|
||||
// ...
|
||||
// AddressesResolved: [ 127.0.0.1, ::1 ],
|
||||
// AddressUsed: 127.0.0.1
|
||||
// AddressesTried: [ ::1 ],
|
||||
// ...
|
||||
// }
|
||||
AddressesTried []net.IP `json:"addressesTried,omitempty"`
|
||||
|
||||
// OldTLS is true if any request in the validation chain used HTTPS and negotiated
|
||||
// a TLS version lower than 1.2.
|
||||
// TODO(#6011): Remove once TLS 1.0 and 1.1 support is gone.
|
||||
OldTLS bool `json:"oldTLS,omitempty"`
|
||||
}
|
||||
|
||||
func looksLikeKeyAuthorization(str string) error {
|
||||
parts := strings.Split(str, ".")
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("Invalid key authorization: does not look like a key authorization")
|
||||
} else if !LooksLikeAToken(parts[0]) {
|
||||
return fmt.Errorf("Invalid key authorization: malformed token")
|
||||
} else if !LooksLikeAToken(parts[1]) {
|
||||
// Thumbprints have the same syntax as tokens in boulder
|
||||
// Both are base64-encoded and 32 octets
|
||||
return fmt.Errorf("Invalid key authorization: malformed key thumbprint")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Challenge is an aggregate of all data needed for any challenges.
|
||||
//
|
||||
// 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 AcmeChallenge `json:"type"`
|
||||
|
||||
// The status of this challenge
|
||||
Status AcmeStatus `json:"status,omitempty"`
|
||||
|
||||
// Contains the error that occurred during challenge validation, if any
|
||||
Error *probs.ProblemDetails `json:"error,omitempty"`
|
||||
|
||||
// A URI to which a response can be POSTed
|
||||
URI string `json:"uri,omitempty"`
|
||||
|
||||
// For the V2 API the "URI" field is deprecated in favour of URL.
|
||||
URL string `json:"url,omitempty"`
|
||||
|
||||
// Used by http-01, tls-sni-01, tls-alpn-01 and dns-01 challenges
|
||||
Token string `json:"token,omitempty"`
|
||||
|
||||
// The expected KeyAuthorization for validation of the challenge. Populated by
|
||||
// the RA prior to passing the challenge to the VA. For legacy reasons this
|
||||
// field is called "ProvidedKeyAuthorization" because it was initially set by
|
||||
// the content of the challenge update POST from the client. It is no longer
|
||||
// set that way and should be renamed to "KeyAuthorization".
|
||||
// TODO(@cpu): Rename `ProvidedKeyAuthorization` to `KeyAuthorization`.
|
||||
ProvidedKeyAuthorization string `json:"keyAuthorization,omitempty"`
|
||||
|
||||
// Contains information about URLs used or redirected to and IPs resolved and
|
||||
// used
|
||||
ValidationRecord []ValidationRecord `json:"validationRecord,omitempty"`
|
||||
// The time at which the server validated the challenge. Required by
|
||||
// RFC8555 if status is valid.
|
||||
Validated *time.Time `json:"validated,omitempty"`
|
||||
}
|
||||
|
||||
// ExpectedKeyAuthorization computes the expected KeyAuthorization value for
|
||||
// the challenge.
|
||||
func (ch Challenge) ExpectedKeyAuthorization(key *jose.JSONWebKey) (string, error) {
|
||||
if key == nil {
|
||||
return "", fmt.Errorf("Cannot authorize a nil key")
|
||||
}
|
||||
|
||||
thumbprint, err := key.Thumbprint(crypto.SHA256)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return ch.Token + "." + base64.RawURLEncoding.EncodeToString(thumbprint), nil
|
||||
}
|
||||
|
||||
// RecordsSane checks the sanity of a ValidationRecord object before sending it
|
||||
// back to the RA to be stored.
|
||||
func (ch Challenge) RecordsSane() bool {
|
||||
if ch.ValidationRecord == nil || len(ch.ValidationRecord) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch ch.Type {
|
||||
case ChallengeTypeHTTP01:
|
||||
for _, rec := range ch.ValidationRecord {
|
||||
if rec.URL == "" || rec.Hostname == "" || rec.Port == "" || rec.AddressUsed == nil ||
|
||||
len(rec.AddressesResolved) == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
case ChallengeTypeTLSALPN01:
|
||||
if len(ch.ValidationRecord) > 1 {
|
||||
return false
|
||||
}
|
||||
if ch.ValidationRecord[0].URL != "" {
|
||||
return false
|
||||
}
|
||||
if ch.ValidationRecord[0].Hostname == "" || ch.ValidationRecord[0].Port == "" ||
|
||||
ch.ValidationRecord[0].AddressUsed == nil || len(ch.ValidationRecord[0].AddressesResolved) == 0 {
|
||||
return false
|
||||
}
|
||||
case ChallengeTypeDNS01:
|
||||
if len(ch.ValidationRecord) > 1 {
|
||||
return false
|
||||
}
|
||||
if ch.ValidationRecord[0].Hostname == "" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
default: // Unsupported challenge type
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// CheckConsistencyForClientOffer checks the fields of a challenge object before it is
|
||||
// given to the client.
|
||||
func (ch Challenge) CheckConsistencyForClientOffer() error {
|
||||
err := ch.checkConsistency()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Before completion, the key authorization field should be empty
|
||||
if ch.ProvidedKeyAuthorization != "" {
|
||||
return fmt.Errorf("A response to this challenge was already submitted.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckConsistencyForValidation checks the fields of a challenge object before it is
|
||||
// given to the VA.
|
||||
func (ch Challenge) CheckConsistencyForValidation() error {
|
||||
err := ch.checkConsistency()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the challenge is completed, then there should be a key authorization
|
||||
return looksLikeKeyAuthorization(ch.ProvidedKeyAuthorization)
|
||||
}
|
||||
|
||||
// checkConsistency checks the sanity of a challenge object before issued to the client.
|
||||
func (ch Challenge) checkConsistency() error {
|
||||
if ch.Status != StatusPending {
|
||||
return fmt.Errorf("The challenge is not pending.")
|
||||
}
|
||||
|
||||
// There always needs to be a token
|
||||
if !LooksLikeAToken(ch.Token) {
|
||||
return fmt.Errorf("The token is missing.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StringID is used to generate a ID for challenges associated with new style authorizations.
|
||||
// This is necessary as these challenges no longer have a unique non-sequential identifier
|
||||
// in the new storage scheme. This identifier is generated by constructing a fnv hash over the
|
||||
// challenge token and type and encoding the first 4 bytes of it using the base64 URL encoding.
|
||||
func (ch Challenge) StringID() string {
|
||||
h := fnv.New128a()
|
||||
h.Write([]byte(ch.Token))
|
||||
h.Write([]byte(ch.Type))
|
||||
return base64.RawURLEncoding.EncodeToString(h.Sum(nil)[0:4])
|
||||
}
|
||||
|
||||
// Authorization 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, regID) 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" db:"id"`
|
||||
|
||||
// The identifier for which authorization is being given
|
||||
Identifier identifier.ACMEIdentifier `json:"identifier,omitempty" db:"identifier"`
|
||||
|
||||
// The registration ID associated with the authorization
|
||||
RegistrationID int64 `json:"regId,omitempty" db:"registrationID"`
|
||||
|
||||
// The status of the validation of this authorization
|
||||
Status AcmeStatus `json:"status,omitempty" db:"status"`
|
||||
|
||||
// The date after which this authorization will be no
|
||||
// longer be considered valid. Note: a certificate may be issued even on the
|
||||
// last day of an authorization's lifetime. The last day for which someone can
|
||||
// hold a valid certificate based on an authorization is authorization
|
||||
// lifetime + certificate lifetime.
|
||||
Expires *time.Time `json:"expires,omitempty" db:"expires"`
|
||||
|
||||
// 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.
|
||||
//
|
||||
// There should only ever be one challenge of each type in this
|
||||
// slice and the order of these challenges may not be predictable.
|
||||
Challenges []Challenge `json:"challenges,omitempty" db:"-"`
|
||||
|
||||
// This field is deprecated. It's filled in by WFE for the ACMEv1 API.
|
||||
Combinations [][]int `json:"combinations,omitempty" db:"combinations"`
|
||||
|
||||
// Wildcard is a Boulder-specific Authorization field that indicates the
|
||||
// authorization was created as a result of an order containing a name with
|
||||
// a `*.`wildcard prefix. This will help convey to users that an
|
||||
// Authorization with the identifier `example.com` and one DNS-01 challenge
|
||||
// corresponds to a name `*.example.com` from an associated order.
|
||||
Wildcard bool `json:"wildcard,omitempty" db:"-"`
|
||||
}
|
||||
|
||||
// FindChallengeByStringID will look for a challenge matching the given ID inside
|
||||
// this authorization. If found, it will return the index of that challenge within
|
||||
// the Authorization's Challenges array. Otherwise it will return -1.
|
||||
func (authz *Authorization) FindChallengeByStringID(id string) int {
|
||||
for i, c := range authz.Challenges {
|
||||
if c.StringID() == id {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// SolvedBy will look through the Authorizations challenges, returning the type
|
||||
// of the *first* challenge it finds with Status: valid, or an error if no
|
||||
// challenge is valid.
|
||||
func (authz *Authorization) SolvedBy() (*AcmeChallenge, error) {
|
||||
if len(authz.Challenges) == 0 {
|
||||
return nil, fmt.Errorf("Authorization has no challenges")
|
||||
}
|
||||
for _, chal := range authz.Challenges {
|
||||
if chal.Status == StatusValid {
|
||||
return &chal.Type, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Authorization not solved by any challenge")
|
||||
}
|
||||
|
||||
// JSONBuffer fields get encoded and decoded JOSE-style, in base64url encoding
|
||||
// with stripped padding.
|
||||
type JSONBuffer []byte
|
||||
|
||||
// URL-safe base64 encode that strips padding
|
||||
func base64URLEncode(data []byte) string {
|
||||
var result = base64.URLEncoding.EncodeToString(data)
|
||||
return strings.TrimRight(result, "=")
|
||||
}
|
||||
|
||||
// URL-safe base64 decoder that adds padding
|
||||
func base64URLDecode(data string) ([]byte, error) {
|
||||
var missing = (4 - len(data)%4) % 4
|
||||
data += strings.Repeat("=", missing)
|
||||
return base64.URLEncoding.DecodeString(data)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes a JSONBuffer for transmission.
|
||||
func (jb JSONBuffer) MarshalJSON() (result []byte, err error) {
|
||||
return json.Marshal(base64URLEncode(jb))
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes a JSONBuffer to an object.
|
||||
func (jb *JSONBuffer) UnmarshalJSON(data []byte) (err error) {
|
||||
var str string
|
||||
err = json.Unmarshal(data, &str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*jb, err = base64URLDecode(str)
|
||||
return
|
||||
}
|
||||
|
||||
// Certificate objects are entirely internal to the server. The only
|
||||
// thing exposed on the wire is the certificate itself.
|
||||
type Certificate struct {
|
||||
ID int64 `db:"id"`
|
||||
RegistrationID int64 `db:"registrationID"`
|
||||
|
||||
Serial string `db:"serial"`
|
||||
Digest string `db:"digest"`
|
||||
DER []byte `db:"der"`
|
||||
Issued time.Time `db:"issued"`
|
||||
Expires time.Time `db:"expires"`
|
||||
}
|
||||
|
||||
// CertificateStatus structs are internal to the server. They represent the
|
||||
// latest data about the status of the certificate, required for OCSP updating
|
||||
// and for validating that the subscriber has accepted the certificate.
|
||||
type CertificateStatus struct {
|
||||
ID int64 `db:"id"`
|
||||
|
||||
Serial string `db:"serial"`
|
||||
|
||||
// status: 'good' or 'revoked'. Note that good, expired certificates remain
|
||||
// with status 'good' but don't necessarily get fresh OCSP responses.
|
||||
Status OCSPStatus `db:"status"`
|
||||
|
||||
// ocspLastUpdated: The date and time of the last time we generated an OCSP
|
||||
// response. If we have never generated one, this has the zero value of
|
||||
// time.Time, i.e. Jan 1 1970.
|
||||
OCSPLastUpdated time.Time `db:"ocspLastUpdated"`
|
||||
|
||||
// revokedDate: If status is 'revoked', this is the date and time it was
|
||||
// revoked. Otherwise it has the zero value of time.Time, i.e. Jan 1 1970.
|
||||
RevokedDate time.Time `db:"revokedDate"`
|
||||
|
||||
// revokedReason: If status is 'revoked', this is the reason code for the
|
||||
// revocation. Otherwise it is zero (which happens to be the reason
|
||||
// code for 'unspecified').
|
||||
RevokedReason revocation.Reason `db:"revokedReason"`
|
||||
|
||||
LastExpirationNagSent time.Time `db:"lastExpirationNagSent"`
|
||||
|
||||
// The encoded and signed OCSP response.
|
||||
OCSPResponse []byte `db:"ocspResponse"`
|
||||
|
||||
// For performance reasons[0] we duplicate the `Expires` field of the
|
||||
// `Certificates` object/table in `CertificateStatus` to avoid a costly `JOIN`
|
||||
// later on just to retrieve this `Time` value. This helps both the OCSP
|
||||
// updater and the expiration-mailer stay performant.
|
||||
//
|
||||
// Similarly, we add an explicit `IsExpired` boolean to `CertificateStatus`
|
||||
// table that the OCSP updater so that the database can create a meaningful
|
||||
// index on `(isExpired, ocspLastUpdated)` without a `JOIN` on `certificates`.
|
||||
// For more detail see Boulder #1864[0].
|
||||
//
|
||||
// [0]: https://github.com/letsencrypt/boulder/issues/1864
|
||||
NotAfter time.Time `db:"notAfter"`
|
||||
IsExpired bool `db:"isExpired"`
|
||||
|
||||
// TODO(#5152): Change this to an issuance.Issuer(Name)ID after it no longer
|
||||
// has to support both IssuerNameIDs and IssuerIDs.
|
||||
IssuerID int64
|
||||
}
|
||||
|
||||
// FQDNSet contains the SHA256 hash of the lowercased, comma joined dNSNames
|
||||
// contained in a certificate.
|
||||
type FQDNSet struct {
|
||||
ID int64
|
||||
SetHash []byte
|
||||
Serial string
|
||||
Issued time.Time
|
||||
Expires time.Time
|
||||
}
|
||||
|
||||
// SCTDERs is a convenience type
|
||||
type SCTDERs [][]byte
|
||||
|
||||
// CertDER is a convenience type that helps differentiate what the
|
||||
// underlying byte slice contains
|
||||
type CertDER []byte
|
||||
|
||||
// SuggestedWindow is a type exposed inside the RenewalInfo resource.
|
||||
type SuggestedWindow struct {
|
||||
Start time.Time `json:"start"`
|
||||
End time.Time `json:"end"`
|
||||
}
|
||||
|
||||
// RenewalInfo is a type which is exposed to clients which query the renewalInfo
|
||||
// endpoint specified in draft-aaron-ari.
|
||||
type RenewalInfo struct {
|
||||
SuggestedWindow SuggestedWindow `json:"suggestedWindow"`
|
||||
}
|
1100
vendor/github.com/letsencrypt/boulder/core/proto/core.pb.go
generated
vendored
Normal file
1100
vendor/github.com/letsencrypt/boulder/core/proto/core.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
95
vendor/github.com/letsencrypt/boulder/core/proto/core.proto
generated
vendored
Normal file
95
vendor/github.com/letsencrypt/boulder/core/proto/core.proto
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package core;
|
||||
option go_package = "github.com/letsencrypt/boulder/core/proto";
|
||||
|
||||
message Challenge {
|
||||
int64 id = 1;
|
||||
string type = 2;
|
||||
string status = 6;
|
||||
string uri = 9;
|
||||
string token = 3;
|
||||
string keyAuthorization = 5;
|
||||
repeated ValidationRecord validationrecords = 10;
|
||||
ProblemDetails error = 7;
|
||||
int64 validated = 11;
|
||||
}
|
||||
|
||||
message ValidationRecord {
|
||||
string hostname = 1;
|
||||
string port = 2;
|
||||
repeated bytes addressesResolved = 3; // net.IP.MarshalText()
|
||||
bytes addressUsed = 4; // net.IP.MarshalText()
|
||||
|
||||
repeated string authorities = 5;
|
||||
string url = 6;
|
||||
// A list of addresses tried before the address used (see
|
||||
// core/objects.go and the comment on the ValidationRecord structure
|
||||
// definition for more information.
|
||||
repeated bytes addressesTried = 7; // net.IP.MarshalText()
|
||||
}
|
||||
|
||||
message ProblemDetails {
|
||||
string problemType = 1;
|
||||
string detail = 2;
|
||||
int32 httpStatus = 3;
|
||||
}
|
||||
|
||||
message Certificate {
|
||||
int64 registrationID = 1;
|
||||
string serial = 2;
|
||||
string digest = 3;
|
||||
bytes der = 4;
|
||||
int64 issued = 5; // Unix timestamp (nanoseconds)
|
||||
int64 expires = 6; // Unix timestamp (nanoseconds)
|
||||
}
|
||||
|
||||
message CertificateStatus {
|
||||
string serial = 1;
|
||||
reserved 2; // previously subscriberApproved
|
||||
string status = 3;
|
||||
int64 ocspLastUpdated = 4;
|
||||
int64 revokedDate = 5;
|
||||
int64 revokedReason = 6;
|
||||
int64 lastExpirationNagSent = 7;
|
||||
bytes ocspResponse = 8;
|
||||
int64 notAfter = 9;
|
||||
bool isExpired = 10;
|
||||
int64 issuerID = 11;
|
||||
}
|
||||
|
||||
message Registration {
|
||||
int64 id = 1;
|
||||
bytes key = 2;
|
||||
repeated string contact = 3;
|
||||
bool contactsPresent = 4;
|
||||
string agreement = 5;
|
||||
bytes initialIP = 6;
|
||||
int64 createdAt = 7; // Unix timestamp (nanoseconds)
|
||||
string status = 8;
|
||||
}
|
||||
|
||||
message Authorization {
|
||||
string id = 1;
|
||||
string identifier = 2;
|
||||
int64 registrationID = 3;
|
||||
string status = 4;
|
||||
int64 expires = 5; // Unix timestamp (nanoseconds)
|
||||
repeated core.Challenge challenges = 6;
|
||||
reserved 7; // previously combinations
|
||||
reserved 8; // previously v2
|
||||
}
|
||||
|
||||
message Order {
|
||||
int64 id = 1;
|
||||
int64 registrationID = 2;
|
||||
int64 expires = 3;
|
||||
ProblemDetails error = 4;
|
||||
string certificateSerial = 5;
|
||||
reserved 6; // previously authorizations, deprecated in favor of v2Authorizations
|
||||
string status = 7;
|
||||
repeated string names = 8;
|
||||
bool beganProcessing = 9;
|
||||
int64 created = 10;
|
||||
repeated int64 v2Authorizations = 11;
|
||||
}
|
298
vendor/github.com/letsencrypt/boulder/core/util.go
generated
vendored
Normal file
298
vendor/github.com/letsencrypt/boulder/core/util.go
generated
vendored
Normal file
@@ -0,0 +1,298 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"expvar"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
mrand "math/rand"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
jose "gopkg.in/square/go-jose.v2"
|
||||
)
|
||||
|
||||
// Package Variables Variables
|
||||
|
||||
// BuildID is set by the compiler (using -ldflags "-X core.BuildID $(git rev-parse --short HEAD)")
|
||||
// and is used by GetBuildID
|
||||
var BuildID string
|
||||
|
||||
// BuildHost is set by the compiler and is used by GetBuildHost
|
||||
var BuildHost string
|
||||
|
||||
// BuildTime is set by the compiler and is used by GetBuildTime
|
||||
var BuildTime string
|
||||
|
||||
func init() {
|
||||
expvar.NewString("BuildID").Set(BuildID)
|
||||
expvar.NewString("BuildTime").Set(BuildTime)
|
||||
}
|
||||
|
||||
// Random stuff
|
||||
|
||||
type randSource interface {
|
||||
Read(p []byte) (n int, err error)
|
||||
}
|
||||
|
||||
// RandReader is used so that it can be replaced in tests that require
|
||||
// deterministic output
|
||||
var RandReader randSource = rand.Reader
|
||||
|
||||
// RandomString returns a randomly generated string of the requested length.
|
||||
func RandomString(byteLength int) string {
|
||||
b := make([]byte, byteLength)
|
||||
_, err := io.ReadFull(RandReader, b)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error reading random bytes: %s", err))
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(b)
|
||||
}
|
||||
|
||||
// NewToken produces a random string for Challenges, etc.
|
||||
func NewToken() string {
|
||||
return RandomString(32)
|
||||
}
|
||||
|
||||
var tokenFormat = regexp.MustCompile(`^[\w-]{43}$`)
|
||||
|
||||
// LooksLikeAToken checks whether a string represents a 32-octet value in
|
||||
// the URL-safe base64 alphabet.
|
||||
func LooksLikeAToken(token string) bool {
|
||||
return tokenFormat.MatchString(token)
|
||||
}
|
||||
|
||||
// Fingerprints
|
||||
|
||||
// Fingerprint256 produces an unpadded, URL-safe Base64-encoded SHA256 digest
|
||||
// of the data.
|
||||
func Fingerprint256(data []byte) string {
|
||||
d := sha256.New()
|
||||
_, _ = d.Write(data) // Never returns an error
|
||||
return base64.RawURLEncoding.EncodeToString(d.Sum(nil))
|
||||
}
|
||||
|
||||
type Sha256Digest [sha256.Size]byte
|
||||
|
||||
// KeyDigest produces a Base64-encoded SHA256 digest of a
|
||||
// provided public key.
|
||||
func KeyDigest(key crypto.PublicKey) (Sha256Digest, error) {
|
||||
switch t := key.(type) {
|
||||
case *jose.JSONWebKey:
|
||||
if t == nil {
|
||||
return Sha256Digest{}, fmt.Errorf("Cannot compute digest of nil key")
|
||||
}
|
||||
return KeyDigest(t.Key)
|
||||
case jose.JSONWebKey:
|
||||
return KeyDigest(t.Key)
|
||||
default:
|
||||
keyDER, err := x509.MarshalPKIXPublicKey(key)
|
||||
if err != nil {
|
||||
return Sha256Digest{}, err
|
||||
}
|
||||
return sha256.Sum256(keyDER), nil
|
||||
}
|
||||
}
|
||||
|
||||
// KeyDigestB64 produces a padded, standard Base64-encoded SHA256 digest of a
|
||||
// provided public key.
|
||||
func KeyDigestB64(key crypto.PublicKey) (string, error) {
|
||||
digest, err := KeyDigest(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(digest[:]), nil
|
||||
}
|
||||
|
||||
// KeyDigestEquals determines whether two public keys have the same digest.
|
||||
func KeyDigestEquals(j, k crypto.PublicKey) bool {
|
||||
digestJ, errJ := KeyDigestB64(j)
|
||||
digestK, errK := KeyDigestB64(k)
|
||||
// Keys that don't have a valid digest (due to marshalling problems)
|
||||
// are never equal. So, e.g. nil keys are not equal.
|
||||
if errJ != nil || errK != nil {
|
||||
return false
|
||||
}
|
||||
return digestJ == digestK
|
||||
}
|
||||
|
||||
// PublicKeysEqual determines whether two public keys have the same marshalled
|
||||
// bytes as one another
|
||||
func PublicKeysEqual(a, b interface{}) (bool, error) {
|
||||
if a == nil || b == nil {
|
||||
return false, errors.New("One or more nil arguments to PublicKeysEqual")
|
||||
}
|
||||
aBytes, err := x509.MarshalPKIXPublicKey(a)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
bBytes, err := x509.MarshalPKIXPublicKey(b)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return bytes.Equal(aBytes, bBytes), nil
|
||||
}
|
||||
|
||||
// SerialToString converts a certificate serial number (big.Int) to a String
|
||||
// consistently.
|
||||
func SerialToString(serial *big.Int) string {
|
||||
return fmt.Sprintf("%036x", serial)
|
||||
}
|
||||
|
||||
// StringToSerial converts a string into a certificate serial number (big.Int)
|
||||
// consistently.
|
||||
func StringToSerial(serial string) (*big.Int, error) {
|
||||
var serialNum big.Int
|
||||
if !ValidSerial(serial) {
|
||||
return &serialNum, errors.New("Invalid serial number")
|
||||
}
|
||||
_, err := fmt.Sscanf(serial, "%036x", &serialNum)
|
||||
return &serialNum, err
|
||||
}
|
||||
|
||||
// ValidSerial tests whether the input string represents a syntactically
|
||||
// valid serial number, i.e., that it is a valid hex string between 32
|
||||
// and 36 characters long.
|
||||
func ValidSerial(serial string) bool {
|
||||
// Originally, serial numbers were 32 hex characters long. We later increased
|
||||
// them to 36, but we allow the shorter ones because they exist in some
|
||||
// production databases.
|
||||
if len(serial) != 32 && len(serial) != 36 {
|
||||
return false
|
||||
}
|
||||
_, err := hex.DecodeString(serial)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// GetBuildID identifies what build is running.
|
||||
func GetBuildID() (retID string) {
|
||||
retID = BuildID
|
||||
if retID == "" {
|
||||
retID = "Unspecified"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetBuildTime identifies when this build was made
|
||||
func GetBuildTime() (retID string) {
|
||||
retID = BuildTime
|
||||
if retID == "" {
|
||||
retID = "Unspecified"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetBuildHost identifies the building host
|
||||
func GetBuildHost() (retID string) {
|
||||
retID = BuildHost
|
||||
if retID == "" {
|
||||
retID = "Unspecified"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// IsAnyNilOrZero returns whether any of the supplied values are nil, or (if not)
|
||||
// if any of them is its type's zero-value. This is useful for validating that
|
||||
// all required fields on a proto message are present.
|
||||
func IsAnyNilOrZero(vals ...interface{}) bool {
|
||||
for _, val := range vals {
|
||||
switch v := val.(type) {
|
||||
case nil:
|
||||
return true
|
||||
case []byte:
|
||||
if len(v) == 0 {
|
||||
return true
|
||||
}
|
||||
default:
|
||||
if reflect.ValueOf(v).IsZero() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// UniqueLowerNames returns the set of all unique names in the input after all
|
||||
// of them are lowercased. The returned names will be in their lowercased form
|
||||
// and sorted alphabetically.
|
||||
func UniqueLowerNames(names []string) (unique []string) {
|
||||
nameMap := make(map[string]int, len(names))
|
||||
for _, name := range names {
|
||||
nameMap[strings.ToLower(name)] = 1
|
||||
}
|
||||
|
||||
unique = make([]string, 0, len(nameMap))
|
||||
for name := range nameMap {
|
||||
unique = append(unique, name)
|
||||
}
|
||||
sort.Strings(unique)
|
||||
return
|
||||
}
|
||||
|
||||
// LoadCert loads a PEM certificate specified by filename or returns an error
|
||||
func LoadCert(filename string) (*x509.Certificate, error) {
|
||||
certPEM, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block, _ := pem.Decode(certPEM)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("No data in cert PEM file %s", filename)
|
||||
}
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// retryJitter is used to prevent bunched retried queries from falling into lockstep
|
||||
const retryJitter = 0.2
|
||||
|
||||
// RetryBackoff calculates a backoff time based on number of retries, will always
|
||||
// add jitter so requests that start in unison won't fall into lockstep. Because of
|
||||
// this the returned duration can always be larger than the maximum by a factor of
|
||||
// retryJitter. Adapted from
|
||||
// https://github.com/grpc/grpc-go/blob/v1.11.3/backoff.go#L77-L96
|
||||
func RetryBackoff(retries int, base, max time.Duration, factor float64) time.Duration {
|
||||
if retries == 0 {
|
||||
return 0
|
||||
}
|
||||
backoff, fMax := float64(base), float64(max)
|
||||
for backoff < fMax && retries > 1 {
|
||||
backoff *= factor
|
||||
retries--
|
||||
}
|
||||
if backoff > fMax {
|
||||
backoff = fMax
|
||||
}
|
||||
// Randomize backoff delays so that if a cluster of requests start at
|
||||
// the same time, they won't operate in lockstep.
|
||||
backoff *= (1 - retryJitter) + 2*retryJitter*mrand.Float64()
|
||||
return time.Duration(backoff)
|
||||
}
|
||||
|
||||
// IsASCII determines if every character in a string is encoded in
|
||||
// the ASCII character set.
|
||||
func IsASCII(str string) bool {
|
||||
for _, r := range str {
|
||||
if r > unicode.MaxASCII {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
150
vendor/github.com/letsencrypt/boulder/errors/errors.go
generated
vendored
Normal file
150
vendor/github.com/letsencrypt/boulder/errors/errors.go
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/letsencrypt/boulder/identifier"
|
||||
)
|
||||
|
||||
// ErrorType provides a coarse category for BoulderErrors.
|
||||
// Objects of type ErrorType should never be directly returned by other
|
||||
// functions; instead use the methods below to create an appropriate
|
||||
// BoulderError wrapping one of these types.
|
||||
type ErrorType int
|
||||
|
||||
const (
|
||||
InternalServer ErrorType = iota
|
||||
_
|
||||
Malformed
|
||||
Unauthorized
|
||||
NotFound
|
||||
RateLimit
|
||||
RejectedIdentifier
|
||||
InvalidEmail
|
||||
ConnectionFailure
|
||||
_ // Reserved, previously WrongAuthorizationState
|
||||
CAA
|
||||
MissingSCTs
|
||||
Duplicate
|
||||
OrderNotReady
|
||||
DNS
|
||||
BadPublicKey
|
||||
BadCSR
|
||||
AlreadyRevoked
|
||||
BadRevocationReason
|
||||
)
|
||||
|
||||
func (ErrorType) Error() string {
|
||||
return "urn:ietf:params:acme:error"
|
||||
}
|
||||
|
||||
// BoulderError represents internal Boulder errors
|
||||
type BoulderError struct {
|
||||
Type ErrorType
|
||||
Detail string
|
||||
SubErrors []SubBoulderError
|
||||
}
|
||||
|
||||
// SubBoulderError represents sub-errors specific to an identifier that are
|
||||
// related to a top-level internal Boulder error.
|
||||
type SubBoulderError struct {
|
||||
*BoulderError
|
||||
Identifier identifier.ACMEIdentifier
|
||||
}
|
||||
|
||||
func (be *BoulderError) Error() string {
|
||||
return be.Detail
|
||||
}
|
||||
|
||||
func (be *BoulderError) Unwrap() error {
|
||||
return be.Type
|
||||
}
|
||||
|
||||
// WithSubErrors returns a new BoulderError instance created by adding the
|
||||
// provided subErrs to the existing BoulderError.
|
||||
func (be *BoulderError) WithSubErrors(subErrs []SubBoulderError) *BoulderError {
|
||||
return &BoulderError{
|
||||
Type: be.Type,
|
||||
Detail: be.Detail,
|
||||
SubErrors: append(be.SubErrors, subErrs...),
|
||||
}
|
||||
}
|
||||
|
||||
// New is a convenience function for creating a new BoulderError
|
||||
func New(errType ErrorType, msg string, args ...interface{}) error {
|
||||
return &BoulderError{
|
||||
Type: errType,
|
||||
Detail: fmt.Sprintf(msg, args...),
|
||||
}
|
||||
}
|
||||
|
||||
func InternalServerError(msg string, args ...interface{}) error {
|
||||
return New(InternalServer, msg, args...)
|
||||
}
|
||||
|
||||
func MalformedError(msg string, args ...interface{}) error {
|
||||
return New(Malformed, msg, args...)
|
||||
}
|
||||
|
||||
func UnauthorizedError(msg string, args ...interface{}) error {
|
||||
return New(Unauthorized, msg, args...)
|
||||
}
|
||||
|
||||
func NotFoundError(msg string, args ...interface{}) error {
|
||||
return New(NotFound, msg, args...)
|
||||
}
|
||||
|
||||
func RateLimitError(msg string, args ...interface{}) error {
|
||||
return &BoulderError{
|
||||
Type: RateLimit,
|
||||
Detail: fmt.Sprintf(msg+": see https://letsencrypt.org/docs/rate-limits/", args...),
|
||||
}
|
||||
}
|
||||
|
||||
func RejectedIdentifierError(msg string, args ...interface{}) error {
|
||||
return New(RejectedIdentifier, msg, args...)
|
||||
}
|
||||
|
||||
func InvalidEmailError(msg string, args ...interface{}) error {
|
||||
return New(InvalidEmail, msg, args...)
|
||||
}
|
||||
|
||||
func ConnectionFailureError(msg string, args ...interface{}) error {
|
||||
return New(ConnectionFailure, msg, args...)
|
||||
}
|
||||
|
||||
func CAAError(msg string, args ...interface{}) error {
|
||||
return New(CAA, msg, args...)
|
||||
}
|
||||
|
||||
func MissingSCTsError(msg string, args ...interface{}) error {
|
||||
return New(MissingSCTs, msg, args...)
|
||||
}
|
||||
|
||||
func DuplicateError(msg string, args ...interface{}) error {
|
||||
return New(Duplicate, msg, args...)
|
||||
}
|
||||
|
||||
func OrderNotReadyError(msg string, args ...interface{}) error {
|
||||
return New(OrderNotReady, msg, args...)
|
||||
}
|
||||
|
||||
func DNSError(msg string, args ...interface{}) error {
|
||||
return New(DNS, msg, args...)
|
||||
}
|
||||
|
||||
func BadPublicKeyError(msg string, args ...interface{}) error {
|
||||
return New(BadPublicKey, msg, args...)
|
||||
}
|
||||
|
||||
func BadCSRError(msg string, args ...interface{}) error {
|
||||
return New(BadCSR, msg, args...)
|
||||
}
|
||||
|
||||
func AlreadyRevokedError(msg string, args ...interface{}) error {
|
||||
return New(AlreadyRevoked, msg, args...)
|
||||
}
|
||||
|
||||
func BadRevocationReasonError(reason int64) error {
|
||||
return New(BadRevocationReason, "disallowed revocation reason: %d", reason)
|
||||
}
|
45
vendor/github.com/letsencrypt/boulder/features/featureflag_string.go
generated
vendored
Normal file
45
vendor/github.com/letsencrypt/boulder/features/featureflag_string.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// Code generated by "stringer -type=FeatureFlag"; DO NOT EDIT.
|
||||
|
||||
package features
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[unused-0]
|
||||
_ = x[PrecertificateRevocation-1]
|
||||
_ = x[StripDefaultSchemePort-2]
|
||||
_ = x[NonCFSSLSigner-3]
|
||||
_ = x[StoreIssuerInfo-4]
|
||||
_ = x[StreamlineOrderAndAuthzs-5]
|
||||
_ = x[V1DisableNewValidations-6]
|
||||
_ = x[CAAValidationMethods-7]
|
||||
_ = x[CAAAccountURI-8]
|
||||
_ = x[EnforceMultiVA-9]
|
||||
_ = x[MultiVAFullResults-10]
|
||||
_ = x[MandatoryPOSTAsGET-11]
|
||||
_ = x[AllowV1Registration-12]
|
||||
_ = x[StoreRevokerInfo-13]
|
||||
_ = x[RestrictRSAKeySizes-14]
|
||||
_ = x[FasterNewOrdersRateLimit-15]
|
||||
_ = x[ECDSAForAll-16]
|
||||
_ = x[ServeRenewalInfo-17]
|
||||
_ = x[GetAuthzReadOnly-18]
|
||||
_ = x[GetAuthzUseIndex-19]
|
||||
_ = x[CheckFailedAuthorizationsFirst-20]
|
||||
_ = x[AllowReRevocation-21]
|
||||
_ = x[MozRevocationReasons-22]
|
||||
}
|
||||
|
||||
const _FeatureFlag_name = "unusedPrecertificateRevocationStripDefaultSchemePortNonCFSSLSignerStoreIssuerInfoStreamlineOrderAndAuthzsV1DisableNewValidationsCAAValidationMethodsCAAAccountURIEnforceMultiVAMultiVAFullResultsMandatoryPOSTAsGETAllowV1RegistrationStoreRevokerInfoRestrictRSAKeySizesFasterNewOrdersRateLimitECDSAForAllServeRenewalInfoGetAuthzReadOnlyGetAuthzUseIndexCheckFailedAuthorizationsFirstAllowReRevocationMozRevocationReasons"
|
||||
|
||||
var _FeatureFlag_index = [...]uint16{0, 6, 30, 52, 66, 81, 105, 128, 148, 161, 175, 193, 211, 230, 246, 265, 289, 300, 316, 332, 348, 378, 395, 415}
|
||||
|
||||
func (i FeatureFlag) String() string {
|
||||
if i < 0 || i >= FeatureFlag(len(_FeatureFlag_index)-1) {
|
||||
return "FeatureFlag(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _FeatureFlag_name[_FeatureFlag_index[i]:_FeatureFlag_index[i+1]]
|
||||
}
|
158
vendor/github.com/letsencrypt/boulder/features/features.go
generated
vendored
Normal file
158
vendor/github.com/letsencrypt/boulder/features/features.go
generated
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
//go:generate stringer -type=FeatureFlag
|
||||
|
||||
package features
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type FeatureFlag int
|
||||
|
||||
const (
|
||||
unused FeatureFlag = iota // unused is used for testing
|
||||
// Deprecated features, these can be removed once stripped from production configs
|
||||
PrecertificateRevocation
|
||||
StripDefaultSchemePort
|
||||
NonCFSSLSigner
|
||||
StoreIssuerInfo
|
||||
StreamlineOrderAndAuthzs
|
||||
V1DisableNewValidations
|
||||
|
||||
// Currently in-use features
|
||||
// Check CAA and respect validationmethods parameter.
|
||||
CAAValidationMethods
|
||||
// Check CAA and respect accounturi parameter.
|
||||
CAAAccountURI
|
||||
// EnforceMultiVA causes the VA to block on remote VA PerformValidation
|
||||
// requests in order to make a valid/invalid decision with the results.
|
||||
EnforceMultiVA
|
||||
// MultiVAFullResults will cause the main VA to wait for all of the remote VA
|
||||
// results, not just the threshold required to make a decision.
|
||||
MultiVAFullResults
|
||||
// MandatoryPOSTAsGET forbids legacy unauthenticated GET requests for ACME
|
||||
// resources.
|
||||
MandatoryPOSTAsGET
|
||||
// Allow creation of new registrations in ACMEv1.
|
||||
AllowV1Registration
|
||||
// StoreRevokerInfo enables storage of the revoker and a bool indicating if the row
|
||||
// was checked for extant unrevoked certificates in the blockedKeys table.
|
||||
StoreRevokerInfo
|
||||
// RestrictRSAKeySizes enables restriction of acceptable RSA public key moduli to
|
||||
// the common sizes (2048, 3072, and 4096 bits).
|
||||
RestrictRSAKeySizes
|
||||
// FasterNewOrdersRateLimit enables use of a separate table for counting the
|
||||
// new orders rate limit.
|
||||
FasterNewOrdersRateLimit
|
||||
// ECDSAForAll enables all accounts, regardless of their presence in the CA's
|
||||
// ecdsaAllowedAccounts config value, to get issuance from ECDSA issuers.
|
||||
ECDSAForAll
|
||||
// ServeRenewalInfo exposes the renewalInfo endpoint in the directory and for
|
||||
// GET requests. WARNING: This feature is a draft and highly unstable.
|
||||
ServeRenewalInfo
|
||||
// GetAuthzReadOnly causes the SA to use its read-only database connection
|
||||
// (which is generally pointed at a replica rather than the primary db) when
|
||||
// querying the authz2 table.
|
||||
GetAuthzReadOnly
|
||||
// GetAuthzUseIndex causes the SA to use to add a USE INDEX hint when it
|
||||
// queries the authz2 table.
|
||||
GetAuthzUseIndex
|
||||
// Check the failed authorization limit before doing authz reuse.
|
||||
CheckFailedAuthorizationsFirst
|
||||
// AllowReRevocation causes the RA to allow the revocation reason of an
|
||||
// already-revoked certificate to be updated to `keyCompromise` from any
|
||||
// other reason if that compromise is demonstrated by making the second
|
||||
// revocation request signed by the certificate keypair.
|
||||
AllowReRevocation
|
||||
// MozRevocationReasons causes the RA to enforce the following upcoming
|
||||
// Mozilla policies regarding revocation:
|
||||
// - A subscriber can request that their certificate be revoked with reason
|
||||
// keyCompromise, even without demonstrating that compromise at the time.
|
||||
// However, the cert's pubkey will not be added to the blocked keys list.
|
||||
// - When an applicant other than the original subscriber requests that a
|
||||
// certificate be revoked (by demonstrating control over all names in it),
|
||||
// the cert will be revoked with reason cessationOfOperation, regardless of
|
||||
// what revocation reason they request.
|
||||
// - When anyone requests that a certificate be revoked by signing the request
|
||||
// with the certificate's keypair, the cert will be revoked with reason
|
||||
// keyCompromise, regardless of what revocation reason they request.
|
||||
MozRevocationReasons
|
||||
)
|
||||
|
||||
// List of features and their default value, protected by fMu
|
||||
var features = map[FeatureFlag]bool{
|
||||
unused: false,
|
||||
CAAValidationMethods: false,
|
||||
CAAAccountURI: false,
|
||||
EnforceMultiVA: false,
|
||||
MultiVAFullResults: false,
|
||||
MandatoryPOSTAsGET: false,
|
||||
AllowV1Registration: true,
|
||||
V1DisableNewValidations: false,
|
||||
PrecertificateRevocation: false,
|
||||
StripDefaultSchemePort: false,
|
||||
StoreIssuerInfo: false,
|
||||
StoreRevokerInfo: false,
|
||||
RestrictRSAKeySizes: false,
|
||||
FasterNewOrdersRateLimit: false,
|
||||
NonCFSSLSigner: false,
|
||||
ECDSAForAll: false,
|
||||
StreamlineOrderAndAuthzs: false,
|
||||
ServeRenewalInfo: false,
|
||||
GetAuthzReadOnly: false,
|
||||
GetAuthzUseIndex: false,
|
||||
CheckFailedAuthorizationsFirst: false,
|
||||
AllowReRevocation: false,
|
||||
MozRevocationReasons: false,
|
||||
}
|
||||
|
||||
var fMu = new(sync.RWMutex)
|
||||
|
||||
var initial = map[FeatureFlag]bool{}
|
||||
|
||||
var nameToFeature = make(map[string]FeatureFlag, len(features))
|
||||
|
||||
func init() {
|
||||
for f, v := range features {
|
||||
nameToFeature[f.String()] = f
|
||||
initial[f] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Set accepts a list of features and whether they should
|
||||
// be enabled or disabled, it will return a error if passed
|
||||
// a feature name that it doesn't know
|
||||
func Set(featureSet map[string]bool) error {
|
||||
fMu.Lock()
|
||||
defer fMu.Unlock()
|
||||
for n, v := range featureSet {
|
||||
f, present := nameToFeature[n]
|
||||
if !present {
|
||||
return fmt.Errorf("feature '%s' doesn't exist", n)
|
||||
}
|
||||
features[f] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Enabled returns true if the feature is enabled or false
|
||||
// if it isn't, it will panic if passed a feature that it
|
||||
// doesn't know.
|
||||
func Enabled(n FeatureFlag) bool {
|
||||
fMu.RLock()
|
||||
defer fMu.RUnlock()
|
||||
v, present := features[n]
|
||||
if !present {
|
||||
panic(fmt.Sprintf("feature '%s' doesn't exist", n.String()))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Reset resets the features to their initial state
|
||||
func Reset() {
|
||||
fMu.Lock()
|
||||
defer fMu.Unlock()
|
||||
for k, v := range initial {
|
||||
features[k] = v
|
||||
}
|
||||
}
|
98
vendor/github.com/letsencrypt/boulder/goodkey/blocked.go
generated
vendored
Normal file
98
vendor/github.com/letsencrypt/boulder/goodkey/blocked.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
package goodkey
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// blockedKeys is a type for maintaining a map of SHA256 hashes
|
||||
// of SubjectPublicKeyInfo's that should be considered blocked.
|
||||
// blockedKeys are created by using loadBlockedKeysList.
|
||||
type blockedKeys map[core.Sha256Digest]bool
|
||||
|
||||
var ErrWrongDecodedSize = errors.New("not enough bytes decoded for sha256 hash")
|
||||
|
||||
// blocked checks if the given public key is considered administratively
|
||||
// blocked based on a SHA256 hash of the SubjectPublicKeyInfo.
|
||||
// Important: blocked should not be called except on a blockedKeys instance
|
||||
// returned from loadBlockedKeysList.
|
||||
// function should not be used until after `loadBlockedKeysList` has returned.
|
||||
func (b blockedKeys) blocked(key crypto.PublicKey) (bool, error) {
|
||||
hash, err := core.KeyDigest(key)
|
||||
if err != nil {
|
||||
// the bool result should be ignored when err is != nil but to be on the
|
||||
// paranoid side return true anyway so that a key we can't compute the
|
||||
// digest for will always be blocked even if a caller foolishly discards the
|
||||
// err result.
|
||||
return true, err
|
||||
}
|
||||
return b[hash], nil
|
||||
}
|
||||
|
||||
// loadBlockedKeysList creates a blockedKeys object that can be used to check if
|
||||
// a key is blocked. It creates a lookup map from a list of
|
||||
// SHA256 hashes of SubjectPublicKeyInfo's in the input YAML file
|
||||
// with the expected format:
|
||||
//
|
||||
// ```
|
||||
// blocked:
|
||||
// - cuwGhNNI6nfob5aqY90e7BleU6l7rfxku4X3UTJ3Z7M=
|
||||
// <snipped>
|
||||
// - Qebc1V3SkX3izkYRGNJilm9Bcuvf0oox4U2Rn+b4JOE=
|
||||
// ```
|
||||
//
|
||||
// If no hashes are found in the input YAML an error is returned.
|
||||
func loadBlockedKeysList(filename string) (*blockedKeys, error) {
|
||||
yamlBytes, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var list struct {
|
||||
BlockedHashes []string `yaml:"blocked"`
|
||||
BlockedHashesHex []string `yaml:"blockedHashesHex"`
|
||||
}
|
||||
err = yaml.Unmarshal(yamlBytes, &list)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(list.BlockedHashes) == 0 && len(list.BlockedHashesHex) == 0 {
|
||||
return nil, errors.New("no blocked hashes in YAML")
|
||||
}
|
||||
|
||||
blockedKeys := make(blockedKeys, len(list.BlockedHashes)+len(list.BlockedHashesHex))
|
||||
for _, b64Hash := range list.BlockedHashes {
|
||||
decoded, err := base64.StdEncoding.DecodeString(b64Hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(decoded) != sha256.Size {
|
||||
return nil, ErrWrongDecodedSize
|
||||
}
|
||||
var sha256Digest core.Sha256Digest
|
||||
copy(sha256Digest[:], decoded[0:sha256.Size])
|
||||
blockedKeys[sha256Digest] = true
|
||||
}
|
||||
for _, hexHash := range list.BlockedHashesHex {
|
||||
decoded, err := hex.DecodeString(hexHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(decoded) != sha256.Size {
|
||||
return nil, ErrWrongDecodedSize
|
||||
}
|
||||
var sha256Digest core.Sha256Digest
|
||||
copy(sha256Digest[:], decoded[0:sha256.Size])
|
||||
blockedKeys[sha256Digest] = true
|
||||
}
|
||||
return &blockedKeys, nil
|
||||
}
|
432
vendor/github.com/letsencrypt/boulder/goodkey/good_key.go
generated
vendored
Normal file
432
vendor/github.com/letsencrypt/boulder/goodkey/good_key.go
generated
vendored
Normal file
@@ -0,0 +1,432 @@
|
||||
package goodkey
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
berrors "github.com/letsencrypt/boulder/errors"
|
||||
"github.com/letsencrypt/boulder/features"
|
||||
sapb "github.com/letsencrypt/boulder/sa/proto"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/titanous/rocacheck"
|
||||
)
|
||||
|
||||
// To generate, run: primes 2 752 | tr '\n' ,
|
||||
var smallPrimeInts = []int64{
|
||||
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
|
||||
53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107,
|
||||
109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167,
|
||||
173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
|
||||
233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283,
|
||||
293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359,
|
||||
367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431,
|
||||
433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491,
|
||||
499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571,
|
||||
577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641,
|
||||
643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709,
|
||||
719, 727, 733, 739, 743, 751,
|
||||
}
|
||||
|
||||
// singleton defines the object of a Singleton pattern
|
||||
var (
|
||||
smallPrimesSingleton sync.Once
|
||||
smallPrimesProduct *big.Int
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
// WeakKeyFile is the path to a JSON file containing truncated modulus hashes
|
||||
// of known weak RSA keys. If this config value is empty, then RSA modulus
|
||||
// hash checking will be disabled.
|
||||
WeakKeyFile string
|
||||
// BlockedKeyFile is the path to a YAML file containing base64-encoded SHA256
|
||||
// hashes of PKIX Subject Public Keys that should be blocked. If this config
|
||||
// value is empty, then blocked key checking will be disabled.
|
||||
BlockedKeyFile string
|
||||
// FermatRounds is an integer number of rounds of Fermat's factorization
|
||||
// method that should be performed to attempt to detect keys whose modulus can
|
||||
// be trivially factored because the two factors are very close to each other.
|
||||
// If this config value is empty (0), no factorization will be attempted.
|
||||
FermatRounds int
|
||||
}
|
||||
|
||||
// ErrBadKey represents an error with a key. It is distinct from the various
|
||||
// ways in which an ACME request can have an erroneous key (BadPublicKeyError,
|
||||
// BadCSRError) because this library is used to check both JWS signing keys and
|
||||
// keys in CSRs.
|
||||
var ErrBadKey = errors.New("")
|
||||
|
||||
func badKey(msg string, args ...interface{}) error {
|
||||
return fmt.Errorf("%w%s", ErrBadKey, fmt.Errorf(msg, args...))
|
||||
}
|
||||
|
||||
// BlockedKeyCheckFunc is used to pass in the sa.BlockedKey method to KeyPolicy,
|
||||
// rather than storing a full sa.SQLStorageAuthority. This makes testing
|
||||
// significantly simpler.
|
||||
type BlockedKeyCheckFunc func(context.Context, *sapb.KeyBlockedRequest, ...grpc.CallOption) (*sapb.Exists, error)
|
||||
|
||||
// KeyPolicy determines which types of key may be used with various boulder
|
||||
// operations.
|
||||
type KeyPolicy struct {
|
||||
AllowRSA bool // Whether RSA keys should be allowed.
|
||||
AllowECDSANISTP256 bool // Whether ECDSA NISTP256 keys should be allowed.
|
||||
AllowECDSANISTP384 bool // Whether ECDSA NISTP384 keys should be allowed.
|
||||
weakRSAList *WeakRSAKeys
|
||||
blockedList *blockedKeys
|
||||
fermatRounds int
|
||||
dbCheck BlockedKeyCheckFunc
|
||||
}
|
||||
|
||||
// NewKeyPolicy returns a KeyPolicy that allows RSA, ECDSA256 and ECDSA384.
|
||||
// weakKeyFile contains the path to a JSON file containing truncated modulus
|
||||
// hashes of known weak RSA keys. If this argument is empty RSA modulus hash
|
||||
// checking will be disabled. blockedKeyFile contains the path to a YAML file
|
||||
// containing Base64 encoded SHA256 hashes of pkix subject public keys that
|
||||
// should be blocked. If this argument is empty then no blocked key checking is
|
||||
// performed.
|
||||
func NewKeyPolicy(config *Config, bkc BlockedKeyCheckFunc) (KeyPolicy, error) {
|
||||
kp := KeyPolicy{
|
||||
AllowRSA: true,
|
||||
AllowECDSANISTP256: true,
|
||||
AllowECDSANISTP384: true,
|
||||
dbCheck: bkc,
|
||||
}
|
||||
if config.WeakKeyFile != "" {
|
||||
keyList, err := LoadWeakRSASuffixes(config.WeakKeyFile)
|
||||
if err != nil {
|
||||
return KeyPolicy{}, err
|
||||
}
|
||||
kp.weakRSAList = keyList
|
||||
}
|
||||
if config.BlockedKeyFile != "" {
|
||||
blocked, err := loadBlockedKeysList(config.BlockedKeyFile)
|
||||
if err != nil {
|
||||
return KeyPolicy{}, err
|
||||
}
|
||||
kp.blockedList = blocked
|
||||
}
|
||||
if config.FermatRounds < 0 {
|
||||
return KeyPolicy{}, fmt.Errorf("Fermat factorization rounds cannot be negative: %d", config.FermatRounds)
|
||||
}
|
||||
kp.fermatRounds = config.FermatRounds
|
||||
return kp, nil
|
||||
}
|
||||
|
||||
// GoodKey returns true if the key is acceptable for both TLS use and account
|
||||
// key use (our requirements are the same for either one), according to basic
|
||||
// strength and algorithm checking. GoodKey only supports pointers: *rsa.PublicKey
|
||||
// and *ecdsa.PublicKey. It will reject non-pointer types.
|
||||
// TODO: Support JSONWebKeys once go-jose migration is done.
|
||||
func (policy *KeyPolicy) GoodKey(ctx context.Context, key crypto.PublicKey) error {
|
||||
// Early rejection of unacceptable key types to guard subsequent checks.
|
||||
switch t := key.(type) {
|
||||
case *rsa.PublicKey, *ecdsa.PublicKey:
|
||||
break
|
||||
default:
|
||||
return badKey("unsupported key type %T", t)
|
||||
}
|
||||
// If there is a blocked list configured then check if the public key is one
|
||||
// that has been administratively blocked.
|
||||
if policy.blockedList != nil {
|
||||
if blocked, err := policy.blockedList.blocked(key); err != nil {
|
||||
return berrors.InternalServerError("error checking blocklist for key: %v", key)
|
||||
} else if blocked {
|
||||
return badKey("public key is forbidden")
|
||||
}
|
||||
}
|
||||
if policy.dbCheck != nil {
|
||||
digest, err := core.KeyDigest(key)
|
||||
if err != nil {
|
||||
return badKey("%w", err)
|
||||
}
|
||||
exists, err := policy.dbCheck(ctx, &sapb.KeyBlockedRequest{KeyHash: digest[:]})
|
||||
if err != nil {
|
||||
return err
|
||||
} else if exists.Exists {
|
||||
return badKey("public key is forbidden")
|
||||
}
|
||||
}
|
||||
switch t := key.(type) {
|
||||
case *rsa.PublicKey:
|
||||
return policy.goodKeyRSA(t)
|
||||
case *ecdsa.PublicKey:
|
||||
return policy.goodKeyECDSA(t)
|
||||
default:
|
||||
return badKey("unsupported key type %T", key)
|
||||
}
|
||||
}
|
||||
|
||||
// GoodKeyECDSA determines if an ECDSA pubkey meets our requirements
|
||||
func (policy *KeyPolicy) goodKeyECDSA(key *ecdsa.PublicKey) (err error) {
|
||||
// Check the curve.
|
||||
//
|
||||
// The validity of the curve is an assumption for all following tests.
|
||||
err = policy.goodCurve(key.Curve)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Key validation routine adapted from NIST SP800-56A § 5.6.2.3.2.
|
||||
// <http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf>
|
||||
//
|
||||
// Assuming a prime field since a) we are only allowing such curves and b)
|
||||
// crypto/elliptic only supports prime curves. Where this assumption
|
||||
// simplifies the code below, it is explicitly stated and explained. If ever
|
||||
// adapting this code to support non-prime curves, refer to NIST SP800-56A §
|
||||
// 5.6.2.3.2 and adapt this code appropriately.
|
||||
params := key.Params()
|
||||
|
||||
// SP800-56A § 5.6.2.3.2 Step 1.
|
||||
// Partial check of the public key for an invalid range in the EC group:
|
||||
// Verify that key is not the point at infinity O.
|
||||
// This code assumes that the point at infinity is (0,0), which is the
|
||||
// case for all supported curves.
|
||||
if isPointAtInfinityNISTP(key.X, key.Y) {
|
||||
return badKey("key x, y must not be the point at infinity")
|
||||
}
|
||||
|
||||
// SP800-56A § 5.6.2.3.2 Step 2.
|
||||
// "Verify that x_Q and y_Q are integers in the interval [0,p-1] in the
|
||||
// case that q is an odd prime p, or that x_Q and y_Q are bit strings
|
||||
// of length m bits in the case that q = 2**m."
|
||||
//
|
||||
// Prove prime field: ASSUMED.
|
||||
// Prove q != 2: ASSUMED. (Curve parameter. No supported curve has q == 2.)
|
||||
// Prime field && q != 2 => q is an odd prime p
|
||||
// Therefore "verify that x, y are in [0, p-1]" satisfies step 2.
|
||||
//
|
||||
// Therefore verify that both x and y of the public key point have the unique
|
||||
// correct representation of an element in the underlying field by verifying
|
||||
// that x and y are integers in [0, p-1].
|
||||
if key.X.Sign() < 0 || key.Y.Sign() < 0 {
|
||||
return badKey("key x, y must not be negative")
|
||||
}
|
||||
|
||||
if key.X.Cmp(params.P) >= 0 || key.Y.Cmp(params.P) >= 0 {
|
||||
return badKey("key x, y must not exceed P-1")
|
||||
}
|
||||
|
||||
// SP800-56A § 5.6.2.3.2 Step 3.
|
||||
// "If q is an odd prime p, verify that (y_Q)**2 === (x_Q)***3 + a*x_Q + b (mod p).
|
||||
// If q = 2**m, verify that (y_Q)**2 + (x_Q)*(y_Q) == (x_Q)**3 + a*(x_Q)*2 + b in
|
||||
// the finite field of size 2**m.
|
||||
// (Ensures that the public key is on the correct elliptic curve.)"
|
||||
//
|
||||
// q is an odd prime p: proven/assumed above.
|
||||
// a = -3 for all supported curves.
|
||||
//
|
||||
// Therefore step 3 is satisfied simply by showing that
|
||||
// y**2 === x**3 - 3*x + B (mod P).
|
||||
//
|
||||
// This proves that the public key is on the correct elliptic curve.
|
||||
// But in practice, this test is provided by crypto/elliptic, so use that.
|
||||
if !key.Curve.IsOnCurve(key.X, key.Y) {
|
||||
return badKey("key point is not on the curve")
|
||||
}
|
||||
|
||||
// SP800-56A § 5.6.2.3.2 Step 4.
|
||||
// "Verify that n*Q == Ø.
|
||||
// (Ensures that the public key has the correct order. Along with check 1,
|
||||
// ensures that the public key is in the correct range in the correct EC
|
||||
// subgroup, that is, it is in the correct EC subgroup and is not the
|
||||
// identity element.)"
|
||||
//
|
||||
// Ensure that public key has the correct order:
|
||||
// verify that n*Q = Ø.
|
||||
//
|
||||
// n*Q = Ø iff n*Q is the point at infinity (see step 1).
|
||||
ox, oy := key.Curve.ScalarMult(key.X, key.Y, params.N.Bytes())
|
||||
if !isPointAtInfinityNISTP(ox, oy) {
|
||||
return badKey("public key does not have correct order")
|
||||
}
|
||||
|
||||
// End of SP800-56A § 5.6.2.3.2 Public Key Validation Routine.
|
||||
// Key is valid.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns true iff the point (x,y) on NIST P-256, NIST P-384 or NIST P-521 is
|
||||
// the point at infinity. These curves all have the same point at infinity
|
||||
// (0,0). This function must ONLY be used on points on curves verified to have
|
||||
// (0,0) as their point at infinity.
|
||||
func isPointAtInfinityNISTP(x, y *big.Int) bool {
|
||||
return x.Sign() == 0 && y.Sign() == 0
|
||||
}
|
||||
|
||||
// GoodCurve determines if an elliptic curve meets our requirements.
|
||||
func (policy *KeyPolicy) goodCurve(c elliptic.Curve) (err error) {
|
||||
// Simply use a whitelist for now.
|
||||
params := c.Params()
|
||||
switch {
|
||||
case policy.AllowECDSANISTP256 && params == elliptic.P256().Params():
|
||||
return nil
|
||||
case policy.AllowECDSANISTP384 && params == elliptic.P384().Params():
|
||||
return nil
|
||||
default:
|
||||
return badKey("ECDSA curve %v not allowed", params.Name)
|
||||
}
|
||||
}
|
||||
|
||||
var acceptableRSAKeySizes = map[int]bool{
|
||||
2048: true,
|
||||
3072: true,
|
||||
4096: true,
|
||||
}
|
||||
|
||||
// GoodKeyRSA determines if a RSA pubkey meets our requirements
|
||||
func (policy *KeyPolicy) goodKeyRSA(key *rsa.PublicKey) (err error) {
|
||||
if !policy.AllowRSA {
|
||||
return badKey("RSA keys are not allowed")
|
||||
}
|
||||
if policy.weakRSAList != nil && policy.weakRSAList.Known(key) {
|
||||
return badKey("key is on a known weak RSA key list")
|
||||
}
|
||||
|
||||
// Baseline Requirements Appendix A
|
||||
// Modulus must be >= 2048 bits and <= 4096 bits
|
||||
modulus := key.N
|
||||
modulusBitLen := modulus.BitLen()
|
||||
if features.Enabled(features.RestrictRSAKeySizes) {
|
||||
if !acceptableRSAKeySizes[modulusBitLen] {
|
||||
return badKey("key size not supported: %d", modulusBitLen)
|
||||
}
|
||||
} else {
|
||||
const maxKeySize = 4096
|
||||
if modulusBitLen < 2048 {
|
||||
return badKey("key too small: %d", modulusBitLen)
|
||||
}
|
||||
if modulusBitLen > maxKeySize {
|
||||
return badKey("key too large: %d > %d", modulusBitLen, maxKeySize)
|
||||
}
|
||||
// Bit lengths that are not a multiple of 8 may cause problems on some
|
||||
// client implementations.
|
||||
if modulusBitLen%8 != 0 {
|
||||
return badKey("key length wasn't a multiple of 8: %d", modulusBitLen)
|
||||
}
|
||||
}
|
||||
|
||||
// Rather than support arbitrary exponents, which significantly increases
|
||||
// the size of the key space we allow, we restrict E to the defacto standard
|
||||
// RSA exponent 65537. There is no specific standards document that specifies
|
||||
// 65537 as the 'best' exponent, but ITU X.509 Annex C suggests there are
|
||||
// notable merits for using it if using a fixed exponent.
|
||||
//
|
||||
// The CABF Baseline Requirements state:
|
||||
// The CA SHALL confirm that the value of the public exponent is an
|
||||
// odd number equal to 3 or more. Additionally, the public exponent
|
||||
// SHOULD be in the range between 2^16 + 1 and 2^256-1.
|
||||
//
|
||||
// By only allowing one exponent, which fits these constraints, we satisfy
|
||||
// these requirements.
|
||||
if key.E != 65537 {
|
||||
return badKey("key exponent must be 65537")
|
||||
}
|
||||
|
||||
// The modulus SHOULD also have the following characteristics: an odd
|
||||
// number, not the power of a prime, and have no factors smaller than 752.
|
||||
// TODO: We don't yet check for "power of a prime."
|
||||
if checkSmallPrimes(modulus) {
|
||||
return badKey("key divisible by small prime")
|
||||
}
|
||||
// Check for weak keys generated by Infineon hardware
|
||||
// (see https://crocs.fi.muni.cz/public/papers/rsa_ccs17)
|
||||
if rocacheck.IsWeak(key) {
|
||||
return badKey("key generated by vulnerable Infineon-based hardware")
|
||||
}
|
||||
// Check if the key can be easily factored via Fermat's factorization method.
|
||||
if policy.fermatRounds > 0 {
|
||||
err := checkPrimeFactorsTooClose(modulus, policy.fermatRounds)
|
||||
if err != nil {
|
||||
return badKey("key generated with factors too close together: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns true iff integer i is divisible by any of the primes in smallPrimes.
|
||||
//
|
||||
// Short circuits; execution time is dependent on i. Do not use this on secret
|
||||
// values.
|
||||
//
|
||||
// Rather than checking each prime individually (invoking Mod on each),
|
||||
// multiply the primes together and let GCD do our work for us: if the
|
||||
// GCD between <key> and <product of primes> is not one, we know we have
|
||||
// a bad key. This is substantially faster than checking each prime
|
||||
// individually.
|
||||
func checkSmallPrimes(i *big.Int) bool {
|
||||
smallPrimesSingleton.Do(func() {
|
||||
smallPrimesProduct = big.NewInt(1)
|
||||
for _, prime := range smallPrimeInts {
|
||||
smallPrimesProduct.Mul(smallPrimesProduct, big.NewInt(prime))
|
||||
}
|
||||
})
|
||||
|
||||
// When the GCD is 1, i and smallPrimesProduct are coprime, meaning they
|
||||
// share no common factors. When the GCD is not one, it is the product of
|
||||
// all common factors, meaning we've identified at least one small prime
|
||||
// which invalidates i as a valid key.
|
||||
|
||||
var result big.Int
|
||||
result.GCD(nil, nil, i, smallPrimesProduct)
|
||||
return result.Cmp(big.NewInt(1)) != 0
|
||||
}
|
||||
|
||||
// Returns an error if the modulus n is able to be factored into primes p and q
|
||||
// via Fermat's factorization method. This method relies on the two primes being
|
||||
// very close together, which means that they were almost certainly not picked
|
||||
// independently from a uniform random distribution. Basically, if we can factor
|
||||
// the key this easily, so can anyone else.
|
||||
func checkPrimeFactorsTooClose(n *big.Int, rounds int) error {
|
||||
// Pre-allocate some big numbers that we'll use a lot down below.
|
||||
one := big.NewInt(1)
|
||||
bb := new(big.Int)
|
||||
|
||||
// Any odd integer is equal to a difference of squares of integers:
|
||||
// n = a^2 - b^2 = (a + b)(a - b)
|
||||
// Any RSA public key modulus is equal to a product of two primes:
|
||||
// n = pq
|
||||
// Here we try to find values for a and b, since doing so also gives us the
|
||||
// prime factors p = (a + b) and q = (a - b).
|
||||
|
||||
// We start with a close to the square root of the modulus n, to start with
|
||||
// two candidate prime factors that are as close together as possible and
|
||||
// work our way out from there. Specifically, we set a = ceil(sqrt(n)), the
|
||||
// first integer greater than the square root of n. Unfortunately, big.Int's
|
||||
// built-in square root function takes the floor, so we have to add one to get
|
||||
// the ceil.
|
||||
a := new(big.Int)
|
||||
a.Sqrt(n).Add(a, one)
|
||||
|
||||
// We calculate b2 to see if it is a perfect square (i.e. b^2), and therefore
|
||||
// b is an integer. Specifically, b2 = a^2 - n.
|
||||
b2 := new(big.Int)
|
||||
b2.Mul(a, a).Sub(b2, n)
|
||||
|
||||
for i := 0; i < rounds; i++ {
|
||||
// To see if b2 is a perfect square, we take its square root, square that,
|
||||
// and check to see if we got the same result back.
|
||||
bb.Sqrt(b2).Mul(bb, bb)
|
||||
if b2.Cmp(bb) == 0 {
|
||||
// b2 is a perfect square, so we've found integer values of a and b,
|
||||
// and can easily compute p and q as their sum and difference.
|
||||
bb.Sqrt(bb)
|
||||
p := new(big.Int).Add(a, bb)
|
||||
q := new(big.Int).Sub(a, bb)
|
||||
return fmt.Errorf("public modulus n = pq factored into p: %s; q: %s", p, q)
|
||||
}
|
||||
|
||||
// Set up the next iteration by incrementing a by one and recalculating b2.
|
||||
a.Add(a, one)
|
||||
b2.Mul(a, a).Sub(b2, n)
|
||||
}
|
||||
return nil
|
||||
}
|
66
vendor/github.com/letsencrypt/boulder/goodkey/weak.go
generated
vendored
Normal file
66
vendor/github.com/letsencrypt/boulder/goodkey/weak.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
package goodkey
|
||||
|
||||
// This file defines a basic method for testing if a given RSA public key is on one of
|
||||
// the Debian weak key lists and is therefore considered compromised. Instead of
|
||||
// directly loading the hash suffixes from the individual lists we flatten them all
|
||||
// into a single JSON list using cmd/weak-key-flatten for ease of use.
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
type truncatedHash [10]byte
|
||||
|
||||
type WeakRSAKeys struct {
|
||||
suffixes map[truncatedHash]struct{}
|
||||
}
|
||||
|
||||
func LoadWeakRSASuffixes(path string) (*WeakRSAKeys, error) {
|
||||
f, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var suffixList []string
|
||||
err = json.Unmarshal(f, &suffixList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wk := &WeakRSAKeys{suffixes: make(map[truncatedHash]struct{})}
|
||||
for _, suffix := range suffixList {
|
||||
err := wk.addSuffix(suffix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return wk, nil
|
||||
}
|
||||
|
||||
func (wk *WeakRSAKeys) addSuffix(str string) error {
|
||||
var suffix truncatedHash
|
||||
decoded, err := hex.DecodeString(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(decoded) != 10 {
|
||||
return fmt.Errorf("unexpected suffix length of %d", len(decoded))
|
||||
}
|
||||
copy(suffix[:], decoded)
|
||||
wk.suffixes[suffix] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wk *WeakRSAKeys) Known(key *rsa.PublicKey) bool {
|
||||
// Hash input is in the format "Modulus={upper-case hex of modulus}\n"
|
||||
hash := sha1.Sum([]byte(fmt.Sprintf("Modulus=%X\n", key.N.Bytes())))
|
||||
var suffix truncatedHash
|
||||
copy(suffix[:], hash[10:])
|
||||
_, present := wk.suffixes[suffix]
|
||||
return present
|
||||
}
|
32
vendor/github.com/letsencrypt/boulder/identifier/identifier.go
generated
vendored
Normal file
32
vendor/github.com/letsencrypt/boulder/identifier/identifier.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// The identifier package defines types for RFC 8555 ACME identifiers.
|
||||
package identifier
|
||||
|
||||
// IdentifierType is a named string type for registered ACME identifier types.
|
||||
// See https://tools.ietf.org/html/rfc8555#section-9.7.7
|
||||
type IdentifierType string
|
||||
|
||||
const (
|
||||
// DNS is specified in RFC 8555 for DNS type identifiers.
|
||||
DNS = IdentifierType("dns")
|
||||
)
|
||||
|
||||
// ACMEIdentifier is a struct encoding an identifier that can be validated. The
|
||||
// protocol allows for different types of identifier to be supported (DNS
|
||||
// names, IP addresses, etc.), but currently we only support RFC 8555 DNS type
|
||||
// identifiers for domain names.
|
||||
type ACMEIdentifier struct {
|
||||
// Type is the registered IdentifierType of the identifier.
|
||||
Type IdentifierType `json:"type"`
|
||||
// Value is the value of the identifier. For a DNS type identifier it is
|
||||
// a domain name.
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// DNSIdentifier is a convenience function for creating an ACMEIdentifier with
|
||||
// Type DNS for a given domain name.
|
||||
func DNSIdentifier(domain string) ACMEIdentifier {
|
||||
return ACMEIdentifier{
|
||||
Type: DNS,
|
||||
Value: domain,
|
||||
}
|
||||
}
|
349
vendor/github.com/letsencrypt/boulder/probs/probs.go
generated
vendored
Normal file
349
vendor/github.com/letsencrypt/boulder/probs/probs.go
generated
vendored
Normal file
@@ -0,0 +1,349 @@
|
||||
package probs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/letsencrypt/boulder/identifier"
|
||||
)
|
||||
|
||||
// Error types that can be used in ACME payloads
|
||||
const (
|
||||
ConnectionProblem = ProblemType("connection")
|
||||
MalformedProblem = ProblemType("malformed")
|
||||
ServerInternalProblem = ProblemType("serverInternal")
|
||||
TLSProblem = ProblemType("tls")
|
||||
UnauthorizedProblem = ProblemType("unauthorized")
|
||||
RateLimitedProblem = ProblemType("rateLimited")
|
||||
BadNonceProblem = ProblemType("badNonce")
|
||||
InvalidEmailProblem = ProblemType("invalidEmail")
|
||||
RejectedIdentifierProblem = ProblemType("rejectedIdentifier")
|
||||
AccountDoesNotExistProblem = ProblemType("accountDoesNotExist")
|
||||
CAAProblem = ProblemType("caa")
|
||||
DNSProblem = ProblemType("dns")
|
||||
AlreadyRevokedProblem = ProblemType("alreadyRevoked")
|
||||
OrderNotReadyProblem = ProblemType("orderNotReady")
|
||||
BadSignatureAlgorithmProblem = ProblemType("badSignatureAlgorithm")
|
||||
BadPublicKeyProblem = ProblemType("badPublicKey")
|
||||
BadRevocationReasonProblem = ProblemType("badRevocationReason")
|
||||
BadCSRProblem = ProblemType("badCSR")
|
||||
|
||||
V1ErrorNS = "urn:acme:error:"
|
||||
V2ErrorNS = "urn:ietf:params:acme:error:"
|
||||
)
|
||||
|
||||
// ProblemType defines the error types in the ACME protocol
|
||||
type ProblemType string
|
||||
|
||||
// ProblemDetails objects represent problem documents
|
||||
// https://tools.ietf.org/html/draft-ietf-appsawg-http-problem-00
|
||||
type ProblemDetails struct {
|
||||
Type ProblemType `json:"type,omitempty"`
|
||||
Detail string `json:"detail,omitempty"`
|
||||
// HTTPStatus is the HTTP status code the ProblemDetails should probably be sent
|
||||
// as.
|
||||
HTTPStatus int `json:"status,omitempty"`
|
||||
// SubProblems are optional additional per-identifier problems. See
|
||||
// RFC 8555 Section 6.7.1: https://tools.ietf.org/html/rfc8555#section-6.7.1
|
||||
SubProblems []SubProblemDetails `json:"subproblems,omitempty"`
|
||||
}
|
||||
|
||||
// SubProblemDetails represents sub-problems specific to an identifier that are
|
||||
// related to a top-level ProblemDetails.
|
||||
// See RFC 8555 Section 6.7.1: https://tools.ietf.org/html/rfc8555#section-6.7.1
|
||||
type SubProblemDetails struct {
|
||||
ProblemDetails
|
||||
Identifier identifier.ACMEIdentifier `json:"identifier"`
|
||||
}
|
||||
|
||||
func (pd *ProblemDetails) Error() string {
|
||||
return fmt.Sprintf("%s :: %s", pd.Type, pd.Detail)
|
||||
}
|
||||
|
||||
// WithSubProblems returns a new ProblemsDetails instance created by adding the
|
||||
// provided subProbs to the existing ProblemsDetail.
|
||||
func (pd *ProblemDetails) WithSubProblems(subProbs []SubProblemDetails) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: pd.Type,
|
||||
Detail: pd.Detail,
|
||||
HTTPStatus: pd.HTTPStatus,
|
||||
SubProblems: append(pd.SubProblems, subProbs...),
|
||||
}
|
||||
}
|
||||
|
||||
// statusTooManyRequests is the HTTP status code meant for rate limiting
|
||||
// errors. It's not currently in the net/http library so we add it here.
|
||||
const statusTooManyRequests = 429
|
||||
|
||||
// ProblemDetailsToStatusCode inspects the given ProblemDetails to figure out
|
||||
// what HTTP status code it should represent. It should only be used by the WFE
|
||||
// but is included in this package because of its reliance on ProblemTypes.
|
||||
func ProblemDetailsToStatusCode(prob *ProblemDetails) int {
|
||||
if prob.HTTPStatus != 0 {
|
||||
return prob.HTTPStatus
|
||||
}
|
||||
switch prob.Type {
|
||||
case
|
||||
ConnectionProblem,
|
||||
MalformedProblem,
|
||||
BadSignatureAlgorithmProblem,
|
||||
BadPublicKeyProblem,
|
||||
TLSProblem,
|
||||
BadNonceProblem,
|
||||
InvalidEmailProblem,
|
||||
RejectedIdentifierProblem,
|
||||
AccountDoesNotExistProblem,
|
||||
BadRevocationReasonProblem:
|
||||
return http.StatusBadRequest
|
||||
case ServerInternalProblem:
|
||||
return http.StatusInternalServerError
|
||||
case
|
||||
UnauthorizedProblem,
|
||||
CAAProblem:
|
||||
return http.StatusForbidden
|
||||
case RateLimitedProblem:
|
||||
return statusTooManyRequests
|
||||
default:
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
}
|
||||
|
||||
// BadNonce returns a ProblemDetails with a BadNonceProblem and a 400 Bad
|
||||
// Request status code.
|
||||
func BadNonce(detail string) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: BadNonceProblem,
|
||||
Detail: detail,
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// RejectedIdentifier returns a ProblemDetails with a RejectedIdentifierProblem and a 400 Bad
|
||||
// Request status code.
|
||||
func RejectedIdentifier(detail string) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: RejectedIdentifierProblem,
|
||||
Detail: detail,
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// Conflict returns a ProblemDetails with a MalformedProblem and a 409 Conflict
|
||||
// status code.
|
||||
func Conflict(detail string) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: MalformedProblem,
|
||||
Detail: detail,
|
||||
HTTPStatus: http.StatusConflict,
|
||||
}
|
||||
}
|
||||
|
||||
// AlreadyRevoked returns a ProblemDetails with a AlreadyRevokedProblem and a 400 Bad
|
||||
// Request status code.
|
||||
func AlreadyRevoked(detail string, a ...interface{}) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: AlreadyRevokedProblem,
|
||||
Detail: fmt.Sprintf(detail, a...),
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// Malformed returns a ProblemDetails with a MalformedProblem and a 400 Bad
|
||||
// Request status code.
|
||||
func Malformed(detail string, args ...interface{}) *ProblemDetails {
|
||||
if len(args) > 0 {
|
||||
detail = fmt.Sprintf(detail, args...)
|
||||
}
|
||||
return &ProblemDetails{
|
||||
Type: MalformedProblem,
|
||||
Detail: detail,
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// Canceled returns a ProblemDetails with a MalformedProblem and a 408 Request
|
||||
// Timeout status code.
|
||||
func Canceled(detail string, args ...interface{}) *ProblemDetails {
|
||||
if len(args) > 0 {
|
||||
detail = fmt.Sprintf(detail, args...)
|
||||
}
|
||||
return &ProblemDetails{
|
||||
Type: MalformedProblem,
|
||||
Detail: detail,
|
||||
HTTPStatus: http.StatusRequestTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
// BadSignatureAlgorithm returns a ProblemDetails with a BadSignatureAlgorithmProblem
|
||||
// and a 400 Bad Request status code.
|
||||
func BadSignatureAlgorithm(detail string, a ...interface{}) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: BadSignatureAlgorithmProblem,
|
||||
Detail: fmt.Sprintf(detail, a...),
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// BadPublicKey returns a ProblemDetails with a BadPublicKeyProblem and a 400 Bad
|
||||
// Request status code.
|
||||
func BadPublicKey(detail string, a ...interface{}) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: BadPublicKeyProblem,
|
||||
Detail: fmt.Sprintf(detail, a...),
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// NotFound returns a ProblemDetails with a MalformedProblem and a 404 Not Found
|
||||
// status code.
|
||||
func NotFound(detail string) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: MalformedProblem,
|
||||
Detail: detail,
|
||||
HTTPStatus: http.StatusNotFound,
|
||||
}
|
||||
}
|
||||
|
||||
// ServerInternal returns a ProblemDetails with a ServerInternalProblem and a
|
||||
// 500 Internal Server Failure status code.
|
||||
func ServerInternal(detail string) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: ServerInternalProblem,
|
||||
Detail: detail,
|
||||
HTTPStatus: http.StatusInternalServerError,
|
||||
}
|
||||
}
|
||||
|
||||
// Unauthorized returns a ProblemDetails with an UnauthorizedProblem and a 403
|
||||
// Forbidden status code.
|
||||
func Unauthorized(detail string) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: UnauthorizedProblem,
|
||||
Detail: detail,
|
||||
HTTPStatus: http.StatusForbidden,
|
||||
}
|
||||
}
|
||||
|
||||
// MethodNotAllowed returns a ProblemDetails representing a disallowed HTTP
|
||||
// method error.
|
||||
func MethodNotAllowed() *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: MalformedProblem,
|
||||
Detail: "Method not allowed",
|
||||
HTTPStatus: http.StatusMethodNotAllowed,
|
||||
}
|
||||
}
|
||||
|
||||
// ContentLengthRequired returns a ProblemDetails representing a missing
|
||||
// Content-Length header error
|
||||
func ContentLengthRequired() *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: MalformedProblem,
|
||||
Detail: "missing Content-Length header",
|
||||
HTTPStatus: http.StatusLengthRequired,
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidContentType returns a ProblemDetails suitable for a missing
|
||||
// ContentType header, or an incorrect ContentType header
|
||||
func InvalidContentType(detail string) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: MalformedProblem,
|
||||
Detail: detail,
|
||||
HTTPStatus: http.StatusUnsupportedMediaType,
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidEmail returns a ProblemDetails representing an invalid email address
|
||||
// error
|
||||
func InvalidEmail(detail string) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: InvalidEmailProblem,
|
||||
Detail: detail,
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// ConnectionFailure returns a ProblemDetails representing a ConnectionProblem
|
||||
// error
|
||||
func ConnectionFailure(detail string) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: ConnectionProblem,
|
||||
Detail: detail,
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// RateLimited returns a ProblemDetails representing a RateLimitedProblem error
|
||||
func RateLimited(detail string) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: RateLimitedProblem,
|
||||
Detail: detail,
|
||||
HTTPStatus: statusTooManyRequests,
|
||||
}
|
||||
}
|
||||
|
||||
// TLSError returns a ProblemDetails representing a TLSProblem error
|
||||
func TLSError(detail string) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: TLSProblem,
|
||||
Detail: detail,
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// AccountDoesNotExist returns a ProblemDetails representing an
|
||||
// AccountDoesNotExistProblem error
|
||||
func AccountDoesNotExist(detail string) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: AccountDoesNotExistProblem,
|
||||
Detail: detail,
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// CAA returns a ProblemDetails representing a CAAProblem
|
||||
func CAA(detail string) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: CAAProblem,
|
||||
Detail: detail,
|
||||
HTTPStatus: http.StatusForbidden,
|
||||
}
|
||||
}
|
||||
|
||||
// DNS returns a ProblemDetails representing a DNSProblem
|
||||
func DNS(detail string) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: DNSProblem,
|
||||
Detail: detail,
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// OrderNotReady returns a ProblemDetails representing a OrderNotReadyProblem
|
||||
func OrderNotReady(detail string, a ...interface{}) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: OrderNotReadyProblem,
|
||||
Detail: fmt.Sprintf(detail, a...),
|
||||
HTTPStatus: http.StatusForbidden,
|
||||
}
|
||||
}
|
||||
|
||||
// BadRevocationReason returns a ProblemDetails representing
|
||||
// a BadRevocationReasonProblem
|
||||
func BadRevocationReason(detail string, a ...interface{}) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: BadRevocationReasonProblem,
|
||||
Detail: fmt.Sprintf(detail, a...),
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// BadCSR returns a ProblemDetails representing a BadCSRProblem.
|
||||
func BadCSR(detail string, a ...interface{}) *ProblemDetails {
|
||||
return &ProblemDetails{
|
||||
Type: BadCSRProblem,
|
||||
Detail: fmt.Sprintf(detail, a...),
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
}
|
||||
}
|
74
vendor/github.com/letsencrypt/boulder/revocation/reasons.go
generated
vendored
Normal file
74
vendor/github.com/letsencrypt/boulder/revocation/reasons.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
package revocation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/ocsp"
|
||||
)
|
||||
|
||||
// Reason is used to specify a certificate revocation reason
|
||||
type Reason int
|
||||
|
||||
// ReasonToString provides a map from reason code to string
|
||||
var ReasonToString = map[Reason]string{
|
||||
ocsp.Unspecified: "unspecified",
|
||||
ocsp.KeyCompromise: "keyCompromise",
|
||||
ocsp.CACompromise: "cACompromise",
|
||||
ocsp.AffiliationChanged: "affiliationChanged",
|
||||
ocsp.Superseded: "superseded",
|
||||
ocsp.CessationOfOperation: "cessationOfOperation",
|
||||
ocsp.CertificateHold: "certificateHold",
|
||||
// 7 is unused
|
||||
ocsp.RemoveFromCRL: "removeFromCRL",
|
||||
ocsp.PrivilegeWithdrawn: "privilegeWithdrawn",
|
||||
ocsp.AACompromise: "aAcompromise",
|
||||
}
|
||||
|
||||
// UserAllowedReasons contains the subset of Reasons which users are
|
||||
// allowed to use
|
||||
var UserAllowedReasons = map[Reason]struct{}{
|
||||
ocsp.Unspecified: {},
|
||||
ocsp.KeyCompromise: {},
|
||||
ocsp.AffiliationChanged: {},
|
||||
ocsp.Superseded: {},
|
||||
ocsp.CessationOfOperation: {},
|
||||
}
|
||||
|
||||
// AdminAllowedReasons contains the subset of Reasons which admins are allowed
|
||||
// to use. Reasons not found here will soon be forbidden from appearing in CRLs
|
||||
// or OCSP responses by root programs.
|
||||
var AdminAllowedReasons = map[Reason]struct{}{
|
||||
ocsp.Unspecified: {},
|
||||
ocsp.KeyCompromise: {},
|
||||
ocsp.AffiliationChanged: {},
|
||||
ocsp.Superseded: {},
|
||||
ocsp.CessationOfOperation: {},
|
||||
ocsp.PrivilegeWithdrawn: {},
|
||||
}
|
||||
|
||||
// UserAllowedReasonsMessage contains a string describing a list of user allowed
|
||||
// revocation reasons. This is useful when a revocation is rejected because it
|
||||
// is not a valid user supplied reason and the allowed values must be
|
||||
// communicated. This variable is populated during package initialization.
|
||||
var UserAllowedReasonsMessage = ""
|
||||
|
||||
func init() {
|
||||
// Build a slice of ints from the allowed reason codes.
|
||||
// We want a slice because iterating `UserAllowedReasons` will change order
|
||||
// and make the message unpredictable and cumbersome for unit testing.
|
||||
// We use []ints instead of []Reason to use `sort.Ints` without fuss.
|
||||
var allowed []int
|
||||
for reason := range UserAllowedReasons {
|
||||
allowed = append(allowed, int(reason))
|
||||
}
|
||||
sort.Ints(allowed)
|
||||
|
||||
var reasonStrings []string
|
||||
for _, reason := range allowed {
|
||||
reasonStrings = append(reasonStrings, fmt.Sprintf("%s (%d)",
|
||||
ReasonToString[Reason(reason)], reason))
|
||||
}
|
||||
UserAllowedReasonsMessage = strings.Join(reasonStrings, ", ")
|
||||
}
|
3449
vendor/github.com/letsencrypt/boulder/sa/proto/sa.pb.go
generated
vendored
Normal file
3449
vendor/github.com/letsencrypt/boulder/sa/proto/sa.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
272
vendor/github.com/letsencrypt/boulder/sa/proto/sa.proto
generated
vendored
Normal file
272
vendor/github.com/letsencrypt/boulder/sa/proto/sa.proto
generated
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package sa;
|
||||
option go_package = "github.com/letsencrypt/boulder/sa/proto";
|
||||
|
||||
import "core/proto/core.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
|
||||
service StorageAuthority {
|
||||
// Getters
|
||||
rpc GetRegistration(RegistrationID) returns (core.Registration) {}
|
||||
rpc GetRegistrationByKey(JSONWebKey) returns (core.Registration) {}
|
||||
rpc GetSerialMetadata(Serial) returns (SerialMetadata) {}
|
||||
rpc GetCertificate(Serial) returns (core.Certificate) {}
|
||||
rpc GetPrecertificate(Serial) returns (core.Certificate) {}
|
||||
rpc GetCertificateStatus(Serial) returns (core.CertificateStatus) {}
|
||||
rpc CountCertificatesByNames(CountCertificatesByNamesRequest) returns (CountByNames) {}
|
||||
rpc CountRegistrationsByIP(CountRegistrationsByIPRequest) returns (Count) {}
|
||||
rpc CountRegistrationsByIPRange(CountRegistrationsByIPRequest) returns (Count) {}
|
||||
rpc CountOrders(CountOrdersRequest) returns (Count) {}
|
||||
// Return a count of authorizations with status "invalid" that belong to
|
||||
// a given registration ID and expire in the given time range.
|
||||
rpc CountFQDNSets(CountFQDNSetsRequest) returns (Count) {}
|
||||
rpc FQDNSetExists(FQDNSetExistsRequest) returns (Exists) {}
|
||||
rpc PreviousCertificateExists(PreviousCertificateExistsRequest) returns (Exists) {}
|
||||
rpc GetAuthorization2(AuthorizationID2) returns (core.Authorization) {}
|
||||
rpc GetAuthorizations2(GetAuthorizationsRequest) returns (Authorizations) {}
|
||||
rpc GetPendingAuthorization2(GetPendingAuthorizationRequest) returns (core.Authorization) {}
|
||||
rpc CountPendingAuthorizations2(RegistrationID) returns (Count) {}
|
||||
rpc GetValidOrderAuthorizations2(GetValidOrderAuthorizationsRequest) returns (Authorizations) {}
|
||||
rpc CountInvalidAuthorizations2(CountInvalidAuthorizationsRequest) returns (Count) {}
|
||||
rpc GetValidAuthorizations2(GetValidAuthorizationsRequest) returns (Authorizations) {}
|
||||
rpc KeyBlocked(KeyBlockedRequest) returns (Exists) {}
|
||||
// Adders
|
||||
rpc NewRegistration(core.Registration) returns (core.Registration) {}
|
||||
rpc UpdateRegistration(core.Registration) returns (google.protobuf.Empty) {}
|
||||
rpc AddCertificate(AddCertificateRequest) returns (AddCertificateResponse) {}
|
||||
rpc AddPrecertificate(AddCertificateRequest) returns (google.protobuf.Empty) {}
|
||||
rpc AddSerial(AddSerialRequest) returns (google.protobuf.Empty) {}
|
||||
rpc DeactivateRegistration(RegistrationID) returns (google.protobuf.Empty) {}
|
||||
rpc NewOrder(NewOrderRequest) returns (core.Order) {}
|
||||
rpc NewOrderAndAuthzs(NewOrderAndAuthzsRequest) returns (core.Order) {}
|
||||
rpc SetOrderProcessing(OrderRequest) returns (google.protobuf.Empty) {}
|
||||
rpc SetOrderError(SetOrderErrorRequest) returns (google.protobuf.Empty) {}
|
||||
rpc FinalizeOrder(FinalizeOrderRequest) returns (google.protobuf.Empty) {}
|
||||
rpc GetOrder(OrderRequest) returns (core.Order) {}
|
||||
rpc GetOrderForNames(GetOrderForNamesRequest) returns (core.Order) {}
|
||||
rpc RevokeCertificate(RevokeCertificateRequest) returns (google.protobuf.Empty) {}
|
||||
rpc UpdateRevokedCertificate(RevokeCertificateRequest) returns (google.protobuf.Empty) {}
|
||||
rpc NewAuthorizations2(AddPendingAuthorizationsRequest) returns (Authorization2IDs) {}
|
||||
rpc FinalizeAuthorization2(FinalizeAuthorizationRequest) returns (google.protobuf.Empty) {}
|
||||
rpc DeactivateAuthorization2(AuthorizationID2) returns (google.protobuf.Empty) {}
|
||||
rpc AddBlockedKey(AddBlockedKeyRequest) returns (google.protobuf.Empty) {}
|
||||
}
|
||||
|
||||
message RegistrationID {
|
||||
int64 id = 1;
|
||||
}
|
||||
|
||||
message JSONWebKey {
|
||||
bytes jwk = 1;
|
||||
}
|
||||
|
||||
message AuthorizationID {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message GetPendingAuthorizationRequest {
|
||||
int64 registrationID = 1;
|
||||
string identifierType = 2;
|
||||
string identifierValue = 3;
|
||||
// Result must be valid until at least this Unix timestamp (nanos)
|
||||
int64 validUntil = 4;
|
||||
}
|
||||
|
||||
message GetValidAuthorizationsRequest {
|
||||
int64 registrationID = 1;
|
||||
repeated string domains = 2;
|
||||
int64 now = 3; // Unix timestamp (nanoseconds)
|
||||
}
|
||||
|
||||
message ValidAuthorizations {
|
||||
message MapElement {
|
||||
string domain = 1;
|
||||
core.Authorization authz = 2;
|
||||
}
|
||||
repeated MapElement valid = 1;
|
||||
}
|
||||
|
||||
message Serial {
|
||||
string serial = 1;
|
||||
}
|
||||
|
||||
message SerialMetadata {
|
||||
string serial = 1;
|
||||
int64 registrationID = 2;
|
||||
int64 created = 3; // Unix timestamp (nanoseconds)
|
||||
int64 expires = 4; // Unix timestamp (nanoseconds)
|
||||
}
|
||||
|
||||
message Range {
|
||||
int64 earliest = 1; // Unix timestamp (nanoseconds)
|
||||
int64 latest = 2; // Unix timestamp (nanoseconds)
|
||||
}
|
||||
|
||||
message Count {
|
||||
int64 count = 1;
|
||||
}
|
||||
|
||||
message CountCertificatesByNamesRequest {
|
||||
Range range = 1;
|
||||
repeated string names = 2;
|
||||
}
|
||||
|
||||
message CountByNames {
|
||||
map<string, int64> counts = 1;
|
||||
}
|
||||
|
||||
message CountRegistrationsByIPRequest {
|
||||
bytes ip = 1;
|
||||
Range range = 2;
|
||||
}
|
||||
|
||||
message CountInvalidAuthorizationsRequest {
|
||||
int64 registrationID = 1;
|
||||
string hostname = 2;
|
||||
// Count authorizations that expire in this range.
|
||||
Range range = 3;
|
||||
}
|
||||
|
||||
message CountOrdersRequest {
|
||||
int64 accountID = 1;
|
||||
Range range = 2;
|
||||
}
|
||||
|
||||
message CountFQDNSetsRequest {
|
||||
int64 window = 1;
|
||||
repeated string domains = 2;
|
||||
}
|
||||
|
||||
message FQDNSetExistsRequest {
|
||||
repeated string domains = 1;
|
||||
}
|
||||
|
||||
message PreviousCertificateExistsRequest {
|
||||
string domain = 1;
|
||||
int64 regID = 2;
|
||||
}
|
||||
|
||||
message Exists {
|
||||
bool exists = 1;
|
||||
}
|
||||
|
||||
message AddSerialRequest {
|
||||
int64 regID = 1;
|
||||
string serial = 2;
|
||||
int64 created = 3; // Unix timestamp (nanoseconds)
|
||||
int64 expires = 4; // Unix timestamp (nanoseconds)
|
||||
}
|
||||
|
||||
message AddCertificateRequest {
|
||||
bytes der = 1;
|
||||
int64 regID = 2;
|
||||
// A signed OCSP response for the certificate contained in "der".
|
||||
// Note: The certificate status in the OCSP response is assumed to be 0 (good).
|
||||
bytes ocsp = 3;
|
||||
// An issued time. When not present the SA defaults to using
|
||||
// the current time. The orphan-finder uses this parameter to add
|
||||
// certificates with the correct historic issued date
|
||||
int64 issued = 4;
|
||||
int64 issuerID = 5;
|
||||
}
|
||||
|
||||
message AddCertificateResponse {
|
||||
string digest = 1;
|
||||
}
|
||||
|
||||
message OrderRequest {
|
||||
int64 id = 1;
|
||||
}
|
||||
|
||||
message NewOrderRequest {
|
||||
int64 registrationID = 1;
|
||||
int64 expires = 2;
|
||||
repeated string names = 3;
|
||||
repeated int64 v2Authorizations = 4;
|
||||
}
|
||||
|
||||
message NewOrderAndAuthzsRequest {
|
||||
NewOrderRequest newOrder = 1;
|
||||
repeated core.Authorization newAuthzs = 2;
|
||||
}
|
||||
|
||||
message SetOrderErrorRequest {
|
||||
int64 id = 1;
|
||||
core.ProblemDetails error = 2;
|
||||
}
|
||||
|
||||
message GetValidOrderAuthorizationsRequest {
|
||||
int64 id = 1;
|
||||
int64 acctID = 2;
|
||||
}
|
||||
|
||||
message GetOrderForNamesRequest {
|
||||
int64 acctID = 1;
|
||||
repeated string names = 2;
|
||||
}
|
||||
|
||||
message FinalizeOrderRequest {
|
||||
int64 id = 1;
|
||||
string certificateSerial = 2;
|
||||
}
|
||||
|
||||
message GetAuthorizationsRequest {
|
||||
int64 registrationID = 1;
|
||||
repeated string domains = 2;
|
||||
int64 now = 3; // Unix timestamp (nanoseconds)
|
||||
}
|
||||
|
||||
message Authorizations {
|
||||
message MapElement {
|
||||
string domain = 1;
|
||||
core.Authorization authz = 2;
|
||||
}
|
||||
repeated MapElement authz = 1;
|
||||
}
|
||||
|
||||
message AddPendingAuthorizationsRequest {
|
||||
repeated core.Authorization authz = 1;
|
||||
}
|
||||
|
||||
message AuthorizationIDs {
|
||||
repeated string ids = 1;
|
||||
}
|
||||
|
||||
message AuthorizationID2 {
|
||||
int64 id = 1;
|
||||
}
|
||||
|
||||
message Authorization2IDs {
|
||||
repeated int64 ids = 1;
|
||||
}
|
||||
|
||||
message RevokeCertificateRequest {
|
||||
string serial = 1;
|
||||
int64 reason = 2;
|
||||
int64 date = 3; // Unix timestamp (nanoseconds)
|
||||
int64 backdate = 5; // Unix timestamp (nanoseconds)
|
||||
bytes response = 4;
|
||||
}
|
||||
|
||||
message FinalizeAuthorizationRequest {
|
||||
int64 id = 1;
|
||||
string status = 2;
|
||||
int64 expires = 3; // Unix timestamp (nanoseconds)
|
||||
string attempted = 4;
|
||||
repeated core.ValidationRecord validationRecords = 5;
|
||||
core.ProblemDetails validationError = 6;
|
||||
int64 attemptedAt = 7; // Unix timestamp (nanoseconds)
|
||||
}
|
||||
|
||||
message AddBlockedKeyRequest {
|
||||
bytes keyHash = 1;
|
||||
int64 added = 2; // Unix timestamp (nanoseconds)
|
||||
string source = 3;
|
||||
string comment = 4;
|
||||
int64 revokedBy = 5;
|
||||
}
|
||||
|
||||
message KeyBlockedRequest {
|
||||
bytes keyHash = 1;
|
||||
}
|
1515
vendor/github.com/letsencrypt/boulder/sa/proto/sa_grpc.pb.go
generated
vendored
Normal file
1515
vendor/github.com/letsencrypt/boulder/sa/proto/sa_grpc.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
46
vendor/github.com/letsencrypt/boulder/sa/proto/subsets.go
generated
vendored
Normal file
46
vendor/github.com/letsencrypt/boulder/sa/proto/subsets.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copied from the auto-generated sa_grpc.pb.go
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
proto "github.com/letsencrypt/boulder/core/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
// StorageAuthorityGetterClient is a read-only subset of the sapb.StorageAuthorityClient interface
|
||||
type StorageAuthorityGetterClient interface {
|
||||
GetRegistration(ctx context.Context, in *RegistrationID, opts ...grpc.CallOption) (*proto.Registration, error)
|
||||
GetRegistrationByKey(ctx context.Context, in *JSONWebKey, opts ...grpc.CallOption) (*proto.Registration, error)
|
||||
GetCertificate(ctx context.Context, in *Serial, opts ...grpc.CallOption) (*proto.Certificate, error)
|
||||
GetPrecertificate(ctx context.Context, in *Serial, opts ...grpc.CallOption) (*proto.Certificate, error)
|
||||
GetCertificateStatus(ctx context.Context, in *Serial, opts ...grpc.CallOption) (*proto.CertificateStatus, error)
|
||||
CountCertificatesByNames(ctx context.Context, in *CountCertificatesByNamesRequest, opts ...grpc.CallOption) (*CountByNames, error)
|
||||
CountRegistrationsByIP(ctx context.Context, in *CountRegistrationsByIPRequest, opts ...grpc.CallOption) (*Count, error)
|
||||
CountRegistrationsByIPRange(ctx context.Context, in *CountRegistrationsByIPRequest, opts ...grpc.CallOption) (*Count, error)
|
||||
CountOrders(ctx context.Context, in *CountOrdersRequest, opts ...grpc.CallOption) (*Count, error)
|
||||
CountFQDNSets(ctx context.Context, in *CountFQDNSetsRequest, opts ...grpc.CallOption) (*Count, error)
|
||||
FQDNSetExists(ctx context.Context, in *FQDNSetExistsRequest, opts ...grpc.CallOption) (*Exists, error)
|
||||
PreviousCertificateExists(ctx context.Context, in *PreviousCertificateExistsRequest, opts ...grpc.CallOption) (*Exists, error)
|
||||
GetAuthorization2(ctx context.Context, in *AuthorizationID2, opts ...grpc.CallOption) (*proto.Authorization, error)
|
||||
GetAuthorizations2(ctx context.Context, in *GetAuthorizationsRequest, opts ...grpc.CallOption) (*Authorizations, error)
|
||||
GetPendingAuthorization2(ctx context.Context, in *GetPendingAuthorizationRequest, opts ...grpc.CallOption) (*proto.Authorization, error)
|
||||
CountPendingAuthorizations2(ctx context.Context, in *RegistrationID, opts ...grpc.CallOption) (*Count, error)
|
||||
GetValidOrderAuthorizations2(ctx context.Context, in *GetValidOrderAuthorizationsRequest, opts ...grpc.CallOption) (*Authorizations, error)
|
||||
CountInvalidAuthorizations2(ctx context.Context, in *CountInvalidAuthorizationsRequest, opts ...grpc.CallOption) (*Count, error)
|
||||
GetValidAuthorizations2(ctx context.Context, in *GetValidAuthorizationsRequest, opts ...grpc.CallOption) (*Authorizations, error)
|
||||
KeyBlocked(ctx context.Context, in *KeyBlockedRequest, opts ...grpc.CallOption) (*Exists, error)
|
||||
GetOrder(ctx context.Context, in *OrderRequest, opts ...grpc.CallOption) (*proto.Order, error)
|
||||
GetOrderForNames(ctx context.Context, in *GetOrderForNamesRequest, opts ...grpc.CallOption) (*proto.Order, error)
|
||||
}
|
||||
|
||||
// StorageAuthorityCertificateClient is a subset of the sapb.StorageAuthorityClient interface that only reads and writes certificates
|
||||
type StorageAuthorityCertificateClient interface {
|
||||
AddSerial(ctx context.Context, in *AddSerialRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
AddPrecertificate(ctx context.Context, in *AddCertificateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
GetPrecertificate(ctx context.Context, in *Serial, opts ...grpc.CallOption) (*proto.Certificate, error)
|
||||
AddCertificate(ctx context.Context, in *AddCertificateRequest, opts ...grpc.CallOption) (*AddCertificateResponse, error)
|
||||
GetCertificate(ctx context.Context, in *Serial, opts ...grpc.CallOption) (*proto.Certificate, error)
|
||||
}
|
Reference in New Issue
Block a user