面向读者:Web 地图/看板开发者、第三方客户端开发者、协议对接者。

目的:一份自包含的参考文档,用于解析和验证 APRS-IS 上的 FMO 消息。所有开发者均可通过根证书,验证一条FMO消息是否为真。

声明: FMO自V4后,将不再在APRS-IS广播任何加密内容,需身份确认内容,均使用数字签名技术保证其真实性。

目录

  1. 总览
  2. APRS 包头约定
  3. 通用 Payload 结构
  4. CERT Blob — 证书容器
  5. SIG — Ed25519 签名
  6. 消息类型:CQ 族
  7. 消息类型:BEACON 信标
  8. 消息类型:STATION 台站
  9. 消息类型:JOINT + EVENT 事件联合
  10. 消息类报文 (APFM00)
  11. 验证流程
  12. 常量与编码参考
  13. 完整示例

1. 总览

FMO(NFM Over Internet)设备使用全球 APRS-IS 网络作为发现媒介。设备广播结构化的 POSITION 和 STATUS 帧,携带身份、位置、能力和实时动作。V4 用 PKI 替代了共享密钥认证:每条广播包含用户证书(CERT blob)和 Ed25519 签名(SIG),私钥永不离开设备。

TOCALL 通道

通道 TOCALL 用途
广播 APFMO4 带 CERT + SIG 的 POSITION/STATUS 帧。发现、CQ、信标、台站、事件。

本文档详细介绍广播通道APFMO4)。


2. APRS 包头约定

所有 V4 广播消息遵循此包头格式:

