# C Message Signing (mavgen)

One of the key features of [MAVLink 2](../guide/mavlink_2.md) is support for [Message Signing](../guide/message_signing.md) (authentication).

The C libraries generated by _mavgen_ provide almost everything needed to support signing in your MAVLink system.
You will need to add some code to:

- Handle the [SETUP_SIGNING](../messages/common.md#SETUP_SIGNING) message.
- Setup and teardown signing on a link.
- Save and load the secret key and timestamp in persistent storage
- Implement a callback to define which (if any) unsigned messages will be accepted.

## Secret Key Management (SETUP_SIGNING)

A secret key is 32 bytes of binary data that are used to create message signatures that can be verified by other holders of the key.
The general requirements for creating, storing, logging and sharing keys are covered in: [Message Signing > Secret Key Management](../guide/message_signing.md#secret_key).

The section [Enabling Signing on a Channel](#enabling_signing_channel) below shows how to set the secret key used by each channel.

<!--
The [SETUP_SIGNING](../messages/common.md#SETUP_SIGNING) message should generally be used for sharing the secret key, and support for it must be implemented on both sending and receiving systems. Receiving systems must also store the key in secure storage.

how pass key to system after calculation? i.e. is there a set-key method?
What this should show is
- how to generate sha256 from paraphrase
- how to handle received message and store the key (on nuttx and Linux)
-->

## Handling Timestamps

The timestamp is a 48 bit number with units of 10 microseconds since 1st January 2015 GMT.
The general requirements for managing timestamps are covered in [Message Signing > Timestamp Handling](../guide/message_signing.md#timestamp).

The library automatically handles some of the rules:

- timestamps are incremented by one on every message send from a link.
- timestamps are updated to match that of the last accepted incoming message (if it is greater than the current local timestamp).
- messages are rejected if the timestamp of a message on a channel is before the last message received on that channel.

It is the responsibility of each MAVLink system to store and restore the timestamp into persistent storage (this is critical for the security of the signing system).
The section [Enabling Signing on a Channel](#enabling_signing_channel) below shows how to set the timestamp.

<!--
For systems where the time since 1/1/1970 is available (the unix epoch) you can use an offset in seconds of 1420070400.
It is the responsibility of each MAVLink system to store and restore the timestamp into persistent storage (this is critical for the security of the signing system).
* The current timestamp should be stored regularly in persistent storage (suggested at least once a minute)
* The timestamp used on startup should be the maximum of the timestamp implied by the system clock and the stored timestamp
* If the system does not have a RTC mechanism then it should update its timestamp when GPS lock is achieved. The maximum of the timestamp from the GPS and the stored timestamp should be used
* The timestamp should be incremented by one on each message sent on a particular link. This is done for you by the generated headers.
* When a correctly signed message is decoded the timestamp should be replaced by the timestamp of the incoming message if that timestamp is greater than the current timestamp. This is done for you by the generated headers
* The timestamp on incoming signed messages should be checked against the previous timestamp for the incoming `(linkID,srcSystem,SrcComponent)` tuple and the message rejected if it is smaller. This is done for you by generated headers.
* If there is no previous message with the given `(linkID,srcSystem,SrcComponent)` then the timestamp should be accepted if it not more than 6 million (one minute) behind the current timestamp
-->

## Enabling Signing on a Channel {#enabling_signing_channel}

To enable signing on a channel you need to fill in two pointers in the `status` structure for the channel.
The two pointers are:

```c
mavlink_signing_t *signing;
mavlink_signing_streams_t *signing_streams;
```

The `signing` pointer controls signing for this stream.
It is per-stream, and contains the secret key, the timestamp and a set of flags, plus an optional callback function for accepting unsigned packets. Typical setup would be:

```c
mavlink_signing_t signing;
memset(&signing, 0, sizeof(signing));
memcpy(signing.secret_key, key.secret_key, 32);
signing.link_id = (uint8_t)chan;
signing.timestamp = key.timestamp;
signing.flags = MAVLINK_SIGNING_FLAG_SIGN_OUTGOING;
signing.accept_unsigned_callback = accept_unsigned_callback;

mavlink_status_t *status = mavlink_get_channel_status(chan);
status.signing = &signing;
```

The `signing_streams` pointer is a structure used to record the previous timestamp for a `(linkId,srcSystem,SrcComponent)` tuple.
This must point to a structure that is common to all channels in order to prevent inter-channel replay attacks.
Typical setup is:

```c
mavlink_status_t *status = mavlink_get_channel_status(chan);
status.signing_streams = &signing_streams;
```

The maximum number of signing streams supported is given by the `MAVLINK_MAX_SIGNING_STREAMS` macro.
This defaults to 16, but it may be worth raising this for GCS implementations.
If the C implementation runs out of signing streams then new streams will be rejected.

## Using accept_unsigned_callback

[Message Signing > Accepting Unsigned Packets](../guide/message_signing.md#accepting_unsigned_packets) and [Accepting Incorrectly Signed Packets](../guide/message_signing.md#accepting_incorrectly_signed_packets) specify that a message signing implementation should provide mechanisms such that library users can choose to conditionally accept unsigned or incorrectly signed packets.

The C implementation provides the `accept_unsigned_callback()` function pointer for this purpose, which may optionally be set in the [signing](#enabling_signing_channel) structure.
The C prototype for this function is:

```c
bool accept_unsigned_callback(const mavlink_status_t *status, uint32_t msgId);
```

If set then this function will be called on any unsigned packet (including all _MAVLink 1_ packets) or any packet where the signature is incorrect.
The function offers a way for the implementation to allow unsigned packets to be accepted (and incorrectly signed packets, which might be accepted under some circumstances).

The rules for what unsigned packets should be accepted is implementation specific, but it is suggested the following rules be considered:

- have a mechanism for marking a particular communication channel as being secure (such as a USB connection) to allow for signing setup.
- always accept `RADIO_STATUS` packets for feedback from 3DR radios (which don't do signing)

For example:

```c
static const uint32_t unsigned_messages[] = {
	MAVLINK_MSG_ID_RADIO_STATUS
};

static bool accept_unsigned_callback(const mavlink_status_t *status, uint32_t message_id)
{
	// Make the assumption that channel 0 is USB and should always be accessible
	if (status == mavlink_get_channel_status(MAVLINK_COMM_0)) {
		return true;
	}

	for (unsigned i = 0; i < sizeof(unsigned_messages) / sizeof(unsigned_messages[0]); i++) {
		if (unsigned_messages[i] == message_id) {
			return true;
		}
	}

	return false;
}
```

## Handling Link IDs {#handling_link_ids}

The purpose of the `link_id` field in the _MAVLink 2_ signing structure is to prevent cross-channel replay attacks.
Without the `link_id` an attacker could record a packet (such as a disarm request) on one channel, then play it back on a different channel.

The intention with the link IDs is that each channel of communication between an autopilot and a GCS uses a different link ID.
There is no requirement that the same link ID be used in both directions however.

For C implementations the obvious mechanism is to use the MAVLink channel number as the link ID.
That works well for an autopilot, but runs into an issue for a GCS implementation.
The issue is that a user may launch multiple GCS instances talking to the same autopilot via different communication links (such as two radios, or USB and a radio).
These multiple GCS instances will not be aware of each other, and so may choose the same link ID.
If that happens then a large number of correctly signed packets will be rejected by the autopilot as they will have timestamps that are older than the timestamp received for the same stream tuple on the other communication link.

The solution adopted for _MAVProxy_ is shown below:

```c
if (msg.get_signed() and
	self.mav.signing.link_id == 0 and
	msg.get_link_id() != 0 and
	self.target_system == msg.get_srcSystem() and
	self.target_component == msg.get_srcComponent()):
	# change to link_id from incoming packet
	self.mav.signing.link_id = msg.get_link_id()
```

What that says is that if the current link ID in use by MAVProxy is zero, and it receives a correctly signed packet with a non-zero link ID then it switches link ID to the one from the incoming packet.

This has the effect of making the GCS slave its link ID to the link ID of the autopilot.
