4. Presentation Language (表示语言)
本文档处理外部表示中数据的格式化. 将使用以下非常基本且定义较为随意的表示语法. 该语法在结构上借鉴了多个来源. 尽管它在语法上类似于编程语言"C", 在语法和意图上类似于XDR [XDR], 但过度类比是有风险的. 这种表示语言的目的仅是记录TLS; 它在该特定目标之外没有通用应用.
4.1. Basic Block Size (基本块大小)
所有数据项的表示都是明确指定的. 基本数据块大小为一个字节 (即8位). 多字节数据项是字节的连接, 从左到右, 从上到下. 从字节流中, 多字节项 (示例中的数字) 通过以下方式形成 (使用C表示法):
value = (byte[0] << 8*(n-1)) | (byte[1] << 8*(n-2)) |
... | byte[n-1];
这种多字节值的字节顺序是常见的网络字节顺序 (network byte order) 或大端格式 (big-endian format).
4.2. Miscellaneous (杂项)
注释以 /* 开始, 以 */ 结束.
可选组件用双方括号 [[ ]] 括起来表示.
包含未解释数据的单字节实体是opaque类型.
4.3. Vectors (向量)
向量 (单维数组) 是同构数据元素的流. 向量的大小可以在文档编写时指定, 也可以在运行时保持未指定. 在任何情况下, 长度都声明字节数, 而不是元素数. 指定新类型T'的语法, T'是类型T的固定长度向量:
T T'[n];
这里, T'在数据流中占用n个字节, 其中n是T大小的倍数. 向量的长度不包含在编码流中.
在以下示例中, Datum被定义为协议不解释的三个连续字节, 而Data是三个连续的Datum, 总共消耗九个字节.
opaque Datum[3]; /* three uninterpreted bytes */
Datum Data[9]; /* 3 consecutive 3 byte vectors */
可变长度向量通过使用符号 <floor..ceiling> 指定合法长度的子范围 (包含边界) 来定义. 当编码这些向量时, 实际长度在字节流中位于向量内容之前. 长度将采用数字形式, 消耗足够多的字节以容纳向量指定的最大 (ceiling) 长度. 实际长度字段为零的可变长度向量称为空向量.
T T'<floor..ceiling>;
在以下示例中, mandatory是一个向量, 必须包含300到400字节的opaque类型数据. 它永远不能为空. 实际长度字段消耗两个字节, 即uint16, 足以表示值400 (参见第4.4节). 另一方面, longer可以表示最多800字节的数据, 或400个uint16元素, 并且它可以为空. 它的编码将包括在向量之前添加的两字节实际长度字段. 编码向量的长度必须是单个元素长度的偶数倍 (例如, 17字节的uint16向量将是非法的).
opaque mandatory<300..400>;
/* length field is 2 bytes, cannot be empty */
uint16 longer<0..800>;
/* zero to 400 16-bit unsigned integers */
4.4. Numbers (数字)
基本数字数据类型是无符号字节 (uint8). 所有更大的数字数据类型都由固定长度的字节序列形成, 如第4.1节所述进行连接, 并且也是无符号的. 以下数字类型是预定义的.
uint8 uint16[2];
uint8 uint24[3];
uint8 uint32[4];
uint8 uint64[8];
所有值, 无论是此处还是规范中的其他地方, 都以网络字节 (大端) 顺序存储; 由十六进制字节01 02 03 04表示的uint32等同于十进制值16909060.
请注意, 在某些情况下 (例如, DH参数), 需要将整数表示为opaque向量. 在这种情况下, 它们表示为无符号整数 (即, 即使最高有效位被设置, 也不需要前导零八位字节).
4.5. Enumerateds (枚举类型)
还有一种额外的稀疏数据类型, 称为enum. enum类型的字段只能假定定义中声明的值. 每个定义都是不同的类型. 只有相同类型的枚举可以赋值或比较. 枚举的每个元素都必须分配一个值, 如以下示例所示. 由于枚举的元素没有排序, 可以按任何顺序为它们分配任何唯一值.
enum { e1(v1), e2(v2), ... , en(vn) [[, (n)]] } Te;
枚举在字节流中占用的空间与其最大定义的序数值相同. 以下定义将导致使用一个字节来携带Color类型的字段.
enum { red(3), blue(5), white(7) } Color;
可以选择指定不带其关联标签的值, 以强制宽度定义而不定义多余的元素.
在以下示例中, Taste将在数据流中消耗两个字节, 但只能假定值1, 2或4.
enum { sweet(1), sour(2), bitter(4), (32000) } Taste;
枚举元素的名称作用域限定在定义的类型内. 在第一个示例中, 对枚举第二个元素的完全限定引用将是Color.blue. 如果赋值目标被良好指定, 则不需要这样的限定.
Color color = Color.blue; /* overspecified, legal */
Color color = blue; /* correct, type implicit */
对于从未转换为外部表示的枚举, 可以省略数字信息.
enum { low, medium, high } Amount;
4.6. Constructed Types (构造类型)
结构类型可以从原始类型构造以方便使用. 每个规范声明一个新的唯一类型. 定义的语法与C非常相似.
struct {
T1 f1;
T2 f2;
...
Tn fn;
} [[T]];
结构内的字段可以使用类型名称进行限定, 语法与可用于枚举的语法非常相似. 例如, T.f2指的是前一个声明的第二个字段. 结构定义可以嵌入.
4.6.1. Variants (变体)
定义的结构可能具有基于环境中可用的某些知识的变体. 选择器必须是枚举类型, 该类型定义结构定义的可能变体. 必须为枚举中声明的每个元素都有一个case分支. Case分支具有有限的fall-through特性: 如果两个case分支紧接在一起且之间没有字段, 那么它们都包含相同的字段. 因此, 在下面的示例中, "orange"和"banana"都包含V2. 请注意, 这是TLS 1.2中的一个新语法特性.
变体结构的主体可以被赋予一个标签以供参考. 在运行时选择变体的机制不由表示语言规定.
struct {
T1 f1;
T2 f2;
....
Tn fn;
select (E) {
case e1: Te1;
case e2: Te2;
case e3: case e4: Te3;
....
case en: Ten;
} [[fv]];
} [[Tv]];
例如:
enum { apple, orange, banana } VariantTag;
struct {
uint16 number;
opaque string<0..10>; /* variable length */
} V1;
struct {
uint32 number;
opaque string[10]; /* fixed length */
} V2;
struct {
select (VariantTag) { /* value of selector is implicit */
case apple:
V1; /* VariantBody, tag = apple */
case orange:
case banana:
V2; /* VariantBody, tag = orange or banana */
} variant_body; /* optional label on variant */
} VariantRecord;
4.7. Cryptographic Attributes (密码学属性)
五种密码学操作 -- 数字签名 (digital signing), 流密码加密 (stream cipher encryption), 分组密码加密 (block cipher encryption), 带附加数据的认证加密 (authenticated encryption with additional data, AEAD), 以及公钥加密 (public key encryption) -- 分别指定为digitally-signed, stream-ciphered, block-ciphered, aead-ciphered和public-key-encrypted. 字段的密码学处理通过在字段类型规范之前添加适当的关键词来指定. 密码学密钥由当前会话状态隐含 (参见第6.1节).
数字签名元素编码为struct DigitallySigned:
struct {
SignatureAndHashAlgorithm algorithm;
opaque signature<0..2^16-1>;
} DigitallySigned;
algorithm字段指定使用的算法 (参见第7.4.1.4.1节了解该字段的定义). 请注意, 引入algorithm字段是与先前版本的更改. signature是使用这些算法对元素内容的数字签名. 内容本身不出现在线路上, 而只是被计算. 签名的长度由签名算法和密钥指定.
在RSA签名中, opaque向量包含使用 [PKCS1] 中定义的RSASSA-PKCS1-v1_5签名方案生成的签名. 如 [PKCS1] 中所讨论的, DigestInfo必须 (MUST) 是DER编码的 [X680] [X690]. 对于没有参数的哈希算法 (包括SHA-1), DigestInfo.AlgorithmIdentifier.parameters字段必须 (MUST) 为NULL, 但实现必须 (MUST) 同时接受没有参数和带有NULL参数的情况. 请注意, TLS的早期版本使用了不同的RSA签名方案, 该方案不包括DigestInfo编码.
在DSA中, SHA-1哈希的20个字节直接通过数字签名算法运行, 无需额外的哈希. 这产生两个值r和s. DSA签名是一个opaque向量, 如上所述, 其内容是以下的DER编码:
Dss-Sig-Value ::= SEQUENCE {
r INTEGER,
s INTEGER
}
注意: 在当前术语中, DSA指的是数字签名算法 (Digital Signature Algorithm), DSS指的是NIST标准. 在原始SSL和TLS规范中, 普遍使用"DSS". 本文档使用"DSA"来指代算法, 使用"DSS"来指代标准, 并在代码点定义中使用"DSS"以保持历史连续性.
在流密码加密中, 明文与密码学安全的带密钥伪随机数生成器生成的相同数量的输出进行异或运算.
在分组密码加密中, 明文的每个块都加密为密文块. 所有分组密码加密都以CBC (密码块链接, Cipher Block Chaining) 模式完成, 所有被分组密码加密的项都将是密码块长度的精确倍数.
在AEAD加密中, 明文被同时加密和完整性保护. 输入可以是任意长度, aead-ciphered输出通常大于输入, 以容纳完整性检查值.
在公钥加密中, 使用公钥算法加密数据, 使得只能用匹配的私钥解密. 公钥加密的元素编码为opaque向量 <0..2^16-1>, 其中长度由加密算法和密钥指定.
RSA加密使用 [PKCS1] 中定义的RSAES-PKCS1-v1_5加密方案完成.
在以下示例中:
stream-ciphered struct {
uint8 field1;
uint8 field2;
digitally-signed opaque {
uint8 field3<0..255>;
uint8 field4;
};
} UserType;
内部结构的内容 (field3和field4) 用作签名/哈希算法的输入, 然后整个结构用流密码加密. 该结构的长度 (以字节为单位) 将等于field1和field2的两个字节, 加上签名和哈希算法的两个字节, 加上签名长度的两个字节, 加上签名算法输出的长度. 签名的长度是已知的, 因为在编码或解码此结构之前已知用于签名的算法和密钥.
4.8. Constants (常量)
可以通过声明所需类型的符号并为其分配值来为规范目的定义类型化常量.
未充分指定的类型 (opaque, 可变长度向量, 以及包含opaque的结构) 不能被赋值. 多元素结构或向量的任何字段都不能省略.
例如:
struct {
uint8 f1;
uint8 f2;
} Example1;
Example1 ex1 = {1, 4}; /* assigns f1 = 1, f2 = 4 */