diff --git a/curves/bls12381/init.go b/curves/bls12381/init.go
index a0a0e23..6a4fe26 100644
--- a/curves/bls12381/init.go
+++ b/curves/bls12381/init.go
@@ -6,8 +6,9 @@ import (
 	"fmt"
 	"runtime"
 
-	"github.com/cometbft/cometbft/crypto/bls/cache"
 	blst "github.com/supranational/blst/bindings/go"
+
+	"github.com/cosmos/crypto/utils/cache"
 )
 
 func init() {
diff --git a/curves/bls12381/pubkey.go b/curves/bls12381/pubkey.go
index 1336bdf..1643f36 100644
--- a/curves/bls12381/pubkey.go
+++ b/curves/bls12381/pubkey.go
@@ -6,7 +6,7 @@ import (
 	"errors"
 	"fmt"
 
-	"github.com/cometbft/cometbft/crypto/bls/cache"
+	"github.com/cosmos/crypto/utils/cache"
 )
 
 const (
diff --git a/curves/bls12381/pubkey_test.go b/curves/bls12381/pubkey_test.go
index 0926b72..fd656ea 100644
--- a/curves/bls12381/pubkey_test.go
+++ b/curves/bls12381/pubkey_test.go
@@ -10,7 +10,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/cometbft/cometbft/crypto/bls/blst"
+	blst "github.com/cosmos/crypto/curves/bls12381"
 )
 
 func TestPublicKeyFromBytes(t *testing.T) {
diff --git a/curves/bls12381/secret_key.go b/curves/bls12381/secret_key.go
index 0679995..5585c85 100644
--- a/curves/bls12381/secret_key.go
+++ b/curves/bls12381/secret_key.go
@@ -7,9 +7,9 @@ import (
 	"errors"
 	"fmt"
 
-	"github.com/cometbft/cometbft/crypto/bls/rand"
-
 	blst "github.com/supranational/blst/bindings/go"
+
+	"github.com/cosmos/crypto/utils/rand"
 )
 
 // bls12SecretKey used in the BLS signature scheme.
diff --git a/curves/bls12381/secret_key_test.go b/curves/bls12381/secret_key_test.go
index aa808d6..bfea916 100644
--- a/curves/bls12381/secret_key_test.go
+++ b/curves/bls12381/secret_key_test.go
@@ -11,7 +11,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/cometbft/cometbft/crypto/bls/blst"
+	blst "github.com/cosmos/crypto/curves/bls12381"
 )
 
 func TestMarshalUnmarshal(t *testing.T) {
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..9e62890
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,14 @@
+module github.com/cosmos/crypto
+
+go 1.21.8
+
+require (
+	github.com/stretchr/testify v1.9.0
+	github.com/supranational/blst v0.3.11
+)
+
+require (
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..69ab4d1
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,12 @@
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4=
+github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/utils/cache/cache.go b/utils/cache/cache.go
new file mode 100644
index 0000000..066e8b8
--- /dev/null
+++ b/utils/cache/cache.go
@@ -0,0 +1,144 @@
+package cache
+
+import (
+	"errors"
+	"sync"
+)
+
+// EvictCallback is used to get a callback when a cache entry is evicted.
+type EvictCallback[K comparable, V any] func(key K, value V)
+
+// LRU implements a non-thread safe fixed size LRU cache.
+type LRU[K comparable, V any] struct {
+	itemsLock     sync.RWMutex
+	evictListLock sync.RWMutex
+	size          int
+	evictList     *lruList[K, V]
+	items         map[K]*entry[K, V]
+	onEvict       EvictCallback[K, V]
+	getChan       chan *entry[K, V]
+}
+
+// NewLRU constructs an LRU of the given size.
+func NewLRU[K comparable, V any](size int, onEvict EvictCallback[K, V]) (*LRU[K, V], error) {
+	if size <= 0 {
+		return nil, errors.New("must provide a positive size")
+	}
+	// Initialize the channel buffer size as being 10% of the cache size.
+	chanSize := size / 10
+
+	c := &LRU[K, V]{
+		size:      size,
+		evictList: newList[K, V](),
+		items:     make(map[K]*entry[K, V]),
+		onEvict:   onEvict,
+		getChan:   make(chan *entry[K, V], chanSize),
+	}
+	// Spin off separate go-routine to handle evict list
+	// operations.
+	go c.handleGetRequests()
+	return c, nil
+}
+
+// Add adds a value to the cache. Returns true if an eviction occurred.
+func (c *LRU[K, V]) Add(key K, value V) (evicted bool) {
+	// Check for existing item
+	c.itemsLock.RLock()
+	if ent, ok := c.items[key]; ok {
+		c.itemsLock.RUnlock()
+
+		c.evictListLock.Lock()
+		c.evictList.moveToFront(ent)
+		c.evictListLock.Unlock()
+		ent.value = value
+		return false
+	}
+	c.itemsLock.RUnlock()
+
+	// Add new item
+	c.evictListLock.Lock()
+	ent := c.evictList.pushFront(key, value)
+	c.evictListLock.Unlock()
+
+	c.itemsLock.Lock()
+	c.items[key] = ent
+	c.itemsLock.Unlock()
+
+	c.evictListLock.RLock()
+	evict := c.evictList.length() > c.size
+	c.evictListLock.RUnlock()
+
+	// Verify size not exceeded
+	if evict {
+		c.removeOldest()
+	}
+	return evict
+}
+
+// Get looks up a key's value from the cache.
+func (c *LRU[K, V]) Get(key K) (value V, ok bool) {
+	c.itemsLock.RLock()
+	if ent, ok := c.items[key]; ok {
+		c.itemsLock.RUnlock()
+
+		// Make this get function non-blocking for multiple readers.
+		c.getChan <- ent
+		return ent.value, true
+	}
+	c.itemsLock.RUnlock()
+	return
+}
+
+// Len returns the number of items in the cache.
+func (c *LRU[K, V]) Len() int {
+	c.evictListLock.RLock()
+	defer c.evictListLock.RUnlock()
+	return c.evictList.length()
+}
+
+// Resize changes the cache size.
+func (c *LRU[K, V]) Resize(size int) (evicted int) {
+	diff := c.Len() - size
+	if diff < 0 {
+		diff = 0
+	}
+	for i := 0; i < diff; i++ {
+		c.removeOldest()
+	}
+	c.size = size
+	return diff
+}
+
+// removeOldest removes the oldest item from the cache.
+func (c *LRU[K, V]) removeOldest() {
+	c.evictListLock.RLock()
+	if ent := c.evictList.back(); ent != nil {
+		c.evictListLock.RUnlock()
+		c.removeElement(ent)
+		return
+	}
+	c.evictListLock.RUnlock()
+}
+
+// removeElement is used to remove a given list element from the cache.
+func (c *LRU[K, V]) removeElement(e *entry[K, V]) {
+	c.evictListLock.Lock()
+	c.evictList.remove(e)
+	c.evictListLock.Unlock()
+
+	c.itemsLock.Lock()
+	delete(c.items, e.key)
+	c.itemsLock.Unlock()
+	if c.onEvict != nil {
+		c.onEvict(e.key, e.value)
+	}
+}
+
+func (c *LRU[K, V]) handleGetRequests() {
+	for {
+		entry := <-c.getChan
+		c.evictListLock.Lock()
+		c.evictList.moveToFront(entry)
+		c.evictListLock.Unlock()
+	}
+}
diff --git a/utils/cache/list.go b/utils/cache/list.go
new file mode 100644
index 0000000..5bf4f56
--- /dev/null
+++ b/utils/cache/list.go
@@ -0,0 +1,123 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE_list file.
+package cache
+
+// entry is an LRU entry.
+type entry[K comparable, V any] struct {
+	// Next and previous pointers in the doubly-linked list of elements.
+	// To simplify the implementation, internally a list l is implemented
+	// as a ring, such that &l.root is both the next element of the last
+	// list element (l.Back()) and the previous element of the first list
+	// element (l.Front()).
+	next, prev *entry[K, V]
+
+	// The list to which this element belongs.
+	list *lruList[K, V]
+
+	// The LRU key of this element.
+	key K
+
+	// The value stored with this element.
+	value V
+}
+
+// lruList represents a doubly linked list.
+// The zero value for lruList is an empty list ready to use.
+type lruList[K comparable, V any] struct {
+	root entry[K, V] // sentinel list element, only &root, root.prev, and root.next are used
+	len  int         // current list length excluding (this) sentinel element
+}
+
+// init initializes or clears list l.
+func (l *lruList[K, V]) init() *lruList[K, V] {
+	l.root.next = &l.root
+	l.root.prev = &l.root
+	l.len = 0
+	return l
+}
+
+// newList returns an initialized list.
+func newList[K comparable, V any]() *lruList[K, V] { return new(lruList[K, V]).init() }
+
+// length returns the number of elements of list l.
+// The complexity is O(1).
+func (l *lruList[K, V]) length() int { return l.len }
+
+// back returns the last element of list l or nil if the list is empty.
+func (l *lruList[K, V]) back() *entry[K, V] {
+	if l.len == 0 {
+		return nil
+	}
+	return l.root.prev
+}
+
+// lazyInit lazily initializes a zero List value.
+func (l *lruList[K, V]) lazyInit() {
+	if l.root.next == nil {
+		l.init()
+	}
+}
+
+// insert inserts e after at, increments l.len, and returns e.
+func (l *lruList[K, V]) insert(e, at *entry[K, V]) *entry[K, V] {
+	e.prev = at
+	e.next = at.next
+	e.prev.next = e
+	e.next.prev = e
+	e.list = l
+	l.len++
+	return e
+}
+
+// insertValue is a convenience wrapper for insert(&Element{Value: v}, at).
+func (l *lruList[K, V]) insertValue(k K, v V, at *entry[K, V]) *entry[K, V] {
+	return l.insert(&entry[K, V]{value: v, key: k}, at)
+}
+
+// remove removes e from its list, decrements l.len.
+func (l *lruList[K, V]) remove(e *entry[K, V]) V {
+	// If already removed, do nothing.
+	if e.prev == nil && e.next == nil {
+		return e.value
+	}
+	e.prev.next = e.next
+	e.next.prev = e.prev
+	e.next = nil // avoid memory leaks
+	e.prev = nil // avoid memory leaks
+	e.list = nil
+	l.len--
+
+	return e.value
+}
+
+// move moves e to next to at.
+func (*lruList[K, V]) move(e, at *entry[K, V]) {
+	if e == at {
+		return
+	}
+	e.prev.next = e.next
+	e.next.prev = e.prev
+
+	e.prev = at
+	e.next = at.next
+	e.prev.next = e
+	e.next.prev = e
+}
+
+// pushFront inserts a new element e with value v at the front of list l and returns e.
+func (l *lruList[K, V]) pushFront(k K, v V) *entry[K, V] {
+	l.lazyInit()
+	return l.insertValue(k, v, &l.root)
+}
+
+// moveToFront moves element e to the front of list l.
+// If e is not an element of l, the list is not modified.
+// The element must not be nil.
+func (l *lruList[K, V]) moveToFront(e *entry[K, V]) {
+	if e.list != l || l.root.next == e {
+		return
+	}
+	// see comment in List.Remove about initialization of l
+	l.move(e, &l.root)
+}
diff --git a/utils/rand/rand.go b/utils/rand/rand.go
new file mode 100644
index 0000000..3fe4a94
--- /dev/null
+++ b/utils/rand/rand.go
@@ -0,0 +1,45 @@
+package rand
+
+import (
+	"crypto/rand"
+	"encoding/binary"
+	mrand "math/rand"
+	"sync"
+)
+
+type source struct{}
+
+var lock sync.RWMutex
+var _ mrand.Source64 = (*source)(nil) // #nosec G404 -- This ensures we meet the interface
+
+// Seed does nothing when crypto/rand is used as source.
+func (_ *source) Seed(_ int64) {}
+
+// Int63 returns uniformly-distributed random (as in CSPRNG) int64 value within [0, 1<<63) range.
+// Panics if random generator reader cannot return data.
+func (s *source) Int63() int64 {
+	return int64(s.Uint64() & ^uint64(1<<63))
+}
+
+// Uint64 returns uniformly-distributed random (as in CSPRNG) uint64 value within [0, 1<<64) range.
+// Panics if random generator reader cannot return data.
+func (_ *source) Uint64() (val uint64) {
+	lock.RLock()
+	defer lock.RUnlock()
+	if err := binary.Read(rand.Reader, binary.BigEndian, &val); err != nil {
+		panic(err)
+	}
+	return
+}
+
+// Rand is alias for underlying random generator.
+type Rand = mrand.Rand // #nosec G404
+
+// NewGenerator returns a new generator that uses random values from crypto/rand as a source
+// (cryptographically secure random number generator).
+// Panics if crypto/rand input cannot be read.
+// Use it for everything where crypto secure non-deterministic randomness is required. Performance
+// takes a hit, so use sparingly.
+func NewGenerator() *Rand {
+	return mrand.New(&source{}) // #nosec G404 -- excluded
+}