mirror of
https://github.com/hashicorp/vault.git
synced 2025-11-19 01:31:39 +01:00
* Build a better nonce service Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add internal nonce service for testing Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add benchmarks for nonce service Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add statistics around how long tidy took Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Replace ACME nonces with shared nonce service Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add an initialize method to nonce services Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Use the new initialize helper on nonce service in PKI Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add additional tests for nonces Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Format sdk/helper/nonce Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Use default 90s nonce expiry in PKI Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Remove parallel test case as covered by benchmark Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add additional commentary to encrypted nonce implementation Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Add nonce to test_packages Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> --------- Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
108 lines
2.1 KiB
Go
108 lines
2.1 KiB
Go
package nonce
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"io"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
type syncMapNonceService struct {
|
|
validity time.Duration
|
|
issued *atomic.Uint64
|
|
nextExpiry *atomic.Int64
|
|
nonces *sync.Map // map[string]time.Time
|
|
}
|
|
|
|
var _ NonceService = &syncMapNonceService{}
|
|
|
|
func newSyncMapNonceService(validity time.Duration) *syncMapNonceService {
|
|
return &syncMapNonceService{
|
|
validity: validity,
|
|
issued: new(atomic.Uint64),
|
|
nextExpiry: new(atomic.Int64),
|
|
nonces: new(sync.Map),
|
|
}
|
|
}
|
|
|
|
func (a *syncMapNonceService) Initialize() error { return nil }
|
|
func (a *syncMapNonceService) IsStrict() bool { return true }
|
|
func (a *syncMapNonceService) IsCrossNode() bool { return false }
|
|
|
|
func generateNonce() (string, error) {
|
|
return generateRandomBase64(21)
|
|
}
|
|
|
|
func generateRandomBase64(srcBytes int) (string, error) {
|
|
data := make([]byte, 21)
|
|
if _, err := io.ReadFull(rand.Reader, data); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return base64.RawURLEncoding.EncodeToString(data), nil
|
|
}
|
|
|
|
func (a *syncMapNonceService) Get() (string, time.Time, error) {
|
|
now := time.Now()
|
|
nonce, err := generateNonce()
|
|
if err != nil {
|
|
return "", now, err
|
|
}
|
|
|
|
then := now.Add(a.validity)
|
|
a.nonces.Store(nonce, then)
|
|
|
|
nextExpiry := a.nextExpiry.Load()
|
|
next := time.Unix(nextExpiry, 0)
|
|
if then.Before(next) {
|
|
a.nextExpiry.Store(then.Unix())
|
|
}
|
|
|
|
a.issued.Add(1)
|
|
|
|
return nonce, then, nil
|
|
}
|
|
|
|
func (a *syncMapNonceService) Redeem(nonce string) bool {
|
|
rawTimeout, present := a.nonces.LoadAndDelete(nonce)
|
|
if !present {
|
|
return false
|
|
}
|
|
|
|
timeout := rawTimeout.(time.Time)
|
|
if time.Now().After(timeout) {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (a *syncMapNonceService) Tidy() *NonceStatus {
|
|
now := time.Now()
|
|
nextRun := now.Add(a.validity)
|
|
var outstanding uint64
|
|
a.nonces.Range(func(key, value any) bool {
|
|
timeout := value.(time.Time)
|
|
if now.After(timeout) {
|
|
a.nonces.Delete(key)
|
|
} else {
|
|
outstanding += 1
|
|
}
|
|
|
|
if timeout.Before(nextRun) {
|
|
nextRun = timeout
|
|
}
|
|
|
|
return false /* don't quit looping */
|
|
})
|
|
|
|
a.nextExpiry.Store(nextRun.Unix())
|
|
|
|
return &NonceStatus{
|
|
Issued: a.issued.Load(),
|
|
Outstanding: outstanding,
|
|
}
|
|
}
|