C 消息签名 (mavgen)
One of the key features of MAVLink 2 is support for Message Signing (authentication).
The C libraries generated by mavgen provide almost everything needed to support signing in your MAVLink system. 您需要将一些代码添加到:
- Handle the SETUP_SIGNING message.
- 在链接上设置和拆分签名。
- 在持久存储中保存并加载密钥和时间戳
- 执行回拨以确定哪些(如果有) 未签名消息将被接受。
密钥管理 (SETUP_SIgner)
密钥是32字节的二进制数据, 用于创建可由密钥的其他持有者验证的消息签名。 密钥应在网络中的一个系统 (通常是 GCS) 上创建, 并通过安全通道共享到其他受信任的设备。 系统必须具有共享密钥才能进行通信。 The general requirements for creating, storing, logging and sharing keys are covered in: Message Signing > Secret Key Management.
The section Enabling Signing on a Channel below shows how to set the secret key used by each channel.
处理时间戳
时间戳是48位,从2015年1月1日起,单位为10微秒。 The general requirements for managing timestamps are covered in Message Signing > Timestamp Handling.
该库自动处理一些规则:
- 时间戳在从链接发送的每包消息上都增加一个。
- 更新时间戳,以便与最后接受的消息(如果它大于当前的当地时间戳)相匹配。
- 如果频道上的消息时间戳是在该频道上收到的最后一条消息之前,消息将被拒绝。
每个 MAVLink 系统都有责任储存和恢复时间戳(这对于签字系统的安全至关重要)。 下面的 Enabling Signing on a Channel 部分显示了如何设置时间戳。 The section Enabling Signing on a Channel below shows how to set the timestamp.
Enabling Signing on a Channel
To enable signing on a channel you need to fill in two pointers in the status
structure for the channel. 这两个指针是:
mavlink_signing_t *signing;
mavlink_signing_streams_t *signing_streams;
The signing
pointer controls signing for this stream. 它是按流进行的,包含密匙、时间戳和一组标志,加上接受未签名数据包的可选回调函数。 典型的设置是:
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. 这必须指向所有通道共有的结构, 以防止通道间重播攻击。 典型设置:
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. 这默认为 16, 但对于 gcs 实现来说, 这一点可能是值得的。 如果C的执行超出了签名的流程,那么新流将被拒绝。
使用 accept_unsigned_callback
Message Signing > Accepting Unsigned Packets and 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 structure. 此函数的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. 该功能为执行工作提供了一个途径,允许未签名的数据包被接受(并且错误地签名的数据包,在某些情况下可以接受)。
关于哪些未签名包应该接受的规则是具体的执行,但建议考虑以下规则:
- 有一个机制标记一个特定的通信频道,使其安全(例如 USB 连接),以便能够签名设置。
- always accept
RADIO_STATUS
packets for feedback from 3DR radios (which don't do signing)
例如:
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
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.
使用链接ID的意图是,自动试验机和GCS之间的每个通信渠道都使用不同的链接ID。 但是,没有要求在两个方案中使用同样的链接ID。 但是,没有要求在两个方案中使用同样的链接ID。
C 执行显然的机制是使用 MAVLink 频道编号为链接ID。 这对于自驾仪工作大有助益,但成为 GCS 实现的一个问题。 问题是,用户可以通过不同的通信链接(例如两个无线电或 USB 和 USB)无线电发射多个 GCS 实例,与同一自动飞行器进行交谈。 这些多个 GCS 实例将不会意识到彼此,因此可以选择同一链接ID。 如果发生这种情况,许多正确签名的数据包将被自驾仪拒绝,因为它们的时间戳比收到其他通信链接上相同的流时间戳更早。
The solution adopted for MAVProxy is shown below:
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()
这就是说,如果 MAVProx 使用的当前链接ID为零,它收到一个与非零链接ID的正确签名数据包,那么它将链接ID切换到接收的数据包。
这将使 GCS 从链接ID与自动试验器的链接ID具有影响。