Skip to content

Commit

Permalink
Parser improvements and added tests
Browse files Browse the repository at this point in the history
  • Loading branch information
xBlaz3kx committed Dec 17, 2024
1 parent 57780e3 commit bc2fd4d
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 32 deletions.
8 changes: 5 additions & 3 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
)

var (
ErrInvalidFormat = errors.New("invalid OCMF message format")
ErrInvalidFormat = errors.New("invalid OCMF message format")
ErrVerificationFailure = errors.New("verification failed")
ErrPayloadEmpty = errors.New("payload is empty")
)

type Parser struct {
Expand Down Expand Up @@ -73,7 +75,7 @@ func (p *Parser) GetSignature() (*Signature, error) {

if p.opts.withAutomaticSignatureVerification {
if p.payload == nil {
return nil, errors.New("payload is empty")
return nil, ErrPayloadEmpty
}

valid, err := p.signature.Verify(*p.payload, p.opts.publicKey)
Expand All @@ -83,7 +85,7 @@ func (p *Parser) GetSignature() (*Signature, error) {

// Even if the signature is valid, we still return an error if the verification failed
if !valid {
return p.signature, errors.New("verification failed")
return p.signature, ErrVerificationFailure
}
}

Expand Down
5 changes: 0 additions & 5 deletions parser_opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ func WithAutomaticValidation() Opt {

func WithAutomaticSignatureVerification(publicKey *ecdsa.PublicKey) Opt {
return func(p *ParserOpts) {
if publicKey == nil {
return
}

// If a public key is provided, enable automatic signature verification
p.withAutomaticSignatureVerification = true
p.publicKey = publicKey
}
Expand Down
2 changes: 1 addition & 1 deletion parser_opts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (s *parserOptsTestSuite) TestParserOptions() {
},
expectedOptions: ParserOpts{
withAutomaticValidation: false,
withAutomaticSignatureVerification: false,
withAutomaticSignatureVerification: true,
publicKey: nil,
},
},
Expand Down
140 changes: 117 additions & 23 deletions parser_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package ocmf_go

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"strings"
"testing"

Expand Down Expand Up @@ -39,63 +42,154 @@ func (s *parserTestSuite) TestParseOcmfMessageFromString_invalid_format() {
s.Nil(signature)
}

func (s *parserTestSuite) TestGetPayload() {
tests := []struct {
name string
}{}
func (s *parserTestSuite) TestGetPayload_valid() {
parser := NewParser().ParseOcmfMessageFromString(examplePayload)

for _, tt := range tests {
s.T().Run(tt.name, func(t *testing.T) {
payload, err := parser.GetPayload()
s.NoError(err)
s.NotNil(payload)

})
}
s.Equal("EEM-350-D-MCB", payload.MeterModel)
s.Equal("BQ27400330016", payload.MeterSerial)
}

func (s *parserTestSuite) TestGetPayload_unparsable() {
malformedPayload := "OCMF|{}|{"
parser := NewParser().ParseOcmfMessageFromString(malformedPayload)

payload, err := parser.GetPayload()
s.ErrorContains(err, "failed to unmarshal signature")
s.Nil(payload)
}

func (s *parserTestSuite) TestGetSignature_valid() {
// Generate private and public ECDSA keys
curve := elliptic.P256()
privateKey, err := ecdsa.GenerateKey(curve, rand.Reader)
s.Require().NoError(err)

builder := NewBuilder(privateKey).
WithPagination("1").
WithMeterSerial("exampleSerial123").
WithIdentificationStatus(true).
WithIdentificationType(string(RfidNone)).
AddReading(Reading{
Time: "2018-07-24T13:22:04,000+0200 S",
ReadingValue: 1.0,
ReadingUnit: string(UnitskWh),
Status: string(MeterOk),
})

message, err := builder.Build()
s.Require().NoError(err)

tests := []struct {
name string
parserOpts []Opt
data string
expectedSignature *Signature
}{
{
name: "No validation",
parserOpts: []Opt{},
data: *message,
expectedSignature: &builder.signature,
},
{
name: "With automatic signature verification",
parserOpts: []Opt{
WithAutomaticSignatureVerification(&privateKey.PublicKey),
},
data: *message,
expectedSignature: &builder.signature,
},
{
name: "With automatic signature validation",
name: "With automatic payload validation",
parserOpts: []Opt{
WithAutomaticValidation(),
},
data: *message,
expectedSignature: &builder.signature,
},
}

for _, tt := range tests {
s.T().Run(tt.name, func(t *testing.T) {
parser := NewParser(tt.parserOpts...).ParseOcmfMessageFromString(tt.data)

signature, err := parser.GetSignature()
s.NoError(err)
s.NotNil(signature)
s.Equal(*tt.expectedSignature, *signature)
})
}
}

func (s *parserTestSuite) TestGetSignature_invalid() {
// Generate private and public ECDSA keys
curve := elliptic.P256()
privateKey, err := ecdsa.GenerateKey(curve, rand.Reader)
s.Require().NoError(err)

builder := NewBuilder(privateKey).
WithPagination("1").
WithMeterSerial("exampleSerial123").
WithIdentificationStatus(true).
WithIdentificationType(string(RfidNone)).
AddReading(Reading{
Time: "2018-07-24T13:22:04,000+0200 S",
ReadingValue: 1.0,
ReadingUnit: string(UnitskWh),
Status: string(MeterOk),
})

message, err := builder.Build()
s.Require().NoError(err)

privateKey2, err := ecdsa.GenerateKey(curve, rand.Reader)
s.Require().NoError(err)

tests := []struct {
name string
parserOpts []Opt
data string
expectedSignature *Signature
error string
name string
parserOpts []Opt
data string
error string
}{
{},
{
name: "Signature validation failed",
parserOpts: []Opt{
WithAutomaticSignatureVerification(&privateKey2.PublicKey),
},
data: *message,
error: "verification failed",
}, {
name: "Nil public key",
parserOpts: []Opt{
WithAutomaticSignatureVerification(nil),
},
data: *message,
error: "unable to verify signature",
},
{
name: "Payload empty",
parserOpts: []Opt{
WithAutomaticSignatureVerification(&privateKey.PublicKey),
},
data: *message,
error: "payload is empty",
},
}

for _, tt := range tests {
s.T().Run(tt.name, func(t *testing.T) {
parser := NewParser(tt.parserOpts...)
parser := NewParser(tt.parserOpts...).ParseOcmfMessageFromString(tt.data)

signature, err := parser.GetSignature()
if tt.error != "" {
s.ErrorContains(err, tt.error)
} else {
s.NoError(err)
s.NotNil(signature)
s.Equal(*tt.expectedSignature, *signature)
if tt.name == "Payload empty" {
parser.payload = nil
}

_, err := parser.GetSignature()
s.ErrorContains(err, tt.error)
})
}
}
Expand Down

0 comments on commit bc2fd4d

Please sign in to comment.