From f62dffc82a3deeae3dd3811c567769b4afce5507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Dular?= <22869613+xBlaz3kx@users.noreply.github.com> Date: Thu, 12 Dec 2024 20:49:42 +0100 Subject: [PATCH] Feature: Signature verification (#1) * Added working signatures * Added some signature tests --- builder.go | 29 ++------- builder_opts.go | 30 +++++++++ builder_opts_test.go | 118 +++++++++++++++++++++++++++++++++++ builder_test.go | 111 +++------------------------------ go.mod | 5 +- go.sum | 20 +++++- signature.go | 88 +++++++++++++++++++++++--- signature_test.go | 143 +++++++++++++++++++++++++++++++------------ 8 files changed, 366 insertions(+), 178 deletions(-) create mode 100644 builder_opts.go create mode 100644 builder_opts_test.go diff --git a/builder.go b/builder.go index 108e974..7e7982d 100644 --- a/builder.go +++ b/builder.go @@ -1,38 +1,20 @@ package ocmf_go import ( - "crypto" + "crypto/ecdsa" "encoding/json" "fmt" "github.com/samber/lo" ) -type BuilderOption func(*Builder) - -func WithSignatureAlgorithm(algorithm SignatureAlgorithm) BuilderOption { - return func(b *Builder) { - if isValidSignatureAlgorithm(algorithm) { - b.signature.Algorithm = algorithm - } - } -} - -func WithSignatureEncoding(encoding SignatureEncoding) BuilderOption { - return func(b *Builder) { - if isValidSignatureEncoding(encoding) { - b.signature.Encoding = encoding - } - } -} - type Builder struct { payload PayloadSection signature Signature - privateKey crypto.PrivateKey + privateKey *ecdsa.PrivateKey } -func NewBuilder(privateKey crypto.PrivateKey, opts ...BuilderOption) *Builder { +func NewBuilder(privateKey *ecdsa.PrivateKey, opts ...BuilderOption) *Builder { builder := &Builder{ payload: PayloadSection{ FormatVersion: OcmfVersion, @@ -130,11 +112,10 @@ func (b *Builder) AddLossCompensation(lossCompensation LossCompensation) *Builde return b } -func (b *Builder) ClearPayloadSection() *Builder { +func (b *Builder) ClearPayloadSection() { b.payload = PayloadSection{ FormatVersion: OcmfVersion, } - return b } func (b *Builder) Build() (*string, error) { @@ -145,7 +126,7 @@ func (b *Builder) Build() (*string, error) { } // Sign payload with private key - err = b.signature.Sign(b.privateKey) + err = b.signature.Sign(b.payload, b.privateKey) if err != nil { return nil, err } diff --git a/builder_opts.go b/builder_opts.go new file mode 100644 index 0000000..f3e0b7a --- /dev/null +++ b/builder_opts.go @@ -0,0 +1,30 @@ +package ocmf_go + +type BuilderOption func(*Builder) + +func WithSignatureAlgorithm(algorithm SignatureAlgorithm) BuilderOption { + return func(b *Builder) { + if isValidSignatureAlgorithm(algorithm) { + b.signature.Algorithm = algorithm + } + } +} + +func WithSignatureEncoding(encoding SignatureEncoding) BuilderOption { + return func(b *Builder) { + if isValidSignatureEncoding(encoding) { + b.signature.Encoding = encoding + } + } +} + +func WithSignature(signature Signature) BuilderOption { + return func(b *Builder) { + err := signature.Validate() + if err != nil { + return + } + + b.signature = signature + } +} diff --git a/builder_opts_test.go b/builder_opts_test.go new file mode 100644 index 0000000..b05c37c --- /dev/null +++ b/builder_opts_test.go @@ -0,0 +1,118 @@ +package ocmf_go + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "testing" + + "github.com/stretchr/testify/suite" +) + +type builderOptsTestSuite struct { + suite.Suite +} + +func (s *builderOptsTestSuite) TestWithSignatureAlgorithm() { + tests := []struct { + name string + algorithm SignatureAlgorithm + want bool + }{ + { + name: "ECDSA-secp192k1-SHA256", + algorithm: SignatureAlgorithmECDSAsecp192k1SHA256, + want: true, + }, + { + name: "ECDSA-secp256k1-SHA256", + algorithm: SignatureAlgorithmECDSAsecp256k1SHA256, + want: true, + }, + { + name: "ECDSA-secp384r1-SHA256", + algorithm: SignatureAlgorithmECDSAsecp384r1SHA256, + want: true, + }, + { + name: "ECDSA-brainpool256r1-SHA256", + algorithm: SignatureAlgorithmECDSAbrainpool256r11SHA256, + want: true, + }, + { + name: "ECDSA-secp256r1-SHA256", + algorithm: SignatureAlgorithmECDSAsecp256r1SHA256, + want: true, + }, + { + name: "ECDSA-secp192r1-SHA256", + algorithm: SignatureAlgorithmECDSAsecp192r1SHA256, + want: true, + }, + { + name: "Unknown algorithm", + algorithm: SignatureAlgorithm("ABCD"), + want: false, + }, + { + name: "Empty algorithm", + algorithm: SignatureAlgorithm(""), + want: false, + }, + } + + for _, tt := range tests { + s.T().Run(tt.name, func(t *testing.T) { + curve := elliptic.P256() + privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) + s.Require().NoError(err) + + builder := NewBuilder(privateKey, WithSignatureAlgorithm(tt.algorithm)) + + if tt.name == "Unknown algorithm" || tt.name == "Empty algorithm" { + s.NotEqual(tt.algorithm, builder.signature.Algorithm) + } else { + s.Equal(tt.algorithm, builder.signature.Algorithm) + } + }) + } +} + +func (s *builderOptsTestSuite) TestWithWithSignatureEncoding() { + tests := []struct { + name string + encoding SignatureEncoding + }{ + { + name: "Base64 encoding", + encoding: SignatureEncodingBase64, + }, + { + name: "Hex encoding", + encoding: SignatureEncodingHex, + }, + { + name: "Empty encoding", + encoding: SignatureEncoding(""), + }, + { + name: "Unknown encoding", + encoding: SignatureEncoding("ABDD"), + }, + } + + for _, tt := range tests { + s.T().Run(tt.name, func(t *testing.T) { + builder := NewBuilder(nil, WithSignatureEncoding(tt.encoding)) + + if tt.encoding == SignatureEncodingBase64 || tt.encoding == SignatureEncodingHex { + s.Equal(tt.encoding, builder.signature.Encoding) + } else { + s.NotEqual(tt.encoding, builder.signature.Encoding) + } + }) + } +} +func TestBuilderOpts(t *testing.T) { + suite.Run(t, new(builderOptsTestSuite)) +} diff --git a/builder_test.go b/builder_test.go index 51bfc70..7481496 100644 --- a/builder_test.go +++ b/builder_test.go @@ -1,6 +1,9 @@ package ocmf_go import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" "testing" "github.com/stretchr/testify/suite" @@ -32,7 +35,10 @@ func (s *builderTestSuite) TestNewBuilder() { } func (s *builderTestSuite) TestBuilder_Valid() { - privateKey := "" // todo + curve := elliptic.P256() + privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) + s.Require().NoError(err) + builder := NewBuilder(privateKey). WithPagination("1"). WithMeterSerial("exampleSerial123"). @@ -61,7 +67,7 @@ func (s *builderTestSuite) TestBuilder_Valid() { } func (s *builderTestSuite) TestBuilder_MissingAttributes() { - builder := NewBuilder("privateKey"). + builder := NewBuilder(nil). // WithPagination("1"). WithMeterSerial("exampleSerial123"). WithIdentificationStatus(true). @@ -79,7 +85,7 @@ func (s *builderTestSuite) TestBuilder_MissingAttributes() { } func (s *builderTestSuite) TestBuilder_CantSign() { - builder := NewBuilder("privateKey"). + builder := NewBuilder(nil). WithPagination("1"). WithMeterSerial("exampleSerial123"). WithIdentificationStatus(true). @@ -91,110 +97,11 @@ func (s *builderTestSuite) TestBuilder_CantSign() { Status: string(MeterOk), }) - builder.privateKey = "" - payload, err := builder.Build() s.Error(err) s.Nil(payload) } -func (s *builderTestSuite) TestWithSignatureAlgorithm() { - tests := []struct { - name string - algorithm SignatureAlgorithm - want bool - }{ - { - name: "ECDSA-secp192k1-SHA256", - algorithm: SignatureAlgorithmECDSAsecp192k1SHA256, - want: true, - }, - { - name: "ECDSA-secp256k1-SHA256", - algorithm: SignatureAlgorithmECDSAsecp256k1SHA256, - want: true, - }, - { - name: "ECDSA-secp384r1-SHA256", - algorithm: SignatureAlgorithmECDSAsecp384r1SHA256, - want: true, - }, - { - name: "ECDSA-brainpool256r1-SHA256", - algorithm: SignatureAlgorithmECDSAbrainpool256r11SHA256, - want: true, - }, - { - name: "ECDSA-secp256r1-SHA256", - algorithm: SignatureAlgorithmECDSAsecp256r1SHA256, - want: true, - }, - { - name: "ECDSA-secp192r1-SHA256", - algorithm: SignatureAlgorithmECDSAsecp192r1SHA256, - want: true, - }, - { - name: "Unknown algorithm", - algorithm: SignatureAlgorithm("ABCD"), - want: false, - }, - { - name: "Empty algorithm", - algorithm: SignatureAlgorithm(""), - want: false, - }, - } - - for _, tt := range tests { - s.T().Run(tt.name, func(t *testing.T) { - builder := NewBuilder("privateKey", WithSignatureAlgorithm(tt.algorithm)) - - if tt.name == "Unknown algorithm" || tt.name == "Empty algorithm" { - s.NotEqual(tt.algorithm, builder.signature.Algorithm) - } else { - s.Equal(tt.algorithm, builder.signature.Algorithm) - } - }) - } -} - -func (s *builderTestSuite) TestWithWithSignatureEncoding() { - tests := []struct { - name string - encoding SignatureEncoding - }{ - { - name: "Base64 encoding", - encoding: SignatureEncodingBase64, - }, - { - name: "Hex encoding", - encoding: SignatureEncodingHex, - }, - { - name: "Empty encoding", - encoding: SignatureEncoding(""), - }, - { - name: "Unknown encoding", - encoding: SignatureEncoding("ABDD"), - }, - } - - for _, tt := range tests { - s.T().Run(tt.name, func(t *testing.T) { - builder := NewBuilder("privateKey", WithSignatureEncoding(tt.encoding)) - - if tt.encoding == SignatureEncodingBase64 || tt.encoding == SignatureEncodingHex { - s.Equal(tt.encoding, builder.signature.Encoding) - } else { - s.NotEqual(tt.encoding, builder.signature.Encoding) - } - }) - } -} - func TestBuilder(t *testing.T) { suite.Run(t, new(builderTestSuite)) } diff --git a/go.mod b/go.mod index fd0cc64..5898bf4 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.22 require ( github.com/go-playground/validator/v10 v10.22.1 + github.com/pkg/errors v0.9.1 github.com/samber/lo v1.47.0 github.com/stretchr/testify v1.9.0 ) @@ -13,11 +14,13 @@ require ( github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/crypto v0.23.0 // indirect golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.20.0 // indirect + golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index c294cfc..ccf0683 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= @@ -10,10 +11,22 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= @@ -22,11 +35,12 @@ golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.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/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/signature.go b/signature.go index b224b5b..bda16ae 100644 --- a/signature.go +++ b/signature.go @@ -1,10 +1,15 @@ package ocmf_go import ( - "crypto" + "crypto/ecdsa" + "crypto/rand" + "crypto/sha256" "encoding/base64" "encoding/hex" + "encoding/json" "fmt" + + "github.com/pkg/errors" ) type SignatureMimeType string @@ -73,30 +78,44 @@ func (s *Signature) Validate() error { return signatureValidator.Struct(s) } -func (s *Signature) Sign(privateKey crypto.PrivateKey) error { - var signedData string +func (s *Signature) Sign(payload PayloadSection, privateKey *ecdsa.PrivateKey) error { + if privateKey == nil { + return errors.New("private key is required") + } + + // Marshal payload + payloadBytes, err := json.Marshal(payload) + if err != nil { + return errors.Wrap(err, "failed to marshal payload") + } switch s.Algorithm { case SignatureAlgorithmECDSAsecp192k1SHA256: - // TODO case SignatureAlgorithmECDSAsecp256k1SHA256: - // TODO case SignatureAlgorithmECDSAsecp384r1SHA256: - // TODO case SignatureAlgorithmECDSAbrainpool256r11SHA256: - // TODO case SignatureAlgorithmECDSAsecp256r1SHA256: - // TODO default: return fmt.Errorf("unsupported signature algorithm: %s", s.Algorithm) } + // Hash data + messageHash := sha256.Sum256(payloadBytes) + + // Sign data + sign, err := ecdsa.SignASN1(rand.Reader, privateKey, messageHash[:]) + if err != nil { + return errors.Wrap(err, "failed to sign data") + } + + var signedData string + // Encode signed data switch s.Encoding { case SignatureEncodingBase64: - signedData = base64.StdEncoding.EncodeToString([]byte(signedData)) + signedData = base64.StdEncoding.EncodeToString(sign) case SignatureEncodingHex: - signedData = hex.EncodeToString([]byte(signedData)) + signedData = hex.EncodeToString(sign) default: return fmt.Errorf("unsupported signature encoding: %s", s.Encoding) } @@ -104,3 +123,52 @@ func (s *Signature) Sign(privateKey crypto.PrivateKey) error { s.Data = signedData return nil } + +func (s *Signature) Verify(payload PayloadSection, publicKey *ecdsa.PublicKey) (bool, error) { + var decoded []byte + + if publicKey == nil { + return false, errors.New("public key is required") + } + + // Decode the signature + switch s.Encoding { + case SignatureEncodingBase64: + decodedString, err := base64.StdEncoding.DecodeString(s.Data) + if err != nil { + return false, errors.Wrap(err, "failed to decode base64 data") + } + + decoded = decodedString + case SignatureEncodingHex: + decodedString, err := hex.DecodeString(s.Data) + if err != nil { + return false, errors.Wrap(err, "failed to decode hex data") + } + + decoded = decodedString + default: + return false, fmt.Errorf("unsupported signature encoding: %s", s.Encoding) + } + + switch s.Algorithm { + case SignatureAlgorithmECDSAsecp192k1SHA256: + case SignatureAlgorithmECDSAsecp256k1SHA256: + case SignatureAlgorithmECDSAsecp384r1SHA256: + case SignatureAlgorithmECDSAbrainpool256r11SHA256: + case SignatureAlgorithmECDSAsecp256r1SHA256: + default: + return false, fmt.Errorf("unsupported signature algorithm: %s", s.Algorithm) + } + + payloadBytes, err := json.Marshal(payload) + if err != nil { + return false, errors.Wrap(err, "failed to marshal payload") + } + + // Hash the payload to compare with the signature + messageHash := sha256.Sum256(payloadBytes) + + // Verify signature + return ecdsa.VerifyASN1(publicKey, messageHash[:], decoded), nil +} diff --git a/signature_test.go b/signature_test.go index 90d5b26..7019fc6 100644 --- a/signature_test.go +++ b/signature_test.go @@ -1,7 +1,10 @@ package ocmf_go import ( - "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "encoding/hex" "testing" "github.com/stretchr/testify/assert" @@ -103,8 +106,6 @@ type signatureTestSuite struct { suite.Suite } -func (s *signatureTestSuite) SetupTest() {} - func (s *signatureTestSuite) TestValidate() { tests := []struct { name string @@ -164,45 +165,111 @@ func (s *signatureTestSuite) TestValidate() { } } -func (s *signatureTestSuite) TestSign() { - tests := []struct { - name string - signature Signature - privateKey crypto.PrivateKey - publicKey crypto.PublicKey - error bool - }{ - { - name: "Valid signature", - }, - { - name: "Invalid algorithm", - }, - { - name: "Invalid encoding", - }, - { - name: "Invalid private key", - }, +func (s *signatureTestSuite) TestSignature_valid() { + signature := &Signature{ + Algorithm: SignatureAlgorithmECDSAsecp256r1SHA256, + Encoding: SignatureEncodingHex, + MimeType: SignatureMimeTypeDer, } + payload := PayloadSection{} - for _, tt := range tests { - s.T().Run(tt.name, func(t *testing.T) { - err := tt.signature.Sign(tt.privateKey) - if tt.error { - s.Error(err) - } else { - s.NoError(err) - } + // Generate private and public ECDSA keys + curve := elliptic.P256() + privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) + s.Require().NoError(err) - err = tt.signature.Verify(tt.publicKey) - if tt.error { - s.Error(err) - } else { - s.NoError(err) - } - }) + err = signature.Sign(payload, privateKey) + s.Require().NoError(err) + + publicKey := &privateKey.PublicKey + valid, err := signature.Verify(payload, publicKey) + s.Require().NoError(err) + s.True(valid) +} + +func (s *signatureTestSuite) TestSignature_invalid_signature() { + signature := &Signature{ + Algorithm: SignatureAlgorithmECDSAsecp256r1SHA256, + Encoding: SignatureEncodingHex, + MimeType: SignatureMimeTypeDer, + } + payload := PayloadSection{} + + // Generate private and public ECDSA keys + curve := elliptic.P256() + privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) + s.Require().NoError(err) + + err = signature.Sign(payload, privateKey) + s.Require().NoError(err) + + // Tamper with the signature + signature.Data = hex.EncodeToString([]byte("IhaveBeenTampered")) + + publicKey := &privateKey.PublicKey + valid, err := signature.Verify(payload, publicKey) + s.NoError(err) + s.False(valid) +} + +func (s *signatureTestSuite) TestSignature_invalid_public_key() { + signature := &Signature{ + Algorithm: SignatureAlgorithmECDSAsecp256r1SHA256, + Encoding: SignatureEncodingHex, + MimeType: SignatureMimeTypeDer, + } + payload := PayloadSection{} + + // Generate private and public ECDSA keys + curve := elliptic.P256() + privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) + s.Require().NoError(err) + + key2, err := ecdsa.GenerateKey(curve, rand.Reader) + s.Require().NoError(err) + + err = signature.Sign(payload, privateKey) + s.Require().NoError(err) + + publicKey := &key2.PublicKey + valid, err := signature.Verify(payload, publicKey) + s.NoError(err) + s.False(valid) +} + +func (s *signatureTestSuite) TestSignature_tampered_payload() { + signature := &Signature{ + Algorithm: SignatureAlgorithmECDSAsecp256r1SHA256, + Encoding: SignatureEncodingHex, + MimeType: SignatureMimeTypeDer, + } + payload := PayloadSection{ + FormatVersion: OcmfVersion, + MeterVendor: "ExampleMeterVendor", + MeterModel: "ExampleMeterModel", + MeterSerial: "ExampleSerial", + MeterFirmware: "0.0.1", + } + + // Generate private and public ECDSA keys + curve := elliptic.P256() + privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) + s.Require().NoError(err) + + err = signature.Sign(payload, privateKey) + s.Require().NoError(err) + + publicKey := &privateKey.PublicKey + tamperedPayload := PayloadSection{ + FormatVersion: OcmfVersion, + MeterVendor: "ExampleeMeterVendor", + MeterModel: "ExampleMeterModel", + MeterSerial: "ExampleSerial", + MeterFirmware: "0.0.1", } + valid, err := signature.Verify(tamperedPayload, publicKey) + s.NoError(err) + s.False(valid) } func TestSignature(t *testing.T) {