github.com/letsencrypt/boulder@v0.20251208.0/nonce/nonce_test.go (about)

     1  package nonce
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/prometheus/client_golang/prometheus"
     8  
     9  	"github.com/letsencrypt/boulder/metrics"
    10  	"github.com/letsencrypt/boulder/test"
    11  )
    12  
    13  func TestValidNonce(t *testing.T) {
    14  	ns, err := NewNonceService(metrics.NoopRegisterer, 0, "")
    15  	test.AssertNotError(t, err, "Could not create nonce service")
    16  	n, err := ns.nonce()
    17  	test.AssertNotError(t, err, "Could not create nonce")
    18  	test.AssertNotError(t, ns.valid(n), fmt.Sprintf("Did not recognize fresh nonce %s", n))
    19  	test.AssertMetricWithLabelsEquals(t, ns.nonceRedeems, prometheus.Labels{
    20  		"result": "valid", "error": "",
    21  	}, 1)
    22  	test.AssertHistogramBucketCount(t, ns.nonceAges, prometheus.Labels{
    23  		"result": "valid",
    24  	}, 0, 1)
    25  }
    26  
    27  func TestAlreadyUsed(t *testing.T) {
    28  	ns, err := NewNonceService(metrics.NoopRegisterer, 0, "")
    29  	test.AssertNotError(t, err, "Could not create nonce service")
    30  	n, err := ns.nonce()
    31  	test.AssertNotError(t, err, "Could not create nonce")
    32  	test.AssertNotError(t, ns.valid(n), "Did not recognize fresh nonce")
    33  	test.AssertError(t, ns.valid(n), "Recognized the same nonce twice")
    34  	test.AssertMetricWithLabelsEquals(t, ns.nonceRedeems, prometheus.Labels{
    35  		"result": "invalid", "error": "already used",
    36  	}, 1)
    37  	test.AssertHistogramBucketCount(t, ns.nonceAges, prometheus.Labels{
    38  		"result": "invalid",
    39  	}, 0, 1)
    40  }
    41  
    42  func TestRejectMalformed(t *testing.T) {
    43  	ns, err := NewNonceService(metrics.NoopRegisterer, 0, "")
    44  	test.AssertNotError(t, err, "Could not create nonce service")
    45  	n, err := ns.nonce()
    46  	test.AssertNotError(t, err, "Could not create nonce")
    47  	test.AssertError(t, ns.valid("asdf"+n), "Accepted an invalid nonce")
    48  	test.AssertMetricWithLabelsEquals(t, ns.nonceRedeems, prometheus.Labels{
    49  		"result": "invalid", "error": "decrypt",
    50  	}, 1)
    51  }
    52  
    53  func TestRejectShort(t *testing.T) {
    54  	ns, err := NewNonceService(metrics.NoopRegisterer, 0, "")
    55  	test.AssertNotError(t, err, "Could not create nonce service")
    56  	test.AssertError(t, ns.valid("aGkK"), "Accepted an invalid nonce")
    57  	test.AssertMetricWithLabelsEquals(t, ns.nonceRedeems, prometheus.Labels{
    58  		"result": "invalid", "error": "decrypt",
    59  	}, 1)
    60  }
    61  
    62  func TestRejectUnknown(t *testing.T) {
    63  	ns1, err := NewNonceService(metrics.NoopRegisterer, 0, "")
    64  	test.AssertNotError(t, err, "Could not create nonce service")
    65  	ns2, err := NewNonceService(metrics.NoopRegisterer, 0, "")
    66  	test.AssertNotError(t, err, "Could not create nonce service")
    67  
    68  	n, err := ns1.nonce()
    69  	test.AssertNotError(t, err, "Could not create nonce")
    70  	test.AssertError(t, ns2.valid(n), "Accepted a foreign nonce")
    71  	test.AssertMetricWithLabelsEquals(t, ns2.nonceRedeems, prometheus.Labels{
    72  		"result": "invalid", "error": "decrypt",
    73  	}, 1)
    74  }
    75  
    76  func TestRejectTooLate(t *testing.T) {
    77  	ns, err := NewNonceService(metrics.NoopRegisterer, 0, "")
    78  	test.AssertNotError(t, err, "Could not create nonce service")
    79  
    80  	ns.latest = 2
    81  	n, err := ns.nonce()
    82  	test.AssertNotError(t, err, "Could not create nonce")
    83  	ns.latest = 1
    84  	test.AssertError(t, ns.valid(n), "Accepted a nonce with a too-high counter")
    85  	test.AssertMetricWithLabelsEquals(t, ns.nonceRedeems, prometheus.Labels{
    86  		"result": "invalid", "error": "too high",
    87  	}, 1)
    88  	test.AssertHistogramBucketCount(t, ns.nonceAges, prometheus.Labels{
    89  		"result": "invalid",
    90  	}, -1, 1)
    91  }
    92  
    93  func TestRejectTooEarly(t *testing.T) {
    94  	// Use a very low value for maxUsed so the loop below can be short.
    95  	ns, err := NewNonceService(metrics.NoopRegisterer, 2, "")
    96  	test.AssertNotError(t, err, "Could not create nonce service")
    97  
    98  	n, err := ns.nonce()
    99  	test.AssertNotError(t, err, "Could not create nonce")
   100  
   101  	// Generate and redeem enough nonces to surpass maxUsed, forcing the nonce
   102  	// service to move ns.earliest upwards, invalidating n.
   103  	for range ns.maxUsed + 1 {
   104  		n, err := ns.nonce()
   105  		test.AssertNotError(t, err, "Could not create nonce")
   106  		test.AssertNotError(t, ns.valid(n), "Rejected a valid nonce")
   107  	}
   108  
   109  	test.AssertError(t, ns.valid(n), "Accepted a nonce that we should have forgotten")
   110  	test.AssertMetricWithLabelsEquals(t, ns.nonceRedeems, prometheus.Labels{
   111  		"result": "invalid", "error": "too low",
   112  	}, 1)
   113  	test.AssertHistogramBucketCount(t, ns.nonceAges, prometheus.Labels{
   114  		"result": "invalid",
   115  	}, 1.5, 1)
   116  }
   117  
   118  func TestNonceMetrics(t *testing.T) {
   119  	// Use a low value for maxUsed so the loop below can be short.
   120  	ns, err := NewNonceService(metrics.NoopRegisterer, 2, "")
   121  	test.AssertNotError(t, err, "Could not create nonce service")
   122  
   123  	// After issuing (but not redeeming) many nonces, the latest should have
   124  	// increased by the same amount and the earliest should have moved at all.
   125  	var nonces []string
   126  	for range 10 * ns.maxUsed {
   127  		n, err := ns.nonce()
   128  		test.AssertNotError(t, err, "Could not create nonce")
   129  		nonces = append(nonces, n)
   130  	}
   131  	test.AssertMetricWithLabelsEquals(t, ns.nonceEarliest, nil, 0)
   132  	test.AssertMetricWithLabelsEquals(t, ns.nonceLatest, nil, 20)
   133  
   134  	// Redeeming maxUsed nonces shouldn't cause either metric to change, because
   135  	// no redeemed nonces have been dropped from the used heap yet.
   136  	test.AssertNotError(t, ns.valid(nonces[0]), "Rejected a valid nonce")
   137  	test.AssertNotError(t, ns.valid(nonces[1]), "Rejected a valid nonce")
   138  	test.AssertMetricWithLabelsEquals(t, ns.nonceEarliest, nil, 0)
   139  	test.AssertMetricWithLabelsEquals(t, ns.nonceLatest, nil, 20)
   140  
   141  	// Redeeming one more nonce should cause the earliest to move forward one, as
   142  	// the earliest redeemed nonce is popped from the heap.
   143  	test.AssertNotError(t, ns.valid(nonces[2]), "Rejected a valid nonce")
   144  	test.AssertMetricWithLabelsEquals(t, ns.nonceEarliest, nil, 1)
   145  	test.AssertMetricWithLabelsEquals(t, ns.nonceLatest, nil, 20)
   146  
   147  	// Redeeming maxUsed+1 much later nonces should cause the earliest to skip
   148  	// forward to the first of those.
   149  	test.AssertNotError(t, ns.valid(nonces[17]), "Rejected a valid nonce")
   150  	test.AssertNotError(t, ns.valid(nonces[18]), "Rejected a valid nonce")
   151  	test.AssertNotError(t, ns.valid(nonces[19]), "Rejected a valid nonce")
   152  	test.AssertMetricWithLabelsEquals(t, ns.nonceEarliest, nil, 18)
   153  	test.AssertMetricWithLabelsEquals(t, ns.nonceLatest, nil, 20)
   154  }
   155  
   156  func BenchmarkNonces(b *testing.B) {
   157  	ns, err := NewNonceService(metrics.NoopRegisterer, 0, "")
   158  	if err != nil {
   159  		b.Fatal("creating nonce service", err)
   160  	}
   161  
   162  	for range ns.maxUsed {
   163  		n, err := ns.nonce()
   164  		if err != nil {
   165  			b.Fatal("noncing", err)
   166  		}
   167  		if ns.valid(n) != nil {
   168  			b.Fatal("generated invalid nonce")
   169  		}
   170  	}
   171  
   172  	b.ResetTimer()
   173  	b.RunParallel(func(pb *testing.PB) {
   174  		for pb.Next() {
   175  			n, err := ns.nonce()
   176  			if err != nil {
   177  				b.Fatal("noncing", err)
   178  			}
   179  			if ns.valid(n) != nil {
   180  				b.Fatal("generated invalid nonce")
   181  			}
   182  		}
   183  	})
   184  }
   185  
   186  func TestNoncePrefixing(t *testing.T) {
   187  	ns, err := NewNonceService(metrics.NoopRegisterer, 0, "aluminum")
   188  	test.AssertNotError(t, err, "Could not create nonce service")
   189  
   190  	n, err := ns.nonce()
   191  	test.AssertNotError(t, err, "Could not create nonce")
   192  	test.AssertNotError(t, ns.valid(n), "Valid nonce rejected")
   193  
   194  	n, err = ns.nonce()
   195  	test.AssertNotError(t, err, "Could not create nonce")
   196  	n = n[1:]
   197  	test.AssertError(t, ns.valid(n), "Valid nonce with incorrect prefix accepted")
   198  
   199  	n, err = ns.nonce()
   200  	test.AssertNotError(t, err, "Could not create nonce")
   201  	test.AssertError(t, ns.valid(n[6:]), "Valid nonce without prefix accepted")
   202  }
   203  
   204  func TestNoncePrefixValidation(t *testing.T) {
   205  	_, err := NewNonceService(metrics.NoopRegisterer, 0, "whatsup")
   206  	test.AssertError(t, err, "NewNonceService didn't fail with short prefix")
   207  	_, err = NewNonceService(metrics.NoopRegisterer, 0, "whatsup!")
   208  	test.AssertError(t, err, "NewNonceService didn't fail with invalid base64")
   209  	_, err = NewNonceService(metrics.NoopRegisterer, 0, "whatsupp")
   210  	test.AssertNotError(t, err, "NewNonceService failed with valid nonce prefix")
   211  }
   212  
   213  func TestDerivePrefix(t *testing.T) {
   214  	prefix := DerivePrefix("192.168.1.1:8080", []byte("3b8c758dd85e113ea340ce0b3a99f389d40a308548af94d1730a7692c1874f1f"))
   215  	test.AssertEquals(t, prefix, "P9qQaK4o")
   216  }