github.com/letsencrypt/boulder@v0.20251208.0/sa/sa_test.go (about)

     1  package sa
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/ecdsa"
     7  	"crypto/elliptic"
     8  	"crypto/rand"
     9  	"crypto/sha256"
    10  	"crypto/x509"
    11  	"database/sql"
    12  	"encoding/base64"
    13  	"encoding/json"
    14  	"errors"
    15  	"fmt"
    16  	"io"
    17  	"math/big"
    18  	"math/bits"
    19  	mrand "math/rand/v2"
    20  	"net/netip"
    21  	"os"
    22  	"reflect"
    23  	"slices"
    24  	"strconv"
    25  	"strings"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/go-jose/go-jose/v4"
    30  	"github.com/go-sql-driver/mysql"
    31  	"github.com/jmhodges/clock"
    32  	"github.com/prometheus/client_golang/prometheus"
    33  	"google.golang.org/grpc"
    34  	"google.golang.org/protobuf/types/known/durationpb"
    35  	"google.golang.org/protobuf/types/known/emptypb"
    36  	"google.golang.org/protobuf/types/known/timestamppb"
    37  
    38  	"github.com/letsencrypt/boulder/core"
    39  	corepb "github.com/letsencrypt/boulder/core/proto"
    40  	"github.com/letsencrypt/boulder/db"
    41  	berrors "github.com/letsencrypt/boulder/errors"
    42  	"github.com/letsencrypt/boulder/features"
    43  	bgrpc "github.com/letsencrypt/boulder/grpc"
    44  	"github.com/letsencrypt/boulder/identifier"
    45  	blog "github.com/letsencrypt/boulder/log"
    46  	"github.com/letsencrypt/boulder/metrics"
    47  	"github.com/letsencrypt/boulder/probs"
    48  	"github.com/letsencrypt/boulder/revocation"
    49  	sapb "github.com/letsencrypt/boulder/sa/proto"
    50  	"github.com/letsencrypt/boulder/test"
    51  	"github.com/letsencrypt/boulder/test/vars"
    52  )
    53  
    54  var log = blog.UseMock()
    55  var ctx = context.Background()
    56  
    57  var (
    58  	theKey = `{
    59      "kty": "RSA",
    60      "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw",
    61      "e": "AQAB"
    62  }`
    63  )
    64  
    65  func mustTime(s string) time.Time {
    66  	t, err := time.Parse("2006-01-02 15:04", s)
    67  	if err != nil {
    68  		panic(fmt.Sprintf("parsing %q: %s", s, err))
    69  	}
    70  	return t.UTC()
    71  }
    72  
    73  func mustTimestamp(s string) *timestamppb.Timestamp {
    74  	return timestamppb.New(mustTime(s))
    75  }
    76  
    77  type fakeServerStream[T any] struct {
    78  	grpc.ServerStream
    79  	output chan<- *T
    80  }
    81  
    82  func (s *fakeServerStream[T]) Send(msg *T) error {
    83  	s.output <- msg
    84  	return nil
    85  }
    86  
    87  func (s *fakeServerStream[T]) Context() context.Context {
    88  	return context.Background()
    89  }
    90  
    91  // initSA constructs a SQLStorageAuthority and a clean up function that should
    92  // be defer'ed to the end of the test.
    93  func initSA(t testing.TB) (*SQLStorageAuthority, clock.FakeClock, func()) {
    94  	t.Helper()
    95  	features.Reset()
    96  
    97  	dbMap, err := DBMapForTest(vars.DBConnSA)
    98  	if err != nil {
    99  		t.Fatalf("Failed to create dbMap: %s", err)
   100  	}
   101  
   102  	dbIncidentsMap, err := DBMapForTest(vars.DBConnIncidents)
   103  	if err != nil {
   104  		t.Fatalf("Failed to create dbMap: %s", err)
   105  	}
   106  
   107  	fc := clock.NewFake()
   108  	fc.Set(mustTime("2015-03-04 05:00"))
   109  
   110  	saro, err := NewSQLStorageAuthorityRO(dbMap, dbIncidentsMap, metrics.NoopRegisterer, 1, 0, fc, log)
   111  	if err != nil {
   112  		t.Fatalf("Failed to create SA: %s", err)
   113  	}
   114  
   115  	sa, err := NewSQLStorageAuthorityWrapping(saro, dbMap, metrics.NoopRegisterer)
   116  	if err != nil {
   117  		t.Fatalf("Failed to create SA: %s", err)
   118  	}
   119  
   120  	return sa, fc, test.ResetBoulderTestDatabase(t)
   121  }
   122  
   123  // CreateWorkingTestRegistration inserts a new, correct Registration into the
   124  // given SA.
   125  func createWorkingRegistration(t testing.TB, sa *SQLStorageAuthority) *corepb.Registration {
   126  	reg, err := sa.NewRegistration(context.Background(), &corepb.Registration{
   127  		Key:       []byte(theKey),
   128  		CreatedAt: mustTimestamp("2003-05-10 00:00"),
   129  		Status:    string(core.StatusValid),
   130  	})
   131  	if err != nil {
   132  		t.Fatalf("Unable to create new registration: %s", err)
   133  	}
   134  	return reg
   135  }
   136  
   137  func createPendingAuthorization(t *testing.T, sa *SQLStorageAuthority, regID int64, ident identifier.ACMEIdentifier, exp time.Time) int64 {
   138  	t.Helper()
   139  
   140  	tokenStr := core.NewToken()
   141  	token, err := base64.RawURLEncoding.DecodeString(tokenStr)
   142  	test.AssertNotError(t, err, "computing test authorization challenge token")
   143  
   144  	am := authzModel{
   145  		IdentifierType:  identifierTypeToUint[string(ident.Type)],
   146  		IdentifierValue: ident.Value,
   147  		RegistrationID:  regID,
   148  		Status:          statusToUint[core.StatusPending],
   149  		Expires:         exp,
   150  		Challenges:      1 << challTypeToUint[string(core.ChallengeTypeHTTP01)],
   151  		Token:           token,
   152  	}
   153  
   154  	err = sa.dbMap.Insert(context.Background(), &am)
   155  	test.AssertNotError(t, err, "creating test authorization")
   156  
   157  	return am.ID
   158  }
   159  
   160  func createFinalizedAuthorization(t *testing.T, sa *SQLStorageAuthority, regID int64, ident identifier.ACMEIdentifier, exp time.Time,
   161  	status string, attemptedAt time.Time) int64 {
   162  	t.Helper()
   163  	pendingID := createPendingAuthorization(t, sa, regID, ident, exp)
   164  	attempted := string(core.ChallengeTypeHTTP01)
   165  	_, err := sa.FinalizeAuthorization2(context.Background(), &sapb.FinalizeAuthorizationRequest{
   166  		Id:          pendingID,
   167  		Status:      status,
   168  		Expires:     timestamppb.New(exp),
   169  		Attempted:   attempted,
   170  		AttemptedAt: timestamppb.New(attemptedAt),
   171  	})
   172  	test.AssertNotError(t, err, "sa.FinalizeAuthorizations2 failed")
   173  	return pendingID
   174  }
   175  
   176  func goodTestJWK() *jose.JSONWebKey {
   177  	var jwk jose.JSONWebKey
   178  	err := json.Unmarshal([]byte(theKey), &jwk)
   179  	if err != nil {
   180  		panic("known-good theKey is no longer known-good")
   181  	}
   182  	return &jwk
   183  }
   184  
   185  func TestAddRegistration(t *testing.T) {
   186  	sa, clk, cleanUp := initSA(t)
   187  	defer cleanUp()
   188  
   189  	jwkJSON, _ := goodTestJWK().MarshalJSON()
   190  	reg, err := sa.NewRegistration(ctx, &corepb.Registration{
   191  		Key: jwkJSON,
   192  	})
   193  	if err != nil {
   194  		t.Fatalf("Couldn't create new registration: %s", err)
   195  	}
   196  	test.Assert(t, reg.Id != 0, "ID shouldn't be 0")
   197  
   198  	// Confirm that the registration can be retrieved by ID.
   199  	dbReg, err := sa.GetRegistration(ctx, &sapb.RegistrationID{Id: reg.Id})
   200  	test.AssertNotError(t, err, fmt.Sprintf("Couldn't get registration with ID %v", reg.Id))
   201  
   202  	createdAt := clk.Now()
   203  	test.AssertEquals(t, dbReg.Id, reg.Id)
   204  	test.AssertByteEquals(t, dbReg.Key, jwkJSON)
   205  	test.AssertDeepEquals(t, dbReg.CreatedAt.AsTime(), createdAt)
   206  
   207  	_, err = sa.GetRegistration(ctx, &sapb.RegistrationID{Id: 0})
   208  	test.AssertError(t, err, "Registration object for ID 0 was returned")
   209  
   210  	// Confirm that the registration can be retrieved by key.
   211  	dbReg, err = sa.GetRegistrationByKey(ctx, &sapb.JSONWebKey{Jwk: jwkJSON})
   212  	test.AssertNotError(t, err, "Couldn't get registration by key")
   213  	test.AssertEquals(t, dbReg.Id, dbReg.Id)
   214  	test.AssertEquals(t, dbReg.Agreement, dbReg.Agreement)
   215  
   216  	anotherKey := `{
   217  		"kty":"RSA",
   218  		"n": "vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw",
   219  		"e":"AQAB"
   220  	}`
   221  	_, err = sa.GetRegistrationByKey(ctx, &sapb.JSONWebKey{Jwk: []byte(anotherKey)})
   222  	test.AssertError(t, err, "Registration object for invalid key was returned")
   223  }
   224  
   225  func TestNoSuchRegistrationErrors(t *testing.T) {
   226  	sa, _, cleanUp := initSA(t)
   227  	defer cleanUp()
   228  
   229  	_, err := sa.GetRegistration(ctx, &sapb.RegistrationID{Id: 100})
   230  	test.AssertErrorIs(t, err, berrors.NotFound)
   231  
   232  	jwk := goodTestJWK()
   233  	jwkJSON, _ := jwk.MarshalJSON()
   234  
   235  	_, err = sa.GetRegistrationByKey(ctx, &sapb.JSONWebKey{Jwk: jwkJSON})
   236  	test.AssertErrorIs(t, err, berrors.NotFound)
   237  
   238  	_, err = sa.UpdateRegistrationKey(ctx, &sapb.UpdateRegistrationKeyRequest{RegistrationID: 100, Jwk: jwkJSON})
   239  	test.AssertErrorIs(t, err, berrors.InternalServer)
   240  }
   241  
   242  func TestSelectRegistration(t *testing.T) {
   243  	sa, _, cleanUp := initSA(t)
   244  	defer cleanUp()
   245  	var ctx = context.Background()
   246  	jwk := goodTestJWK()
   247  	jwkJSON, _ := jwk.MarshalJSON()
   248  	sha, err := core.KeyDigestB64(jwk.Key)
   249  	test.AssertNotError(t, err, "couldn't parse jwk.Key")
   250  
   251  	reg, err := sa.NewRegistration(ctx, &corepb.Registration{
   252  		Key: jwkJSON,
   253  	})
   254  	test.AssertNotError(t, err, fmt.Sprintf("couldn't create new registration: %s", err))
   255  	test.Assert(t, reg.Id != 0, "ID shouldn't be 0")
   256  
   257  	_, err = selectRegistration(ctx, sa.dbMap, "id", reg.Id)
   258  	test.AssertNotError(t, err, "selecting by id should work")
   259  	_, err = selectRegistration(ctx, sa.dbMap, "jwk_sha256", sha)
   260  	test.AssertNotError(t, err, "selecting by jwk_sha256 should work")
   261  }
   262  
   263  func TestReplicationLagRetries(t *testing.T) {
   264  	sa, clk, cleanUp := initSA(t)
   265  	defer cleanUp()
   266  
   267  	reg := createWorkingRegistration(t, sa)
   268  
   269  	// First, set the lagFactor to 0. Neither selecting a real registration nor
   270  	// selecting a nonexistent registration should cause the clock to advance.
   271  	sa.lagFactor = 0
   272  	start := clk.Now()
   273  
   274  	_, err := sa.GetRegistration(ctx, &sapb.RegistrationID{Id: reg.Id})
   275  	test.AssertNotError(t, err, "selecting extant registration")
   276  	test.AssertEquals(t, clk.Now(), start)
   277  	test.AssertMetricWithLabelsEquals(t, sa.lagFactorCounter, prometheus.Labels{"method": "GetRegistration", "result": "notfound"}, 0)
   278  
   279  	_, err = sa.GetRegistration(ctx, &sapb.RegistrationID{Id: reg.Id + 1})
   280  	test.AssertError(t, err, "selecting nonexistent registration")
   281  	test.AssertEquals(t, clk.Now(), start)
   282  	// With lagFactor disabled, we should never enter the retry codepath, as a
   283  	// result the metric should not increment.
   284  	test.AssertMetricWithLabelsEquals(t, sa.lagFactorCounter, prometheus.Labels{"method": "GetRegistration", "result": "notfound"}, 0)
   285  
   286  	// Now, set the lagFactor to 1. Trying to select a nonexistent registration
   287  	// should cause the clock to advance when GetRegistration sleeps and retries.
   288  	sa.lagFactor = 1
   289  	start = clk.Now()
   290  
   291  	_, err = sa.GetRegistration(ctx, &sapb.RegistrationID{Id: reg.Id})
   292  	test.AssertNotError(t, err, "selecting extant registration")
   293  	test.AssertEquals(t, clk.Now(), start)
   294  	// lagFactor is enabled, but the registration exists.
   295  	test.AssertMetricWithLabelsEquals(t, sa.lagFactorCounter, prometheus.Labels{"method": "GetRegistration", "result": "notfound"}, 0)
   296  
   297  	_, err = sa.GetRegistration(ctx, &sapb.RegistrationID{Id: reg.Id + 1})
   298  	test.AssertError(t, err, "selecting nonexistent registration")
   299  	test.AssertEquals(t, clk.Now(), start.Add(1))
   300  	// With lagFactor enabled, we should enter the retry codepath and as a result
   301  	// the metric should increment.
   302  	test.AssertMetricWithLabelsEquals(t, sa.lagFactorCounter, prometheus.Labels{"method": "GetRegistration", "result": "notfound"}, 1)
   303  }
   304  
   305  // findIssuedName is a small helper test function to directly query the
   306  // issuedNames table for a given name to find a serial (or return an err).
   307  func findIssuedName(ctx context.Context, dbMap db.OneSelector, issuedName string) (string, error) {
   308  	var issuedNamesSerial string
   309  	err := dbMap.SelectOne(
   310  		ctx,
   311  		&issuedNamesSerial,
   312  		`SELECT serial FROM issuedNames
   313  		WHERE reversedName = ?
   314  		ORDER BY notBefore DESC
   315  		LIMIT 1`,
   316  		issuedName)
   317  	return issuedNamesSerial, err
   318  }
   319  
   320  func TestAddSerial(t *testing.T) {
   321  	sa, clk, cleanUp := initSA(t)
   322  	defer cleanUp()
   323  
   324  	reg := createWorkingRegistration(t, sa)
   325  	serial, testCert := test.ThrowAwayCert(t, clk)
   326  
   327  	_, err := sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
   328  		RegID:   reg.Id,
   329  		Created: timestamppb.New(testCert.NotBefore),
   330  		Expires: timestamppb.New(testCert.NotAfter),
   331  	})
   332  	test.AssertError(t, err, "adding without serial should fail")
   333  
   334  	_, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
   335  		Serial:  serial,
   336  		Created: timestamppb.New(testCert.NotBefore),
   337  		Expires: timestamppb.New(testCert.NotAfter),
   338  	})
   339  	test.AssertError(t, err, "adding without regid should fail")
   340  
   341  	_, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
   342  		Serial:  serial,
   343  		RegID:   reg.Id,
   344  		Expires: timestamppb.New(testCert.NotAfter),
   345  	})
   346  	test.AssertError(t, err, "adding without created should fail")
   347  
   348  	_, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
   349  		Serial:  serial,
   350  		RegID:   reg.Id,
   351  		Created: timestamppb.New(testCert.NotBefore),
   352  	})
   353  	test.AssertError(t, err, "adding without expires should fail")
   354  
   355  	_, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
   356  		Serial:  serial,
   357  		RegID:   reg.Id,
   358  		Created: timestamppb.New(testCert.NotBefore),
   359  		Expires: timestamppb.New(testCert.NotAfter),
   360  	})
   361  	test.AssertNotError(t, err, "adding serial should have succeeded")
   362  }
   363  
   364  func TestGetSerialMetadata(t *testing.T) {
   365  	sa, clk, cleanUp := initSA(t)
   366  	defer cleanUp()
   367  
   368  	reg := createWorkingRegistration(t, sa)
   369  	serial, _ := test.ThrowAwayCert(t, clk)
   370  
   371  	_, err := sa.GetSerialMetadata(context.Background(), &sapb.Serial{Serial: serial})
   372  	test.AssertError(t, err, "getting nonexistent serial should have failed")
   373  
   374  	now := clk.Now()
   375  	hourLater := now.Add(time.Hour)
   376  	_, err = sa.AddSerial(context.Background(), &sapb.AddSerialRequest{
   377  		Serial:  serial,
   378  		RegID:   reg.Id,
   379  		Created: timestamppb.New(now),
   380  		Expires: timestamppb.New(hourLater),
   381  	})
   382  	test.AssertNotError(t, err, "failed to add test serial")
   383  
   384  	m, err := sa.GetSerialMetadata(context.Background(), &sapb.Serial{Serial: serial})
   385  
   386  	test.AssertNotError(t, err, "getting serial should have succeeded")
   387  	test.AssertEquals(t, m.Serial, serial)
   388  	test.AssertEquals(t, m.RegistrationID, reg.Id)
   389  	test.AssertEquals(t, now, timestamppb.New(now).AsTime())
   390  	test.AssertEquals(t, m.Expires.AsTime(), timestamppb.New(hourLater).AsTime())
   391  }
   392  
   393  func TestAddPrecertificate(t *testing.T) {
   394  	ctx := context.Background()
   395  	sa, clk, cleanUp := initSA(t)
   396  	defer cleanUp()
   397  
   398  	reg := createWorkingRegistration(t, sa)
   399  
   400  	// Create a throw-away self signed certificate with a random name and
   401  	// serial number
   402  	serial, testCert := test.ThrowAwayCert(t, clk)
   403  
   404  	// Add the cert as a precertificate
   405  	regID := reg.Id
   406  	issuedTime := mustTimestamp("2018-04-01 07:00")
   407  	_, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
   408  		Der:          testCert.Raw,
   409  		RegID:        regID,
   410  		Issued:       issuedTime,
   411  		IssuerNameID: 1,
   412  	})
   413  	test.AssertNotError(t, err, "Couldn't add test cert")
   414  
   415  	// It should have the expected certificate status
   416  	certStatus, err := sa.GetCertificateStatus(ctx, &sapb.Serial{Serial: serial})
   417  	test.AssertNotError(t, err, "Couldn't get status for test cert")
   418  	test.AssertEquals(t, certStatus.Status, string(core.OCSPStatusGood))
   419  	now := clk.Now()
   420  	test.AssertEquals(t, now, certStatus.OcspLastUpdated.AsTime())
   421  
   422  	// It should show up in the issued names table
   423  	issuedNamesSerial, err := findIssuedName(ctx, sa.dbMap, reverseFQDN(testCert.DNSNames[0]))
   424  	test.AssertNotError(t, err, "expected no err querying issuedNames for precert")
   425  	test.AssertEquals(t, issuedNamesSerial, serial)
   426  
   427  	// We should also be able to call AddCertificate with the same cert
   428  	// without it being an error. The duplicate err on inserting to
   429  	// issuedNames should be ignored.
   430  	_, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{
   431  		Der:    testCert.Raw,
   432  		RegID:  regID,
   433  		Issued: issuedTime,
   434  	})
   435  	test.AssertNotError(t, err, "unexpected err adding final cert after precert")
   436  }
   437  
   438  func TestAddPrecertificateNoOCSP(t *testing.T) {
   439  	sa, clk, cleanUp := initSA(t)
   440  	defer cleanUp()
   441  
   442  	reg := createWorkingRegistration(t, sa)
   443  	_, testCert := test.ThrowAwayCert(t, clk)
   444  
   445  	regID := reg.Id
   446  	issuedTime := mustTimestamp("2018-04-01 07:00")
   447  	_, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
   448  		Der:          testCert.Raw,
   449  		RegID:        regID,
   450  		Issued:       issuedTime,
   451  		IssuerNameID: 1,
   452  	})
   453  	test.AssertNotError(t, err, "Couldn't add test cert")
   454  }
   455  
   456  func TestAddPreCertificateDuplicate(t *testing.T) {
   457  	sa, clk, cleanUp := initSA(t)
   458  	defer cleanUp()
   459  
   460  	reg := createWorkingRegistration(t, sa)
   461  
   462  	_, testCert := test.ThrowAwayCert(t, clk)
   463  	issuedTime := clk.Now()
   464  
   465  	_, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
   466  		Der:          testCert.Raw,
   467  		Issued:       timestamppb.New(issuedTime),
   468  		RegID:        reg.Id,
   469  		IssuerNameID: 1,
   470  	})
   471  	test.AssertNotError(t, err, "Couldn't add test certificate")
   472  
   473  	_, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
   474  		Der:          testCert.Raw,
   475  		Issued:       timestamppb.New(issuedTime),
   476  		RegID:        reg.Id,
   477  		IssuerNameID: 1,
   478  	})
   479  	test.AssertDeepEquals(t, err, berrors.DuplicateError("cannot add a duplicate cert"))
   480  }
   481  
   482  func TestAddPrecertificateIncomplete(t *testing.T) {
   483  	sa, clk, cleanUp := initSA(t)
   484  	defer cleanUp()
   485  
   486  	reg := createWorkingRegistration(t, sa)
   487  
   488  	// Create a throw-away self signed certificate with a random name and
   489  	// serial number
   490  	_, testCert := test.ThrowAwayCert(t, clk)
   491  
   492  	// Add the cert as a precertificate
   493  	regID := reg.Id
   494  	_, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
   495  		Der:    testCert.Raw,
   496  		RegID:  regID,
   497  		Issued: mustTimestamp("2018-04-01 07:00"),
   498  		// Leaving out IssuerNameID
   499  	})
   500  
   501  	test.AssertError(t, err, "Adding precert with no issuer did not fail")
   502  }
   503  
   504  func TestAddPrecertificateKeyHash(t *testing.T) {
   505  	sa, clk, cleanUp := initSA(t)
   506  	defer cleanUp()
   507  	reg := createWorkingRegistration(t, sa)
   508  
   509  	serial, testCert := test.ThrowAwayCert(t, clk)
   510  	_, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
   511  		Der:          testCert.Raw,
   512  		RegID:        reg.Id,
   513  		Issued:       timestamppb.New(testCert.NotBefore),
   514  		IssuerNameID: 1,
   515  	})
   516  	test.AssertNotError(t, err, "failed to add precert")
   517  
   518  	var keyHashes []keyHashModel
   519  	_, err = sa.dbMap.Select(context.Background(), &keyHashes, "SELECT * FROM keyHashToSerial")
   520  	test.AssertNotError(t, err, "failed to retrieve rows from keyHashToSerial")
   521  	test.AssertEquals(t, len(keyHashes), 1)
   522  	test.AssertEquals(t, keyHashes[0].CertSerial, serial)
   523  	test.AssertEquals(t, keyHashes[0].CertNotAfter, testCert.NotAfter)
   524  	test.AssertEquals(t, keyHashes[0].CertNotAfter, timestamppb.New(testCert.NotAfter).AsTime())
   525  	spkiHash := sha256.Sum256(testCert.RawSubjectPublicKeyInfo)
   526  	test.Assert(t, bytes.Equal(keyHashes[0].KeyHash, spkiHash[:]), "spki hash mismatch")
   527  }
   528  
   529  func TestAddCertificate(t *testing.T) {
   530  	sa, clk, cleanUp := initSA(t)
   531  	defer cleanUp()
   532  
   533  	reg := createWorkingRegistration(t, sa)
   534  
   535  	serial, testCert := test.ThrowAwayCert(t, clk)
   536  
   537  	issuedTime := sa.clk.Now()
   538  	_, err := sa.AddCertificate(ctx, &sapb.AddCertificateRequest{
   539  		Der:    testCert.Raw,
   540  		RegID:  reg.Id,
   541  		Issued: timestamppb.New(issuedTime),
   542  	})
   543  	test.AssertNotError(t, err, "Couldn't add test cert")
   544  
   545  	retrievedCert, err := sa.GetCertificate(ctx, &sapb.Serial{Serial: serial})
   546  	test.AssertNotError(t, err, "Couldn't get test cert by full serial")
   547  	test.AssertByteEquals(t, testCert.Raw, retrievedCert.Der)
   548  	test.AssertEquals(t, retrievedCert.Issued.AsTime(), issuedTime)
   549  
   550  	// Calling AddCertificate with empty args should fail.
   551  	_, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{
   552  		Der:    nil,
   553  		RegID:  reg.Id,
   554  		Issued: timestamppb.New(issuedTime),
   555  	})
   556  	test.AssertError(t, err, "shouldn't be able to add cert with no DER")
   557  	_, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{
   558  		Der:    testCert.Raw,
   559  		RegID:  0,
   560  		Issued: timestamppb.New(issuedTime),
   561  	})
   562  	test.AssertError(t, err, "shouldn't be able to add cert with no regID")
   563  	_, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{
   564  		Der:    testCert.Raw,
   565  		RegID:  reg.Id,
   566  		Issued: nil,
   567  	})
   568  	test.AssertError(t, err, "shouldn't be able to add cert with no issued timestamp")
   569  	_, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{
   570  		Der:    testCert.Raw,
   571  		RegID:  reg.Id,
   572  		Issued: timestamppb.New(time.Time{}),
   573  	})
   574  	test.AssertError(t, err, "shouldn't be able to add cert with zero issued timestamp")
   575  }
   576  
   577  func TestAddCertificateDuplicate(t *testing.T) {
   578  	sa, clk, cleanUp := initSA(t)
   579  	defer cleanUp()
   580  
   581  	reg := createWorkingRegistration(t, sa)
   582  
   583  	_, testCert := test.ThrowAwayCert(t, clk)
   584  
   585  	issuedTime := clk.Now()
   586  	_, err := sa.AddCertificate(ctx, &sapb.AddCertificateRequest{
   587  		Der:    testCert.Raw,
   588  		RegID:  reg.Id,
   589  		Issued: timestamppb.New(issuedTime),
   590  	})
   591  	test.AssertNotError(t, err, "Couldn't add test certificate")
   592  
   593  	_, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{
   594  		Der:    testCert.Raw,
   595  		RegID:  reg.Id,
   596  		Issued: timestamppb.New(issuedTime),
   597  	})
   598  	test.AssertDeepEquals(t, err, berrors.DuplicateError("cannot add a duplicate cert"))
   599  
   600  }
   601  
   602  func TestFQDNSetTimestampsForWindow(t *testing.T) {
   603  	sa, fc, cleanUp := initSA(t)
   604  	defer cleanUp()
   605  
   606  	tx, err := sa.dbMap.BeginTx(ctx)
   607  	test.AssertNotError(t, err, "Failed to open transaction")
   608  
   609  	idents := identifier.ACMEIdentifiers{
   610  		identifier.NewDNS("a.example.com"),
   611  		identifier.NewDNS("B.example.com"),
   612  	}
   613  
   614  	// Invalid Window
   615  	req := &sapb.CountFQDNSetsRequest{
   616  		Identifiers: idents.ToProtoSlice(),
   617  		Window:      nil,
   618  	}
   619  	_, err = sa.FQDNSetTimestampsForWindow(ctx, req)
   620  	test.AssertErrorIs(t, err, errIncompleteRequest)
   621  
   622  	window := time.Hour * 3
   623  	req = &sapb.CountFQDNSetsRequest{
   624  		Identifiers: idents.ToProtoSlice(),
   625  		Window:      durationpb.New(window),
   626  	}
   627  
   628  	// Ensure zero issuance has occurred for names.
   629  	resp, err := sa.FQDNSetTimestampsForWindow(ctx, req)
   630  	test.AssertNotError(t, err, "Failed to count name sets")
   631  	test.AssertEquals(t, len(resp.Timestamps), 0)
   632  
   633  	// Add an issuance for names inside the window.
   634  	expires := fc.Now().Add(time.Hour * 2).UTC()
   635  	firstIssued := fc.Now()
   636  	err = addFQDNSet(ctx, tx, idents, "serial", firstIssued, expires)
   637  	test.AssertNotError(t, err, "Failed to add name set")
   638  	test.AssertNotError(t, tx.Commit(), "Failed to commit transaction")
   639  
   640  	// Ensure there's 1 issuance timestamp for names inside the window.
   641  	resp, err = sa.FQDNSetTimestampsForWindow(ctx, req)
   642  	test.AssertNotError(t, err, "Failed to count name sets")
   643  	test.AssertEquals(t, len(resp.Timestamps), 1)
   644  	test.AssertEquals(t, firstIssued, resp.Timestamps[len(resp.Timestamps)-1].AsTime())
   645  
   646  	// Ensure that the hash isn't affected by changing name order/casing.
   647  	req.Identifiers = []*corepb.Identifier{
   648  		identifier.NewDNS("b.example.com").ToProto(),
   649  		identifier.NewDNS("A.example.COM").ToProto(),
   650  	}
   651  	resp, err = sa.FQDNSetTimestampsForWindow(ctx, req)
   652  	test.AssertNotError(t, err, "Failed to count name sets")
   653  	test.AssertEquals(t, len(resp.Timestamps), 1)
   654  	test.AssertEquals(t, firstIssued, resp.Timestamps[len(resp.Timestamps)-1].AsTime())
   655  
   656  	// Add another issuance for names inside the window.
   657  	tx, err = sa.dbMap.BeginTx(ctx)
   658  	test.AssertNotError(t, err, "Failed to open transaction")
   659  	err = addFQDNSet(ctx, tx, idents, "anotherSerial", firstIssued, expires)
   660  	test.AssertNotError(t, err, "Failed to add name set")
   661  	test.AssertNotError(t, tx.Commit(), "Failed to commit transaction")
   662  
   663  	// Ensure there are two issuance timestamps for names inside the window.
   664  	req.Identifiers = idents.ToProtoSlice()
   665  	resp, err = sa.FQDNSetTimestampsForWindow(ctx, req)
   666  	test.AssertNotError(t, err, "Failed to count name sets")
   667  	test.AssertEquals(t, len(resp.Timestamps), 2)
   668  	test.AssertEquals(t, firstIssued, resp.Timestamps[len(resp.Timestamps)-1].AsTime())
   669  
   670  	// Add another issuance for names but just outside the window.
   671  	tx, err = sa.dbMap.BeginTx(ctx)
   672  	test.AssertNotError(t, err, "Failed to open transaction")
   673  	err = addFQDNSet(ctx, tx, idents, "yetAnotherSerial", firstIssued.Add(-window), expires)
   674  	test.AssertNotError(t, err, "Failed to add name set")
   675  	test.AssertNotError(t, tx.Commit(), "Failed to commit transaction")
   676  
   677  	// Ensure there are still only two issuance timestamps in the window.
   678  	resp, err = sa.FQDNSetTimestampsForWindow(ctx, req)
   679  	test.AssertNotError(t, err, "Failed to count name sets")
   680  	test.AssertEquals(t, len(resp.Timestamps), 2)
   681  	test.AssertEquals(t, firstIssued, resp.Timestamps[len(resp.Timestamps)-1].AsTime())
   682  
   683  	resp, err = sa.FQDNSetTimestampsForWindow(ctx, &sapb.CountFQDNSetsRequest{
   684  		Identifiers: idents.ToProtoSlice(),
   685  		Window:      durationpb.New(window),
   686  		Limit:       1,
   687  	})
   688  	test.AssertNotError(t, err, "Failed to count name sets")
   689  	test.AssertEquals(t, len(resp.Timestamps), 1)
   690  	test.AssertEquals(t, firstIssued, resp.Timestamps[len(resp.Timestamps)-1].AsTime())
   691  }
   692  
   693  func TestFQDNSetExists(t *testing.T) {
   694  	sa, fc, cleanUp := initSA(t)
   695  	defer cleanUp()
   696  
   697  	idents := identifier.ACMEIdentifiers{
   698  		identifier.NewDNS("a.example.com"),
   699  		identifier.NewDNS("B.example.com"),
   700  	}
   701  
   702  	exists, err := sa.FQDNSetExists(ctx, &sapb.FQDNSetExistsRequest{Identifiers: idents.ToProtoSlice()})
   703  	test.AssertNotError(t, err, "Failed to check FQDN set existence")
   704  	test.Assert(t, !exists.Exists, "FQDN set shouldn't exist")
   705  
   706  	tx, err := sa.dbMap.BeginTx(ctx)
   707  	test.AssertNotError(t, err, "Failed to open transaction")
   708  	expires := fc.Now().Add(time.Hour * 2).UTC()
   709  	issued := fc.Now()
   710  	err = addFQDNSet(ctx, tx, idents, "serial", issued, expires)
   711  	test.AssertNotError(t, err, "Failed to add name set")
   712  	test.AssertNotError(t, tx.Commit(), "Failed to commit transaction")
   713  
   714  	exists, err = sa.FQDNSetExists(ctx, &sapb.FQDNSetExistsRequest{Identifiers: idents.ToProtoSlice()})
   715  	test.AssertNotError(t, err, "Failed to check FQDN set existence")
   716  	test.Assert(t, exists.Exists, "FQDN set does exist")
   717  }
   718  
   719  type execRecorder struct {
   720  	valuesPerRow int
   721  	query        string
   722  	args         []any
   723  }
   724  
   725  func (e *execRecorder) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) {
   726  	e.query = query
   727  	e.args = args
   728  	return rowsResult{int64(len(args) / e.valuesPerRow)}, nil
   729  }
   730  
   731  type rowsResult struct {
   732  	rowsAffected int64
   733  }
   734  
   735  func (r rowsResult) LastInsertId() (int64, error) {
   736  	return r.rowsAffected, nil
   737  }
   738  
   739  func (r rowsResult) RowsAffected() (int64, error) {
   740  	return r.rowsAffected, nil
   741  }
   742  
   743  func TestAddIssuedNames(t *testing.T) {
   744  	serial := big.NewInt(1)
   745  	expectedSerial := "000000000000000000000000000000000001"
   746  	notBefore := mustTime("2018-02-14 12:00")
   747  	expectedNotBefore := notBefore.Truncate(24 * time.Hour)
   748  	placeholdersPerName := "(?,?,?,?)"
   749  	baseQuery := "INSERT INTO issuedNames (reversedName,serial,notBefore,renewal) VALUES"
   750  
   751  	testCases := []struct {
   752  		Name         string
   753  		IssuedNames  []string
   754  		SerialNumber *big.Int
   755  		NotBefore    time.Time
   756  		Renewal      bool
   757  		ExpectedArgs []any
   758  	}{
   759  		{
   760  			Name:         "One domain, not a renewal",
   761  			IssuedNames:  []string{"example.co.uk"},
   762  			SerialNumber: serial,
   763  			NotBefore:    notBefore,
   764  			Renewal:      false,
   765  			ExpectedArgs: []any{
   766  				"uk.co.example",
   767  				expectedSerial,
   768  				expectedNotBefore,
   769  				false,
   770  			},
   771  		},
   772  		{
   773  			Name:         "Two domains, not a renewal",
   774  			IssuedNames:  []string{"example.co.uk", "example.xyz"},
   775  			SerialNumber: serial,
   776  			NotBefore:    notBefore,
   777  			Renewal:      false,
   778  			ExpectedArgs: []any{
   779  				"uk.co.example",
   780  				expectedSerial,
   781  				expectedNotBefore,
   782  				false,
   783  				"xyz.example",
   784  				expectedSerial,
   785  				expectedNotBefore,
   786  				false,
   787  			},
   788  		},
   789  		{
   790  			Name:         "One domain, renewal",
   791  			IssuedNames:  []string{"example.co.uk"},
   792  			SerialNumber: serial,
   793  			NotBefore:    notBefore,
   794  			Renewal:      true,
   795  			ExpectedArgs: []any{
   796  				"uk.co.example",
   797  				expectedSerial,
   798  				expectedNotBefore,
   799  				true,
   800  			},
   801  		},
   802  		{
   803  			Name:         "Two domains, renewal",
   804  			IssuedNames:  []string{"example.co.uk", "example.xyz"},
   805  			SerialNumber: serial,
   806  			NotBefore:    notBefore,
   807  			Renewal:      true,
   808  			ExpectedArgs: []any{
   809  				"uk.co.example",
   810  				expectedSerial,
   811  				expectedNotBefore,
   812  				true,
   813  				"xyz.example",
   814  				expectedSerial,
   815  				expectedNotBefore,
   816  				true,
   817  			},
   818  		},
   819  	}
   820  
   821  	for _, tc := range testCases {
   822  		t.Run(tc.Name, func(t *testing.T) {
   823  			e := execRecorder{valuesPerRow: 4}
   824  			err := addIssuedNames(
   825  				ctx,
   826  				&e,
   827  				&x509.Certificate{
   828  					DNSNames:     tc.IssuedNames,
   829  					SerialNumber: tc.SerialNumber,
   830  					NotBefore:    tc.NotBefore,
   831  				},
   832  				tc.Renewal)
   833  			test.AssertNotError(t, err, "addIssuedNames failed")
   834  			expectedPlaceholders := placeholdersPerName
   835  			for range len(tc.IssuedNames) - 1 {
   836  				expectedPlaceholders = fmt.Sprintf("%s,%s", expectedPlaceholders, placeholdersPerName)
   837  			}
   838  			expectedQuery := fmt.Sprintf("%s %s", baseQuery, expectedPlaceholders)
   839  			test.AssertEquals(t, e.query, expectedQuery)
   840  			if !reflect.DeepEqual(e.args, tc.ExpectedArgs) {
   841  				t.Errorf("Wrong args: got\n%#v, expected\n%#v", e.args, tc.ExpectedArgs)
   842  			}
   843  		})
   844  	}
   845  }
   846  
   847  func TestDeactivateAuthorization2(t *testing.T) {
   848  	sa, fc, cleanUp := initSA(t)
   849  	defer cleanUp()
   850  
   851  	reg := createWorkingRegistration(t, sa)
   852  
   853  	// deactivate a pending authorization
   854  	expires := fc.Now().Add(time.Hour).UTC()
   855  	attemptedAt := fc.Now()
   856  	authzID := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("example.com"), expires)
   857  	_, err := sa.DeactivateAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID})
   858  	test.AssertNotError(t, err, "sa.DeactivateAuthorization2 failed")
   859  
   860  	// deactivate a valid authorization
   861  	authzID = createFinalizedAuthorization(t, sa, reg.Id, identifier.NewDNS("example.com"), expires, "valid", attemptedAt)
   862  	_, err = sa.DeactivateAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID})
   863  	test.AssertNotError(t, err, "sa.DeactivateAuthorization2 failed")
   864  }
   865  
   866  func TestDeactivateAccount(t *testing.T) {
   867  	sa, _, cleanUp := initSA(t)
   868  	defer cleanUp()
   869  
   870  	reg := createWorkingRegistration(t, sa)
   871  
   872  	// An incomplete request should be rejected.
   873  	_, err := sa.DeactivateRegistration(context.Background(), &sapb.RegistrationID{})
   874  	test.AssertError(t, err, "Incomplete request should fail")
   875  	test.AssertContains(t, err.Error(), "incomplete")
   876  
   877  	// Deactivating should work, and return the same account but with updated
   878  	// status and cleared contacts.
   879  	got, err := sa.DeactivateRegistration(context.Background(), &sapb.RegistrationID{Id: reg.Id})
   880  	test.AssertNotError(t, err, "DeactivateRegistration failed")
   881  	test.AssertEquals(t, got.Id, reg.Id)
   882  	test.AssertEquals(t, core.AcmeStatus(got.Status), core.StatusDeactivated)
   883  
   884  	// Double-check that the DeactivateRegistration method returned the right
   885  	// thing, by fetching the same account ourselves.
   886  	got, err = sa.GetRegistration(context.Background(), &sapb.RegistrationID{Id: reg.Id})
   887  	test.AssertNotError(t, err, "GetRegistration failed")
   888  	test.AssertEquals(t, got.Id, reg.Id)
   889  	test.AssertEquals(t, core.AcmeStatus(got.Status), core.StatusDeactivated)
   890  
   891  	// Attempting to deactivate it a second time should fail, since it is already
   892  	// deactivated.
   893  	_, err = sa.DeactivateRegistration(context.Background(), &sapb.RegistrationID{Id: reg.Id})
   894  	test.AssertError(t, err, "Deactivating an already-deactivated account should fail")
   895  }
   896  
   897  func TestReverseFQDN(t *testing.T) {
   898  	testCases := []struct {
   899  		fqdn     string
   900  		reversed string
   901  	}{
   902  		{"", ""},
   903  		{"...", "..."},
   904  		{"com", "com"},
   905  		{"example.com", "com.example"},
   906  		{"www.example.com", "com.example.www"},
   907  		{"world.wide.web.example.com", "com.example.web.wide.world"},
   908  	}
   909  
   910  	for _, tc := range testCases {
   911  		output := reverseFQDN(tc.fqdn)
   912  		test.AssertEquals(t, output, tc.reversed)
   913  
   914  		output = reverseFQDN(tc.reversed)
   915  		test.AssertEquals(t, output, tc.fqdn)
   916  	}
   917  }
   918  
   919  func TestEncodeIssuedName(t *testing.T) {
   920  	testCases := []struct {
   921  		issuedName string
   922  		reversed   string
   923  		oneWay     bool
   924  	}{
   925  		// Empty strings and bare separators/TLDs should be unchanged.
   926  		{"", "", false},
   927  		{"...", "...", false},
   928  		{"com", "com", false},
   929  		// FQDNs should be reversed.
   930  		{"example.com", "com.example", false},
   931  		{"www.example.com", "com.example.www", false},
   932  		{"world.wide.web.example.com", "com.example.web.wide.world", false},
   933  		// IP addresses should stay the same.
   934  		{"1.2.3.4", "1.2.3.4", false},
   935  		{"2602:ff3a:1:abad:c0f:fee:abad:cafe", "2602:ff3a:1:abad:c0f:fee:abad:cafe", false},
   936  		// Tricksy FQDNs that look like IPv6 addresses should be parsed as FQDNs.
   937  		{"2602.ff3a.1.abad.c0f.fee.abad.cafe", "cafe.abad.fee.c0f.abad.1.ff3a.2602", false},
   938  		{"2602.ff3a.0001.abad.0c0f.0fee.abad.cafe", "cafe.abad.0fee.0c0f.abad.0001.ff3a.2602", false},
   939  		// IPv6 addresses should be returned in RFC 5952 format.
   940  		{"2602:ff3a:0001:abad:0c0f:0fee:abad:cafe", "2602:ff3a:1:abad:c0f:fee:abad:cafe", true},
   941  	}
   942  
   943  	for _, tc := range testCases {
   944  		output := EncodeIssuedName(tc.issuedName)
   945  		test.AssertEquals(t, output, tc.reversed)
   946  
   947  		if !tc.oneWay {
   948  			output = EncodeIssuedName(tc.reversed)
   949  			test.AssertEquals(t, output, tc.issuedName)
   950  		}
   951  	}
   952  }
   953  
   954  func TestNewOrderAndAuthzs(t *testing.T) {
   955  	sa, _, cleanup := initSA(t)
   956  	defer cleanup()
   957  
   958  	reg := createWorkingRegistration(t, sa)
   959  
   960  	// Insert two pre-existing authorizations to reference
   961  	idA := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("a.com"), sa.clk.Now().Add(time.Hour))
   962  	idB := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("b.com"), sa.clk.Now().Add(time.Hour))
   963  
   964  	nowC := sa.clk.Now().Add(time.Hour)
   965  	nowD := sa.clk.Now().Add(time.Hour)
   966  	expires := sa.clk.Now().Add(2 * time.Hour)
   967  	req := &sapb.NewOrderAndAuthzsRequest{
   968  		// Insert an order for four names, two of which already have authzs
   969  		NewOrder: &sapb.NewOrderRequest{
   970  			RegistrationID: reg.Id,
   971  			Expires:        timestamppb.New(expires),
   972  			Identifiers: []*corepb.Identifier{
   973  				identifier.NewDNS("a.com").ToProto(),
   974  				identifier.NewDNS("b.com").ToProto(),
   975  				identifier.NewDNS("c.com").ToProto(),
   976  				identifier.NewDNS("d.com").ToProto(),
   977  			},
   978  			V2Authorizations: []int64{idA, idB},
   979  		},
   980  		// And add new authorizations for the other two names.
   981  		NewAuthzs: []*sapb.NewAuthzRequest{
   982  			{
   983  				Identifier:     &corepb.Identifier{Type: "dns", Value: "c.com"},
   984  				RegistrationID: reg.Id,
   985  				Expires:        timestamppb.New(nowC),
   986  				ChallengeTypes: []string{string(core.ChallengeTypeHTTP01)},
   987  				Token:          core.NewToken(),
   988  			},
   989  			{
   990  				Identifier:     &corepb.Identifier{Type: "dns", Value: "d.com"},
   991  				RegistrationID: reg.Id,
   992  				Expires:        timestamppb.New(nowD),
   993  				ChallengeTypes: []string{string(core.ChallengeTypeHTTP01)},
   994  				Token:          core.NewToken(),
   995  			},
   996  		},
   997  	}
   998  	order, err := sa.NewOrderAndAuthzs(context.Background(), req)
   999  	test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed")
  1000  	test.Assert(t, order.Id != 0, "order ID should be non-zero")
  1001  	test.AssertEquals(t, len(order.V2Authorizations), 4)
  1002  	test.AssertSliceContains(t, order.V2Authorizations, idA)
  1003  	test.AssertSliceContains(t, order.V2Authorizations, idB)
  1004  	// Ensure that two new authzs were created.
  1005  	var newAuthzIDs []int64
  1006  	for _, id := range order.V2Authorizations {
  1007  		if id != idA && id != idB {
  1008  			newAuthzIDs = append(newAuthzIDs, id)
  1009  		}
  1010  	}
  1011  	test.AssertEquals(t, len(newAuthzIDs), 2)
  1012  	test.Assert(t, newAuthzIDs[0] != newAuthzIDs[1], "expected distinct new authz IDs")
  1013  
  1014  	var authzIDs []int64
  1015  	_, err = sa.dbMap.Select(ctx, &authzIDs, "SELECT authzID FROM orderToAuthz2 WHERE orderID = ?;", order.Id)
  1016  	test.AssertNotError(t, err, "Failed to count orderToAuthz entries")
  1017  	test.AssertEquals(t, len(authzIDs), 4)
  1018  	slices.Sort(authzIDs)
  1019  	expectedIDs := append([]int64{idA, idB}, newAuthzIDs...)
  1020  	slices.Sort(expectedIDs)
  1021  	test.AssertDeepEquals(t, authzIDs, expectedIDs)
  1022  }
  1023  
  1024  func TestNewOrderAndAuthzs_ReuseOnly(t *testing.T) {
  1025  	sa, fc, cleanup := initSA(t)
  1026  	defer cleanup()
  1027  
  1028  	reg := createWorkingRegistration(t, sa)
  1029  	expires := fc.Now().Add(2 * time.Hour)
  1030  
  1031  	// Insert two pre-existing authorizations to reference
  1032  	idA := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("a.com"), sa.clk.Now().Add(time.Hour))
  1033  	idB := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("b.com"), sa.clk.Now().Add(time.Hour))
  1034  	req := &sapb.NewOrderAndAuthzsRequest{
  1035  		// Insert an order for four names, two of which already have authzs
  1036  		NewOrder: &sapb.NewOrderRequest{
  1037  			RegistrationID: reg.Id,
  1038  			Expires:        timestamppb.New(expires),
  1039  			Identifiers: []*corepb.Identifier{
  1040  				identifier.NewDNS("a.com").ToProto(),
  1041  				identifier.NewDNS("b.com").ToProto(),
  1042  			},
  1043  			V2Authorizations: []int64{idA, idB},
  1044  		},
  1045  	}
  1046  	order, err := sa.NewOrderAndAuthzs(context.Background(), req)
  1047  	if err != nil {
  1048  		t.Fatal("sa.NewOrderAndAuthzs:", err)
  1049  	}
  1050  	if !reflect.DeepEqual(order.V2Authorizations, []int64{idA, idB}) {
  1051  		t.Errorf("sa.NewOrderAndAuthzs().V2Authorizations: want [%d, %d], got %v", idA, idB, order.V2Authorizations)
  1052  	}
  1053  }
  1054  
  1055  func TestNewOrderAndAuthzs_CreateOnly(t *testing.T) {
  1056  	sa, fc, cleanup := initSA(t)
  1057  	defer cleanup()
  1058  
  1059  	reg := createWorkingRegistration(t, sa)
  1060  	expires := fc.Now().Add(2 * time.Hour)
  1061  
  1062  	// Insert two pre-existing authorizations to reference
  1063  	_ = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("a.com"), sa.clk.Now().Add(time.Hour))
  1064  	_ = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("b.com"), sa.clk.Now().Add(time.Hour))
  1065  	req := &sapb.NewOrderAndAuthzsRequest{
  1066  		// Insert an order for four names, two of which already have authzs
  1067  		NewOrder: &sapb.NewOrderRequest{
  1068  			RegistrationID: reg.Id,
  1069  			Expires:        timestamppb.New(expires),
  1070  			Identifiers: []*corepb.Identifier{
  1071  				identifier.NewDNS("a.com").ToProto(),
  1072  				identifier.NewDNS("b.com").ToProto(),
  1073  			},
  1074  		},
  1075  		NewAuthzs: []*sapb.NewAuthzRequest{
  1076  			{
  1077  				Identifier:     &corepb.Identifier{Type: "dns", Value: "a.com"},
  1078  				RegistrationID: reg.Id,
  1079  				Expires:        timestamppb.New(expires),
  1080  				ChallengeTypes: []string{string(core.ChallengeTypeDNS01)},
  1081  				Token:          core.NewToken(),
  1082  			},
  1083  		},
  1084  	}
  1085  	order, err := sa.NewOrderAndAuthzs(context.Background(), req)
  1086  	if err != nil {
  1087  		t.Fatal("sa.NewOrderAndAuthzs:", err)
  1088  	}
  1089  	if len(order.V2Authorizations) != 1 {
  1090  		t.Fatalf("len(sa.NewOrderAndAuthzs().V2Authorizations): want 1, got %v", len(order.V2Authorizations))
  1091  	}
  1092  	gotAuthz, err := sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: order.V2Authorizations[0]})
  1093  	if err != nil {
  1094  		t.Fatalf("retrieving inserted authz: %s", err)
  1095  	}
  1096  	if gotAuthz.Identifier.Value != "a.com" {
  1097  		t.Errorf("New order authz identifier = %v, want %v", gotAuthz.Identifier.Value, "a.com")
  1098  	}
  1099  }
  1100  
  1101  func TestNewOrderAndAuthzs_NoAuthzsError(t *testing.T) {
  1102  	sa, fc, cleanup := initSA(t)
  1103  	defer cleanup()
  1104  
  1105  	reg := createWorkingRegistration(t, sa)
  1106  	expires := fc.Now().Add(2 * time.Hour)
  1107  
  1108  	// Insert two pre-existing authorizations to reference
  1109  	req := &sapb.NewOrderAndAuthzsRequest{
  1110  		// Insert an order for four names, two of which already have authzs
  1111  		NewOrder: &sapb.NewOrderRequest{
  1112  			RegistrationID: reg.Id,
  1113  			Expires:        timestamppb.New(expires),
  1114  			Identifiers:    nil,
  1115  		},
  1116  		NewAuthzs: nil,
  1117  	}
  1118  	_, err := sa.NewOrderAndAuthzs(context.Background(), req)
  1119  	if err != errIncompleteRequest {
  1120  		t.Errorf("sa.NewOrderAndAuthzs with no authzs: want %v, got %v", errIncompleteRequest, err)
  1121  	}
  1122  }
  1123  
  1124  // TestNewOrderAndAuthzs_NonNilInnerOrder verifies that a nil
  1125  // sapb.NewOrderAndAuthzsRequest NewOrder object returns an error.
  1126  func TestNewOrderAndAuthzs_NonNilInnerOrder(t *testing.T) {
  1127  	sa, fc, cleanup := initSA(t)
  1128  	defer cleanup()
  1129  
  1130  	reg := createWorkingRegistration(t, sa)
  1131  
  1132  	expires := fc.Now().Add(2 * time.Hour)
  1133  	_, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{
  1134  		NewAuthzs: []*sapb.NewAuthzRequest{
  1135  			{
  1136  				Identifier:     &corepb.Identifier{Type: "dns", Value: "c.com"},
  1137  				RegistrationID: reg.Id,
  1138  				Expires:        timestamppb.New(expires),
  1139  				ChallengeTypes: []string{string(core.ChallengeTypeDNS01)},
  1140  				Token:          core.NewToken(),
  1141  			},
  1142  		},
  1143  	})
  1144  	test.AssertErrorIs(t, err, errIncompleteRequest)
  1145  }
  1146  
  1147  func TestNewOrderAndAuthzs_MismatchedRegID(t *testing.T) {
  1148  	sa, _, cleanup := initSA(t)
  1149  	defer cleanup()
  1150  
  1151  	_, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{
  1152  		NewOrder: &sapb.NewOrderRequest{
  1153  			RegistrationID: 1,
  1154  		},
  1155  		NewAuthzs: []*sapb.NewAuthzRequest{
  1156  			{
  1157  				RegistrationID: 2,
  1158  			},
  1159  		},
  1160  	})
  1161  	test.AssertError(t, err, "mismatched regIDs should fail")
  1162  	test.AssertContains(t, err.Error(), "same account")
  1163  }
  1164  
  1165  func TestNewOrderAndAuthzs_NewAuthzExpectedFields(t *testing.T) {
  1166  	sa, fc, cleanup := initSA(t)
  1167  	defer cleanup()
  1168  
  1169  	reg := createWorkingRegistration(t, sa)
  1170  	expires := fc.Now().Add(time.Hour)
  1171  	domain := "a.com"
  1172  
  1173  	// Create an authz that does not yet exist in the database with some invalid
  1174  	// data smuggled in.
  1175  	order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{
  1176  		NewAuthzs: []*sapb.NewAuthzRequest{
  1177  			{
  1178  				Identifier:     &corepb.Identifier{Type: "dns", Value: domain},
  1179  				RegistrationID: reg.Id,
  1180  				Expires:        timestamppb.New(expires),
  1181  				ChallengeTypes: []string{string(core.ChallengeTypeHTTP01)},
  1182  				Token:          core.NewToken(),
  1183  			},
  1184  		},
  1185  		NewOrder: &sapb.NewOrderRequest{
  1186  			RegistrationID: reg.Id,
  1187  			Expires:        timestamppb.New(expires),
  1188  			Identifiers:    []*corepb.Identifier{identifier.NewDNS(domain).ToProto()},
  1189  		},
  1190  	})
  1191  	test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed")
  1192  
  1193  	// Safely get the authz for the order we created above.
  1194  	obj, err := sa.dbReadOnlyMap.Get(ctx, authzModel{}, order.V2Authorizations[0])
  1195  	test.AssertNotError(t, err, fmt.Sprintf("authorization %d not found", order.V2Authorizations[0]))
  1196  
  1197  	// To access the data stored in obj at compile time, we type assert obj
  1198  	// into a pointer to an authzModel.
  1199  	am, ok := obj.(*authzModel)
  1200  	test.Assert(t, ok, "Could not type assert obj into authzModel")
  1201  
  1202  	// If we're making a brand new authz, it should have the pending status
  1203  	// regardless of what incorrect status value was passed in during construction.
  1204  	test.AssertEquals(t, am.Status, statusUint(core.StatusPending))
  1205  
  1206  	// Testing for the existence of these boxed nils is a definite break from
  1207  	// our paradigm of avoiding passing around boxed nils whenever possible.
  1208  	// However, the existence of these boxed nils in relation to this test is
  1209  	// actually expected. If these tests fail, then a possible SA refactor or RA
  1210  	// bug placed incorrect data into brand new authz input fields.
  1211  	test.AssertBoxedNil(t, am.Attempted, "am.Attempted should be nil")
  1212  	test.AssertBoxedNil(t, am.AttemptedAt, "am.AttemptedAt should be nil")
  1213  	test.AssertBoxedNil(t, am.ValidationError, "am.ValidationError should be nil")
  1214  	test.AssertBoxedNil(t, am.ValidationRecord, "am.ValidationRecord should be nil")
  1215  }
  1216  
  1217  func TestNewOrderAndAuthzs_Profile(t *testing.T) {
  1218  	sa, fc, cleanup := initSA(t)
  1219  	defer cleanup()
  1220  
  1221  	reg := createWorkingRegistration(t, sa)
  1222  	expires := fc.Now().Add(time.Hour)
  1223  
  1224  	// Create and order and authz while specifying a profile.
  1225  	order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{
  1226  		NewOrder: &sapb.NewOrderRequest{
  1227  			RegistrationID:         reg.Id,
  1228  			Expires:                timestamppb.New(expires),
  1229  			Identifiers:            []*corepb.Identifier{identifier.NewDNS("example.com").ToProto()},
  1230  			CertificateProfileName: "test",
  1231  		},
  1232  		NewAuthzs: []*sapb.NewAuthzRequest{
  1233  			{
  1234  				Identifier:     &corepb.Identifier{Type: "dns", Value: "example.com"},
  1235  				RegistrationID: reg.Id,
  1236  				Expires:        timestamppb.New(expires),
  1237  				ChallengeTypes: []string{string(core.ChallengeTypeHTTP01)},
  1238  				Token:          core.NewToken(),
  1239  			},
  1240  		},
  1241  	})
  1242  	if err != nil {
  1243  		t.Fatalf("inserting order and authzs: %s", err)
  1244  	}
  1245  
  1246  	// Retrieve the order and check that the profile is correct.
  1247  	gotOrder, err := sa.GetOrder(context.Background(), &sapb.OrderRequest{Id: order.Id})
  1248  	if err != nil {
  1249  		t.Fatalf("retrieving inserted order: %s", err)
  1250  	}
  1251  	if gotOrder.CertificateProfileName != "test" {
  1252  		t.Errorf("order.CertificateProfileName = %v, want %v", gotOrder.CertificateProfileName, "test")
  1253  	}
  1254  
  1255  	// Retrieve the authz and check that the profile is correct.
  1256  	// Safely get the authz for the order we created above.
  1257  	gotAuthz, err := sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: order.V2Authorizations[0]})
  1258  	if err != nil {
  1259  		t.Fatalf("retrieving inserted authz: %s", err)
  1260  	}
  1261  	if gotAuthz.CertificateProfileName != "test" {
  1262  		t.Errorf("authz.CertificateProfileName = %v, want %v", gotAuthz.CertificateProfileName, "test")
  1263  	}
  1264  }
  1265  
  1266  func TestSetOrderProcessing(t *testing.T) {
  1267  	sa, fc, cleanup := initSA(t)
  1268  	defer cleanup()
  1269  
  1270  	reg := createWorkingRegistration(t, sa)
  1271  
  1272  	// Add one valid authz
  1273  	expires := fc.Now().Add(time.Hour)
  1274  	attemptedAt := fc.Now()
  1275  	authzID := createFinalizedAuthorization(t, sa, reg.Id, identifier.NewDNS("example.com"), expires, "valid", attemptedAt)
  1276  
  1277  	// Add a new order in pending status with no certificate serial
  1278  	expires1Year := sa.clk.Now().Add(365 * 24 * time.Hour)
  1279  	order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{
  1280  		NewOrder: &sapb.NewOrderRequest{
  1281  			RegistrationID:   reg.Id,
  1282  			Expires:          timestamppb.New(expires1Year),
  1283  			Identifiers:      []*corepb.Identifier{identifier.NewDNS("example.com").ToProto()},
  1284  			V2Authorizations: []int64{authzID},
  1285  		},
  1286  	})
  1287  	test.AssertNotError(t, err, "NewOrderAndAuthzs failed")
  1288  
  1289  	// Set the order to be processing
  1290  	_, err = sa.SetOrderProcessing(context.Background(), &sapb.OrderRequest{Id: order.Id})
  1291  	test.AssertNotError(t, err, "SetOrderProcessing failed")
  1292  
  1293  	// Read the order by ID from the DB to check the status was correctly updated
  1294  	// to processing
  1295  	updatedOrder, err := sa.GetOrder(
  1296  		context.Background(),
  1297  		&sapb.OrderRequest{Id: order.Id})
  1298  	test.AssertNotError(t, err, "GetOrder failed")
  1299  	test.AssertEquals(t, updatedOrder.Status, string(core.StatusProcessing))
  1300  	test.AssertEquals(t, updatedOrder.BeganProcessing, true)
  1301  
  1302  	// Try to set the same order to be processing again. We should get an error.
  1303  	_, err = sa.SetOrderProcessing(context.Background(), &sapb.OrderRequest{Id: order.Id})
  1304  	test.AssertError(t, err, "Set the same order processing twice. This should have been an error.")
  1305  	test.AssertErrorIs(t, err, berrors.OrderNotReady)
  1306  }
  1307  
  1308  func TestFinalizeOrder(t *testing.T) {
  1309  	sa, fc, cleanup := initSA(t)
  1310  	defer cleanup()
  1311  
  1312  	reg := createWorkingRegistration(t, sa)
  1313  	expires := fc.Now().Add(time.Hour)
  1314  	attemptedAt := fc.Now()
  1315  	authzID := createFinalizedAuthorization(t, sa, reg.Id, identifier.NewDNS("example.com"), expires, "valid", attemptedAt)
  1316  
  1317  	// Add a new order in pending status with no certificate serial
  1318  	expires1Year := sa.clk.Now().Add(365 * 24 * time.Hour)
  1319  	order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{
  1320  		NewOrder: &sapb.NewOrderRequest{
  1321  			RegistrationID:   reg.Id,
  1322  			Expires:          timestamppb.New(expires1Year),
  1323  			Identifiers:      []*corepb.Identifier{identifier.NewDNS("example.com").ToProto()},
  1324  			V2Authorizations: []int64{authzID},
  1325  		},
  1326  	})
  1327  	test.AssertNotError(t, err, "NewOrderAndAuthzs failed")
  1328  
  1329  	// Set the order to processing so it can be finalized
  1330  	_, err = sa.SetOrderProcessing(ctx, &sapb.OrderRequest{Id: order.Id})
  1331  	test.AssertNotError(t, err, "SetOrderProcessing failed")
  1332  
  1333  	// Finalize the order with a certificate serial
  1334  	order.CertificateSerial = "eat.serial.for.breakfast"
  1335  	_, err = sa.FinalizeOrder(context.Background(), &sapb.FinalizeOrderRequest{Id: order.Id, CertificateSerial: order.CertificateSerial})
  1336  	test.AssertNotError(t, err, "FinalizeOrder failed")
  1337  
  1338  	// Read the order by ID from the DB to check the certificate serial and status
  1339  	// was correctly updated
  1340  	updatedOrder, err := sa.GetOrder(
  1341  		context.Background(),
  1342  		&sapb.OrderRequest{Id: order.Id})
  1343  	test.AssertNotError(t, err, "GetOrder failed")
  1344  	test.AssertEquals(t, updatedOrder.CertificateSerial, "eat.serial.for.breakfast")
  1345  	test.AssertEquals(t, updatedOrder.Status, string(core.StatusValid))
  1346  }
  1347  
  1348  // TestGetOrder tests that round-tripping a simple order through
  1349  // NewOrderAndAuthzs and GetOrder has the expected result.
  1350  func TestGetOrder(t *testing.T) {
  1351  	sa, fc, cleanup := initSA(t)
  1352  	defer cleanup()
  1353  
  1354  	reg := createWorkingRegistration(t, sa)
  1355  	ident := identifier.NewDNS("example.com")
  1356  	authzExpires := fc.Now().Add(time.Hour)
  1357  	authzID := createPendingAuthorization(t, sa, reg.Id, ident, authzExpires)
  1358  
  1359  	// Set the order to expire in two hours
  1360  	expires := fc.Now().Add(2 * time.Hour)
  1361  
  1362  	inputOrder := &corepb.Order{
  1363  		RegistrationID:   reg.Id,
  1364  		Expires:          timestamppb.New(expires),
  1365  		Identifiers:      []*corepb.Identifier{ident.ToProto()},
  1366  		V2Authorizations: []int64{authzID},
  1367  	}
  1368  
  1369  	// Create the order
  1370  	order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{
  1371  		NewOrder: &sapb.NewOrderRequest{
  1372  			RegistrationID:   inputOrder.RegistrationID,
  1373  			Expires:          inputOrder.Expires,
  1374  			Identifiers:      inputOrder.Identifiers,
  1375  			V2Authorizations: inputOrder.V2Authorizations,
  1376  		},
  1377  	})
  1378  	test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed")
  1379  
  1380  	// Fetch the order by its ID and make sure it matches the expected
  1381  	storedOrder, err := sa.GetOrder(context.Background(), &sapb.OrderRequest{Id: order.Id})
  1382  	test.AssertNotError(t, err, "sa.GetOrder failed")
  1383  	created := sa.clk.Now()
  1384  	test.AssertDeepEquals(t, storedOrder, &corepb.Order{
  1385  		// The registration ID, authorizations, expiry, and identifiers should match the
  1386  		// input to NewOrderAndAuthzs
  1387  		RegistrationID:   inputOrder.RegistrationID,
  1388  		V2Authorizations: inputOrder.V2Authorizations,
  1389  		Identifiers:      inputOrder.Identifiers,
  1390  		Expires:          inputOrder.Expires,
  1391  		// The ID should have been set to 1 by the SA
  1392  		Id: storedOrder.Id,
  1393  		// The status should be pending
  1394  		Status: string(core.StatusPending),
  1395  		// The serial should be empty since this is a pending order
  1396  		CertificateSerial: "",
  1397  		// We should not be processing it
  1398  		BeganProcessing: false,
  1399  		// The created timestamp should have been set to the current time
  1400  		Created: timestamppb.New(created),
  1401  	})
  1402  
  1403  	if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
  1404  		// A migration is needed before initializing an SA with StoreAuthzsInOrders
  1405  		return
  1406  	}
  1407  
  1408  	features.Set(features.Config{
  1409  		StoreAuthzsInOrders: false,
  1410  	})
  1411  
  1412  	dbMap, err := DBMapForTest(vars.DBConnSA)
  1413  	if err != nil {
  1414  		t.Fatalf("Failed to create dbMap: %s", err)
  1415  	}
  1416  
  1417  	dbIncidentsMap, err := DBMapForTest(vars.DBConnIncidents)
  1418  	if err != nil {
  1419  		t.Fatalf("Failed to create dbMap: %s", err)
  1420  	}
  1421  
  1422  	sa2, err := NewSQLStorageAuthorityRO(dbMap, dbIncidentsMap, metrics.NoopRegisterer, 1, 0, fc, log)
  1423  	if err != nil {
  1424  		t.Fatalf("Failed to create SA: %s", err)
  1425  	}
  1426  
  1427  	storedOrder2, err := sa2.GetOrder(context.Background(), &sapb.OrderRequest{Id: order.Id})
  1428  	test.AssertNotError(t, err, "fetching order after enabling StoreAuthzsInOrders")
  1429  	test.AssertDeepEquals(t, storedOrder2, storedOrder)
  1430  }
  1431  
  1432  // TestGetOrderWithProfile tests that round-tripping a simple order through
  1433  // NewOrderAndAuthzs and GetOrder has the expected result.
  1434  func TestGetOrderWithProfile(t *testing.T) {
  1435  	sa, fc, cleanup := initSA(t)
  1436  	defer cleanup()
  1437  
  1438  	reg := createWorkingRegistration(t, sa)
  1439  	ident := identifier.NewDNS("example.com")
  1440  	authzExpires := fc.Now().Add(time.Hour)
  1441  	authzID := createPendingAuthorization(t, sa, reg.Id, ident, authzExpires)
  1442  
  1443  	// Set the order to expire in two hours
  1444  	expires := fc.Now().Add(2 * time.Hour)
  1445  
  1446  	inputOrder := &corepb.Order{
  1447  		RegistrationID:         reg.Id,
  1448  		Expires:                timestamppb.New(expires),
  1449  		Identifiers:            []*corepb.Identifier{ident.ToProto()},
  1450  		V2Authorizations:       []int64{authzID},
  1451  		CertificateProfileName: "tbiapb",
  1452  	}
  1453  
  1454  	// Create the order
  1455  	order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{
  1456  		NewOrder: &sapb.NewOrderRequest{
  1457  			RegistrationID:         inputOrder.RegistrationID,
  1458  			Expires:                inputOrder.Expires,
  1459  			Identifiers:            inputOrder.Identifiers,
  1460  			V2Authorizations:       inputOrder.V2Authorizations,
  1461  			CertificateProfileName: inputOrder.CertificateProfileName,
  1462  		},
  1463  	})
  1464  	test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed")
  1465  
  1466  	// Fetch the order by its ID and make sure it matches the expected
  1467  	storedOrder, err := sa.GetOrder(context.Background(), &sapb.OrderRequest{Id: order.Id})
  1468  	test.AssertNotError(t, err, "sa.GetOrder failed")
  1469  	created := sa.clk.Now()
  1470  	test.AssertDeepEquals(t, storedOrder, &corepb.Order{
  1471  		// The registration ID, authorizations, expiry, and names should match the
  1472  		// input to NewOrderAndAuthzs
  1473  		RegistrationID:   inputOrder.RegistrationID,
  1474  		V2Authorizations: inputOrder.V2Authorizations,
  1475  		Identifiers:      inputOrder.Identifiers,
  1476  		Expires:          inputOrder.Expires,
  1477  		// The ID should have been set to 1 by the SA
  1478  		Id: storedOrder.Id,
  1479  		// The status should be pending
  1480  		Status: string(core.StatusPending),
  1481  		// The serial should be empty since this is a pending order
  1482  		CertificateSerial: "",
  1483  		// We should not be processing it
  1484  		BeganProcessing: false,
  1485  		// The created timestamp should have been set to the current time
  1486  		Created:                timestamppb.New(created),
  1487  		CertificateProfileName: "tbiapb",
  1488  	})
  1489  }
  1490  
  1491  // TestGetAuthorization2NoRows ensures that the GetAuthorization2 function returns
  1492  // the correct error when there are no results for the provided ID.
  1493  func TestGetAuthorization2NoRows(t *testing.T) {
  1494  	sa, _, cleanUp := initSA(t)
  1495  	defer cleanUp()
  1496  
  1497  	// An empty authz ID should result in a not found berror.
  1498  	id := int64(123)
  1499  	_, err := sa.GetAuthorization2(ctx, &sapb.AuthorizationID2{Id: id})
  1500  	test.AssertError(t, err, "Didn't get an error looking up non-existent authz ID")
  1501  	test.AssertErrorIs(t, err, berrors.NotFound)
  1502  }
  1503  
  1504  func TestFasterGetOrderForNames(t *testing.T) {
  1505  	sa, fc, cleanUp := initSA(t)
  1506  	defer cleanUp()
  1507  
  1508  	ident := identifier.NewDNS("example.com")
  1509  	expires := fc.Now().Add(time.Hour)
  1510  
  1511  	key, _ := goodTestJWK().MarshalJSON()
  1512  	reg, err := sa.NewRegistration(ctx, &corepb.Registration{
  1513  		Key: key,
  1514  	})
  1515  	test.AssertNotError(t, err, "Couldn't create test registration")
  1516  
  1517  	authzIDs := createPendingAuthorization(t, sa, reg.Id, ident, expires)
  1518  
  1519  	_, err = sa.NewOrderAndAuthzs(ctx, &sapb.NewOrderAndAuthzsRequest{
  1520  		NewOrder: &sapb.NewOrderRequest{
  1521  			RegistrationID:   reg.Id,
  1522  			Expires:          timestamppb.New(expires),
  1523  			V2Authorizations: []int64{authzIDs},
  1524  			Identifiers:      []*corepb.Identifier{ident.ToProto()},
  1525  		},
  1526  	})
  1527  	test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed")
  1528  
  1529  	_, err = sa.NewOrderAndAuthzs(ctx, &sapb.NewOrderAndAuthzsRequest{
  1530  		NewOrder: &sapb.NewOrderRequest{
  1531  			RegistrationID:   reg.Id,
  1532  			Expires:          timestamppb.New(expires),
  1533  			V2Authorizations: []int64{authzIDs},
  1534  			Identifiers:      []*corepb.Identifier{ident.ToProto()},
  1535  		},
  1536  	})
  1537  	test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed")
  1538  
  1539  	_, err = sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{
  1540  		AcctID:      reg.Id,
  1541  		Identifiers: []*corepb.Identifier{ident.ToProto()},
  1542  	})
  1543  	test.AssertNotError(t, err, "sa.GetOrderForNames failed")
  1544  }
  1545  
  1546  func TestGetOrderForNames(t *testing.T) {
  1547  	sa, fc, cleanUp := initSA(t)
  1548  	defer cleanUp()
  1549  
  1550  	// Give the order we create a short lifetime
  1551  	orderLifetime := time.Hour
  1552  	expires := fc.Now().Add(orderLifetime)
  1553  
  1554  	// Create two test registrations to associate with orders
  1555  	key, _ := goodTestJWK().MarshalJSON()
  1556  	regA, err := sa.NewRegistration(ctx, &corepb.Registration{
  1557  		Key: key,
  1558  	})
  1559  	test.AssertNotError(t, err, "Couldn't create test registration")
  1560  
  1561  	// Add one pending authz for the first name for regA and one
  1562  	// pending authz for the second name for regA
  1563  	authzExpires := fc.Now().Add(time.Hour)
  1564  	authzIDA := createPendingAuthorization(t, sa, regA.Id, identifier.NewDNS("example.com"), authzExpires)
  1565  	authzIDB := createPendingAuthorization(t, sa, regA.Id, identifier.NewDNS("just.another.example.com"), authzExpires)
  1566  
  1567  	ctx := context.Background()
  1568  	idents := identifier.ACMEIdentifiers{
  1569  		identifier.NewDNS("example.com"),
  1570  		identifier.NewDNS("just.another.example.com"),
  1571  	}
  1572  
  1573  	// Call GetOrderForNames for a set of names we haven't created an order for
  1574  	// yet
  1575  	result, err := sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{
  1576  		AcctID:      regA.Id,
  1577  		Identifiers: idents.ToProtoSlice(),
  1578  	})
  1579  	// We expect the result to return an error
  1580  	test.AssertError(t, err, "sa.GetOrderForNames did not return an error for an empty result")
  1581  	// The error should be a notfound error
  1582  	test.AssertErrorIs(t, err, berrors.NotFound)
  1583  	// The result should be nil
  1584  	test.Assert(t, result == nil, "sa.GetOrderForNames for non-existent order returned non-nil result")
  1585  
  1586  	// Add a new order for a set of names
  1587  	order, err := sa.NewOrderAndAuthzs(ctx, &sapb.NewOrderAndAuthzsRequest{
  1588  		NewOrder: &sapb.NewOrderRequest{
  1589  			RegistrationID:   regA.Id,
  1590  			Expires:          timestamppb.New(expires),
  1591  			V2Authorizations: []int64{authzIDA, authzIDB},
  1592  			Identifiers:      idents.ToProtoSlice(),
  1593  		},
  1594  	})
  1595  	// It shouldn't error
  1596  	test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed")
  1597  	// The order ID shouldn't be nil
  1598  	test.AssertNotNil(t, order.Id, "NewOrderAndAuthzs returned with a nil Id")
  1599  
  1600  	// Call GetOrderForNames with the same account ID and set of names as the
  1601  	// above NewOrderAndAuthzs call
  1602  	result, err = sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{
  1603  		AcctID:      regA.Id,
  1604  		Identifiers: idents.ToProtoSlice(),
  1605  	})
  1606  	// It shouldn't error
  1607  	test.AssertNotError(t, err, "sa.GetOrderForNames failed")
  1608  	// The order returned should have the same ID as the order we created above
  1609  	test.AssertNotNil(t, result, "Returned order was nil")
  1610  	test.AssertEquals(t, result.Id, order.Id)
  1611  
  1612  	// Call GetOrderForNames with a different account ID from the NewOrderAndAuthzs call
  1613  	regB := int64(1337)
  1614  	result, err = sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{
  1615  		AcctID:      regB,
  1616  		Identifiers: idents.ToProtoSlice(),
  1617  	})
  1618  	// It should error
  1619  	test.AssertError(t, err, "sa.GetOrderForNames did not return an error for an empty result")
  1620  	// The error should be a notfound error
  1621  	test.AssertErrorIs(t, err, berrors.NotFound)
  1622  	// The result should be nil
  1623  	test.Assert(t, result == nil, "sa.GetOrderForNames for diff AcctID returned non-nil result")
  1624  
  1625  	// Advance the clock beyond the initial order's lifetime
  1626  	fc.Add(2 * orderLifetime)
  1627  
  1628  	// Call GetOrderForNames again with the same account ID and set of names as
  1629  	// the initial NewOrderAndAuthzs call
  1630  	result, err = sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{
  1631  		AcctID:      regA.Id,
  1632  		Identifiers: idents.ToProtoSlice(),
  1633  	})
  1634  	// It should error since there is no result
  1635  	test.AssertError(t, err, "sa.GetOrderForNames did not return an error for an empty result")
  1636  	// The error should be a notfound error
  1637  	test.AssertErrorIs(t, err, berrors.NotFound)
  1638  	// The result should be nil because the initial order expired & we don't want
  1639  	// to return expired orders
  1640  	test.Assert(t, result == nil, "sa.GetOrderForNames returned non-nil result for expired order case")
  1641  
  1642  	// Create two valid authorizations
  1643  	authzExpires = fc.Now().Add(time.Hour)
  1644  	attemptedAt := fc.Now()
  1645  	authzIDC := createFinalizedAuthorization(t, sa, regA.Id, identifier.NewDNS("zombo.com"), authzExpires, "valid", attemptedAt)
  1646  	authzIDD := createFinalizedAuthorization(t, sa, regA.Id, identifier.NewDNS("welcome.to.zombo.com"), authzExpires, "valid", attemptedAt)
  1647  
  1648  	// Add a fresh order that uses the authorizations created above
  1649  	expires = fc.Now().Add(orderLifetime)
  1650  	order, err = sa.NewOrderAndAuthzs(ctx, &sapb.NewOrderAndAuthzsRequest{
  1651  		NewOrder: &sapb.NewOrderRequest{
  1652  			RegistrationID:   regA.Id,
  1653  			Expires:          timestamppb.New(expires),
  1654  			V2Authorizations: []int64{authzIDC, authzIDD},
  1655  			Identifiers:      idents.ToProtoSlice(),
  1656  		},
  1657  	})
  1658  	// It shouldn't error
  1659  	test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed")
  1660  	// The order ID shouldn't be nil
  1661  	test.AssertNotNil(t, order.Id, "NewOrderAndAuthzs returned with a nil Id")
  1662  
  1663  	// Call GetOrderForNames with the same account ID and set of names as
  1664  	// the earlier NewOrderAndAuthzs call
  1665  	result, err = sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{
  1666  		AcctID:      regA.Id,
  1667  		Identifiers: idents.ToProtoSlice(),
  1668  	})
  1669  	// It should not error since a ready order can be reused.
  1670  	test.AssertNotError(t, err, "sa.GetOrderForNames returned an unexpected error for ready order reuse")
  1671  	// The order returned should have the same ID as the order we created above
  1672  	test.AssertNotNil(t, result, "sa.GetOrderForNames returned nil result")
  1673  	test.AssertEquals(t, result.Id, order.Id)
  1674  
  1675  	// Set the order processing so it can be finalized
  1676  	_, err = sa.SetOrderProcessing(ctx, &sapb.OrderRequest{Id: order.Id})
  1677  	test.AssertNotError(t, err, "sa.SetOrderProcessing failed")
  1678  
  1679  	// Finalize the order
  1680  	order.CertificateSerial = "cinnamon toast crunch"
  1681  	_, err = sa.FinalizeOrder(ctx, &sapb.FinalizeOrderRequest{Id: order.Id, CertificateSerial: order.CertificateSerial})
  1682  	test.AssertNotError(t, err, "sa.FinalizeOrder failed")
  1683  
  1684  	// Call GetOrderForNames with the same account ID and set of names as
  1685  	// the earlier NewOrderAndAuthzs call
  1686  	result, err = sa.GetOrderForNames(ctx, &sapb.GetOrderForNamesRequest{
  1687  		AcctID:      regA.Id,
  1688  		Identifiers: idents.ToProtoSlice(),
  1689  	})
  1690  	// It should error since a valid order should not be reused.
  1691  	test.AssertError(t, err, "sa.GetOrderForNames did not return an error for an empty result")
  1692  	// The error should be a notfound error
  1693  	test.AssertErrorIs(t, err, berrors.NotFound)
  1694  	// The result should be nil because the one matching order has been finalized
  1695  	// already
  1696  	test.Assert(t, result == nil, "sa.GetOrderForNames returned non-nil result for finalized order case")
  1697  }
  1698  
  1699  func TestStatusForOrder(t *testing.T) {
  1700  	sa, fc, cleanUp := initSA(t)
  1701  	defer cleanUp()
  1702  
  1703  	ctx := context.Background()
  1704  	expires := fc.Now().Add(time.Hour)
  1705  	alreadyExpired := expires.Add(-2 * time.Hour)
  1706  	attemptedAt := fc.Now()
  1707  
  1708  	// Create a registration to work with
  1709  	reg := createWorkingRegistration(t, sa)
  1710  
  1711  	// Create a pending authz, an expired authz, an invalid authz, a deactivated authz,
  1712  	// and a valid authz
  1713  	pendingID := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("pending.your.order.is.up"), expires)
  1714  	expiredID := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("expired.your.order.is.up"), alreadyExpired)
  1715  	invalidID := createFinalizedAuthorization(t, sa, reg.Id, identifier.NewDNS("invalid.your.order.is.up"), expires, "invalid", attemptedAt)
  1716  	validID := createFinalizedAuthorization(t, sa, reg.Id, identifier.NewDNS("valid.your.order.is.up"), expires, "valid", attemptedAt)
  1717  	deactivatedID := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("deactivated.your.order.is.up"), expires)
  1718  	_, err := sa.DeactivateAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: deactivatedID})
  1719  	test.AssertNotError(t, err, "sa.DeactivateAuthorization2 failed")
  1720  
  1721  	testCases := []struct {
  1722  		Name             string
  1723  		AuthorizationIDs []int64
  1724  		OrderIdents      identifier.ACMEIdentifiers
  1725  		OrderExpires     *timestamppb.Timestamp
  1726  		ExpectedStatus   string
  1727  		SetProcessing    bool
  1728  		Finalize         bool
  1729  	}{
  1730  		{
  1731  			Name: "Order with an invalid authz",
  1732  			OrderIdents: identifier.ACMEIdentifiers{
  1733  				identifier.NewDNS("pending.your.order.is.up"),
  1734  				identifier.NewDNS("invalid.your.order.is.up"),
  1735  				identifier.NewDNS("deactivated.your.order.is.up"),
  1736  				identifier.NewDNS("valid.your.order.is.up"),
  1737  			},
  1738  			AuthorizationIDs: []int64{pendingID, invalidID, deactivatedID, validID},
  1739  			ExpectedStatus:   string(core.StatusInvalid),
  1740  		},
  1741  		{
  1742  			Name: "Order with an expired authz",
  1743  			OrderIdents: identifier.ACMEIdentifiers{
  1744  				identifier.NewDNS("pending.your.order.is.up"),
  1745  				identifier.NewDNS("expired.your.order.is.up"),
  1746  				identifier.NewDNS("deactivated.your.order.is.up"),
  1747  				identifier.NewDNS("valid.your.order.is.up"),
  1748  			},
  1749  			AuthorizationIDs: []int64{pendingID, expiredID, deactivatedID, validID},
  1750  			ExpectedStatus:   string(core.StatusInvalid),
  1751  		},
  1752  		{
  1753  			Name: "Order with a deactivated authz",
  1754  			OrderIdents: identifier.ACMEIdentifiers{
  1755  				identifier.NewDNS("pending.your.order.is.up"),
  1756  				identifier.NewDNS("deactivated.your.order.is.up"),
  1757  				identifier.NewDNS("valid.your.order.is.up"),
  1758  			},
  1759  			AuthorizationIDs: []int64{pendingID, deactivatedID, validID},
  1760  			ExpectedStatus:   string(core.StatusInvalid),
  1761  		},
  1762  		{
  1763  			Name: "Order with a pending authz",
  1764  			OrderIdents: identifier.ACMEIdentifiers{
  1765  				identifier.NewDNS("valid.your.order.is.up"),
  1766  				identifier.NewDNS("pending.your.order.is.up"),
  1767  			},
  1768  			AuthorizationIDs: []int64{validID, pendingID},
  1769  			ExpectedStatus:   string(core.StatusPending),
  1770  		},
  1771  		{
  1772  			Name:             "Order with only valid authzs, not yet processed or finalized",
  1773  			OrderIdents:      identifier.ACMEIdentifiers{identifier.NewDNS("valid.your.order.is.up")},
  1774  			AuthorizationIDs: []int64{validID},
  1775  			ExpectedStatus:   string(core.StatusReady),
  1776  		},
  1777  		{
  1778  			Name:             "Order with only valid authzs, set processing",
  1779  			OrderIdents:      identifier.ACMEIdentifiers{identifier.NewDNS("valid.your.order.is.up")},
  1780  			AuthorizationIDs: []int64{validID},
  1781  			SetProcessing:    true,
  1782  			ExpectedStatus:   string(core.StatusProcessing),
  1783  		},
  1784  		{
  1785  			Name:             "Order with only valid authzs, not yet processed or finalized, OrderReadyStatus feature flag",
  1786  			OrderIdents:      identifier.ACMEIdentifiers{identifier.NewDNS("valid.your.order.is.up")},
  1787  			AuthorizationIDs: []int64{validID},
  1788  			ExpectedStatus:   string(core.StatusReady),
  1789  		},
  1790  		{
  1791  			Name:             "Order with only valid authzs, set processing",
  1792  			OrderIdents:      identifier.ACMEIdentifiers{identifier.NewDNS("valid.your.order.is.up")},
  1793  			AuthorizationIDs: []int64{validID},
  1794  			SetProcessing:    true,
  1795  			ExpectedStatus:   string(core.StatusProcessing),
  1796  		},
  1797  		{
  1798  			Name:             "Order with only valid authzs, set processing and finalized",
  1799  			OrderIdents:      identifier.ACMEIdentifiers{identifier.NewDNS("valid.your.order.is.up")},
  1800  			AuthorizationIDs: []int64{validID},
  1801  			SetProcessing:    true,
  1802  			Finalize:         true,
  1803  			ExpectedStatus:   string(core.StatusValid),
  1804  		},
  1805  	}
  1806  
  1807  	for _, tc := range testCases {
  1808  		t.Run(tc.Name, func(t *testing.T) {
  1809  			// If the testcase doesn't specify an order expiry use a default timestamp
  1810  			// in the near future.
  1811  			orderExpiry := tc.OrderExpires
  1812  			if !orderExpiry.IsValid() {
  1813  				orderExpiry = timestamppb.New(expires)
  1814  			}
  1815  
  1816  			newOrder, err := sa.NewOrderAndAuthzs(ctx, &sapb.NewOrderAndAuthzsRequest{
  1817  				NewOrder: &sapb.NewOrderRequest{
  1818  					RegistrationID:   reg.Id,
  1819  					Expires:          orderExpiry,
  1820  					V2Authorizations: tc.AuthorizationIDs,
  1821  					Identifiers:      tc.OrderIdents.ToProtoSlice(),
  1822  				},
  1823  			})
  1824  			test.AssertNotError(t, err, "NewOrderAndAuthzs errored unexpectedly")
  1825  			// If requested, set the order to processing
  1826  			if tc.SetProcessing {
  1827  				_, err := sa.SetOrderProcessing(ctx, &sapb.OrderRequest{Id: newOrder.Id})
  1828  				test.AssertNotError(t, err, "Error setting order to processing status")
  1829  			}
  1830  			// If requested, finalize the order
  1831  			if tc.Finalize {
  1832  				newOrder.CertificateSerial = "lucky charms"
  1833  				_, err = sa.FinalizeOrder(ctx, &sapb.FinalizeOrderRequest{Id: newOrder.Id, CertificateSerial: newOrder.CertificateSerial})
  1834  				test.AssertNotError(t, err, "Error finalizing order")
  1835  			}
  1836  			// Fetch the order by ID to get its calculated status
  1837  			storedOrder, err := sa.GetOrder(ctx, &sapb.OrderRequest{Id: newOrder.Id})
  1838  			test.AssertNotError(t, err, "GetOrder failed")
  1839  			// The status shouldn't be nil
  1840  			test.AssertNotNil(t, storedOrder.Status, "Order status was nil")
  1841  			// The status should match expected
  1842  			test.AssertEquals(t, storedOrder.Status, tc.ExpectedStatus)
  1843  		})
  1844  	}
  1845  
  1846  }
  1847  
  1848  func TestUpdateChallengesDeleteUnused(t *testing.T) {
  1849  	sa, fc, cleanUp := initSA(t)
  1850  	defer cleanUp()
  1851  
  1852  	expires := fc.Now().Add(time.Hour)
  1853  	ctx := context.Background()
  1854  	attemptedAt := fc.Now()
  1855  
  1856  	// Create a valid authz
  1857  	reg := createWorkingRegistration(t, sa)
  1858  	authzID := createFinalizedAuthorization(t, sa, reg.Id, identifier.NewDNS("example.com"), expires, "valid", attemptedAt)
  1859  
  1860  	result, err := sa.GetAuthorization2(ctx, &sapb.AuthorizationID2{Id: authzID})
  1861  	test.AssertNotError(t, err, "sa.GetAuthorization2 failed")
  1862  
  1863  	if len(result.Challenges) != 1 {
  1864  		t.Fatalf("expected 1 challenge left after finalization, got %d", len(result.Challenges))
  1865  	}
  1866  	if result.Challenges[0].Status != string(core.StatusValid) {
  1867  		t.Errorf("expected challenge status %q, got %q", core.StatusValid, result.Challenges[0].Status)
  1868  	}
  1869  	if result.Challenges[0].Type != "http-01" {
  1870  		t.Errorf("expected challenge type %q, got %q", "http-01", result.Challenges[0].Type)
  1871  	}
  1872  }
  1873  
  1874  func TestRevokeCertificate(t *testing.T) {
  1875  	sa, fc, cleanUp := initSA(t)
  1876  	defer cleanUp()
  1877  
  1878  	reg := createWorkingRegistration(t, sa)
  1879  	// Add a cert to the DB to test with.
  1880  	serial, testCert := test.ThrowAwayCert(t, fc)
  1881  	issuedTime := sa.clk.Now()
  1882  	_, err := sa.AddSerial(ctx, &sapb.AddSerialRequest{
  1883  		RegID:   reg.Id,
  1884  		Serial:  core.SerialToString(testCert.SerialNumber),
  1885  		Created: timestamppb.New(testCert.NotBefore),
  1886  		Expires: timestamppb.New(testCert.NotAfter),
  1887  	})
  1888  	test.AssertNotError(t, err, "failed to add test serial")
  1889  	_, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
  1890  		Der:          testCert.Raw,
  1891  		RegID:        reg.Id,
  1892  		Issued:       timestamppb.New(issuedTime),
  1893  		IssuerNameID: 1,
  1894  	})
  1895  	test.AssertNotError(t, err, "Couldn't add test cert")
  1896  
  1897  	status, err := sa.GetCertificateStatus(ctx, &sapb.Serial{Serial: serial})
  1898  	test.AssertNotError(t, err, "GetCertificateStatus failed")
  1899  	test.AssertEquals(t, core.OCSPStatus(status.Status), core.OCSPStatusGood)
  1900  
  1901  	fc.Add(1 * time.Hour)
  1902  
  1903  	now := fc.Now()
  1904  	reason := int64(1)
  1905  
  1906  	_, err = sa.RevokeCertificate(context.Background(), &sapb.RevokeCertificateRequest{
  1907  		IssuerID: 1,
  1908  		Serial:   serial,
  1909  		Date:     timestamppb.New(now),
  1910  		Reason:   reason,
  1911  		ShardIdx: 1,
  1912  	})
  1913  	test.AssertNotError(t, err, "RevokeCertificate with no OCSP response should succeed")
  1914  
  1915  	status, err = sa.GetCertificateStatus(ctx, &sapb.Serial{Serial: serial})
  1916  	test.AssertNotError(t, err, "GetCertificateStatus failed")
  1917  	test.AssertEquals(t, core.OCSPStatus(status.Status), core.OCSPStatusRevoked)
  1918  	test.AssertEquals(t, status.RevokedReason, reason)
  1919  	test.AssertEquals(t, status.RevokedDate.AsTime(), now)
  1920  	test.AssertEquals(t, status.OcspLastUpdated.AsTime(), now)
  1921  
  1922  	_, err = sa.RevokeCertificate(context.Background(), &sapb.RevokeCertificateRequest{
  1923  		IssuerID: 1,
  1924  		Serial:   serial,
  1925  		Date:     timestamppb.New(now),
  1926  		Reason:   reason,
  1927  	})
  1928  	test.AssertError(t, err, "RevokeCertificate should've failed when certificate already revoked")
  1929  }
  1930  
  1931  func TestRevokeCertificateWithShard(t *testing.T) {
  1932  	sa, fc, cleanUp := initSA(t)
  1933  	defer cleanUp()
  1934  
  1935  	// Add a cert to the DB to test with.
  1936  	reg := createWorkingRegistration(t, sa)
  1937  	eeCert, err := core.LoadCert("../test/hierarchy/ee-e1.cert.pem")
  1938  	test.AssertNotError(t, err, "failed to load test cert")
  1939  	_, err = sa.AddSerial(ctx, &sapb.AddSerialRequest{
  1940  		RegID:   reg.Id,
  1941  		Serial:  core.SerialToString(eeCert.SerialNumber),
  1942  		Created: timestamppb.New(eeCert.NotBefore),
  1943  		Expires: timestamppb.New(eeCert.NotAfter),
  1944  	})
  1945  	test.AssertNotError(t, err, "failed to add test serial")
  1946  	_, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
  1947  		Der:          eeCert.Raw,
  1948  		RegID:        reg.Id,
  1949  		Issued:       timestamppb.New(eeCert.NotBefore),
  1950  		IssuerNameID: 1,
  1951  	})
  1952  	test.AssertNotError(t, err, "failed to add test cert")
  1953  
  1954  	serial := core.SerialToString(eeCert.SerialNumber)
  1955  	fc.Add(1 * time.Hour)
  1956  	now := fc.Now()
  1957  	reason := int64(1)
  1958  
  1959  	_, err = sa.RevokeCertificate(context.Background(), &sapb.RevokeCertificateRequest{
  1960  		IssuerID: 1,
  1961  		ShardIdx: 9,
  1962  		Serial:   serial,
  1963  		Date:     timestamppb.New(now),
  1964  		Reason:   reason,
  1965  	})
  1966  	test.AssertNotError(t, err, "RevokeCertificate with no OCSP response should succeed")
  1967  
  1968  	status, err := sa.GetCertificateStatus(ctx, &sapb.Serial{Serial: serial})
  1969  	test.AssertNotError(t, err, "GetCertificateStatus failed")
  1970  	test.AssertEquals(t, core.OCSPStatus(status.Status), core.OCSPStatusRevoked)
  1971  	test.AssertEquals(t, status.RevokedReason, reason)
  1972  	test.AssertEquals(t, status.RevokedDate.AsTime(), now)
  1973  	test.AssertEquals(t, status.OcspLastUpdated.AsTime(), now)
  1974  	test.AssertEquals(t, status.NotAfter.AsTime(), eeCert.NotAfter)
  1975  
  1976  	var result revokedCertModel
  1977  	err = sa.dbMap.SelectOne(
  1978  		ctx, &result, `SELECT * FROM revokedCertificates WHERE serial = ?`, core.SerialToString(eeCert.SerialNumber))
  1979  	test.AssertNotError(t, err, "should be exactly one row in revokedCertificates")
  1980  	test.AssertEquals(t, result.ShardIdx, int64(9))
  1981  	test.AssertEquals(t, result.RevokedReason, revocation.KeyCompromise)
  1982  }
  1983  
  1984  func TestUpdateRevokedCertificate(t *testing.T) {
  1985  	sa, fc, cleanUp := initSA(t)
  1986  	defer cleanUp()
  1987  
  1988  	// Add a cert to the DB to test with.
  1989  	reg := createWorkingRegistration(t, sa)
  1990  	serial, testCert := test.ThrowAwayCert(t, fc)
  1991  	issuedTime := fc.Now()
  1992  	_, err := sa.AddSerial(ctx, &sapb.AddSerialRequest{
  1993  		RegID:   reg.Id,
  1994  		Serial:  core.SerialToString(testCert.SerialNumber),
  1995  		Created: timestamppb.New(testCert.NotBefore),
  1996  		Expires: timestamppb.New(testCert.NotAfter),
  1997  	})
  1998  	test.AssertNotError(t, err, "failed to add test serial")
  1999  	_, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
  2000  		Der:          testCert.Raw,
  2001  		RegID:        reg.Id,
  2002  		Issued:       timestamppb.New(issuedTime),
  2003  		IssuerNameID: 1,
  2004  	})
  2005  	test.AssertNotError(t, err, "Couldn't add test cert")
  2006  	fc.Add(1 * time.Hour)
  2007  
  2008  	// Try to update it before its been revoked
  2009  	now := fc.Now()
  2010  	_, err = sa.UpdateRevokedCertificate(context.Background(), &sapb.RevokeCertificateRequest{
  2011  		IssuerID: 1,
  2012  		Serial:   serial,
  2013  		Date:     timestamppb.New(now),
  2014  		Backdate: timestamppb.New(now),
  2015  		Reason:   int64(revocation.KeyCompromise),
  2016  		Response: []byte{4, 5, 6},
  2017  		ShardIdx: 1,
  2018  	})
  2019  	test.AssertError(t, err, "UpdateRevokedCertificate should have failed")
  2020  	test.AssertContains(t, err.Error(), "no certificate with serial")
  2021  
  2022  	// Now revoke it, so we can update it.
  2023  	revokedTime := fc.Now()
  2024  	_, err = sa.RevokeCertificate(context.Background(), &sapb.RevokeCertificateRequest{
  2025  		IssuerID: 1,
  2026  		Serial:   serial,
  2027  		Date:     timestamppb.New(revokedTime),
  2028  		Reason:   int64(revocation.CessationOfOperation),
  2029  		Response: []byte{1, 2, 3},
  2030  		ShardIdx: 1,
  2031  	})
  2032  	test.AssertNotError(t, err, "RevokeCertificate failed")
  2033  
  2034  	// Double check that setup worked.
  2035  	status, err := sa.GetCertificateStatus(ctx, &sapb.Serial{Serial: serial})
  2036  	test.AssertNotError(t, err, "GetCertificateStatus failed")
  2037  	test.AssertEquals(t, core.OCSPStatus(status.Status), core.OCSPStatusRevoked)
  2038  	test.AssertEquals(t, revocation.Reason(status.RevokedReason), revocation.CessationOfOperation)
  2039  	fc.Add(1 * time.Hour)
  2040  
  2041  	// Try to update its revocation info with no backdate
  2042  	now = fc.Now()
  2043  	_, err = sa.UpdateRevokedCertificate(context.Background(), &sapb.RevokeCertificateRequest{
  2044  		IssuerID: 1,
  2045  		Serial:   serial,
  2046  		Date:     timestamppb.New(now),
  2047  		Reason:   int64(revocation.KeyCompromise),
  2048  		Response: []byte{4, 5, 6},
  2049  		ShardIdx: 1,
  2050  	})
  2051  	test.AssertError(t, err, "UpdateRevokedCertificate should have failed")
  2052  	test.AssertContains(t, err.Error(), "incomplete")
  2053  
  2054  	// Try to update its revocation info for a reason other than keyCompromise
  2055  	_, err = sa.UpdateRevokedCertificate(context.Background(), &sapb.RevokeCertificateRequest{
  2056  		IssuerID: 1,
  2057  		Serial:   serial,
  2058  		Date:     timestamppb.New(now),
  2059  		Backdate: timestamppb.New(revokedTime),
  2060  		Reason:   int64(revocation.Unspecified),
  2061  		Response: []byte{4, 5, 6},
  2062  		ShardIdx: 1,
  2063  	})
  2064  	test.AssertError(t, err, "UpdateRevokedCertificate should have failed")
  2065  	test.AssertContains(t, err.Error(), "cannot update revocation for any reason other than keyCompromise")
  2066  
  2067  	// Try to update the revocation info of the wrong certificate
  2068  	_, err = sa.UpdateRevokedCertificate(context.Background(), &sapb.RevokeCertificateRequest{
  2069  		IssuerID: 1,
  2070  		Serial:   "000000000000000000000000000000021bd5",
  2071  		Date:     timestamppb.New(now),
  2072  		Backdate: timestamppb.New(revokedTime),
  2073  		Reason:   int64(revocation.KeyCompromise),
  2074  		Response: []byte{4, 5, 6},
  2075  		ShardIdx: 1,
  2076  	})
  2077  	test.AssertError(t, err, "UpdateRevokedCertificate should have failed")
  2078  	test.AssertContains(t, err.Error(), "no certificate with serial")
  2079  
  2080  	// Try to update its revocation info with the wrong backdate
  2081  	_, err = sa.UpdateRevokedCertificate(context.Background(), &sapb.RevokeCertificateRequest{
  2082  		IssuerID: 1,
  2083  		Serial:   serial,
  2084  		Date:     timestamppb.New(now),
  2085  		Backdate: timestamppb.New(now),
  2086  		Reason:   int64(revocation.KeyCompromise),
  2087  		Response: []byte{4, 5, 6},
  2088  		ShardIdx: 1,
  2089  	})
  2090  	test.AssertError(t, err, "UpdateRevokedCertificate should have failed")
  2091  	test.AssertContains(t, err.Error(), "no certificate with serial")
  2092  
  2093  	// Try to update its revocation info with the wrong shard
  2094  	_, err = sa.UpdateRevokedCertificate(context.Background(), &sapb.RevokeCertificateRequest{
  2095  		IssuerID: 1,
  2096  		Serial:   serial,
  2097  		Date:     timestamppb.New(now),
  2098  		Backdate: timestamppb.New(revokedTime),
  2099  		Reason:   int64(revocation.KeyCompromise),
  2100  		Response: []byte{4, 5, 6},
  2101  		ShardIdx: 2,
  2102  	})
  2103  	test.AssertError(t, err, "UpdateRevokedCertificate should have failed")
  2104  	test.AssertContains(t, err.Error(), "mismatched shard index")
  2105  
  2106  	// Try to update its revocation info correctly
  2107  	_, err = sa.UpdateRevokedCertificate(context.Background(), &sapb.RevokeCertificateRequest{
  2108  		IssuerID: 1,
  2109  		Serial:   serial,
  2110  		Date:     timestamppb.New(now),
  2111  		Backdate: timestamppb.New(revokedTime),
  2112  		Reason:   int64(revocation.KeyCompromise),
  2113  		Response: []byte{4, 5, 6},
  2114  		ShardIdx: 1,
  2115  	})
  2116  	test.AssertNotError(t, err, "UpdateRevokedCertificate failed")
  2117  }
  2118  
  2119  func TestAddCertificateRenewalBit(t *testing.T) {
  2120  	sa, fc, cleanUp := initSA(t)
  2121  	defer cleanUp()
  2122  
  2123  	reg := createWorkingRegistration(t, sa)
  2124  
  2125  	assertIsRenewal := func(t *testing.T, issuedName string, expected bool) {
  2126  		t.Helper()
  2127  		var count int
  2128  		err := sa.dbMap.SelectOne(
  2129  			ctx,
  2130  			&count,
  2131  			`SELECT COUNT(*) FROM issuedNames
  2132  		WHERE reversedName = ?
  2133  		AND renewal = ?`,
  2134  			issuedName,
  2135  			expected,
  2136  		)
  2137  		test.AssertNotError(t, err, "Unexpected error from SelectOne on issuedNames")
  2138  		test.AssertEquals(t, count, 1)
  2139  	}
  2140  
  2141  	// Add a certificate with never-before-seen identifiers.
  2142  	_, testCert := test.ThrowAwayCert(t, fc)
  2143  	_, err := sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
  2144  		Der:          testCert.Raw,
  2145  		Issued:       timestamppb.New(testCert.NotBefore),
  2146  		RegID:        reg.Id,
  2147  		IssuerNameID: 1,
  2148  	})
  2149  	test.AssertNotError(t, err, "Failed to add precertificate")
  2150  	_, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{
  2151  		Der:    testCert.Raw,
  2152  		RegID:  reg.Id,
  2153  		Issued: timestamppb.New(testCert.NotBefore),
  2154  	})
  2155  	test.AssertNotError(t, err, "Failed to add certificate")
  2156  
  2157  	// No identifier should have an issuedNames row marking it as a renewal.
  2158  	for _, name := range testCert.DNSNames {
  2159  		assertIsRenewal(t, reverseFQDN(name), false)
  2160  	}
  2161  	for _, ip := range testCert.IPAddresses {
  2162  		assertIsRenewal(t, ip.String(), false)
  2163  	}
  2164  
  2165  	// Make a new cert and add its FQDN set to the db so it will be considered a
  2166  	// renewal
  2167  	serial, testCert := test.ThrowAwayCert(t, fc)
  2168  	err = addFQDNSet(ctx, sa.dbMap, identifier.FromCert(testCert), serial, testCert.NotBefore, testCert.NotAfter)
  2169  	test.AssertNotError(t, err, "Failed to add identifier set")
  2170  	_, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
  2171  		Der:          testCert.Raw,
  2172  		Issued:       timestamppb.New(testCert.NotBefore),
  2173  		RegID:        reg.Id,
  2174  		IssuerNameID: 1,
  2175  	})
  2176  	test.AssertNotError(t, err, "Failed to add precertificate")
  2177  	_, err = sa.AddCertificate(ctx, &sapb.AddCertificateRequest{
  2178  		Der:    testCert.Raw,
  2179  		RegID:  reg.Id,
  2180  		Issued: timestamppb.New(testCert.NotBefore),
  2181  	})
  2182  	test.AssertNotError(t, err, "Failed to add certificate")
  2183  
  2184  	// Each identifier should have an issuedNames row marking it as a renewal.
  2185  	for _, name := range testCert.DNSNames {
  2186  		assertIsRenewal(t, reverseFQDN(name), true)
  2187  	}
  2188  	for _, ip := range testCert.IPAddresses {
  2189  		assertIsRenewal(t, ip.String(), true)
  2190  	}
  2191  }
  2192  
  2193  func TestFinalizeAuthorization2(t *testing.T) {
  2194  	sa, fc, cleanUp := initSA(t)
  2195  	defer cleanUp()
  2196  
  2197  	fc.Set(mustTime("2021-01-01 00:00"))
  2198  
  2199  	reg := createWorkingRegistration(t, sa)
  2200  	authzID := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("aaa"), fc.Now().Add(time.Hour))
  2201  	expires := fc.Now().Add(time.Hour * 2).UTC()
  2202  	attemptedAt := fc.Now()
  2203  	ip, _ := netip.MustParseAddr("1.1.1.1").MarshalText()
  2204  
  2205  	_, err := sa.FinalizeAuthorization2(context.Background(), &sapb.FinalizeAuthorizationRequest{
  2206  		Id: authzID,
  2207  		ValidationRecords: []*corepb.ValidationRecord{
  2208  			{
  2209  				Hostname:      "example.com",
  2210  				Port:          "80",
  2211  				Url:           "http://example.com",
  2212  				AddressUsed:   ip,
  2213  				ResolverAddrs: []string{"resolver:5353"},
  2214  			},
  2215  		},
  2216  		Status:      string(core.StatusValid),
  2217  		Expires:     timestamppb.New(expires),
  2218  		Attempted:   string(core.ChallengeTypeHTTP01),
  2219  		AttemptedAt: timestamppb.New(attemptedAt),
  2220  	})
  2221  	test.AssertNotError(t, err, "sa.FinalizeAuthorization2 failed")
  2222  
  2223  	dbVer, err := sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID})
  2224  	test.AssertNotError(t, err, "sa.GetAuthorization2 failed")
  2225  	test.AssertEquals(t, dbVer.Status, string(core.StatusValid))
  2226  	test.AssertEquals(t, dbVer.Expires.AsTime(), expires)
  2227  	test.AssertEquals(t, dbVer.Challenges[0].Status, string(core.StatusValid))
  2228  	test.AssertEquals(t, len(dbVer.Challenges[0].Validationrecords), 1)
  2229  	test.AssertEquals(t, dbVer.Challenges[0].Validationrecords[0].Hostname, "example.com")
  2230  	test.AssertEquals(t, dbVer.Challenges[0].Validationrecords[0].Port, "80")
  2231  	test.AssertEquals(t, dbVer.Challenges[0].Validationrecords[0].ResolverAddrs[0], "resolver:5353")
  2232  	test.AssertEquals(t, dbVer.Challenges[0].Validated.AsTime(), attemptedAt)
  2233  
  2234  	authzID = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("aaa"), fc.Now().Add(time.Hour))
  2235  	prob, _ := bgrpc.ProblemDetailsToPB(probs.Connection("it went bad captain"))
  2236  
  2237  	_, err = sa.FinalizeAuthorization2(context.Background(), &sapb.FinalizeAuthorizationRequest{
  2238  		Id: authzID,
  2239  		ValidationRecords: []*corepb.ValidationRecord{
  2240  			{
  2241  				Hostname:      "example.com",
  2242  				Port:          "80",
  2243  				Url:           "http://example.com",
  2244  				AddressUsed:   ip,
  2245  				ResolverAddrs: []string{"resolver:5353"},
  2246  			},
  2247  		},
  2248  		ValidationError: prob,
  2249  		Status:          string(core.StatusInvalid),
  2250  		Attempted:       string(core.ChallengeTypeHTTP01),
  2251  		Expires:         timestamppb.New(expires),
  2252  	})
  2253  	test.AssertNotError(t, err, "sa.FinalizeAuthorization2 failed")
  2254  
  2255  	dbVer, err = sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID})
  2256  	test.AssertNotError(t, err, "sa.GetAuthorization2 failed")
  2257  	test.AssertEquals(t, dbVer.Status, string(core.StatusInvalid))
  2258  	test.AssertEquals(t, dbVer.Challenges[0].Status, string(core.StatusInvalid))
  2259  	test.AssertEquals(t, len(dbVer.Challenges[0].Validationrecords), 1)
  2260  	test.AssertEquals(t, dbVer.Challenges[0].Validationrecords[0].Hostname, "example.com")
  2261  	test.AssertEquals(t, dbVer.Challenges[0].Validationrecords[0].Port, "80")
  2262  	test.AssertEquals(t, dbVer.Challenges[0].Validationrecords[0].ResolverAddrs[0], "resolver:5353")
  2263  	test.AssertDeepEquals(t, dbVer.Challenges[0].Error, prob)
  2264  }
  2265  
  2266  func TestRehydrateHostPort(t *testing.T) {
  2267  	sa, fc, cleanUp := initSA(t)
  2268  	defer cleanUp()
  2269  
  2270  	fc.Set(mustTime("2021-01-01 00:00"))
  2271  
  2272  	reg := createWorkingRegistration(t, sa)
  2273  	expires := fc.Now().Add(time.Hour * 2).UTC()
  2274  	attemptedAt := fc.Now()
  2275  	ip, _ := netip.MustParseAddr("1.1.1.1").MarshalText()
  2276  
  2277  	// Implicit good port with good scheme
  2278  	authzID := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("aaa"), fc.Now().Add(time.Hour))
  2279  	_, err := sa.FinalizeAuthorization2(context.Background(), &sapb.FinalizeAuthorizationRequest{
  2280  		Id: authzID,
  2281  		ValidationRecords: []*corepb.ValidationRecord{
  2282  			{
  2283  				Hostname:    "example.com",
  2284  				Port:        "80",
  2285  				Url:         "http://example.com",
  2286  				AddressUsed: ip,
  2287  			},
  2288  		},
  2289  		Status:      string(core.StatusValid),
  2290  		Expires:     timestamppb.New(expires),
  2291  		Attempted:   string(core.ChallengeTypeHTTP01),
  2292  		AttemptedAt: timestamppb.New(attemptedAt),
  2293  	})
  2294  	test.AssertNotError(t, err, "sa.FinalizeAuthorization2 failed")
  2295  	_, err = sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID})
  2296  	test.AssertNotError(t, err, "rehydration failed in some fun and interesting way")
  2297  
  2298  	// Explicit good port with good scheme
  2299  	authzID = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("aaa"), fc.Now().Add(time.Hour))
  2300  	_, err = sa.FinalizeAuthorization2(context.Background(), &sapb.FinalizeAuthorizationRequest{
  2301  		Id: authzID,
  2302  		ValidationRecords: []*corepb.ValidationRecord{
  2303  			{
  2304  				Hostname:    "example.com",
  2305  				Port:        "80",
  2306  				Url:         "http://example.com:80",
  2307  				AddressUsed: ip,
  2308  			},
  2309  		},
  2310  		Status:      string(core.StatusValid),
  2311  		Expires:     timestamppb.New(expires),
  2312  		Attempted:   string(core.ChallengeTypeHTTP01),
  2313  		AttemptedAt: timestamppb.New(attemptedAt),
  2314  	})
  2315  	test.AssertNotError(t, err, "sa.FinalizeAuthorization2 failed")
  2316  	_, err = sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID})
  2317  	test.AssertNotError(t, err, "rehydration failed in some fun and interesting way")
  2318  
  2319  	// Explicit bad port with good scheme
  2320  	authzID = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("aaa"), fc.Now().Add(time.Hour))
  2321  	_, err = sa.FinalizeAuthorization2(context.Background(), &sapb.FinalizeAuthorizationRequest{
  2322  		Id: authzID,
  2323  		ValidationRecords: []*corepb.ValidationRecord{
  2324  			{
  2325  				Hostname:    "example.com",
  2326  				Port:        "444",
  2327  				Url:         "http://example.com:444",
  2328  				AddressUsed: ip,
  2329  			},
  2330  		},
  2331  		Status:      string(core.StatusValid),
  2332  		Expires:     timestamppb.New(expires),
  2333  		Attempted:   string(core.ChallengeTypeHTTP01),
  2334  		AttemptedAt: timestamppb.New(attemptedAt),
  2335  	})
  2336  	test.AssertNotError(t, err, "sa.FinalizeAuthorization2 failed")
  2337  	_, err = sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID})
  2338  	test.AssertError(t, err, "only ports 80/tcp and 443/tcp are allowed in URL \"http://example.com:444\"")
  2339  
  2340  	// Explicit bad port with bad scheme
  2341  	authzID = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("aaa"), fc.Now().Add(time.Hour))
  2342  	_, err = sa.FinalizeAuthorization2(context.Background(), &sapb.FinalizeAuthorizationRequest{
  2343  		Id: authzID,
  2344  		ValidationRecords: []*corepb.ValidationRecord{
  2345  			{
  2346  				Hostname:    "example.com",
  2347  				Port:        "80",
  2348  				Url:         "httpx://example.com",
  2349  				AddressUsed: ip,
  2350  			},
  2351  		},
  2352  		Status:      string(core.StatusValid),
  2353  		Expires:     timestamppb.New(expires),
  2354  		Attempted:   string(core.ChallengeTypeHTTP01),
  2355  		AttemptedAt: timestamppb.New(attemptedAt),
  2356  	})
  2357  	test.AssertNotError(t, err, "sa.FinalizeAuthorization2 failed")
  2358  	_, err = sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID})
  2359  	test.AssertError(t, err, "unknown scheme \"httpx\" in URL \"httpx://example.com\"")
  2360  
  2361  	// Missing URL field
  2362  	authzID = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("aaa"), fc.Now().Add(time.Hour))
  2363  	_, err = sa.FinalizeAuthorization2(context.Background(), &sapb.FinalizeAuthorizationRequest{
  2364  		Id: authzID,
  2365  		ValidationRecords: []*corepb.ValidationRecord{
  2366  			{
  2367  				Hostname:    "example.com",
  2368  				Port:        "80",
  2369  				AddressUsed: ip,
  2370  			},
  2371  		},
  2372  		Status:      string(core.StatusValid),
  2373  		Expires:     timestamppb.New(expires),
  2374  		Attempted:   string(core.ChallengeTypeHTTP01),
  2375  		AttemptedAt: timestamppb.New(attemptedAt),
  2376  	})
  2377  	test.AssertNotError(t, err, "sa.FinalizeAuthorization2 failed")
  2378  	_, err = sa.GetAuthorization2(context.Background(), &sapb.AuthorizationID2{Id: authzID})
  2379  	test.AssertError(t, err, "URL field cannot be empty")
  2380  }
  2381  
  2382  func TestCountPendingAuthorizations2(t *testing.T) {
  2383  	sa, fc, cleanUp := initSA(t)
  2384  	defer cleanUp()
  2385  
  2386  	reg := createWorkingRegistration(t, sa)
  2387  	expiresA := fc.Now().Add(time.Hour).UTC()
  2388  	expiresB := fc.Now().Add(time.Hour * 3).UTC()
  2389  	_ = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("example.com"), expiresA)
  2390  	_ = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("example.com"), expiresB)
  2391  
  2392  	// Registration has two new style pending authorizations
  2393  	regID := reg.Id
  2394  	count, err := sa.CountPendingAuthorizations2(context.Background(), &sapb.RegistrationID{
  2395  		Id: regID,
  2396  	})
  2397  	test.AssertNotError(t, err, "sa.CountPendingAuthorizations2 failed")
  2398  	test.AssertEquals(t, count.Count, int64(2))
  2399  
  2400  	// Registration has two new style pending authorizations, one of which has expired
  2401  	fc.Add(time.Hour * 2)
  2402  	count, err = sa.CountPendingAuthorizations2(context.Background(), &sapb.RegistrationID{
  2403  		Id: regID,
  2404  	})
  2405  	test.AssertNotError(t, err, "sa.CountPendingAuthorizations2 failed")
  2406  	test.AssertEquals(t, count.Count, int64(1))
  2407  
  2408  	// Registration with no authorizations should be 0
  2409  	noReg := reg.Id + 100
  2410  	count, err = sa.CountPendingAuthorizations2(context.Background(), &sapb.RegistrationID{
  2411  		Id: noReg,
  2412  	})
  2413  	test.AssertNotError(t, err, "sa.CountPendingAuthorizations2 failed")
  2414  	test.AssertEquals(t, count.Count, int64(0))
  2415  }
  2416  
  2417  func TestAuthzModelMapToPB(t *testing.T) {
  2418  	baseExpires := time.Now()
  2419  	input := map[identifier.ACMEIdentifier]authzModel{
  2420  		identifier.NewDNS("example.com"): {
  2421  			ID:              123,
  2422  			IdentifierType:  0,
  2423  			IdentifierValue: "example.com",
  2424  			RegistrationID:  77,
  2425  			Status:          1,
  2426  			Expires:         baseExpires,
  2427  			Challenges:      4,
  2428  		},
  2429  		identifier.NewDNS("www.example.com"): {
  2430  			ID:              124,
  2431  			IdentifierType:  0,
  2432  			IdentifierValue: "www.example.com",
  2433  			RegistrationID:  77,
  2434  			Status:          1,
  2435  			Expires:         baseExpires,
  2436  			Challenges:      1,
  2437  		},
  2438  		identifier.NewDNS("other.example.net"): {
  2439  			ID:              125,
  2440  			IdentifierType:  0,
  2441  			IdentifierValue: "other.example.net",
  2442  			RegistrationID:  77,
  2443  			Status:          1,
  2444  			Expires:         baseExpires,
  2445  			Challenges:      3,
  2446  		},
  2447  		identifier.NewIP(netip.MustParseAddr("10.10.10.10")): {
  2448  			ID:              126,
  2449  			IdentifierType:  1,
  2450  			IdentifierValue: "10.10.10.10",
  2451  			RegistrationID:  77,
  2452  			Status:          1,
  2453  			Expires:         baseExpires,
  2454  			Challenges:      5,
  2455  		},
  2456  	}
  2457  
  2458  	out, err := authzModelMapToPB(input)
  2459  	if err != nil {
  2460  		t.Fatal(err)
  2461  	}
  2462  
  2463  	for _, authzPB := range out.Authzs {
  2464  		model, ok := input[identifier.FromProto(authzPB.Identifier)]
  2465  		if !ok {
  2466  			t.Errorf("output had element for %q, an identifier not present in input", authzPB.Identifier.Value)
  2467  		}
  2468  		test.AssertEquals(t, authzPB.Id, fmt.Sprintf("%d", model.ID))
  2469  		test.AssertEquals(t, authzPB.Identifier.Type, string(uintToIdentifierType[model.IdentifierType]))
  2470  		test.AssertEquals(t, authzPB.Identifier.Value, model.IdentifierValue)
  2471  		test.AssertEquals(t, authzPB.RegistrationID, model.RegistrationID)
  2472  		test.AssertEquals(t, authzPB.Status, string(uintToStatus[model.Status]))
  2473  		gotTime := authzPB.Expires.AsTime()
  2474  		if !model.Expires.Equal(gotTime) {
  2475  			t.Errorf("Times didn't match. Got %s, expected %s (%s)", gotTime, model.Expires, authzPB.Expires.AsTime())
  2476  		}
  2477  		if len(authzPB.Challenges) != bits.OnesCount(uint(model.Challenges)) {
  2478  			t.Errorf("wrong number of challenges for %q: got %d, expected %d", authzPB.Identifier.Value,
  2479  				len(authzPB.Challenges), bits.OnesCount(uint(model.Challenges)))
  2480  		}
  2481  		switch model.Challenges {
  2482  		case 1:
  2483  			test.AssertEquals(t, authzPB.Challenges[0].Type, "http-01")
  2484  		case 3:
  2485  			test.AssertEquals(t, authzPB.Challenges[0].Type, "http-01")
  2486  			test.AssertEquals(t, authzPB.Challenges[1].Type, "dns-01")
  2487  		case 4:
  2488  			test.AssertEquals(t, authzPB.Challenges[0].Type, "tls-alpn-01")
  2489  		}
  2490  
  2491  		delete(input, identifier.FromProto(authzPB.Identifier))
  2492  	}
  2493  
  2494  	for k := range input {
  2495  		t.Errorf("hostname %q was not present in output", k)
  2496  	}
  2497  }
  2498  
  2499  func TestGetValidOrderAuthorizations2(t *testing.T) {
  2500  	for _, val := range []bool{true, false} {
  2501  		features.Set(features.Config{
  2502  			StoreAuthzsInOrders: val,
  2503  		})
  2504  		if features.Get().StoreAuthzsInOrders && os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
  2505  			t.Skip("need DB migration to test StoreAuthzsInOrders=true")
  2506  		}
  2507  		sa, fc, cleanup := initSA(t)
  2508  		defer cleanup()
  2509  
  2510  		// Create three new valid authorizations
  2511  		reg := createWorkingRegistration(t, sa)
  2512  		identA := identifier.NewDNS("a.example.com")
  2513  		identB := identifier.NewDNS("b.example.com")
  2514  		identC := identifier.NewIP(netip.MustParseAddr("3fff:aaa:aaaa:aaaa:abad:0ff1:cec0:ffee"))
  2515  		expires := fc.Now().Add(time.Hour * 24 * 7).UTC()
  2516  		attemptedAt := fc.Now()
  2517  
  2518  		authzIDA := createFinalizedAuthorization(t, sa, reg.Id, identA, expires, "valid", attemptedAt)
  2519  		authzIDB := createFinalizedAuthorization(t, sa, reg.Id, identB, expires, "valid", attemptedAt)
  2520  		authzIDC := createFinalizedAuthorization(t, sa, reg.Id, identC, expires, "valid", attemptedAt)
  2521  
  2522  		orderExpr := fc.Now().Truncate(time.Second)
  2523  		order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{
  2524  			NewOrder: &sapb.NewOrderRequest{
  2525  				RegistrationID: reg.Id,
  2526  				Expires:        timestamppb.New(orderExpr),
  2527  				Identifiers: []*corepb.Identifier{
  2528  					identifier.NewDNS("a.example.com").ToProto(),
  2529  					identifier.NewDNS("b.example.com").ToProto(),
  2530  					identifier.NewIP(netip.MustParseAddr("3fff:aaa:aaaa:aaaa:abad:0ff1:cec0:ffee")).ToProto(),
  2531  				},
  2532  				V2Authorizations: []int64{authzIDA, authzIDB, authzIDC},
  2533  			},
  2534  		})
  2535  		test.AssertNotError(t, err, "AddOrder failed")
  2536  
  2537  		authzPBs, err := sa.GetValidOrderAuthorizations2(
  2538  			context.Background(),
  2539  			&sapb.GetValidOrderAuthorizationsRequest{
  2540  				Id:     order.Id,
  2541  				AcctID: reg.Id,
  2542  			})
  2543  		test.AssertNotError(t, err, "sa.GetValidOrderAuthorizations failed")
  2544  		test.AssertNotNil(t, authzPBs, "sa.GetValidOrderAuthorizations result was nil")
  2545  		test.AssertEquals(t, len(authzPBs.Authzs), 3)
  2546  
  2547  		identsToCheck := map[identifier.ACMEIdentifier]int64{
  2548  			identifier.NewDNS("a.example.com"):                                              authzIDA,
  2549  			identifier.NewDNS("b.example.com"):                                              authzIDB,
  2550  			identifier.NewIP(netip.MustParseAddr("3fff:aaa:aaaa:aaaa:abad:0ff1:cec0:ffee")): authzIDC,
  2551  		}
  2552  		for _, a := range authzPBs.Authzs {
  2553  			ident := identifier.ACMEIdentifier{Type: identifier.IdentifierType(a.Identifier.Type), Value: a.Identifier.Value}
  2554  			if fmt.Sprintf("%d", identsToCheck[ident]) != a.Id {
  2555  				t.Fatalf("incorrect identifier %q with id %s", a.Identifier.Value, a.Id)
  2556  			}
  2557  			test.AssertEquals(t, a.Expires.AsTime(), expires)
  2558  			delete(identsToCheck, ident)
  2559  		}
  2560  
  2561  		// Getting the order authorizations for an order that doesn't exist should return nothing
  2562  		missingID := int64(0xC0FFEEEEEEE)
  2563  		authzPBs, err = sa.GetValidOrderAuthorizations2(
  2564  			context.Background(),
  2565  			&sapb.GetValidOrderAuthorizationsRequest{
  2566  				Id:     missingID,
  2567  				AcctID: reg.Id,
  2568  			})
  2569  		test.AssertNotError(t, err, "sa.GetValidOrderAuthorizations failed")
  2570  		test.AssertEquals(t, len(authzPBs.Authzs), 0)
  2571  	}
  2572  }
  2573  
  2574  func TestCountInvalidAuthorizations2(t *testing.T) {
  2575  	sa, fc, cleanUp := initSA(t)
  2576  	defer cleanUp()
  2577  
  2578  	fc.Add(time.Hour)
  2579  	reg := createWorkingRegistration(t, sa)
  2580  	idents := identifier.ACMEIdentifiers{
  2581  		identifier.NewDNS("aaa"),
  2582  		identifier.NewIP(netip.MustParseAddr("10.10.10.10")),
  2583  	}
  2584  	for _, ident := range idents {
  2585  		// Create two authorizations, one pending, one invalid
  2586  		expiresA := fc.Now().Add(time.Hour).UTC()
  2587  		expiresB := fc.Now().Add(time.Hour * 3).UTC()
  2588  		attemptedAt := fc.Now()
  2589  		_ = createFinalizedAuthorization(t, sa, reg.Id, ident, expiresA, "invalid", attemptedAt)
  2590  		_ = createPendingAuthorization(t, sa, reg.Id, ident, expiresB)
  2591  
  2592  		earliest := fc.Now().Add(-time.Hour).UTC()
  2593  		latest := fc.Now().Add(time.Hour * 5).UTC()
  2594  		count, err := sa.CountInvalidAuthorizations2(context.Background(), &sapb.CountInvalidAuthorizationsRequest{
  2595  			RegistrationID: reg.Id,
  2596  			Identifier:     ident.ToProto(),
  2597  			Range: &sapb.Range{
  2598  				Earliest: timestamppb.New(earliest),
  2599  				Latest:   timestamppb.New(latest),
  2600  			},
  2601  		})
  2602  		test.AssertNotError(t, err, "sa.CountInvalidAuthorizations2 failed")
  2603  		test.AssertEquals(t, count.Count, int64(1))
  2604  	}
  2605  }
  2606  
  2607  func TestGetValidAuthorizations2(t *testing.T) {
  2608  	sa, fc, cleanUp := initSA(t)
  2609  	defer cleanUp()
  2610  
  2611  	var aaa int64
  2612  	{
  2613  		tokenStr := core.NewToken()
  2614  		token, err := base64.RawURLEncoding.DecodeString(tokenStr)
  2615  		test.AssertNotError(t, err, "computing test authorization challenge token")
  2616  
  2617  		profile := "test"
  2618  		attempted := challTypeToUint[string(core.ChallengeTypeHTTP01)]
  2619  		attemptedAt := fc.Now()
  2620  		vr, _ := json.Marshal([]core.ValidationRecord{})
  2621  
  2622  		am := authzModel{
  2623  			IdentifierType:         identifierTypeToUint[string(identifier.TypeDNS)],
  2624  			IdentifierValue:        "aaa",
  2625  			RegistrationID:         1,
  2626  			CertificateProfileName: &profile,
  2627  			Status:                 statusToUint[core.StatusValid],
  2628  			Expires:                fc.Now().Add(24 * time.Hour),
  2629  			Challenges:             1 << challTypeToUint[string(core.ChallengeTypeHTTP01)],
  2630  			Attempted:              &attempted,
  2631  			AttemptedAt:            &attemptedAt,
  2632  			Token:                  token,
  2633  			ValidationError:        nil,
  2634  			ValidationRecord:       vr,
  2635  		}
  2636  
  2637  		err = sa.dbMap.Insert(context.Background(), &am)
  2638  		test.AssertNotError(t, err, "failed to insert valid authz")
  2639  
  2640  		aaa = am.ID
  2641  	}
  2642  
  2643  	var dac int64
  2644  	{
  2645  		tokenStr := core.NewToken()
  2646  		token, err := base64.RawURLEncoding.DecodeString(tokenStr)
  2647  		test.AssertNotError(t, err, "computing test authorization challenge token")
  2648  
  2649  		profile := "test"
  2650  		attempted := challTypeToUint[string(core.ChallengeTypeDNSAccount01)]
  2651  		attemptedAt := fc.Now()
  2652  		vr, _ := json.Marshal([]core.ValidationRecord{})
  2653  
  2654  		am := authzModel{
  2655  			IdentifierType:         identifierTypeToUint[string(identifier.TypeDNS)],
  2656  			IdentifierValue:        "aaa",
  2657  			RegistrationID:         3,
  2658  			CertificateProfileName: &profile,
  2659  			Status:                 statusToUint[core.StatusValid],
  2660  			Expires:                fc.Now().Add(24 * time.Hour),
  2661  			Challenges:             1 << challTypeToUint[string(core.ChallengeTypeDNSAccount01)],
  2662  			Attempted:              &attempted,
  2663  			AttemptedAt:            &attemptedAt,
  2664  			Token:                  token,
  2665  			ValidationError:        nil,
  2666  			ValidationRecord:       vr,
  2667  		}
  2668  		err = sa.dbMap.Insert(context.Background(), &am)
  2669  		test.AssertNotError(t, err, "failed to insert valid authz with dns-account-01")
  2670  		dac = am.ID
  2671  	}
  2672  
  2673  	for _, tc := range []struct {
  2674  		name        string
  2675  		regID       int64
  2676  		identifiers []*corepb.Identifier
  2677  		profile     string
  2678  		validUntil  time.Time
  2679  		wantIDs     []int64
  2680  	}{
  2681  		{
  2682  			name:        "happy path, DNS identifier",
  2683  			regID:       1,
  2684  			identifiers: []*corepb.Identifier{identifier.NewDNS("aaa").ToProto()},
  2685  			profile:     "test",
  2686  			validUntil:  fc.Now().Add(time.Hour),
  2687  			wantIDs:     []int64{aaa},
  2688  		},
  2689  		{
  2690  			name:        "happy path, dns-account-01 challenge",
  2691  			regID:       3,
  2692  			identifiers: []*corepb.Identifier{identifier.NewDNS("aaa").ToProto()},
  2693  			profile:     "test",
  2694  			validUntil:  fc.Now().Add(time.Hour),
  2695  			wantIDs:     []int64{dac},
  2696  		},
  2697  		{
  2698  			name:        "different identifier type",
  2699  			regID:       1,
  2700  			identifiers: []*corepb.Identifier{identifier.NewIP(netip.MustParseAddr("10.10.10.10")).ToProto()},
  2701  			profile:     "test",
  2702  			validUntil:  fc.Now().Add(time.Hour),
  2703  			wantIDs:     []int64{},
  2704  		},
  2705  		{
  2706  			name:        "different regID",
  2707  			regID:       2,
  2708  			identifiers: []*corepb.Identifier{identifier.NewDNS("aaa").ToProto()},
  2709  			profile:     "test",
  2710  			validUntil:  fc.Now().Add(time.Hour),
  2711  			wantIDs:     []int64{},
  2712  		},
  2713  		{
  2714  			name:        "different DNS identifier",
  2715  			regID:       1,
  2716  			identifiers: []*corepb.Identifier{identifier.NewDNS("bbb").ToProto()},
  2717  			profile:     "test",
  2718  			validUntil:  fc.Now().Add(time.Hour),
  2719  			wantIDs:     []int64{},
  2720  		},
  2721  		{
  2722  			name:        "different profile",
  2723  			regID:       1,
  2724  			identifiers: []*corepb.Identifier{identifier.NewDNS("aaa").ToProto()},
  2725  			profile:     "other",
  2726  			validUntil:  fc.Now().Add(time.Hour),
  2727  			wantIDs:     []int64{},
  2728  		},
  2729  		{
  2730  			name:        "too-far-out validUntil",
  2731  			regID:       2,
  2732  			identifiers: []*corepb.Identifier{identifier.NewDNS("aaa").ToProto()},
  2733  			profile:     "test",
  2734  			validUntil:  fc.Now().Add(25 * time.Hour),
  2735  			wantIDs:     []int64{},
  2736  		},
  2737  	} {
  2738  		t.Run(tc.name, func(t *testing.T) {
  2739  			got, err := sa.GetValidAuthorizations2(context.Background(), &sapb.GetValidAuthorizationsRequest{
  2740  				RegistrationID: tc.regID,
  2741  				Identifiers:    tc.identifiers,
  2742  				Profile:        tc.profile,
  2743  				ValidUntil:     timestamppb.New(tc.validUntil),
  2744  			})
  2745  			if err != nil {
  2746  				t.Fatalf("GetValidAuthorizations2 got error %q, want success", err)
  2747  			}
  2748  
  2749  			var gotIDs []int64
  2750  			for _, authz := range got.Authzs {
  2751  				id, err := strconv.Atoi(authz.Id)
  2752  				if err != nil {
  2753  					t.Fatalf("parsing authz id: %s", err)
  2754  				}
  2755  				gotIDs = append(gotIDs, int64(id))
  2756  			}
  2757  
  2758  			slices.Sort(gotIDs)
  2759  			slices.Sort(tc.wantIDs)
  2760  			if !slices.Equal(gotIDs, tc.wantIDs) {
  2761  				t.Errorf("GetValidAuthorizations2() = %+v, want %+v", gotIDs, tc.wantIDs)
  2762  			}
  2763  		})
  2764  	}
  2765  }
  2766  
  2767  func TestGetOrderExpired(t *testing.T) {
  2768  	for _, storeAuthzsInOrders := range []bool{true, false} {
  2769  		if storeAuthzsInOrders && os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
  2770  			t.Skip("need DB migration to test StoreAuthzsInOrders=true")
  2771  		}
  2772  		features.Set(features.Config{
  2773  			StoreAuthzsInOrders: storeAuthzsInOrders,
  2774  		})
  2775  		sa, fc, cleanUp := initSA(t)
  2776  		defer cleanUp()
  2777  		fc.Add(time.Hour * 5)
  2778  		now := fc.Now()
  2779  		reg := createWorkingRegistration(t, sa)
  2780  		order, err := sa.NewOrderAndAuthzs(context.Background(), &sapb.NewOrderAndAuthzsRequest{
  2781  			NewOrder: &sapb.NewOrderRequest{
  2782  				RegistrationID:   reg.Id,
  2783  				Expires:          timestamppb.New(now.Add(-time.Hour)),
  2784  				Identifiers:      []*corepb.Identifier{identifier.NewDNS("example.com").ToProto()},
  2785  				V2Authorizations: []int64{666},
  2786  			},
  2787  		})
  2788  		test.AssertNotError(t, err, "NewOrderAndAuthzs failed")
  2789  		_, err = sa.GetOrder(context.Background(), &sapb.OrderRequest{
  2790  			Id: order.Id,
  2791  		})
  2792  		test.AssertError(t, err, "GetOrder didn't fail for an expired order")
  2793  		test.AssertErrorIs(t, err, berrors.NotFound)
  2794  	}
  2795  }
  2796  
  2797  func TestBlockedKey(t *testing.T) {
  2798  	sa, _, cleanUp := initSA(t)
  2799  	defer cleanUp()
  2800  
  2801  	hashA := make([]byte, 32)
  2802  	hashA[0] = 1
  2803  	hashB := make([]byte, 32)
  2804  	hashB[0] = 2
  2805  
  2806  	added := time.Now()
  2807  	source := "API"
  2808  	_, err := sa.AddBlockedKey(context.Background(), &sapb.AddBlockedKeyRequest{
  2809  		KeyHash: hashA,
  2810  		Added:   timestamppb.New(added),
  2811  		Source:  source,
  2812  	})
  2813  	test.AssertNotError(t, err, "AddBlockedKey failed")
  2814  	_, err = sa.AddBlockedKey(context.Background(), &sapb.AddBlockedKeyRequest{
  2815  		KeyHash: hashA,
  2816  		Added:   timestamppb.New(added),
  2817  		Source:  source,
  2818  	})
  2819  	test.AssertNotError(t, err, "AddBlockedKey failed with duplicate insert")
  2820  
  2821  	comment := "testing comments"
  2822  	_, err = sa.AddBlockedKey(context.Background(), &sapb.AddBlockedKeyRequest{
  2823  		KeyHash: hashB,
  2824  		Added:   timestamppb.New(added),
  2825  		Source:  source,
  2826  		Comment: comment,
  2827  	})
  2828  	test.AssertNotError(t, err, "AddBlockedKey failed")
  2829  
  2830  	exists, err := sa.KeyBlocked(context.Background(), &sapb.SPKIHash{
  2831  		KeyHash: hashA,
  2832  	})
  2833  	test.AssertNotError(t, err, "KeyBlocked failed")
  2834  	test.Assert(t, exists != nil, "*sapb.Exists is nil")
  2835  	test.Assert(t, exists.Exists, "KeyBlocked returned false for blocked key")
  2836  	exists, err = sa.KeyBlocked(context.Background(), &sapb.SPKIHash{
  2837  		KeyHash: hashB,
  2838  	})
  2839  	test.AssertNotError(t, err, "KeyBlocked failed")
  2840  	test.Assert(t, exists != nil, "*sapb.Exists is nil")
  2841  	test.Assert(t, exists.Exists, "KeyBlocked returned false for blocked key")
  2842  	exists, err = sa.KeyBlocked(context.Background(), &sapb.SPKIHash{
  2843  		KeyHash: []byte{5},
  2844  	})
  2845  	test.AssertNotError(t, err, "KeyBlocked failed")
  2846  	test.Assert(t, exists != nil, "*sapb.Exists is nil")
  2847  	test.Assert(t, !exists.Exists, "KeyBlocked returned true for non-blocked key")
  2848  }
  2849  
  2850  func TestAddBlockedKeyUnknownSource(t *testing.T) {
  2851  	sa, fc, cleanUp := initSA(t)
  2852  	defer cleanUp()
  2853  
  2854  	_, err := sa.AddBlockedKey(context.Background(), &sapb.AddBlockedKeyRequest{
  2855  		KeyHash: []byte{1, 2, 3},
  2856  		Added:   timestamppb.New(fc.Now()),
  2857  		Source:  "heyo",
  2858  	})
  2859  	test.AssertError(t, err, "AddBlockedKey didn't fail with unknown source")
  2860  	test.AssertEquals(t, err.Error(), "unknown source")
  2861  }
  2862  
  2863  func TestBlockedKeyRevokedBy(t *testing.T) {
  2864  	sa, fc, cleanUp := initSA(t)
  2865  	defer cleanUp()
  2866  
  2867  	now := fc.Now()
  2868  	_, err := sa.AddBlockedKey(context.Background(), &sapb.AddBlockedKeyRequest{
  2869  		KeyHash: []byte{1},
  2870  		Added:   timestamppb.New(now),
  2871  		Source:  "API",
  2872  	})
  2873  	test.AssertNotError(t, err, "AddBlockedKey failed")
  2874  
  2875  	_, err = sa.AddBlockedKey(context.Background(), &sapb.AddBlockedKeyRequest{
  2876  		KeyHash:   []byte{2},
  2877  		Added:     timestamppb.New(now),
  2878  		Source:    "API",
  2879  		RevokedBy: 1,
  2880  	})
  2881  	test.AssertNotError(t, err, "AddBlockedKey failed")
  2882  }
  2883  
  2884  func TestIncidentsForSerial(t *testing.T) {
  2885  	sa, _, cleanUp := initSA(t)
  2886  	defer cleanUp()
  2887  
  2888  	testSADbMap, err := DBMapForTest(vars.DBConnSAFullPerms)
  2889  	test.AssertNotError(t, err, "Couldn't create test dbMap")
  2890  
  2891  	testIncidentsDbMap, err := DBMapForTest(vars.DBConnIncidentsFullPerms)
  2892  	test.AssertNotError(t, err, "Couldn't create test dbMap")
  2893  	defer test.ResetIncidentsTestDatabase(t)
  2894  
  2895  	weekAgo := sa.clk.Now().Add(-time.Hour * 24 * 7)
  2896  
  2897  	// Add a disabled incident.
  2898  	err = testSADbMap.Insert(ctx, &incidentModel{
  2899  		SerialTable: "incident_foo",
  2900  		URL:         "https://example.com/foo-incident",
  2901  		RenewBy:     sa.clk.Now().Add(time.Hour * 24 * 7),
  2902  		Enabled:     false,
  2903  	})
  2904  	test.AssertNotError(t, err, "Failed to insert disabled incident")
  2905  
  2906  	// No incidents are enabled, so this should return in error.
  2907  	result, err := sa.IncidentsForSerial(context.Background(), &sapb.Serial{Serial: "1337"})
  2908  	test.AssertNotError(t, err, "fetching from no incidents")
  2909  	test.AssertEquals(t, len(result.Incidents), 0)
  2910  
  2911  	// Add an enabled incident.
  2912  	err = testSADbMap.Insert(ctx, &incidentModel{
  2913  		SerialTable: "incident_bar",
  2914  		URL:         "https://example.com/test-incident",
  2915  		RenewBy:     sa.clk.Now().Add(time.Hour * 24 * 7),
  2916  		Enabled:     true,
  2917  	})
  2918  	test.AssertNotError(t, err, "Failed to insert enabled incident")
  2919  
  2920  	// Add a row to the incident table with serial '1338'.
  2921  	one := int64(1)
  2922  	affectedCertA := incidentSerialModel{
  2923  		Serial:         "1338",
  2924  		RegistrationID: &one,
  2925  		OrderID:        &one,
  2926  		LastNoticeSent: &weekAgo,
  2927  	}
  2928  	_, err = testIncidentsDbMap.ExecContext(ctx,
  2929  		fmt.Sprintf("INSERT INTO incident_bar (%s) VALUES ('%s', %d, %d, '%s')",
  2930  			"serial, registrationID, orderID, lastNoticeSent",
  2931  			affectedCertA.Serial,
  2932  			affectedCertA.RegistrationID,
  2933  			affectedCertA.OrderID,
  2934  			affectedCertA.LastNoticeSent.Format(time.DateTime),
  2935  		),
  2936  	)
  2937  	test.AssertNotError(t, err, "Error while inserting row for '1338' into incident table")
  2938  
  2939  	// The incident table should not contain a row with serial '1337'.
  2940  	result, err = sa.IncidentsForSerial(context.Background(), &sapb.Serial{Serial: "1337"})
  2941  	test.AssertNotError(t, err, "fetching from one incident")
  2942  	test.AssertEquals(t, len(result.Incidents), 0)
  2943  
  2944  	// Add a row to the incident table with serial '1337'.
  2945  	two := int64(2)
  2946  	affectedCertB := incidentSerialModel{
  2947  		Serial:         "1337",
  2948  		RegistrationID: &two,
  2949  		OrderID:        &two,
  2950  		LastNoticeSent: &weekAgo,
  2951  	}
  2952  	_, err = testIncidentsDbMap.ExecContext(ctx,
  2953  		fmt.Sprintf("INSERT INTO incident_bar (%s) VALUES ('%s', %d, %d, '%s')",
  2954  			"serial, registrationID, orderID, lastNoticeSent",
  2955  			affectedCertB.Serial,
  2956  			affectedCertB.RegistrationID,
  2957  			affectedCertB.OrderID,
  2958  			affectedCertB.LastNoticeSent.Format(time.DateTime),
  2959  		),
  2960  	)
  2961  	test.AssertNotError(t, err, "Error while inserting row for '1337' into incident table")
  2962  
  2963  	// The incident table should now contain a row with serial '1337'.
  2964  	result, err = sa.IncidentsForSerial(context.Background(), &sapb.Serial{Serial: "1337"})
  2965  	test.AssertNotError(t, err, "Failed to retrieve incidents for serial")
  2966  	test.AssertEquals(t, len(result.Incidents), 1)
  2967  }
  2968  
  2969  func TestSerialsForIncident(t *testing.T) {
  2970  	sa, _, cleanUp := initSA(t)
  2971  	defer cleanUp()
  2972  
  2973  	testIncidentsDbMap, err := DBMapForTest(vars.DBConnIncidentsFullPerms)
  2974  	test.AssertNotError(t, err, "Couldn't create test dbMap")
  2975  	defer test.ResetIncidentsTestDatabase(t)
  2976  
  2977  	// Request serials from a malformed incident table name.
  2978  	mockServerStream := &fakeServerStream[sapb.IncidentSerial]{}
  2979  	err = sa.SerialsForIncident(
  2980  		&sapb.SerialsForIncidentRequest{
  2981  			IncidentTable: "incidesnt_Baz",
  2982  		},
  2983  		mockServerStream,
  2984  	)
  2985  	test.AssertError(t, err, "Expected error for malformed table name")
  2986  	test.AssertContains(t, err.Error(), "malformed table name \"incidesnt_Baz\"")
  2987  
  2988  	// Request serials from another malformed incident table name.
  2989  	mockServerStream = &fakeServerStream[sapb.IncidentSerial]{}
  2990  	longTableName := "incident_l" + strings.Repeat("o", 1000) + "ng"
  2991  	err = sa.SerialsForIncident(
  2992  		&sapb.SerialsForIncidentRequest{
  2993  			IncidentTable: longTableName,
  2994  		},
  2995  		mockServerStream,
  2996  	)
  2997  	test.AssertError(t, err, "Expected error for long table name")
  2998  	test.AssertContains(t, err.Error(), fmt.Sprintf("malformed table name %q", longTableName))
  2999  
  3000  	// Request serials for an incident table which doesn't exists.
  3001  	mockServerStream = &fakeServerStream[sapb.IncidentSerial]{}
  3002  	err = sa.SerialsForIncident(
  3003  		&sapb.SerialsForIncidentRequest{
  3004  			IncidentTable: "incident_baz",
  3005  		},
  3006  		mockServerStream,
  3007  	)
  3008  	test.AssertError(t, err, "Expected error for nonexistent table name")
  3009  
  3010  	// Assert that the error is a MySQL error so we can inspect the error code.
  3011  	var mysqlErr *mysql.MySQLError
  3012  	if errors.As(err, &mysqlErr) {
  3013  		// We expect the error code to be 1146 (ER_NO_SUCH_TABLE):
  3014  		// https://mariadb.com/kb/en/mariadb-error-codes/
  3015  		test.AssertEquals(t, mysqlErr.Number, uint16(1146))
  3016  	} else {
  3017  		t.Fatalf("Expected MySQL Error 1146 (ER_NO_SUCH_TABLE) from Recv(), got %q", err)
  3018  	}
  3019  
  3020  	// Request serials from table 'incident_foo', which we expect to exist but
  3021  	// be empty.
  3022  	stream := make(chan *sapb.IncidentSerial)
  3023  	mockServerStream = &fakeServerStream[sapb.IncidentSerial]{output: stream}
  3024  	go func() {
  3025  		err = sa.SerialsForIncident(
  3026  			&sapb.SerialsForIncidentRequest{
  3027  				IncidentTable: "incident_foo",
  3028  			},
  3029  			mockServerStream,
  3030  		)
  3031  		close(stream) // Let our main test thread continue.
  3032  	}()
  3033  	for range stream {
  3034  		t.Fatal("No serials should have been written to this stream")
  3035  	}
  3036  	test.AssertNotError(t, err, "Error calling SerialsForIncident on empty table")
  3037  
  3038  	// Add 4 rows of incident serials to 'incident_foo'.
  3039  	expectedSerials := map[string]bool{
  3040  		"1335": true, "1336": true, "1337": true, "1338": true,
  3041  	}
  3042  	for i := range expectedSerials {
  3043  		randInt := func() int64 { return mrand.Int64() }
  3044  		_, err := testIncidentsDbMap.ExecContext(ctx,
  3045  			fmt.Sprintf("INSERT INTO incident_foo (%s) VALUES ('%s', %d, %d, '%s')",
  3046  				"serial, registrationID, orderID, lastNoticeSent",
  3047  				i,
  3048  				randInt(),
  3049  				randInt(),
  3050  				sa.clk.Now().Add(time.Hour*24*7).Format(time.DateTime),
  3051  			),
  3052  		)
  3053  		test.AssertNotError(t, err, fmt.Sprintf("Error while inserting row for '%s' into incident table", i))
  3054  	}
  3055  
  3056  	// Request all 4 serials from the incident table we just added entries to.
  3057  	stream = make(chan *sapb.IncidentSerial)
  3058  	mockServerStream = &fakeServerStream[sapb.IncidentSerial]{output: stream}
  3059  	go func() {
  3060  		err = sa.SerialsForIncident(
  3061  			&sapb.SerialsForIncidentRequest{
  3062  				IncidentTable: "incident_foo",
  3063  			},
  3064  			mockServerStream,
  3065  		)
  3066  		close(stream)
  3067  	}()
  3068  	receivedSerials := make(map[string]bool)
  3069  	for serial := range stream {
  3070  		if len(receivedSerials) > 4 {
  3071  			t.Fatal("Received too many serials")
  3072  		}
  3073  		if _, ok := receivedSerials[serial.Serial]; ok {
  3074  			t.Fatalf("Received serial %q more than once", serial.Serial)
  3075  		}
  3076  		receivedSerials[serial.Serial] = true
  3077  	}
  3078  	test.AssertDeepEquals(t, receivedSerials, map[string]bool{
  3079  		"1335": true, "1336": true, "1337": true, "1338": true,
  3080  	})
  3081  	test.AssertNotError(t, err, "Error getting serials for incident")
  3082  }
  3083  
  3084  func TestGetRevokedCertsByShard(t *testing.T) {
  3085  	sa, _, cleanUp := initSA(t)
  3086  	defer cleanUp()
  3087  
  3088  	// Add a cert to the DB to test with. We use AddPrecertificate because it sets
  3089  	// up the certificateStatus row we need. This particular cert has a notAfter
  3090  	// date of Mar 6 2023, and we lie about its IssuerNameID to make things easy.
  3091  	reg := createWorkingRegistration(t, sa)
  3092  	eeCert, err := core.LoadCert("../test/hierarchy/ee-e1.cert.pem")
  3093  	test.AssertNotError(t, err, "failed to load test cert")
  3094  	_, err = sa.AddSerial(ctx, &sapb.AddSerialRequest{
  3095  		RegID:   reg.Id,
  3096  		Serial:  core.SerialToString(eeCert.SerialNumber),
  3097  		Created: timestamppb.New(eeCert.NotBefore),
  3098  		Expires: timestamppb.New(eeCert.NotAfter),
  3099  	})
  3100  	test.AssertNotError(t, err, "failed to add test serial")
  3101  	_, err = sa.AddPrecertificate(ctx, &sapb.AddCertificateRequest{
  3102  		Der:          eeCert.Raw,
  3103  		RegID:        reg.Id,
  3104  		Issued:       timestamppb.New(eeCert.NotBefore),
  3105  		IssuerNameID: 1,
  3106  	})
  3107  	test.AssertNotError(t, err, "failed to add test cert")
  3108  
  3109  	// Check that it worked.
  3110  	status, err := sa.GetCertificateStatus(
  3111  		ctx, &sapb.Serial{Serial: core.SerialToString(eeCert.SerialNumber)})
  3112  	test.AssertNotError(t, err, "GetCertificateStatus failed")
  3113  	test.AssertEquals(t, core.OCSPStatus(status.Status), core.OCSPStatusGood)
  3114  
  3115  	// Here's a little helper func we'll use to call GetRevokedCertsByShard and count
  3116  	// how many results it returned.
  3117  	countRevokedCerts := func(req *sapb.GetRevokedCertsByShardRequest) (int, error) {
  3118  		stream := make(chan *corepb.CRLEntry)
  3119  		mockServerStream := &fakeServerStream[corepb.CRLEntry]{output: stream}
  3120  		var err error
  3121  		go func() {
  3122  			err = sa.GetRevokedCertsByShard(req, mockServerStream)
  3123  			close(stream)
  3124  		}()
  3125  		entriesReceived := 0
  3126  		for range stream {
  3127  			entriesReceived++
  3128  		}
  3129  		return entriesReceived, err
  3130  	}
  3131  
  3132  	// The basic request covers a time range and shard that should include this certificate.
  3133  	basicRequest := &sapb.GetRevokedCertsByShardRequest{
  3134  		IssuerNameID:  1,
  3135  		ShardIdx:      9,
  3136  		ExpiresAfter:  mustTimestamp("2023-03-01 00:00"),
  3137  		RevokedBefore: mustTimestamp("2023-04-01 00:00"),
  3138  	}
  3139  
  3140  	// Nothing's been revoked yet. Count should be zero.
  3141  	count, err := countRevokedCerts(basicRequest)
  3142  	test.AssertNotError(t, err, "zero rows shouldn't result in error")
  3143  	test.AssertEquals(t, count, 0)
  3144  
  3145  	// Revoke the certificate, providing the ShardIdx so it gets written into
  3146  	// both the certificateStatus and revokedCertificates tables.
  3147  	_, err = sa.RevokeCertificate(context.Background(), &sapb.RevokeCertificateRequest{
  3148  		IssuerID: 1,
  3149  		Serial:   core.SerialToString(eeCert.SerialNumber),
  3150  		Date:     mustTimestamp("2023-01-01 00:00"),
  3151  		Reason:   1,
  3152  		Response: []byte{1, 2, 3},
  3153  		ShardIdx: 9,
  3154  	})
  3155  	test.AssertNotError(t, err, "failed to revoke test cert")
  3156  
  3157  	// Check that it worked in the most basic way.
  3158  	c, err := sa.dbMap.SelectNullInt(
  3159  		ctx, "SELECT count(*) FROM revokedCertificates")
  3160  	test.AssertNotError(t, err, "SELECT from revokedCertificates failed")
  3161  	test.Assert(t, c.Valid, "SELECT from revokedCertificates got no result")
  3162  	test.AssertEquals(t, c.Int64, int64(1))
  3163  
  3164  	// Asking for revoked certs now should return one result.
  3165  	count, err = countRevokedCerts(basicRequest)
  3166  	test.AssertNotError(t, err, "normal usage shouldn't result in error")
  3167  	test.AssertEquals(t, count, 1)
  3168  
  3169  	// Asking for revoked certs from a different issuer should return zero results.
  3170  	count, err = countRevokedCerts(&sapb.GetRevokedCertsByShardRequest{
  3171  		IssuerNameID:  5678,
  3172  		ShardIdx:      basicRequest.ShardIdx,
  3173  		ExpiresAfter:  basicRequest.ExpiresAfter,
  3174  		RevokedBefore: basicRequest.RevokedBefore,
  3175  	})
  3176  	test.AssertNotError(t, err, "zero rows shouldn't result in error")
  3177  	test.AssertEquals(t, count, 0)
  3178  
  3179  	// Asking for revoked certs from a different shard should return zero results.
  3180  	count, err = countRevokedCerts(&sapb.GetRevokedCertsByShardRequest{
  3181  		IssuerNameID:  basicRequest.IssuerNameID,
  3182  		ShardIdx:      8,
  3183  		ExpiresAfter:  basicRequest.ExpiresAfter,
  3184  		RevokedBefore: basicRequest.RevokedBefore,
  3185  	})
  3186  	test.AssertNotError(t, err, "zero rows shouldn't result in error")
  3187  	test.AssertEquals(t, count, 0)
  3188  
  3189  	// Asking for revoked certs with an old RevokedBefore should return no results.
  3190  	count, err = countRevokedCerts(&sapb.GetRevokedCertsByShardRequest{
  3191  		IssuerNameID:  basicRequest.IssuerNameID,
  3192  		ShardIdx:      basicRequest.ShardIdx,
  3193  		ExpiresAfter:  basicRequest.ExpiresAfter,
  3194  		RevokedBefore: mustTimestamp("2020-03-01 00:00"),
  3195  	})
  3196  	test.AssertNotError(t, err, "zero rows shouldn't result in error")
  3197  	test.AssertEquals(t, count, 0)
  3198  }
  3199  
  3200  func TestLeaseOldestCRLShard(t *testing.T) {
  3201  	sa, clk, cleanUp := initSA(t)
  3202  	defer cleanUp()
  3203  
  3204  	// Create 8 shards: 4 for each of 2 issuers. For each issuer, one shard is
  3205  	// currently leased, three are available, and one of those failed to update.
  3206  	_, err := sa.dbMap.ExecContext(ctx,
  3207  		`INSERT INTO crlShards (issuerID, idx, thisUpdate, nextUpdate, leasedUntil) VALUES
  3208  		(1, 0, ?, ?, ?),
  3209  		(1, 1, ?, ?, ?),
  3210  		(1, 2, ?, ?, ?),
  3211  		(1, 3, NULL, NULL, ?),
  3212  		(2, 0, ?, ?, ?),
  3213  		(2, 1, ?, ?, ?),
  3214  		(2, 2, ?, ?, ?),
  3215  		(2, 3, NULL, NULL, ?);`,
  3216  		clk.Now().Add(-7*24*time.Hour), clk.Now().Add(3*24*time.Hour), clk.Now().Add(time.Hour),
  3217  		clk.Now().Add(-6*24*time.Hour), clk.Now().Add(4*24*time.Hour), clk.Now().Add(-6*24*time.Hour),
  3218  		clk.Now().Add(-5*24*time.Hour), clk.Now().Add(5*24*time.Hour), clk.Now().Add(-5*24*time.Hour),
  3219  		clk.Now().Add(-4*24*time.Hour),
  3220  		clk.Now().Add(-7*24*time.Hour), clk.Now().Add(3*24*time.Hour), clk.Now().Add(time.Hour),
  3221  		clk.Now().Add(-6*24*time.Hour), clk.Now().Add(4*24*time.Hour), clk.Now().Add(-6*24*time.Hour),
  3222  		clk.Now().Add(-5*24*time.Hour), clk.Now().Add(5*24*time.Hour), clk.Now().Add(-5*24*time.Hour),
  3223  		clk.Now().Add(-4*24*time.Hour),
  3224  	)
  3225  	test.AssertNotError(t, err, "setting up test shards")
  3226  
  3227  	until := clk.Now().Add(time.Hour).Truncate(time.Second).UTC()
  3228  	var untilModel struct {
  3229  		LeasedUntil time.Time `db:"leasedUntil"`
  3230  	}
  3231  
  3232  	// Leasing from a fully-leased subset should fail.
  3233  	_, err = sa.leaseOldestCRLShard(
  3234  		context.Background(),
  3235  		&sapb.LeaseCRLShardRequest{
  3236  			IssuerNameID: 1,
  3237  			MinShardIdx:  0,
  3238  			MaxShardIdx:  0,
  3239  			Until:        timestamppb.New(until),
  3240  		},
  3241  	)
  3242  	test.AssertError(t, err, "leasing when all shards are leased")
  3243  
  3244  	// Leasing any known shard should return the never-before-leased one (3).
  3245  	res, err := sa.leaseOldestCRLShard(
  3246  		context.Background(),
  3247  		&sapb.LeaseCRLShardRequest{
  3248  			IssuerNameID: 1,
  3249  			MinShardIdx:  0,
  3250  			MaxShardIdx:  3,
  3251  			Until:        timestamppb.New(until),
  3252  		},
  3253  	)
  3254  	test.AssertNotError(t, err, "leasing available shard")
  3255  	test.AssertEquals(t, res.IssuerNameID, int64(1))
  3256  	test.AssertEquals(t, res.ShardIdx, int64(3))
  3257  
  3258  	err = sa.dbMap.SelectOne(
  3259  		ctx,
  3260  		&untilModel,
  3261  		`SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`,
  3262  		res.IssuerNameID,
  3263  		res.ShardIdx,
  3264  	)
  3265  	test.AssertNotError(t, err, "getting updated lease timestamp")
  3266  	test.Assert(t, untilModel.LeasedUntil.Equal(until), "checking updated lease timestamp")
  3267  
  3268  	// Leasing any known shard *again* should now return the oldest one (1).
  3269  	res, err = sa.leaseOldestCRLShard(
  3270  		context.Background(),
  3271  		&sapb.LeaseCRLShardRequest{
  3272  			IssuerNameID: 1,
  3273  			MinShardIdx:  0,
  3274  			MaxShardIdx:  3,
  3275  			Until:        timestamppb.New(until),
  3276  		},
  3277  	)
  3278  	test.AssertNotError(t, err, "leasing available shard")
  3279  	test.AssertEquals(t, res.IssuerNameID, int64(1))
  3280  	test.AssertEquals(t, res.ShardIdx, int64(1))
  3281  
  3282  	err = sa.dbMap.SelectOne(
  3283  		ctx,
  3284  		&untilModel,
  3285  		`SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`,
  3286  		res.IssuerNameID,
  3287  		res.ShardIdx,
  3288  	)
  3289  	test.AssertNotError(t, err, "getting updated lease timestamp")
  3290  	test.Assert(t, untilModel.LeasedUntil.Equal(until), "checking updated lease timestamp")
  3291  
  3292  	// Leasing from a superset of known shards should succeed and return one of
  3293  	// the previously-unknown shards.
  3294  	res, err = sa.leaseOldestCRLShard(
  3295  		context.Background(),
  3296  		&sapb.LeaseCRLShardRequest{
  3297  			IssuerNameID: 2,
  3298  			MinShardIdx:  0,
  3299  			MaxShardIdx:  7,
  3300  			Until:        timestamppb.New(until),
  3301  		},
  3302  	)
  3303  	test.AssertNotError(t, err, "leasing available shard")
  3304  	test.AssertEquals(t, res.IssuerNameID, int64(2))
  3305  	test.Assert(t, res.ShardIdx >= 4, "checking leased index")
  3306  	test.Assert(t, res.ShardIdx <= 7, "checking leased index")
  3307  
  3308  	err = sa.dbMap.SelectOne(
  3309  		ctx,
  3310  		&untilModel,
  3311  		`SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`,
  3312  		res.IssuerNameID,
  3313  		res.ShardIdx,
  3314  	)
  3315  	test.AssertNotError(t, err, "getting updated lease timestamp")
  3316  	test.Assert(t, untilModel.LeasedUntil.Equal(until), "checking updated lease timestamp")
  3317  }
  3318  
  3319  func TestLeaseSpecificCRLShard(t *testing.T) {
  3320  	sa, clk, cleanUp := initSA(t)
  3321  	defer cleanUp()
  3322  
  3323  	// Create 8 shards: 4 for each of 2 issuers. For each issuer, one shard is
  3324  	// currently leased, three are available, and one of those failed to update.
  3325  	_, err := sa.dbMap.ExecContext(ctx,
  3326  		`INSERT INTO crlShards (issuerID, idx, thisUpdate, nextUpdate, leasedUntil) VALUES
  3327  		(1, 0, ?, ?, ?),
  3328  		(1, 1, ?, ?, ?),
  3329  		(1, 2, ?, ?, ?),
  3330  		(1, 3, NULL, NULL, ?),
  3331  		(2, 0, ?, ?, ?),
  3332  		(2, 1, ?, ?, ?),
  3333  		(2, 2, ?, ?, ?),
  3334  		(2, 3, NULL, NULL, ?);`,
  3335  		clk.Now().Add(-7*24*time.Hour), clk.Now().Add(3*24*time.Hour), clk.Now().Add(time.Hour),
  3336  		clk.Now().Add(-6*24*time.Hour), clk.Now().Add(4*24*time.Hour), clk.Now().Add(-6*24*time.Hour),
  3337  		clk.Now().Add(-5*24*time.Hour), clk.Now().Add(5*24*time.Hour), clk.Now().Add(-5*24*time.Hour),
  3338  		clk.Now().Add(-4*24*time.Hour),
  3339  		clk.Now().Add(-7*24*time.Hour), clk.Now().Add(3*24*time.Hour), clk.Now().Add(time.Hour),
  3340  		clk.Now().Add(-6*24*time.Hour), clk.Now().Add(4*24*time.Hour), clk.Now().Add(-6*24*time.Hour),
  3341  		clk.Now().Add(-5*24*time.Hour), clk.Now().Add(5*24*time.Hour), clk.Now().Add(-5*24*time.Hour),
  3342  		clk.Now().Add(-4*24*time.Hour),
  3343  	)
  3344  	test.AssertNotError(t, err, "setting up test shards")
  3345  
  3346  	until := clk.Now().Add(time.Hour).Truncate(time.Second).UTC()
  3347  	var untilModel struct {
  3348  		LeasedUntil time.Time `db:"leasedUntil"`
  3349  	}
  3350  
  3351  	// Leasing an unleased shard should work.
  3352  	res, err := sa.leaseSpecificCRLShard(
  3353  		context.Background(),
  3354  		&sapb.LeaseCRLShardRequest{
  3355  			IssuerNameID: 1,
  3356  			MinShardIdx:  1,
  3357  			MaxShardIdx:  1,
  3358  			Until:        timestamppb.New(until),
  3359  		},
  3360  	)
  3361  	test.AssertNotError(t, err, "leasing available shard")
  3362  	test.AssertEquals(t, res.IssuerNameID, int64(1))
  3363  	test.AssertEquals(t, res.ShardIdx, int64(1))
  3364  
  3365  	err = sa.dbMap.SelectOne(
  3366  		ctx,
  3367  		&untilModel,
  3368  		`SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`,
  3369  		res.IssuerNameID,
  3370  		res.ShardIdx,
  3371  	)
  3372  	test.AssertNotError(t, err, "getting updated lease timestamp")
  3373  	test.Assert(t, untilModel.LeasedUntil.Equal(until), "checking updated lease timestamp")
  3374  
  3375  	// Leasing a never-before-leased shard should work.
  3376  	res, err = sa.leaseSpecificCRLShard(
  3377  		context.Background(),
  3378  		&sapb.LeaseCRLShardRequest{
  3379  			IssuerNameID: 2,
  3380  			MinShardIdx:  3,
  3381  			MaxShardIdx:  3,
  3382  			Until:        timestamppb.New(until),
  3383  		},
  3384  	)
  3385  	test.AssertNotError(t, err, "leasing available shard")
  3386  	test.AssertEquals(t, res.IssuerNameID, int64(2))
  3387  	test.AssertEquals(t, res.ShardIdx, int64(3))
  3388  
  3389  	err = sa.dbMap.SelectOne(
  3390  		ctx,
  3391  		&untilModel,
  3392  		`SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`,
  3393  		res.IssuerNameID,
  3394  		res.ShardIdx,
  3395  	)
  3396  	test.AssertNotError(t, err, "getting updated lease timestamp")
  3397  	test.Assert(t, untilModel.LeasedUntil.Equal(until), "checking updated lease timestamp")
  3398  
  3399  	// Leasing a previously-unknown specific shard should work (to ease the
  3400  	// transition into using leasing).
  3401  	res, err = sa.leaseSpecificCRLShard(
  3402  		context.Background(),
  3403  		&sapb.LeaseCRLShardRequest{
  3404  			IssuerNameID: 1,
  3405  			MinShardIdx:  9,
  3406  			MaxShardIdx:  9,
  3407  			Until:        timestamppb.New(until),
  3408  		},
  3409  	)
  3410  	test.AssertNotError(t, err, "leasing unknown shard")
  3411  
  3412  	err = sa.dbMap.SelectOne(
  3413  		ctx,
  3414  		&untilModel,
  3415  		`SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`,
  3416  		res.IssuerNameID,
  3417  		res.ShardIdx,
  3418  	)
  3419  	test.AssertNotError(t, err, "getting updated lease timestamp")
  3420  	test.Assert(t, untilModel.LeasedUntil.Equal(until), "checking updated lease timestamp")
  3421  
  3422  	// Leasing a leased shard should fail.
  3423  	_, err = sa.leaseSpecificCRLShard(
  3424  		context.Background(),
  3425  		&sapb.LeaseCRLShardRequest{
  3426  			IssuerNameID: 1,
  3427  			MinShardIdx:  0,
  3428  			MaxShardIdx:  0,
  3429  			Until:        timestamppb.New(until),
  3430  		},
  3431  	)
  3432  	test.AssertError(t, err, "leasing unavailable shard")
  3433  
  3434  	// Leasing more than one shard should fail.
  3435  	_, err = sa.leaseSpecificCRLShard(
  3436  		context.Background(),
  3437  		&sapb.LeaseCRLShardRequest{
  3438  			IssuerNameID: 1,
  3439  			MinShardIdx:  1,
  3440  			MaxShardIdx:  2,
  3441  			Until:        timestamppb.New(until),
  3442  		},
  3443  	)
  3444  	test.AssertError(t, err, "did not lease one specific shard")
  3445  }
  3446  
  3447  func TestUpdateCRLShard(t *testing.T) {
  3448  	sa, clk, cleanUp := initSA(t)
  3449  	defer cleanUp()
  3450  
  3451  	// Create 8 shards: 4 for each of 2 issuers. For each issuer, one shard is
  3452  	// currently leased, three are available, and one of those failed to update.
  3453  	_, err := sa.dbMap.ExecContext(ctx,
  3454  		`INSERT INTO crlShards (issuerID, idx, thisUpdate, nextUpdate, leasedUntil) VALUES
  3455  		(1, 0, ?, ?, ?),
  3456  		(1, 1, ?, ?, ?),
  3457  		(1, 2, ?, ?, ?),
  3458  		(1, 3, NULL, NULL, ?),
  3459  		(2, 0, ?, ?, ?),
  3460  		(2, 1, ?, ?, ?),
  3461  		(2, 2, ?, ?, ?),
  3462  		(2, 3, NULL, NULL, ?);`,
  3463  		clk.Now().Add(-7*24*time.Hour), clk.Now().Add(3*24*time.Hour), clk.Now().Add(time.Hour),
  3464  		clk.Now().Add(-6*24*time.Hour), clk.Now().Add(4*24*time.Hour), clk.Now().Add(-6*24*time.Hour),
  3465  		clk.Now().Add(-5*24*time.Hour), clk.Now().Add(5*24*time.Hour), clk.Now().Add(-5*24*time.Hour),
  3466  		clk.Now().Add(-4*24*time.Hour),
  3467  		clk.Now().Add(-7*24*time.Hour), clk.Now().Add(3*24*time.Hour), clk.Now().Add(time.Hour),
  3468  		clk.Now().Add(-6*24*time.Hour), clk.Now().Add(4*24*time.Hour), clk.Now().Add(-6*24*time.Hour),
  3469  		clk.Now().Add(-5*24*time.Hour), clk.Now().Add(5*24*time.Hour), clk.Now().Add(-5*24*time.Hour),
  3470  		clk.Now().Add(-4*24*time.Hour),
  3471  	)
  3472  	test.AssertNotError(t, err, "setting up test shards")
  3473  
  3474  	thisUpdate := clk.Now().Truncate(time.Second).UTC()
  3475  	var crlModel struct {
  3476  		ThisUpdate *time.Time
  3477  		NextUpdate *time.Time
  3478  	}
  3479  
  3480  	// Updating a leased shard should work.
  3481  	_, err = sa.UpdateCRLShard(
  3482  		context.Background(),
  3483  		&sapb.UpdateCRLShardRequest{
  3484  			IssuerNameID: 1,
  3485  			ShardIdx:     0,
  3486  			ThisUpdate:   timestamppb.New(thisUpdate),
  3487  			NextUpdate:   timestamppb.New(thisUpdate.Add(10 * 24 * time.Hour)),
  3488  		},
  3489  	)
  3490  	test.AssertNotError(t, err, "updating leased shard")
  3491  
  3492  	err = sa.dbMap.SelectOne(
  3493  		ctx,
  3494  		&crlModel,
  3495  		`SELECT thisUpdate FROM crlShards WHERE issuerID = 1 AND idx = 0 LIMIT 1`,
  3496  	)
  3497  	test.AssertNotError(t, err, "getting updated thisUpdate timestamp")
  3498  	test.Assert(t, crlModel.ThisUpdate.Equal(thisUpdate), "checking updated thisUpdate timestamp")
  3499  
  3500  	// Updating an unleased shard should work.
  3501  	_, err = sa.UpdateCRLShard(
  3502  		context.Background(),
  3503  		&sapb.UpdateCRLShardRequest{
  3504  			IssuerNameID: 1,
  3505  			ShardIdx:     1,
  3506  			ThisUpdate:   timestamppb.New(thisUpdate),
  3507  			NextUpdate:   timestamppb.New(thisUpdate.Add(10 * 24 * time.Hour)),
  3508  		},
  3509  	)
  3510  	test.AssertNotError(t, err, "updating unleased shard")
  3511  
  3512  	err = sa.dbMap.SelectOne(
  3513  		ctx,
  3514  		&crlModel,
  3515  		`SELECT thisUpdate FROM crlShards WHERE issuerID = 1 AND idx = 1 LIMIT 1`,
  3516  	)
  3517  	test.AssertNotError(t, err, "getting updated thisUpdate timestamp")
  3518  	test.Assert(t, crlModel.ThisUpdate.Equal(thisUpdate), "checking updated thisUpdate timestamp")
  3519  
  3520  	// Updating without supplying a NextUpdate should work.
  3521  	_, err = sa.UpdateCRLShard(
  3522  		context.Background(),
  3523  		&sapb.UpdateCRLShardRequest{
  3524  			IssuerNameID: 1,
  3525  			ShardIdx:     3,
  3526  			ThisUpdate:   timestamppb.New(thisUpdate.Add(time.Second)),
  3527  		},
  3528  	)
  3529  	test.AssertNotError(t, err, "updating shard without NextUpdate")
  3530  
  3531  	err = sa.dbMap.SelectOne(
  3532  		ctx,
  3533  		&crlModel,
  3534  		`SELECT nextUpdate FROM crlShards WHERE issuerID = 1 AND idx = 3 LIMIT 1`,
  3535  	)
  3536  	test.AssertNotError(t, err, "getting updated nextUpdate timestamp")
  3537  	test.AssertBoxedNil(t, crlModel.NextUpdate, "checking updated nextUpdate timestamp")
  3538  
  3539  	// Updating a shard to an earlier time should fail.
  3540  	_, err = sa.UpdateCRLShard(
  3541  		context.Background(),
  3542  		&sapb.UpdateCRLShardRequest{
  3543  			IssuerNameID: 1,
  3544  			ShardIdx:     1,
  3545  			ThisUpdate:   timestamppb.New(thisUpdate.Add(-24 * time.Hour)),
  3546  			NextUpdate:   timestamppb.New(thisUpdate.Add(9 * 24 * time.Hour)),
  3547  		},
  3548  	)
  3549  	test.AssertError(t, err, "updating shard to an earlier time")
  3550  
  3551  	// Updating an unknown shard should fail.
  3552  	_, err = sa.UpdateCRLShard(
  3553  		context.Background(),
  3554  		&sapb.UpdateCRLShardRequest{
  3555  			IssuerNameID: 1,
  3556  			ShardIdx:     4,
  3557  			ThisUpdate:   timestamppb.New(thisUpdate),
  3558  			NextUpdate:   timestamppb.New(thisUpdate.Add(10 * 24 * time.Hour)),
  3559  		},
  3560  	)
  3561  	test.AssertError(t, err, "updating an unknown shard")
  3562  }
  3563  
  3564  func TestReplacementOrderExists(t *testing.T) {
  3565  	sa, fc, cleanUp := initSA(t)
  3566  	defer cleanUp()
  3567  
  3568  	oldCertSerial := "1234567890"
  3569  
  3570  	// Check that a non-existent replacement order does not exist.
  3571  	exists, err := sa.ReplacementOrderExists(ctx, &sapb.Serial{Serial: oldCertSerial})
  3572  	test.AssertNotError(t, err, "failed to check for replacement order")
  3573  	test.Assert(t, !exists.Exists, "replacement for non-existent serial should not exist")
  3574  
  3575  	// Create a test registration to reference.
  3576  	reg := createWorkingRegistration(t, sa)
  3577  
  3578  	// Add one valid authz.
  3579  	expires := fc.Now().Add(time.Hour)
  3580  	attemptedAt := fc.Now()
  3581  	authzID := createFinalizedAuthorization(t, sa, reg.Id, identifier.NewDNS("example.com"), expires, "valid", attemptedAt)
  3582  
  3583  	// Add a new order in pending status with no certificate serial.
  3584  	expires1Year := sa.clk.Now().Add(365 * 24 * time.Hour)
  3585  	order, err := sa.NewOrderAndAuthzs(ctx, &sapb.NewOrderAndAuthzsRequest{
  3586  		NewOrder: &sapb.NewOrderRequest{
  3587  			RegistrationID:   reg.Id,
  3588  			Expires:          timestamppb.New(expires1Year),
  3589  			Identifiers:      []*corepb.Identifier{identifier.NewDNS("example.com").ToProto()},
  3590  			V2Authorizations: []int64{authzID},
  3591  		},
  3592  	})
  3593  	test.AssertNotError(t, err, "NewOrderAndAuthzs failed")
  3594  
  3595  	// Set the order to processing so it can be finalized
  3596  	_, err = sa.SetOrderProcessing(ctx, &sapb.OrderRequest{Id: order.Id})
  3597  	test.AssertNotError(t, err, "SetOrderProcessing failed")
  3598  
  3599  	// Finalize the order with a certificate oldCertSerial.
  3600  	order.CertificateSerial = oldCertSerial
  3601  	_, err = sa.FinalizeOrder(ctx, &sapb.FinalizeOrderRequest{Id: order.Id, CertificateSerial: order.CertificateSerial})
  3602  	test.AssertNotError(t, err, "FinalizeOrder failed")
  3603  
  3604  	// Create a replacement order.
  3605  	order, err = sa.NewOrderAndAuthzs(ctx, &sapb.NewOrderAndAuthzsRequest{
  3606  		NewOrder: &sapb.NewOrderRequest{
  3607  			RegistrationID:   reg.Id,
  3608  			Expires:          timestamppb.New(expires1Year),
  3609  			Identifiers:      []*corepb.Identifier{identifier.NewDNS("example.com").ToProto()},
  3610  			V2Authorizations: []int64{authzID},
  3611  			ReplacesSerial:   oldCertSerial,
  3612  		},
  3613  	})
  3614  	test.AssertNotError(t, err, "NewOrderAndAuthzs failed")
  3615  
  3616  	// Check that a pending replacement order exists.
  3617  	exists, err = sa.ReplacementOrderExists(ctx, &sapb.Serial{Serial: oldCertSerial})
  3618  	test.AssertNotError(t, err, "failed to check for replacement order")
  3619  	test.Assert(t, exists.Exists, "replacement order should exist")
  3620  
  3621  	// Set the order to processing so it can be finalized.
  3622  	_, err = sa.SetOrderProcessing(ctx, &sapb.OrderRequest{Id: order.Id})
  3623  	test.AssertNotError(t, err, "SetOrderProcessing failed")
  3624  
  3625  	// Check that a replacement order in processing still exists.
  3626  	exists, err = sa.ReplacementOrderExists(ctx, &sapb.Serial{Serial: oldCertSerial})
  3627  	test.AssertNotError(t, err, "failed to check for replacement order")
  3628  	test.Assert(t, exists.Exists, "replacement order in processing should still exist")
  3629  
  3630  	order.CertificateSerial = "0123456789"
  3631  	_, err = sa.FinalizeOrder(ctx, &sapb.FinalizeOrderRequest{Id: order.Id, CertificateSerial: order.CertificateSerial})
  3632  	test.AssertNotError(t, err, "FinalizeOrder failed")
  3633  
  3634  	// Check that a finalized replacement order still exists.
  3635  	exists, err = sa.ReplacementOrderExists(ctx, &sapb.Serial{Serial: oldCertSerial})
  3636  	test.AssertNotError(t, err, "failed to check for replacement order")
  3637  	test.Assert(t, exists.Exists, "replacement order in processing should still exist")
  3638  
  3639  	// Try updating the replacement order.
  3640  
  3641  	// Create a replacement order.
  3642  	newReplacementOrder, err := sa.NewOrderAndAuthzs(ctx, &sapb.NewOrderAndAuthzsRequest{
  3643  		NewOrder: &sapb.NewOrderRequest{
  3644  			RegistrationID:   reg.Id,
  3645  			Expires:          timestamppb.New(expires1Year),
  3646  			Identifiers:      []*corepb.Identifier{identifier.NewDNS("example.com").ToProto()},
  3647  			V2Authorizations: []int64{authzID},
  3648  			ReplacesSerial:   oldCertSerial,
  3649  		},
  3650  	})
  3651  	test.AssertNotError(t, err, "NewOrderAndAuthzs failed")
  3652  
  3653  	// Fetch the replacement order so we can ensure it was updated.
  3654  	var replacementRow replacementOrderModel
  3655  	err = sa.dbReadOnlyMap.SelectOne(
  3656  		ctx,
  3657  		&replacementRow,
  3658  		"SELECT * FROM replacementOrders WHERE serial = ? LIMIT 1",
  3659  		oldCertSerial,
  3660  	)
  3661  	test.AssertNotError(t, err, "SELECT from replacementOrders failed")
  3662  	test.AssertEquals(t, newReplacementOrder.Id, replacementRow.OrderID)
  3663  	test.AssertEquals(t, newReplacementOrder.Expires.AsTime(), replacementRow.OrderExpires)
  3664  }
  3665  
  3666  func TestGetSerialsByKey(t *testing.T) {
  3667  	sa, fc, cleanUp := initSA(t)
  3668  	defer cleanUp()
  3669  
  3670  	// Insert four rows into keyHashToSerial: two that should match the query,
  3671  	// one that should not match due to keyHash mismatch, and one that should not
  3672  	// match due to being already expired.
  3673  	expectedHash := make([]byte, 32)
  3674  	expectedHash[0] = 1
  3675  	differentHash := make([]byte, 32)
  3676  	differentHash[0] = 2
  3677  	inserts := []keyHashModel{
  3678  		{
  3679  			KeyHash:      expectedHash,
  3680  			CertSerial:   "1",
  3681  			CertNotAfter: fc.Now().Add(time.Hour),
  3682  		},
  3683  		{
  3684  			KeyHash:      expectedHash,
  3685  			CertSerial:   "2",
  3686  			CertNotAfter: fc.Now().Add(2 * time.Hour),
  3687  		},
  3688  		{
  3689  			KeyHash:      expectedHash,
  3690  			CertSerial:   "3",
  3691  			CertNotAfter: fc.Now().Add(-1 * time.Hour),
  3692  		},
  3693  		{
  3694  			KeyHash:      differentHash,
  3695  			CertSerial:   "4",
  3696  			CertNotAfter: fc.Now().Add(time.Hour),
  3697  		},
  3698  	}
  3699  
  3700  	for _, row := range inserts {
  3701  		err := sa.dbMap.Insert(context.Background(), &row)
  3702  		test.AssertNotError(t, err, "inserting test keyHash")
  3703  	}
  3704  
  3705  	// Expect the result res to have two entries.
  3706  	res := make(chan *sapb.Serial)
  3707  	stream := &fakeServerStream[sapb.Serial]{output: res}
  3708  	var err error
  3709  	go func() {
  3710  		err = sa.GetSerialsByKey(&sapb.SPKIHash{KeyHash: expectedHash}, stream)
  3711  		close(res) // Let our main test thread continue.
  3712  	}()
  3713  
  3714  	var seen []string
  3715  	for serial := range res {
  3716  		if !slices.Contains([]string{"1", "2"}, serial.Serial) {
  3717  			t.Errorf("Received unexpected serial %q", serial.Serial)
  3718  		}
  3719  		if slices.Contains(seen, serial.Serial) {
  3720  			t.Errorf("Received serial %q more than once", serial.Serial)
  3721  		}
  3722  		seen = append(seen, serial.Serial)
  3723  	}
  3724  	test.AssertNotError(t, err, "calling GetSerialsByKey")
  3725  	test.AssertEquals(t, len(seen), 2)
  3726  }
  3727  
  3728  func TestGetSerialsByAccount(t *testing.T) {
  3729  	sa, fc, cleanUp := initSA(t)
  3730  	defer cleanUp()
  3731  
  3732  	expectedReg := createWorkingRegistration(t, sa)
  3733  
  3734  	// Insert three rows into the serials table: two that should match the query,
  3735  	// and one that should not match due to being already expired. We do not here
  3736  	// test filtering on the regID itself, because our test setup makes it very
  3737  	// hard to insert two fake registrations rows with different IDs.
  3738  	inserts := []recordedSerialModel{
  3739  		{
  3740  			Serial:         "1",
  3741  			RegistrationID: expectedReg.Id,
  3742  			Created:        fc.Now().Add(-23 * time.Hour),
  3743  			Expires:        fc.Now().Add(time.Hour),
  3744  		},
  3745  		{
  3746  			Serial:         "2",
  3747  			RegistrationID: expectedReg.Id,
  3748  			Created:        fc.Now().Add(-22 * time.Hour),
  3749  			Expires:        fc.Now().Add(2 * time.Hour),
  3750  		},
  3751  		{
  3752  			Serial:         "3",
  3753  			RegistrationID: expectedReg.Id,
  3754  			Created:        fc.Now().Add(-23 * time.Hour),
  3755  			Expires:        fc.Now().Add(-1 * time.Hour),
  3756  		},
  3757  	}
  3758  
  3759  	for _, row := range inserts {
  3760  		err := sa.dbMap.Insert(context.Background(), &row)
  3761  		test.AssertNotError(t, err, "inserting test serial")
  3762  	}
  3763  
  3764  	// Expect the result stream to have two entries.
  3765  	res := make(chan *sapb.Serial)
  3766  	stream := &fakeServerStream[sapb.Serial]{output: res}
  3767  	var err error
  3768  	go func() {
  3769  		err = sa.GetSerialsByAccount(&sapb.RegistrationID{Id: expectedReg.Id}, stream)
  3770  		close(res) // Let our main test thread continue.
  3771  	}()
  3772  
  3773  	var seen []string
  3774  	for serial := range res {
  3775  		if !slices.Contains([]string{"1", "2"}, serial.Serial) {
  3776  			t.Errorf("Received unexpected serial %q", serial.Serial)
  3777  		}
  3778  		if slices.Contains(seen, serial.Serial) {
  3779  			t.Errorf("Received serial %q more than once", serial.Serial)
  3780  		}
  3781  		seen = append(seen, serial.Serial)
  3782  	}
  3783  	test.AssertNotError(t, err, "calling GetSerialsByAccount")
  3784  	test.AssertEquals(t, len(seen), 2)
  3785  }
  3786  
  3787  func TestUnpauseAccount(t *testing.T) {
  3788  	sa, _, cleanUp := initSA(t)
  3789  	defer cleanUp()
  3790  
  3791  	reg := createWorkingRegistration(t, sa)
  3792  
  3793  	tests := []struct {
  3794  		name  string
  3795  		state []pausedModel
  3796  		req   *sapb.RegistrationID
  3797  	}{
  3798  		{
  3799  			name:  "UnpauseAccount with no paused identifiers",
  3800  			state: nil,
  3801  			req:   &sapb.RegistrationID{Id: reg.Id},
  3802  		},
  3803  		{
  3804  			name: "UnpauseAccount with one paused identifier",
  3805  			state: []pausedModel{
  3806  				{
  3807  					RegistrationID: reg.Id,
  3808  					identifierModel: identifierModel{
  3809  						Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  3810  						Value: "example.com",
  3811  					},
  3812  					PausedAt: sa.clk.Now().Add(-time.Hour),
  3813  				},
  3814  			},
  3815  			req: &sapb.RegistrationID{Id: reg.Id},
  3816  		},
  3817  		{
  3818  			name: "UnpauseAccount with multiple paused identifiers",
  3819  			state: []pausedModel{
  3820  				{
  3821  					RegistrationID: reg.Id,
  3822  					identifierModel: identifierModel{
  3823  						Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  3824  						Value: "example.com",
  3825  					},
  3826  					PausedAt: sa.clk.Now().Add(-time.Hour),
  3827  				},
  3828  				{
  3829  					RegistrationID: reg.Id,
  3830  					identifierModel: identifierModel{
  3831  						Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  3832  						Value: "example.net",
  3833  					},
  3834  					PausedAt: sa.clk.Now().Add(-time.Hour),
  3835  				},
  3836  				{
  3837  					RegistrationID: reg.Id,
  3838  					identifierModel: identifierModel{
  3839  						Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  3840  						Value: "example.org",
  3841  					},
  3842  					PausedAt: sa.clk.Now().Add(-time.Hour),
  3843  				},
  3844  			},
  3845  			req: &sapb.RegistrationID{Id: reg.Id},
  3846  		},
  3847  	}
  3848  	for _, tt := range tests {
  3849  		t.Run(tt.name, func(t *testing.T) {
  3850  			defer func() {
  3851  				_, err := sa.dbMap.ExecContext(ctx, "DELETE FROM paused WHERE 1 = 1")
  3852  				test.AssertNotError(t, err, "cleaning up paused table")
  3853  			}()
  3854  
  3855  			// Setup table state.
  3856  			for _, state := range tt.state {
  3857  				err := sa.dbMap.Insert(ctx, &state)
  3858  				test.AssertNotError(t, err, "inserting test identifier")
  3859  			}
  3860  
  3861  			_, err := sa.UnpauseAccount(ctx, tt.req)
  3862  			test.AssertNotError(t, err, "Unexpected error for UnpauseAccount()")
  3863  
  3864  			// Count the number of paused identifiers.
  3865  			var count int
  3866  			err = sa.dbReadOnlyMap.SelectOne(
  3867  				ctx,
  3868  				&count,
  3869  				"SELECT COUNT(*) FROM paused WHERE registrationID = ? AND unpausedAt IS NULL",
  3870  				tt.req.Id,
  3871  			)
  3872  			test.AssertNotError(t, err, "SELECT COUNT(*) failed")
  3873  			test.AssertEquals(t, count, 0)
  3874  		})
  3875  	}
  3876  }
  3877  
  3878  func bulkInsertPausedIdentifiers(ctx context.Context, sa *SQLStorageAuthority, regID int64, count int) error {
  3879  	const batchSize = 1000
  3880  
  3881  	values := make([]any, 0, batchSize*4)
  3882  	now := sa.clk.Now().Add(-time.Hour)
  3883  	batches := (count + batchSize - 1) / batchSize
  3884  
  3885  	for batch := range batches {
  3886  		query := `
  3887  		INSERT INTO paused (registrationID, identifierType, identifierValue, pausedAt)
  3888  		VALUES`
  3889  
  3890  		start := batch * batchSize
  3891  		end := min(start+batchSize, count)
  3892  
  3893  		for i := start; i < end; i++ {
  3894  			if i > start {
  3895  				query += ","
  3896  			}
  3897  			query += "(?, ?, ?, ?)"
  3898  			values = append(values, regID, identifierTypeToUint[string(identifier.TypeDNS)], fmt.Sprintf("example%d.com", i), now)
  3899  		}
  3900  
  3901  		_, err := sa.dbMap.ExecContext(ctx, query, values...)
  3902  		if err != nil {
  3903  			return fmt.Errorf("bulk inserting paused identifiers: %w", err)
  3904  		}
  3905  		values = values[:0]
  3906  	}
  3907  
  3908  	return nil
  3909  }
  3910  
  3911  func TestUnpauseAccountWithTwoLoops(t *testing.T) {
  3912  	sa, _, cleanUp := initSA(t)
  3913  	defer cleanUp()
  3914  
  3915  	reg := createWorkingRegistration(t, sa)
  3916  
  3917  	err := bulkInsertPausedIdentifiers(ctx, sa, reg.Id, 12000)
  3918  	test.AssertNotError(t, err, "bulk inserting paused identifiers")
  3919  
  3920  	result, err := sa.UnpauseAccount(ctx, &sapb.RegistrationID{Id: reg.Id})
  3921  	test.AssertNotError(t, err, "Unexpected error for UnpauseAccount()")
  3922  	test.AssertEquals(t, result.Count, int64(12000))
  3923  }
  3924  
  3925  func TestUnpauseAccountWithMaxLoops(t *testing.T) {
  3926  	sa, _, cleanUp := initSA(t)
  3927  	defer cleanUp()
  3928  
  3929  	reg := createWorkingRegistration(t, sa)
  3930  	err := bulkInsertPausedIdentifiers(ctx, sa, reg.Id, 50001)
  3931  	test.AssertNotError(t, err, "bulk inserting paused identifiers")
  3932  
  3933  	result, err := sa.UnpauseAccount(ctx, &sapb.RegistrationID{Id: reg.Id})
  3934  	test.AssertNotError(t, err, "Unexpected error for UnpauseAccount()")
  3935  	test.AssertEquals(t, result.Count, int64(50000))
  3936  }
  3937  
  3938  func TestPauseIdentifiers(t *testing.T) {
  3939  	sa, _, cleanUp := initSA(t)
  3940  	defer cleanUp()
  3941  
  3942  	reg := createWorkingRegistration(t, sa)
  3943  	ptrTime := func(t time.Time) *time.Time {
  3944  		return &t
  3945  	}
  3946  
  3947  	fourWeeksAgo := sa.clk.Now().Add(-4 * 7 * 24 * time.Hour)
  3948  	threeWeeksAgo := sa.clk.Now().Add(-3 * 7 * 24 * time.Hour)
  3949  
  3950  	tests := []struct {
  3951  		name  string
  3952  		state []pausedModel
  3953  		req   *sapb.PauseRequest
  3954  		want  *sapb.PauseIdentifiersResponse
  3955  	}{
  3956  		{
  3957  			name:  "An identifier which is not now or previously paused",
  3958  			state: nil,
  3959  			req: &sapb.PauseRequest{
  3960  				RegistrationID: reg.Id,
  3961  				Identifiers: []*corepb.Identifier{
  3962  					{
  3963  						Type:  string(identifier.TypeDNS),
  3964  						Value: "example.com",
  3965  					},
  3966  				},
  3967  			},
  3968  			want: &sapb.PauseIdentifiersResponse{
  3969  				Paused:   1,
  3970  				Repaused: 0,
  3971  			},
  3972  		},
  3973  		{
  3974  			name: "One unpaused entry which was previously paused",
  3975  			state: []pausedModel{
  3976  				{
  3977  					RegistrationID: reg.Id,
  3978  					identifierModel: identifierModel{
  3979  						Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  3980  						Value: "example.com",
  3981  					},
  3982  					PausedAt:   fourWeeksAgo,
  3983  					UnpausedAt: ptrTime(threeWeeksAgo),
  3984  				},
  3985  			},
  3986  			req: &sapb.PauseRequest{
  3987  				RegistrationID: reg.Id,
  3988  				Identifiers: []*corepb.Identifier{
  3989  					{
  3990  						Type:  string(identifier.TypeDNS),
  3991  						Value: "example.com",
  3992  					},
  3993  				},
  3994  			},
  3995  			want: &sapb.PauseIdentifiersResponse{
  3996  				Paused:   0,
  3997  				Repaused: 1,
  3998  			},
  3999  		},
  4000  		{
  4001  			name: "One unpaused entry which was previously paused and unpaused less than 2 weeks ago",
  4002  			state: []pausedModel{
  4003  				{
  4004  					RegistrationID: reg.Id,
  4005  					identifierModel: identifierModel{
  4006  						Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  4007  						Value: "example.com",
  4008  					},
  4009  					PausedAt:   fourWeeksAgo,
  4010  					UnpausedAt: ptrTime(sa.clk.Now().Add(-13 * 24 * time.Hour)),
  4011  				},
  4012  			},
  4013  			req: &sapb.PauseRequest{
  4014  				RegistrationID: reg.Id,
  4015  				Identifiers: []*corepb.Identifier{
  4016  					{
  4017  						Type:  string(identifier.TypeDNS),
  4018  						Value: "example.com",
  4019  					},
  4020  				},
  4021  			},
  4022  			want: &sapb.PauseIdentifiersResponse{
  4023  				Paused:   0,
  4024  				Repaused: 0,
  4025  			},
  4026  		},
  4027  		{
  4028  			name: "An identifier which is currently paused",
  4029  			state: []pausedModel{
  4030  				{
  4031  					RegistrationID: reg.Id,
  4032  					identifierModel: identifierModel{
  4033  						Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  4034  						Value: "example.com",
  4035  					},
  4036  					PausedAt: fourWeeksAgo,
  4037  				},
  4038  			},
  4039  			req: &sapb.PauseRequest{
  4040  				RegistrationID: reg.Id,
  4041  				Identifiers: []*corepb.Identifier{
  4042  					{
  4043  						Type:  string(identifier.TypeDNS),
  4044  						Value: "example.com",
  4045  					},
  4046  				},
  4047  			},
  4048  			want: &sapb.PauseIdentifiersResponse{
  4049  				Paused:   0,
  4050  				Repaused: 0,
  4051  			},
  4052  		},
  4053  		{
  4054  			name: "Two previously paused entries and one new entry",
  4055  			state: []pausedModel{
  4056  				{
  4057  					RegistrationID: reg.Id,
  4058  					identifierModel: identifierModel{
  4059  						Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  4060  						Value: "example.com",
  4061  					},
  4062  					PausedAt:   fourWeeksAgo,
  4063  					UnpausedAt: ptrTime(threeWeeksAgo),
  4064  				},
  4065  				{
  4066  					RegistrationID: reg.Id,
  4067  					identifierModel: identifierModel{
  4068  						Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  4069  						Value: "example.net",
  4070  					},
  4071  					PausedAt:   fourWeeksAgo,
  4072  					UnpausedAt: ptrTime(threeWeeksAgo),
  4073  				},
  4074  			},
  4075  			req: &sapb.PauseRequest{
  4076  				RegistrationID: reg.Id,
  4077  				Identifiers: []*corepb.Identifier{
  4078  					{
  4079  						Type:  string(identifier.TypeDNS),
  4080  						Value: "example.com",
  4081  					},
  4082  					{
  4083  						Type:  string(identifier.TypeDNS),
  4084  						Value: "example.net",
  4085  					},
  4086  					{
  4087  						Type:  string(identifier.TypeDNS),
  4088  						Value: "example.org",
  4089  					},
  4090  				},
  4091  			},
  4092  			want: &sapb.PauseIdentifiersResponse{
  4093  				Paused:   1,
  4094  				Repaused: 2,
  4095  			},
  4096  		},
  4097  	}
  4098  	for _, tt := range tests {
  4099  		t.Run(tt.name, func(t *testing.T) {
  4100  			defer func() {
  4101  				_, err := sa.dbMap.ExecContext(ctx, "DELETE FROM paused WHERE 1 = 1")
  4102  				test.AssertNotError(t, err, "cleaning up paused table")
  4103  			}()
  4104  
  4105  			// Setup table state.
  4106  			for _, state := range tt.state {
  4107  				err := sa.dbMap.Insert(ctx, &state)
  4108  				test.AssertNotError(t, err, "inserting test identifier")
  4109  			}
  4110  
  4111  			got, err := sa.PauseIdentifiers(ctx, tt.req)
  4112  			test.AssertNotError(t, err, "Unexpected error for PauseIdentifiers()")
  4113  			test.AssertEquals(t, got.Paused, tt.want.Paused)
  4114  			test.AssertEquals(t, got.Repaused, tt.want.Repaused)
  4115  		})
  4116  	}
  4117  }
  4118  
  4119  func TestCheckIdentifiersPaused(t *testing.T) {
  4120  	sa, _, cleanUp := initSA(t)
  4121  	defer cleanUp()
  4122  
  4123  	ptrTime := func(t time.Time) *time.Time {
  4124  		return &t
  4125  	}
  4126  
  4127  	reg := createWorkingRegistration(t, sa)
  4128  	tests := []struct {
  4129  		name  string
  4130  		state []pausedModel
  4131  		req   *sapb.PauseRequest
  4132  		want  *sapb.Identifiers
  4133  	}{
  4134  		{
  4135  			name:  "No paused identifiers",
  4136  			state: nil,
  4137  			req: &sapb.PauseRequest{
  4138  				RegistrationID: reg.Id,
  4139  				Identifiers: []*corepb.Identifier{
  4140  					{
  4141  						Type:  string(identifier.TypeDNS),
  4142  						Value: "example.com",
  4143  					},
  4144  				},
  4145  			},
  4146  			want: &sapb.Identifiers{
  4147  				Identifiers: []*corepb.Identifier{},
  4148  			},
  4149  		},
  4150  		{
  4151  			name: "One paused identifier",
  4152  			state: []pausedModel{
  4153  				{
  4154  					RegistrationID: reg.Id,
  4155  					identifierModel: identifierModel{
  4156  						Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  4157  						Value: "example.com",
  4158  					},
  4159  					PausedAt: sa.clk.Now().Add(-time.Hour),
  4160  				},
  4161  			},
  4162  			req: &sapb.PauseRequest{
  4163  				RegistrationID: reg.Id,
  4164  				Identifiers: []*corepb.Identifier{
  4165  					{
  4166  						Type:  string(identifier.TypeDNS),
  4167  						Value: "example.com",
  4168  					},
  4169  				},
  4170  			},
  4171  			want: &sapb.Identifiers{
  4172  				Identifiers: []*corepb.Identifier{
  4173  					{
  4174  						Type:  string(identifier.TypeDNS),
  4175  						Value: "example.com",
  4176  					},
  4177  				},
  4178  			},
  4179  		},
  4180  		{
  4181  			name: "Two paused identifiers, one unpaused",
  4182  			state: []pausedModel{
  4183  				{
  4184  					RegistrationID: reg.Id,
  4185  					identifierModel: identifierModel{
  4186  						Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  4187  						Value: "example.com",
  4188  					},
  4189  					PausedAt: sa.clk.Now().Add(-time.Hour),
  4190  				},
  4191  				{
  4192  					RegistrationID: reg.Id,
  4193  					identifierModel: identifierModel{
  4194  						Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  4195  						Value: "example.net",
  4196  					},
  4197  					PausedAt: sa.clk.Now().Add(-time.Hour),
  4198  				},
  4199  				{
  4200  					RegistrationID: reg.Id,
  4201  					identifierModel: identifierModel{
  4202  						Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  4203  						Value: "example.org",
  4204  					},
  4205  					PausedAt:   sa.clk.Now().Add(-time.Hour),
  4206  					UnpausedAt: ptrTime(sa.clk.Now().Add(-time.Minute)),
  4207  				},
  4208  			},
  4209  			req: &sapb.PauseRequest{
  4210  				RegistrationID: reg.Id,
  4211  				Identifiers: []*corepb.Identifier{
  4212  					{
  4213  						Type:  string(identifier.TypeDNS),
  4214  						Value: "example.com",
  4215  					},
  4216  					{
  4217  						Type:  string(identifier.TypeDNS),
  4218  						Value: "example.net",
  4219  					},
  4220  					{
  4221  						Type:  string(identifier.TypeDNS),
  4222  						Value: "example.org",
  4223  					},
  4224  				},
  4225  			},
  4226  			want: &sapb.Identifiers{
  4227  				Identifiers: []*corepb.Identifier{
  4228  					{
  4229  						Type:  string(identifier.TypeDNS),
  4230  						Value: "example.com",
  4231  					},
  4232  					{
  4233  						Type:  string(identifier.TypeDNS),
  4234  						Value: "example.net",
  4235  					},
  4236  				},
  4237  			},
  4238  		},
  4239  	}
  4240  	for _, tt := range tests {
  4241  		t.Run(tt.name, func(t *testing.T) {
  4242  			defer func() {
  4243  				_, err := sa.dbMap.ExecContext(ctx, "DELETE FROM paused WHERE 1 = 1")
  4244  				test.AssertNotError(t, err, "cleaning up paused table")
  4245  			}()
  4246  
  4247  			// Setup table state.
  4248  			for _, state := range tt.state {
  4249  				err := sa.dbMap.Insert(ctx, &state)
  4250  				test.AssertNotError(t, err, "inserting test identifier")
  4251  			}
  4252  
  4253  			got, err := sa.CheckIdentifiersPaused(ctx, tt.req)
  4254  			test.AssertNotError(t, err, "Unexpected error for PauseIdentifiers()")
  4255  			test.AssertDeepEquals(t, got.Identifiers, tt.want.Identifiers)
  4256  		})
  4257  	}
  4258  }
  4259  
  4260  func TestGetPausedIdentifiers(t *testing.T) {
  4261  	sa, _, cleanUp := initSA(t)
  4262  	defer cleanUp()
  4263  
  4264  	ptrTime := func(t time.Time) *time.Time {
  4265  		return &t
  4266  	}
  4267  
  4268  	reg := createWorkingRegistration(t, sa)
  4269  
  4270  	tests := []struct {
  4271  		name  string
  4272  		state []pausedModel
  4273  		req   *sapb.RegistrationID
  4274  		want  *sapb.Identifiers
  4275  	}{
  4276  		{
  4277  			name:  "No paused identifiers",
  4278  			state: nil,
  4279  			req:   &sapb.RegistrationID{Id: reg.Id},
  4280  			want: &sapb.Identifiers{
  4281  				Identifiers: []*corepb.Identifier{},
  4282  			},
  4283  		},
  4284  		{
  4285  			name: "One paused identifier",
  4286  			state: []pausedModel{
  4287  				{
  4288  					RegistrationID: reg.Id,
  4289  					identifierModel: identifierModel{
  4290  						Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  4291  						Value: "example.com",
  4292  					},
  4293  					PausedAt: sa.clk.Now().Add(-time.Hour),
  4294  				},
  4295  			},
  4296  			req: &sapb.RegistrationID{Id: reg.Id},
  4297  			want: &sapb.Identifiers{
  4298  				Identifiers: []*corepb.Identifier{
  4299  					{
  4300  						Type:  string(identifier.TypeDNS),
  4301  						Value: "example.com",
  4302  					},
  4303  				},
  4304  			},
  4305  		},
  4306  		{
  4307  			name: "Two paused identifiers, one unpaused",
  4308  			state: []pausedModel{
  4309  				{
  4310  					RegistrationID: reg.Id,
  4311  					identifierModel: identifierModel{
  4312  						Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  4313  						Value: "example.com",
  4314  					},
  4315  					PausedAt: sa.clk.Now().Add(-time.Hour),
  4316  				},
  4317  				{
  4318  					RegistrationID: reg.Id,
  4319  					identifierModel: identifierModel{
  4320  						Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  4321  						Value: "example.net",
  4322  					},
  4323  					PausedAt: sa.clk.Now().Add(-time.Hour),
  4324  				},
  4325  				{
  4326  					RegistrationID: reg.Id,
  4327  					identifierModel: identifierModel{
  4328  						Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  4329  						Value: "example.org",
  4330  					},
  4331  					PausedAt:   sa.clk.Now().Add(-time.Hour),
  4332  					UnpausedAt: ptrTime(sa.clk.Now().Add(-time.Minute)),
  4333  				},
  4334  			},
  4335  			req: &sapb.RegistrationID{Id: reg.Id},
  4336  			want: &sapb.Identifiers{
  4337  				Identifiers: []*corepb.Identifier{
  4338  					{
  4339  						Type:  string(identifier.TypeDNS),
  4340  						Value: "example.com",
  4341  					},
  4342  					{
  4343  						Type:  string(identifier.TypeDNS),
  4344  						Value: "example.net",
  4345  					},
  4346  				},
  4347  			},
  4348  		},
  4349  	}
  4350  	for _, tt := range tests {
  4351  		t.Run(tt.name, func(t *testing.T) {
  4352  			defer func() {
  4353  				_, err := sa.dbMap.ExecContext(ctx, "DELETE FROM paused WHERE 1 = 1")
  4354  				test.AssertNotError(t, err, "cleaning up paused table")
  4355  			}()
  4356  
  4357  			// Setup table state.
  4358  			for _, state := range tt.state {
  4359  				err := sa.dbMap.Insert(ctx, &state)
  4360  				test.AssertNotError(t, err, "inserting test identifier")
  4361  			}
  4362  
  4363  			got, err := sa.GetPausedIdentifiers(ctx, tt.req)
  4364  			test.AssertNotError(t, err, "Unexpected error for PauseIdentifiers()")
  4365  			test.AssertDeepEquals(t, got.Identifiers, tt.want.Identifiers)
  4366  		})
  4367  	}
  4368  }
  4369  
  4370  func TestGetPausedIdentifiersOnlyUnpausesOneAccount(t *testing.T) {
  4371  	sa, _, cleanUp := initSA(t)
  4372  	defer cleanUp()
  4373  
  4374  	reg1 := createWorkingRegistration(t, sa)
  4375  	reg2, err := sa.NewRegistration(ctx, &corepb.Registration{
  4376  		Key:       newAcctKey(t),
  4377  		CreatedAt: mustTimestamp("2018-04-01 07:00"),
  4378  		Status:    string(core.StatusValid),
  4379  	})
  4380  	test.AssertNotError(t, err, "creating second registration")
  4381  
  4382  	// Insert two paused identifiers for two different accounts.
  4383  	err = sa.dbMap.Insert(ctx, &pausedModel{
  4384  		RegistrationID: reg1.Id,
  4385  		identifierModel: identifierModel{
  4386  			Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  4387  			Value: "example.com",
  4388  		},
  4389  		PausedAt: sa.clk.Now().Add(-time.Hour),
  4390  	})
  4391  	test.AssertNotError(t, err, "inserting test identifier")
  4392  
  4393  	err = sa.dbMap.Insert(ctx, &pausedModel{
  4394  		RegistrationID: reg2.Id,
  4395  		identifierModel: identifierModel{
  4396  			Type:  identifierTypeToUint[string(identifier.TypeDNS)],
  4397  			Value: "example.net",
  4398  		},
  4399  		PausedAt: sa.clk.Now().Add(-time.Hour),
  4400  	})
  4401  	test.AssertNotError(t, err, "inserting test identifier")
  4402  
  4403  	// Unpause the first account.
  4404  	_, err = sa.UnpauseAccount(ctx, &sapb.RegistrationID{Id: reg1.Id})
  4405  	test.AssertNotError(t, err, "UnpauseAccount failed")
  4406  
  4407  	// Check that the second account's identifier is still paused.
  4408  	idents, err := sa.GetPausedIdentifiers(ctx, &sapb.RegistrationID{Id: reg2.Id})
  4409  	test.AssertNotError(t, err, "GetPausedIdentifiers failed")
  4410  	test.AssertEquals(t, len(idents.Identifiers), 1)
  4411  	test.AssertEquals(t, idents.Identifiers[0].Value, "example.net")
  4412  }
  4413  
  4414  func newAcctKey(t *testing.T) []byte {
  4415  	key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  4416  	jwk := &jose.JSONWebKey{Key: key.Public()}
  4417  	acctKey, err := jwk.MarshalJSON()
  4418  	test.AssertNotError(t, err, "failed to marshal account key")
  4419  	return acctKey
  4420  }
  4421  
  4422  func TestUpdateRegistrationKey(t *testing.T) {
  4423  	sa, _, cleanUp := initSA(t)
  4424  	defer cleanUp()
  4425  
  4426  	_, err := sa.UpdateRegistrationKey(ctx, &sapb.UpdateRegistrationKeyRequest{})
  4427  	test.AssertError(t, err, "should not have been able to update registration key without a registration ID")
  4428  	test.AssertContains(t, err.Error(), "incomplete gRPC request message")
  4429  
  4430  	existingReg, err := sa.NewRegistration(ctx, &corepb.Registration{
  4431  		Key: newAcctKey(t),
  4432  	})
  4433  	test.AssertNotError(t, err, "creating new registration")
  4434  
  4435  	tests := []struct {
  4436  		name          string
  4437  		newJwk        []byte
  4438  		expectedError string
  4439  	}{
  4440  		{
  4441  			name:   "update a valid registration with a new account key",
  4442  			newJwk: newAcctKey(t),
  4443  		},
  4444  		{
  4445  			name:          "update a valid registration with a duplicate account key",
  4446  			newJwk:        existingReg.Key,
  4447  			expectedError: "key is already in use for a different account",
  4448  		},
  4449  		{
  4450  			name:          "update a valid registration with a malformed account key",
  4451  			newJwk:        []byte("Eat at Joe's"),
  4452  			expectedError: "parsing JWK",
  4453  		},
  4454  	}
  4455  	for _, tt := range tests {
  4456  		t.Run(tt.name, func(t *testing.T) {
  4457  			reg, err := sa.NewRegistration(ctx, &corepb.Registration{
  4458  				Key: newAcctKey(t),
  4459  			})
  4460  			test.AssertNotError(t, err, "creating new registration")
  4461  
  4462  			updatedReg, err := sa.UpdateRegistrationKey(ctx, &sapb.UpdateRegistrationKeyRequest{
  4463  				RegistrationID: reg.Id,
  4464  				Jwk:            tt.newJwk,
  4465  			})
  4466  			if tt.expectedError != "" {
  4467  				test.AssertError(t, err, "should have errored")
  4468  				test.AssertContains(t, err.Error(), tt.expectedError)
  4469  			} else {
  4470  				test.AssertNotError(t, err, "unexpected error for UpdateRegistrationKey()")
  4471  				test.AssertEquals(t, updatedReg.Id, reg.Id)
  4472  				test.AssertDeepEquals(t, updatedReg.Key, tt.newJwk)
  4473  
  4474  				refetchedReg, err := sa.GetRegistration(ctx, &sapb.RegistrationID{
  4475  					Id: reg.Id,
  4476  				})
  4477  				test.AssertNotError(t, err, "retrieving registration")
  4478  				test.AssertDeepEquals(t, refetchedReg.Key, tt.newJwk)
  4479  			}
  4480  		})
  4481  	}
  4482  }
  4483  
  4484  type mockRLOStream struct {
  4485  	grpc.ServerStream
  4486  	sent []*sapb.RateLimitOverrideResponse
  4487  	ctx  context.Context
  4488  }
  4489  
  4490  func newMockRLOStream() *mockRLOStream {
  4491  	return &mockRLOStream{ctx: ctx}
  4492  }
  4493  func (m *mockRLOStream) Context() context.Context { return m.ctx }
  4494  func (m *mockRLOStream) RecvMsg(any) error        { return io.EOF }
  4495  func (m *mockRLOStream) Send(ov *sapb.RateLimitOverrideResponse) error {
  4496  	m.sent = append(m.sent, ov)
  4497  	return nil
  4498  }
  4499  
  4500  func TestAddRateLimitOverrideInsertThenUpdate(t *testing.T) {
  4501  	if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
  4502  		// TODO(#8147): Remove this skip.
  4503  		t.Skip("skipping, this overrides table must exist for this test to run")
  4504  	}
  4505  
  4506  	sa, _, cleanup := initSA(t)
  4507  	defer cleanup()
  4508  
  4509  	expectBucketKey := core.RandomString(10)
  4510  	ov := &sapb.RateLimitOverride{
  4511  		LimitEnum: 1,
  4512  		BucketKey: expectBucketKey,
  4513  		Comment:   "insert",
  4514  		Period:    durationpb.New(time.Hour),
  4515  		Count:     100,
  4516  		Burst:     100,
  4517  	}
  4518  
  4519  	// Insert
  4520  	resp, err := sa.AddRateLimitOverride(ctx, &sapb.AddRateLimitOverrideRequest{Override: ov})
  4521  	test.AssertNotError(t, err, "expected successful insert, got error")
  4522  	test.Assert(t, resp.Inserted && resp.Enabled, fmt.Sprintf("expected (Inserted=true, Enabled=true) for initial insert, got (%v,%v)", resp.Inserted, resp.Enabled))
  4523  
  4524  	// Update (change comment)
  4525  	ov.Comment = "updated"
  4526  	resp, err = sa.AddRateLimitOverride(ctx, &sapb.AddRateLimitOverrideRequest{Override: ov})
  4527  	test.AssertNotError(t, err, "expected successful update, got error")
  4528  	test.Assert(t, !resp.Inserted && resp.Enabled, fmt.Sprintf("expected (Inserted=false, Enabled=true) for update, got (%v, %v)", resp.Inserted, resp.Enabled))
  4529  
  4530  	got, err := sa.GetRateLimitOverride(ctx, &sapb.GetRateLimitOverrideRequest{LimitEnum: 1, BucketKey: expectBucketKey})
  4531  	test.AssertNotError(t, err, "expected GetRateLimitOverride to succeed, got error")
  4532  	test.AssertEquals(t, got.Override.Comment, "updated")
  4533  
  4534  	// Disable
  4535  	_, err = sa.DisableRateLimitOverride(ctx, &sapb.DisableRateLimitOverrideRequest{LimitEnum: 1, BucketKey: expectBucketKey})
  4536  	test.AssertNotError(t, err, "expected DisableRateLimitOverride to succeed, got error")
  4537  
  4538  	// Update and check that it's still disabled.
  4539  	got, err = sa.GetRateLimitOverride(ctx, &sapb.GetRateLimitOverrideRequest{LimitEnum: 1, BucketKey: expectBucketKey})
  4540  	test.AssertNotError(t, err, "expected GetRateLimitOverride to succeed, got error")
  4541  	test.Assert(t, !got.Enabled, fmt.Sprintf("expected Enabled=false after disable, got Enabled=%v", got.Enabled))
  4542  
  4543  	// Update (change period, count, and burst)
  4544  	ov.Period = durationpb.New(2 * time.Hour)
  4545  	ov.Count = 200
  4546  	ov.Burst = 200
  4547  	_, err = sa.AddRateLimitOverride(ctx, &sapb.AddRateLimitOverrideRequest{Override: ov})
  4548  	test.AssertNotError(t, err, "expected successful update, got error")
  4549  
  4550  	got, err = sa.GetRateLimitOverride(ctx, &sapb.GetRateLimitOverrideRequest{LimitEnum: 1, BucketKey: expectBucketKey})
  4551  	test.AssertNotError(t, err, "expected GetRateLimitOverride to succeed, got error")
  4552  	test.AssertEquals(t, got.Override.Period.AsDuration(), 2*time.Hour)
  4553  	test.AssertEquals(t, got.Override.Count, int64(200))
  4554  	test.AssertEquals(t, got.Override.Burst, int64(200))
  4555  }
  4556  
  4557  func TestDisableEnableRateLimitOverride(t *testing.T) {
  4558  	if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
  4559  		// TODO(#8147): Remove this skip.
  4560  		t.Skip("skipping, this overrides table must exist for this test to run")
  4561  	}
  4562  
  4563  	sa, _, cleanup := initSA(t)
  4564  	defer cleanup()
  4565  
  4566  	expectBucketKey := core.RandomString(10)
  4567  	ov := &sapb.RateLimitOverride{
  4568  		LimitEnum: 2,
  4569  		BucketKey: expectBucketKey,
  4570  		Period:    durationpb.New(time.Hour),
  4571  		Count:     1,
  4572  		Burst:     1,
  4573  		Comment:   "test",
  4574  	}
  4575  	_, _ = sa.AddRateLimitOverride(ctx, &sapb.AddRateLimitOverrideRequest{Override: ov})
  4576  
  4577  	// Disable
  4578  	_, err := sa.DisableRateLimitOverride(ctx,
  4579  		&sapb.DisableRateLimitOverrideRequest{LimitEnum: 2, BucketKey: expectBucketKey})
  4580  	test.AssertNotError(t, err, "expected DisableRateLimitOverride to succeed, got error")
  4581  
  4582  	st, _ := sa.GetRateLimitOverride(ctx,
  4583  		&sapb.GetRateLimitOverrideRequest{LimitEnum: 2, BucketKey: expectBucketKey})
  4584  	test.Assert(t, !st.Enabled,
  4585  		fmt.Sprintf("expected Enabled=false after disable, got Enabled=%v", st.Enabled))
  4586  
  4587  	// Enable
  4588  	_, err = sa.EnableRateLimitOverride(ctx,
  4589  		&sapb.EnableRateLimitOverrideRequest{LimitEnum: 2, BucketKey: expectBucketKey})
  4590  	test.AssertNotError(t, err, "expected EnableRateLimitOverride to succeed, got error")
  4591  
  4592  	st, _ = sa.GetRateLimitOverride(ctx,
  4593  		&sapb.GetRateLimitOverrideRequest{LimitEnum: 2, BucketKey: expectBucketKey})
  4594  	test.Assert(t, st.Enabled,
  4595  		fmt.Sprintf("expected Enabled=true after enable, got Enabled=%v", st.Enabled))
  4596  }
  4597  
  4598  func TestGetEnabledRateLimitOverrides(t *testing.T) {
  4599  	if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
  4600  		// TODO(#8147): Remove this skip.
  4601  		t.Skip("skipping, this overrides table must exist for this test to run")
  4602  	}
  4603  
  4604  	sa, _, cleanup := initSA(t)
  4605  	defer cleanup()
  4606  
  4607  	// Enabled
  4608  	ov1 := &sapb.RateLimitOverride{
  4609  		LimitEnum: 10, BucketKey: "on", Period: durationpb.New(time.Second), Count: 1, Burst: 1, Comment: "on",
  4610  	}
  4611  	// Disabled
  4612  	ov2 := &sapb.RateLimitOverride{
  4613  		LimitEnum: 11, BucketKey: "off", Period: durationpb.New(time.Second), Count: 1, Burst: 1, Comment: "off",
  4614  	}
  4615  
  4616  	_, err := sa.AddRateLimitOverride(ctx, &sapb.AddRateLimitOverrideRequest{Override: ov1})
  4617  	test.AssertNotError(t, err, "expected successful insert of ov1, got error")
  4618  	_, err = sa.AddRateLimitOverride(ctx, &sapb.AddRateLimitOverrideRequest{Override: ov2})
  4619  	test.AssertNotError(t, err, "expected successful insert of ov2, got error")
  4620  	_, err = sa.DisableRateLimitOverride(ctx, &sapb.DisableRateLimitOverrideRequest{LimitEnum: 11, BucketKey: "off"})
  4621  	test.AssertNotError(t, err, "expected DisableRateLimitOverride of ov2 to succeed, got error")
  4622  	_, err = sa.EnableRateLimitOverride(ctx, &sapb.EnableRateLimitOverrideRequest{LimitEnum: 10, BucketKey: "on"})
  4623  	test.AssertNotError(t, err, "expected EnableRateLimitOverride of ov1 to succeed, got error")
  4624  
  4625  	stream := newMockRLOStream()
  4626  	err = sa.GetEnabledRateLimitOverrides(&emptypb.Empty{}, stream)
  4627  	test.AssertNotError(t, err, "expected streaming enabled overrides to succeed, got error")
  4628  	test.AssertEquals(t, len(stream.sent), 1)
  4629  	test.AssertEquals(t, stream.sent[0].Override.BucketKey, "on")
  4630  }