mirror of
https://github.com/slackhq/nebula.git
synced 2025-11-09 21:53:59 +01:00
Cache cert verification methods (#871)
* cache cert verification CheckSignature and Verify are expensive methods, and certificates are static. Cache the results. * use atomics * make sure public key bytes match * add VerifyWithCache and ResetCache * cleanup * use VerifyWithCache * doc
This commit is contained in:
parent
eb9f22a8fa
commit
9a7ed57a3f
10
cert/ca.go
10
cert/ca.go
@ -91,9 +91,15 @@ func (ncp *NebulaCAPool) ResetCertBlocklist() {
|
|||||||
ncp.certBlocklist = make(map[string]struct{})
|
ncp.certBlocklist = make(map[string]struct{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsBlocklisted returns true if the fingerprint fails to generate or has been explicitly blocklisted
|
// NOTE: This uses an internal cache for Sha256Sum() that will not be invalidated
|
||||||
|
// automatically if you manually change any fields in the NebulaCertificate.
|
||||||
func (ncp *NebulaCAPool) IsBlocklisted(c *NebulaCertificate) bool {
|
func (ncp *NebulaCAPool) IsBlocklisted(c *NebulaCertificate) bool {
|
||||||
h, err := c.Sha256Sum()
|
return ncp.isBlocklistedWithCache(c, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBlocklisted returns true if the fingerprint fails to generate or has been explicitly blocklisted
|
||||||
|
func (ncp *NebulaCAPool) isBlocklistedWithCache(c *NebulaCertificate, useCache bool) bool {
|
||||||
|
h, err := c.sha256SumWithCache(useCache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
72
cert/cert.go
72
cert/cert.go
@ -17,6 +17,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/curve25519"
|
"golang.org/x/crypto/curve25519"
|
||||||
@ -42,6 +43,14 @@ const (
|
|||||||
type NebulaCertificate struct {
|
type NebulaCertificate struct {
|
||||||
Details NebulaCertificateDetails
|
Details NebulaCertificateDetails
|
||||||
Signature []byte
|
Signature []byte
|
||||||
|
|
||||||
|
// the cached hex string of the calculated sha256sum
|
||||||
|
// for VerifyWithCache
|
||||||
|
sha256sum atomic.Pointer[string]
|
||||||
|
|
||||||
|
// the cached public key bytes if they were verified as the signer
|
||||||
|
// for VerifyWithCache
|
||||||
|
signatureVerified atomic.Pointer[[]byte]
|
||||||
}
|
}
|
||||||
|
|
||||||
type NebulaCertificateDetails struct {
|
type NebulaCertificateDetails struct {
|
||||||
@ -562,6 +571,27 @@ func (nc *NebulaCertificate) CheckSignature(key []byte) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: This uses an internal cache that will not be invalidated automatically
|
||||||
|
// if you manually change any fields in the NebulaCertificate.
|
||||||
|
func (nc *NebulaCertificate) checkSignatureWithCache(key []byte, useCache bool) bool {
|
||||||
|
if !useCache {
|
||||||
|
return nc.CheckSignature(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := nc.signatureVerified.Load(); v != nil {
|
||||||
|
return bytes.Equal(*v, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
verified := nc.CheckSignature(key)
|
||||||
|
if verified {
|
||||||
|
keyCopy := make([]byte, len(key))
|
||||||
|
copy(keyCopy, key)
|
||||||
|
nc.signatureVerified.Store(&keyCopy)
|
||||||
|
}
|
||||||
|
|
||||||
|
return verified
|
||||||
|
}
|
||||||
|
|
||||||
// Expired will return true if the nebula cert is too young or too old compared to the provided time, otherwise false
|
// Expired will return true if the nebula cert is too young or too old compared to the provided time, otherwise false
|
||||||
func (nc *NebulaCertificate) Expired(t time.Time) bool {
|
func (nc *NebulaCertificate) Expired(t time.Time) bool {
|
||||||
return nc.Details.NotBefore.After(t) || nc.Details.NotAfter.Before(t)
|
return nc.Details.NotBefore.After(t) || nc.Details.NotAfter.Before(t)
|
||||||
@ -569,7 +599,26 @@ func (nc *NebulaCertificate) Expired(t time.Time) bool {
|
|||||||
|
|
||||||
// Verify will ensure a certificate is good in all respects (expiry, group membership, signature, cert blocklist, etc)
|
// Verify will ensure a certificate is good in all respects (expiry, group membership, signature, cert blocklist, etc)
|
||||||
func (nc *NebulaCertificate) Verify(t time.Time, ncp *NebulaCAPool) (bool, error) {
|
func (nc *NebulaCertificate) Verify(t time.Time, ncp *NebulaCAPool) (bool, error) {
|
||||||
if ncp.IsBlocklisted(nc) {
|
return nc.verify(t, ncp, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyWithCache will ensure a certificate is good in all respects (expiry, group membership, signature, cert blocklist, etc)
|
||||||
|
//
|
||||||
|
// NOTE: This uses an internal cache that will not be invalidated automatically
|
||||||
|
// if you manually change any fields in the NebulaCertificate.
|
||||||
|
func (nc *NebulaCertificate) VerifyWithCache(t time.Time, ncp *NebulaCAPool) (bool, error) {
|
||||||
|
return nc.verify(t, ncp, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetCache resets the cache used by VerifyWithCache.
|
||||||
|
func (nc *NebulaCertificate) ResetCache() {
|
||||||
|
nc.sha256sum.Store(nil)
|
||||||
|
nc.signatureVerified.Store(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify will ensure a certificate is good in all respects (expiry, group membership, signature, cert blocklist, etc)
|
||||||
|
func (nc *NebulaCertificate) verify(t time.Time, ncp *NebulaCAPool, useCache bool) (bool, error) {
|
||||||
|
if ncp.isBlocklistedWithCache(nc, useCache) {
|
||||||
return false, ErrBlockListed
|
return false, ErrBlockListed
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -586,7 +635,7 @@ func (nc *NebulaCertificate) Verify(t time.Time, ncp *NebulaCAPool) (bool, error
|
|||||||
return false, ErrExpired
|
return false, ErrExpired
|
||||||
}
|
}
|
||||||
|
|
||||||
if !nc.CheckSignature(signer.Details.PublicKey) {
|
if !nc.checkSignatureWithCache(signer.Details.PublicKey, useCache) {
|
||||||
return false, ErrSignatureMismatch
|
return false, ErrSignatureMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -809,6 +858,25 @@ func (nc *NebulaCertificate) Sha256Sum() (string, error) {
|
|||||||
return hex.EncodeToString(sum[:]), nil
|
return hex.EncodeToString(sum[:]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: This uses an internal cache that will not be invalidated automatically
|
||||||
|
// if you manually change any fields in the NebulaCertificate.
|
||||||
|
func (nc *NebulaCertificate) sha256SumWithCache(useCache bool) (string, error) {
|
||||||
|
if !useCache {
|
||||||
|
return nc.Sha256Sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
if s := nc.sha256sum.Load(); s != nil {
|
||||||
|
return *s, nil
|
||||||
|
}
|
||||||
|
s, err := nc.Sha256Sum()
|
||||||
|
if err != nil {
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nc.sha256sum.Store(&s)
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (nc *NebulaCertificate) MarshalJSON() ([]byte, error) {
|
func (nc *NebulaCertificate) MarshalJSON() ([]byte, error) {
|
||||||
toString := func(ips []*net.IPNet) []string {
|
toString := func(ips []*net.IPNet) []string {
|
||||||
s := []string{}
|
s := []string{}
|
||||||
|
|||||||
@ -427,7 +427,7 @@ func (n *connectionManager) isInvalidCertificate(now time.Time, hostinfo *HostIn
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
valid, err := remoteCert.Verify(now, n.intf.caPool)
|
valid, err := remoteCert.VerifyWithCache(now, n.intf.caPool)
|
||||||
if valid {
|
if valid {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user