Documentation
¶
Overview ¶
Package tlv implements streaming encoding and decoding of the tag-length-value (TLV) format used by the Basic Encoding Rules (BER) and related encoding rules as specified in Rec. ITU-T X.690. See also “A Layman's Guide to a Subset of ASN.1, BER, and DER”.
The Encoder and Decoder types are used to encode or decode a stream of TLV headers and their values. This package deals with the syntactic layer of TLV-encoding while other packages such as codello.dev/asn1/ber deal with the semantic layer of BER.
Headers and Values ¶
In BER each value is encoded using a tag-length-value format. The tag and length (we call them a header) are represented by the Header type. Values can use the primitive or constructed encoding. Primitive values can be read and written via io.Reader and io.Writer interfaces. Values using the constructed encoding are followed by more BER-encoded values and can either end implicitly (when using definite-length encoding) or explicitly (indefinite length).
The end of a constructed data value is signalled by a zero Header (or, equivalently, using TagEndOfContents or EndOfContents). The Encoder and Decoder types expect and produce an end-of-contents marker at the end of every constructed encoding, regardless of whether it uses the definite or indefinite-length encoding.
The Encoder and Decoder types contain methods to read and write BER values as a stream of headers, values, and end-of-content markers. They maintain an internal state to validate whether the sequence of TLVs forms a valid BER encoding.
Index ¶
- Constants
- Variables
- func CombinedLength(ls ...int) int
- func HeaderSize(h Header) int
- func MinLength(l1, l2 int) int
- type Decoder
- func (d *Decoder) DataValueOffset() int64
- func (d *Decoder) InputOffset() int64
- func (d *Decoder) PeekHeader() (Header, error)
- func (d *Decoder) ReadHeader() (Header, io.ReadCloser, error)
- func (d *Decoder) Reset(r io.Reader)
- func (d *Decoder) Skip() (err error)
- func (d *Decoder) StackDepth() int
- func (d *Decoder) StackIndex(i int) Header
- type Encoder
- type Header
- type Sequence
- type SyntaxError
Examples ¶
Constants ¶
const LengthIndefinite = -1
LengthIndefinite when used as a magic number for the length of a Header indicates that the data value is encoded using the constructed indefinite-length format.
const TagEndOfContents = asn1.TagReserved
TagEndOfContents is the tag that signifies the end of a constructed data value. You can use this constant for clarity, the following are the same:
tlv.Header{}
tlv.Header{Tag: tlv.TagEndOfContents}
tlv.EndOfContents
Variables ¶
var EndOfContents = Header{Tag: TagEndOfContents}
EndOfContents is the end-of-contents marker signalling the end of a constructed data value. The following are equivalent:
tlv.Header{}
tlv.Header{Tag: tlv.TagEndOfContents}
tlv.EndOfContents
Functions ¶
func CombinedLength ¶
CombinedLength returns the length of a data value encoding (not including its header) consisting of data value encodings of the specified lengths. If any of the passed lengths are LengthIndefinite or the result does not fit into the int type, the result is LengthIndefinite.
Example ¶
fmt.Println(CombinedLength(42, LengthIndefinite)) fmt.Println(CombinedLength(math.MaxInt, 2))
Output: -1 -1
func HeaderSize ¶
HeaderSize returns the minimum number of bytes required to encode h.
Types ¶
type Decoder ¶
type Decoder struct {
// contains filtered or unexported fields
}
Decoder is a streaming decoder for the TLV format used by ASN.1 encoding rules such as BER, DER or CER. It is used to read a stream of top-level tag-length-value (TLV) constructs.
Decoder can be used in presence of transient errors from the underlying reader. If an error occurs, the decoder is - in effect - reset to the state before the last ReadHeader call.
func NewDecoder ¶
NewDecoder creates a new Decoder reading from r. If r does not implement io.ByteReader, Decoder will do its own buffering. The buffering mechanism of Decoder attempts to buffer at most the number of bytes that belong to the current top-level TLV. However, if a top-level TLV uses the indefinite length format, the Decoder may buffer past the end of the value.
func (*Decoder) DataValueOffset ¶
DataValueOffset returns the input byte offset where the current data value starts. This is the first byte of the identifier octets of the current value.
func (*Decoder) InputOffset ¶
InputOffset returns the current input byte offset. The number of bytes actually read from the underlying io.Reader may be more than this offset due to internal buffering effects.
- If the current TLV uses the primitive encoding, it gives the number of bytes that have been read from the input, including any bytes read from the current value.
- If the current TLV uses the constructed encoding, it gives the location of the first byte of the next TLV header in the input.
func (*Decoder) PeekHeader ¶
PeekHeader reads the next TLV header from the input without advancing d. You can consume the peeked header using the ReadHeader method.
PeekHeader shares the same semantics as ReadHeader. In particular at the end of constructed data values there is always an EndOfContents (even for definite-length data values) and transient errors from the underlying reader can be retried.
func (*Decoder) ReadHeader ¶
func (d *Decoder) ReadHeader() (Header, io.ReadCloser, error)
ReadHeader reads the next TLV header from the input. At the end of constructed TLVs a Header with TagEndOfContents will be returned (for both definite and indefinite-length encodings). If an error occurs during decoding the TLV header, or it is detected that the TLV structure is invalid, an error is returned.
The second return value is non-nil iff the decoded Header indicates the use of the primitive encoding. The io.ReadCloser can be used to read the contents of the primitive TLV. It also implements io.ByteReader. io.Closer.Close must be called before the next call of Decoder.ReadHeader or Decoder.PeekHeader.
ReadHeader can be used in presence of transient errors. If the underlying reader returns an error during the read operation, ReadHeader will return that error (potentially wrapped). If errors in the underlying reader are non-fatal, you can retry ReadHeader to resume the previous, erroneous call.
func (*Decoder) Reset ¶
Reset resets the state of d to read from r. See NewDecoder for details.
Reset reuses the internal buffer of d which may save some allocations compared to NewDecoder.
func (*Decoder) Skip ¶
Skip discards the remainder of the current data value. If it uses the primitive encoding, only that value is discarded. If it is constructed, everything until the matching end-of-contents is skipped.
If at any point an error is encountered, the skipping will be stopped and the error returned.
func (*Decoder) StackDepth ¶
StackDepth returns the number of nested constructed TLVs of the current location of d. Each level represents a constructed TLV. It is incremented whenever a constructed TLV is encountered and decremented whenever a constructed TLV ends. The depth is zero-indexed, where zero represents the (virtual) top-level TLV.
func (*Decoder) StackIndex ¶
StackIndex returns information about the specified stack level. It must be a number between 0 and Decoder.StackDepth, inclusive.
The TLV header at level 0 represents the top level and is not present in the input data. The top-level TLV header is a constructed, indefinite-length data value with tag 0.
type Encoder ¶
type Encoder struct {
// contains filtered or unexported fields
}
Encoder is a streaming encoder for the TLV format used by ASN.1 encoding rules such as BER, DER or CER. It is used to write a stream of top-level tag-length-value (TLV) constructs.
Encoder can be used in presence of transient errors from the underlying writer. If an error occurs, the encoder is - in effect - reset to the state before the last WriteHeader call.
func NewEncoder ¶
NewEncoder creates a new Encoder writing to w. If w does not implement io.ByteWriter, Encoder will do its own buffering. The buffer is automatically flushed at the end of each top-level data value.
func (*Encoder) DataValueOffset ¶
DataValueOffset returns the output byte offset where the current data value begins. This is the first byte of the identifier octets of the current data value.
func (*Encoder) OutputOffset ¶
OutputOffset returns the current output byte offset. It gives the location of the next byte immediately after the most recently written header or value. The number of bytes actually written to the underlying io.Writer may be less than this offset due to internal buffering effects.
func (*Encoder) Reset ¶
Reset resets the state of e to write to w. See NewEncoder for details.
Reset reuses the internal buffer of e which may save some allocations compared to NewEncoder.
func (*Encoder) StackDepth ¶
StackDepth returns the depth of nested constructed TLVs that have been opened and not closed by WriteHeader. Each level on the stack represents a constructed TLV. It is incremented whenever a constructed TLV is encountered by WriteHeader and decremented whenever the corresponding EndOfContents is encountered. The depth is zero-indexed, where zero represents the (virtual) top-level TLV.
func (*Encoder) StackIndex ¶
StackIndex returns information about the specified stack level. It must be a number between 0 and Encoder.StackDepth, inclusive.
The TLV header at level 0 represents the top level and is not written to the output. The top-level TLV header is a constructed, indefinite-length data value with tag 0.
func (*Encoder) WriteHeader ¶
func (e *Encoder) WriteHeader(h Header) (io.WriteCloser, error)
WriteHeader writes the next TLV header to the output. At the end of constructed TLVs, a Header with TagEndOfContents must be written (for both definite and indefinite-length encodings). Encoder validates that the written sequence of headers and values is valid and will return an error if h cannot be written at the current place in the TLV structure.
If h indicates the use of the primitive encoding, WriteHeader returns an io.WriteCloser that can be used to write the contents of the value. It also implements io.ByteWriter. Before the next call to WriteHeader, the full value (as indicated by h.Length) must be written and io.Closer.Close must be called.
WriteHeader can be used in presence of transient errors. If the underlying writer returns an error during the write operation, WriteHeader will return that error (potentially wrapped). If the underlying writer maintains a consistent state after an error, you can retry the WriteHeader operation (using the same value for h) to resume the previous write operation.
type Header ¶
Header represents a TLV header. The [Header.Length] may be LengthIndefinite if an indefinite-length encoding is used. It is invalid to use the indefinite-length encoding when [Header.Constructed] = false.
type Sequence ¶
Sequence can be used to build constructed TLVs for writing. Despite it name it can be used to build any constructed value, not just SEQUENCE values. The zero value is an empty constructed value. Note that Tag must be set before the value is written.
Example ¶
var out bytes.Buffer
enc := NewEncoder(&out)
seq := Sequence{Tag: asn1.TagSequence}
// append a value to the sequence
seq.Append(func(enc *Encoder) error {
val, err := enc.WriteHeader(Header{asn1.TagInteger, false, 1})
if err != nil {
return err
}
err = val.(io.ByteWriter).WriteByte(0x15)
if err != nil {
return err
}
return val.Close()
})
if err := seq.WriteTo(enc); err != nil {
panic(err)
}
fmt.Printf("%# x\n", out.Bytes())
Output: 0x30 0x03 0x02 0x01 0x15
func (*Sequence) Append ¶
Append adds the given values to the end of the sequence. A value is a function that encodes a single value into an Encoder. This function does not call any of the value functions.
func (*Sequence) WriteTo ¶
WriteTo encodes the values of s into enc. Writing is a three-step process:
- All values are encoded until they call Encoder.WriteHeader for the first time, at which point encoding pauses.
- The total length of all the values is calculated and an appropriate header for the sequence is written.
- The encoding of the individual values is resumed and each value is written to enc.
type SyntaxError ¶
type SyntaxError struct {
Err error // underlying error
// ByteOffset is the location of the error. The location is usually the start of
// the TLV header containing the error.
ByteOffset int64
// Header is the TLV header of the constructed TLV whose value contained the
// malformed data.
Header Header
// contains filtered or unexported fields
}
SyntaxError represents an error in the TLV encoding. The error value contains the location of the error within the input as well as the Header of the surrounding data value.
func (*SyntaxError) Error ¶
func (e *SyntaxError) Error() string
func (*SyntaxError) Unwrap ¶
func (e *SyntaxError) Unwrap() error