iso7816

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 28, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

README

go-ctap/iso7816

Go Reference Go

go-ctap/iso7816 is a transport-independent Go library for ISO/IEC 7816-4 command and response APDUs.

[!WARNING] This module is under active development. Its public API may change during v0.x.

Support

The package supports:

  • command APDU cases 1 through 4;
  • automatic, short and extended length encoding;
  • response APDU and status-word parsing;
  • ISO 7816 GET RESPONSE, including the commonly used 0x9fxx variant;
  • command chaining;
  • response and command size validation;
  • contextual raw-card connections such as pcsc.Card.

The package has no platform-specific code and no runtime dependencies.

Installation

go get github.com/go-ctap/iso7816@latest

See go.mod for the required Go version.

Quick start

Card is the contextual raw-APDU interface implemented by go-ctap/pcsc:

type Card interface {
    Transmit(context.Context, []byte) ([]byte, error)
}

This example selects an application and collects a potentially chained response:

response, err := iso7816.Exchange(ctx, card, iso7816.Command{
    CLA:  0x00,
    INS:  0xa4,
    P1:   0x04,
    P2:   0x00,
    Data: aid,
    Le:   256,
}, iso7816.WithMoreDataStatusBytes(0x61, 0x9f))
if err != nil {
    log.Fatal(err)
}
if err := response.APDUError(); err != nil {
    log.Fatal(err)
}
fmt.Printf("application response: %x\n", response.Data)

Exchange follows 0x61xx with GET RESPONSE by default. It returns the final status word without interpreting application-specific meanings. Transmit sends exactly one command when automatic response collection is not wanted.

APDU encoding

Le uses the decoded expected length. Zero omits the field, 256 becomes a zero byte in a short APDU, and 65536 becomes two zero bytes in an extended APDU:

apdu, err := (iso7816.Command{
    CLA:      0x80,
    INS:      0xda,
    P1:       0x01,
    Data:     payload,
    Le:       65536,
    Encoding: iso7816.EncodingExtended,
}).MarshalBinary()

EncodingAuto, the zero value, uses short encoding whenever the command fits.

Responses contain copied data and a StatusWord:

response, err := iso7816.ParseResponse(raw)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("data=%x SW=%s\n", response.Data, response.Status)

Command chaining

Chain creates command fragments and sets the command-chaining CLA bit on every non-final fragment:

commands, err := iso7816.Chain(command, 240)
if err != nil {
    log.Fatal(err)
}
for _, command := range commands {
    response, err := iso7816.Transmit(ctx, card, command)
    // Check every intermediate response before sending the next fragment.
}

Application protocols may need to modify fields on individual fragments. Chain only applies ISO 7816 framing rules and does not transmit them automatically.

Cancellation and concurrency

Context cancellation is passed to the underlying Card. A canceled APDU is not retried: the card or reader may already have processed it.

Exchange performs several APDUs when it receives a chained response. The caller must prevent another application or goroutine from interleaving APDUs, for example by using an exclusive PC/SC connection or transaction.

Errors

Malformed commands and responses support errors.Is with package errors such as ErrInvalidCommand and ErrInvalidResponse. Size limits return ErrResponseTooLarge or ErrInvalidFragmentSize.

Response.APDUError converts a non-success final status into *APDUError while retaining SW1 and SW2:

var apduErr *iso7816.APDUError
if errors.As(err, &apduErr) {
    log.Printf("card returned SW=%04x", apduErr.StatusWord())
}

Underlying card errors are returned unchanged.

Scope

This package does not provide:

  • PC/SC reader discovery, events, or connection management;
  • application identifiers or application commands;
  • CTAP, PIV, OpenPGP, or GlobalPlatform semantics;
  • BER-TLV data models;
  • secure messaging, PIN workflows, or key management.

Use go-ctap/pcsc for PC/SC access and go-ctap/ctap for FIDO2 commands.

Testing

go test ./...

The test suite uses in-memory cards and does not require hardware.

License

Apache License 2.0. See LICENSE.

Documentation

Overview

Package iso7816 encodes and exchanges ISO/IEC 7816-4 command and response APDUs.

The package is independent of the connection backend and smart-card application. A Card can be backed by PC/SC, NFC, a secure element, or a test double. Application packages such as CTAP, PIV, and OpenPGP define their own commands, status semantics, and higher-level workflows on top.

Index

Constants

View Source
const CommandChainingBit byte = 0x10

CommandChainingBit is the CLA bit set on every non-final command fragment.

Variables

View Source
var (
	ErrNilCard             = errors.New("iso7816: nil card")
	ErrInvalidCommand      = errors.New("iso7816: invalid command APDU")
	ErrInvalidResponse     = errors.New("iso7816: invalid response APDU")
	ErrInvalidOption       = errors.New("iso7816: invalid option")
	ErrInvalidFragmentSize = errors.New("iso7816: invalid command fragment size")
	ErrResponseTooLarge    = errors.New("iso7816: response data is too large")
)

