chatdesk-ui/auth_v2.169.0/internal/utilities/hibpcache.go

77 lines
1.4 KiB
Go

package utilities
import (
"context"
"sync"
"github.com/bits-and-blooms/bloom/v3"
)
const (
// hibpHashLength is the length of a hex-encoded SHA1 hash.
hibpHashLength = 40
// hibpHashPrefixLength is the length of the hashed password prefix.
hibpHashPrefixLength = 5
)
type HIBPBloomCache struct {
sync.RWMutex
n uint
items uint
filter *bloom.BloomFilter
}
func NewHIBPBloomCache(n uint, fp float64) *HIBPBloomCache {
cache := &HIBPBloomCache{
n: n,
filter: bloom.NewWithEstimates(n, fp),
}
return cache
}
func (c *HIBPBloomCache) Cap() uint {
return c.filter.Cap()
}
func (c *HIBPBloomCache) Add(ctx context.Context, prefix []byte, suffixes [][]byte) error {
c.Lock()
defer c.Unlock()
c.items += uint(len(suffixes))
if c.items > (4*c.n)/5 {
// clear the filter if 80% full to keep the actual false
// positive rate low
c.filter.ClearAll()
// reduce memory footprint when this happens
c.filter.BitSet().Compact()
c.items = uint(len(suffixes))
}
var combined [hibpHashLength]byte
copy(combined[:], prefix)
for _, suffix := range suffixes {
copy(combined[hibpHashPrefixLength:], suffix)
c.filter.Add(combined[:])
}
return nil
}
func (c *HIBPBloomCache) Contains(ctx context.Context, prefix, suffix []byte) (bool, error) {
var combined [hibpHashLength]byte
copy(combined[:], prefix)
copy(combined[hibpHashPrefixLength:], suffix)
c.RLock()
defer c.RUnlock()
return c.filter.Test(combined[:]), nil
}