Packages

Encodes and decodes Apple TLV8 structures

Current section

Files

Jump to
tlv8 lib tlv8.ex
Raw

lib/tlv8.ex

defmodule TLV8 do
@moduledoc """
A small library that decodes and encodes TLV8 according to Apple's specifications. It is intended to be compatible
with TLV8 data structures used by HomeKit.
* Encodes a list of type & value pairs into binary
* Decodes binary into a list of type & value pairs
* Automatically handles values longer than 255 bytes by splitting them into fragments
This TLV8 library is written by Charles Gutjahr, but was inspired by [elixir_tlv](https://github.com/bitgamma/elixir_tlv).
That library implements the ASN.1 BER-TLV format, a format that is somewhat similar to the Apple TLV8 format
but is sufficiently different for that library to be unsuitable for working with Apple TLV8.
Licensed under the MIT license, see the LICENSE file for details.
"""
@typedoc """
Represents a type and value pair, a single item in an Apple TLV8 structure. TLV8 binary data may contain
one or more of these items.
The `type` must be an integer between 0 and 255. Specifying a value outside this range will cause encoding to fail.
Note that the 'L' in TLV8, the length, is not included because it is calculated automatically during encoding and so
does not need to be manually specified.
"""
@type t :: %TLV8{type: integer, value: binary}
defstruct [:type, :value]
@doc """
Decodes the given binary TLV8 data, returning a list of all the items found (as TLV8 type & value pairs). The decoder
will recursively extract all the items in the binary data until the data is exhausted.
If invalid data is found, the decoder will return a list containing as many valid TLV8 type & value pairs as possible,
then return the atom `:invalid_tlv8` and stop. If the binary data cannot be parsed at all a list containing a single
`:invalid_tlv8` will be returned.
## Examples
# Decoding binary data containing a single TLV8
iex> TLV8.decode(<<0xFF, 0x02, 0x03, 0x04>>)
[%TLV8{type: 255, value: <<3, 4>>}]
# Decoding binary data containing multiple TLV8s
iex> TLV8.decode(<<0x06, 0x01, 0x03, 0x01, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f>>)
[%TLV8{type: 6, value: <<3>>}, %TLV8{type: 1, value: "hello"}]
# Invalid TLV8
iex> TLV8.decode(<<0x80, 0x02, 0x00>>)
[:invalid_tlv8]
"""
@spec decode(binary) :: [ TLV8.t | :invalid_tlv8 ]
defdelegate decode(tlv8), to: TLV8Decoder
@doc """
Encodes the given single TLV8 item or list of TLV8 items, and returns it in Apple TLV8 binary format.
If any of the items given for encoding contain an invalid type, the encoder will return the atom `:invalid_tlv8`.
## Examples
# Encoding a single TLV8
iex> TLV8.encode(%TLV8{type: 0xFF, value: <<0x03, 0x04>>})
<<0xFF, 0x02, 0x03, 0x04>>
# Encoding multiple TLV8s
iex> TLV8.encode([%TLV8{type: 0x06, value: <<0x03>>}, %TLV8{type: 1, value: "hello"}])
<<0x06, 0x01, 0x03, 0x01, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f>>
# Encoding an invalid TLV8 type
iex> TLV8.encode([%TLV8{type: 0x00, value: <<>>}, %TLV8{type: 0x100, value: <<0x00>>}])
:invalid_tlv8
"""
@spec encode(TLV8.t | [ TLV8.t ]) :: binary | :invalid_tlv8
defdelegate encode(tlv8), to: TLV8Encoder
end