RFC8446 TLS1.3中文版(5-7)

5. 记录协议

TLS记录协议将要传输的数据分片为可管理的块,加密后传输。接收到的数据经过验证、解密、重组,然后传递给上层协议。

TLS记录是分类型的,允许多个上层协议在同一个记录层上复用。 本文规定了四种内容类型:握手、应用数据、警报和change_cipher_spec。 change_cipher_spec记录仅用于兼容性目的(见附录D.4)。
可能会在发送或接收第一个ClientHello消息之后、接收到对端的Finished消息之前的任何时候接收到一个类型为change_cipher_spec的单字节值0x01的未加密记录,这种情况必须简单地丢弃而不做进一步处理。 需要注意的是,该记录可能出现在握手时的某一点上(在等待加密的记录),因此,在试图解密记录之前,有必要检测这种情况。 接收到任何其他change_cipher_spec值,或者接收到加密change_cipher_spec记录,必须以unexpected_message警报中止握手。 如果在第一个ClientHello消息之前或在对端Finished消息之后收到change_cipher_spec记录,必须视为意外的记录类型(尽管无状态服务器可能无法将这些情况与允许的情况区别开)。
除非经过扩展协商,否则不能发送本文中没定义的记录类型。如果收到意外的记录类型,必须用unexpected_message警告来终止连接。新的记录内容类型值由IANA在TLS ContentType注册表中分配,如11节所述。

5.1. 记录层

记录层将信息块分段为TLSPlaintext记录,每块携带的数据不多于2^14字节。根据底层ContentType的不同,信息边界的处理方式也不同。 任何未来的内容类型必须指定适当的规则。 请注意,这些规则比TLS 1.2中强制的规则更严格。
握手信息可以合并进一条TLSPlaintext记录,也可以分散在几条记录中,但前提是:

  • 握手消息不得与其他记录类型交织在一起。 也就是说,如果一个握手消息被分割成两条或更多的记录,它们之间不能有任何其他记录。
  • 握手消息不得跨越密钥变化。 必须确保紧接在密钥变化之前的所有消息是否与记录边界一致;如果不一致,则必须用unexpected_message警报终止连接。 因为ClientHello、EndOfEarlyData、ServerHello、Finished和KeyUpdate消息可以在秘钥变化之后立即发送,所以必须按照记录边界来发送这些消息。

不得发送零长度的握手类型片段,即使这些片段包含填充。
警报消息(第6节)绝不能分散在多个记录里,并且多个警报消息绝不能合并到一个TLSPlaintext 记录中。换句话说,Alert 类型的记录必须只包含一条消息。
Application Data消息包含对TLS不透明的数据。Application Data消息总是加密的。 可以发送零长度的Application Data片段,因为可能作为流量分析手段。Application Data片段可以分散在多个记录中,也可以合并成一个记录。

  enum {
      invalid(0),
      change_cipher_spec(20),
      alert(21),
      handshake(22),
      application_data(23),
      (255)
  } ContentType;

  struct {
      ContentType type;
      ProtocolVersion legacy_record_version;
      uint16 length;
      opaque fragment[TLSPlaintext.length];
  } TLSPlaintext;

type: 用于处理所附片段的上层协议。

legacy_record_version:对于所有TLS1.3的实现都必须设置为0x0303,除了初始的ClientHello可以出于兼容性考虑设置为0x0301(比如在HelloRetryRequest之后没有生成)。 这个字段已经被废弃,必须忽略。以前版本的TLS在某些情况下会在这个字段中使用其他的值。

length:下面TLSPlaintext.fragment的长度(以字节为单位),长度不得超过2^14字节。 接收到超过此长度的记录必须使用record_overflow警报终止连接。

fragment:正在传输的数据。这个值视为独立的块透明传递给类型字段指定的上层协议处理。

本文介绍了TLS 1.3,使用的版本是0x0304。 这个版本值是历史性的,源于TLS 1.0的0x0301和SSL 3.0的0x0300。 为了最大限度地提高向后兼容性,包含初始ClientHello的记录必须有0x0301版本(代表TLS 1.0),包含第二个ClientHello或ServerHello的记录必须有0x0303版本(代表TLS 1.2)。 当协商以前版本的TLS时,端点遵循附录D中提供的程序和要求。
当记录保护尚未参与时,TLSPlaintext结构会直接发送。 记录保护开始后,TLSPlaintext 记录将受到保护,并按下一节所述发送。 请注意,Application Data记录不得在未受保护的情况下发送(详情见第2节)。

