From b2ef28fa0a50391bebb15907d4e81cb05d192eb5 Mon Sep 17 00:00:00 2001 From: Andrea Franz Date: Fri, 14 Sep 2018 13:30:16 +0200 Subject: [PATCH] add command and response structs --- apdu/command.go | 60 +++++++++++++++++++++++++++++++++++++++++++ apdu/command_test.go | 43 +++++++++++++++++++++++++++++++ apdu/response.go | 46 +++++++++++++++++++++++++++++++++ apdu/response_test.go | 26 +++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 apdu/command.go create mode 100644 apdu/command_test.go create mode 100644 apdu/response.go create mode 100644 apdu/response_test.go diff --git a/apdu/command.go b/apdu/command.go new file mode 100644 index 0000000..1d76f61 --- /dev/null +++ b/apdu/command.go @@ -0,0 +1,60 @@ +package apdu + +import ( + "bytes" + "encoding/binary" +) + +type Command struct { + Cla uint8 + Ins uint8 + P1 uint8 + P2 uint8 + Data []byte + Le uint8 +} + +func NewCommand(cla, ins, p1, p2 uint8, data []byte) *Command { + return &Command{ + Cla: cla, + Ins: ins, + P1: p1, + P2: p2, + Data: data, + } +} + +func (c *Command) serialize() ([]byte, error) { + buf := new(bytes.Buffer) + + if err := binary.Write(buf, binary.BigEndian, c.Cla); err != nil { + return nil, err + } + + if err := binary.Write(buf, binary.BigEndian, c.Ins); err != nil { + return nil, err + } + + if err := binary.Write(buf, binary.BigEndian, c.P1); err != nil { + return nil, err + } + + if err := binary.Write(buf, binary.BigEndian, c.P2); err != nil { + return nil, err + } + + if len(c.Data) > 0 { + if err := binary.Write(buf, binary.BigEndian, uint8(len(c.Data))); err != nil { + return nil, err + } + if err := binary.Write(buf, binary.BigEndian, c.Data); err != nil { + return nil, err + } + } + + if err := binary.Write(buf, binary.BigEndian, c.Le); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} diff --git a/apdu/command_test.go b/apdu/command_test.go new file mode 100644 index 0000000..2fe13bc --- /dev/null +++ b/apdu/command_test.go @@ -0,0 +1,43 @@ +package apdu + +import ( + "encoding/hex" + "fmt" + "log" + "testing" + + "github.com/stretchr/testify/assert" +) + +func hexToBytes(s string) []byte { + b := make([]byte, hex.DecodedLen(len(s))) + _, err := hex.Decode(b, []byte(s)) + if err != nil { + log.Fatal(err) + } + + return b[:] +} + +func bytesToHexWithSpaces(b []byte) string { + return fmt.Sprintf("% X", b) +} + +func bytesToHex(b []byte) string { + return fmt.Sprintf("%X", b) +} + +func TestNewCommand(t *testing.T) { + var cla uint8 = 0x80 + var ins uint8 = 0x50 + var p1 uint8 = 1 + var p2 uint8 = 2 + data := hexToBytes("84762336c5187fe8") + + cmd := NewCommand(cla, ins, p1, p2, data) + expected := "80 50 01 02 08 84 76 23 36 C5 18 7F E8 00" + + result, err := cmd.serialize() + assert.NoError(t, err) + assert.Equal(t, expected, bytesToHexWithSpaces(result)) +} diff --git a/apdu/response.go b/apdu/response.go new file mode 100644 index 0000000..f2f0878 --- /dev/null +++ b/apdu/response.go @@ -0,0 +1,46 @@ +package apdu + +import ( + "bytes" + "encoding/binary" + "errors" +) + +type Response struct { + Data []byte + Sw1 uint8 + Sw2 uint8 + Sw uint16 +} + +var ErrBadRawResponse = errors.New("response data must be at least 2 bytes") + +func (r *Response) deserialize(data []byte) error { + if len(data) < 2 { + return ErrBadRawResponse + } + + r.Data = make([]byte, len(data)-2) + buf := bytes.NewReader(data) + + if err := binary.Read(buf, binary.BigEndian, &r.Data); err != nil { + return err + } + + if err := binary.Read(buf, binary.BigEndian, &r.Sw1); err != nil { + return err + } + + if err := binary.Read(buf, binary.BigEndian, &r.Sw2); err != nil { + return err + } + + r.Sw = (uint16(r.Sw1) << 8) | uint16(r.Sw2) + + return nil +} + +func ParseResponse(data []byte) (*Response, error) { + r := &Response{} + return r, r.deserialize(data) +} diff --git a/apdu/response_test.go b/apdu/response_test.go new file mode 100644 index 0000000..8e27c51 --- /dev/null +++ b/apdu/response_test.go @@ -0,0 +1,26 @@ +package apdu + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseResponse(t *testing.T) { + raw := hexToBytes("000002650183039536622002003b5e508f751c0af3016e3fbc23d3a69000") + resp, err := ParseResponse(raw) + + assert.NoError(t, err) + assert.Equal(t, uint8(0x90), resp.Sw1) + assert.Equal(t, uint8(0x00), resp.Sw2) + assert.Equal(t, uint16(0x9000), resp.Sw) + + expected := "000002650183039536622002003B5E508F751C0AF3016E3FBC23D3A6" + assert.Equal(t, expected, bytesToHex(resp.Data)) +} + +func TestParseResponse_BadData(t *testing.T) { + raw := hexToBytes("") + _, err := ParseResponse(raw) + assert.Equal(t, ErrBadRawResponse, err) +}