Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use length of the field value for prefixer, not length of encoded data #227

Merged
merged 6 commits into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion encoding/encoder.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package encoding

type Encoder interface {
// Encode encodes source data (ASCII, characters, digits, etc.) into
// destination bytes. It returns encoded bytes and any error
Encode([]byte) ([]byte, error)
// Returns data decoded into ASCII (or bytes), how many bytes were read, error

// Decode decodes data into into bytes (ASCII, characters, digits,
// etc.). It returns the bytes representing the decoded data, the
// number of bytes read from the input, and any error
Decode([]byte, int) (data []byte, read int, err error)
}
2 changes: 1 addition & 1 deletion field/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (f *Binary) Pack() ([]byte, error) {
return nil, fmt.Errorf("failed to encode content: %w", err)
}

packedLength, err := f.spec.Pref.EncodeLength(f.spec.Length, len(packed))
packedLength, err := f.spec.Pref.EncodeLength(f.spec.Length, len(data))
if err != nil {
return nil, fmt.Errorf("failed to encode length: %w", err)
}
Expand Down
3 changes: 3 additions & 0 deletions field/composite.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ var _ json.Unmarshaler = (*Composite)(nil)
// documentation and error messages. These subfields are defined using the
// 'Subfields' field on the field.Spec struct.
//
// Because composite subfields may be encoded with different encodings, the
// Length field on the field.Spec struct is in bytes.
//
// Composite handles aggregate fields of the following format:
// - Length (if variable)
// - []Subfield
Expand Down
120 changes: 89 additions & 31 deletions field/composite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,14 +233,14 @@ var (
Sort: sort.StringsByHex,
},
Subfields: map[string]Field{
"9A": NewString(&Spec{
"9A": NewHex(&Spec{
Description: "Transaction Date",
Enc: encoding.ASCIIHexToBytes,
Enc: encoding.Binary,
Pref: prefix.BerTLV,
}),
"9F02": NewString(&Spec{
"9F02": NewHex(&Spec{
Description: "Amount, Authorized (Numeric)",
Enc: encoding.ASCIIHexToBytes,
Enc: encoding.Binary,
Pref: prefix.BerTLV,
}),
},
Expand All @@ -255,14 +255,14 @@ var (
Sort: sort.StringsByHex,
},
Subfields: map[string]Field{
"82": NewString(&Spec{
"82": NewHex(&Spec{
Description: "Application Interchange Profile",
Enc: encoding.ASCIIHexToBytes,
Enc: encoding.Binary,
Pref: prefix.BerTLV,
}),
"9F36": NewString(&Spec{
"9F36": NewHex(&Spec{
Description: "Currency Code, Application Reference",
Enc: encoding.ASCIIHexToBytes,
Enc: encoding.Binary,
Pref: prefix.BerTLV,
}),
"9F3B": NewComposite(&Spec{
Expand All @@ -273,9 +273,9 @@ var (
Sort: sort.StringsByHex,
},
Subfields: map[string]Field{
"9F45": NewString(&Spec{
"9F45": NewHex(&Spec{
Description: "Data Authentication Code",
Enc: encoding.ASCIIHexToBytes,
Enc: encoding.Binary,
Pref: prefix.BerTLV,
}),
},
Expand Down Expand Up @@ -306,18 +306,18 @@ type CompositeTestDataWithoutTagPaddingWithIndexTag struct {
}

type TLVTestData struct {
F9A *String
F9F02 *String
F9A *Hex
F9F02 *Hex
}

type ConstructedTLVTestData struct {
F82 *String
F9F36 *String
F82 *Hex
F9F36 *Hex
F9F3B *SubConstructedTLVTestData
}

type SubConstructedTLVTestData struct {
F9F45 *String
F9F45 *Hex
}

func TestComposite_SetData(t *testing.T) {
Expand All @@ -334,8 +334,8 @@ func TestCompositeFieldUnmarshal(t *testing.T) {
// we will do it by packing the field
composite := NewComposite(tlvTestSpec)
err := composite.SetData(&TLVTestData{
F9A: NewStringValue("210720"),
F9F02: NewStringValue("000000000501"),
F9A: NewHexValue("210720"),
F9F02: NewHexValue("000000000501"),
})
require.NoError(t, err)

Expand All @@ -353,10 +353,10 @@ func TestCompositeFieldUnmarshal(t *testing.T) {
t.Run("Unmarshal gets data for composite field (constructed)", func(t *testing.T) {
composite := NewComposite(constructedBERTLVTestSpec)
err := composite.SetData(&ConstructedTLVTestData{
F82: NewStringValue("017F"),
F9F36: NewStringValue("027F"),
F82: NewHexValue("017F"),
F9F36: NewHexValue("027F"),
F9F3B: &SubConstructedTLVTestData{
F9F45: NewStringValue("047F"),
F9F45: NewHexValue("047F"),
},
})
require.NoError(t, err)
Expand All @@ -375,15 +375,15 @@ func TestCompositeFieldUnmarshal(t *testing.T) {

t.Run("Unmarshal gets data for composite field using field tag `index`", func(t *testing.T) {
type tlvTestData struct {
Date *String `index:"9A"`
TransactionID *String `index:"9F02"`
Date *Hex `index:"9A"`
TransactionID *Hex `index:"9F02"`
}
// first, we need to populate fields of composite field
// we will do it by packing the field
composite := NewComposite(tlvTestSpec)
err := composite.SetData(&TLVTestData{
F9A: NewStringValue("210720"),
F9F02: NewStringValue("000000000501"),
F9A: NewHexValue("210720"),
F9F02: NewHexValue("000000000501"),
})
require.NoError(t, err)

Expand All @@ -402,8 +402,8 @@ func TestCompositeFieldUnmarshal(t *testing.T) {
func TestTLVPacking(t *testing.T) {
t.Run("Pack correctly serializes data to bytes (general tlv)", func(t *testing.T) {
data := &TLVTestData{
F9A: NewStringValue("210720"),
F9F02: NewStringValue("000000000501"),
F9A: NewHexValue("210720"),
F9F02: NewHexValue("000000000501"),
}

composite := NewComposite(tlvTestSpec)
Expand Down Expand Up @@ -465,10 +465,10 @@ func TestTLVPacking(t *testing.T) {

t.Run("Pack correctly serializes data to bytes (constructed ber-tlv)", func(t *testing.T) {
data := &ConstructedTLVTestData{
F82: NewStringValue("017f"),
F9F36: NewStringValue("027f"),
F82: NewHexValue("017f"),
F9F36: NewHexValue("027f"),
F9F3B: &SubConstructedTLVTestData{
F9F45: NewStringValue("047f"),
F9F45: NewHexValue("047f"),
},
}

Expand Down Expand Up @@ -629,6 +629,64 @@ func TestCompositePacking(t *testing.T) {
require.Equal(t, "ABCD12", string(packed))
})

t.Run("Pack and unpack data with BCD encoding", func(t *testing.T) {
var compositeSpecWithBCD = &Spec{
Length: 2, // always in bytes
Description: "Point of Service Entry Mode",
Pref: prefix.BCD.Fixed,
Tag: &TagSpec{
Sort: sort.StringsByInt,
},
Subfields: map[string]Field{
"1": NewString(&Spec{
Length: 2,
Description: "PAN/Date Entry Mode",
Enc: encoding.BCD,
Pref: prefix.BCD.Fixed,
}),
"2": NewString(&Spec{
Length: 2,
Description: "PIN Entry Capability",
Enc: encoding.BCD,
Pref: prefix.BCD.Fixed,
}),
},
}

type data struct {
PANEntryMode *String `index:"1"`
PINEntryMode *String `index:"2"`
}

f := NewComposite(compositeSpecWithBCD)

d := &data{
PANEntryMode: NewStringValue("01"),
PINEntryMode: NewStringValue("02"),
}

err := f.Marshal(d)
require.NoError(t, err)

packed, err := f.Pack()
require.NoError(t, err)
require.Equal(t, []byte{0x01, 0x02}, packed)

// unpacking

f = NewComposite(compositeSpecWithBCD)
read, err := f.Unpack(packed)
require.NoError(t, err)
require.Equal(t, 2, read) // two bytes read

d = &data{}
err = f.Unmarshal(d)
require.NoError(t, err)

require.Equal(t, "01", d.PANEntryMode.Value())
require.Equal(t, "02", d.PINEntryMode.Value())
})

t.Run("Unpack returns an error on mismatch of subfield types", func(t *testing.T) {
type TestDataIncorrectType struct {
F1 *Numeric
Expand Down Expand Up @@ -1671,8 +1729,8 @@ func TestTLVJSONConversion(t *testing.T) {

t.Run("MarshalJSON TLV Data Ok", func(t *testing.T) {
data := &TLVTestData{
F9A: NewStringValue("210720"),
F9F02: NewStringValue("000000000501"),
F9A: NewHexValue("210720"),
F9F02: NewHexValue("000000000501"),
}

composite := NewComposite(tlvTestSpec)
Expand Down
Loading