Update go versions in CI and release (#7971)
Update from go1.23.1 to go1.23.6 for our primary CI and release builds. This brings in a few security fixes that aren't directly relevant to us. Add go1.24.0 to our matrix of CI and release versions, to prepare for switching to this next major version in prod.
This commit is contained in:
parent
eab90ee2f5
commit
212a66ab49
|
@ -36,7 +36,8 @@ jobs:
|
|||
matrix:
|
||||
# Add additional docker image tags here and all tests will be run with the additional image.
|
||||
BOULDER_TOOLS_TAG:
|
||||
- go1.23.1_2024-09-05
|
||||
- go1.23.6_2025-02-11
|
||||
- go1.24.0_2025-02-11
|
||||
# Tests command definitions. Use the entire "docker compose" command you want to run.
|
||||
tests:
|
||||
# Run ./test.sh --help for a description of each of the flags.
|
||||
|
|
|
@ -15,7 +15,8 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
GO_VERSION:
|
||||
- "1.23.1"
|
||||
- "1.23.6"
|
||||
- "1.24.0"
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
contents: write
|
||||
|
@ -51,7 +52,7 @@ jobs:
|
|||
run: gh release upload "${GITHUB_REF_NAME}" boulder*.deb boulder*.tar.gz boulder*.checksums.txt
|
||||
|
||||
- name: Build ct-test-srv Container
|
||||
run: docker buildx build . --build-arg "GO_VERSION=${{ matrix.GO_VERSION }}" -f test/ct-test-srv/Dockerfile -t "ghcr.io/letsencrypt/ct-test-srv:${{ github.ref_name }}"
|
||||
run: docker buildx build . --build-arg "GO_VERSION=${{ matrix.GO_VERSION }}" -f test/ct-test-srv/Dockerfile -t "ghcr.io/letsencrypt/ct-test-srv:${{ github.ref_name }}-go${{ matrix.GO_VERSION }}"
|
||||
|
||||
- name: Login to ghcr.io
|
||||
run: printenv GITHUB_TOKEN | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
|
||||
|
@ -59,4 +60,4 @@ jobs:
|
|||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Push ct-test-srv Container
|
||||
run: docker push "ghcr.io/letsencrypt/ct-test-srv:${{ github.ref_name }}"
|
||||
run: docker push "ghcr.io/letsencrypt/ct-test-srv:${{ github.ref_name }}-go${{ matrix.GO_VERSION }}"
|
||||
|
|
|
@ -16,7 +16,8 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
GO_VERSION:
|
||||
- "1.23.1"
|
||||
- "1.23.6"
|
||||
- "1.24.0"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
@ -44,4 +45,4 @@ jobs:
|
|||
run: cat boulder*.checksums.txt
|
||||
|
||||
- name: Build ct-test-srv Container
|
||||
run: docker buildx build . --build-arg "GO_VERSION=${{ matrix.GO_VERSION }}" -f test/ct-test-srv/Dockerfile -t "ghcr.io/letsencrypt/ct-test-srv:${{ github.sha }}"
|
||||
run: docker buildx build . --build-arg "GO_VERSION=${{ matrix.GO_VERSION }}" -f test/ct-test-srv/Dockerfile -t "ghcr.io/letsencrypt/ct-test-srv:${{ github.sha }}-go${{ matrix.GO_VERSION }}"
|
||||
|
|
|
@ -8,6 +8,7 @@ linters:
|
|||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
- nolintlint
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
|
@ -55,6 +56,7 @@ linters-settings:
|
|||
- G402 # TLS InsecureSkipVerify set true.
|
||||
- G403 # RSA keys should be at least 2048 bits
|
||||
- G404 # Use of weak random number generator (math/rand instead of crypto/rand)
|
||||
- G501 # Blacklisted import `crypto/md5`: weak cryptographic primitive
|
||||
- G505 # Blacklisted import `crypto/sha1`: weak cryptographic primitive
|
||||
- G601 # Implicit memory aliasing in for loop (this is fixed by go1.22)
|
||||
nolintlint:
|
||||
allow-unused: false
|
||||
require-explanation: true
|
||||
require-specific: true
|
||||
|
|
|
@ -3,7 +3,7 @@ package akamai
|
|||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/md5" //nolint: gosec // MD5 is required by the Akamai API.
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
|
|
|
@ -42,7 +42,7 @@ type subcommandRevokeCert struct {
|
|||
incidentTable string
|
||||
serialsFile string
|
||||
privKey string
|
||||
regID uint
|
||||
regID int64
|
||||
certFile string
|
||||
crlShard int64
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ func (s *subcommandRevokeCert) Flags(flag *flag.FlagSet) {
|
|||
flag.StringVar(&s.incidentTable, "incident-table", "", "Revoke all certificates whose serials are in this table")
|
||||
flag.StringVar(&s.serialsFile, "serials-file", "", "Revoke all certificates whose hex serials are in this file")
|
||||
flag.StringVar(&s.privKey, "private-key", "", "Revoke all certificates whose pubkey matches this private key")
|
||||
flag.UintVar(&s.regID, "reg-id", 0, "Revoke all certificates issued to this account")
|
||||
flag.Int64Var(&s.regID, "reg-id", 0, "Revoke all certificates issued to this account")
|
||||
flag.StringVar(&s.certFile, "cert-file", "", "Revoke the single PEM-formatted certificate in this file")
|
||||
}
|
||||
|
||||
|
@ -126,7 +126,7 @@ func (s *subcommandRevokeCert) Run(ctx context.Context, a *admin) error {
|
|||
case "-private-key":
|
||||
serials, err = a.serialsFromPrivateKey(ctx, s.privKey)
|
||||
case "-reg-id":
|
||||
serials, err = a.serialsFromRegID(ctx, int64(s.regID))
|
||||
serials, err = a.serialsFromRegID(ctx, s.regID)
|
||||
case "-cert-file":
|
||||
serials, err = a.serialsFromCertPEM(ctx, s.certFile)
|
||||
default:
|
||||
|
|
|
@ -2,8 +2,9 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
|
@ -551,7 +552,7 @@ func TestGenerateCSR(t *testing.T) {
|
|||
Country: "country",
|
||||
}
|
||||
|
||||
signer, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||
signer, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
test.AssertNotError(t, err, "failed to generate test key")
|
||||
|
||||
csrBytes, err := generateCSR(profile, &wrappedSigner{signer})
|
||||
|
|
|
@ -96,7 +96,7 @@ func postIssuanceLinting(fc *x509.Certificate, skipLints []string) error {
|
|||
|
||||
type keyGenConfig struct {
|
||||
Type string `yaml:"type"`
|
||||
RSAModLength uint `yaml:"rsa-mod-length"`
|
||||
RSAModLength int `yaml:"rsa-mod-length"`
|
||||
ECDSACurve string `yaml:"ecdsa-curve"`
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,9 @@ import (
|
|||
"log"
|
||||
"math/big"
|
||||
|
||||
"github.com/letsencrypt/boulder/pkcs11helpers"
|
||||
"github.com/miekg/pkcs11"
|
||||
|
||||
"github.com/letsencrypt/boulder/pkcs11helpers"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -18,10 +19,10 @@ const (
|
|||
// device and specifies which mechanism should be used. modulusLen specifies the
|
||||
// length of the modulus to be generated on the device in bits and exponent
|
||||
// specifies the public exponent that should be used.
|
||||
func rsaArgs(label string, modulusLen, exponent uint, keyID []byte) generateArgs {
|
||||
func rsaArgs(label string, modulusLen int, keyID []byte) generateArgs {
|
||||
// Encode as unpadded big endian encoded byte slice
|
||||
expSlice := big.NewInt(int64(exponent)).Bytes()
|
||||
log.Printf("\tEncoded public exponent (%d) as: %0X\n", exponent, expSlice)
|
||||
expSlice := big.NewInt(rsaExp).Bytes()
|
||||
log.Printf("\tEncoded public exponent (%d) as: %0X\n", rsaExp, expSlice)
|
||||
return generateArgs{
|
||||
mechanism: []*pkcs11.Mechanism{
|
||||
pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_KEY_PAIR_GEN, nil),
|
||||
|
@ -55,15 +56,15 @@ func rsaArgs(label string, modulusLen, exponent uint, keyID []byte) generateArgs
|
|||
// handle, and constructs a rsa.PublicKey. It also checks that the key has the
|
||||
// correct length modulus and that the public exponent is what was requested in
|
||||
// the public key template.
|
||||
func rsaPub(session *pkcs11helpers.Session, object pkcs11.ObjectHandle, modulusLen, exponent uint) (*rsa.PublicKey, error) {
|
||||
func rsaPub(session *pkcs11helpers.Session, object pkcs11.ObjectHandle, modulusLen int) (*rsa.PublicKey, error) {
|
||||
pubKey, err := session.GetRSAPublicKey(object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pubKey.E != int(exponent) {
|
||||
if pubKey.E != rsaExp {
|
||||
return nil, errors.New("returned CKA_PUBLIC_EXPONENT doesn't match expected exponent")
|
||||
}
|
||||
if pubKey.N.BitLen() != int(modulusLen) {
|
||||
if pubKey.N.BitLen() != modulusLen {
|
||||
return nil, errors.New("returned CKA_MODULUS isn't of the expected bit length")
|
||||
}
|
||||
log.Printf("\tPublic exponent: %d\n", pubKey.E)
|
||||
|
@ -75,21 +76,21 @@ func rsaPub(session *pkcs11helpers.Session, object pkcs11.ObjectHandle, modulusL
|
|||
// specified by modulusLen and with the exponent 65537.
|
||||
// It returns the public part of the generated key pair as a rsa.PublicKey
|
||||
// and the random key ID that the HSM uses to identify the key pair.
|
||||
func rsaGenerate(session *pkcs11helpers.Session, label string, modulusLen uint) (*rsa.PublicKey, []byte, error) {
|
||||
func rsaGenerate(session *pkcs11helpers.Session, label string, modulusLen int) (*rsa.PublicKey, []byte, error) {
|
||||
keyID := make([]byte, 4)
|
||||
_, err := newRandReader(session).Read(keyID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
log.Printf("Generating RSA key with %d bit modulus and public exponent %d and ID %x\n", modulusLen, rsaExp, keyID)
|
||||
args := rsaArgs(label, modulusLen, rsaExp, keyID)
|
||||
args := rsaArgs(label, modulusLen, keyID)
|
||||
pub, _, err := session.GenerateKeyPair(args.mechanism, args.publicAttrs, args.privateAttrs)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
log.Println("Key generated")
|
||||
log.Println("Extracting public key")
|
||||
pk, err := rsaPub(session, pub, modulusLen, rsaExp)
|
||||
pk, err := rsaPub(session, pub, modulusLen)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
@ -8,24 +8,15 @@ import (
|
|||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
|
||||
"github.com/letsencrypt/boulder/pkcs11helpers"
|
||||
"github.com/letsencrypt/boulder/test"
|
||||
"github.com/miekg/pkcs11"
|
||||
)
|
||||
|
||||
func TestRSAPub(t *testing.T) {
|
||||
s, ctx := pkcs11helpers.NewSessionWithMock()
|
||||
|
||||
// test we fail to construct key with non-matching exp
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, []byte{1, 0, 1}),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_MODULUS, []byte{255}),
|
||||
}, nil
|
||||
}
|
||||
_, err := rsaPub(s, 0, 0, 255)
|
||||
test.AssertError(t, err, "rsaPub didn't fail with non-matching exp")
|
||||
|
||||
// test we fail to construct key with non-matching modulus
|
||||
ctx.GetAttributeValueFunc = func(pkcs11.SessionHandle, pkcs11.ObjectHandle, []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) {
|
||||
return []*pkcs11.Attribute{
|
||||
|
@ -33,7 +24,7 @@ func TestRSAPub(t *testing.T) {
|
|||
pkcs11.NewAttribute(pkcs11.CKA_MODULUS, []byte{255}),
|
||||
}, nil
|
||||
}
|
||||
_, err = rsaPub(s, 0, 16, 65537)
|
||||
_, err := rsaPub(s, 0, 16)
|
||||
test.AssertError(t, err, "rsaPub didn't fail with non-matching modulus size")
|
||||
|
||||
// test we don't fail with the correct attributes
|
||||
|
@ -43,7 +34,7 @@ func TestRSAPub(t *testing.T) {
|
|||
pkcs11.NewAttribute(pkcs11.CKA_MODULUS, []byte{255}),
|
||||
}, nil
|
||||
}
|
||||
_, err = rsaPub(s, 0, 8, 65537)
|
||||
_, err = rsaPub(s, 0, 8)
|
||||
test.AssertNotError(t, err, "rsaPub failed with valid attributes")
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ func init() {
|
|||
|
||||
func BenchmarkCheckCert(b *testing.B) {
|
||||
checker := newChecker(nil, clock.New(), pa, kp, time.Hour, testValidityDurations, blog.NewMock())
|
||||
testKey, _ := rsa.GenerateKey(rand.Reader, 1024)
|
||||
testKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
expiry := time.Now().AddDate(0, 0, 1)
|
||||
serial := big.NewInt(1337)
|
||||
rawCert := x509.Certificate{
|
||||
|
@ -341,7 +341,7 @@ func TestGetAndProcessCerts(t *testing.T) {
|
|||
saCleanUp()
|
||||
}()
|
||||
|
||||
testKey, _ := rsa.GenerateKey(rand.Reader, 1024)
|
||||
testKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
// Problems
|
||||
// Expiry period is too long
|
||||
rawCert := x509.Certificate{
|
||||
|
|
|
@ -2,11 +2,11 @@ package notmain
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
|
@ -14,6 +14,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/jmhodges/clock"
|
||||
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
corepb "github.com/letsencrypt/boulder/core/proto"
|
||||
blog "github.com/letsencrypt/boulder/log"
|
||||
|
@ -321,17 +322,9 @@ func (tc testCtx) addCertificates(t *testing.T) {
|
|||
serial3String := core.SerialToString(serial3)
|
||||
serial4 := big.NewInt(1339)
|
||||
serial4String := core.SerialToString(serial4)
|
||||
n := bigIntFromB64("n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw==")
|
||||
e := intFromB64("AQAB")
|
||||
d := bigIntFromB64("bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ==")
|
||||
p := bigIntFromB64("uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc=")
|
||||
q := bigIntFromB64("uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc=")
|
||||
|
||||
testKey := rsa.PrivateKey{
|
||||
PublicKey: rsa.PublicKey{N: n, E: e},
|
||||
D: d,
|
||||
Primes: []*big.Int{p, q},
|
||||
}
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
test.AssertNotError(t, err, "creating test key")
|
||||
|
||||
fc := clock.NewFake()
|
||||
|
||||
|
@ -344,14 +337,14 @@ func (tc testCtx) addCertificates(t *testing.T) {
|
|||
DNSNames: []string{"example-a.com"},
|
||||
SerialNumber: serial1,
|
||||
}
|
||||
certDerA, _ := x509.CreateCertificate(rand.Reader, &rawCertA, &rawCertA, &testKey.PublicKey, &testKey)
|
||||
certDerA, _ := x509.CreateCertificate(rand.Reader, &rawCertA, &rawCertA, key.Public(), key)
|
||||
certA := &core.Certificate{
|
||||
RegistrationID: regA.Id,
|
||||
Serial: serial1String,
|
||||
Expires: rawCertA.NotAfter,
|
||||
DER: certDerA,
|
||||
}
|
||||
err := tc.c.dbMap.Insert(ctx, certA)
|
||||
err = tc.c.dbMap.Insert(ctx, certA)
|
||||
test.AssertNotError(t, err, "Couldn't add certA")
|
||||
_, err = tc.c.dbMap.ExecContext(
|
||||
ctx,
|
||||
|
@ -370,7 +363,7 @@ func (tc testCtx) addCertificates(t *testing.T) {
|
|||
DNSNames: []string{"example-b.com"},
|
||||
SerialNumber: serial2,
|
||||
}
|
||||
certDerB, _ := x509.CreateCertificate(rand.Reader, &rawCertB, &rawCertB, &testKey.PublicKey, &testKey)
|
||||
certDerB, _ := x509.CreateCertificate(rand.Reader, &rawCertB, &rawCertB, key.Public(), key)
|
||||
certB := &core.Certificate{
|
||||
RegistrationID: regB.Id,
|
||||
Serial: serial2String,
|
||||
|
@ -396,7 +389,7 @@ func (tc testCtx) addCertificates(t *testing.T) {
|
|||
DNSNames: []string{"example-c.com"},
|
||||
SerialNumber: serial3,
|
||||
}
|
||||
certDerC, _ := x509.CreateCertificate(rand.Reader, &rawCertC, &rawCertC, &testKey.PublicKey, &testKey)
|
||||
certDerC, _ := x509.CreateCertificate(rand.Reader, &rawCertC, &rawCertC, key.Public(), key)
|
||||
certC := &core.Certificate{
|
||||
RegistrationID: regC.Id,
|
||||
Serial: serial3String,
|
||||
|
@ -422,7 +415,7 @@ func (tc testCtx) addCertificates(t *testing.T) {
|
|||
DNSNames: []string{"example-d.com"},
|
||||
SerialNumber: serial4,
|
||||
}
|
||||
certDerD, _ := x509.CreateCertificate(rand.Reader, &rawCertD, &rawCertD, &testKey.PublicKey, &testKey)
|
||||
certDerD, _ := x509.CreateCertificate(rand.Reader, &rawCertD, &rawCertD, key.Public(), key)
|
||||
certD := &core.Certificate{
|
||||
RegistrationID: regD.Id,
|
||||
Serial: serial4String,
|
||||
|
@ -466,14 +459,3 @@ func setup(t *testing.T) testCtx {
|
|||
cleanUp: cleanUp,
|
||||
}
|
||||
}
|
||||
|
||||
func bigIntFromB64(b64 string) *big.Int {
|
||||
bytes, _ := base64.URLEncoding.DecodeString(b64)
|
||||
x := big.NewInt(0)
|
||||
x.SetBytes(bytes)
|
||||
return x
|
||||
}
|
||||
|
||||
func intFromB64(b64 string) int {
|
||||
return int(bigIntFromB64(b64).Int64())
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ type client struct {
|
|||
// for a single certificateStatus ID. If `err` is non-nil, it indicates the
|
||||
// attempt failed.
|
||||
type processResult struct {
|
||||
id uint64
|
||||
id int64
|
||||
err error
|
||||
}
|
||||
|
||||
|
@ -181,7 +181,7 @@ func (cl *client) scanFromDBOneBatch(ctx context.Context, prevID int64, frequenc
|
|||
return fmt.Errorf("scanning row %d (previous ID %d): %w", scanned, previousID, err)
|
||||
}
|
||||
scanned++
|
||||
inflightIDs.add(uint64(status.ID))
|
||||
inflightIDs.add(status.ID)
|
||||
// Emit a log line every 100000 rows. For our current ~215M rows, that
|
||||
// will emit about 2150 log lines. This probably strikes a good balance
|
||||
// between too spammy and having a reasonably frequent checkpoint.
|
||||
|
@ -213,25 +213,25 @@ func (cl *client) signAndStoreResponses(ctx context.Context, input <-chan *sa.Ce
|
|||
Serial: status.Serial,
|
||||
IssuerID: status.IssuerID,
|
||||
Status: string(status.Status),
|
||||
Reason: int32(status.RevokedReason),
|
||||
Reason: int32(status.RevokedReason), //nolint: gosec // Revocation reasons are guaranteed to be small, no risk of overflow.
|
||||
RevokedAt: timestamppb.New(status.RevokedDate),
|
||||
}
|
||||
result, err := cl.ocspGenerator.GenerateOCSP(ctx, ocspReq)
|
||||
if err != nil {
|
||||
output <- processResult{id: uint64(status.ID), err: err}
|
||||
output <- processResult{id: status.ID, err: err}
|
||||
continue
|
||||
}
|
||||
resp, err := ocsp.ParseResponse(result.Response, nil)
|
||||
if err != nil {
|
||||
output <- processResult{id: uint64(status.ID), err: err}
|
||||
output <- processResult{id: status.ID, err: err}
|
||||
continue
|
||||
}
|
||||
|
||||
err = cl.redis.StoreResponse(ctx, resp)
|
||||
if err != nil {
|
||||
output <- processResult{id: uint64(status.ID), err: err}
|
||||
output <- processResult{id: status.ID, err: err}
|
||||
} else {
|
||||
output <- processResult{id: uint64(status.ID), err: nil}
|
||||
output <- processResult{id: status.ID, err: nil}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,22 +4,22 @@ import "sync"
|
|||
|
||||
type inflight struct {
|
||||
sync.RWMutex
|
||||
items map[uint64]struct{}
|
||||
items map[int64]struct{}
|
||||
}
|
||||
|
||||
func newInflight() *inflight {
|
||||
return &inflight{
|
||||
items: make(map[uint64]struct{}),
|
||||
items: make(map[int64]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (i *inflight) add(n uint64) {
|
||||
func (i *inflight) add(n int64) {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
i.items[n] = struct{}{}
|
||||
}
|
||||
|
||||
func (i *inflight) remove(n uint64) {
|
||||
func (i *inflight) remove(n int64) {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
delete(i.items, n)
|
||||
|
@ -34,13 +34,13 @@ func (i *inflight) len() int {
|
|||
// min returns the numerically smallest key inflight. If nothing is inflight,
|
||||
// it returns 0. Note: this takes O(n) time in the number of keys and should
|
||||
// be called rarely.
|
||||
func (i *inflight) min() uint64 {
|
||||
func (i *inflight) min() int64 {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
if len(i.items) == 0 {
|
||||
return 0
|
||||
}
|
||||
var min uint64
|
||||
var min int64
|
||||
for k := range i.items {
|
||||
if min == 0 {
|
||||
min = k
|
||||
|
|
|
@ -9,25 +9,25 @@ import (
|
|||
func TestInflight(t *testing.T) {
|
||||
ifl := newInflight()
|
||||
test.AssertEquals(t, ifl.len(), 0)
|
||||
test.AssertEquals(t, ifl.min(), uint64(0))
|
||||
test.AssertEquals(t, ifl.min(), int64(0))
|
||||
|
||||
ifl.add(1337)
|
||||
test.AssertEquals(t, ifl.len(), 1)
|
||||
test.AssertEquals(t, ifl.min(), uint64(1337))
|
||||
test.AssertEquals(t, ifl.min(), int64(1337))
|
||||
|
||||
ifl.remove(1337)
|
||||
test.AssertEquals(t, ifl.len(), 0)
|
||||
test.AssertEquals(t, ifl.min(), uint64(0))
|
||||
test.AssertEquals(t, ifl.min(), int64(0))
|
||||
|
||||
ifl.add(7341)
|
||||
ifl.add(3317)
|
||||
ifl.add(1337)
|
||||
test.AssertEquals(t, ifl.len(), 3)
|
||||
test.AssertEquals(t, ifl.min(), uint64(1337))
|
||||
test.AssertEquals(t, ifl.min(), int64(1337))
|
||||
|
||||
ifl.remove(3317)
|
||||
ifl.remove(1337)
|
||||
ifl.remove(7341)
|
||||
test.AssertEquals(t, ifl.len(), 0)
|
||||
test.AssertEquals(t, ifl.min(), uint64(0))
|
||||
test.AssertEquals(t, ifl.min(), int64(0))
|
||||
}
|
||||
|
|
|
@ -273,9 +273,7 @@ func TestFailExit(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
// gosec points out that os.Args[0] is tainted, but we only run this as a test
|
||||
// so we are not worried about it containing an untrusted value.
|
||||
//nolint:gosec
|
||||
//nolint: gosec // Test-only code is not concerned about untrusted values in os.Args[0]
|
||||
cmd := exec.Command(os.Args[0], "-test.run=TestFailExit")
|
||||
cmd.Env = append(os.Environ(), "TIME_TO_DIE=1")
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
@ -288,7 +286,7 @@ func TestFailExit(t *testing.T) {
|
|||
|
||||
func testPanicStackTraceHelper() {
|
||||
var x *int
|
||||
*x = 1 //nolint:govet
|
||||
*x = 1 //nolint: govet // Purposeful nil pointer dereference to trigger a panic
|
||||
}
|
||||
|
||||
func TestPanicStackTrace(t *testing.T) {
|
||||
|
@ -302,9 +300,7 @@ func TestPanicStackTrace(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
// gosec points out that os.Args[0] is tainted, but we only run this as a test
|
||||
// so we are not worried about it containing an untrusted value.
|
||||
//nolint:gosec
|
||||
//nolint: gosec // Test-only code is not concerned about untrusted values in os.Args[0]
|
||||
cmd := exec.Command(os.Args[0], "-test.run=TestPanicStackTrace")
|
||||
cmd.Env = append(os.Environ(), "AT_THE_DISCO=1")
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
|
|
@ -8,7 +8,7 @@ services:
|
|||
context: test/boulder-tools/
|
||||
# Should match one of the GO_CI_VERSIONS in test/boulder-tools/tag_and_upload.sh.
|
||||
args:
|
||||
GO_VERSION: 1.23.1
|
||||
GO_VERSION: 1.23.6
|
||||
environment:
|
||||
# To solve HTTP-01 and TLS-ALPN-01 challenges, change the IP in FAKE_DNS
|
||||
# to the IP address where your ACME client's solver is listening.
|
||||
|
|
|
@ -150,8 +150,17 @@ func (be *BoulderError) WithSubErrors(subErrs []SubBoulderError) *BoulderError {
|
|||
}
|
||||
}
|
||||
|
||||
// New is a convenience function for creating a new BoulderError
|
||||
func New(errType ErrorType, msg string, args ...interface{}) error {
|
||||
// New is a convenience function for creating a new BoulderError.
|
||||
func New(errType ErrorType, msg string) error {
|
||||
return &BoulderError{
|
||||
Type: errType,
|
||||
Detail: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// newf is a convenience function for creating a new BoulderError with a
|
||||
// formatted message.
|
||||
func newf(errType ErrorType, msg string, args ...interface{}) error {
|
||||
return &BoulderError{
|
||||
Type: errType,
|
||||
Detail: fmt.Sprintf(msg, args...),
|
||||
|
@ -159,19 +168,19 @@ func New(errType ErrorType, msg string, args ...interface{}) error {
|
|||
}
|
||||
|
||||
func InternalServerError(msg string, args ...interface{}) error {
|
||||
return New(InternalServer, msg, args...)
|
||||
return newf(InternalServer, msg, args...)
|
||||
}
|
||||
|
||||
func MalformedError(msg string, args ...interface{}) error {
|
||||
return New(Malformed, msg, args...)
|
||||
return newf(Malformed, msg, args...)
|
||||
}
|
||||
|
||||
func UnauthorizedError(msg string, args ...interface{}) error {
|
||||
return New(Unauthorized, msg, args...)
|
||||
return newf(Unauthorized, msg, args...)
|
||||
}
|
||||
|
||||
func NotFoundError(msg string, args ...interface{}) error {
|
||||
return New(NotFound, msg, args...)
|
||||
return newf(NotFound, msg, args...)
|
||||
}
|
||||
|
||||
func RateLimitError(retryAfter time.Duration, msg string, args ...interface{}) error {
|
||||
|
@ -231,65 +240,65 @@ func FailedAuthorizationsPerDomainPerAccountError(retryAfter time.Duration, msg
|
|||
}
|
||||
|
||||
func RejectedIdentifierError(msg string, args ...interface{}) error {
|
||||
return New(RejectedIdentifier, msg, args...)
|
||||
return newf(RejectedIdentifier, msg, args...)
|
||||
}
|
||||
|
||||
func InvalidEmailError(msg string, args ...interface{}) error {
|
||||
return New(InvalidEmail, msg, args...)
|
||||
return newf(InvalidEmail, msg, args...)
|
||||
}
|
||||
|
||||
func UnsupportedContactError(msg string, args ...interface{}) error {
|
||||
return New(UnsupportedContact, msg, args...)
|
||||
return newf(UnsupportedContact, msg, args...)
|
||||
}
|
||||
|
||||
func ConnectionFailureError(msg string, args ...interface{}) error {
|
||||
return New(ConnectionFailure, msg, args...)
|
||||
return newf(ConnectionFailure, msg, args...)
|
||||
}
|
||||
|
||||
func CAAError(msg string, args ...interface{}) error {
|
||||
return New(CAA, msg, args...)
|
||||
return newf(CAA, msg, args...)
|
||||
}
|
||||
|
||||
func MissingSCTsError(msg string, args ...interface{}) error {
|
||||
return New(MissingSCTs, msg, args...)
|
||||
return newf(MissingSCTs, msg, args...)
|
||||
}
|
||||
|
||||
func DuplicateError(msg string, args ...interface{}) error {
|
||||
return New(Duplicate, msg, args...)
|
||||
return newf(Duplicate, msg, args...)
|
||||
}
|
||||
|
||||
func OrderNotReadyError(msg string, args ...interface{}) error {
|
||||
return New(OrderNotReady, msg, args...)
|
||||
return newf(OrderNotReady, msg, args...)
|
||||
}
|
||||
|
||||
func DNSError(msg string, args ...interface{}) error {
|
||||
return New(DNS, msg, args...)
|
||||
return newf(DNS, msg, args...)
|
||||
}
|
||||
|
||||
func BadPublicKeyError(msg string, args ...interface{}) error {
|
||||
return New(BadPublicKey, msg, args...)
|
||||
return newf(BadPublicKey, msg, args...)
|
||||
}
|
||||
|
||||
func BadCSRError(msg string, args ...interface{}) error {
|
||||
return New(BadCSR, msg, args...)
|
||||
return newf(BadCSR, msg, args...)
|
||||
}
|
||||
|
||||
func AlreadyRevokedError(msg string, args ...interface{}) error {
|
||||
return New(AlreadyRevoked, msg, args...)
|
||||
return newf(AlreadyRevoked, msg, args...)
|
||||
}
|
||||
|
||||
func BadRevocationReasonError(reason int64) error {
|
||||
return New(BadRevocationReason, "disallowed revocation reason: %d", reason)
|
||||
return newf(BadRevocationReason, "disallowed revocation reason: %d", reason)
|
||||
}
|
||||
|
||||
func UnknownSerialError() error {
|
||||
return New(UnknownSerial, "unknown serial")
|
||||
return newf(UnknownSerial, "unknown serial")
|
||||
}
|
||||
|
||||
func ConflictError(msg string, args ...interface{}) error {
|
||||
return New(Conflict, msg, args...)
|
||||
return newf(Conflict, msg, args...)
|
||||
}
|
||||
|
||||
func InvalidProfileError(msg string, args ...interface{}) error {
|
||||
return New(InvalidProfile, msg, args...)
|
||||
return newf(InvalidProfile, msg, args...)
|
||||
}
|
||||
|
|
|
@ -404,7 +404,7 @@ func checkPrimeFactorsTooClose(n *big.Int, rounds int) error {
|
|||
b2 := new(big.Int)
|
||||
b2.Mul(a, a).Sub(b2, n)
|
||||
|
||||
for range rounds {
|
||||
for round := range rounds {
|
||||
// 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)
|
||||
|
@ -414,7 +414,7 @@ func checkPrimeFactorsTooClose(n *big.Int, rounds int) error {
|
|||
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)
|
||||
return fmt.Errorf("public modulus n = pq factored in %d rounds into p: %s and q: %s", round+1, p, q)
|
||||
}
|
||||
|
||||
// Set up the next iteration by incrementing a by one and recalculating b2.
|
||||
|
|
|
@ -311,43 +311,96 @@ func TestRSAStrangeSize(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCheckPrimeFactorsTooClose(t *testing.T) {
|
||||
// The prime factors of 5959 are 59 and 101. The values a and b calculated
|
||||
// by Fermat's method will be 80 and 21. The ceil of the square root of 5959
|
||||
// is 78. Therefore it takes 3 rounds of Fermat's method to find the factors.
|
||||
n := big.NewInt(5959)
|
||||
err := checkPrimeFactorsTooClose(n, 2)
|
||||
test.AssertNotError(t, err, "factored n in too few iterations")
|
||||
err = checkPrimeFactorsTooClose(n, 3)
|
||||
test.AssertError(t, err, "failed to factor n")
|
||||
test.AssertContains(t, err.Error(), "p: 101")
|
||||
test.AssertContains(t, err.Error(), "q: 59")
|
||||
type testCase struct {
|
||||
name string
|
||||
p string
|
||||
q string
|
||||
expectRounds int
|
||||
}
|
||||
|
||||
// These factors differ only in their second-to-last digit. They're so close
|
||||
// that a single iteration of Fermat's method is sufficient to find them.
|
||||
p, ok := new(big.Int).SetString("12451309173743450529024753538187635497858772172998414407116324997634262083672423797183640278969532658774374576700091736519352600717664126766443002156788367", 10)
|
||||
test.Assert(t, ok, "failed to create large prime")
|
||||
q, ok := new(big.Int).SetString("12451309173743450529024753538187635497858772172998414407116324997634262083672423797183640278969532658774374576700091736519352600717664126766443002156788337", 10)
|
||||
test.Assert(t, ok, "failed to create large prime")
|
||||
n = n.Mul(p, q)
|
||||
err = checkPrimeFactorsTooClose(n, 0)
|
||||
test.AssertNotError(t, err, "factored n in too few iterations")
|
||||
err = checkPrimeFactorsTooClose(n, 1)
|
||||
test.AssertError(t, err, "failed to factor n")
|
||||
test.AssertContains(t, err.Error(), fmt.Sprintf("p: %s", p))
|
||||
test.AssertContains(t, err.Error(), fmt.Sprintf("q: %s", q))
|
||||
testCases := []testCase{
|
||||
{
|
||||
// The factors 59 and 101 multiply to 5959. The values a and b calculated
|
||||
// by Fermat's method will be 80 and 21. The ceil of the square root of
|
||||
// 5959 is 78. Therefore it takes 3 rounds of Fermat's method to find the
|
||||
// factors.
|
||||
name: "tiny",
|
||||
p: "101",
|
||||
q: "59",
|
||||
expectRounds: 3,
|
||||
},
|
||||
{
|
||||
// These factors differ only in their second-to-last digit. They're so close
|
||||
// that a single iteration of Fermat's method is sufficient to find them.
|
||||
name: "very close",
|
||||
p: "12451309173743450529024753538187635497858772172998414407116324997634262083672423797183640278969532658774374576700091736519352600717664126766443002156788367",
|
||||
q: "12451309173743450529024753538187635497858772172998414407116324997634262083672423797183640278969532658774374576700091736519352600717664126766443002156788337",
|
||||
expectRounds: 1,
|
||||
},
|
||||
{
|
||||
// These factors differ by slightly more than 2^256, which takes fourteen
|
||||
// rounds to factor.
|
||||
name: "still too close",
|
||||
p: "11779932606551869095289494662458707049283241949932278009554252037480401854504909149712949171865707598142483830639739537075502512627849249573564209082969463",
|
||||
q: "11779932606551869095289494662458707049283241949932278009554252037480401854503793357623711855670284027157475142731886267090836872063809791989556295953329083",
|
||||
expectRounds: 14,
|
||||
},
|
||||
{
|
||||
// These factors come from a real canon printer in the wild with a broken
|
||||
// key generation mechanism.
|
||||
name: "canon printer (2048 bit, 1 round)",
|
||||
p: "155536235030272749691472293262418471207550926406427515178205576891522284497518443889075039382254334975506248481615035474816604875321501901699955105345417152355947783063521554077194367454070647740704883461064399268622437721385112646454393005862535727615809073410746393326688230040267160616554768771412289114449",
|
||||
q: "155536235030272749691472293262418471207550926406427515178205576891522284497518443889075039382254334975506248481615035474816604875321501901699955105345417152355947783063521554077194367454070647740704883461064399268622437721385112646454393005862535727615809073410746393326688230040267160616554768771412289114113",
|
||||
expectRounds: 1,
|
||||
},
|
||||
{
|
||||
// These factors come from a real innsbruck printer in the wild with a
|
||||
// broken key generation mechanism.
|
||||
name: "innsbruck printer (4096 bit, 1 round)",
|
||||
p: "25868808535211632564072019392873831934145242707953960515208595626279836366691068618582894100813803673421320899654654938470888358089618966238341690624345530870988951109006149164192566967552401505863871260691612081236189439839963332690997129144163260418447718577834226720411404568398865166471102885763673744513186211985402019037772108416694793355840983833695882936201196462579254234744648546792097397517107797153785052856301942321429858537224127598198913168345965493941246097657533085617002572245972336841716321849601971924830462771411171570422802773095537171762650402420866468579928479284978914972383512240254605625661",
|
||||
q: "25868808535211632564072019392873831934145242707953960515208595626279836366691068618582894100813803673421320899654654938470888358089618966238341690624345530870988951109006149164192566967552401505863871260691612081236189439839963332690997129144163260418447718577834226720411404568398865166471102885763673744513186211985402019037772108416694793355840983833695882936201196462579254234744648546792097397517107797153785052856301942321429858537224127598198913168345965493941246097657533085617002572245972336841716321849601971924830462771411171570422802773095537171762650402420866468579928479284978914972383512240254605624819",
|
||||
expectRounds: 1,
|
||||
},
|
||||
{
|
||||
// FIPS requires that |p-q| > 2^(nlen/2 - 100). For example, a 2048-bit
|
||||
// RSA key must have prime factors with a difference of at least 2^924.
|
||||
// These two factors have a difference of exactly 2^924 + 4, just *barely*
|
||||
// FIPS-compliant. Their first different digit is in column 52 of this
|
||||
// file, which makes them vastly further apart than the cases above. Their
|
||||
// product cannot be factored even with 100,000,000 rounds of Fermat's
|
||||
// Algorithm.
|
||||
name: "barely FIPS compliant (2048 bit)",
|
||||
p: "151546560166767007654995655231369126386504564489055366370313539237722892921762327477057109592614214965864835328962951695621854530739049166771701397343693962526456985866167580660948398404000483264137738772983130282095332559392185543017295488346592188097443414824871619976114874896240350402349774470198190454623",
|
||||
q: "151546560166767007654995655231510939369872272987323309037144546294925352276321214430320942815891873491060949332482502812040326472743233767963240491605860423063942576391584034077877871768428333113881339606298282107984376151546711223157061364850161576363709081794948857957944390170575452970542651659150041855843",
|
||||
expectRounds: -1,
|
||||
},
|
||||
}
|
||||
|
||||
// These factors differ by slightly more than 2^256.
|
||||
p, ok = p.SetString("11779932606551869095289494662458707049283241949932278009554252037480401854504909149712949171865707598142483830639739537075502512627849249573564209082969463", 10)
|
||||
test.Assert(t, ok, "failed to create large prime")
|
||||
q, ok = q.SetString("11779932606551869095289494662458707049283241949932278009554252037480401854503793357623711855670284027157475142731886267090836872063809791989556295953329083", 10)
|
||||
test.Assert(t, ok, "failed to create large prime")
|
||||
n = n.Mul(p, q)
|
||||
err = checkPrimeFactorsTooClose(n, 13)
|
||||
test.AssertNotError(t, err, "factored n in too few iterations")
|
||||
err = checkPrimeFactorsTooClose(n, 14)
|
||||
test.AssertError(t, err, "failed to factor n")
|
||||
test.AssertContains(t, err.Error(), fmt.Sprintf("p: %s", p))
|
||||
test.AssertContains(t, err.Error(), fmt.Sprintf("q: %s", q))
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
p, ok := new(big.Int).SetString(tc.p, 10)
|
||||
if !ok {
|
||||
t.Fatalf("failed to load prime factor p (%s)", tc.p)
|
||||
}
|
||||
|
||||
q, ok := new(big.Int).SetString(tc.q, 10)
|
||||
if !ok {
|
||||
t.Fatalf("failed to load prime factor q (%s)", tc.q)
|
||||
}
|
||||
|
||||
n := new(big.Int).Mul(p, q)
|
||||
err := checkPrimeFactorsTooClose(n, 100)
|
||||
|
||||
if tc.expectRounds > 0 {
|
||||
test.AssertError(t, err, "failed to factor n")
|
||||
test.AssertContains(t, err.Error(), fmt.Sprintf("p: %s", tc.p))
|
||||
test.AssertContains(t, err.Error(), fmt.Sprintf("q: %s", tc.q))
|
||||
test.AssertContains(t, err.Error(), fmt.Sprintf("in %d rounds", tc.expectRounds))
|
||||
} else {
|
||||
test.AssertNil(t, err, "factored the unfactorable")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func benchFermat(rounds int, b *testing.B) {
|
||||
|
|
|
@ -2,8 +2,9 @@ package creds
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"math/big"
|
||||
|
@ -80,8 +81,8 @@ func TestServerTransportCredentials(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClientTransportCredentials(t *testing.T) {
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||
test.AssertNotError(t, err, "rsa.GenerateKey failed")
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
test.AssertNotError(t, err, "failed to generate test key")
|
||||
|
||||
temp := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
|
|
|
@ -91,7 +91,7 @@ func unwrapError(err error, md metadata.MD) error {
|
|||
inErrMsg,
|
||||
)
|
||||
}
|
||||
inErr := berrors.New(berrors.ErrorType(inErrType), inErrMsg) //nolint:govet
|
||||
inErr := berrors.New(berrors.ErrorType(inErrType), inErrMsg)
|
||||
var outErr *berrors.BoulderError
|
||||
if !errors.As(inErr, &outErr) {
|
||||
return fmt.Errorf(
|
||||
|
|
|
@ -36,7 +36,7 @@ func ProblemDetailsToPB(prob *probs.ProblemDetails) (*corepb.ProblemDetails, err
|
|||
return &corepb.ProblemDetails{
|
||||
ProblemType: string(prob.Type),
|
||||
Detail: prob.Detail,
|
||||
HttpStatus: int32(prob.HTTPStatus),
|
||||
HttpStatus: int32(prob.HTTPStatus), //nolint: gosec // HTTP status codes are guaranteed to be small, no risk of overflow.
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/sha1"
|
||||
"crypto/sha1" //nolint: gosec // SHA1 is required by the RFC 5019 Lightweight OCSP Profile
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/hex"
|
||||
|
|
|
@ -332,15 +332,16 @@ func (pub *Impl) singleLogSubmit(
|
|||
"http_status": "",
|
||||
}).Observe(took)
|
||||
|
||||
timestamp := time.Unix(int64(sct.Timestamp)/1000, 0)
|
||||
if time.Until(timestamp) > time.Minute {
|
||||
return nil, fmt.Errorf("SCT Timestamp was too far in the future (%s)", timestamp)
|
||||
threshold := uint64(time.Now().Add(time.Minute).UnixMilli()) //nolint: gosec // Current-ish timestamp is guaranteed to fit in a uint64
|
||||
if sct.Timestamp > threshold {
|
||||
return nil, fmt.Errorf("SCT Timestamp was too far in the future (%d > %d)", sct.Timestamp, threshold)
|
||||
}
|
||||
|
||||
// For regular certificates, we could get an old SCT, but that shouldn't
|
||||
// happen for precertificates.
|
||||
if kind != pubpb.SubmissionType_final && time.Until(timestamp) < -10*time.Minute {
|
||||
return nil, fmt.Errorf("SCT Timestamp was too far in the past (%s)", timestamp)
|
||||
threshold = uint64(time.Now().Add(-10 * time.Minute).UnixMilli()) //nolint: gosec // Current-ish timestamp is guaranteed to fit in a uint64
|
||||
if kind != pubpb.SubmissionType_final && sct.Timestamp < threshold {
|
||||
return nil, fmt.Errorf("SCT Timestamp was too far in the past (%d < %d)", sct.Timestamp, threshold)
|
||||
}
|
||||
|
||||
return sct, nil
|
||||
|
@ -371,7 +372,7 @@ func CreateTestingSignedSCT(req []string, k *ecdsa.PrivateKey, precert bool, tim
|
|||
// Sign the SCT
|
||||
rawKey, _ := x509.MarshalPKIXPublicKey(&k.PublicKey)
|
||||
logID := sha256.Sum256(rawKey)
|
||||
timestampMillis := uint64(timestamp.UnixNano()) / 1e6
|
||||
timestampMillis := uint64(timestamp.UnixMilli()) //nolint: gosec // Current-ish timestamp is guaranteed to fit in a uint64
|
||||
serialized, _ := ct.SerializeSCTSignatureInput(ct.SignedCertificateTimestamp{
|
||||
SCTVersion: ct.V1,
|
||||
LogID: ct.LogID{KeyID: logID},
|
||||
|
|
2
ra/ra.go
2
ra/ra.go
|
@ -2276,7 +2276,7 @@ func (ra *RegistrationAuthorityImpl) GenerateOCSP(ctx context.Context, req *rapb
|
|||
return ra.OCSP.GenerateOCSP(ctx, &capb.GenerateOCSPRequest{
|
||||
Serial: req.Serial,
|
||||
Status: status.Status,
|
||||
Reason: int32(status.RevokedReason),
|
||||
Reason: int32(status.RevokedReason), //nolint: gosec // Revocation reasons are guaranteed to be small, no risk of overflow.
|
||||
RevokedAt: status.RevokedDate,
|
||||
IssuerID: status.IssuerID,
|
||||
})
|
||||
|
|
|
@ -18,7 +18,7 @@ var ErrInvalidCostOverLimit = fmt.Errorf("invalid cost, must be <= limit.Burst")
|
|||
|
||||
// newIPAddressBucketKey validates and returns a bucketKey for limits that use
|
||||
// the 'enum:ipAddress' bucket key format.
|
||||
func newIPAddressBucketKey(name Name, ip net.IP) (string, error) { //nolint: unparam
|
||||
func newIPAddressBucketKey(name Name, ip net.IP) (string, error) { //nolint:unparam // Only one named rate limit uses this helper
|
||||
id := ip.String()
|
||||
err := validateIdForName(name, id)
|
||||
if err != nil {
|
||||
|
@ -78,7 +78,7 @@ func NewRegIdDomainBucketKey(name Name, regId int64, orderName string) (string,
|
|||
|
||||
// newFQDNSetBucketKey validates and returns a bucketKey for limits that use the
|
||||
// 'enum:fqdnSet' bucket key format.
|
||||
func newFQDNSetBucketKey(name Name, orderNames []string) (string, error) { //nolint: unparam
|
||||
func newFQDNSetBucketKey(name Name, orderNames []string) (string, error) { //nolint: unparam // Only one named rate limit uses this helper
|
||||
err := validateIdForName(name, strings.Join(orderNames, ","))
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
package sa
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIncrementIP(t *testing.T) {
|
||||
testCases := []struct {
|
||||
ip string
|
||||
index int
|
||||
expected string
|
||||
}{
|
||||
{"0.0.0.0", 128, "0.0.0.1"},
|
||||
{"0.0.0.255", 128, "0.0.1.0"},
|
||||
{"127.0.0.1", 128, "127.0.0.2"},
|
||||
{"1.2.3.4", 120, "1.2.4.4"},
|
||||
{"::1", 128, "::2"},
|
||||
{"2002:1001:4008::", 128, "2002:1001:4008::1"},
|
||||
{"2002:1001:4008::", 48, "2002:1001:4009::"},
|
||||
{"2002:1001:ffff::", 48, "2002:1002::"},
|
||||
{"ffff:ffff:ffff::", 48, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
ip := net.ParseIP(tc.ip).To16()
|
||||
actual := incrementIP(ip, tc.index)
|
||||
expectedIP := net.ParseIP(tc.expected)
|
||||
if !actual.Equal(expectedIP) {
|
||||
t.Errorf("Expected incrementIP(%s, %d) to be %s, instead got %s",
|
||||
tc.ip, tc.index, expectedIP, actual.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPRange(t *testing.T) {
|
||||
testCases := []struct {
|
||||
ip string
|
||||
expectedBegin string
|
||||
expectedEnd string
|
||||
}{
|
||||
{"28.45.45.28", "28.45.45.28", "28.45.45.29"},
|
||||
{"2002:1001:4008::", "2002:1001:4008::", "2002:1001:4009::"},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
ip := net.ParseIP(tc.ip)
|
||||
expectedBegin := net.ParseIP(tc.expectedBegin)
|
||||
expectedEnd := net.ParseIP(tc.expectedEnd)
|
||||
actualBegin, actualEnd := ipRange(ip)
|
||||
if !expectedBegin.Equal(actualBegin) || !expectedEnd.Equal(actualEnd) {
|
||||
t.Errorf("Expected ipRange(%s) to be (%s, %s), got (%s, %s)",
|
||||
tc.ip, tc.expectedBegin, tc.expectedEnd, actualBegin, actualEnd)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,12 +2,12 @@ package sa
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
|
@ -344,37 +344,27 @@ func insertCertificate(ctx context.Context, dbMap *db.WrappedMap, fc clock.FakeC
|
|||
SerialNumber: serialBigInt,
|
||||
}
|
||||
|
||||
testKey := makeKey()
|
||||
certDer, _ := x509.CreateCertificate(rand.Reader, &template, &template, &testKey.PublicKey, &testKey)
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generating test key: %w", err)
|
||||
}
|
||||
certDer, err := x509.CreateCertificate(rand.Reader, &template, &template, key.Public(), key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generating test cert: %w", err)
|
||||
}
|
||||
cert := &core.Certificate{
|
||||
RegistrationID: regID,
|
||||
Serial: serialString,
|
||||
Expires: template.NotAfter,
|
||||
DER: certDer,
|
||||
}
|
||||
err := dbMap.Insert(ctx, cert)
|
||||
err = dbMap.Insert(ctx, cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func bigIntFromB64(b64 string) *big.Int {
|
||||
bytes, _ := base64.URLEncoding.DecodeString(b64)
|
||||
x := big.NewInt(0)
|
||||
x.SetBytes(bytes)
|
||||
return x
|
||||
}
|
||||
|
||||
func makeKey() rsa.PrivateKey {
|
||||
n := bigIntFromB64("n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw==")
|
||||
e := int(bigIntFromB64("AQAB").Int64())
|
||||
d := bigIntFromB64("bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ==")
|
||||
p := bigIntFromB64("uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc=")
|
||||
q := bigIntFromB64("uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc=")
|
||||
return rsa.PrivateKey{PublicKey: rsa.PublicKey{N: n, E: e}, D: d, Primes: []*big.Int{p, q}}
|
||||
}
|
||||
|
||||
func TestIncidentSerialModel(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
|
|
52
sa/saro.go
52
sa/saro.go
|
@ -5,8 +5,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"net"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -156,52 +154,6 @@ func (ssa *SQLStorageAuthorityRO) GetRegistrationByKey(ctx context.Context, req
|
|||
return registrationModelToPb(model)
|
||||
}
|
||||
|
||||
// incrementIP returns a copy of `ip` incremented at a bit index `index`,
|
||||
// or in other words the first IP of the next highest subnet given a mask of
|
||||
// length `index`.
|
||||
// In order to easily account for overflow, we treat ip as a big.Int and add to
|
||||
// it. If the increment overflows the max size of a net.IP, return the highest
|
||||
// possible net.IP.
|
||||
func incrementIP(ip net.IP, index int) net.IP {
|
||||
bigInt := new(big.Int)
|
||||
bigInt.SetBytes([]byte(ip))
|
||||
incr := new(big.Int).Lsh(big.NewInt(1), 128-uint(index))
|
||||
bigInt.Add(bigInt, incr)
|
||||
// bigInt.Bytes can be shorter than 16 bytes, so stick it into a
|
||||
// full-sized net.IP.
|
||||
resultBytes := bigInt.Bytes()
|
||||
if len(resultBytes) > 16 {
|
||||
return net.ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
|
||||
}
|
||||
result := make(net.IP, 16)
|
||||
copy(result[16-len(resultBytes):], resultBytes)
|
||||
return result
|
||||
}
|
||||
|
||||
// ipRange returns a range of IP addresses suitable for querying MySQL for the
|
||||
// purpose of rate limiting using a range that is inclusive on the lower end and
|
||||
// exclusive at the higher end. If ip is an IPv4 address, it returns that address,
|
||||
// plus the one immediately higher than it. If ip is an IPv6 address, it applies
|
||||
// a /48 mask to it and returns the lowest IP in the resulting network, and the
|
||||
// first IP outside of the resulting network.
|
||||
func ipRange(ip net.IP) (net.IP, net.IP) {
|
||||
ip = ip.To16()
|
||||
// For IPv6, match on a certain subnet range, since one person can commonly
|
||||
// have an entire /48 to themselves.
|
||||
maskLength := 48
|
||||
// For IPv4 addresses, do a match on exact address, so begin = ip and end =
|
||||
// next higher IP.
|
||||
if ip.To4() != nil {
|
||||
maskLength = 128
|
||||
}
|
||||
|
||||
mask := net.CIDRMask(maskLength, 128)
|
||||
begin := ip.Mask(mask)
|
||||
end := incrementIP(begin, maskLength)
|
||||
|
||||
return begin, end
|
||||
}
|
||||
|
||||
func ReverseName(domain string) string {
|
||||
labels := strings.Split(domain, ".")
|
||||
for i, j := 0, len(labels)-1; i < j; i, j = i+1, j-1 {
|
||||
|
@ -992,7 +944,7 @@ func (ssa *SQLStorageAuthorityRO) GetRevokedCertsByShard(req *sapb.GetRevokedCer
|
|||
|
||||
return stream.Send(&corepb.CRLEntry{
|
||||
Serial: row.Serial,
|
||||
Reason: int32(row.RevokedReason),
|
||||
Reason: int32(row.RevokedReason), //nolint: gosec // Revocation reasons are guaranteed to be small, no risk of overflow.
|
||||
RevokedAt: timestamppb.New(row.RevokedDate),
|
||||
})
|
||||
})
|
||||
|
@ -1045,7 +997,7 @@ func (ssa *SQLStorageAuthorityRO) GetRevokedCerts(req *sapb.GetRevokedCertsReque
|
|||
|
||||
return stream.Send(&corepb.CRLEntry{
|
||||
Serial: row.Serial,
|
||||
Reason: int32(row.RevokedReason),
|
||||
Reason: int32(row.RevokedReason), //nolint: gosec // Revocation reasons are guaranteed to be small, no risk of overflow.
|
||||
RevokedAt: timestamppb.New(row.RevokedDate),
|
||||
})
|
||||
})
|
||||
|
|
|
@ -13,8 +13,8 @@ RUN go install github.com/rubenv/sql-migrate/sql-migrate@v1.1.2
|
|||
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.34.1
|
||||
RUN go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@bb9882e6ae58f0a80a6390b50a5ec3bd63e46a3c
|
||||
RUN go install github.com/letsencrypt/pebble/v2/cmd/pebble-challtestsrv@17d64a3
|
||||
RUN go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.60.0
|
||||
RUN go install honnef.co/go/tools/cmd/staticcheck@2024.1
|
||||
RUN go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.0
|
||||
RUN go install honnef.co/go/tools/cmd/staticcheck@2025.1
|
||||
RUN go install github.com/jsha/minica@v1.1.0
|
||||
|
||||
FROM rust:bullseye as rustdeps
|
||||
|
|
|
@ -12,7 +12,7 @@ DOCKER_REPO="letsencrypt/boulder-tools"
|
|||
# .github/workflows/release.yml,
|
||||
# .github/workflows/try-release.yml if appropriate,
|
||||
# and .github/workflows/boulder-ci.yml with the new container tag.
|
||||
GO_CI_VERSIONS=( "1.23.1" )
|
||||
GO_CI_VERSIONS=( "1.23.6" "1.24.0" )
|
||||
|
||||
echo "Please login to allow push to DockerHub"
|
||||
docker login
|
||||
|
|
|
@ -228,10 +228,7 @@ func runPersonality(p Personality) {
|
|||
m.HandleFunc("/ct/v1/add-chain", is.addChain)
|
||||
m.HandleFunc("/add-reject-host", is.addRejectHost)
|
||||
m.HandleFunc("/get-rejections", is.getRejections)
|
||||
// The gosec linter complains that ReadHeaderTimeout is not set. That's fine,
|
||||
// because this is test-only code.
|
||||
////nolint:gosec
|
||||
srv := &http.Server{
|
||||
srv := &http.Server{ //nolint: gosec // No ReadHeaderTimeout is fine for test-only code.
|
||||
Addr: p.Addr,
|
||||
Handler: m,
|
||||
}
|
||||
|
|
|
@ -3,11 +3,9 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"math/big"
|
||||
"encoding/pem"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/eggsampler/acme/v3"
|
||||
|
@ -20,101 +18,49 @@ import (
|
|||
func TestFermat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
p string
|
||||
q string
|
||||
// Create a client and complete an HTTP-01 challenge for a fake domain.
|
||||
c, err := makeClient()
|
||||
test.AssertNotError(t, err, "creating acme client")
|
||||
|
||||
domain := random_domain()
|
||||
|
||||
order, err := c.Client.NewOrder(
|
||||
c.Account, []acme.Identifier{{Type: "dns", Value: domain}})
|
||||
test.AssertNotError(t, err, "creating new order")
|
||||
test.AssertEquals(t, len(order.Authorizations), 1)
|
||||
|
||||
authUrl := order.Authorizations[0]
|
||||
|
||||
auth, err := c.Client.FetchAuthorization(c.Account, authUrl)
|
||||
test.AssertNotError(t, err, "fetching authorization")
|
||||
|
||||
chal, ok := auth.ChallengeMap[acme.ChallengeTypeHTTP01]
|
||||
test.Assert(t, ok, "getting HTTP-01 challenge")
|
||||
|
||||
err = addHTTP01Response(chal.Token, chal.KeyAuthorization)
|
||||
defer delHTTP01Response(chal.Token)
|
||||
test.AssertNotError(t, err, "adding HTTP-01 response")
|
||||
|
||||
chal, err = c.Client.UpdateChallenge(c.Account, chal)
|
||||
test.AssertNotError(t, err, "updating HTTP-01 challenge")
|
||||
|
||||
// Load the Fermat-weak CSR that we'll submit for finalize. This CSR was
|
||||
// generated using test/integration/testdata/fermat_csr.go, has prime factors
|
||||
// that differ by only 2^516 + 254, and can be factored in 42 rounds.
|
||||
csrPem, err := os.ReadFile("test/integration/testdata/fermat_csr.pem")
|
||||
test.AssertNotError(t, err, "reading CSR PEM from disk")
|
||||
|
||||
csrDer, _ := pem.Decode(csrPem)
|
||||
if csrDer == nil {
|
||||
t.Fatal("failed to decode CSR PEM")
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
name: "canon printer (2048 bit, 1 round)",
|
||||
p: "155536235030272749691472293262418471207550926406427515178205576891522284497518443889075039382254334975506248481615035474816604875321501901699955105345417152355947783063521554077194367454070647740704883461064399268622437721385112646454393005862535727615809073410746393326688230040267160616554768771412289114449",
|
||||
q: "155536235030272749691472293262418471207550926406427515178205576891522284497518443889075039382254334975506248481615035474816604875321501901699955105345417152355947783063521554077194367454070647740704883461064399268622437721385112646454393005862535727615809073410746393326688230040267160616554768771412289114113",
|
||||
},
|
||||
{
|
||||
name: "innsbruck printer (4096 bit, 1 round)",
|
||||
p: "25868808535211632564072019392873831934145242707953960515208595626279836366691068618582894100813803673421320899654654938470888358089618966238341690624345530870988951109006149164192566967552401505863871260691612081236189439839963332690997129144163260418447718577834226720411404568398865166471102885763673744513186211985402019037772108416694793355840983833695882936201196462579254234744648546792097397517107797153785052856301942321429858537224127598198913168345965493941246097657533085617002572245972336841716321849601971924830462771411171570422802773095537171762650402420866468579928479284978914972383512240254605625661",
|
||||
q: "25868808535211632564072019392873831934145242707953960515208595626279836366691068618582894100813803673421320899654654938470888358089618966238341690624345530870988951109006149164192566967552401505863871260691612081236189439839963332690997129144163260418447718577834226720411404568398865166471102885763673744513186211985402019037772108416694793355840983833695882936201196462579254234744648546792097397517107797153785052856301942321429858537224127598198913168345965493941246097657533085617002572245972336841716321849601971924830462771411171570422802773095537171762650402420866468579928479284978914972383512240254605624819",
|
||||
},
|
||||
// Ideally we'd have a 2408-bit, nearly-100-rounds test case, but it turns
|
||||
// out purposefully generating keys that require 1 < N < 100 rounds to be
|
||||
// factored is surprisingly tricky.
|
||||
}
|
||||
csr, err := x509.ParseCertificateRequest(csrDer.Bytes)
|
||||
test.AssertNotError(t, err, "parsing CSR")
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a client and complete an HTTP-01 challenge for a fake domain.
|
||||
c, err := makeClient()
|
||||
test.AssertNotError(t, err, "creating acme client")
|
||||
|
||||
domain := random_domain()
|
||||
|
||||
order, err := c.Client.NewOrder(
|
||||
c.Account, []acme.Identifier{{Type: "dns", Value: domain}})
|
||||
test.AssertNotError(t, err, "creating new order")
|
||||
test.AssertEquals(t, len(order.Authorizations), 1)
|
||||
|
||||
authUrl := order.Authorizations[0]
|
||||
|
||||
auth, err := c.Client.FetchAuthorization(c.Account, authUrl)
|
||||
test.AssertNotError(t, err, "fetching authorization")
|
||||
|
||||
chal, ok := auth.ChallengeMap[acme.ChallengeTypeHTTP01]
|
||||
test.Assert(t, ok, "getting HTTP-01 challenge")
|
||||
|
||||
err = addHTTP01Response(chal.Token, chal.KeyAuthorization)
|
||||
defer delHTTP01Response(chal.Token)
|
||||
test.AssertNotError(t, err, "adding HTTP-01 response")
|
||||
|
||||
chal, err = c.Client.UpdateChallenge(c.Account, chal)
|
||||
test.AssertNotError(t, err, "updating HTTP-01 challenge")
|
||||
|
||||
// Reconstruct the public modulus N from the test case's prime factors.
|
||||
p, ok := new(big.Int).SetString(tc.p, 10)
|
||||
test.Assert(t, ok, "failed to create large prime")
|
||||
q, ok := new(big.Int).SetString(tc.q, 10)
|
||||
test.Assert(t, ok, "failed to create large prime")
|
||||
n := new(big.Int).Mul(p, q)
|
||||
|
||||
// Reconstruct the private exponent D from the test case's prime factors.
|
||||
p_1 := new(big.Int).Sub(p, big.NewInt(1))
|
||||
q_1 := new(big.Int).Sub(q, big.NewInt(1))
|
||||
field := new(big.Int).Mul(p_1, q_1)
|
||||
d := new(big.Int).ModInverse(big.NewInt(65537), field)
|
||||
|
||||
// Create a CSR containing the reconstructed pubkey and signed with the
|
||||
// reconstructed private key.
|
||||
pubkey := rsa.PublicKey{
|
||||
N: n,
|
||||
E: 65537,
|
||||
}
|
||||
|
||||
privkey := rsa.PrivateKey{
|
||||
PublicKey: pubkey,
|
||||
D: d,
|
||||
Primes: []*big.Int{p, q},
|
||||
}
|
||||
|
||||
csrDer, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{
|
||||
SignatureAlgorithm: x509.SHA256WithRSA,
|
||||
PublicKeyAlgorithm: x509.RSA,
|
||||
PublicKey: &pubkey,
|
||||
Subject: pkix.Name{CommonName: domain},
|
||||
DNSNames: []string{domain},
|
||||
}, &privkey)
|
||||
test.AssertNotError(t, err, "creating CSR")
|
||||
|
||||
csr, err := x509.ParseCertificateRequest(csrDer)
|
||||
test.AssertNotError(t, err, "parsing CSR")
|
||||
|
||||
// Finalizing the order should fail as we reject the public key.
|
||||
_, err = c.Client.FinalizeOrder(c.Account, order, csr)
|
||||
test.AssertError(t, err, "finalizing order")
|
||||
test.AssertContains(t, err.Error(), "urn:ietf:params:acme:error:badCSR")
|
||||
test.AssertContains(t, err.Error(), "key generated with factors too close together")
|
||||
})
|
||||
}
|
||||
// Finalizing the order should fail as we reject the public key.
|
||||
_, err = c.Client.FinalizeOrder(c.Account, order, csr)
|
||||
test.AssertError(t, err, "finalizing order")
|
||||
test.AssertContains(t, err.Error(), "urn:ietf:params:acme:error:badCSR")
|
||||
test.AssertContains(t, err.Error(), "key generated with factors too close together")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/big"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
// bits is the size of the resulting RSA key, also known as "nlen" or "Length
|
||||
// of the modulus N". Usually 1024, 2048, or 4096.
|
||||
bits = 2048
|
||||
// gap is the exponent of the different between the prime factors of the RSA
|
||||
// key, i.e. |p-q| ~= 2^gap. For FIPS compliance, set this to (bits/2 - 100).
|
||||
gap = 516
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Generate q, which will be the smaller of the two factors. We set its length
|
||||
// so that the product of two similarly-sized factors will be the desired
|
||||
// bit length.
|
||||
q, err := rand.Prime(rand.Reader, (bits+1)/2)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// Our starting point for p is q + 2^gap.
|
||||
p := new(big.Int).Add(q, new(big.Int).Exp(big.NewInt(2), big.NewInt(gap), nil))
|
||||
|
||||
// Now we just keep incrementing P until we find a prime. You might think
|
||||
// this would take a while, but it won't: there are a lot of primes.
|
||||
attempts := 0
|
||||
for {
|
||||
// Using 34 rounds of Miller-Rabin primality testing is enough for the go
|
||||
// stdlib, so it's enough for us.
|
||||
if p.ProbablyPrime(34) {
|
||||
break
|
||||
}
|
||||
|
||||
// We know P is odd because it started as a prime (odd) plus a power of two
|
||||
// (even), so we can increment by 2 to remain odd.
|
||||
p.Add(p, big.NewInt(2))
|
||||
attempts++
|
||||
}
|
||||
|
||||
fmt.Println("p:", p.String())
|
||||
fmt.Println("q:", q.String())
|
||||
fmt.Println("Differ by", fmt.Sprintf("2^%d + %d", gap, 2*attempts))
|
||||
|
||||
// Construct the public modulus N from the prime factors.
|
||||
n := new(big.Int).Mul(p, q)
|
||||
|
||||
// Construct the public key from the modulus and (fixed) public exponent.
|
||||
pubkey := rsa.PublicKey{
|
||||
N: n,
|
||||
E: 65537,
|
||||
}
|
||||
|
||||
// Construct the private exponent D from the prime factors.
|
||||
p_1 := new(big.Int).Sub(p, big.NewInt(1))
|
||||
q_1 := new(big.Int).Sub(q, big.NewInt(1))
|
||||
field := new(big.Int).Mul(p_1, q_1)
|
||||
d := new(big.Int).ModInverse(big.NewInt(65537), field)
|
||||
|
||||
// Construct the private key from the factors and private exponent.
|
||||
privkey := rsa.PrivateKey{
|
||||
PublicKey: pubkey,
|
||||
D: d,
|
||||
Primes: []*big.Int{p, q},
|
||||
}
|
||||
privkey.Precompute()
|
||||
|
||||
// Sign a CSR using this key, so we can use it in integration tests.
|
||||
// Note that this step *only works on go1.23 and earlier*. Later versions of
|
||||
// go detect that the prime factors are too close together and refuse to
|
||||
// produce a signature.
|
||||
csrDER, err := x509.CreateCertificateRequest(
|
||||
rand.Reader,
|
||||
&x509.CertificateRequest{
|
||||
Subject: pkix.Name{CommonName: "example.com"},
|
||||
PublicKey: &pubkey,
|
||||
},
|
||||
&privkey)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
csrPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE REQUEST",
|
||||
Bytes: csrDER,
|
||||
})
|
||||
fmt.Fprint(os.Stdout, string(csrPEM))
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICWzCCAUMCAQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQCVIY5cKFJU+qqXCtls7VA+oAwcDnsIk3W8+4ZO
|
||||
y5vKEk3Ye9rWglKPqHSDvr4UdEv5cP6RaByWaL7PUswIPwQD8HFywR84V82+3pl+
|
||||
sEVo88M3HK1ZwI19FcmsaZn3Zh0gVymEYi4VJof2toYUK8M2DRjJGvVrnpG2P6y0
|
||||
VKpq7jBTR6G8PXr4q2JjGJaBci1Bzw2sWMUcyfOdIpdKpe185e7WSl9N0YT4pg7t
|
||||
lHMoGHWYPQ6Pd7TR6EmGzKs+MThsWhREx91ViA9UmYe4n607lGevm2nHV2PJ09PR
|
||||
tn+136BIE30E4uVgPVuHp5y36PKylfA5NHA9M0TMgpn0AK0/AgMBAAGgADANBgkq
|
||||
hkiG9w0BAQsFAAOCAQEAk3xNRIahAtVzlygRwh57gRBqEi6uJXh651rNSSdvk1YA
|
||||
MR4bhkA9IXSwrOlb8euRWGdRMnxSqx+16OqZ0MDGrTMg3RaQoSkmFo28zbMNtHgd
|
||||
4243lzDF0KrZCSyQHh9bSmcMuPjbCRPZJObg70ALw1K2pdrUamTh7EjKWPbGA3hg
|
||||
lrfl9RsMzC/6UDUoMUyCHRJx6pT6t6PwDl8g+tesQemnVxKNEY8WZOyf/1uEEhNb
|
||||
1PmpgfnV+NQp3sOXSLsxlDpl0zRlbWq6QGnvW2O6FalxoVSZ3WIXX/FyT2rxePWg
|
||||
LDaCwR0qj4byFL2On7FsbU4Wfx6bD70cplaxfv8uQQ==
|
||||
-----END CERTIFICATE REQUEST-----
|
|
@ -1,16 +1,14 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
|
@ -153,10 +151,9 @@ func newAccount(s *State, c *acmeCache) error {
|
|||
func randDomain(base string) string {
|
||||
// This approach will cause some repeat domains but not enough to make rate
|
||||
// limits annoying!
|
||||
n := time.Now().UnixNano()
|
||||
b := new(bytes.Buffer)
|
||||
binary.Write(b, binary.LittleEndian, n)
|
||||
return fmt.Sprintf("%x.%s", sha1.Sum(b.Bytes()), base)
|
||||
var bytes [3]byte
|
||||
_, _ = rand.Read(bytes[:])
|
||||
return hex.EncodeToString(bytes[:]) + base
|
||||
}
|
||||
|
||||
// newOrder creates a new pending order object for a random set of domains using
|
||||
|
|
|
@ -233,10 +233,7 @@ func main() {
|
|||
|
||||
srv.setupHTTP(http.DefaultServeMux)
|
||||
go func() {
|
||||
// The gosec linter complains that timeouts cannot be set here. That's fine,
|
||||
// because this is test-only code.
|
||||
////nolint:gosec
|
||||
err := http.ListenAndServe(*listenAPI, http.DefaultServeMux)
|
||||
err := http.ListenAndServe(*listenAPI, http.DefaultServeMux) //nolint: gosec // No request timeout is fine for test-only code.
|
||||
if err != nil {
|
||||
log.Fatalln("Couldn't start HTTP server", err)
|
||||
}
|
||||
|
|
|
@ -9,9 +9,10 @@ import (
|
|||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/letsencrypt/boulder/test/ocsp/helper"
|
||||
prom "github.com/prometheus/client_golang/prometheus"
|
||||
promhttp "github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
|
||||
"github.com/letsencrypt/boulder/test/ocsp/helper"
|
||||
)
|
||||
|
||||
var listenAddress = flag.String("listen", ":8080", "Port to listen on")
|
||||
|
@ -86,10 +87,7 @@ func main() {
|
|||
}
|
||||
http.Handle("/metrics", promhttp.Handler())
|
||||
go func() {
|
||||
// The gosec linter complains that timeouts cannot be set here. That's fine,
|
||||
// because this is test-only code.
|
||||
////nolint:gosec
|
||||
err := http.ListenAndServe(*listenAddress, nil)
|
||||
err := http.ListenAndServe(*listenAddress, nil) //nolint: gosec // No request timeout is fine for test-only code.
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -1249,7 +1249,7 @@ func TestHTTPKeyAuthorizationFileMismatch(t *testing.T) {
|
|||
if err == nil {
|
||||
t.Fatalf("Expected validation to fail when file mismatched.")
|
||||
}
|
||||
expected := `The key authorization file from the server did not match this challenge. Expected "LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowyjxAjEuX0.9jg46WB3rR_AHD-EBXdN7cBkH1WOu0tA3M9fm21mqTI" (got "\xef\xffAABBCC")`
|
||||
expected := fmt.Sprintf(`The key authorization file from the server did not match this challenge. Expected "%s" (got "\xef\xffAABBCC")`, expectedKeyAuthorization)
|
||||
if err.Error() != expected {
|
||||
t.Errorf("validation failed with %s, expected %s", err, expected)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package va
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
|
@ -29,8 +31,26 @@ import (
|
|||
"github.com/letsencrypt/boulder/test"
|
||||
)
|
||||
|
||||
func tlsCertTemplate(names []string) *x509.Certificate {
|
||||
return &x509.Certificate{
|
||||
// acmeExtension returns the ACME TLS-ALPN-01 extension for the given key
|
||||
// authorization. The OID can also be changed for the sake of testing.
|
||||
func acmeExtension(oid asn1.ObjectIdentifier, keyAuthorization string) pkix.Extension {
|
||||
shasum := sha256.Sum256([]byte(keyAuthorization))
|
||||
encHash, _ := asn1.Marshal(shasum[:])
|
||||
return pkix.Extension{
|
||||
Id: oid,
|
||||
Critical: true,
|
||||
Value: encHash,
|
||||
}
|
||||
}
|
||||
|
||||
// testACMEExt is the ACME TLS-ALPN-01 extension with the default OID and
|
||||
// key authorization used in most tests.
|
||||
var testACMEExt = acmeExtension(IdPeAcmeIdentifier, expectedKeyAuthorization)
|
||||
|
||||
// testTLSCert returns a ready-to-use self-signed certificate with the given
|
||||
// SANs and Extensions. It generates a new ECDSA key on each call.
|
||||
func testTLSCert(names []string, extensions []pkix.Extension) *tls.Certificate {
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1337),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"tests"},
|
||||
|
@ -38,43 +58,31 @@ func tlsCertTemplate(names []string) *x509.Certificate {
|
|||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(0, 0, 1),
|
||||
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
|
||||
DNSNames: names,
|
||||
DNSNames: names,
|
||||
ExtraExtensions: extensions,
|
||||
}
|
||||
}
|
||||
key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
certBytes, _ := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
|
||||
|
||||
func makeACert(names []string) *tls.Certificate {
|
||||
template := tlsCertTemplate(names)
|
||||
certBytes, _ := x509.CreateCertificate(rand.Reader, template, template, &TheKey.PublicKey, &TheKey)
|
||||
return &tls.Certificate{
|
||||
Certificate: [][]byte{certBytes},
|
||||
PrivateKey: &TheKey,
|
||||
PrivateKey: key,
|
||||
}
|
||||
}
|
||||
|
||||
// tlssniSrvWithNames is kept around for the use of TestValidateTLSALPN01UnawareSrv
|
||||
func tlssniSrvWithNames(t *testing.T, names ...string) *httptest.Server {
|
||||
t.Helper()
|
||||
|
||||
cert := makeACert(names)
|
||||
tlsConfig := &tls.Config{
|
||||
Certificates: []tls.Certificate{*cert},
|
||||
ClientAuth: tls.NoClientCert,
|
||||
GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
return cert, nil
|
||||
},
|
||||
NextProtos: []string{"http/1.1"},
|
||||
}
|
||||
|
||||
hs := httptest.NewUnstartedServer(http.DefaultServeMux)
|
||||
hs.TLS = tlsConfig
|
||||
hs.StartTLS()
|
||||
return hs
|
||||
// testACMECert returns a certificate with the correctly-formed ACME TLS-ALPN-01
|
||||
// extension with our default test values. Use acmeExtension and testCert if you
|
||||
// need to customize the contents of that extension.
|
||||
func testACMECert(names ...string) *tls.Certificate {
|
||||
return testTLSCert(names, []pkix.Extension{testACMEExt})
|
||||
}
|
||||
|
||||
// tlsalpn01SrvWithCert creates a test server which will present the given
|
||||
// certificate when asked to do a tls-alpn-01 handshake.
|
||||
func tlsalpn01SrvWithCert(t *testing.T, acmeCert *tls.Certificate, tlsVersion uint16) *httptest.Server {
|
||||
t.Helper()
|
||||
|
||||
|
@ -100,46 +108,19 @@ func tlsalpn01SrvWithCert(t *testing.T, acmeCert *tls.Certificate, tlsVersion ui
|
|||
return hs
|
||||
}
|
||||
|
||||
func tlsalpn01Srv(
|
||||
t *testing.T,
|
||||
keyAuthorization string,
|
||||
oid asn1.ObjectIdentifier,
|
||||
tlsVersion uint16,
|
||||
names ...string) (*httptest.Server, error) {
|
||||
template := tlsCertTemplate(names)
|
||||
|
||||
shasum := sha256.Sum256([]byte(keyAuthorization))
|
||||
encHash, err := asn1.Marshal(shasum[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
acmeExtension := pkix.Extension{
|
||||
Id: oid,
|
||||
Critical: true,
|
||||
Value: encHash,
|
||||
}
|
||||
template.ExtraExtensions = []pkix.Extension{acmeExtension}
|
||||
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, template, template, &TheKey.PublicKey, &TheKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
acmeCert := &tls.Certificate{
|
||||
Certificate: [][]byte{certBytes},
|
||||
PrivateKey: &TheKey,
|
||||
}
|
||||
|
||||
return tlsalpn01SrvWithCert(t, acmeCert, tlsVersion), nil
|
||||
// testTLSALPN01Srv creates a test server with all default values, for tests
|
||||
// that don't need to customize specific names or extensions in the certificate
|
||||
// served by the TLS server.
|
||||
func testTLSALPN01Srv(t *testing.T) *httptest.Server {
|
||||
return tlsalpn01SrvWithCert(t, testACMECert("expected"), 0)
|
||||
}
|
||||
|
||||
func TestTLSALPN01FailIP(t *testing.T) {
|
||||
hs, err := tlsalpn01Srv(t, expectedKeyAuthorization, IdPeAcmeIdentifier, 0, "expected")
|
||||
test.AssertNotError(t, err, "Error creating test server")
|
||||
hs := testTLSALPN01Srv(t)
|
||||
|
||||
va, _ := setup(hs, "", nil, nil)
|
||||
|
||||
_, err = va.validateTLSALPN01(ctx, identifier.NewIP(netip.MustParseAddr("127.0.0.1")), expectedKeyAuthorization)
|
||||
_, err := va.validateTLSALPN01(ctx, identifier.NewIP(netip.MustParseAddr("127.0.0.1")), expectedKeyAuthorization)
|
||||
if err == nil {
|
||||
t.Fatalf("IdentifierType IP shouldn't have worked.")
|
||||
}
|
||||
|
@ -148,12 +129,13 @@ func TestTLSALPN01FailIP(t *testing.T) {
|
|||
}
|
||||
|
||||
func slowTLSSrv() *httptest.Server {
|
||||
cert := testTLSCert([]string{"nomatter"}, nil)
|
||||
server := httptest.NewUnstartedServer(http.DefaultServeMux)
|
||||
server.TLS = &tls.Config{
|
||||
NextProtos: []string{"http/1.1", ACMETLS1Protocol},
|
||||
GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
return makeACert([]string{"nomatter"}), nil
|
||||
return cert, nil
|
||||
},
|
||||
}
|
||||
server.StartTLS()
|
||||
|
@ -246,13 +228,13 @@ func TestTLSALPN01DialTimeout(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTLSALPN01Refused(t *testing.T) {
|
||||
hs, err := tlsalpn01Srv(t, expectedKeyAuthorization, IdPeAcmeIdentifier, 0, "expected")
|
||||
test.AssertNotError(t, err, "Error creating test server")
|
||||
hs := testTLSALPN01Srv(t)
|
||||
|
||||
va, _ := setup(hs, "", nil, nil)
|
||||
|
||||
// Take down validation server and check that validation fails.
|
||||
hs.Close()
|
||||
_, err = va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
_, err := va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
if err == nil {
|
||||
t.Fatalf("Server's down; expected refusal. Where did we connect?")
|
||||
}
|
||||
|
@ -265,14 +247,15 @@ func TestTLSALPN01Refused(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTLSALPN01TalkingToHTTP(t *testing.T) {
|
||||
hs, err := tlsalpn01Srv(t, expectedKeyAuthorization, IdPeAcmeIdentifier, 0, "expected")
|
||||
test.AssertNotError(t, err, "Error creating test server")
|
||||
hs := testTLSALPN01Srv(t)
|
||||
|
||||
va, _ := setup(hs, "", nil, nil)
|
||||
|
||||
// Make the server only speak HTTP.
|
||||
httpOnly := httpSrv(t, "")
|
||||
va.tlsPort = getPort(httpOnly)
|
||||
|
||||
_, err = va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
_, err := va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
test.AssertError(t, err, "TLS-SNI-01 validation passed when talking to a HTTP-only server")
|
||||
prob := detailedError(err)
|
||||
expected := "Server only speaks HTTP, not TLS"
|
||||
|
@ -359,6 +342,16 @@ func TestCertNames(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
// Round-trip the certificate through generation and parsing, to make sure
|
||||
// certAltNames can handle "real" certificates and not just templates.
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
test.AssertNotError(t, err, "Error creating test key")
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
|
||||
test.AssertNotError(t, err, "Error creating certificate")
|
||||
|
||||
cert, err := x509.ParseCertificate(certBytes)
|
||||
test.AssertNotError(t, err, "Error parsing certificate")
|
||||
|
||||
// We expect only unique names, in sorted order.
|
||||
expected := []string{
|
||||
"192.168.0.1",
|
||||
|
@ -371,26 +364,18 @@ func TestCertNames(t *testing.T) {
|
|||
"hello@world.gov",
|
||||
}
|
||||
|
||||
// Create the certificate, check that certNames provides the expected result
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, template, template, &TheKey.PublicKey, &TheKey)
|
||||
test.AssertNotError(t, err, "Error creating certificate")
|
||||
|
||||
cert, err := x509.ParseCertificate(certBytes)
|
||||
test.AssertNotError(t, err, "Error parsing certificate")
|
||||
|
||||
actual := certAltNames(cert)
|
||||
test.AssertDeepEquals(t, actual, expected)
|
||||
}
|
||||
|
||||
func TestTLSALPN01Success(t *testing.T) {
|
||||
hs, err := tlsalpn01Srv(t, expectedKeyAuthorization, IdPeAcmeIdentifier, 0, "expected")
|
||||
test.AssertNotError(t, err, "Error creating test server")
|
||||
hs := testTLSALPN01Srv(t)
|
||||
|
||||
va, _ := setup(hs, "", nil, nil)
|
||||
|
||||
_, prob := va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
if prob != nil {
|
||||
t.Errorf("Validation failed: %v", prob)
|
||||
_, err := va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
if err != nil {
|
||||
t.Errorf("Validation failed: %v", err)
|
||||
}
|
||||
test.AssertMetricWithLabelsEquals(
|
||||
t, va.metrics.tlsALPNOIDCounter, prometheus.Labels{"oid": IdPeAcmeIdentifier.String()}, 1)
|
||||
|
@ -408,25 +393,25 @@ func TestTLSALPN01ObsoleteFailure(t *testing.T) {
|
|||
// id-pe OID + 30 (acmeIdentifier) + 1 (v1)
|
||||
IdPeAcmeIdentifierV1Obsolete := asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 30, 1}
|
||||
|
||||
hs, err := tlsalpn01Srv(t, expectedKeyAuthorization, IdPeAcmeIdentifierV1Obsolete, 0, "expected")
|
||||
test.AssertNotError(t, err, "Error creating test server")
|
||||
cert := testTLSCert([]string{"expected"}, []pkix.Extension{acmeExtension(IdPeAcmeIdentifierV1Obsolete, expectedKeyAuthorization)})
|
||||
hs := tlsalpn01SrvWithCert(t, cert, 0)
|
||||
|
||||
va, _ := setup(hs, "", nil, nil)
|
||||
|
||||
_, prob := va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
test.AssertNotNil(t, prob, "expected validation to fail")
|
||||
_, err := va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
test.AssertNotNil(t, err, "expected validation to fail")
|
||||
test.AssertContains(t, err.Error(), "Required extension OID 1.3.6.1.5.5.7.1.31 is not present")
|
||||
}
|
||||
|
||||
func TestValidateTLSALPN01BadChallenge(t *testing.T) {
|
||||
badKeyAuthorization := ka("bad token")
|
||||
|
||||
hs, err := tlsalpn01Srv(t, badKeyAuthorization, IdPeAcmeIdentifier, 0, "expected")
|
||||
test.AssertNotError(t, err, "Error creating test server")
|
||||
cert := testTLSCert([]string{"expected"}, []pkix.Extension{acmeExtension(IdPeAcmeIdentifier, badKeyAuthorization)})
|
||||
hs := tlsalpn01SrvWithCert(t, cert, 0)
|
||||
|
||||
va, _ := setup(hs, "", nil, nil)
|
||||
|
||||
_, err = va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
|
||||
_, err := va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
if err == nil {
|
||||
t.Fatalf("TLS ALPN validation should have failed.")
|
||||
}
|
||||
|
@ -456,7 +441,17 @@ func TestValidateTLSALPN01BrokenSrv(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestValidateTLSALPN01UnawareSrv(t *testing.T) {
|
||||
hs := tlssniSrvWithNames(t, "expected")
|
||||
cert := testTLSCert([]string{"expected"}, nil)
|
||||
hs := httptest.NewUnstartedServer(http.DefaultServeMux)
|
||||
hs.TLS = &tls.Config{
|
||||
Certificates: []tls.Certificate{},
|
||||
ClientAuth: tls.NoClientCert,
|
||||
GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
return cert, nil
|
||||
},
|
||||
NextProtos: []string{"http/1.1"}, // Doesn't list ACMETLS1Protocol
|
||||
}
|
||||
hs.StartTLS()
|
||||
|
||||
va, _ := setup(hs, "", nil, nil)
|
||||
|
||||
|
@ -468,22 +463,11 @@ func TestValidateTLSALPN01UnawareSrv(t *testing.T) {
|
|||
test.AssertEquals(t, prob.Type, probs.TLSProblem)
|
||||
}
|
||||
|
||||
// TestValidateTLSALPN01BadUTFSrv tests that validating TLS-ALPN-01 against
|
||||
// a host that returns a certificate with a SAN/CN that contains invalid UTF-8
|
||||
// will result in a problem with the invalid UTF-8.
|
||||
func TestValidateTLSALPN01BadUTFSrv(t *testing.T) {
|
||||
_, err := tlsalpn01Srv(t, expectedKeyAuthorization, IdPeAcmeIdentifier, 0, "expected", "\xf0\x28\x8c\xbc")
|
||||
test.AssertContains(t, err.Error(), "cannot be encoded as an IA5String")
|
||||
}
|
||||
|
||||
// TestValidateTLSALPN01MalformedExtnValue tests that validating TLS-ALPN-01
|
||||
// against a host that returns a certificate that contains an ASN.1 DER
|
||||
// acmeValidation extension value that does not parse or is the wrong length
|
||||
// will result in an Unauthorized problem
|
||||
func TestValidateTLSALPN01MalformedExtnValue(t *testing.T) {
|
||||
names := []string{"expected"}
|
||||
template := tlsCertTemplate(names)
|
||||
|
||||
wrongTypeDER, _ := asn1.Marshal("a string")
|
||||
wrongLengthDER, _ := asn1.Marshal(make([]byte, 31))
|
||||
badExtensions := []pkix.Extension{
|
||||
|
@ -500,13 +484,7 @@ func TestValidateTLSALPN01MalformedExtnValue(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, badExt := range badExtensions {
|
||||
template.ExtraExtensions = []pkix.Extension{badExt}
|
||||
certBytes, _ := x509.CreateCertificate(rand.Reader, template, template, &TheKey.PublicKey, &TheKey)
|
||||
acmeCert := &tls.Certificate{
|
||||
Certificate: [][]byte{certBytes},
|
||||
PrivateKey: &TheKey,
|
||||
}
|
||||
|
||||
acmeCert := testTLSCert([]string{"expected"}, []pkix.Extension{badExt})
|
||||
hs := tlsalpn01SrvWithCert(t, acmeCert, 0)
|
||||
va, _ := setup(hs, "", nil, nil)
|
||||
|
||||
|
@ -526,6 +504,8 @@ func TestValidateTLSALPN01MalformedExtnValue(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTLSALPN01TLSVersion(t *testing.T) {
|
||||
cert := testACMECert("expected")
|
||||
|
||||
for _, tc := range []struct {
|
||||
version uint16
|
||||
expectError bool
|
||||
|
@ -544,21 +524,21 @@ func TestTLSALPN01TLSVersion(t *testing.T) {
|
|||
},
|
||||
} {
|
||||
// Create a server that only negotiates the given TLS version
|
||||
hs, err := tlsalpn01Srv(t, expectedKeyAuthorization, IdPeAcmeIdentifier, tc.version, "expected")
|
||||
test.AssertNotError(t, err, "Error creating test server")
|
||||
hs := tlsalpn01SrvWithCert(t, cert, tc.version)
|
||||
|
||||
va, _ := setup(hs, "", nil, nil)
|
||||
|
||||
_, prob := va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
_, err := va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
if !tc.expectError {
|
||||
if prob != nil {
|
||||
t.Errorf("expected success, got: %v", prob)
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got: %v", err)
|
||||
}
|
||||
// The correct TLS-ALPN-01 OID counter should have been incremented
|
||||
test.AssertMetricWithLabelsEquals(
|
||||
t, va.metrics.tlsALPNOIDCounter, prometheus.Labels{"oid": IdPeAcmeIdentifier.String()}, 1)
|
||||
} else {
|
||||
test.AssertNotNil(t, prob, "expected validation error")
|
||||
test.AssertNotNil(t, err, "expected validation error")
|
||||
test.AssertContains(t, err.Error(), "protocol version not supported")
|
||||
test.AssertMetricWithLabelsEquals(
|
||||
t, va.metrics.tlsALPNOIDCounter, prometheus.Labels{"oid": IdPeAcmeIdentifier.String()}, 0)
|
||||
}
|
||||
|
@ -569,29 +549,30 @@ func TestTLSALPN01TLSVersion(t *testing.T) {
|
|||
|
||||
func TestTLSALPN01WrongName(t *testing.T) {
|
||||
// Create a cert with a different name from what we're validating
|
||||
hs, err := tlsalpn01Srv(t, expectedKeyAuthorization, IdPeAcmeIdentifier, tls.VersionTLS12, "incorrect")
|
||||
test.AssertNotError(t, err, "failed to set up tls-alpn-01 server")
|
||||
hs := tlsalpn01SrvWithCert(t, testACMECert("incorrect"), 0)
|
||||
|
||||
va, _ := setup(hs, "", nil, nil)
|
||||
|
||||
_, prob := va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
test.AssertError(t, prob, "validation should have failed")
|
||||
_, err := va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
test.AssertError(t, err, "validation should have failed")
|
||||
test.AssertContains(t, err.Error(), "dNSName does not match expected identifier")
|
||||
}
|
||||
|
||||
func TestTLSALPN01ExtraNames(t *testing.T) {
|
||||
// Create a cert with two names when we only want to validate one.
|
||||
hs, err := tlsalpn01Srv(t, expectedKeyAuthorization, IdPeAcmeIdentifier, tls.VersionTLS12, "expected", "extra")
|
||||
test.AssertNotError(t, err, "failed to set up tls-alpn-01 server")
|
||||
hs := tlsalpn01SrvWithCert(t, testACMECert("expected", "extra"), 0)
|
||||
|
||||
va, _ := setup(hs, "", nil, nil)
|
||||
|
||||
_, prob := va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
test.AssertError(t, prob, "validation should have failed")
|
||||
_, err := va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
test.AssertError(t, err, "validation should have failed")
|
||||
test.AssertContains(t, err.Error(), "wrong number of dNSNames")
|
||||
}
|
||||
|
||||
func TestTLSALPN01NotSelfSigned(t *testing.T) {
|
||||
// Create a cert with an extra non-dnsName identifier.
|
||||
template := &x509.Certificate{
|
||||
// Create a normal-looking cert. We don't use testTLSCert because we need to
|
||||
// control the issuer.
|
||||
eeTemplate := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1337),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"tests"},
|
||||
|
@ -602,22 +583,15 @@ func TestTLSALPN01NotSelfSigned(t *testing.T) {
|
|||
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
|
||||
DNSNames: []string{"expected"},
|
||||
IPAddresses: []net.IP{net.ParseIP("192.168.0.1")},
|
||||
DNSNames: []string{"expected"},
|
||||
IPAddresses: []net.IP{net.ParseIP("192.168.0.1")},
|
||||
ExtraExtensions: []pkix.Extension{testACMEExt},
|
||||
}
|
||||
|
||||
shasum := sha256.Sum256([]byte(expectedKeyAuthorization))
|
||||
encHash, err := asn1.Marshal(shasum[:])
|
||||
test.AssertNotError(t, err, "failed to create key authorization")
|
||||
eeKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
test.AssertNotError(t, err, "creating test key")
|
||||
|
||||
acmeExtension := pkix.Extension{
|
||||
Id: IdPeAcmeIdentifier,
|
||||
Critical: true,
|
||||
Value: encHash,
|
||||
}
|
||||
template.ExtraExtensions = []pkix.Extension{acmeExtension}
|
||||
|
||||
parent := &x509.Certificate{
|
||||
issuerCert := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1234),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"testissuer"},
|
||||
|
@ -627,14 +601,74 @@ func TestTLSALPN01NotSelfSigned(t *testing.T) {
|
|||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
// Note that this currently only tests that the subject and issuer are the
|
||||
// same; it does not test the case where the cert is signed by a different key.
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, template, parent, &TheKey.PublicKey, &TheKey)
|
||||
issuerKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
test.AssertNotError(t, err, "creating test key")
|
||||
|
||||
// Test that a cert with mismatched subject and issuer fields is rejected,
|
||||
// even though its signature is produced with the right (self-signed) key.
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, eeTemplate, issuerCert, eeKey.Public(), eeKey)
|
||||
test.AssertNotError(t, err, "failed to create acme-tls/1 cert")
|
||||
|
||||
acmeCert := &tls.Certificate{
|
||||
Certificate: [][]byte{certBytes},
|
||||
PrivateKey: &TheKey,
|
||||
PrivateKey: eeKey,
|
||||
}
|
||||
|
||||
hs := tlsalpn01SrvWithCert(t, acmeCert, 0)
|
||||
|
||||
va, _ := setup(hs, "", nil, nil)
|
||||
|
||||
_, err = va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
test.AssertError(t, err, "validation should have failed")
|
||||
test.AssertContains(t, err.Error(), "not self-signed")
|
||||
|
||||
// Test that a cert whose signature was produced by some other key is rejected,
|
||||
// even though its subject and issuer fields claim that it is self-signed.
|
||||
certBytes, err = x509.CreateCertificate(rand.Reader, eeTemplate, eeTemplate, eeKey.Public(), issuerKey)
|
||||
test.AssertNotError(t, err, "failed to create acme-tls/1 cert")
|
||||
|
||||
acmeCert = &tls.Certificate{
|
||||
Certificate: [][]byte{certBytes},
|
||||
PrivateKey: eeKey,
|
||||
}
|
||||
|
||||
hs = tlsalpn01SrvWithCert(t, acmeCert, 0)
|
||||
|
||||
va, _ = setup(hs, "", nil, nil)
|
||||
|
||||
_, err = va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
test.AssertError(t, err, "validation should have failed")
|
||||
test.AssertContains(t, err.Error(), "not self-signed")
|
||||
}
|
||||
|
||||
func TestTLSALPN01ExtraIdentifiers(t *testing.T) {
|
||||
// Create a cert with an extra non-dnsName identifier. We don't use testTLSCert
|
||||
// because we need to set the IPAddresses field.
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1337),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"tests"},
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(0, 0, 1),
|
||||
|
||||
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
|
||||
DNSNames: []string{"expected"},
|
||||
IPAddresses: []net.IP{net.ParseIP("192.168.0.1")},
|
||||
ExtraExtensions: []pkix.Extension{testACMEExt},
|
||||
}
|
||||
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
test.AssertNotError(t, err, "creating test key")
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
|
||||
test.AssertNotError(t, err, "failed to create acme-tls/1 cert")
|
||||
|
||||
acmeCert := &tls.Certificate{
|
||||
Certificate: [][]byte{certBytes},
|
||||
PrivateKey: key,
|
||||
}
|
||||
|
||||
hs := tlsalpn01SrvWithCert(t, acmeCert, tls.VersionTLS12)
|
||||
|
@ -643,104 +677,24 @@ func TestTLSALPN01NotSelfSigned(t *testing.T) {
|
|||
|
||||
_, err = va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
test.AssertError(t, err, "validation should have failed")
|
||||
test.AssertContains(t, err.Error(), "not self-signed")
|
||||
}
|
||||
|
||||
func TestTLSALPN01ExtraIdentifiers(t *testing.T) {
|
||||
// Create a cert with an extra non-dnsName identifier.
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1337),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"tests"},
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(0, 0, 1),
|
||||
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
|
||||
DNSNames: []string{"expected"},
|
||||
IPAddresses: []net.IP{net.ParseIP("192.168.0.1")},
|
||||
}
|
||||
|
||||
shasum := sha256.Sum256([]byte(expectedKeyAuthorization))
|
||||
encHash, err := asn1.Marshal(shasum[:])
|
||||
test.AssertNotError(t, err, "failed to create key authorization")
|
||||
|
||||
acmeExtension := pkix.Extension{
|
||||
Id: IdPeAcmeIdentifier,
|
||||
Critical: true,
|
||||
Value: encHash,
|
||||
}
|
||||
template.ExtraExtensions = []pkix.Extension{acmeExtension}
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, template, template, &TheKey.PublicKey, &TheKey)
|
||||
test.AssertNotError(t, err, "failed to create acme-tls/1 cert")
|
||||
|
||||
acmeCert := &tls.Certificate{
|
||||
Certificate: [][]byte{certBytes},
|
||||
PrivateKey: &TheKey,
|
||||
}
|
||||
|
||||
hs := tlsalpn01SrvWithCert(t, acmeCert, tls.VersionTLS12)
|
||||
|
||||
va, _ := setup(hs, "", nil, nil)
|
||||
|
||||
_, prob := va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
test.AssertError(t, prob, "validation should have failed")
|
||||
test.AssertContains(t, err.Error(), "Received certificate with unexpected identifiers")
|
||||
}
|
||||
|
||||
func TestTLSALPN01ExtraSANs(t *testing.T) {
|
||||
// Create a cert with multiple SAN extensions
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1337),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"tests"},
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(0, 0, 1),
|
||||
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
shasum := sha256.Sum256([]byte(expectedKeyAuthorization))
|
||||
encHash, err := asn1.Marshal(shasum[:])
|
||||
test.AssertNotError(t, err, "failed to create key authorization")
|
||||
|
||||
acmeExtension := pkix.Extension{
|
||||
Id: IdPeAcmeIdentifier,
|
||||
Critical: true,
|
||||
Value: encHash,
|
||||
}
|
||||
|
||||
subjectAltName := pkix.Extension{}
|
||||
subjectAltName.Id = asn1.ObjectIdentifier{2, 5, 29, 17}
|
||||
subjectAltName.Critical = false
|
||||
subjectAltName.Value, err = asn1.Marshal([]asn1.RawValue{
|
||||
sanValue, err := asn1.Marshal([]asn1.RawValue{
|
||||
{Tag: 2, Class: 2, Bytes: []byte(`expected`)},
|
||||
})
|
||||
test.AssertNotError(t, err, "failed to marshal first SAN")
|
||||
test.AssertNotError(t, err, "failed to marshal test SAN")
|
||||
|
||||
extraSubjectAltName := pkix.Extension{}
|
||||
extraSubjectAltName.Id = asn1.ObjectIdentifier{2, 5, 29, 17}
|
||||
extraSubjectAltName.Critical = false
|
||||
extraSubjectAltName.Value, err = asn1.Marshal([]asn1.RawValue{
|
||||
{Tag: 2, Class: 2, Bytes: []byte(`expected`)},
|
||||
})
|
||||
test.AssertNotError(t, err, "failed to marshal extra SAN")
|
||||
|
||||
template.ExtraExtensions = []pkix.Extension{acmeExtension, subjectAltName, extraSubjectAltName}
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, template, template, &TheKey.PublicKey, &TheKey)
|
||||
test.AssertNotError(t, err, "failed to create acme-tls/1 cert")
|
||||
|
||||
acmeCert := &tls.Certificate{
|
||||
Certificate: [][]byte{certBytes},
|
||||
PrivateKey: &TheKey,
|
||||
subjectAltName := pkix.Extension{
|
||||
Id: asn1.ObjectIdentifier{2, 5, 29, 17},
|
||||
Critical: false,
|
||||
Value: sanValue,
|
||||
}
|
||||
|
||||
hs := tlsalpn01SrvWithCert(t, acmeCert, tls.VersionTLS12)
|
||||
extensions := []pkix.Extension{testACMEExt, subjectAltName, subjectAltName}
|
||||
hs := tlsalpn01SrvWithCert(t, testTLSCert([]string{"expected"}, extensions), 0)
|
||||
|
||||
va, _ := setup(hs, "", nil, nil)
|
||||
|
||||
|
@ -754,55 +708,16 @@ func TestTLSALPN01ExtraSANs(t *testing.T) {
|
|||
|
||||
func TestTLSALPN01ExtraAcmeExtensions(t *testing.T) {
|
||||
// Create a cert with multiple SAN extensions
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1337),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"tests"},
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(0, 0, 1),
|
||||
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
|
||||
DNSNames: []string{"expected"},
|
||||
}
|
||||
|
||||
shasum := sha256.Sum256([]byte(expectedKeyAuthorization))
|
||||
encHash, err := asn1.Marshal(shasum[:])
|
||||
test.AssertNotError(t, err, "failed to create key authorization")
|
||||
|
||||
acmeExtension := pkix.Extension{
|
||||
Id: IdPeAcmeIdentifier,
|
||||
Critical: true,
|
||||
Value: encHash,
|
||||
}
|
||||
|
||||
extraAcmeExtension := pkix.Extension{
|
||||
Id: IdPeAcmeIdentifier,
|
||||
Critical: true,
|
||||
Value: encHash,
|
||||
}
|
||||
|
||||
template.ExtraExtensions = []pkix.Extension{acmeExtension, extraAcmeExtension}
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, template, template, &TheKey.PublicKey, &TheKey)
|
||||
test.AssertNotError(t, err, "failed to create acme-tls/1 cert")
|
||||
|
||||
acmeCert := &tls.Certificate{
|
||||
Certificate: [][]byte{certBytes},
|
||||
PrivateKey: &TheKey,
|
||||
}
|
||||
|
||||
hs := tlsalpn01SrvWithCert(t, acmeCert, tls.VersionTLS12)
|
||||
extensions := []pkix.Extension{testACMEExt, testACMEExt}
|
||||
hs := tlsalpn01SrvWithCert(t, testTLSCert([]string{"expected"}, extensions), 0)
|
||||
|
||||
va, _ := setup(hs, "", nil, nil)
|
||||
|
||||
_, err = va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
_, err := va.validateTLSALPN01(ctx, dnsi("expected"), expectedKeyAuthorization)
|
||||
test.AssertError(t, err, "validation should have failed")
|
||||
prob := detailedError(err)
|
||||
// In go >= 1.19, the TLS client library detects that the certificate has
|
||||
// a duplicate extension and terminates the connection itself.
|
||||
prob := detailedError(err)
|
||||
test.AssertContains(t, prob.Error(), "Error getting validation data")
|
||||
}
|
||||
|
||||
|
@ -812,14 +727,15 @@ func TestAcceptableExtensions(t *testing.T) {
|
|||
IdCeSubjectAltName,
|
||||
}
|
||||
|
||||
var err error
|
||||
subjectAltName := pkix.Extension{}
|
||||
subjectAltName.Id = asn1.ObjectIdentifier{2, 5, 29, 17}
|
||||
subjectAltName.Critical = false
|
||||
subjectAltName.Value, err = asn1.Marshal([]asn1.RawValue{
|
||||
sanValue, err := asn1.Marshal([]asn1.RawValue{
|
||||
{Tag: 2, Class: 2, Bytes: []byte(`expected`)},
|
||||
})
|
||||
test.AssertNotError(t, err, "failed to marshal SAN")
|
||||
test.AssertNotError(t, err, "failed to marshal test SAN")
|
||||
subjectAltName := pkix.Extension{
|
||||
Id: asn1.ObjectIdentifier{2, 5, 29, 17},
|
||||
Critical: false,
|
||||
Value: sanValue,
|
||||
}
|
||||
|
||||
acmeExtension := pkix.Extension{
|
||||
Id: IdPeAcmeIdentifier,
|
||||
|
|
|
@ -35,10 +35,6 @@ import (
|
|||
vapb "github.com/letsencrypt/boulder/va/proto"
|
||||
)
|
||||
|
||||
var expectedToken = "LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowyjxAjEuX0"
|
||||
var expectedThumbprint = "9jg46WB3rR_AHD-EBXdN7cBkH1WOu0tA3M9fm21mqTI"
|
||||
var expectedKeyAuthorization = ka(expectedToken)
|
||||
|
||||
func ka(token string) string {
|
||||
return token + "." + expectedThumbprint
|
||||
}
|
||||
|
@ -54,19 +50,25 @@ func intFromB64(b64 string) int {
|
|||
return int(bigIntFromB64(b64).Int64())
|
||||
}
|
||||
|
||||
// Any changes to this key must be reflected in //bdns/mocks.go, where values
|
||||
// derived from it are hardcoded as the "correct" responses for DNS challenges.
|
||||
// This key should not be used for anything other than computing Key
|
||||
// Authorizations, i.e. it should not be used as the key to create a self-signed
|
||||
// TLS-ALPN-01 certificate.
|
||||
var n = bigIntFromB64("n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw==")
|
||||
var e = intFromB64("AQAB")
|
||||
var d = bigIntFromB64("bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ==")
|
||||
var p = bigIntFromB64("uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc=")
|
||||
var q = bigIntFromB64("uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc=")
|
||||
|
||||
var TheKey = rsa.PrivateKey{
|
||||
PublicKey: rsa.PublicKey{N: n, E: e},
|
||||
D: d,
|
||||
Primes: []*big.Int{p, q},
|
||||
}
|
||||
|
||||
var accountKey = &jose.JSONWebKey{Key: TheKey.Public()}
|
||||
var expectedToken = "LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowyjxAjEuX0"
|
||||
var expectedThumbprint = "9jg46WB3rR_AHD-EBXdN7cBkH1WOu0tA3M9fm21mqTI"
|
||||
var expectedKeyAuthorization = ka(expectedToken)
|
||||
|
||||
// Return an ACME DNS identifier for the given hostname
|
||||
func dnsi(hostname string) identifier.ACMEIdentifier {
|
||||
|
|
Loading…
Reference in New Issue