5.2. 记录负载保护(Record Payload Protection)

记录保护函数将TLSPlaintext结构转换为TLSCiphertext结构。 去保护函数则相反。 TLS 1.3与之前TLS版本不同,所有的密码都被建模为关联数据认证加密(AEAD,Authenticated Encryption with Associated Data)[RFC5116]。 AEAD功能提供了统一的加密和认证操作,将明文转变成经过认证的密文,然后再转回来。 每条加密记录由一个plaintext头开始,后面是一个加密体,加密体包含一个类型和可选填充。

  struct {
      opaque content[TLSPlaintext.length];
      ContentType type;
      uint8 zeros[length_of_padding];
  } TLSInnerPlaintext;

  struct {
      ContentType opaque_type = application_data; /* 23 */
      ProtocolVersion legacy_record_version = 0x0303; /* TLS v1.2 */
      uint16 length;
      opaque encrypted_record[TLSCiphertext.length];
  } TLSCiphertext;

Content:TLSPlaintext.fragment值,包含握手或警报消息的字节编码,或应用程序要发送的原始数据。
type:TLSPlaintext.type值,包含记录的内容类型。
zeros:类型字段后的cleartext中可能出现任意长度的零值字节。 这为发送者提供了一个机会,只要总长度不超过记录大小的限制,发送者就可以用选择的数量来填充任何TLS记录。 更多细节见5.4。
opaque_type:TLSCiphertext记录的外层opaque_type字段总是设置为23(application_data),以兼容习惯于以前版本TLS的中间件。 记录的实际内容类型可以在解密后的TLSInnerPlaintext.type中找到。
legacy_record_version: legacy_record_version字段总是0x0303。 TLS 1.3的TLSCiphertexts是在TLS 1.3协商后才生成的,所以不存在收到其他值的历史兼容性问题。 请注意,握手协议,包括ClientHello和ServerHello消息,都会对协议版本进行认证,所以这个值是冗余的。
length:以下TLSCiphertext.encrypted_record的长度(以字节为单位),是内容长度加上填充长度,加上内部内容类型的长度,再加上AEAD算法添加的任何扩展。长度不得超过2^14+256字节。 接收到超过这个长度的记录必须用record_overflow警报终止连接。
encrypted_record:序列化TLSInnerPlaintext结构的AEAD加密格式

       AEAD算法的输入是一个密钥、一个nonce、一个明文和附加数据(这些数据将被包含在认证检查中,如[RFC5116]2.1所述)。秘钥是client_write_key或server_write_key,nonce是从序列号和client_write_iv或server_write_iv中分离的(见5.3),附加数据是记录头。
如:

additional_data = TLSCiphertext.opaque_type ||
                  TLSCiphertext.legacy_record_version ||
                  TLSCiphertext.length

        AEAD算法的明文输入是经过编码的TLSInnerPlaintext结构。 流量密钥的派生在7.3中定义。
        AEAD输出由AEAD加密操作输出的密文组成。 由于包含了TLSInnerPlaintext.type和发送者的填充,明文的长度大于相应的TLSPlaintext.length。 AEAD输出的长度一般会比明文大,但大小随AEAD算法而变化。
        由于密文可能包含填充,开销的数量可能会随着不同长度的明文而变化。 典型地:

  AEADEncrypted = AEAD-Encrypt(write_key, nonce, additional_data, plaintext)

       TLSCiphertext的encrypted_record字段设置为AEADEncrypted。
       为了解密和验证,解密函数将密钥、nonce、附加数据和AEADEncrypted值作为输入。 输出是明文或表示解密失败的错误。 没有单独的完整性检查。表示为:

  plaintext of encrypted_record =
                AEAD-Decrypt(peer_write_key, nonce, additional_data, AEADEncrypted)

        如果解密失败,接收方必须以bad_record_mac警告终止连接。
        在TLS 1.3中使用的AEAD算法不得产生大于255字节的扩展。如果从对端接收到TLSCiphertext.length大于2^14+256字节的记录,必须用record_overflow警报终止连接。 这个限制是由最大的TLSInnerPlaintext长度2^14字节+ContentType的1字节+最大的AEAD扩展255字节得出的。

