From 7a680801c7ffaf66d5a791209e2704374418c857 Mon Sep 17 00:00:00 2001 From: Madhur Shrimal Date: Wed, 8 Jan 2025 09:55:15 -0800 Subject: [PATCH 1/6] feat: implement g1 sign api --- go.mod | 2 +- go.sum | 4 ++-- internal/services/signing/signing.go | 27 +++++++++++++++++++++++ internal/services/signing/signing_test.go | 26 ++++++++++++++++++++++ 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 28db7f5..87527af 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.22.3 require ( cloud.google.com/go/secretmanager v1.14.2 github.com/Layr-Labs/bn254-keystore-go v0.0.0-20250107020618-26bd412fae87 - github.com/Layr-Labs/cerberus-api v0.0.2-0.20250107174124-05df6050f723 + github.com/Layr-Labs/cerberus-api v0.0.2-0.20250108174619-d5e1eb03fbd5 github.com/aws/aws-sdk-go-v2 v1.32.5 github.com/aws/aws-sdk-go-v2/config v1.28.5 github.com/aws/aws-sdk-go-v2/credentials v1.17.46 diff --git a/go.sum b/go.sum index 0dd9863..33356a2 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg6 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Layr-Labs/bn254-keystore-go v0.0.0-20250107020618-26bd412fae87 h1:EkaBNT0o8RTgtFeYSKaoNHNbnCVxrcsAyRpUeN29hiQ= github.com/Layr-Labs/bn254-keystore-go v0.0.0-20250107020618-26bd412fae87/go.mod h1:7J8hptSX8cFq7KmVb+rEO5aEifj7E44c3i0afIyr4WA= -github.com/Layr-Labs/cerberus-api v0.0.2-0.20250107174124-05df6050f723 h1:f6gJS/egys133nGcOGKduiPHq9hyK9KRiEB9fARB5t0= -github.com/Layr-Labs/cerberus-api v0.0.2-0.20250107174124-05df6050f723/go.mod h1:Lm4fhzy0S3P7GjerzuseGaBFVczsIKmEhIjcT52Hluo= +github.com/Layr-Labs/cerberus-api v0.0.2-0.20250108174619-d5e1eb03fbd5 h1:s24M6HYObEuV9OSY36jUM09kp5fOhuz/g1ev2qWDPzU= +github.com/Layr-Labs/cerberus-api v0.0.2-0.20250108174619-d5e1eb03fbd5/go.mod h1:Lm4fhzy0S3P7GjerzuseGaBFVczsIKmEhIjcT52Hluo= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/aws/aws-sdk-go-v2 v1.32.5 h1:U8vdWJuY7ruAkzaOdD7guwJjD06YSKmnKCJs7s3IkIo= diff --git a/internal/services/signing/signing.go b/internal/services/signing/signing.go index c55fcea..fa421e9 100644 --- a/internal/services/signing/signing.go +++ b/internal/services/signing/signing.go @@ -73,3 +73,30 @@ func (s *Service) SignGeneric( s.logger.Info(fmt.Sprintf("Signed a message successfully using %s", pubKeyHex)) return &v1.SignGenericResponse{Signature: sig.Serialize()}, nil } + +func (s *Service) SignG1( + ctx context.Context, + req *v1.SignG1Request, +) (*v1.SignG1Response, error) { + // Take the public key and data from the request + pubKeyHex := common.Trim0x(req.GetPublicKeyG1()) + password := req.GetPassword() + + if _, ok := s.keyCache[pubKeyHex]; !ok { + s.logger.Info(fmt.Sprintf("In memory cache miss. Retrieving key for %s", pubKeyHex)) + blsKey, err := s.store.RetrieveKey(ctx, pubKeyHex, password) + if err != nil { + s.logger.Error(fmt.Sprintf("Failed to retrieve key: %v", err)) + return nil, status.Error(codes.Internal, err.Error()) + } + s.keyCache[pubKeyHex] = blsKey + } + blsKey := s.keyCache[pubKeyHex] + + g1Bytes := req.GetData() + g1Point := new(crypto.G1Point) + g1Point = g1Point.Deserialize(g1Bytes) + + sig := blsKey.SignHashedToCurveMessage(g1Point.G1Affine) + return &v1.SignG1Response{Signature: sig.Serialize()}, nil +} diff --git a/internal/services/signing/signing_test.go b/internal/services/signing/signing_test.go index b171c15..378a4c7 100644 --- a/internal/services/signing/signing_test.go +++ b/internal/services/signing/signing_test.go @@ -40,3 +40,29 @@ func TestSigning(t *testing.T) { assert.NoError(t, err) assert.Equal(t, expectedSig, hex.EncodeToString(resp.Signature)) } + +func TestSigningG1(t *testing.T) { + // private key: 0x040ad69253b921aca71dd714cccc3095576fbe1a21f86c9b10cb5b119b1c6899 + pubKeyHex := "a3111a2232584734d526d62cbb7c9a0d4ce1984a92b7ecb85bde8878fea5d1b0" + password := "p@$$w0rd" + expectedSig := "24a87f9eab63a40c62831d2e9598e698f8819b15093c268b89c1a521f7d986650000000000000000000000000000000000000000000000000000000000000000" + data := []byte("somedata") + var bytes [64]byte + copy(bytes[:], data) + + config := &configuration.Configuration{ + KeystoreDir: "testdata/keystore", + } + logger := testutils.GetTestLogger() + store := filesystem.NewStore(config.KeystoreDir, logger) + m := metrics.NewNoopRPCMetrics() + signingService := NewService(config, store, logger, m) + + resp, err := signingService.SignG1(context.Background(), &v1.SignG1Request{ + PublicKeyG1: pubKeyHex, + Data: bytes[:], + Password: password, + }) + assert.NoError(t, err) + assert.Equal(t, expectedSig, hex.EncodeToString(resp.Signature)) +} From 20f268df62e846d71b29fb55b859ee3bf85e85f1 Mon Sep 17 00:00:00 2001 From: Madhur Shrimal Date: Wed, 8 Jan 2025 09:56:43 -0800 Subject: [PATCH 2/6] make fmt --- internal/services/signing/signing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/services/signing/signing.go b/internal/services/signing/signing.go index fa421e9..d2a970b 100644 --- a/internal/services/signing/signing.go +++ b/internal/services/signing/signing.go @@ -92,7 +92,7 @@ func (s *Service) SignG1( s.keyCache[pubKeyHex] = blsKey } blsKey := s.keyCache[pubKeyHex] - + g1Bytes := req.GetData() g1Point := new(crypto.G1Point) g1Point = g1Point.Deserialize(g1Bytes) From 08da6599271921d790a211971eb024d792903875 Mon Sep 17 00:00:00 2001 From: Madhur Shrimal Date: Mon, 13 Jan 2025 14:45:24 -0800 Subject: [PATCH 3/6] add log line for signing --- internal/services/signing/signing.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/services/signing/signing.go b/internal/services/signing/signing.go index d2a970b..4e431d4 100644 --- a/internal/services/signing/signing.go +++ b/internal/services/signing/signing.go @@ -98,5 +98,6 @@ func (s *Service) SignG1( g1Point = g1Point.Deserialize(g1Bytes) sig := blsKey.SignHashedToCurveMessage(g1Point.G1Affine) + s.logger.Info(fmt.Sprintf("Signed a G1 message successfully using %s", pubKeyHex)) return &v1.SignG1Response{Signature: sig.Serialize()}, nil } From 929f01f70194ad6bdbe421486d84ab72477a6259 Mon Sep 17 00:00:00 2001 From: Madhur Shrimal Date: Mon, 13 Jan 2025 17:16:51 -0800 Subject: [PATCH 4/6] send raw bytes as signatures --- internal/services/signing/signing.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/services/signing/signing.go b/internal/services/signing/signing.go index 4e431d4..524c286 100644 --- a/internal/services/signing/signing.go +++ b/internal/services/signing/signing.go @@ -71,7 +71,8 @@ func (s *Service) SignGeneric( // Sign the data with the private key sig := blsKey.SignMessage(byteArray) s.logger.Info(fmt.Sprintf("Signed a message successfully using %s", pubKeyHex)) - return &v1.SignGenericResponse{Signature: sig.Serialize()}, nil + signatureBytes := sig.RawBytes() + return &v1.SignGenericResponse{Signature: signatureBytes[:]}, nil } func (s *Service) SignG1( @@ -99,5 +100,6 @@ func (s *Service) SignG1( sig := blsKey.SignHashedToCurveMessage(g1Point.G1Affine) s.logger.Info(fmt.Sprintf("Signed a G1 message successfully using %s", pubKeyHex)) - return &v1.SignG1Response{Signature: sig.Serialize()}, nil + signatureBytes := sig.RawBytes() + return &v1.SignG1Response{Signature: signatureBytes[:]}, nil } From 9fb3b76796d5a4dcac6ddb98b776d811fcaa5646 Mon Sep 17 00:00:00 2001 From: Madhur Shrimal Date: Tue, 14 Jan 2025 09:20:06 -0800 Subject: [PATCH 5/6] add basic validation --- internal/services/signing/signing.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/internal/services/signing/signing.go b/internal/services/signing/signing.go index 524c286..e0a04a6 100644 --- a/internal/services/signing/signing.go +++ b/internal/services/signing/signing.go @@ -83,6 +83,15 @@ func (s *Service) SignG1( pubKeyHex := common.Trim0x(req.GetPublicKeyG1()) password := req.GetPassword() + if pubKeyHex == "" { + return nil, status.Error(codes.InvalidArgument, "public key is required") + } + + g1Bytes := req.GetData() + if len(g1Bytes) == 0 { + return nil, status.Error(codes.InvalidArgument, "data must be > 0 bytes") + } + if _, ok := s.keyCache[pubKeyHex]; !ok { s.logger.Info(fmt.Sprintf("In memory cache miss. Retrieving key for %s", pubKeyHex)) blsKey, err := s.store.RetrieveKey(ctx, pubKeyHex, password) @@ -94,7 +103,6 @@ func (s *Service) SignG1( } blsKey := s.keyCache[pubKeyHex] - g1Bytes := req.GetData() g1Point := new(crypto.G1Point) g1Point = g1Point.Deserialize(g1Bytes) From f0a5c490e936f4c23faa60fca859955638c10cd0 Mon Sep 17 00:00:00 2001 From: Madhur Shrimal Date: Tue, 14 Jan 2025 13:20:43 -0800 Subject: [PATCH 6/6] add sync map to avoid concurrent writes --- internal/services/signing/keystore_map.go | 23 ++++++++++++++++ internal/services/signing/signing.go | 32 +++++++++++------------ 2 files changed, 39 insertions(+), 16 deletions(-) create mode 100644 internal/services/signing/keystore_map.go diff --git a/internal/services/signing/keystore_map.go b/internal/services/signing/keystore_map.go new file mode 100644 index 0000000..8dcec18 --- /dev/null +++ b/internal/services/signing/keystore_map.go @@ -0,0 +1,23 @@ +package signing + +import ( + "sync" + + "github.com/Layr-Labs/cerberus/internal/crypto" +) + +type KeyStoreMap struct { + sync.Map +} + +func (k *KeyStoreMap) Load(key string) (*crypto.KeyPair, bool) { + value, ok := k.Map.Load(key) + if !ok { + return nil, false + } + return value.(*crypto.KeyPair), true +} + +func (k *KeyStoreMap) Store(key string, value *crypto.KeyPair) { + k.Map.Store(key, value) +} diff --git a/internal/services/signing/signing.go b/internal/services/signing/signing.go index e0a04a6..9d7d70c 100644 --- a/internal/services/signing/signing.go +++ b/internal/services/signing/signing.go @@ -18,11 +18,11 @@ import ( ) type Service struct { - config *configuration.Configuration - logger *slog.Logger - store store.Store - metrics metrics.Recorder - keyCache map[string]*crypto.KeyPair + config *configuration.Configuration + logger *slog.Logger + store store.Store + metrics metrics.Recorder + keyMap KeyStoreMap v1.UnimplementedSignerServer } @@ -33,11 +33,11 @@ func NewService( metrics metrics.Recorder, ) *Service { return &Service{ - config: config, - store: store, - metrics: metrics, - logger: logger.With("component", "signing"), - keyCache: make(map[string]*crypto.KeyPair), + config: config, + store: store, + metrics: metrics, + logger: logger.With("component", "signing"), + keyMap: KeyStoreMap{}, } } @@ -49,16 +49,16 @@ func (s *Service) SignGeneric( pubKeyHex := common.Trim0x(req.GetPublicKeyG1()) password := req.GetPassword() - if _, ok := s.keyCache[pubKeyHex]; !ok { + if _, ok := s.keyMap.Load(pubKeyHex); !ok { s.logger.Info(fmt.Sprintf("In memory cache miss. Retrieving key for %s", pubKeyHex)) blsKey, err := s.store.RetrieveKey(ctx, pubKeyHex, password) if err != nil { s.logger.Error(fmt.Sprintf("Failed to retrieve key: %v", err)) return nil, status.Error(codes.Internal, err.Error()) } - s.keyCache[pubKeyHex] = blsKey + s.keyMap.Store(pubKeyHex, blsKey) } - blsKey := s.keyCache[pubKeyHex] + blsKey, _ := s.keyMap.Load(pubKeyHex) data := req.GetData() if len(data) > 32 { @@ -92,16 +92,16 @@ func (s *Service) SignG1( return nil, status.Error(codes.InvalidArgument, "data must be > 0 bytes") } - if _, ok := s.keyCache[pubKeyHex]; !ok { + if _, ok := s.keyMap.Load(pubKeyHex); !ok { s.logger.Info(fmt.Sprintf("In memory cache miss. Retrieving key for %s", pubKeyHex)) blsKey, err := s.store.RetrieveKey(ctx, pubKeyHex, password) if err != nil { s.logger.Error(fmt.Sprintf("Failed to retrieve key: %v", err)) return nil, status.Error(codes.Internal, err.Error()) } - s.keyCache[pubKeyHex] = blsKey + s.keyMap.Store(pubKeyHex, blsKey) } - blsKey := s.keyCache[pubKeyHex] + blsKey, _ := s.keyMap.Load(pubKeyHex) g1Point := new(crypto.G1Point) g1Point = g1Point.Deserialize(g1Bytes)