From 6d0c13bd0d8ce89509eddeb38ecdd80313f9026d Mon Sep 17 00:00:00 2001 From: Madhur Shrimal Date: Tue, 14 Jan 2025 13:26:02 -0800 Subject: [PATCH] feat: implement g1 sign api (#25) --- go.mod | 2 +- go.sum | 4 +- internal/services/signing/keystore_map.go | 23 ++++++++ internal/services/signing/signing.go | 66 ++++++++++++++++++----- internal/services/signing/signing_test.go | 26 +++++++++ 5 files changed, 104 insertions(+), 17 deletions(-) create mode 100644 internal/services/signing/keystore_map.go 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/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 c55fcea..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 { @@ -71,5 +71,43 @@ 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( + 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 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.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.keyMap.Store(pubKeyHex, blsKey) + } + blsKey, _ := s.keyMap.Load(pubKeyHex) + + g1Point := new(crypto.G1Point) + g1Point = g1Point.Deserialize(g1Bytes) + + sig := blsKey.SignHashedToCurveMessage(g1Point.G1Affine) + s.logger.Info(fmt.Sprintf("Signed a G1 message successfully using %s", pubKeyHex)) + signatureBytes := sig.RawBytes() + return &v1.SignG1Response{Signature: signatureBytes[:]}, 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)) +}