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:
Aaron Gable 2025-02-19 14:37:01 -08:00 committed by GitHub
parent eab90ee2f5
commit 212a66ab49
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 595 additions and 700 deletions

View File

@ -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.

View File

@ -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 }}"

View File

@ -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 }}"

View File

@ -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

View File

@ -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"

View File

@ -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:

View File

@ -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})

View File

@ -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"`
}

View File

@ -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
}

View File

@ -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")
}

View File

@ -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{

View File

@ -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())
}

View File

@ -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}
}
}
}

View File

@ -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

View File

@ -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))
}

View File

@ -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()

View File

@ -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.

View File

@ -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...)
}

View File

@ -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.

View File

@ -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) {

View File

@ -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),

View File

@ -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(

View File

@ -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
}

View File

@ -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"

View File

@ -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},

View File

@ -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,
})

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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()

View File

@ -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),
})
})

View File

@ -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

View File

@ -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

View File

@ -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,
}

View File

@ -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")
}

99
test/integration/testdata/fermat_csr.go vendored Normal file
View File

@ -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))
}

View File

@ -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-----

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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,

View File

@ -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 {