5.3. 每记录Nonce

          读取和写入记录分别维护一个64位的序列号。 读取或写入每条记录后,相应的序列号都会递增1序列号在连接开始时和改变密钥时都被设置为0;在特定流量密钥下传输的第一条记录必须使用序列号0。

        序列号的大小是64位,所以不应该wrap。如果TLS实现需要对序列号进行wrap,那么它必须rekey(4.6.3)或者终止连接。
       每一种AEAD算法都会规定每记录nonce的长度范围,从N_MIN字节到N_MAX字节的输入[RFC5116]。对于AEAD算法来说,TLS每记录nonce的长度(iv_length)被设置为8字节和N_MIN中较大的一个(见[RFC5116]第4节)。 如果N_MAX小于8字节,那么AEAD算法就不能用于TLS。 AEAD结构中的每记录nonce的构成如下:

  1. 64位记录序列号按网络序编码,并向左加零到iv_length。
  2. 填充的序列号与静态client_write_iv或server_write_iv(取决于角色)进行异或。

所得到的结果(长度为iv_length)被用作每记录的nonce。

     注意:这与TLS 1.2中的结构不同,TLS 1.2指定了一个部分显式的nonce。

5.4. 记录填充

       所有加密的TLS记录都可以被填充以增加TLSCiphertext的大小。 这允许发送者对观察者隐藏流量的大小。
       当生成TLSCiphertext记录时,实现者可以选择填充。 未填充的记录只是填充长度为零的记录。 填充是加密前附加到 ContentType 字段的零值的字符串。 在加密前,实现者必须将padding字节设置为全零。
        如果发送者愿意,Application Data记录可以包含零长度的TLSInnerPlaintext.content。 这允许在敏感活动存在或不存在的情况下生成合理大小的覆盖流量。 不能发送包含零长度TLSInnerPlaintext.content的握手和警报记录;如果收到这样的消息,接收者必须用 unexpected_message警报来终止连接。
        发送的填充由记录保护机制自动验证;在成功解密TLSCiphertext.encrypted_record后,接收者从末尾向开头扫描该字段,直到找到一个非零字节。 这个非零字节就是消息的内容类型。 之所以选择这种填充方案,是因为它允许对任何加密的TLS记录进行任意大小的填充(从零到TLS记录大小限制),而不需要引入新的内容类型。 该设计还强制执行全零的padding字节,这允许快速检测填充错误。
        实现必须将其扫描限制在AEAD解密返回的cleartext上。 如果接收者没有在cleartext中找到非零字节,则必须用unexpected_message警告来终止连接。
       填充的存在不会改变整个记录大小限制:完整编码的TLSInnerPlaintext不得超过2^14+1字节。 如果最大的片段长度被减小(例如,通过[RFC8449]的record_size_limit扩展),那么减小的限制适用于完整的明晚,包括内容类型和填充。
          选择一个填充策略,建议何时填充,填充多少,是复杂的话题,超出了本文的范围。 如果在TLS之上的应用层协议有自己的填充,那么在应用层中对应用数据TLS记录进行填充可能是比较好的。 不过,加密的握手或警报记录的填充仍然必须在TLS层处理。 以后的文档可能会定义padding选择算法,或者通过TLS扩展或其他方式定义一个padding策略请求机制。

5.5. 秘钥使用限制

        在一组给定的密钥下,可以安全加密的明文数量是有密码学限制的。 [AEAD-LIMITS]提供了对这些限制的分析,其假设是底层基元(AES或ChaCha20)没有弱点。在达到这些限制之前,实现应该按照4.6.3的描述进行密钥更新。
        对于AES-GCM来说,在给定的连接上,最多可加密2^24.5大小的记录(约2400万条),同时保持约2^-57的安全系数,以保证验证加密(AE,Authenticated Encryption)的安全性。 对于ChaCha20/Poly1305,记录序列号将在达到安全限值之前被wrap。

