diff --git a/internal/service/signer/service.go b/internal/service/signer/service.go index bd9789da950bb53d8710651a57ebe0af3895ce51..685ecd112e4d870f4f58e8601753780d316307c4 100644 --- a/internal/service/signer/service.go +++ b/internal/service/signer/service.go @@ -4,6 +4,7 @@ import ( "context" "crypto/ed25519" "crypto/x509" + "encoding/base64" "encoding/json" "encoding/pem" "fmt" @@ -523,6 +524,31 @@ func (s *Service) VerifyPresentation(_ context.Context, req *signer.VerifyPresen return &signer.VerifyResult{Valid: true}, nil } +// Sign creates digital signature on base64 encoded binary data. +func (s *Service) Sign(_ context.Context, req *signer.SignRequest) (res *signer.SignResult, err error) { + logger := s.logger.With( + zap.String("operation", "sign"), + zap.String("namespace", req.Namespace), + zap.String("key", req.Key), + ) + + data, err := base64.StdEncoding.DecodeString(req.Data) + if err != nil { + logger.Error("cannot base64 decode data", zap.Error(err)) + return nil, errors.New(errors.BadRequest, "cannot base64 decode data", err) + } + + signature, err := s.vault.WithKey(req.Namespace, req.Key).Sign(data) + if err != nil { + logger.Error("error signing data", zap.Error(err)) + return nil, errors.New(err) + } + + encodedSignature := base64.StdEncoding.EncodeToString(signature) + + return &signer.SignResult{Signature: encodedSignature}, nil +} + func (s *Service) supportedKey(keyType string) bool { for _, kt := range s.supportedKeys { if kt == keyType { diff --git a/internal/service/signer/service_test.go b/internal/service/signer/service_test.go index 6bcbd42f2da3108ae9a2178049c43430ecdb16a6..70c919ad404ace0ed2545cf1811e80539e1aae7a 100644 --- a/internal/service/signer/service_test.go +++ b/internal/service/signer/service_test.go @@ -818,6 +818,75 @@ func TestService_CreateCredential(t *testing.T) { } } +func TestService_Sign(t *testing.T) { + tests := []struct { + name string + // input + signer signer.Vault + data string + // output + signature string + errkind errors.Kind + errtext string + }{ + { + name: "invalid encoding of data", + data: "not base64 encoded string", + errtext: "cannot base64 decode data", + errkind: errors.BadRequest, + }, + { + name: "signing key not found", + data: base64.StdEncoding.EncodeToString([]byte("something")), + signer: &signerfakes.FakeVault{ + WithKeyStub: func(namespace string, key string) signer.Vault { + return &signerfakes.FakeVault{SignStub: func(data []byte) ([]byte, error) { + return nil, errors.New(errors.NotFound, "key not found") + }} + }, + }, + errtext: "key not found", + errkind: errors.NotFound, + }, + { + name: "successful signing", + data: base64.StdEncoding.EncodeToString([]byte("something")), + signer: &signerfakes.FakeVault{ + WithKeyStub: func(namespace string, key string) signer.Vault { + return &signerfakes.FakeVault{SignStub: func(data []byte) ([]byte, error) { + return []byte("signature"), nil + }} + }, + }, + signature: base64.StdEncoding.EncodeToString([]byte("signature")), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + svc := signer.New(test.signer, nil, nil, zap.NewNop()) + result, err := svc.Sign(context.Background(), &goasigner.SignRequest{ + Namespace: "transit", + Key: "key1", + Data: test.data, + }) + if err != nil { + require.NotEmpty(t, test.errtext, "expected no error but got %s", err) + require.Nil(t, result) + assert.ErrorContains(t, err, test.errtext) + e, ok := err.(*errors.Error) + require.True(t, ok) + assert.Equal(t, test.errkind, e.Kind) + return + } + + require.Empty(t, test.errtext, "got no error, but expected: %s", test.errtext) + require.NotNil(t, result) + assert.Equal(t, test.signature, result.Signature) + }) + } +} + // ---------- Verifiable Credentials ---------- // //nolint:gosec