呼号-SSID>APFMO4,TCPIP*:有效载荷
组件 说明
呼号 发送设备的呼号(如 BG5ESN
-SSID APRS SSID,0–15。不属于证书身份字段,仅用于会话/路由上下文。
>APFMO4, TOCALL — V4 广播固定目标地址
TCPIP*: 路径 — 表示消息来自 APRS-IS(非 RF)
有效载荷 V4 payload,详见后续章节

使用的帧类型:

帧类型 前缀 使用场景
POSITION =<lat>N/<table><lon>E<code> CQ、BEACON、STATION、JOINT(除 EVENT STATUS 外的所有消息)
STATUS > 仅 EVENT STATUS(联合模式溢出容器)

完整的 POSITION 帧(包头 + 坐标前缀 + payload)必须在 APRS-IS 512 字符限制之内。所有 V4 消息均在此范围内。


3. 通用 Payload 结构

Payload 部分(POSITION 坐标前缀之后)始终以以下格式开头:

FMO-V4,<TYPE>,...

Token 以逗号(,)分隔。逗号是保留分隔符,不得在文本字段中出现(EVENT.content 例外,见第 9 节)。

3.1 字段编码规则

数据类型 编码 示例
整数 (uid, port, filterKm 等) 十进制 ASCII 文本 12345, 1883, 500
浮点数 (freq) 固定 4 位小数 ASCII 438.5000
文本 (name, url, rig, ant) UTF-8 明文,不含逗号,不做 base64 安吉FMO中继
公钥 (32 字节) Base64url,无 padding= K7gJ2XpQz9...
签名 (64 字节) Base64url,无 padding d4Ef7GhJk1...
哈希/指纹 (32 字节) Base64url,无 padding a1B2c3D4e5...
坐标 (lat/lon) APRS 原始字符串(度-分格式) 3037.98N, 11932.20E

3.2 Payload 中的关键前缀

前缀 含义 示例
FMO-V4, V4 协议标识
CERT: 证书 blob(CBOR → base64url) CERT:dXNLz5G...
SIG: Ed25519 签名(base64url) SIG:R8tY3UjHw5...
SH: SHA-256 哈希(base64url),仅联合模式 SH:y8xV4K...
S 服务器 UID 前缀 S123
FREQ: 工作频率 MHz FREQ:438.5000
HEIGHT: 天线高度 米 HEIGHT:15
RIG: 设备型号 RIG:FT-5DR
ANT: 天线型号 ANT:J型天线
P MQTT 端口前缀 P1883
F 覆盖范围 km,后缀 KM F500KM
U 在线/峰值用户数,格式 U<online>/<peak> U5/200

4. CERT Blob — 证书容器

这是最关键的结构。CERT blob 以紧凑二进制形式携带完整的自包含用户证书。

4.1 线上格式

CERT:<base64url( 10 元素 CBOR 数组 )>

生成步骤:

  1. 构造 10 元素的确定性 CBOR 数组(见下方)。
  2. 将 CBOR 字节编码为 base64url(无 padding)
  3. 前面加上字符串 CERT:

结果约 164–170 字符长(通常 168),例如:

CERT:dXNLz5G...Jq0A

4.2 CBOR 数组结构

CERT blob 是一个10 元素确定性 CBOR 数组。元素 0–8 构成 TBS(待签名)部分;元素 9 是 Intermediate CA 对元素 0–8 的 Ed25519 签名。

索引 CBOR 类型 字段 说明
[0] text string (3) protocol 固定 "FMO"
[1] unsigned integer version 固定 4
[2] text string (8) messageType 固定 "userCert"
[3] unsigned integer issuerSn 签发 Intermediate CA 的序列号
[4] text string callsign 证书主体的呼号(大写 ASCII)
[5] unsigned integer uid 用户唯一数字 ID
[6] byte string (32) publicKey Ed25519 公钥(32 原始字节)
[7] unsigned integer iat 签发时间戳(UTC Unix 秒)
[8] unsigned integer exp 过期时间戳(UTC Unix 秒)
[9] byte string (64) caSignature Intermediate CA 对元素 [0]–[8] 的 Ed25519 签名

TBS(待签名)数组 = 元素 [0] 至 [8]:

["FMO", 4, "userCert", issuerSn, callsign, uid, publicKey(32字节), iat, exp]

位于索引 [9] 的 caSignature 计算方式:

caSignature = Ed25519.sign(IntermediateCA私钥, 确定性CBOR编码(TBS数组))

4.3 certFingerprint 与 certBlobHash

哈希 定义 大小 用途
certFingerprint SHA-256(确定性CBOR编码(TBS数组)) = SHA-256(元素[0]–[8]的CBOR字节) 32 字节 SAS HTTP 认证(MQTT 密码)、CRL 查询
certBlobHash SHA-256(完整CERT blob原始CBOR字节) = SHA-256(元素[0]–[9]的CBOR字节) 32 字节 所有消息类型的 SIG 签名数组

区别很重要:certFingerprint 仅覆盖 TBS(不含签名),而 certBlobHash 覆盖包含 CA 签名的完整 blob,防止 blob 替换攻击。

4.4 解析方法

# 1. 提取 "CERT:" 之后的 base64url 部分
b64 = payload.split("CERT:")[1].split(",")[0]          # 取到下一个逗号前

# 2. Base64url 解码(无 padding)
cbor_bytes = base64url_decode(b64)                       # 约 125 原始字节

# 3. CBOR 解码为 10 元素数组
arr = cbor_decode(cbor_bytes)                            # 确定性 CBOR
assert arr[0] == "FMO"
assert arr[1] == 4
assert arr[2] == "userCert"

# 4. 提取字段
issuer_sn  = arr[3]        # 整数
callsign   = arr[4]        # 字符串(大写)
uid        = arr[5]        # 整数
public_key = arr[6]        # bytes[32]
iat        = arr[7]        # 整数
exp        = arr[8]        # 整数
ca_sig     = arr[9]        # bytes[64]

# 5. 计算 certFingerprint
tbs_cbor = cbor_encode(arr[0:9])                         # 仅元素 0-8
cert_fingerprint = sha256(tbs_cbor)                       # 32 字节

# 6. 计算 certBlobHash
cert_blob_hash = sha256(cbor_bytes)                       # 原始完整 CBOR 字节

CBOR 库建议

  • JavaScript: cbor2 (npm), cbor-x
  • Python: cbor2 (PyPI)
  • Go: github.com/fxamacker/cbor
  • CBOR 为 RFC 8949。确定性编码指:整数用最小形式、文本/字节串用最短长度。

5. SIG — Ed25519 签名

每条广播 POSITION 帧都携带一个 SIG: token,包含 Ed25519 签名。签名不是对 APRS payload 文本的签名,而是对一个固定顺序的确定性 CBOR 数组(称为 TBS 数组)的签名,该数组重构了消息的语义字段。

5.1 域分离

所有签名数组以域分离前缀开始:

["FMO", 4, "<消息类型>", ...]
  • "FMO" — 协议标识(文本字符串)
  • 4 — 协议版本(整数)
  • "<消息类型>" — 以下之一:"CQ""OMCQ""VOCAL""ONLINE""BEACON""STATION""JOINT""userCert"(文本字符串)

这防止了跨上下文签名重放。

5.2 timeSalt — 防重放

每条消息级 SIG 包含一个 timeSalt 作为其最后一个元素

timeSalt = floor(UTC Unix 时间戳 / 600)
  • timeSalt 将时间划分为 10 分钟窗口
  • timeSalt 进入 CBOR 签名数组,但不出现在 APRS payload 文本中。
  • 接收端用三个候选值验证:timeSalttimeSalt - 1timeSalt + 1(±1 窗口容忍网络延迟和时钟偏差)。
  • 消息在发送后最多 ~20 分钟内有效。

5.3 签验

对每种消息类型,接收端必须:

  1. 以完全相同的字段顺序重建完全相同的 CBOR 数组
  2. 对该数组进行确定性 CBOR 编码。
  3. 验证 Ed25519.verify(来自cert的公钥, CBOR字节, 来自SIG token的签名)

公钥来自 CERT blob(元素 [6]),在 cert 自身的 caSignature 已被受信任的 Intermediate CA 验证通过之后使用。

5.4 各消息类型的 TBS 数组

消息 CBOR 数组元素
caSig (CERT 内) ["FMO", 4, "userCert", issuerSn(整数), callsign(文本), uid(整数), pubKey(bytes[32]), iat(整数), exp(整数)]9 元素
CQ / OMCQ / VOCAL / ONLINE ["FMO", 4, "<TYPE>", callsign(文本), ssid(整数), latStr(文本), lonStr(文本), certBlobHash(bytes[32]), serverUID(整数), timeSalt(整数)]10 元素
BEACON ["FMO", 4, "BEACON", callsign(文本), ssid(整数), latStr(文本), lonStr(文本), certBlobHash(bytes[32]), freq(文本"%.4f"), antHeight(整数,可选), rigName(文本,可选), antName(文本,可选), timeSalt(整数)]10–13 元素
STATION ["FMO", 4, "STATION", callsign(文本), ssid(整数), latStr(文本), lonStr(文本), certBlobHash(bytes[32]), cc(大写文本), name(文本), url(文本), port(整数), filterKm(整数), online(整数), peak(整数), timeSalt(整数)]16 元素
JOINT ["FMO", 4, "JOINT", callsign(文本), ssid(整数), latStr(文本), lonStr(文本), certBlobHash(bytes[32]), SH(bytes[32]), timeSalt(整数)]10 元素

5.5 进入 CBOR 编码前的重要规范化

字段 规范化
callsign 大写 ASCII
cc(国家码) 大写,2 字符
latStr / lonStr APRS 原始字符串(如 "3037.98N""11932.20E"
freq(频率) 格式化为 "%.4f" → 文本字符串(如 "438.5000"),不是 CBOR float
certBlobHash / SH 原始 32 字节(不是 base64url)
signature 64 原始字节(不是 base64url)

6. 消息类型:CQ 族

CQ、OMCQ、VOCAL、ONLINE 共用完全相同的线上格式 — 仅 <TYPE> 字段和接收端过滤策略不同。

6.1 线上格式

呼号-SSID>APFMO4,TCPIP*:=<lat>N/<table><lon>E<code> FMO-V4,<TYPE>,CERT:<blob>,S<serverUID>,SIG:<sig>

示例:

BG5ESN-15>APFMO4,TCPIP*:=3037.98N/11932.20EI FMO-V4,CQ,CERT:dXNLz5G...Jq0A,S123,SIG:R8tY3UjHw5...

6.2 字段参考

Token 位置 含义
FMO-V4 0 协议前缀
<TYPE> 1 CQOMCQVOCALONLINE 之一
CERT:... 2 证书 blob
S<uid> 3 发送者当前所连台站 UID(如 S123
SIG:... 4(最后) Ed25519 签名
类型 含义 触发方式
CQ 通用呼叫 手动广播
OMCQ 老友定向呼叫 手动广播
VOCAL 语音活跃通知 PTT ≥ 3 秒自动触发
ONLINE 上线通知 TNC 连接成功自动触发

6.3 TBS 签名数组

TBS = ["FMO", 4, msgType,              # 域分离
       callsign_upper,                  # 文本字符串(大写)
       ssid,                            # 整数
       latStr,                          # 文本字符串,如 "3037.98N"
       lonStr,                          # 文本字符串,如 "11932.20E"
       certBlobHash,                    # bytes[32]
       serverUID,                       # 整数
       timeSalt]                        # 整数, floor(utc/600)

数组长度:10 元素


7. 消息类型:BEACON 信标

BEACON 广播设备的无线电配置。仅用于近距离发现(≤80 km)。

7.1 线上格式

呼号-SSID>APFMO4,TCPIP*:=<lat>N/<table><lon>E<code> FMO-V4,BEACON,CERT:<blob>,FREQ:<freq>,HEIGHT:<h>,RIG:<rig>,ANT:<ant>,SIG:<sig>

示例:

BG5ESN-15>APFMO4,TCPIP*:=3037.98N/11932.20EI FMO-V4,BEACON,CERT:dXNLz5G...Jq0A,FREQ:438.5000,HEIGHT:15,RIG:FT-5DR,ANT:J型天线,SIG:M5nL8VkPq2...

7.2 字段参考

Token 含义 可选?
FREQ:<freq> 工作频率 MHz(4 位小数) 必填
HEIGHT:<h> 天线高度 米(整数) 可选 — 为 0 时省略
RIG:<rig> 设备型号(UTF-8) 可选 — 为空时省略
ANT:<ant> 天线型号(UTF-8) 可选 — 为空时省略

7.3 TBS 签名数组

TBS = ["FMO", 4, "BEACON",
       callsign_upper,     # 文本
       ssid,               # 整数
       latStr,             # 文本
       lonStr,             # 文本
       certBlobHash,       # bytes[32]
       freqStr,            # 文本 "%.4f",如 "438.5000"
       # antHeight (整数)   — 仅 > 0 时包含
       # rigName (文本)     — 仅非空时包含
       # antName (文本)     — 仅非空时包含
       timeSalt]           # 整数

基础长度:10 元素。可选字段会增加数组长度。


8. 消息类型:STATION 台站

STATION 是核心发现消息。台站管理员设备每 600 秒广播一次。携带其他设备用于连接的 MQTT 服务器地址和端口。

8.1 线上格式

呼号-SSID>APFMO4,TCPIP*:=<lat>N/<table><lon>E<code> FMO-V4,STATION,CERT:<blob>,<CC>,<name>,<url>,P<port>,F<km>KM,U<online>/<peak>,SIG:<sig>

示例:

BG5ESN-15>APFMO4,TCPIP*:=3037.98N/11932.20EI FMO-V4,STATION,CERT:dXNLz5G...Jq0A,CN,安吉FMO中继,fmo.bg5esn.com,P1883,F500KM,U5/200,SIG:H1zQ6RfWx8...

8.2 字段参考

Token 顺序 Token 含义 格式
3 CC 国家码(ISO 3166-1 alpha-2) 2 大写字符,如 CNUSJP
4 name 台站/服务器名称 UTF-8 文本,不含逗号
5 url MQTT broker 主机名或 IP fmo.bg5esn.com
6 P<port> MQTT broker 端口 P1883
7 F<km>KM 覆盖半径 km F500KM
8 U<online>/<peak> 当前在线 / 历史峰值用户数 U5/200
最后 SIG:... Ed25519 签名 base64url

8.3 TBS 签名数组

TBS = ["FMO", 4, "STATION",
       callsign_upper,     # 文本
       ssid,               # 整数
       latStr,             # 文本
       lonStr,             # 文本
       certBlobHash,       # bytes[32]
       cc_upper,           # 文本,2 字符
       name,               # 文本,UTF-8
       url,                # 文本
       port,               # 整数
       filterKm,           # 整数
       online,             # 整数
       peak,               # 整数
       timeSalt]           # 整数

数组长度:16 元素


9. 消息类型:JOINT + EVENT 事件联合

EVENT 使用双帧联合模式

  1. POSITION 帧(已签名)— 携带 CERT blob + SH:(STATUS 内容的 SHA-256 哈希)+ SIG。
  2. STATUS 帧(未签名)— 携带实际事件内容。

STATUS 帧不单独签名;其完整性由 POSITION SIG 涵盖 SH 哈希来保护。

9.1 POSITION 帧

呼号-SSID>APFMO4,TCPIP*:=<lat>N/<table><lon>E<code> FMO-V4,JOINT,CERT:<blob>,SH:<hash>,SIG:<sig>

示例:

BG5ESN-15>APFMO4,TCPIP*:=3037.98N/11932.20EI FMO-V4,JOINT,CERT:dXNLz5G...Jq0A,SH:y8xV4K...mP2,SIG:Z3aB9CdEf1...
Token 含义
JOINT 消息类型(POSITION 中始终为 JOINT,与事件类型无关)
SH:<hash> SHA-256(STATUS payload 原始文本) → base64url(43 字符)

9.2 STATUS 帧

呼号-SSID>APFMO4,TCPIP*:>FMO-V4,EVENT,<uid>,<topic>,<content>

示例:

BG5ESN-15>APFMO4,TCPIP*:>FMO-V4,EVENT,12345,WeatherAlert,QTH now heavy rain
Token 含义 约束
EVENT 类型(在 STATUS 中,非 POSITION) 固定
uid 事件发布者 UID 十进制整数
topic 事件主题 最长 32 字符,不含逗号
content 事件内容 最长 140 字符,可包含逗号(占据剩余全部行)

9.3 联合模式流程

发送端:
  1. 构造 STATUS raw = "FMO-V4,EVENT,<uid>,<topic>,<content>"
  2. SH = SHA-256(STATUS raw) → base64url
  3. 广播 POSITION(含 CERT + SH + SIG)
  4. 广播 STATUS(纯文本,无 SIG)

接收端:
  1. 收到 POSITION → 验证 caSig → 提取 pubkey → 验证 SIG → 存储 SH
  2. 收到 STATUS   → 计算 SHA-256(STATUS raw) → 与 SH 比对
  3. SH 匹配 → 处理 EVENT(完整性由 POSITION SIG 保证)

9.4 TBS 签名数组

TBS = ["FMO", 4, "JOINT",
       callsign_upper,     # 文本
       ssid,               # 整数
       latStr,             # 文本
       lonStr,             # 文本
       certBlobHash,       # bytes[32]
       SH,                 # bytes[32] — 原始 SHA-256,不是 base64url
       timeSalt]           # 整数

数组长度:10 元素


10. 消息类报文 (APFM00)

点对点消息使用不同的 TOCALL(APFM00),不属于 V4 PKI 体系。不携带 CERT blob 或签名。受 APRS MESSAGE 67 字节限制。

线上格式:

呼号-SSID>APFM00,TCPIP*::目标9列    :<命令>,<参数...>{<序号>

关键约定:

  • ::: 之间的目标字段固定 9 列,右侧空格填充。
  • 查询使用"无尾缀呼号"作为目标;回复使用"完整呼号"。
  • {INDEX 为透明消息编号,应答/ACK 中原样回传。
命令 方向 用途
QTHQRY A → B 查询好友状态
QTHANS B → A 查询应答
CALL 主叫 → 被叫 发起呼叫
CALLANS 被叫 → 主叫 呼叫回执(RING / ACCEPT / REJECT / …)
CALLCANCEL 主叫 → 被叫 取消呼叫
ACK 双向 消息确认
MSG 双向 短消息
CONTROL 控制端 → 设备 远程控制指令
CTLACK 设备 → 控制端 控制应答

详见 docs/FMOAPRS消息协议.md


11. 验证流程

接收端处理 V4 POSITION 帧的完整验证管道:

输入: 原始 APRS 字符串

1. 解析包头
   - 从包头('>' 左侧)提取 callsign、ssid
   - 从坐标前缀提取 latStr、lonStr

2. 解析 payload
   - 按逗号分割
   - 从 tokens[1] 识别消息类型
   - 从以 "CERT:" 开头的 token 提取 CERT blob
   - 从最后一个以 "SIG:" 开头的 token 提取签名
   - 按消息类型提取其他字段

3. 解码 CERT blob
   - 去掉 "CERT:" 前缀
   - Base64url 解码 → 原始 CBOR 字节
   - CBOR 解码 → 10 元素数组
   - 提取: callsign, uid, pubKey, issuerSn, iat, exp, caSig

4. 呼号交叉验证
   - cert.callsign(大写)必须 == 包头 callsign(大写,忽略 SSID)
   - 失败 → CallMismatch,丢弃

5. 验证 caSig(证书链)
   - 重建 TBS: ["FMO", 4, "userCert", issuerSn, callsign, uid, pubKey, iat, exp]
   - 确定性 CBOR 编码 TBS
   - Ed25519.verify(IntermediateCA.pubKey, TBS_CBOR, caSig)
   - 失败 → CertChainInvalid,丢弃

6. 检查证书有效期
   - iat ≤ nowUtc < exp
   - 失败 → CertExpired,丢弃

7. 验证用户 SIG
   - 按消息类型重建 TBS 数组(见第 5.4 节)
   - 确定性 CBOR 编码 TBS
   - 计算 timeSalt = floor(nowUtc / 600)
   - 分别尝试 timeSalt, timeSalt-1, timeSalt+1
   - Ed25519.verify(cert.pubKey, TBS_CBOR, sig_from_payload)
   - 全部失败 → SigInvalid 或过期/重放,丢弃

8. (可选) 查询 CRL
   - 在 Intermediate CRL 中查询 certFingerprint
   - 在 Root CRL 中查询 issuerSn
   - 找到 → CertRevoked,丢弃

9. 通过 → 处理业务逻辑

12. 常量与编码参考

12.1 加密常量

常量 说明
ED25519_PUBKEY_LEN 32 字节 Ed25519 公钥长度
ED25519_SIGN_LEN 64 字节 Ed25519 签名长度
CERT_FINGERPRINT_LEN 32 字节 SHA-256 输出(指纹 / blob hash)
SHA-256 输出 32 字节 用于指纹和 SH 的哈希算法

12.2 Base64url 编码

  • 字母表:A-Za-z0-9-_(RFC 4648 §5)
  • 无 padding — 始终省略 = 字符。
  • 解码器应同时兼容带 padding 和不带 padding 的输入。

12.3 消息类型常量

常量 线上值 上下文
TOCALL 广播 APFMO4 V4 广播帧
TOCALL 消息 APFM00 点对点消息帧
路径 TCPIP* APRS-IS 来源
Payload 前缀 FMO-V4 所有 V4 payload
CERT 前缀 CERT: 证书 blob
SIG 前缀 SIG: Ed25519 签名
SH 前缀 SH: SHA-256 哈希(联合模式)

12.4 CBOR 类型映射

语义类型 CBOR 主类型 备注
协议字符串 (“FMO”) text string (major 3) 固定
消息类型字符串 text string (major 3) 如 “CQ”, “BEACON”
大写呼号 text string (major 3) 编码前规范化
坐标 (lat/lon) text string (major 3) APRS 原始格式,如 “3037.98N”
自由文本 (name, url) text string (major 3) UTF-8
整数 (uid, port 等) unsigned integer (major 0) 最小形式编码
公钥 (32 字节) byte string (major 2) 原始字节
签名 (64 字节) byte string (major 2) 原始字节
哈希 (32 字节) byte string (major 2) 原始字节
版本号 (4) unsigned integer (major 0) 最小形式
布尔值 special (major 7, value 20/21) 用于 CA 证书 (isCA)
国家码 (2 字符) text string (major 3) 大写,2 字符
freq (BEACON) text string (major 3) 格式化 “%.4f”,非 CBOR float

13. 完整示例

13.1 CQ 广播解析

原始 APRS 帧:

BG5ESN-15>APFMO4,TCPIP*:=3037.98N/11932.20EI FMO-V4,CQ,CERT:g2FDhKdFmP...Lq0A,S123,SIG:R8tY3UjHw5...XpQz9

解析后的包头:

字段
callsign BG5ESN
ssid 15
latStr 3037.98N
lonStr 11932.20E

解析后的 payload token:

索引 Token 含义
0 FMO-V4 协议
1 CQ 消息类型
2 CERT:g2FDhKdFmP...Lq0A 证书 blob (base64url)
3 S123 服务器 UID = 123
4 SIG:R8tY3UjHw5...XpQz9 Ed25519 签名 (base64url, 86 字符)

CERT blob 解码(10 元素 CBOR 数组 → 逻辑字段):

字段
protocol "FMO"
version 4
type "userCert"
issuerSn 1001
callsign "BG5ESN"
uid 1234
publicKey [32 字节]
iat 1783291391
exp 1898999829
caSignature [64 字节]

SIG TBS 数组(10 元素):

["FMO", 4, "CQ", "BG5ESN", 15, "3037.98N", "11932.20E", <certBlobHash 32B>, 123, <timeSalt>]

13.2 STATION 广播解析

原始 APRS 帧:

BG5ESN-15>APFMO4,TCPIP*:=3037.98N/11932.20EI FMO-V4,STATION,CERT:g2FDhKdFmP...,CN,安吉FMO中继,fmo.bg5esn.com,P1883,F500KM,U5/200,SIG:H1zQ6RfWx8...

解析后的 payload:

索引 Token 含义
0 FMO-V4 协议
1 STATION 类型
2 CERT:g2FDhKdFmP... 证书 blob
3 CN 国家码
4 安吉FMO中继 台站名称
5 fmo.bg5esn.com MQTT 服务器 URL
6 P1883 MQTT 端口 = 1883
7 F500KM 覆盖范围 = 500 km
8 U5/200 在线=5, 峰值=200
9 SIG:H1zQ6RfWx8... 签名

SIG TBS 数组(16 元素):

["FMO", 4, "STATION",
 "BG5ESN", 15, "3037.98N", "11932.20E",
 <certBlobHash 32B>,
 "CN", "安吉FMO中继", "fmo.bg5esn.com",
 1883, 500, 5, 200, <timeSalt>]

13.3 EVENT 联合模式

POSITION 帧:

BG5ESN-15>APFMO4,TCPIP*:=3037.98N/11932.20EI FMO-V4,JOINT,CERT:g2FDhKdFmP...,SH:y8xV4KmP2...,SIG:Z3aB9CdEf1...

STATUS 帧:

BG5ESN-15>APFMO4,TCPIP*:>FMO-V4,EVENT,12345,WeatherAlert,QTH now heavy rain

接收端验证:

  1. 验证 POSITION SIG(涵盖 SH)。
  2. 计算 SHA-256("FMO-V4,EVENT,12345,WeatherAlert,QTH now heavy rain") = 原始 SH。
  3. 与 POSITION 中的 SH: 比对。
  4. 匹配 → 信任 EVENT 内容。

附录:快速参考卡

所有 V4 消息类型一览

Payload 类型 帧类型 TBS 元素数 有 SIG 描述
CQ POSITION 10 通用呼叫
OMCQ POSITION 10 老友定向呼叫
VOCAL POSITION 10 语音活跃通知
ONLINE POSITION 10 上线通知
BEACON POSITION 10–13 设备信标
STATION POSITION 16 服务器广播
JOINT POSITION 10 联合模式锚点
EVENT STATUS N/A 联合模式内容
(多种) MESSAGE (APFM00) N/A 点对点消息

关键尺寸一览

项目 原始大小 Base64url 大小
Ed25519 公钥 32 字节 43 字符
Ed25519 签名 64 字节 86 字符
SHA-256 哈希 32 字节 43 字符
CERT blob (CBOR) ~125 字节 ~164–168 字符 + 5 (CERT:)
完整 CQ POSITION 帧 ~339 字符
完整 STATION POSITION 帧 ~411 字符

当前FMO国内所使用根证书和中间证书

//Root self sign CA Cert
{
  "sn": 1,
  "type": "rootCA",
  "issuer": {
    "name": "BG5ESN",
    "email": "xifengzui@yeah.net"
  },
  "subject": {
    "name": "BG5ESN",
    "publicKey": "DCeeVS320f36ToVP2eOADVN-Q0LzpMYmiVkmNYzuysY"
  },
  "extensions": {
    "isCA": true,
    "pathLen": 1,
    "crl": "https://bg5esn.com/share/ca/root_crl.json",
    "license": "https://bg5esn.com/share/ca/license.md",
    "keyId": "1"
  },
  "iat": 1779886213,
  "exp": 2095505415,
  "signatureAlgorithm": "Ed25519",
  "signature": "9QTm906Da0Hf5z2HMdvT02tuYCHyYOcPTNi3Y1Bi-cqpos-Pms_ok7rScAwt2pUSQN34cuKydkyKwdus0N_6CA"
}

//Intermediate CA Cert
{
  "sn": 1001,
  "type": "intermediateCA",
  "issuer": {
    "sn": 1,
    "name": "BG5ESN",
    "publicKey": "DCeeVS320f36ToVP2eOADVN-Q0LzpMYmiVkmNYzuysY"
  },
  "subject": {
    "name": "BG5ESN",
    "email": "xifengzui@yeah.net",
    "publicKey": "gYPN5agzrKZG2iyEztsVjGD1tVNLozHNm_km7n6OQyk"
  },
  "extensions": {
    "isCA": true,
    "pathLen": 0,
    "keyId": "1",
    "crl": "https://bg5esn.com/share/ca/intermediate_crl.json",
    "license": "https://bg5esn.com/share/ca/intermediate_license.md",
    "uidRange": {
      "start": 1,
      "end": 200000
    },
    "issuingCountries": [
      "CN"
    ]
  },
  "iat": 1779886432,
  "exp": 1937652834,
  "signatureAlgorithm": "Ed25519",
  "signature": "d-G7if4xAA5HTe39kT59tGhPY-WjLfXh1PgdpiR_T13zgNVuwHnWLiTORtWLhfW7kF1fN6YGpCpT9FFh0NKFDA"
}

文档版本:v1.0