MAVLink2.Signing (mavlink2 v0.1.0)

Copy Markdown View Source

MAVLink v2 message signing: a 32-byte secret shared by both ends, a link id, and a timestamp that only moves forward.

A signed frame has incompat_flags bit 0 set and 13 bytes after its checksum: the link id, a 48-bit timestamp (little endian, 10 µs units since 2015-01-01 00:00:00 UTC) and the first 6 bytes of

SHA-256(secret ++ frame from STX through checksum ++ link id ++ timestamp)

Receiving, a frame is accepted only if its signature matches and its timestamp is later than the last one accepted from the same stream (link id, system, component). A stream not seen before is accepted if its timestamp is no more than one minute behind this end's timestamp, which is at least the clock. The signature is checked before any stream state changes, so a forged frame moves nothing.

Sending, each frame takes a timestamp later than the previous one and no earlier than the clock, so a clock that steps back cannot repeat one. This end's timestamp also follows the latest accepted from a peer.

Each frame this end signs also records its own stream, so its frames reflected back to it are refused as replays.

The replay window is only as good as the clock: a board that boots with no real time accepts stale frames until its timestamp catches up. Such a board can persist timestamp and pass it back to new/2.

Summary

Functions

Encodes frame signed: the signed bit set before the checksum, then the signature with the next timestamp at or after now.

Signing state for key. Options

Signs wire, an encoded frame whose incompat_flags already carry the signed bit, with the next timestamp at or after now (a timestamp/1).

A timestamp for unix_us, Unix time in microseconds.

Checks a decoded frame at now, this end's clock as a timestamp/1. {:ok, state} for a signed frame that verifies and is not a replay, or an unsigned one of an accept_unsigned id; otherwise {:error, :unsigned | :bad_signature | :replay}.

Types

t()

@type t() :: %MAVLink2.Signing{
  accept_unsigned: [non_neg_integer()] | :all,
  key: <<_::256>>,
  link_id: 0..255,
  streams: %{required({0..255, 0..4_294_967_295, 0..255}) => non_neg_integer()},
  timestamp: non_neg_integer()
}

Functions

encode(s, frame, dialect, now)

@spec encode(t(), MAVLink2.Frame.t(), module(), non_neg_integer()) ::
  {:ok, binary(), t()} | {:error, term()}

Encodes frame signed: the signed bit set before the checksum, then the signature with the next timestamp at or after now.

iflag_signed()

@spec iflag_signed() :: 1

new(key, opts \\ [])

@spec new(<<_::256>>, keyword()) :: t()

Signing state for key. Options:

:link_iddefault 0
:accept_unsignedmessage ids delivered even when unsigned, or :all (for a link that is secure by itself, such as USB); default none
:timestampthe earliest timestamp, for a board that persists it; default 0

sign(s, wire, now)

@spec sign(t(), binary(), non_neg_integer()) :: {binary(), t()}

Signs wire, an encoded frame whose incompat_flags already carry the signed bit, with the next timestamp at or after now (a timestamp/1).

timestamp(unix_us)

@spec timestamp(integer()) :: non_neg_integer()

A timestamp for unix_us, Unix time in microseconds.

verify(s, frame, now)

@spec verify(t(), MAVLink2.Frame.t(), non_neg_integer()) ::
  {:ok, t()} | {:error, :unsigned | :bad_signature | :replay}

Checks a decoded frame at now, this end's clock as a timestamp/1. {:ok, state} for a signed frame that verifies and is not a replay, or an unsigned one of an accept_unsigned id; otherwise {:error, :unsigned | :bad_signature | :replay}.