1
0
mirror of https://github.com/moparisthebest/xeps synced 2024-12-21 23:28:51 -05:00

editorial changes; use XML entities; neutral phrasing

This commit is contained in:
Tim Henkes 2020-03-15 15:13:09 +01:00
parent 4372ac8951
commit 78b10b997c
2 changed files with 27 additions and 22 deletions

View File

@ -208,7 +208,7 @@
<dl>
<di><dt>Device</dt><dd>A communication end point, i.e. a specific client instance</dd></di>
<di><dt>OMEMO element</dt><dd>An &lt;encrypted&gt; element in the <tt>&ns;</tt> namespace</dd></di>
<di><dt>Bundle</dt><dd>A collection of publicly accessible data used by the X3DH key exchange that can be used to build a session with a device, namely its public IdentityKey, a signed PreKey with corresponding signature, and a list of (single use) PreKeys.</dd></di>
<di><dt>Bundle</dt><dd>A collection of publicly accessible data used by the X3DH key agreement protocol that can be used to build a session with a device, namely its public IdentityKey, a signed PreKey with corresponding signature, and a list of (single use) PreKeys.</dd></di>
<di><dt>rid</dt><dd>The device id of the intended recipient of the containing &lt;key&gt;</dd></di>
<di><dt>sid</dt><dd>The device id of the sender of the containing OMEMO element</dd></di>
</dl>
@ -216,15 +216,15 @@
<section1 topic='Protocol Definition' anchor='protocol'>
<section2 topic='Overview' anchor='protocol-overview'>
<p>
This protocol uses the Double Ratchet encryption scheme in conjunction with the X3DH key exchange. The following section provides detailed technical information about the protocol that should be sufficient to build an implementation of the OMEMO Double Ratchet. Readers who do not intend to build an OMEMO-compatible library can safely skip this section, relevant details are repeated where needed.
This protocol uses the &doubleratchet; encryption scheme in conjunction with the &x3dh; key agreement protocol. The following section provides detailed technical information about the protocol that should be sufficient to build an implementation of the OMEMO Double Ratchet. Readers who do not intend to build an OMEMO-compatible library can safely skip this section, relevant details are repeated where needed.
</p>
</section2>
<section2 topic='Key Exchange' anchor='protocol-key_exchange'>
<p>
The X3DH key exchange is specified <link url="https://signal.org/docs/specifications/x3dh/">here</link> and placed under the public domain. OMEMO uses a modified version of this key exchange mechanism with the following parameters/settings:
The &x3dh; key agreement protocol was specified by Trevor Perrin and Moxie Marlinspike and placed under the public domain. OMEMO uses a modified version of this key agreement protocol with the following parameters/settings:
</p>
<dl>
<di><dt>curve</dt><dd>X25519/Ed25519</dd></di>
<di><dt>curve</dt><dd>Curve25519/Ed25519</dd></di>
<di><dt>hash function</dt><dd>SHA-256</dd></di>
<di><dt>info string</dt><dd>&quot;OMEMO X3DH&quot;</dd></di>
<di><dt>signed PreKey rotation period</dt><dd>Signed PreKeys SHOULD be rotated periodically once a week to once a month. A faster or slower rotation period should not be required.</dd></di>
@ -232,37 +232,37 @@
<di><dt>number of PreKeys to provide in the bundle</dt><dd>The bundle SHOULD always contain around 100 PreKeys.</dd></di>
<di><dt>minimum number of PreKeys to provide in the bundle</dt><dd>The bundle MUST always contain at least 25 PreKeys.</dd></di>
<di><dt>associated data</dt><dd>The associated data is created by concatenating the IdentityKeys of Alice and Bob: <tt>AD = Encode(IK_A) || Encode(IK_B)</tt></dd></di>
<di><dt>XEdDSA</dt><dd>The usage of <link url="https://www.signal.org/docs/specifications/xeddsa/">XEdDSA</link> has historically been a pain-point of the X3DH key exchange, as the number of available XEdDSA-implementations is low (at the time of writing) and the license of these implementations might not be optimal for all clients that intend to use OMEMO. For this reason, OMEMO does not mandate the usage of XEdDSA with X3DH. Instead, there are three simple rules that implementations MUST follow:
<di><dt>XEdDSA</dt><dd>OMEMO does not mandate the usage of &xeddsa; with &x3dh; for the IdentityKey. Instead, there are three simple rules that implementations MUST follow:
<ol>
<li>Implementations must use the birational map between the curves X25519 and Ed25519 to convert the public part of the IdentityKey whenever required, as defined in <link url="https://tools.ietf.org/html/rfc7748#page-5">RFC 7748</link>.</li>
<li>Implementations must be able to perform Diffie-Hellman using the IdentityKey.</li>
<li>Implementations must be able to create EdDSA-compatible signatures using the IdentityKey.</li>
<li>Implementations must use the birational map between the curves Curve25519 and Ed25519 to convert the public part of the IdentityKey whenever required, as defined in &rfc7748; (on page 5).</li>
<li>Implementations must be able to perform X25519 (ECDH on Curve25519) using the IdentityKey.</li>
<li>Implementations must be able to create EdDSA-compatible signatures on the curve Ed25519 using the IdentityKey.</li>
</ol>
There are essentially two ways in which libraries can fulfill these requirements:
<ol>
<li>Libraries can use an X25519 key pair as their internal IdentityKey. In this case, the IdentityKey can be used for Diffie-Hellman directly, and XEdDSA has to be used to produce EdDSA-compatible signatures.</li>
<li>Libraries can use an Ed25519 key pair as their internal IdentityKey. In this case, the IdentityKey can create EdDSA-compatible signatures directly, and has to be converted first to perform Diffie-Hellman.</li>
<li>Libraries can use a Curve25519 key pair as their internal IdentityKey. In this case, the IdentityKey can be used for X25519 directly, and XEdDSA has to be used to produce EdDSA-compatible signatures.</li>
<li>Libraries can use an Ed25519 key pair as their internal IdentityKey. In this case, the IdentityKey can create EdDSA-compatible signatures directly, and has to be converted first to perform X25519.</li>
</ol>
Note that this decision is purely local to each client and OMEMO library. The public key is ALWAYS transferred in its Ed25519 form and only valid EdDSA signatures are transferred. The choice between X25519 and Ed25519 affects the definition of the <tt>Sig(PK, M)</tt> and <tt>DH(PK1, PK2)</tt> functions as defined below.</dd></di>
<di><dt>Sig(PK, M)</dt><dd>If the IdentityKey pair is chosen to be an X25519 key pair, the definition of <tt>Sig(PK, M)</tt> found in the X3DH specification applies. If the IdentityKey pair is chosen to be an Ed25519 key pair, the following definition applies: <tt>Sig(PK, M)</tt> represents the byte sequence that is an Ed25519 signature on the byte sequence <tt>M</tt> and verifies with public key <tt>PK</tt>, and which was created by signing <tt>M</tt> with <tt>PK</tt>'s corresponding private key.</dd></di>
<di><dt>DH(PK1, PK2)</dt><dd>The original definition of <tt>DH(PK1, PK2)</tt> found in the X3DH specification applies with one exception: if the IdentityKey pair is chosen to be an Ed25519 key pair and either <tt>PK1</tt> or <tt>PK2</tt> corresponds to the IdentityKey, the respective key first has to be converted into its X25519 equivalent (see <link url="https://tools.ietf.org/html/rfc7748#page-5">RFC 7748</link>). This conversion is implemented by several crypto-libraries, for example <tt>libsodium</tt>, which exports the conversion as <tt>crypto_sign_ed25519_sk_to_curve25519</tt> and <tt>crypto_sign_ed25519_pk_to_curve25519</tt> for the private and public key, respectively (documented <link url="https://download.libsodium.org/doc/advanced/ed25519-curve25519">here</link>).</dd></di>
<di><dt>byte-encoding of the public keys</dt><dd>[TODO: Find out how Ed25519 public keys are usually encoded]. Note that this is always the Ed25519 public key. When using an X25519 key pair internally, the public key has to be converted to Ed25519 first.</dd></di>
Note that this decision is purely local to each client and OMEMO library. The public key is ALWAYS transferred in its Ed25519 form and only valid EdDSA signatures are transferred. The choice between Curve25519 and Ed25519 affects the definition of the <tt>Sig(PK, M)</tt> and <tt>DH(PK1, PK2)</tt> functions as defined below.</dd></di>
<di><dt>Sig(PK, M)</dt><dd>If the IdentityKey pair is chosen to be a Curve25519 key pair, the definition of <tt>Sig(PK, M)</tt> found in the X3DH specification applies. If the IdentityKey pair is chosen to be an Ed25519 key pair, the following definition applies: <tt>Sig(PK, M)</tt> represents the byte sequence that is an Ed25519 signature on the byte sequence <tt>M</tt> and verifies with public key <tt>PK</tt>, and which was created by signing <tt>M</tt> with <tt>PK</tt>'s corresponding private key. The byte-format of the signature is defined in &rfc8032;.</dd></di>
<di><dt>DH(PK1, PK2)</dt><dd>The original definition of <tt>DH(PK1, PK2)</tt> found in the X3DH specification applies with one exception: if the IdentityKey pair is chosen to be an Ed25519 key pair and either <tt>PK1</tt> or <tt>PK2</tt> corresponds to the IdentityKey, the respective key first has to be converted into its Curve25519 equivalent (see above). This conversion is implemented for example by <tt>libsodium</tt>, which exports the conversion as <tt>crypto_sign_ed25519_sk_to_curve25519</tt> and <tt>crypto_sign_ed25519_pk_to_curve25519</tt> for the private and public key, respectively (documented on <link url="https://download.libsodium.org/doc/advanced/ed25519-curve25519">libsodium.org</link>).</dd></di>
<di><dt>Encode(PK)</dt><dd>The public part of the IdentityKey pair is encoded as defined in &rfc8032;. Note that the IdentityKey is always transferred in its Ed25519 form. When using a Curve25519 key pair internally, the public key has to be converted to Ed25519 first. Curve25519 public keys are encoded using the little-endian encoding of the u-coordinate as specified in &rfc7748;.</dd></di>
</dl>
<p>
The key exchange is done just-in-time when sending the first message to a device. Thus, each key exchange message always also contains encrypted content as produced by the Double Ratchet encryption scheme below.
</p>
</section2>
<section2 topic='Double Ratchet' anchor='protocol-double_ratchet'>
<p>NOTE: <tt>OMEMOMessage.proto</tt>, <tt>OMEMOAuthenticatedMessage.proto</tt> and <tt>OMEMOKeyExchange.proto</tt> refer to the protobuf structures as defined <link url="#protobuf-schema">here</link>.</p>
<p>NOTE: <tt>OMEMOMessage.proto</tt>, <tt>OMEMOAuthenticatedMessage.proto</tt> and <tt>OMEMOKeyExchange.proto</tt> refer to the protobuf structures as defined in the <link url="#protobuf-schema">Protobuf Schemas</link>.</p>
<p>
The Double Ratchet encryption scheme is specified <link url="https://signal.org/docs/specifications/doubleratchet/">here</link> and placed under the public domain. OMEMO uses this protocol with the following parameters/settings:
The &doubleratchet; encryption scheme was specified by Trevor Perrin and Moxie Marlinspike and placed under the public domain. OMEMO uses this protocol with the following parameters/settings:
</p>
<dl>
<di><dt>ratchet initialization</dt><dd>The Double Ratchet is initialized using the shared secret, ad and public keys as yielded by the X3DH key exchange, as explained in the Double Ratchet specification.</dd></di>
<di><dt>ratchet initialization</dt><dd>The Double Ratchet is initialized using the shared secret, ad and public keys as yielded by the X3DH key agreement protocol, as explained in the Double Ratchet specification.</dd></di>
<di><dt>MAX_SKIP</dt><dd>It is RECOMMENDED to keep around 1000 skipped message keys.</dd></di>
<di><dt>deletion policy for skipped message keys</dt><dd>Skipped message keys MUST be stored until MAX_SKIP message keys are stored. At that point, keys are discarded on a FIFO basis to make space for new message keys. Implementations SHOULD not keep skipped message keys around forever, but discard old keys on a different implementation-defined policy. It is RECOMMENDED to base this policy on deterministic events rather than time.</dd></di>
<di><dt>authentication tag truncation</dt><dd>Authentication tags are truncated to 16 bytes/128 bits.</dd></di>
<di><dt>CONCAT(ad, header)</dt><dd><tt>CONCAT(ad, header) = ad || OMEMOMessage.proto(header)</tt> NOTE: the <tt>OMEMOMessage.proto</tt> is initialized without the ciphertext, which is optional. NOTE: Implementations are not strictly required to return a parseable byte array here, as the unpacked/parsed data is required later in the protocol.</dd></di>
<di><dt>CONCAT(ad, header)</dt><dd><tt>CONCAT(ad, header) = ad || OMEMOMessage.proto(header)</tt> NOTE: the <tt>OMEMOMessage.proto</tt> is initialized without the ciphertext, which is optional. NOTE: Implementations are not strictly required to return a parseable byte array, as the unpacked/parsed data is required later in the protocol.</dd></di>
<di><dt>KDF_RK(rk, dh_out)</dt><dd>HKDF-SHA-256 using the rk as HKDF salt, dh_out as HKDF input material and &quot;OMEMO Root Chain&quot; as HKDF info.</dd></di>
<di><dt>KDF_CK(ck)</dt><dd>HMAC-SHA-256 using ck as the HMAC key, a single byte constant <tt>0x01</tt> as HMAC input to produce the next message key and a single byte constant <tt>0x02</tt> as HMAC input to produce the next chain key.</dd></di>
<di><dt>ENCRYPT(mk, plaintext, associated_data)</dt><dd>
@ -270,7 +270,7 @@
<ol>
<li>Use HKDF-SHA-256 to generate 80 bytes of output from the message key by providing mk as HKDF input, 256 zero-bits as HKDF salt and &quot;OMEMO Message Key Material&quot; as HKDF info.</li>
<li>Divide the HKDF output into a 32-byte encryption key, a 32-byte authentication key and a 16 byte IV.</li>
<li>Encrypt the plaintext (which consists of a 32 bytes key and a 32 bytes HMAC as specified <link url="#protocol-message_encryption">here</link>) using AES-256-CBC with PKCS#7 padding, using the encryption key and IV derived in the previous step.</li>
<li>Encrypt the plaintext (which consists of a 32 bytes key and a 32 bytes HMAC as specified in the section about <link url="#protocol-message_encryption">Message Encryption</link>) using AES-256-CBC with PKCS#7 padding, using the encryption key and IV derived in the previous step.</li>
<li>Split the associated data as returned by <tt>CONCAT</tt> into the original ad and the <tt>OMEMOMessage.proto</tt> structure.</li>
<li>Add the ciphertext to the <tt>OMEMOMessage.proto</tt> structure.</li>
<li>Serialize the ad and the <tt>OMEMOMessage.proto</tt> structure into a parseable byte array by concatenating ad and the serialized protobuf structure.</li>
@ -280,10 +280,10 @@
</dd></di>
</dl>
<p>
If encrypting this message required a key exchange, the X3DH key exchange header data is placed into a new <tt>OMEMOKeyExchange.proto</tt> structure together with the <tt>OMEMOAuthenticatedMessage.proto</tt> structure.
If encrypting this message required a key exchange, the X3DH header data is placed into a new <tt>OMEMOKeyExchange.proto</tt> structure together with the <tt>OMEMOAuthenticatedMessage.proto</tt> structure.
</p>
<p>
To account for lost and out-of-order messages during the key exchange, <tt>OMEMOKeyExchange.proto</tt> structures are sent until a response by the recipient confirms that the key exchange was successfully completed. To do so, the X3DH key exchange header data is stored and added on each subsequent message until a response is received. This looks roughly as follows:
To account for lost and out-of-order messages during the key exchange, <tt>OMEMOKeyExchange.proto</tt> structures are sent until a response by the recipient confirms that the key exchange was successfully completed. To do so, the X3DH header data is stored and added on each subsequent message until a response is received. This looks roughly as follows:
</p>
<ol>
<li>The first content is encrypted for a new recipient. This results in an X3DH header and a <tt>OMEMOAuthenticatedMessage.proto</tt> structure. Both are packed into an <tt>OMEMOKeyExchange.proto</tt> structure. The X3DH header is stored for following messages.</li>
@ -638,7 +638,7 @@
</section1>
<section1 topic='Security Considerations' anchor='security'>
<p>Clients MUST NOT use a newly built session to transmit data without user intervention. If a client were to opportunistically start using sessions for sending without asking the user whether to trust a device first, an attacker could publish a fake device for this user, which would then receive copies of all messages sent by/to this user. A client MAY use such "not (yet) trusted" sessions for decryption of received messages, but in that case it SHOULD indicate the untrusted nature of such messages to the user.</p>
<p>When prompting the user for a trust decision regarding a key, the client SHOULD present the user with a fingerprint in the form of a hex-string, QR code, or other unique representation, such that it can be compared by the user. To ensure interoperability between clients and older versions of OMEMO, the fingerprint SHOULD be chosen to be the public part of the IdentityKey in its X25519 form (see the <link url="#protocol-key_exchange">notes on XEdDSA in the X3DH protocol section</link> for details). If interoperability and backward compatibility are not of concern, the fingerprint MUST still be chosen as a different combination of data that guarantees absence of a man-in-the-middle when verified. When displaying the fingerprint as a hex-string, one way to make it easier to compare the fingerprint is to split the hex-string into 8 substrings of 8 chars each, then coloring each 8-char group using &xep0392;. Lowercase letters are recommended when displaying the fingerprint as a hex-string.</p>
<p>When prompting the user for a trust decision regarding a key, the client SHOULD present the user with a fingerprint in the form of a hex-string, QR code, or other unique representation, such that it can be compared by the user. To ensure interoperability between clients and older versions of OMEMO, the fingerprint SHOULD be chosen to be the public part of the IdentityKey in its Curve25519 form (see the <link url="#protocol-key_exchange">notes on XEdDSA in the X3DH protocol section</link> for details). If interoperability and backward compatibility are not of concern, the fingerprint MUST still be chosen as a different combination of data that guarantees absence of a man-in-the-middle when verified. When displaying the fingerprint as a hex-string, one way to make it easier to compare the fingerprint is to split the hex-string into 8 substrings of 8 chars each, then coloring each 8-char group using &xep0392;. Lowercase letters are recommended when displaying the fingerprint as a hex-string.</p>
<p>While it is RECOMMENDED that clients postpone private key deletion until after message catch-up, the X3DH standard mandates that clients should not use duplicate-PreKey sessions for sending, so clients MAY delete such keys immediately for security reasons. For additional information on potential security impacts of this decision, refer to <note>Menezes, Alfred, and Berkant Ustaoglu. "On reusing ephemeral keys in Diffie-Hellman key agreement protocols." International Journal of Applied Cryptography 2, no. 2 (2010): 154-158.</note>.</p>
</section1>
<section1 topic='IANA Considerations' anchor='iana'>

