本软件包提供了安全实时传输协议(SRTP)、通用安全转换(UST)以及配套加密内核的实现。SRTP API 文档见 include/srtp.h,库文件为 libsrtp2.a(编译后生成)。
本文档描述 libSRTP,即来自 Cisco Systems, Inc. 的开源安全 RTP 库。RTP 是实时传输协议(Real-time Transport Protocol),是 IETF 定义的用于传输电话、音频和视频等实时数据的标准,参见 RFC 3550。安全 RTP(SRTP)是 RTP 的一种轮廓(profile),用于为 RTP 数据提供机密性,并为 RTP 头部和载荷提供认证。SRTP 是 IETF 标准,定义于 RFC 3711,由 IETF 音视频传输(AVT)工作组制定。本库支持 SRTP 的全部强制特性,但不支持全部可选特性。更多详细信息请参见支持的特性一节。
本文档还用于生成 /doc/ 文件夹中的文档文件,其中可以创建更详细的 libSRTP API 及相关函数的参考手册(需要安装 doxygen)。参考材料由部分 C 头文件中嵌入的注释自动生成。文档按模块组织以提高清晰度,这些模块并不直接对应文件。底层加密内核提供了 libSRTP 的大部分基础功能,但由于其在幕后工作,因此大多没有文档说明。
[email protected] 用于新闻/公告/讨论的通用邮件列表。这是一个开放列表,注册请见 https://lists.packetizer.com/mailman/listinfo/libsrtp。
[email protected] 用于向 libsrtp 维护团队披露安全问题。这是一个封闭列表,但任何人都可以向其发送邮件。
libSRTP 按以下许可证分发,该许可证包含在源代码发行版中。此处将其转载于手册中,以防您从其他来源获取该库。
版权所有(c)2001-2017 Cisco Systems, Inc. 保留所有权利。
允许以源代码和二进制形式重新分发和使用,无论是否经过修改,前提是满足以下条件:
- 源代码的再分发必须保留上述版权声明、此条件列表和以下免责声明。
- 二进制形式的再分发必须在提供的文档和/或其他材料中复现上述版权声明、此条件列表和以下免责声明。
- 未经事先明确书面许可,不得使用 Cisco Systems, Inc. 的名称或其贡献者的名称来认可或推广从本软件衍生的产品。
本软件由版权所有者和贡献者“按原样”提供,不提供任何明示或暗示的担保,包括但不限于适销性和特定用途适用性的暗示担保。在任何情况下,版权所有者和贡献者均不对任何直接、间接、附带、特殊、惩戒性或后果性损害(包括但不限于采购替代商品或服务;使用、数据或利润的损失;或业务中断)承担责任,无论其原因如何,也不论是基于合同、严格责任还是侵权行为(包括疏忽或其他方式),即使已被告知此类损害的可能性。
libSRTP 提供保护 RTP 和 RTCP 的函数。RTP 数据包可以被加密和认证(使用 srtp_protect() 函数),从而转换为 SRTP 数据包。类似地,SRTP 数据包可以被解密并验证其认证信息(使用 srtp_unprotect() 函数),从而转换回 RTP 数据包。类似的函数可将安全性应用于 RTCP 数据包。
typedef srtp_stream_t 指向一个结构体,该结构体保存与 SRTP 流相关的所有状态,包括密钥、密码和消息认证函数的参数以及防重放数据。特定的 srtp_stream_t 保存保护特定 RTP 和 RTCP 流所需的信息。此数据类型有意保持不透明,以便更好地将 libSRTP API 与其实现分离。
在一个 SRTP 会话中,可以有多个流,每个流源自特定的发送方。每个源使用不同的流上下文来保护其发起的 RTP 和 RTCP 流。typedef srtp_t 指向一个结构体,该结构体保存与 SRTP 会话相关的所有状态。一个 srtp_t 可以关联多个流上下文。流上下文不能独立于 srtp_t 存在,当然可以创建一个仅包含单个流上下文的 srtp_t。参与 SRTP 会话的设备必须为会话中的每个源提供一个流上下文,以便处理从每个发送方接收的数据。
在 libSRTP 中,使用 srtp_create() 函数创建会话。要在会话中实施的策略作为不透明的 srtp_policy_t 句柄传递给该函数。单个策略句柄描述一个流策略。要配置多个流,请创建一个会话,并使用 srtp_stream_add() 添加其他策略。
策略句柄通过 srtp_policy_set_* 函数进行配置。至少包括 SSRC 选择、轮廓选择和密钥/盐材料。轮廓配置 RTP/RTCP 加密策略设置,而 SSRC 选择器标识该策略的应用位置和方式。
在本节中,我们回顾 SRTP 并介绍 libSRTP 中使用的一些术语。RTP 会话由一对目标传输地址定义,即一个网络地址加上一对用于 RTP 和 RTCP 的 UDP 端口。RTCP(RTP 控制协议)用于协调 RTP 会话中的参与者,例如提供从接收方到发送方的反馈。SRTP 会话 的定义类似;它只是使用 SRTP 轮廓的 RTP 会话。SRTP 会话由发送到 SRTP 或 SRTCP 目标传输地址的流量组成。会话中的每个参与者由同步源(SSRC)标识符标识。有些参与者可能不发送任何 SRTP 流量;它们被称为接收方,尽管它们会发送 SRTCP 流量,例如接收方报告。
RTP 允许多个源在同一会话期间发送 RTP 和 RTCP 流量。同步源标识符(SSRC)用于区分这些源。在 libSRTP 中,我们将来自特定源的 SRTP 和 SRTCP 流量称为流。每个流都有自己的 SSRC、序列号、翻转计数器和其他数据。特定的选项、加密机制和密钥组合称为策略。会话中的每个流都可以应用不同的策略。
一个给定会话中的所有流可以使用同一个策略,但多个流共享单个密钥的情况需要谨慎处理。当使用密钥共享时,标识流的 SSRC 值必须各不相同。可以通过以下约定来强制满足此要求:每个 SRTP 和 SRTCP 密钥仅由单个发送方用于加密。换句话说,该密钥仅在源自特定设备的流之间共享(当然,其他 SRTP 参与者需要使用该密钥进行解密)。libSRTP 通过检测密钥同时用于入站和出站数据的情况来支持这种强制机制。
本库支持 SRTP 的所有强制实现特性(如 RFC 3711 所定义)。其中一些特性可以在运行时通过使用 srtp_policy_t 句柄设置适当的策略来选择(或取消选择)。协议的其他一些行为可以通过为异常事件定义适当的事件处理程序来调整;请参阅生成文档中的 SRTPevents 一节。
SRTP 规范中描述的某些选项不受支持。这包括
用户应当注意,可能误用本库,从而导致其提供的安全级别不足。如果您正在使用本库实现某项功能,您需要阅读 RFC 3711 的安全注意事项一节。此外,务必阅读并理解许可证与免责声明一节中概述的条款。
本库还支持 RFC 7714 中描述的 AES-GCM 认证加密方法。
可以配置 libSRTP 将使用哪个第三方(例如 openssl/nss 等)加密后端进行构建。如果未设置第三方后端,libSRTP 将提供 AES 和 Sha1 的内部实现。内部实现仅支持 AES-128 和 AES-256,因此要使用 AES-192 或 AES-GCM 密码组,必须配置第三方加密后端。出于此原因以及性能方面的考虑,强烈建议使用第三方加密后端。
srtp_protect() 函数假定保存 rtp 数据包的缓冲区已分配足够的存储空间,以便将认证标签写入该数据包的末尾。如果此假定不成立,将导致内存损坏。
通过 cipher_type_self_test() 和 auth_type_self_test() 函数提供加密函数的自动化测试。应使用这些函数来测试此代码向新平台的每次移植。
重放保护包含在加密引擎中,并提供了相应的测试。
此实现提供了初始化、保护和解保护 RTP 数据包的调用,并尽可能少地对这些函数的调用方式进行假设。例如,不要求调用者按顺序提供数据包(但如果乱序超过 65k 个数据包,同步将会丢失)。
rtp 数据包中的序列号用作发送方本地数据包索引的低 16 位。请注意,RTP 会从随机位置开始其序列号,SRTP 层在其首次调用时直接跳到该数字。此库的早期版本使用小于 32,768 的初始序列号;自版本 1.0.1 起,rdbx_estimate_index(...) 函数已变得更加智能,因此不再需要此技巧。
(S)RTCP 的重放窗口硬编码为 128 位长度。
要安装 libSRTP,请从 https://github.com/cisco/libsrtp/releases 下载最新版本的发行包。您可能希望获取最新的发布版本。解压发行包并提取源文件;源文件所在的目录名为 libsrtp-A-B-C,其中 A 是版本号,B 是主版本号,C 是次版本号。
libSRTP 使用 GNU autoconf 和 make 工具(BSD make 无法使用;如果您的平台上有两个版本的 make,您可以将 GNU make 作为 gmake 调用)。在 libsrtp 目录中,运行 configure 脚本,然后执行 make:~~~.txt
./configure [ options ]
make
The configure script accepts the following options:
选项 | 描述
-------------------------------|--------------------
\-\-help \-h | 显示帮助
\-\-enable-debug-logging | 在所有模块中启用调试日志
\-\-enable-openssl | 启用 OpenSSL 加密引擎
\-\-enable-nss | 启用 NSS 加密引擎
\-\-enable-openssl-kdf | 启用 OpenSSL KDF 算法
\-\-enable-log-stdout | 启用输出到 stdout 的日志
\-\-with-openssl-dir | OpenSSL 安装位置
\-\-with-nss-dir | NSS 安装位置
\-\-with-log-file | 使用文件进行日志记录
默认情况下没有日志输出,可以使用配置选项启用日志,使其输出到 stdout 或指定文件。
此软件包已在以下平台上测试:Mac OS X (powerpc-apple-darwin1.4)、Cygwin (i686-pc-cygwin)、Solaris (sparc-sun-solaris2.6)、RedHat Linux 7.1 和 9 (i686-pc-linux),以及 OpenBSD (sparc-unknown-openbsd2.7)。
--------------------------------------------------------------------------------
<a name="changing-build-configuration"></a>
## 更改构建配置
要构建上面提到的 `./configure` 脚本,libSRTP 依赖于 [automake](https://www.gnu.org/software/automake/) 工具链。由于 `./configure` 是由 automake 从 `configure.in` 构建的,如果你对 `./configure` 的工作方式做出更改(例如,添加新的库依赖),你将需要重新构建 `./configure` 并提交更新后的版本。除了 automake 本身之外,你还需要安装 `pkgconfig` 工具。
例如,在 macOS 上:```
brew install automake pkgconfig
# Edit configure.in
autoremake -ivf
```
<a name="using-visual-studio"></a>
## 使用 Visual Studio
在 Windows 上,可以通过 CMake 使用 Visual Studio。CMake 可在此处下载:
https://cmake.org/ 。例如,要创建 Visual Studio 构建文件,请运行以下命令:```
# Create build subdirectory
mkdir build
cd build
# Make project files
cmake .. -G "Visual Studio 15 2017"
# Or for 64 bit project files
cmake .. -G "Visual Studio 15 2017 Win64"
```
<a name="using-meson"></a>
## 使用 Meson
在所有平台(包括 Windows)上,都可以使用 [Meson](https://mesonbuild.com) 进行构建。
下载 Meson 的步骤参见:https://mesonbuild.com/Getting-meson.html
使用 Meson 构建时,可以执行类似以下操作:```
# Setup the build subdirectory
meson setup --prefix=/path/to/prefix builddir
# Build the project
meson compile -C builddir
# Run tests
meson test -C builddir
# Optionally, install
meson install -C builddir
```
要用 Visual Studio 生成,请从 Visual Studio 命令提示符中运行上述命令,或在命令提示符内使用适当的参数运行 `vcvarsall.bat`。
请注意,你还可以将上述命令替换为相应的 `ninja` 目标:`ninja -C build`、`ninja -C build test`、`ninja -C build install`。
--------------------------------------------------------------------------------
<a name="applications"></a>
# 应用程序
`test/` 子目录中包含几个测试驱动程序和一个简单、可移植的 srtp 应用程序。
测试驱动程序 | 被测试功能
--------- | -------
kernel_driver | 加密内核(加密算法、认证函数、随机数生成器)
srtp_driver | srtp 内存测试(不使用网络)
rdbx_driver | rdbx(扩展重放数据库)
roc_driver | 扩展序列号函数
replay_driver | 重放数据库
cipher_driver | 加密算法
auth_driver | 哈希函数
应用 `rtpw` 是一个简单的 rtp 应用程序,它会从 `/usr/dict/words` 读取单词,并使用 [s]rtp 一次一个地发送出去。手动 srtp 密钥管理使用 -k 选项;使用 gdoi 的自动密钥管理将在以后添加。
用法:~~~.txt
rtpw [[-d <debug>]* [-k|b <key> [-a][-e <key size>][-g]] [-s | -r] dest_ip dest_port] | [-l]
必须选择 -s(发送方)或 -r(接收方)选项。
dest_ip、dest_port 分别是字典将要发送到的 IP 地址和 UDP 端口。
选项如下:
为了获得随机的 30 字节值用作密钥/盐对,你可以使用以下 bash 函数来格式化 /dev/random 的输出(在该设备可用的情况下)。~~~.txt
function randhex() {
cat /dev/random | od --read-bytes=32 --width=32 -x | awk '{ print $2 $3 $4 $5 $6 $7 $8 $9 $10 $11 $12 $13 $14 $15 $16 }'
}
以下是一个使用两个 rtpw 程序的 SRTP 会话示例:~~~.txt
set k=c1eec3717da76195bb878578790af71c4ee9f859e197a414a78d5abc7451
[sh1]$ test/rtpw -s -k $k -e 128 -a 0.0.0.0 9999
Security services: confidentiality message authentication
set master key/salt to C1EEC3717DA76195BB878578790AF71C/4EE9F859E197A414A78D5ABC7451
setting SSRC to 2078917053
sending word: A
sending word: a
sending word: aa
sending word: aal
...
[sh2]$ test/rtpw -r -k $k -e 128 -a 0.0.0.0 9999
security services: confidentiality message authentication
set master key/salt to C1EEC3717DA76195BB878578790AF71C/4EE9F859E197A414A78D5ABC7451
19 octets received from SSRC 2078917053 word: A
19 octets received from SSRC 2078917053 word: a
20 octets received from SSRC 2078917053 word: aa
21 octets received from SSRC 2078917053 word: aal
...
本节提供了一个如何使用 libSRTP 的简单示例。这里我们假设
函数 get_rtp_packet() 和 send_srtp_packet() 可供我们使用。
前者将一个 RTP 数据包放入缓冲区,并返回写入该缓冲区的八位字节数。
后者以缓冲区长度作为第二个参数,发送缓冲区中的 RTP 数据包。~~~.c
srtp_t session;
srtp_policy_t policy;
// Set key/salt to predetermined values. uint8_t master_key[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; uint8_t master_salt[14] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D};
// Initialize libSRTP. srtp_init();
// Create and configure an opaque policy handle. srtp_policy_create(&policy); srtp_policy_set_ssrc(policy, (srtp_ssrc_t){ssrc_any_outbound, 0}); srtp_policy_set_profile(policy, srtp_profile_aes128_cm_sha1_80); srtp_policy_add_key(policy, master_key, sizeof(master_key), master_salt, sizeof(master_salt), NULL, 0);
// Allocate and initialize the SRTP session. srtp_create(&session, policy);
srtp_policy_destroy(policy);
// Main loop: get RTP packets, send SRTP packets. while (1) { char rtp_buffer[2048]; size_t rtp_len; char srtp_buffer[2048]; size_t srtp_len = sizeof(srtp_buffer);
rtp_len = get_rtp_packet(rtp_buffer); srtp_protect(session, rtp_buffer, rtp_len, srtp_buffer, &srtp_len); send_srtp_packet(srtp_buffer, srtp_len); }
srtp_dealloc(session); srtp_shutdown();
--------------------------------------------------------------------------------
<a name="credits"></a>
# 致谢
libSRTP 的原始实现和文档由 Cisco Systems, Inc. 的 David McGrew 编写,旨在促进 Secure RTP 的使用、理解和互操作性。Michael Jerris 为在 MSVC 下构建提供了支持。Andris Pavenis 贡献了许多重要修复。Brian West 贡献了启用动态链接的更改。Yves Shumann 报告了文档错误。Randell Jesup 贡献了可用的 SRTCP 实现和其他修复。Steve Underwood 贡献了 x86_64 可移植性更改。我们还要感谢 Fredrik Thulin、Brian Weis、Mark Baugher、Jeff Chan、Bill Simon、Douglas Smith、Bill May、Richard Preistley、Joe Tardo 以及其他人的贡献、评论和更正。
本文档中的参考材料(如适用)是使用 doxygen 工具自动生成的源代码文档。
版权所有 2001-2005 David A. McGrew, Cisco Systems, Inc.
--------------------------------------------------------------------------------
<a name="references"></a>
# 参考资料
SRTP 与 ICM 参考
2005 年 9 月
Secure RTP 定义于 [RFC 3711](https://tools.ietf.org/html/rfc3711)。
计数器模式定义见 [第 4.1.1 节](https://tools.ietf.org/html/rfc3711#section-4.1.1)。
SHA-1 定义于 [FIPS PUB 180-4](http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf)。
HMAC 定义于 [RFC 2104](https://tools.ietf.org/html/rfc2104),
且 HMAC-SHA1 测试向量可在 [RFC 2202](https://tools.ietf.org/html/rfc2202#section-3) 中获取。
SRTP 中 AES-GCM 的使用定义于 [RFC 7714](https://tools.ietf.org/html/rfc7714)。
| Option | Description |
|---|
| -s | (S)RTP 发送方 - 使应用程序发送单词 |
| -r | (S)RTP 接收方 - 使应用程序接收单词 |
| -k | 使用 SRTP 主密钥 ,其中密钥为十六进制(不带前导 "0x") |
| -b | 与 -k 相同,但使用 base64 编码的密钥 |
| -e | 加密/解密(用于数据机密性)(还需使用 -k 选项)(keysize 使用 128、192 或 256) |
| -g | 使用 AES-GCM 模式(必须与 -e 一起使用) |
| -a | 消息认证(还需使用 -k 选项) |
| -l | 列出可用的调试模块 |
| -d | 为模块 开启调试 |