6. 警告协议(Alert Protocol)

        TLS提供了一个Alert内容类型来指示关闭信息和错误。 和其他消息一样,警告消息也是根据当前连接状态进行加密的。
        警告消息传达了警告的描述和一个遗留字段,在以前的TLS版本中传达了消息的严重程度。 警告分为两类:关闭警告和错误警告。 在TLS 1.3中,警告的严重性是隐含在警告的类型中的,"level"字段可以被忽略。 close_notify警报用于指示连接的一个方向的有序关闭。 当收到这样的告警时,TLS应该向应用程序发出数据结束的提示。
       错误警报表示连接的中止关闭(见6.2节)。 当收到错误警报时,TLS应该向应用程序提示错误,并且不允许再在连接上发送或接收任何数据。 服务器和客户端必须丢弃在失败的连接中建立的秘密值和密钥,但与会话ticket相关联的PSK除外,如果可能的话,这些PSK应该被丢弃。
        6.2节中列出的所有告警必须以AlertLevel=fatal的方式发送,并且不管消息中的AlertLevel是什么都必须作为错误告警处理。 未知的警报类型必须作为错误警报处理。
        注意:TLS定义了两个通用的警报(见第6节),消息解析失败时使用。 当对端收到不能按照语法解析的消息时(例如,消息长度超出了消息边界或包含一个超出范围的长度),必须使用decode_error警报终止连接。如果接收到语法正确但语义无效的消息(例如,DHE share为p - 1,或无效的枚举),必须以illegal_parameter警报终止连接。

  enum { warning(1), fatal(2), (255) } AlertLevel;

  enum {
      close_notify(0),
      unexpected_message(10),
      bad_record_mac(20),
      record_overflow(22),
      handshake_failure(40),
      bad_certificate(42),
      unsupported_certificate(43),
      certificate_revoked(44),
      certificate_expired(45),
      certificate_unknown(46),
      illegal_parameter(47),
      unknown_ca(48),
      access_denied(49),
      decode_error(50),
      decrypt_error(51),
      protocol_version(70),
      insufficient_security(71),
      internal_error(80),
      inappropriate_fallback(86),
      user_canceled(90),
      missing_extension(109),
      unsupported_extension(110),
      unrecognized_name(112),
      bad_certificate_status_response(113),
      unknown_psk_identity(115),
      certificate_required(116),
      no_application_protocol(120),
      (255)
  } AlertDescription;

  struct {
      AlertLevel level;
      AlertDescription description;
  } Alert;

6.1. 关闭警报(Closure Alerts)

       客户端和服务器必须共享连接结束的信息,以避免截断攻击。
close_notify:这个警报通知接收者,发送者不会再在这个连接上发送任何消息。在收到关闭警报后收到任何数据都必须被忽略。
user_canceled:该警报通知接受者,发送者将不再在该连接上发送任何消息。该警报通知接收者,发送者因为某些与协议失败无关的原因取消了握手。如果用户在握手完成后取消了一个操作,那么通过发送close_notify来关闭连接更为合适。这个警报后面应该有一个 close_notify。这个告警一般AlertLevel=warning。
       任何一方都可以通过发送close_notify警报来关闭其写侧的连接。 在收到关闭警报后收到的任何数据都必须忽略。 如果在close_notify之前收到了一个传输级的关闭,那么接收者就不能知道所有发送的数据都已经收到了。
       每一方必须在关闭其连接的写侧之前发送close_notify警报,除非它已经发送了一些错误警报。这对它的读端连接没有任何影响。请注意,这是TLS 1.3对之前TLS版本的一个变化,在TLS 1.3之前的版本中,需要对close_notify做出反应,丢弃等待写的数据,并立即发送close_notify 警报。之前的要求可能会导致读端截断。双方不需要等待收到close_notify 警报后再关闭其读端连接,尽管这样做会带来截断的可能性。
       如果使用TLS的应用协议在TLS连接关闭后仍然有数据想要通过底层传输,TLS实现必须收到一个close_notify警报再向应用层指示数据结束。 这个标准的任何部分都不应该被视为规定了TLS的使用配置文件管理数据传输的方式,包括何时打开或关闭连接。

注意:我们假设关闭连接的写端会在销毁传输之前可靠地传送待处理的数据。

