Skip to content

Commit

Permalink
Test: try with highwayhash to compare performance
Browse files Browse the repository at this point in the history
  • Loading branch information
josephschorr committed Jul 11, 2024
1 parent a75fecd commit 2308290
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 32 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ require (
github.com/gosimple/unidecode v1.0.1 // indirect
github.com/jjti/go-spancheck v0.6.1 // indirect
github.com/lasiar/canonicalheader v1.1.1 // indirect
github.com/minio/highwayhash v1.0.3 // indirect
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 // indirect
github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect
github.com/samber/slog-common v0.17.0 // indirect
Expand Down Expand Up @@ -381,7 +382,7 @@ require (
golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/term v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/tools v0.22.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1336,6 +1336,8 @@ github.com/mgechev/revive v1.3.7 h1:502QY0vQGe9KtYJ9FpxMz9rL+Fc/P13CI5POL4uHCcE=
github.com/mgechev/revive v1.3.7/go.mod h1:RJ16jUbF0OWC3co/+XTxmFNgEpUPwnnA0BRllX2aDNA=
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE=
github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q=
github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
Expand Down Expand Up @@ -1997,6 +1999,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 h1:FemxDzfMUcK2f3YY4H+05K9CDzbSVr2+q/JKN45pey0=
golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
Expand Down
39 changes: 23 additions & 16 deletions internal/datastore/proxy/relationshipintegrity.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ package proxy
import (
"context"
"crypto/hmac"
"crypto/sha256"
"fmt"
"hash"
"sync"
"time"

"google.golang.org/protobuf/types/known/timestamppb"
Expand All @@ -16,6 +14,7 @@ import (
corev1 "github.com/authzed/spicedb/pkg/proto/core/v1"
"github.com/authzed/spicedb/pkg/spiceerrors"
"github.com/authzed/spicedb/pkg/tuple"
"github.com/minio/highwayhash"
)

// KeyConfig is a configuration for a key used to sign relationships.
Expand All @@ -28,24 +27,26 @@ type KeyConfig struct {
type hmacConfig struct {
keyID string
expiredAt *time.Time
hmacPool *sync.Pool
hasher hash.Hash
}

var (
alg = sha256.New
versionByte = byte(0x01)
)

// NewRelationshipIntegrityProxy creates a new datastore proxy that ensures the integrity of
// relationships by using HMACs to sign the data. The current key is used to sign new data,
// and the expired keys are used to verify old data, if any.
func NewRelationshipIntegrityProxy(ds datastore.Datastore, currentKey KeyConfig, expiredKeys []KeyConfig) (datastore.Datastore, error) {
hasher, err := hasherForKey(currentKey.Bytes)
if err != nil {
return nil, err
}

currentKeyHMAC := hmacConfig{
keyID: currentKey.ID,
expiredAt: currentKey.ExpiredAt,
hmacPool: &sync.Pool{
New: func() any { return hmac.New(alg, currentKey.Bytes) },
},
hasher: hasher,
}

if currentKey.ExpiredAt != nil {
Expand All @@ -64,12 +65,15 @@ func NewRelationshipIntegrityProxy(ds datastore.Datastore, currentKey KeyConfig,
return nil, spiceerrors.MustBugf("found duplicate key ID: %s", key.ID)
}

hasher, err := hasherForKey(key.Bytes)
if err != nil {
return nil, err
}

keysByID[key.ID] = hmacConfig{
keyID: key.ID,
expiredAt: key.ExpiredAt,
hmacPool: &sync.Pool{
New: func() any { return hmac.New(alg, key.Bytes) },
},
hasher: hasher,
}
}

Expand All @@ -80,6 +84,10 @@ func NewRelationshipIntegrityProxy(ds datastore.Datastore, currentKey KeyConfig,
}, nil
}

func hasherForKey(key []byte) (hash.Hash, error) {
return highwayhash.New64(key)
}

type relationshipIntegrityProxy struct {
ds datastore.Datastore
primaryKey hmacConfig
Expand All @@ -97,15 +105,14 @@ func (r *relationshipIntegrityProxy) lookupKey(keyID string) (hmacConfig, error)

// computeRelationshipHash computes the HMAC hash of a relationship tuple.
func computeRelationshipHash(tpl *corev1.RelationTuple, key hmacConfig) ([]byte, error) {
hmac := key.hmacPool.Get().(hash.Hash)
defer key.hmacPool.Put(hmac)
defer hmac.Reset()
hasher := key.hasher
hasher.Reset()

bytes := tuple.MustCanonicalBytes(tpl)
if _, err := hmac.Write(bytes); err != nil {
if _, err := hasher.Write(bytes); err != nil {
return nil, err
}
return hmac.Sum(nil)[:16], nil
return hasher.Sum(nil)[:8], nil
}

func (r *relationshipIntegrityProxy) SnapshotReader(rev datastore.Revision) datastore.Reader {
Expand Down Expand Up @@ -264,7 +271,7 @@ func (r *relationshipIntegrityIterator) Next() *corev1.RelationTuple {
}

hashWithoutByte := tpl.Integrity.Hash[1:]
if tpl.Integrity.Hash[0] != versionByte || len(hashWithoutByte) != 16 {
if tpl.Integrity.Hash[0] != versionByte || len(hashWithoutByte) != 8 {
r.err = fmt.Errorf("relationship %s has invalid integrity data", tpl)
return nil
}
Expand Down
54 changes: 39 additions & 15 deletions internal/datastore/proxy/relationshipintegrity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ package proxy

import (
"context"
"crypto/hmac"
"encoding/hex"
"fmt"
"sync"
"testing"
"time"

Expand All @@ -19,19 +18,37 @@ import (
)

var defaultKeyForTesting = KeyConfig{
ID: "defaultfortest",
Bytes: []byte("somedefaultkeyfortesting"),
ID: "defaultfortest",
Bytes: (func() []byte {
b, err := hex.DecodeString("000102030405060708090A0B0C0D0E0FF0E0D0C0B0A090807060504030201000")
if err != nil {
panic(err)
}
return b
})(),
ExpiredAt: nil,
}

var toBeExpiredKeyForTesting = KeyConfig{
ID: "expiredkeyfortest",
Bytes: []byte("somexpiredkeyfortesting"),
ID: "expiredkeyfortest",
Bytes: (func() []byte {
b, err := hex.DecodeString("000102030405060708090A0B0C0D0E0FF0E0D0C0B0A090807060504030201222")
if err != nil {
panic(err)
}
return b
})(),
}

var expiredKeyForTesting = KeyConfig{
ID: "expiredkeyfortest",
Bytes: []byte("somexpiredkeyfortesting"),
ID: "expiredkeyfortest",
Bytes: (func() []byte {
b, err := hex.DecodeString("000102030405060708090A0B0C0D0E0FF0E0D0C0B0A090807060504030201222")
if err != nil {
panic(err)
}
return b
})(),
ExpiredAt: (func() *time.Time {
t, err := time.Parse("2006-01-02", "2021-01-01")
if err != nil {
Expand All @@ -42,8 +59,14 @@ var expiredKeyForTesting = KeyConfig{
}

var notYetExpiredKeyForTesting = KeyConfig{
ID: "expiredkeyfortest",
Bytes: []byte("somexpiredkeyfortesting"),
ID: "expiredkeyfortest",
Bytes: (func() []byte {
b, err := hex.DecodeString("000102030405060708090A0B0C0D0E0FF0E0D0C0B0A090807060504030201222")
if err != nil {
panic(err)
}
return b
})(),
ExpiredAt: (func() *time.Time {
t, err := time.Parse("2006-01-02", "2999-01-01")
if err != nil {
Expand Down Expand Up @@ -296,7 +319,7 @@ func TestBasicIntegrityFailureDueToInvalidHashSignature(t *testing.T) {
invalidTpl := tuple.MustParse("resource:foo#viewer@user:jimmy")
invalidTpl.Integrity = &core.RelationshipIntegrity{
KeyId: "defaultfortest",
Hash: append([]byte{0x01}, []byte("someinvalidhashaasd")[0:16]...),
Hash: append([]byte{0x01}, []byte("someinvalidhashaasd")[0:8]...),
HashedAt: timestamppb.Now(),
}

Expand Down Expand Up @@ -519,11 +542,12 @@ func BenchmarkQueryRelsWithIntegrity(b *testing.B) {
}

func BenchmarkComputeRelationshipHash(b *testing.B) {
hasher, err := hasherForKey(defaultKeyForTesting.Bytes)
require.NoError(b, err)

config := hmacConfig{
keyID: "defaultfortest",
hmacPool: &sync.Pool{
New: func() any { return hmac.New(alg, []byte("sometestbytes")) },
},
keyID: "defaultfortest",
hasher: hasher,
}

tpl := tuple.MustParse("resource:foo#viewer@user:tom")
Expand Down

0 comments on commit 2308290

Please sign in to comment.