View File

@ -361,6 +361,9 @@ THE SOFTWARE.
<!ENTITY whirlpool "<span class='ref'><link url='http://paginas.terra.com.br/informatica/paulobarreto/WhirlpoolPage.html'>Whirlpool</link></span> <note>The Whirlpool Hash Function &lt;<link url='http://paginas.terra.com.br/informatica/paulobarreto/WhirlpoolPage.html'>http://paginas.terra.com.br/informatica/paulobarreto/WhirlpoolPage.html</link>&gt;.</note>" >
<!ENTITY olm "<span class='ref'><link url='https://matrix.org/docs/spec/olm.html'>Olm</link></span> <note>Olm: A Cryptographic Ratchet &lt;<link url='https://matrix.org/docs/spec/olm.html'>https://matrix.org/docs/spec/olm.html</link>&gt;.</note>" >
<!ENTITY curve25519 "<span class='ref'><link url='http://cr.yp.to/ecdh/curve25519-20060209.pdf'>Curve25519</link></span> <note>Curve25519: new Diffie-Hellman speed records &lt;<link url='http://cr.yp.to/ecdh/curve25519-20060209.pdf'>http://cr.yp.to/ecdh/curve25519-20060209.pdf</link>&gt;.</note>" >
<!ENTITY doubleratchet "<span class='ref'><link url='https://signal.org/docs/specifications/doubleratchet/'>DoubleRatchet</link></span> <note>The Double Ratchet Algorithm &lt;<link url='https://signal.org/docs/specifications/doubleratchet/'>https://signal.org/docs/specifications/doubleratchet/</link>&gt;.</note>" >
<!ENTITY x3dh "<span class='ref'><link url='https://www.signal.org/docs/specifications/x3dh/'>X3DH</link></span> <note>The X3DH Key Agreement Protocol &lt;<link url='https://www.signal.org/docs/specifications/x3dh/'>https://www.signal.org/docs/specifications/x3dh/</link>&gt;.</note>" >
<!ENTITY xeddsa "<span class='ref'><link url='https://www.signal.org/docs/specifications/xeddsa/'>XEdDSA</link></span> <note>The XEdDSA and VXEdDSA Signature Schemes &lt;<link url='https://www.signal.org/docs/specifications/xeddsa/'>https://www.signal.org/docs/specifications/xeddsa/</link>&gt;.</note>" >
@ -688,8 +691,10 @@ THE SOFTWARE.
<!ENTITY rfc7693 "<span class='ref'><link url='http://tools.ietf.org/html/rfc7693'>RFC 7693</link></span> <note>RFC 7693: The BLAKE2 Cryptographic Hash and Message Authentication Code (MAC) &lt;<link url='http://tools.ietf.org/html/rfc7693'>http://tools.ietf.org/html/rfc7693</link>&gt;.</note>" >
<!ENTITY rfc7700 "<span class='ref'><link url='http://tools.ietf.org/html/rfc7700'>RFC 7700</link></span> <note>RFC 7700: Preparation, Enforcement, and Comparison of Internationalized Strings Representing Nicknames&lt;<link url='http://tools.ietf.org/html/rfc7700'>http://tools.ietf.org/html/rfc7700</link>&gt;.</note>" >
<!ENTITY rfc7712 "<span class='ref'><link url='http://tools.ietf.org/html/rfc7712'>RFC 7712</link></span> <note>RFC 7712: Domain Name Associations (DNA) in the Extensible Messaging and Presence Protocol (XMPP)&lt;<link url='http://tools.ietf.org/html/rfc7712'>http://tools.ietf.org/html/rfc7712</link>&gt;.</note>" >
<!ENTITY rfc7748 "<span class='ref'><link url='http://tools.ietf.org/html/rfc7748'>RFC 7748</link></span> <note>RFC 7748: Elliptic Curves for Security &lt;<link url='http://tools.ietf.org/html/rfc7748'>http://tools.ietf.org/html/rfc7748</link>&gt;.</note>" >
<!ENTITY rfc7764 "<span class='ref'><link url='http://tools.ietf.org/html/rfc7764'>RFC 7764</link></span> <note>RFC 7764: Guidance on Markdown: Design Philosophies, Stability Strategies, and Select Registrations &lt;<link url='http://tools.ietf.org/html/rfc7764'>http://tools.ietf.org/html/rfc7764</link>&gt;.</note>" >
<!ENTITY rfc7830 "<span class='ref'><link url='http://tools.ietf.org/html/rfc7830'>RFC 7830</link></span> <note>RFC 7830: The EDNS(0) Padding Option &lt;<link url='http://tools.ietf.org/html/rfc7830'>http://tools.ietf.org/html/rfc7830</link>&gt;.</note>" >
<!ENTITY rfc8032 "<span class='ref'><link url='http://tools.ietf.org/html/rfc8032'>RFC 8032</link></span> <note>RFC 8032: Edwards-Curve Digital Signature Algorithm (EdDSA) &lt;<link url='http://tools.ietf.org/html/rfc8032'>http://tools.ietf.org/html/rfc8032</link>&gt;.</note>" >
<!ENTITY rfc8174 "<span class='ref'><link url='http://tools.ietf.org/html/rfc8174'>RFC 8174</link></span> <note>RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words &lt;<link url='http://tools.ietf.org/html/rfc8174'>http://tools.ietf.org/html/rfc8174</link>&gt;.</note>" >
<!ENTITY rfc8264 "<span class='ref'><link url='http://tools.ietf.org/html/rfc8264'>RFC 8264</link></span> <note>RFC 8264: PRECIS Framework: Preparation, Enforcement, and Comparison of Internationalized Strings in Application Protocols &lt;<link url='http://tools.ietf.org/html/rfc8264'>http://tools.ietf.org/html/rfc8264</link>&gt;.</note>" >
<!ENTITY rfc8445 "<span class='ref'><link url='http://tools.ietf.org/html/rfc8445'>RFC 8445</link></span> <note>RFC 8445: Interactive Connectivity Establishment (ICE) &lt;<link url='http://tools.ietf.org/html/rfc8445'>http://tools.ietf.org/html/rfc8445</link>&gt;.</note>" >