6.2. 错误警报(Error Alert)

      TLS中的错误处理非常简单。 当检测到错误时,检测方会向对端发送一条消息。 在发送或收到致命的警报消息后,双方必须立即关闭连接。
      每当遇到致命的错误条件时,应该发送一个适当的致命警告,并且必须关闭连接,不能再发送或接收任何数据。 在本规范的其余部分,当使用"终止连接"(terminate the connection)和"中止握手"(abort the handshake)这两个短语时,如果没有特定的警报,则意味着应该发送下面描述的警报。 短语"终止连接并发出X警报"和"中止握手并发出X警报"意味着如果要发出警报必须发送警报X。 本节中定义的所有警报,以及所有未知的警报,从TLS 1.3开始被认为是致命的(见第6节)。实现应该提供一种方法来方便记录警报的发送和接收。
定义了以下错误提示:
unexpected_message:收到了不适当的信息(例如,错误的握手信息、过早的应用数据等)。在适当的实现之间的通信中绝对不应该观察到这个警报。
bad_record_mac:如果收到一个不能解密的记录,就会返回这个警报。 因为AEAD算法结合了解密和验证,同时也为了避免侧信道攻击,所以这个警报用于所有的解密失败。在适当的实现之间的通信中,除非消息在网络中被破坏,否则绝不应该观察到这个警报。
record_overflow:收到的TLSCiphertext记录长度超过214+256字节,或记录解密后的TLSPlaintext记录长度超过214字节(或其他协商的限制)。 在适当的实现之间的通信中,除非消息在网络中被破坏,否则绝不应该观察到这个警报。
handshake_failure: 收到handshake_failure的警报消息表明发送者无法在给定选项下协商出一套可接受的安全参数。
bad_certificate:证书损坏了,包含的签名不能通过验证等。
unsupported_certificate:证书的类型不受支持。
certificate_revoked:证书被签署者撤销。
certificate_expired:证书已过期。证书已过期或当前无效。
certificate_unknown:在处理证书的过程中出现了一些其他(未指明)问题,导致无法接受。
illegal_parameter:握手中的一个字段不正确或与其他字段不一致。该警报用于符合正式协议语法但其他方面不正确的错误。
unknown_ca:收到了有效的证书链或部分证书链,但证书没有被接受,因为无法找到CA证书或无法与已知的trust anchor匹配。
access_denied:收到了一个有效的证书或PSK,但当使用了访问控制,发送者决定不继续协商。
decode_error: 由于某些字段不在指定范围内或消息的长度不正确,消息不能被解码。 该警报用于消息不符合正式协议语法的错误。在适当的实现之间的通信中绝对不应该观察到这个警报,除非消息在网络中被破坏了。
decrypt_error:握手(不是记录层)解密操作失败,包括签名无法验证通过或无法验证Finished消息或PSK binder。
protocol_version: 对端想要协商的协议版本可以识别但不支持(见附录D)。
insufficient_security:当协商失败时,替代handshake_failure返回,具体原因是服务器要求的参数比客户端支持的更安全。
internal_error: 内部错误。 与对端或协议的正确性无关的内部错误(如内存分配失败)使其无法继续。
inappropriate_fallback:由服务器响应客户端无效连接重试而发送(见[RFC7507])。
missing_extension:收到不包含必要扩展的握手消息则发送。
unsupported_extension:接收到包含禁止在给定握手中使用的扩展则发送,或者ServerHello或Certificate中包含任何没有在相应的ClientHello或CertificateRequest中提供的扩展。
unrecognized_name:当客户端通过 server_name扩展提供的名称所标识的服务器不存在时,由服务器发送(见[RFC6066])。
bad_certificate_status_response:当服务器通过status_request扩展提供无效或不可接受的OCSP响应时,由客户端发送(见[RFC6066])。
unknown_psk_identity:当需要建立PSK密钥,但客户端没有提供可接受的PSK标识时,服务器会发送该警报。发送该警报是可选的;服务器可以选择发送decrypt_error警报,以表明无效的PSK标识。
certificate_required:当需要客户端证书,但客户没有提供证书时,由服务器发送。
no_application_protocol:当客户端的application_layer_protocol_negotiation扩展只提供了服务器不支持的协议时,由服务器发送(参见[RFC7301])。

新的警报值由IANA分配,如第11节所述。

7. 加密计算

       TLS的握手建立了一个或多个输入secret,组合起来创建实际的工作密钥材料,详见下文。 密钥推导过程包含了输入secret和握手transcript。 需要注意的是,由于握手transcript包含了Hello消息中的随机值,因此任何一次握手都会有不同的流量secret,即使使用了相同的输入secret,就像在多个连接中使用相同的PSK一样。

7.1. Key Schedule

       密钥推导过程使用了HKDF定义的HKDF-Extract和HKDF-Expand函数[RFC5869],以及下面定义的函数:

   HKDF-Expand-Label(Secret, Label, Context, Length) =
        HKDF-Expand(Secret, HkdfLabel, Length)

   HkdfLabel指定为:
   struct {
       uint16 length = Length;
       opaque label<7..255> = "tls13 " + Label;
       opaque context<0..255> = Context;
   } HkdfLabel;

   Derive-Secret(Secret, Label, Messages) =
        HKDF-Expand-Label(Secret, Label, Transcript-Hash(Messages), Hash.length)

       Transcript-Hash和HKDF使用的Hash函数是密码套件的散列算法。Hash.length是以字节为单位的输出长度。Messages是指握手消息,包括握手消息类型和长度字段,但不包括记录层首部。请注意,在某些情况下,零长度的Context(用""表示)被传递给HKDF-Expand-Label。本文中提到的标签都是ASCII字符串,不包括尾部的NUL字节。

      注意: 对于普通的散列函数,任何超过12字符的标签都需要散列函数额外迭代计算。本规范中的标签都选在这个限制范围内。

       密钥是使用HKDF-Extract和Derive-Secret函数从两个输入的secret中导出的。 一般来说,添加一个新secret的模式是使用HKDF-Extract,使用Salt作为当前的secret状态,输入密钥材料(IKM,Input Keying Material)作为新secret添加。在TLS 1.3这个版本中,这两个输入secret是:

  • PSK (外部建立的预共享密钥,或从以前连接中的resumption_master_secret值导出)
  • (EC)DHE共享secret (7.4)

这产生了一个完整的密钥推导表,如下图所示。此图使用以下格式约定:

  • HKDF-Extract从上取Salt参数,从左取IKM参数,其输出在底部,输出的名称在右侧。
  • Derive-Secret的Secret参数用进位箭头表示。例如,Early Secret是生成client_early_traffic_secret的Secret。
  • "0"表示一串Hash.length字节设置为0。

       这里的一般模式是,图中左边显示的secret只是没有上下文的原始熵,而右边显示的secret包括握手上下文,因此可以用来导出工作密钥,而不需要额外的上下文。请注意,对Derive-Secret的不同调用可能会使用不同的Messages参数,即使是相同的secret。 在0-RTT中,Derive-Secret被调用时有四个不同的transcript;在仅1-RTT中,被调用时有三个不同的transcript。
       如果给定的secret不可用,则使用Hash.length字节的0值。 请注意,这并不意味着跳过一轮,所以如果没有使用PSK,Early Secret仍将是HKDF-Extract(0,0)。 对于binder_key的计算,外部PSK(在TLS之外提供的PSK)的标签为ext binder,恢复PSK(作为之前握手的恢复主secret提供的PSK)的标签为res binder。 不同的标签防止了一种PSK被另一种PSK所替代。
       有多个潜在的Early Secret值,取决于服务器最终选择的PSK。 客户端需要为每个潜在的PSK计算一个值如果没有选择PSK,则需要计算对应于零PSK的Early Secret。
       一旦计算出所有从某个secret中得出的值,该secret就应该被删除。

7.2. 更新流量Secret

        一旦握手完成,任何一方都可以使用4.6.3中定义的KeyUpdate握手消息更新其发送的流量密钥。 如本节所述,通过从client_/server_application_traffic_secret_N中生成client_/server_application_traffic_secret_N+1来计算下一代流量密钥,然后如7.3所述重新生成流量密钥。
      下一代application_traffic_secret的计算方式为:

   application_traffic_secret_N+1 =
       HKDF-Expand-Label(application_traffic_secret_N, "traffic upd", "", Hash.length)

       一旦计算出client_/server_application_traffic_secret_N+1及其相关的流量密钥,实现者就应该删除client_/server_application_traffic_secret_N及其相关的流量密钥。

7.3. 流量秘钥计算

流量密钥材料由以下输入值生成: 

  • 一个secret值
  • 表示正在生成的具体值的purpose值
  • 正在生成的密钥的长度

流量密钥材料由输入的流量secret值生成,使用:

   [sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length)
   [sender]_write_iv  = HKDF-Expand-Label(Secret, "iv", "", iv_length)

[sender] 表示发送方。 各记录类型的Secret值如下表所示:

   +-------------------+---------------------------------------+
   | Record Type       | Secret                                |
   +-------------------+---------------------------------------+
   | 0-RTT Application | client_early_traffic_secret           |
   |                   |                                       |
   | Handshake         | [sender]_handshake_traffic_secret     |
   |                   |                                       |
   | Application Data  | [sender]_application_traffic_secret_N |
   +-------------------+---------------------------------------+

        每当底层Secret变化时,所有的流量密钥材料都会被重新计算(例如,从握手密钥变为应用数据密钥或密钥更新时)。

7.4. (EC)DHE Shared Secret计算

7.4.1. 有限域DH(Finite Field Diffie-Hellman)

        对于有限域组,进行传统的Diffie-Hellman[DH76]计算。 协商后的密钥(Z)以大字节序转为字节串,并以0左垫,直到质数的大小。 这个字节串在上面的密钥图中被用作shared secret。
请注意,这种结构与之前的TLS版本不同,后者去掉了前面的零。

7.4.2. 椭圆曲线DH(Elliptic Curve Diffie-Hellman)

         对于secp256r1、secp384r1和secp521r1,ECDH计算(包括参数和密钥生成以及shared secret计算)按照[IEEE1363]使用ECKAS-D1H方案进行,以identity map作为密钥派生函数(KDF),因此shared secret是以字节字符串表示的ECDH shared secret椭圆曲线点的x坐标。需要注意的是,FE2OSP(Field Element to Octet String Conversion Primitive)输出的这个字节字串(IEEE1363术语中的"Z")对于任何给定字段来说都是恒定长度的;在这个字节字串中前面的零一定不能被截断。
(请注意,这种身份KDF的使用是一个技术问题。 完整的情况是,ECDH采用了一个重要的KDF,因为TLS除了计算其他secret外,并不直接使用这个secret。)

对于X25519和X448,ECDH的计算方法如下:

  • 放入KeyShareEntry.key_exchange结构的公钥是将ECDH标量乘法函数应用于适当长度的秘钥(放入标量输入)和标准的公共基点(放入u坐标点输入)的结果。
  • ECDH shared secret是将ECDH标量乘法函数应用于秘钥(成标量输入)和对端的公钥(成u坐标点输入)的结果。 输出的结果是不经过任何处理直接使用的。

        对于这些曲线,实现者应该使用[RFC7748]中指定的方法来计算Diffie-Hellman shared secret。实现者必须检查计算出的Diffie-Hellman shared secret是否是全零值,如果是,则按照[RFC7748]第6节的描述中止。 如果实现者使用这些椭圆曲线的替代实现,他们必须执行[RFC7748]第7节中规定的附加检查。

7.5. 导出器(Exporter)

     [RFC5705]用TLS伪随机函数(PRF, pseudorandom function)定义了TLS的密钥材料导出器。 本文用HKDF替换了PRF,因此需要一个新的结构。 输出器接口保持不变。
    导出器的值计算为:

TLS-Exporter(label, context_value, key_length) =
        HKDF-Expand-Label(Derive-Secret(Secret, label, “”),
                         “exporter”, Hash(context_value), key_length)

        其中Secret是early_exporter_master_secret或exporter_master_secret。 除非应用程序明确指定,否则必须使用exporter_master_secret。 early_exporter_master_secret定义为用于0-RTT数据需要exporter的设置。 建议为早期exporter定义一个单独的接口,这样可以避免exporter用户在需要常规exporter时意外地使用早期exporter,反之亦然。
       如果没有提供上下文,context_value的长度为零。因此,不提供上下文和提供空上下文的计算结果是一样的。 这与以前的TLS版本不同,在以前的版本中,空上下文的输出与没有上下文的输出是不同的。 从本文发布之时起,无论是否有上下文,都不使用分配的exporter标签。 未来的规范不得定义导出器使用空上下文和无上下文使用相同的标签。 导出器的新用途应该在所有导出器计算中提供一个上下文,尽管这个值可以是空的。
       对导出器标签格式的要求在[RFC5705]第4节中定义。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值