Functions

This section is empty.

Types

type APDUError

type APDUError struct {
	SW1 byte
	SW2 byte
}

APDUError reports a non-success ISO 7816 status word. Applications may use SW1 and SW2 to map the status to protocol-specific errors.

func (*APDUError) Error

func (e *APDUError) Error() string

func (*APDUError) StatusWord

func (e *APDUError) StatusWord() StatusWord

StatusWord returns SW1 and SW2 as one 16-bit value.

type Card

type Card interface {
	Transmit(ctx context.Context, apdu []byte) ([]byte, error)
}

Card transmits raw command APDUs and returns raw response APDUs, including SW1 and SW2.

type Command

type Command struct {
	CLA      byte
	INS      byte
	P1       byte
	P2       byte
	Data     []byte
	Le       uint32
	Encoding Encoding
}

Command is an ISO 7816 command APDU. Le is the maximum expected response data length: zero omits Le, 256 encodes as zero in a short APDU, and 65536 encodes as two zero bytes in an extended APDU.

func Chain

func Chain(command Command, maxDataLength int) ([]Command, error)

Chain splits command data into ISO 7816 command-chaining fragments. Each intermediate command has CommandChainingBit set and omits Le. The final command retains the original Le.

func (Command) MarshalBinary

func (c Command) MarshalBinary() ([]byte, error)

MarshalBinary encodes c as an ISO 7816 command APDU.

type Encoding

type Encoding uint8

Encoding controls the length encoding used by a command APDU.

const (
	// EncodingAuto selects short encoding when the command fits and extended
	// encoding otherwise.
	EncodingAuto Encoding = iota
	EncodingShort
	EncodingExtended
)

type ExchangeOption

type ExchangeOption func(*exchangeOptions)

ExchangeOption configures response collection for Exchange.

func WithMaxResponseSize

func WithMaxResponseSize(size int) ExchangeOption

WithMaxResponseSize limits the combined response data size. The default is 65536 bytes.

func WithMoreDataStatusBytes

func WithMoreDataStatusBytes(sw1 ...byte) ExchangeOption

WithMoreDataStatusBytes sets the SW1 values which cause Exchange to issue GET RESPONSE. ISO 7816 defines 0x61; some cards use 0x9f.

type Response

type Response struct {
	Data   []byte
	Status StatusWord
}

Response is an ISO 7816 response APDU.

func Exchange

func Exchange(
	ctx context.Context,
	card Card,
	command Command,
	opts ...ExchangeOption,
) (Response, error)

Exchange sends a command and collects response data through ISO 7816 GET RESPONSE commands. It returns the final status without interpreting it.

func ParseResponse

func ParseResponse(raw []byte) (Response, error)

ParseResponse parses response data followed by SW1 and SW2. The returned response owns a copy of the data.

func Transmit

func Transmit(ctx context.Context, card Card, command Command) (Response, error)

Transmit sends one command APDU and parses one response APDU. It does not interpret the status word or issue GET RESPONSE.

func (Response) APDUError

func (r Response) APDUError() error

APDUError returns nil for a successful status word and a typed error for every other final status.

func (Response) MarshalBinary

func (r Response) MarshalBinary() ([]byte, error)

MarshalBinary encodes response data followed by SW1 and SW2.

type StatusWord

type StatusWord uint16

StatusWord contains the SW1 and SW2 bytes returned by a smart card.

const (
	StatusSuccess StatusWord = 0x9000
)

func NewStatusWord

func NewStatusWord(sw1, sw2 byte) StatusWord

NewStatusWord combines SW1 and SW2.

func (StatusWord) CorrectLength

func (s StatusWord) CorrectLength() (uint32, bool)

CorrectLength returns the response length indicated by a 0x6cxx status. An SW2 value of zero means 256 bytes. The package does not retry automatically because application commands may have side effects.

func (StatusWord) IsSuccess

func (s StatusWord) IsSuccess() bool

IsSuccess reports whether the status word is 0x9000.

func (StatusWord) MoreDataLength

func (s StatusWord) MoreDataLength() (uint32, bool)

MoreDataLength returns the response length indicated by a 0x61xx or 0x9fxx status. An SW2 value of zero means 256 bytes.

func (StatusWord) SW1

func (s StatusWord) SW1() byte

SW1 returns the most significant status byte.

func (StatusWord) SW2

func (s StatusWord) SW2() byte

SW2 returns the least significant status byte.

func (StatusWord) String

func (s StatusWord) String() string

String returns the four hexadecimal status-word digits.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL