RFC 7425

Adobe's RTMFP Profile for Flash Communication

๐Ÿ“… published December 2014, from ietf

Adobe's RTMFP Profile for Flash Communication ๐Ÿ’–

  • RFC number: 7425
  • Author: M. Thornburgh (Adobe) ๐ŸŽ€
  • Category: Informational
  • Date: December 2014
  • Stream: Independent Submission
  • ISSN: 2070-1721
  • Obsoletes/Updates: The title block doesn't list any RFCs this one obsoletes or updates โœจ

Abstract ๐Ÿฆ‹

okay so this memo explains how to use Adobe's Secure Real-Time Media Flow Protocol (RTMFP) to carry the video, audio, and data messages from Adobe Flash platform communications ๐Ÿ“น๐ŸŽถ it covers the crypto methods and data formats, the flow metadata formats, and the protocol details for both client-server and peer-to-peer (P2P) communication ๐Ÿ’œ

Status of This Memo ๐ŸŒธ

This document is not an Internet Standards Track specification. It's published for informational purposes, so no pressure, it's just vibes and info ๐Ÿ’…

This is a contribution to the RFC Series, independently of any other RFC stream. The RFC Editor chose to publish it at its discretion and makes no statement about its value for implementation or deployment. Documents approved for publication by the RFC Editor are not a candidate for any level of Internet Standard; see Section 2 of RFC 5741.

Information about the current status of this document, any errata, and how to provide feedback on it may be obtained at http://www.rfc-editor.org/info/rfc7425. ๐Ÿ”—

Copyright (c) 2014 IETF Trust and the persons identified as the document authors. All rights reserved.

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Read the fine print, bestie ๐Ÿ“œ

This document may not be modified, and derivative works of it may not be created, except to format it for publication as an RFC or to translate it into languages other than English. ๐Ÿ™ˆ

Table of Contents ๐Ÿ“š

   1. Introduction ....................................................3
   2. Terminology .....................................................4
   3. Common Syntax Elements ..........................................4
   4. Cryptography Profile ............................................5
      4.1. Default Session Key ........................................5
      4.2. Diffie-Hellman Groups ......................................6
      4.3. Certificates ...............................................6
           4.3.1. Format ..............................................6
           4.3.2. Fingerprint .........................................7
           4.3.3. Options .............................................7
                  4.3.3.1. Hostname ...................................8
                  4.3.3.2. Accepts Ancillary Data .....................8
                  4.3.3.3. Extra Randomness ...........................8
                  4.3.3.4. Supported Ephemeral Diffie-Hellman Group ...9
                  4.3.3.5. Static Diffie-Hellman Public Key ...........9
           4.3.4. Authenticity .......................................10
           4.3.5. Signing and Verifying Messages .....................10
                  4.3.5.1. Options ...................................11
                           4.3.5.1.1. Simple Password ................11
           4.3.6. Glare Resolution ...................................13
           4.3.7. Session Override ...................................13
      4.4. Endpoint Discriminators ...................................13
           4.4.1. Format .............................................14
           4.4.2. Options ............................................14
                  4.4.2.1. Required Hostname .........................15
                  4.4.2.2. Ancillary Data ............................15
                  4.4.2.3. Fingerprint ...............................16
           4.4.3. Certificate Selection ..............................16
           4.4.4. Canonical Endpoint Discriminator ...................17
      4.5. Session Keying Components .................................18
           4.5.1. Format .............................................19
           4.5.2. Options ............................................19
                  4.5.2.1. Ephemeral Diffie-Hellman Public Key .......20
                  4.5.2.2. Extra Randomness ..........................20
                  4.5.2.3. Diffie-Hellman Group Select ...............21
                  4.5.2.4. HMAC Negotiation ..........................21
                  4.5.2.5. Session Sequence Number Negotiation .......22
      4.6. Session Key Computation ...................................23
           4.6.1. Public Key Selection ...............................23
                  4.6.1.1. Initiator and Responder Ephemeral .........23
                  4.6.1.2. Initiator Ephemeral and Responder Static ..23
                  4.6.1.3. Initiator Static and Responder Ephemeral ..24
                  4.6.1.4. Initiator and Responder Static ............24
           4.6.2. Diffie-Hellman Shared Secret .......................24
           4.6.3. Packet Encrypt/Decrypt Keys ........................25
           4.6.4. Packet HMAC Send/Receive Keys ......................25
           4.6.5. Session Nonces .....................................26
           4.6.6. Session Sequence Number ............................26
      4.7. Packet Encryption .........................................27
           4.7.1. Cipher .............................................27
           4.7.2. Format .............................................27
           4.7.3. Verification .......................................29
                  4.7.3.1. Simple Checksum ...........................30
                  4.7.3.2. HMAC ......................................30
                  4.7.3.3. Session Sequence Number ...................31
   5. Flash Communication ............................................31
      5.1. RTMP Messages .............................................31
           5.1.1. Flow Metadata ......................................32
           5.1.2. Message Mapping ....................................34
      5.2. Flow Synchronization ......................................35
      5.3. Client-to-Server Connection ...............................36
           5.3.1. Connecting .........................................36
           5.3.2. Server-to-Client Return Control Flow ...............37
           5.3.3. setPeerInfo Command ................................37
           5.3.4. Set Keepalive Timers Command .......................39
           5.3.5. Additional Flows for Streams .......................40
                  5.3.5.1. To Server .................................40
                  5.3.5.2. From Server ...............................40
                  5.3.5.3. Closing Stream Flows ......................41
           5.3.6. Closing the Connection .............................41
           5.3.7. Example ............................................42
      5.4. Direct Peer-to-Peer Streams ...............................43
           5.4.1. Connecting .........................................43
           5.4.2. Return Flows for Stream ............................43
           5.4.3. Closing the Connection .............................44
   6. IANA Considerations ............................................44
      6.1. RTMFP URI Scheme Registration .............................44
   7. Security Considerations ........................................46
   8. References .....................................................47
      8.1. Normative References ......................................47
      8.2. Informative References ....................................49
   Acknowledgements ..................................................49
   Author's Address ..................................................49

1. Introduction โœจ

Adobe's Secure Real-Time Media Flow Protocol (RTMFP) [RFC7016] is a general-purpose transport service for real-time media and bulk data over IP networks. It's great for both client-server and peer-to-peer (P2P) communication ๐Ÿš€ RTMFP also gives you a generalized framework for securing its communications, tailored to whatever your application needs ๐Ÿ”

The Flash platform is made up of the Flash runtime (including Flash Player) from Adobe Systems Incorporated, communication servers like Adobe Media Server, and interoperable clients and servers from other parties ๐ŸŒธ

Real-time streaming for the Flash platform (video, audio, and data) usually uses Adobe's Real-Time Messaging Protocol (RTMP) [RTMP] messages. Those messages were originally designed to ride over RTMP Chunk Stream in TCP [RTMP], but other transports, like the one in this memo, are possible ๐Ÿฆ‹

This memo spells out the syntax and semantics for carrying RTMP messages over RTMFP, and it extends Flash communication semantics to include direct P2P communication ๐Ÿ’– It also defines a concrete Cryptography Profile for RTMFP that's built for the app and crypto needs of Flash platform client-server and P2P communications ๐Ÿ”ฅ

These protocols and profiles were developed by Adobe Systems Incorporated and are not the product of an IETF activity. ๐ŸŽ€

2. Terminology ๐ŸŽ€

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119]. ๐Ÿ“–

"HMAC" means the Keyed-Hash Message Authentication Code (HMAC) algorithm [RFC2104]. ๐Ÿ”‘

"HMAC-SHA256" means HMAC using the SHA-256 Secure Hash Algorithm [SHA256] [RFC6234]. ๐Ÿ’œ

HMAC-SHA256(K, M) means the calculation of the HMAC-SHA256 of message M using key K. โœจ

3. Common Syntax Elements ๐Ÿงš

Definitions of types and structures in this specification use traditional text diagrams paired with procedural descriptions in a C-like syntax. The C-like procedural descriptions SHALL be construed as definitive. ๐Ÿ‘‘

Structures are packed to take only as many bytes as explicitly indicated. There's no 32-bit alignment constraint, and fields aren't padded for alignment unless explicitly indicated or described. Text diagrams may include a bit ruler across the top. It's just a convenience for counting bits in individual fields, and it doesn't necessarily mean fields are aligned to a multiple of the ruler width ๐Ÿ“

Unless specified otherwise, reserved fields SHOULD be set to 0 by a sender and MUST be ignored by a receiver. ๐Ÿ™ˆ

The procedural syntax of this specification defines correct and error-free encoded inputs to a parser. It does not describe a fully featured parser, including error detection and handling. Implementations MUST include means to identify error circumstances, including truncations that cause elementary or composed types not to fit inside containing structures, fields, or elements. Unless specified otherwise, an error circumstance SHALL abort the parsing and processing of an element and its enclosing elements. ๐Ÿšจ

This memo uses the elementary and composed types described in Section 2.1 of RFC 7016. The definitions in that section are incorporated by reference as though fully set forth here. ๐Ÿซถ

4. Cryptography Profile ๐Ÿ”

RTMFP defines a general security framework, but it delegates the specifics, like packet encryption ciphers and key agreement algorithms, to an application-defined Cryptography Profile ๐Ÿ’…

This section defines the RTMFP Cryptography Profile for Flash platform communication. Let's get into it ๐ŸŒˆ

4.1. Default Session Key

okay so RTMFP has this thing called the Default Session Key ๐Ÿ”‘โœจ and it gets used during session startup handshaking, which is basically the awkward "hi, let's set up our real keys" phase ๐Ÿซถ. Session-specific keys and ciphers get negotiated after that, bestie ๐Ÿ’…

the default cipher is AES ๐ŸŽ€ [AES] with 128-bit keys, running in CBC mode [CBC] (see Section 4.7.1, it's giving organized ๐Ÿ“š). And the Default Session Key itself is the 16 bytes of the string "Adobe Systems 02", encoded in UTF-8 [RFC3629] ๐ŸŒธ:

        Hex: 41 64 6F 62 65 20 53 79 73 74 65 6D 73 20 30 32

the Default Session Key uses checksum mode for packet verification, and it does not use session sequence numbers (Section 4.7.3) ๐Ÿ™ˆ no sequence numbers, no cap ๐Ÿงš

4.2. Diffie-Hellman Groups

okay this part is literally about which Diffie-Hellman groups you need to know ๐Ÿ”ฅ. Implementations conforming to this profile MUST support Diffie-Hellman [DH] modular exponentiation (MODP) group 2 (1024 bits) as defined in [RFC7296] ๐Ÿ’–, and they SHOULD support MODP group 5 (1536 bits) and group 14 (2048 bits) as defined in [RFC3526] โœจ. You MAY support additional groups too, if you're feeling extra ๐Ÿฆ‹

4.3. Certificates

this section is where we define the certificate format for this Cryptography Profile, plus how it maps onto the abstract properties and semantics for RTMFP endpoint identities ๐Ÿ‘‘

4.3.1. Format

so a certificate in this profile is a sequence of zero or more RTMFP Options and Markers (Section 2.1.3 of RFC 7016) ๐ŸŒˆ. The first marker (if there is one) splits the canonical section of the certificate from the rest. Some options get ignored if they show up outside the canonical section (aka after the first marker) ๐Ÿ’€

+~~~/~~~/~~~+   +~~~/~~~/~~~+~~~~~+~~~/~~~/~~~+   +~~~/~~~/~~~+
| L \ T \ V |...| L \ T \ V |  0  | L \ T \ V |...| L \ T \ V |
+~~~/~~~/~~~+   +~~~/~~~/~~~+~~~~~+~~~/~~~/~~~+   +~~~/~~~/~~~+
^                           ^  ^  ^                           ^
|  Zero or more non-empty   |  |  |   Zero or more Options    |
|         Options           |  |  +------  or Markers  -------+
|                           |  |
+---  Canonical Section  ---+  +---- First Marker
                                     (if present)

struct certificate_t
{
    canonicalStart = remainder();
    canonicalEnd = remainder();
    markerFound = false;

    while(remainder() > 0)
    {
        option_t option :variable*8;

        if(0 == option.length)
            markerFound = true;
        else if(!markerFound)
            canonicalEnd = remainder();
    };

    canonicalSectionLength = canonicalStart - canonicalEnd;
} :variable*8;

4.3.2. Fingerprint

a certificate's fingerprint is the SHA-256 hash [SHA256] of its canonical section (so, the hash of the first canonicalSectionLength bytes of the certificate) ๐Ÿ”. The fingerprint is also called the "peer ID" ๐Ÿซง

4.3.3. Options

this section lists the options that can show up in a certificate ๐Ÿ’…. The option type codes defined here are:

0x00:    Hostname (must be in canonical section) (Section 4.3.3.1)

0x0a:    Accepts Ancillary Data (must be in canonical section)
         (Section 4.3.3.2)

0x0e:    Extra Randomness (Section 4.3.3.3)
0x15:    Supported Ephemeral Diffie-Hellman Group (must be in
         canonical section) (Section 4.3.3.4)

0x1d:    Static Diffie-Hellman Public Key (must be in canonical
         section) (Section 4.3.3.5)

An implementation MUST ignore a certificate option type that it doesn't understand ๐Ÿ™…โ€โ™€๏ธ. That one's non-negotiable, bestie ๐Ÿ’€

4.3.3.1. Hostname

this option gives an optional hostname for the endpoint ๐Ÿ . It MUST be ignored if it is not in the canonical section, and it MUST NOT occur more than once in a certificate ๐Ÿšซ

+-------------/-+-------------/-+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|   length    \ |     0x00    \ |         hostname              |
+-------------/-+-------------/-+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/

struct hostnameCertOptionValue_t
{
    uint8_t hostname[remainder()];
} :remainder()*8;

4.3.3.2. Accepts Ancillary Data

this option says the endpoint will accept an Endpoint Discriminator that encodes an Ancillary Data option (Section 4.4.2.2) ๐Ÿ“ฆ. It MUST be ignored if it is not in the canonical section ๐Ÿ™ˆ

+-------------/-+-------------/-+
|   length    \ |     0x0a    \ |
+-------------/-+-------------/-+

4.3.3.3. Extra Randomness

this option adds extra entropy or randomness to a certificate that doesn't have any other cryptographic pseudorandom members (like a public key) ๐ŸŽฒ. It's typically used so that endpoints using ephemeral Diffie-Hellman keying can have a unique certificate fingerprint ๐Ÿฆ„

+-------------/-+-------------/-+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|   length    \ |     0x0e    \ |       extra randomness        |
+-------------/-+-------------/-+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/

struct extraRandomnessCertOptionValue_t
{
    uint_t extraRandomness[remainder()];
} :remainder()*8;

4.3.3.4. Supported Ephemeral Diffie-Hellman Group

this option specifies a Diffie-Hellman group ID that's supported for ephemeral keying ๐ŸŒท. It MUST be ignored if it is not in the canonical section. It may occur more than once in the certificate, and each instance indicates an additional group that's supported for key agreement ๐Ÿ’œ

+-------------/-+-------------/-+-------------/-+
|   length    \ |     0x15    \ |   group ID  \ |
+-------------/-+-------------/-+-------------/-+

struct ephemeralDHGroupCertOptionValue_t
{
    vlu_t groupID :variable*8;
} :variable*8;

the presence of this option means the certificate uses ephemeral Diffie-Hellman public keys only. The certificate MUST NOT contain a Static Diffie-Hellman public key (Section 4.3.3.5) ๐Ÿšซ๐Ÿ”‘

4.3.3.5. Static Diffie-Hellman Public Key

this option specifies a Diffie-Hellman group ID and a static public key in that group ๐Ÿ—๏ธ. It MUST be ignored if it is not in the canonical section. It MAY occur more than once in the certificate, but it SHOULD NOT occur more than once for each group ID. The behavior for specifying more than one public key per group ID is not defined ๐Ÿคทโ€โ™€๏ธ

+-------------/-+-------------/-+-------------/-+
|   length    \ |     0x1d    \ |   group ID  \ |
+-------------/-+-------------/-+-------------/-+
+------------------------------------------------------------------+
|                  Diffie-Hellman Public Key                       |
+------------------------------------------------------------------/

struct staticDHPublicKeyCertOptionValue_t
{
    vlu_t   groupID :variable*8;
    uintn_t publicKey :remainder()*8; // network byte order
} :remainder()*8;

the presence of this option means the certificate uses static Diffie-Hellman public keys only. The certificate MUST NOT contain any Supported Ephemeral Diffie-Hellman Group options (Section 4.3.3.4) ๐Ÿ™…โ€โ™€๏ธ

4.3.4. Authenticity

this profile doesn't use a public key infrastructure, and there are no signing keys in certificates ๐Ÿ™ƒ. So any properly encoded certificate is considered authentic according to Section 3.2 of RFC 7016 โœ…

a certificate with a static public key can only be used successfully for session communication if the holder actually holds the private key that goes with that public key ๐Ÿ”. Authenticity of an identity and its peer ID (Section 4.3.2) for a certificate with a static public key is implied by successful encrypted communication with the associated endpoint (Section 4.6) ๐Ÿซถ

see Section 7 for more on the security issues related to identities ๐Ÿ›ก๏ธ

4.3.5. Signing and Verifying Messages

RTMFP Initiator Initial Keying and Responder Initial Keying messages have a field for the sender's digital signature of the keying parameters (Sections 2.3.7 and 2.3.8 of RFC 7016) โœ๏ธ. In this profile, that signature field is encoded as zero or more RTMFP Options ๐Ÿง

+~~~/~~~/~~~~~~~+               +~~~/~~~/~~~~~~~+
| L \ T \   V   |...............| L \ T \   V   |
+~~~/~~~/~~~~~~~+               +~~~/~~~/~~~~~~~+
^                                               ^
+-------------  Zero or more Options  ----------+

struct initialKeyingSignature_t
{
    while(remainder() > 0)
        option_t option :variable*8;
} :remainder()*8;

if a signer has no signature options to send, it MAY encode the signature as a UTF-8 capital "X" (hex 58) or as empty ๐Ÿซ™. A verifier MUST interpret a malformed signature field, or a signature field that's only a UTF-8 capital "X", as though it were empty ๐Ÿ™

if a verifier doesn't require a signature, it SHALL consider any signature field (even an empty or malformed one) to be valid โœ…. A verifier MAY require a signature made of one or more non-empty options that are valid according to their respective types ๐Ÿ”’

this profile doesn't use a public key infrastructure, and there are no signing keys in certificates. Section 4.3.5.1.1 defines a simple ID/password credential system ๐Ÿ—๏ธ๐Ÿ’Œ

4.3.5.1. Options

this section lists the options that can appear in an RTMFP Initial Keying signature field ๐Ÿ“‹. The option type code defined here is:

0x1d:  Simple Password (Section 4.3.5.1.1)

future or derived profiles may define more signature field options and semantics, so a verifier SHOULD ignore option types it doesn't understand ๐ŸŒธ

4.3.5.1.1. Simple Password ๐Ÿ”‘

okay so this option is like a little ID card ๐Ÿชช it packs in a password identifier (think user name, or some app-specific or implementation-specific selector) plus an HMAC over the signed parameters, using the password tied to that identifier as the HMAC key ๐Ÿ”. it can show up more than once ๐Ÿ’… which is lowkey genius for version upgrades, because your current build and your previous build can both keep working with their own passwords ๐Ÿซถ

for the versioning vibe, a verifier SHOULD ignore a Simple Password option whose password identifier it doesn't recognize ๐Ÿ™ˆ that's the old-build-doesn't-know-you-yet situation. BUT if a recognized password identifier shows up with a wrong HMAC, the verifier SHOULD treat the entire signature as invalid ๐Ÿ’€ no partial credit, bestie.

 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-------------/-+-------------/-+
|   length    \ |     0x1d    \ |
+-------------/-+-------------/-+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                           hmacSHA256                          |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|                           passwordID                          |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/

struct simplePasswordSignatureOptionValue_t
{
    uint8_t hmacSHA256[32];
    uint8_t passwordID[remainder()];
} :remainder()*8;
  • hmacSHA256: HMAC-SHA256(K, M), where K is the password associated with passwordID, and M is the signed parameters.
  • passwordID: The identifier (such as a user name) for the password used as the HMAC key.

4.3.6. Glare Resolution โš”๏ธ

okay so glare is when two endpoints both try to start a session with each other at the same time ๐Ÿ™ƒ total chaos, but we have a plan.

the plan is to compare the near end's certificate with the far end's, using a binary lexicographic comparison ๐Ÿ” one byte at a time, up to the length of the shorter certificate. at the first byte that differs, the certificate with the lower value (treated as an unsigned 8-bit integer) gets ordered before the other one. if the certificates aren't the same length but match all the way up to the shorter one's length, the shorter certificate gets ordered first โœจ

the near end wins and becomes the Initiator if its certificate is ordered before the far end's, or is identical to it ๐Ÿ‘‘ otherwise, the near end's certificate is ordered after, and the near end takes on the Responder role ๐Ÿฅบ

4.3.7. Session Override ๐Ÿšซ

a new incoming session only overrides an existing session if its certificate is identical to the existing session's certificate. no matching cert, no takeover, periodt ๐Ÿ’…

4.4. Endpoint Discriminators ๐Ÿงš

this section is all about the Endpoint Discriminator (EPD) (Section 3.2 of RFC 7016) ๐Ÿ’– it covers the format and semantics for this Cryptography Profile, plus how it maps onto RTMFP's abstract certificate and identity selection semantics. main character energy for sure ๐Ÿ‘‘

4.4.1. Format ๐ŸŽ€

an EPD in this profile is just a sequence of zero or more RTMFP Options ๐Ÿ“

+~~~/~~~/~~~~~~~+               +~~~/~~~/~~~~~~~+
| L \ T \   V   |...............| L \ T \   V   |
+~~~/~~~/~~~~~~~+               +~~~/~~~/~~~~~~~+
^                                               ^
+-------------  Zero or more Options  ----------+

struct endpointDiscriminator_t
{
    while(remainder() > 0)
        option_t option :variable*8;
} :remainder()*8;

4.4.2. Options ๐Ÿญ

this section lists the options that can show up in an EPD. these are the option type codes that are defined:

  • 0x00: Required Hostname (Section 4.4.2.1)
  • 0x0a: Ancillary Data (Section 4.4.2.2)
  • 0x0f: Fingerprint (Section 4.4.2.3)

the way these options get used for picking certificates is in Section 4.4.3 ๐Ÿ”Ž

an implementation MUST ignore EPD option types that it doesn't understand ๐Ÿซ  no drama, just skip them.

4.4.2.1. Required Hostname ๐ŸŒ

this option says which hostname to match against the certificate's Hostname option (Section 4.3.3.1) ๐Ÿ’•

+-------------/-+-------------/-+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|   length    \ |     0x00    \ |         hostname              |
+-------------/-+-------------/-+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/

struct hostnameEPDOptionValue_t
{
    uint8_t hostname[remainder()];
} :remainder()*8;

this option MUST NOT occur more than once in an EPD ๐Ÿ™…โ€โ™€๏ธ

4.4.2.2. Ancillary Data ๐Ÿฆ‹

in this profile, this option tells you the server Uniform Resource Identifier (URI) [RFC3986] that the client is connecting to on this session, encoded in UTF-8 ๐ŸŒธ for example, "rtmfp://server.example.com/app/instance".

+-------------/-+-------------/-+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|   length    \ |     0x0a    \ |       ancillary data          |
+-------------/-+-------------/-+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/

struct ancillaryDataEPDOptionValue_t
{
    uint8_t ancillaryData[remainder()];
} :remainder()*8;

this option MUST NOT occur more than once in an EPD โœจ

4.4.2.3. Fingerprint ๐Ÿ”

this option carries the 256-bit (32-byte) fingerprint (Section 4.3.2) of a certificate ๐Ÿชž

 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-------------/-+-------------/-+
|   length    \ |     0x0f    \ |
+-------------/-+-------------/-+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                          fingerprint                          |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

struct fingerprintEPDOptionValue_t
{
    uint8_t fingerprint[32];
} :256;

this option MUST NOT occur more than once in an EPD ๐Ÿ™ˆ

4.4.3. Certificate Selection ๐Ÿ’˜

this section describes the REQUIRED method for figuring out whether an EPD selects a certificate ๐Ÿ”

an EPD MUST contain at least one of Fingerprint, Required Hostname, or Ancillary Data options to select any certificate ๐Ÿซถ

a Fingerprint EPD option selects or rejects a certificate no matter what other options are present ๐ŸŽฏ

without a Fingerprint option, a Required Hostname EPD option, if present, requires an identical Hostname option in the certificate ๐Ÿ“

without a Fingerprint option, an Ancillary Data EPD option, if present, requires that the certificate has an Accepts Ancillary Data option ๐Ÿฆ‹

if EPD contains a Fingerprint option:
    if certificate.fingerprint == option.fingerprint:
        certificate is selected. stop.
    else:
        certificate is not selected. stop.
else:
    if EPD contains a Required Hostname option:
        if certificate contains a Hostname option:
            if certificate.hostname != option.hostname:
                certificate is not selected. stop.
        else:
            certificate is not selected. stop.
    if EPD contains an Ancillary Data option:
        if certificate doesn't have an Accepts Ancillary Data option:
            certificate is not selected. stop.
    else if EPD does not contain a Required Hostname option:
        certificate is not selected. stop.
    certificate is selected. stop.

Figure 1: Algorithm to Test Whether an EPD Selects a Certificate

4.4.4. Canonical Endpoint Discriminator ๐Ÿ’Ž

in this profile, a Canonical Endpoint Discriminator (Section 3.2 of RFC 7016) has only a Fingerprint option (Section 4.4.2.3) and no other options ๐Ÿ™Œ the option length and type code MUST be encoded as 1-byte VLUs, even though VLU encoding would let those fields take up an arbitrary number of bytes. so the Canonical Endpoint Discriminator MUST be exactly 34 bytes long: a length field of 0x21 in one byte, a type code of 0x0f in one byte, and then 32 bytes of fingerprint ๐Ÿง

 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     0x21      |     0x0f      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                          fingerprint                          |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

struct canonicalEndpointDiscriminator_t
{
    uint8_t length = 0x21;
    uint8_t type = 0x0f;
    uint8_t fingerprint[32];
} :272;

4.5. Session Keying Components ๐Ÿ”‘โœจ

this section describes the format of the Session Key Initiator Component inside the Initiator Initial Keying RTMFP chunk, and the Session Key Responder Component inside the Responder Initial Keying RTMFP chunk (Sections 2.3.7 and 2.3.8 of RFC 7016) ๐Ÿฆ‹ the Initiator and Responder Session Keying Components both have the same format, so no surprises there ๐Ÿ’œ

4.5.1. Format

Okay so a Session Keying Component in this profile is just a bunch of RTMFP Options stacked up, zero or more of them ๐Ÿ’– Picture it like a little train of options:

+~~~/~~~/~~~~~~~+               +~~~/~~~/~~~~~~~+
| L \ T \   V   |...............| L \ T \   V   |
+~~~/~~~/~~~~~~~+               +~~~/~~~/~~~~~~~+
^                                               ^
+-------------  Zero or more Options  ----------+
struct sessionKeyingComponent_t
{
    while(remainder() > 0)
        option_t option :variable*8;
} :remainder()*8;

4.5.2. Options

This section is the menu of options that can show up in a Session Keying Component ๐ŸŽ€ These are the option type codes that are defined:

  • 0x0d: Ephemeral Diffie-Hellman Public Key (Section 4.5.2.1)
  • 0x0e: Extra Randomness (Section 4.5.2.2)
  • 0x1d: Diffie-Hellman Group Select (Section 4.5.2.3)
  • 0x1a: HMAC Negotiation (Section 4.5.2.4)
  • 0x1e: Session Sequence Number Negotiation (Section 4.5.2.5)

An implementation MUST ignore a session keying component option type that is not understood. So if you don't recognize a type, just skip it, no drama ๐Ÿ™ˆ

4.5.2.1. Ephemeral Diffie-Hellman Public Key

This option is where you specify a Diffie-Hellman group ID and a public key in that group ๐Ÿ”‘ It MUST NOT be sent if the sender's certificate has a static Diffie-Hellman public key. It MUST be sent if the sender's certificate does not have a static Diffie-Hellman public key. And it MUST NOT be sent more than once. Don't double up, bestie ๐Ÿ˜ญ

+-------------/-+-------------/-+-------------/-+
|   length    \ |     0x0d    \ |   group ID  \ |
+-------------/-+-------------/-+-------------/-+
+------------------------------------------------------------------+
|                  Diffie-Hellman Public Key                       |
+------------------------------------------------------------------/
struct ephemeralDHPublicKeyKeyingOptionValue_t
{
    vlu_t   groupID :variable*8;
    uintn_t publicKey :remainder()*8; // network byte order
} :remainder()*8;

4.5.2.2. Extra Randomness

This option is for adding extra entropy or randomness to a keying component, especially when the sender is using a static public key ๐ŸŒธ When it's used for that purpose, the extra randomness SHOULD be cryptographically strong pseudorandom bytes, not less than 16 bytes (for cryptographically significant entropy) and not more than 64 bytes (the length of a SHA-256 input block). The extra randomness acts as a salt when the session keys get computed (Section 4.6). Salt era, honestly ๐Ÿง‚โœจ

+-------------/-+-------------/-+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|   length    \ |     0x0e    \ |       extra randomness        |
+-------------/-+-------------/-+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/
struct extraRandomnessKeyingOptionValue_t
{
    uint_t extraRandomness[remainder()];
} :remainder()*8;

4.5.2.3. Diffie-Hellman Group Select

The Initiator sends this option to pick which Diffie-Hellman group to use for key agreement ๐Ÿ‘‘ The Initiator MUST send this option when it advertises a static Diffie-Hellman public key in its certificate, and it MUST NOT send this option if it sends an ephemeral Diffie-Hellman public key. It MUST NOT be sent more than once. Pick one group and stick with it ๐Ÿ’…

+-------------/-+-------------/-+-------------/-+
|   length    \ |     0x1d    \ |   group ID  \ |
+-------------/-+-------------/-+-------------/-+
struct staticDHGroupSelectKeyingOptionValue_t
{
    vlu_t   groupID :variable*8;
} :variable*8;

4.5.2.4. HMAC Negotiation

This option is how two ends negotiate sending and receiving an HMAC field for packet verification ๐Ÿฆ‹

                                 |0 1 2 3 4 5 6 7|
+-------------/-+-------------/-+-+-+-+-+-+-+-+-+-------------/-+
|             \ |             \ |         |S|S|R|             \ |
|   length    / |     0x1a    / |   rsv   |N|O|E|  hmacLength / |
|             \ |             \ |         |D|R|Q|             \ |
+-------------/-+-------------/-+-+-+-+-+-+-+-+-+-------------/-+
struct hmacNegotiationKeyingOptionValue_t
{
    uintn_t reserved :5;          // rsv
    bool_t  willSendAlways :1;    // SND
    bool_t  willSendOnRequest :1; // SOR
    bool_t  request :1;           // REQ
    vlu_t   hmacLength :variable*8;
} :variable*8;

Here's what each bit and field means:

  • willSendAlways: If set, the sender will send an HMAC on packets in this session.
  • willSendOnRequest: If set, the sender will send an HMAC on packets in this session if the other end sets the request flag in its HMAC Negotiation.
  • request: If set, the sender would very much like the receiver to send an HMAC on its packets. If the other end doesn't send an HMAC on its packets, the session can fail. Big yikes if that happens ๐Ÿ˜ฌ
  • hmacLength: If the sender negotiates to send an HMAC on its packets, the HMAC field will be this many bytes long. This value MUST be between 4 and 32 inclusive, or 0 if and only if willSendAlways and willSendOnRequest are clear.

The handshake operational semantics for this option are described in Section 4.6.4. โญ

4.5.2.5. Session Sequence Number Negotiation

This option is how two ends negotiate sending and receiving the Session Sequence Number field for packet verification ๐Ÿงš

                                 |0 1 2 3 4 5 6 7|
+-------------/-+-------------/-+-+-+-+-+-+-+-+-+
|             \ |             \ |         |S|S|R|
|   length    / |     0x1e    / |   rsv   |N|O|E|
|             \ |             \ |         |D|R|Q|
+-------------/-+-------------/-+-+-+-+-+-+-+-+-+
struct sseqNegotiationKeyingOptionValue_t
{
    uintn_t reserved :5;          // rsv
    bool_t  willSendAlways :1;    // SND
    bool_t  willSendOnRequest :1; // SOR
    bool_t  request :1;           // REQ
} :8;
  • willSendAlways: If set, the sender will send a session sequence number in packets in this session.
  • willSendOnRequest: If set, the sender will send a session sequence number in packets in this session if the other end sets the request flag in its Session Sequence Number Negotiation.
  • request: If set, the sender would very much like the receiver to send a session sequence number in its packets. If the other end doesn't send a session sequence number in its packets, the session can fail.

The handshake operational semantics for this option are described in Section 4.6.6. ๐Ÿ’œ

4.6. Session Key Computation

This section is all about how to compute the cryptographic keys and other settings for packet encryption and verification ๐Ÿ”

The Session Key Near Component (SKNC) means the keying component sent by the near end of the session. So it's the Session Key Initiator Component at the Initiator, and the Session Key Responder Component at the Responder ๐Ÿซถ

The Session Key Far Component (SKFC) means the keying component sent by the far end of the session. So it's the Session Key Responder Component at the Initiator, and the Session Key Initiator Component at the Responder ๐ŸŒˆ

4.6.1. Public Key Selection

This section lists the public key selection methods for every possible combo of static or ephemeral public key modes for each endpoint, based on their certificate options (Section 4.3.3). Mix and match, it's giving puzzle ๐Ÿงฉ

4.6.1.1. Initiator and Responder Ephemeral

The Initiator and Responder each list one or more Supported Ephemeral Diffie-Hellman Group options (Section 4.3.3.4) in their certificates. The Initiator sends exactly one Ephemeral Diffie-Hellman Public Key option (Section 4.5.2.1) in its Session Key Initiator Component, which selects one group from among those supported by the Responder and Initiator. The Responder sends exactly one Ephemeral Diffie-Hellman Public Key option in its Session Key Responder Component, in the same group as indicated by the Initiator โœจ

4.6.1.2. Initiator Ephemeral and Responder Static

The Responder lists one or more Static Diffie-Hellman Public Key options (Section 4.3.3.5) in its certificate. The Initiator lists one or more Supported Ephemeral Diffie-Hellman Group options in its certificate. The Initiator sends exactly one Ephemeral Diffie-Hellman Public Key option in its Session Key Initiator Component, which selects one group from among those supported by the Responder and Initiator, and the corresponding public key for the Responder. The Responder uses its public key from the indicated group, and sends only an Extra Randomness option (Section 4.5.2.2) in its Session Key Responder Component to salt the session keys ๐Ÿง‚

4.6.1.3. Initiator Static and Responder Ephemeral

The Responder lists one or more Supported Ephemeral Diffie-Hellman Group options in its certificate. The Initiator lists one or more Static Diffie-Hellman Public Key options in its certificate. The Initiator sends exactly one Diffie-Hellman Group Select option (Section 4.5.2.3) in its Session Key Initiator Component, which selects one group from among those supported by the Responder and Initiator, and the corresponding public key for the Initiator, plus an Extra Randomness option to salt the session keys. The Responder sends an Ephemeral Diffie-Hellman Public Key option in its Session Key Responder Component in the same group as indicated by the Initiator ๐Ÿฆ‹

4.6.1.4. Initiator and Responder Static

The Initiator and Responder each list one or more Static Diffie-Hellman Public Key options in their certificates. The Initiator sends exactly one Diffie-Hellman Group Select option in its Session Key Initiator Component, which selects one group and corresponding public keys from among those supported by the Responder and Initiator, and an Extra Randomness option to salt the session keys. The Responder sends an Extra Randomness option in its Session Key Responder Component to add its own salt to the session keys ๐Ÿง‚๐Ÿง‚

4.6.2. Diffie-Hellman Shared Secret

To be acceptable, a Diffie-Hellman public key MUST have all of the following properties:

  • Be at least 16777216 (2^24);
  • Be at most the group's prime modulus minus 16777216;
  • Have at least 16 "1" bits;
  • Have at least 16 "0" bits, not including leading zeros.

An endpoint MUST NOT complete to an S_OPEN session with a far endpoint using a public key that is not acceptable according to these criteria. Do not skip this check, it's literally a dealbreaker ๐Ÿšซ

Once the group and corresponding public key of the far end is determined, the far end's public key and the near end's private key are combined according to Diffie-Hellman [DH] to compute the Diffie-Hellman Shared Secret, an integer ๐Ÿ’ซ

In the following sections, DH_SECRET means the Diffie-Hellman Shared Secret encoded as a byte-aligned unsigned integer in network byte order with no leading zero bytes. For example, if the shared secret is 4886718345, DH_SECRET would be the five bytes:

Hex: 01 23 45 67 89

4.6.3. Packet Encrypt/Decrypt Keys

Packets get encrypted using a symmetric cipher, such as the Advanced Encryption Standard [AES]. Distinct keys are used for sending and receiving packets. Each end's sending (encrypt) key is the other end's receiving (decrypt) key. Mirror mirror ๐Ÿชž๐Ÿ’–

The raw keys computed in this section for encryption and decryption are transformed in a manner specific to the cipher they're going to be used with. In this profile, AES-128 is the only currently defined cipher. For this cipher, the first 128 bits (16 bytes) of the 256-bit output of the calculation are taken to be the AES-128 key.

Set ENCRYPT_KEY = HMAC-SHA256(DH_SECRET, HMAC-SHA256(SKFC, SKNC));

Set DECRYPT_KEY = HMAC-SHA256(DH_SECRET, HMAC-SHA256(SKNC, SKFC));

The full 256 bits of ENCRYPT_KEY and DECRYPT_KEY are used in the computations in the following sections ๐ŸŒท

4.6.4. Packet HMAC Send/Receive Keys โœจ

okay so packets can get checked to see if they got corrupted or messed with, and the way we do that is by slapping an HMAC onto the end ๐Ÿ” which one you use (HMAC or a simple checksum) gets decided during the initial keying phase, using the HMAC Negotiation option (Section 4.5.2.4) ๐Ÿ’… and the two ends get separate HMAC keys for sending and receiving. your sending key is their receiving key and vice versa, it's giving mirror ๐Ÿชž

Set HMAC_SEND_KEY = HMAC_SHA256(DH_SECRET, ENCRYPT_KEY);

Set HMAC_RECV_KEY = HMAC_SHA256(DH_SECRET, DECRYPT_KEY);

if an endpoint has the willSendAlways flag set in its HMAC Negotiation option, then it MUST send an HMAC on the packets it sends with this session key ๐Ÿซถ no exceptions bestie

if the willSendAlways flag is clear but willSendOnRequest is set, then it MUST send an HMAC on packets it sends with this session key if and only if the other endpoint's request flag is set ๐Ÿ™ˆ so it's basically a "only if you ask nicely" situation

if a sending endpoint has both willSendAlways and willSendOnRequest clear, then the receiving endpoint SHOULD reject that keying component, but only if the receiving endpoint is configured to require the sending endpoint to send HMAC ๐Ÿ’€

if HMAC is negotiated to be used, the corresponding hmacLength MUST be between 4 and 32 inclusive ๐Ÿ“ that's the range, no going outside it

if HMAC is negotiated not to be used, a simple checksum does the verifying instead ๐Ÿงš

and the Default Session Key uses the simple checksum and does not use HMAC. main character of the simple stuff ๐Ÿ‘‘


4.6.5. Session Nonces ๐ŸŒธ

session nonces are per-session, cryptographically strong secret values that only the two endpoints of the session know ๐Ÿคซ they can be used for application-layer cryptographic challenges (things like signing or password verification) and they're basically a convenience, since they get pre-shared and pre-agreed-upon in a secure way during the initial keying handshake ๐ŸŽ€

each end's near nonce is the other end's far nonce, and vice versa ๐Ÿชž mirror energy again

Set NEAR_NONCE = HMAC_SHA256(DH_SECRET, SKNC);

Set FAR_NONCE = HMAC_SHA256(DH_SECRET, SKFC);

4.6.6. Session Sequence Number ๐Ÿ”ข

okay so duplicate packets can get caught and rejected by using an optional session sequence number tucked inside the encrypted packets ๐Ÿ•ต๏ธโ€โ™€๏ธ the session sequence number is a monotonically increasing unbounded integer and it does not wrap, so it just keeps going and going ๐Ÿฆ‹ session sequence numbers SHOULD start at zero and SHOULD increment by one for each packet sent using that session key โœจ and implementations MUST handle session sequence numbers with no less than 64 bits of range ๐Ÿ’ช that's a lot of room bestie

if an endpoint has willSendAlways set in its Session Sequence Number Negotiation option (Section 4.5.2.5), then it MUST send a session sequence number in packets it sends with this session key ๐Ÿซถ

if willSendAlways is clear but willSendOnRequest is set, then it MUST send a session sequence number on packets it sends with this session key if and only if the other endpoint's request flag is set ๐Ÿ™ˆ same "only if you ask" vibe as the HMAC stuff

if a sending endpoint has both willSendAlways and willSendOnRequest clear, then the receiving endpoint SHOULD reject that keying component, but only if the receiving endpoint is configured to require the sending endpoint to send session sequence numbers ๐Ÿ’…

and the Default Session Key does not use session sequence numbers at all ๐Ÿงš


4.7. Packet Encryption ๐Ÿ”

this section is all about the concrete syntax and operational semantics of RTMFP packet encryption for this Cryptography Profile ๐Ÿ’–

4.7.1. Cipher ๐ŸŽ€

this profile defines AES-128 [AES] in CBC [CBC] mode as the only cipher ๐ŸŒˆ the only one! extensions to this profile can specify and negotiate additional ciphers and modes, but only by defining certificate and keying component options and their semantics ๐Ÿงฉ

for AES-128-CBC, the initialization vector (IV) for each packet is 16 zero bytes ๐Ÿค and the IV is not included in the packet, so it's not sent along for the ride ๐Ÿš—

4.7.2. Format ๐Ÿง

the Encrypted Packet is the encryptedPacket field of an RTMFP Multiplex packet (Section 2.2.2 of RFC 7016) ๐Ÿ“ฆ aka the part of the Multiplex packet that comes after the scrambled session ID. here's how it's laid out:

+----------------+     +----------------+~~~~~~~~~~~~~~~~~~~~~~~+
|  CBC Block 1   | ... |  CBC Block N   |     truncatedHMAC     |
+----------------+     +----------------+~~~~~~~~~~~~~~~~~~~~~~~+
^                                       ^                       ^
|     Zero or more AES-128 chained      | hmacLength bytes long |
+--------    cipher blocks   -----------+---  (may be zero)  ---+
struct flashProfileEncryptedPacket_t
{
    if(HMAC is being used)
        hmacLength = negotiated length;
    else
        hmacLength = 0;

    struct
    {
        iv[16 bytes] = { 0 };
        blockCount = 0;
        while((remainder() > hmacLength) && (remainder() >= 16))
        {
            uint8_t cbcBlock[16];
            blockCount++;
        }
    } chainedCipherBlocks :variable*16*8;

    if(HMAC is being used)
    {
        if(remainder() == hmacLength)
            uint8_t truncatedHMAC[hmacLength];
        else
            packetVerificationFailed();
    }
    else if(remainder() > 0)
        packetVerificationFailed();
} :encryptedPacket.length*8;

cbcBlock: the next AES-128-CBC block ๐Ÿงฑ

chainedCipherBlocks: all the cipher blocks in the packet, stuck together. this is what the HMAC gets computed over ๐Ÿ”—

truncatedHMAC: if HMAC was negotiated to be used (Section 4.5.2.4), this field is set to the first negotiated hmacLength bytes of the HMAC of the chainedCipherBlocks ๐Ÿซง

the plaintext data before encryption or after decryption looks like this:

 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+~~~~~~~~~~~~~/~+
| SSEQ (opt.) \ |
+~~~~~~~~~~~~~/~+
+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+
|        Checksum (opt.)        |
+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+~+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
|                        Plain RTMFP Packet                     |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/
struct flashProfilePlainPacket_t
{
    if(session sequence numbers being used)
        vlu_t sessionSequenceNumber :variable*8; // SSEQ
    if(HMAC not being used)
        uint16_t checksum;
    packet_t plainRTMFPPacket :variable*8;
} :chainedCipherBlocks.blockCount*16*8;

sessionSequenceNumber: if session sequence numbers were negotiated to be used (Section 4.6.6), this field shows up and holds the VLU session sequence number of this packet ๐Ÿ”ข

checksum: if HMAC was not negotiated, this field is there and holds the simple checksum (Section 4.7.3.1) of the remaining bytes of this structure โœ…

plainRTMFPPacket: the plain, unencrypted RTMFP Packet (Section 2.2.4 of RFC 7016) plus any padding it needs ๐Ÿงธ

when you're putting this structure together, and before you calculate the checksum (if there is one), check the total length. if it's not an integer multiple of 16 bytes (the AES cipher block size), pad the end of plainRTMFPPacket with 0xff bytes until it hits that multiple of 16 ๐ŸŒŸ the receiver's RTMFP Packet parser (Section 2.2.4 of RFC 7016) will eat that padding up for you ๐Ÿ˜‹

4.7.3. Verification ๐Ÿ”

in RTMFP, the Cryptography Profile is the one responsible for packet verification ๐Ÿ‘‘ in this profile, packets get verified with either an HMAC or a simple checksum, depending on how the endpoints are configured, and they can optionally also get checked against replay or duplication using session sequence numbers ๐Ÿ•ต๏ธโ€โ™€๏ธ the simple checksum sits inside the encrypted packet, so it basically becomes a 16-bit cryptographic checksum. wild, right? ๐Ÿคฏ

4.7.3.1. Simple Checksum ๐Ÿงฎ

the simple checksum is the 16-bit ones' complement of the 16-bit ones' complement sum of all the 16-bit (2 bytes, network byte order) words being checked ๐Ÿ”ข if there's an odd number of bytes, treat the last byte as the lower 8 bits of a 16-bit word whose upper 8 bits are 0. this is also known as the "Internet Checksum" [RFC1071] ๐ŸŒ

when it's there, the checksum gets calculated over all the bytes of the plaintext packet starting after the checksum field, all the way to the end of the plain packet ๐Ÿงพ it can't be calculated until the plain packet has been padded (if needed) to an integer multiple of 16 bytes (the AES cipher block size) ๐Ÿงฑ and the session sequence number field (if present) and the checksum field itself don't get included in the checksum ๐Ÿ™…โ€โ™€๏ธ

when receiving a packet that's verified with a checksum: calculate the checksum over all the bytes of the plaintext packet following the checksum field, then compare it to the value in the checksum field. if they match, the packet is verified โœ… if they don't match, the packet is corrupt and MUST be discarded as though it was never received ๐Ÿ—‘๏ธ it's like it never even happened, bestie

4.7.3.2. HMAC ๐Ÿ”

when present, the HMAC field is the last hmacLength bytes of the packet, and it's calculated over all of the encrypted cipher blocks of the packet that come before the HMAC field ๐Ÿงฑ the value of the HMAC field is the first hmacLength bytes of the HMAC-SHA256 of the checked data, using the computed HMAC keys (Section 4.6.4) and the negotiated hmacLength (Section 4.5.2.4) ๐Ÿซง also, each endpoint independently specifies the length of the HMAC it'll send, via its own hmacLength field. no copying each other here ๐Ÿ™…โ€โ™€๏ธ

when an endpoint has negotiated to send an HMAC, it encrypts the data blocks, computes the HMAC over those encrypted data blocks using its HMAC_SEND_KEY, and appends the first hmacLength bytes of that hash after the final encrypted data block ๐Ÿ“ฎ

when an endpoint has negotiated to receive an HMAC, it computes the HMAC over the encrypted data blocks using its HMAC_RECV_KEY, then compares the first receive hmacLength bytes of the computed HMAC to the HMAC field in the packet ๐Ÿ” if they're identical, the packet is verified โœจ if they're not, the packet is corrupt and MUST be discarded as though it was never received ๐Ÿ’€

HMAC and simple checksum verification are mutually exclusive. you pick one, no doing both ๐Ÿ™…โ€โ™€๏ธ

4.7.3.3. Session Sequence Number ๐Ÿ”ข

session sequence numbers are used to detect and reject a packet that got duplicated in the network or replayed by an attacker ๐Ÿšจ they also make sure the first chained cipher block of every packet is unique, which is a neat trick since it stands in for a full-block initialization vector ๐Ÿง  sequence numbers start at zero, increase by one for each packet sent in the session, don't wrap, and don't repeat ๐Ÿ”

when session sequence numbers are negotiated to be used, the receiver MUST allow for packets to be reordered in the network by up to at least 32 sequence numbers ๐ŸŒ€ but heads up: reordering by more than three packets can trigger loss detection and retransmission by negative acknowledgement, just like with TCP, so it's not likely to happen on the real Internet ๐ŸŒ

[RFC4302], [RFC4303], and [RFC6479] describe Anti-Replay Window methods that can be used to detect duplicate sequence numbers ๐ŸชŸ other methods are possible too, so you're not locked in ๐Ÿฆ‹

any packet that comes in with a session sequence number that was already seen in that session, either directly or by being less than the lowest sequence number in the Anti-Replay Window, is a duplicate and MUST be discarded as though it was never received ๐Ÿ—‘๏ธ bye bye, duplicate ๐Ÿ’…


5. Flash Communication ๐ŸŽฌ

the Flash platform uses RTMP [RTMP] messages for media streaming and communication ๐Ÿ“บ this section explains how to transport RTMP messages over RTMFP flows, plus the extra messages and semantics that are unique to this transport ๐ŸŒˆ

5.1. RTMP Messages ๐ŸŽ€

okay so an RTMP message is basically a little package ๐Ÿ“ฆ with a virtual header and a payload. The virtual header has four parts: a Message Type, a Payload Length, a Timestamp, and a Stream ID. The payload's format depends on what kind of message it is ๐Ÿ’…

An RTMP message gets mapped onto a lower transport layer, like the RTMP Chunk Stream [RTMP] or RTMFP. RTMP messages were originally designed alongside RTMP Chunk Stream, and transported on it. That design puts limits on the values the message header fields can hold โœจ Here's the tea:

  • Message Type is 8 bits wide, so it's limited to values from 0 to 255 inclusive;
  • Payload Length is 24 bits wide, so messages can be at most 16777215 bytes long;
  • Timestamp is 32 bits wide, so timestamps range from 0 to 4294967295 and wrap around;
  • Stream ID is 24 bits wide, so it's limited to values from 0 to 16777215 inclusive.

RTMP Chunk Stream Protocol Control messages (message types 1, 2, 3, 5, and 6) are not used when RTMP messages are transported in RTMFP flows. Messages of those types SHOULD NOT be sent and MUST be ignored ๐Ÿ™ˆ

5.1.1. Flow Metadata ๐Ÿฆ‹

everything in RTMFP travels in flows. In this profile, an RTMFP flow carrying RTMP messages holds messages for exactly one RTMP Stream ID. You can have multiple flows carrying messages for the same Stream ID, though! For example, the audio and video of one stream could go on separate flows, so the audio gets higher transmission priority ๐Ÿ”ฅ

The User Metadata for flows in this profile starts with a distinct signature, so you can tell different kinds of flows apart. For a flow used for RTMP messages, that signature is the two-character string "TC" ๐ŸŒธ

The Stream ID gets encoded in the flow's User Metadata, so it doesn't have to be sent with every single message. Smart, right? ๐Ÿง 

The sender can know ahead of time what kind of media it's going to send on a flow and what that flow is for. It can then give the receiver a hint about whether messages should be delivered as soon as possible or in their original queuing order โฉ For example, the sender might be sending real-time, delay-sensitive audio on a flow, and hint that the receiver should take those messages as soon as they land in the network, so the end-to-end latency of the audio goes down ๐ŸŽง

The receiver gets to choose whether to take delivery of messages as soon as they arrive in the network or in their original queuing order. A receiver that chooses to take delivery as soon as messages arrive in the network MUST be prepared for those messages to arrive out-of-order ๐Ÿ˜ญ For example, a receiver might choose not to render a newly received audio message whose timestamp is earlier than the most recently rendered audio timestamp.

The sender can also abandon a message it has queued in a flow, as long as the message hasn't been delivered to the receiver yet ๐Ÿ—‘๏ธ For example, the sender might abandon a real-time, delay-sensitive audio message that hasn't been delivered within one second, so it doesn't waste transmission resources on stale media that's no longer relevant.

Note: A gap will cause a delay at the receiver of at least one round-trip time if the receiver is taking delivery of messages in original queuing order.
    0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+~~~~~~~~~~~~~/~+
   |               |               |         |S|r|R|             \ |
   |   0x54  'T'   |   0x43  'C'   |   rsv   |I|s|X|   streamID  / |
   |               |               |         |D|v|I|             \ |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+~~~~~~~~~~~~~/~+
   struct RTMPMetadata_t
   {
       uint8_t signature[2] == { 'T', 'C' };
       uintn_t reserved1       :5; // rsv
       bool_t  streamIDPresent :1; // SID
       uintn_t reserved2       :1; // rsv
       uintn_t receiveIntent   :1; // RXI
           // 0: original queuing order, 1: network arrival order
       if(streamIDPresent)
           vlu_t   streamID        :variable*8;
   } :variable*8;

Here's what each field means:

  • signature: the metadata signature for RTMP message flows, which is the two UTF-8 coded characters "TC".
  • streamIDPresent: a boolean flag saying whether the streamID field is present. In this profile, this flag MUST be set.
  • receiveIntent: the sender's hint about the best order to take delivery of messages from the flow. A value of zero means the flow's messages should be received in the order the sender originally queued them (that is, in ascending sequence number order). A value of one means the flow's messages should be received in the order they arrive in the network, even if there are sequence number gaps or reordering. Network arrival order is typically hinted for live, delay-sensitive flows, such as audio media. To take delivery of a message as soon as it arrives in the network, receive it from the receiving flow's RECV_BUFFER as soon as it becomes complete (Section 3.6.3.3 of RFC 7016), and remove it from the RECV_BUFFER. Section 3.6.3.3 of RFC 7016 describes how to take delivery of messages in original queuing order.
  • streamID: if the streamIDPresent flag is set, this field is present and is the RTMP stream ID that the messages in this flow belong to. In this profile, this field MUST be present.

A receiver SHOULD reject an RTMP message flow if its streamIDPresent flag is clear. This profile doesn't define a stream mapping for that case ๐Ÿšซ

Derived or composed profiles can define additional flow types and their matching metadata signatures. A receiver SHOULD reject a flow having an unrecognized metadata signature ๐Ÿ™…โ€โ™€๏ธ

5.1.2. Message Mapping ๐Ÿ’Œ

This section describes the format of an RTMP message (Section 5.1) in an RTMFP flow.

    0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
   +-+-+-+-+-+-+-+-+
   |  messageType  |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           timestamp                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                         messagePayload                        |
   +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/
   struct RTMPMessage_t
   {
       uint8_t  messageType;
       uint32_t timestamp;
       uint8_t  messagePayload[remainder()];
   } :flowMessageLength*8;
  • messageType: the RTMP Message Type;
  • timestamp: the RTMP Timestamp, in network byte order;
  • messagePayload: the payload of the RTMP message;
  • payload length: the RTMP message payload length is inferred from the length of the RTMFP message;
  • Stream ID: the Stream ID for this message is taken from the metadata of the flow on which this message was received.

5.2. Flow Synchronization ๐Ÿšง

RTMFP flows are independent and have no ordering guarantee between flows. RTMP was designed for transport over a single, reliable, strictly ordered byte stream. Some RTMP message semantics take advantage of that ordering. For example, a Stream EOF User Control event must not be processed until after all media messages for the corresponding stream have been received. Flow Synchronization messages act as a barrier to line up message delivery across flows when RTMP semantics require it ๐Ÿงฑ

A Flow Synchronization message is coded as a User Control event message (Type 4) with Event Type 34. Message timestamps are ignored and MAY be set to 0.

    0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
   +-+-+-+-+-+-+-+-+
   |       4       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           timestamp                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         eventType = 34        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                             syncID                            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                             count                             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   struct flowSyncUserControlMessagePayload_t
   {
       uint16_t eventType = 34;
       uint32_t syncID;
       uint32_t count;
   } :10*8;
  • eventType: the RTMP User Control Message Event Type. Flow Synchronization messages have type 34 (0x22);
  • syncID: the identifier for this barrier;
  • count: the number of flows being synchronized by syncID. This field MUST be at least 1 and SHOULD be at least 2.

On receipt of a Flow Synchronization message, a receiver SHOULD suspend receipt of further messages on that flow until count Flow Synchronization messages (including this one) with the same syncID have been received on flows in the same flow association tree ๐Ÿ›‘

Example: Consider flows F1 and F2 in the same NetConnection carrying messages M, and let Sync(syncID,count) denote a Flow Synchronization message.

                                       |                |
             F1: M1  M2  M4  Sync(8,2) | Sync(13,2).....| M7
                                       |                |
             F2:   M3  Sync(8,2).......| M5  Sync(13,2) | M6
                                       |                |
                                   Barrier 8        Barrier 13

Figure 2: Example Flow Synchronization Barriers

Flow Synchronization messages form a delivery barrier that imposes at least a partial message ordering across flows. In this example, message M5 comes after M1..4 and before M6..7. However, M3 could be delivered before or after any of M1, M2, or M4, and M6 could come before or after M7 ๐Ÿงฉ

Flow Synchronization can cause a priority inversion, so it SHOULD NOT be used except when necessary to preserve RTMP ordering semantics. โš ๏ธ

5.3. Client-to-Server Connection ๐Ÿ”Œ

The client connects to a server. The connection has one main control flow in each direction, from client to server and from server to client, for NetConnection messages. It also has zero or more flows in each direction for NetStream media messages. NetStream flows can come and go naturally over time, depending on media transport needs. An exception on a NetConnection control sending flow indicates that the other end has closed the NetConnection and all associated NetStreams ๐Ÿซถ

The client MUST NOT use the same client certificate for more than one server connection. That is, a client's peer ID MUST NOT be reused ๐Ÿ”

5.3.1. Connecting ๐Ÿš€

The client wants a connection to a server that has an RTMFP URI, for example, "rtmfp://server.example.com/app/instance". The client gathers one or more initial candidate addresses for the server named in the URI (for example, by using the Domain Name System (DNS) [RFC1035]). The client creates an EPD with an Ancillary Data option (Section 4.4.2.2) that encodes the URI. The client starts an RTMFP session to the one or more candidate addresses using the EPD ๐Ÿ’–

When the session transitions to the S_OPEN state, the client opens a new flow in that session for Stream ID 0 and Receive Intent 0, "original queuing order". This is the client's NetConnection main control flow. The client sends an RTMP "connect" command on the flow and waits for a response or an exception โœจ

5.3.2. Server-to-Client Return Control Flow โœจ

okay so the server accepts the client's NetConnection control flow, gets the "connect" command, and says yes ๐Ÿซถ Then it opens one or more return flows to the client on Stream ID 0, all tied back to that control flow. Flows on Stream ID 0 are the server's NetConnection control flows ๐ŸŽ€ The server also sends a "_result" or "_error" transaction response for the connect command.

When the client gets the first return flow from the server on Stream ID 0 that's tied to its own NetConnection control flow, it treats that flow as the canonical return NetConnection control flow from the server ๐Ÿ‘‘ Every new client-to-server flow gets associated with it, so it's the main character of the whole connection ๐Ÿ’…

When the client receives a "_result" transaction response on Stream ID 0 for its connect command, the connection is up ๐ŸŽ‰๐Ÿฆ‹

The client MAY open additional return control flows to the server on Stream ID 0, tied to the server's canonical NetConnection control flow. ๐ŸŒธ

5.3.3. setPeerInfo Command ๐Ÿงš

The "setPeerInfo" command is how the client tells the server about the candidate socket addresses it might be reachable on ๐Ÿ“ This list SHOULD include all directly connected interface addresses and proxy addresses, except as covered below. The list MAY be empty ๐Ÿ’œ It doesn't need to include the server's address, even if the server is going to act as an introducer for the client. The list SHOULD NOT include link-local or loopback addresses ๐Ÿ™ˆ

This command goes out as a regular RTMP NetConnection command, meaning an RTMP Type 20 Command Message or an RTMP Type 17 Command Extended Message on Stream ID 0. A Type 20 Command Message SHOULD be used if the object encoding negotiated during the "connect" and "_result" handshake is AMF0 [AMF0]. A Type 17 Command Extended Message SHOULD be used if the negotiated object encoding is AMF3 [AMF3] ๐Ÿ”ฅ

Note: A Type 20 Command Message payload is a sequence of AMF objects encoded in AMF0.
Note: A Type 17 Command Extended Message payload starts with a format selector byte, followed by a sequence of objects in a format-specific encoding. At the time of writing, only format 0 is defined, so the format selector byte MUST be 0. Format 0 is a sequence of AMF objects, each encoded in AMF0 by default. AMF3 encoding for an object can be picked by prefixing it with an "avmplus-object-marker" (0x11), as defined in [AMF0] โœจ

To finish the RTMFP NetConnection handshake, an RTMFP client MUST send a setPeerInfo command to the server after it gets a successful response to the "connect" command ๐Ÿซถ

(
    "setPeerInfo", // AMF String, command name
    0.0,  // AMF Number, transaction ID
    NULL, // AMF Null, no command object
    ...   // zero or more AMF Strings, each an address
)

Each listed socket address is an IPv4 or IPv6 address in presentation format, followed by a UDP port number in decimal, separated by a colon. IPv6 presentation format already uses colons, so IPv6 addresses get wrapped in square brackets [RFC3986] ๐ŸŒˆ

                    (
                        "setPeerInfo",
                        0.0,
                        NULL,
                        "192.0.2.129:50001",
                        "[2001:db8:1::2]:50002"
                    )

Figure 3: Example setPeerInfo Command

A server SHOULD assume the client is behind a Network Address Translator (NAT) if and only if the observed far endpoint address of the session for the flow where this command arrived does not show up in the setPeerInfo address list ๐Ÿ‘€

5.3.4. Set Keepalive Timers Command โฐ

The server can tell the client to set or change its session keepalive timer periods, both for its connection to the server and for its P2P connections ๐Ÿ’– The server MAY pick keepalive periods based on static configuration, application- or deployment-specific circumstances, whether the client looks like it's behind a NAT, or any other reason ๐Ÿฆ‹

The server sends the Set Keepalive Timers command to the client on Stream ID 0 as a User Control event message (Type 4) with Event Type 41. Message timestamps get ignored and MAY be set to 0 ๐Ÿ“

 0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|       4       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           timestamp                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         eventType = 41        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    serverKeepalivePeriodMsec                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                     peerKeepalivePeriodMsec                   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
struct setKeepaliveUserControlMessagePayload_t
{
    uint16_t eventType = 41;
    uint32_t serverKeepalivePeriodMsec;
    uint32_t peerKeepalivePeriodMsec;
} :10*8;
  • eventType: The RTMP User Control Message Event Type. Set Keepalive Timers messages have type 41 (0x29).
  • serverKeepalivePeriodMsec: The keepalive period, in milliseconds, that the client is advised to set on its RTMFP session with the server.
  • peerKeepalivePeriodMsec: The keepalive period, in milliseconds, that the client is advised to use on its RTMFP sessions with any peer that is not the server.

The client MUST define minimum values for these keepalive periods, and it won't set anything below them, no matter what this message says ๐Ÿ™…โ€โ™€๏ธ The minimum keepalive timer periods SHOULD be at least five seconds. The client MAY define maximum values for these keepalive periods too, above which it won't set them ๐Ÿ’…

When the client gets this message from the server, it SHOULD set its RTMFP server and peer keepalive timer periods to the indicated values, subject to the client's own minimum and maximum values โœจ The server MAY send this message more than once, especially if the conditions it uses to pick the timer periods change ๐Ÿ”„

5.3.5. Additional Flows for Streams ๐ŸŒŠ

The client or server opens extra flows to the other side to carry messages for any stream. Those additional flows are tied to the canonical NetConnection control flow from the other side ๐ŸŽ€

     Client                                            Server
     ------>--C2S-Control-Flow------------------------->--+
                                                          |
        +--<------------------------S2C-Control-Flow---<--+
        |                                                 |
        |  <------------------------S2C-Stream-Flow-1--<--+
        |                                  :              |
        |  <------------------------S2C-Stream-Flow-M--<--+
        |
        +-->--C2S-Stream-Flow-1------------------------>
        |               :
        +-->--C2S-Stream-Flow-N------------------------>

Figure 4: Schematic Flow Association Tree for a NetConnection

5.3.5.1. To Server

Extra flows from the client to the server for stream messages are opened with the Stream ID for that stream, and they're tied back to the server's canonical NetConnection control flow ๐Ÿ“ค

The client MAY create as many flows as it wants for any Stream ID (including Stream ID 0) at any time. ๐Ÿ™Œ

5.3.5.2. From Server

Extra flows from the server to the client for stream messages are opened with the Stream ID for that stream, and they're tied back to the client's NetConnection control flow ๐Ÿ“ฅ

The server MAY create as many flows as it wants for any Stream ID (including Stream ID 0) at any time. ๐Ÿ’ซ

5.3.5.3. Closing Stream Flows ๐Ÿšช

Either end MAY close a sending flow that is not for Stream ID 0 at any time, and that has no semantic meaning for the stream ๐Ÿ‘‹

At any time, either end MAY reject a receiving flow that is not one of the other end's NetConnection control flows. This profile doesn't define any flow exception codes, so the receiving end SHOULD use exception code 0 when it rejects the flow. The sending end, once it hears about an exception for a stream flow, SHOULD NOT open a new flow to take the rejected flow's place for carrying that stream's messages ๐Ÿ™…โ€โ™€๏ธ If an end rejects any flow for a stream, it SHOULD reject all the flows for that stream. Otherwise, Flow Synchronization messages (Section 5.2) that were in flight could get thrown away, and some flows might become or stay stuck in a suspended state ๐Ÿ˜ญ

5.3.6. Closing the Connection ๐Ÿ‘‹

The client or server can signal an orderly close of the connection by closing its NetConnection control sending flows and all of its stream sending flows ๐ŸŒธ When the other end gets a close/complete notification for the canonical NetConnection control receiving flow, it closes its own sending flows. Once both ends see that all receiving flows have closed and completed, the connection has cleanly terminated โœจ

Either end can abruptly terminate the connection by rejecting the NetConnection control receiving flows or by closing the underlying RTMFP session ๐Ÿ’€ When the end that sees an exception on a NetConnection control sending flow gets that notification, it knows the other end has terminated abruptly, and it can immediately close all sending and receiving flows for that connection ๐Ÿ˜ฑ

5.3.7. Example ๐ŸŽฌ

okay so here's the whole thing in one picture, bestie ๐Ÿ“ธ the client and server do the RTMFP handshake first (IHello, RHello, IIKeying, RIKeying) and then the RTMP connect handshake, and then the streaming starts, all the way down to the audio and video data ๐Ÿ”ฅ

                 Client                    Server
                   |IHello (EPD:anc=URI)     |
               -+- |------------------------>|
                |  |                         |
                |  |       RHello (RCert:anc)|
          RTMFP |  |<------------------------|
         Session|  |                         |
          Hand- |  |IIKeying                 |
          shake |  |------------------------>|
                |  |                         |
                |  |                 RIKeying|
               -+- |<------------------------|
                   |                         |
               -+- |"connect" command        |
         (Str.ID=0)|-CFlow-0---------------->|
                |  |                         |
                |  |       "_result" response|
          RTMP  |  |<----------------SFlow-0-|(Str.ID=0,
         Connect|  |                         | Assoc=CFlow-0)
          Hand- |  |"setPeerInfo" command    |
          shake |  |-CFlow-0---------------->|
               -+- |                         |
                   |"createStream" command   |
               -+- |-CFlow-0---------------->|
                |  |                         |
                |  |     "_result" (str.ID=5)|
                |  |<----------------SFlow-0-|
                |  |                         |
                |  |"play" command           |
         (Str.ID=5,|-CFlow-1---------------->|
     Assoc=SFlow-0)|                         |
                |  | StreamBegin User Control|
                |  |<----------------SFlow-1-|(Str.ID=5,
                |  |                         | Assoc=CFlow-0)
                |  |  (RTMP stream events)   |
     Streaming  |  |<----------------SFlow-1-|
                |  |                         |
                |  |        Audio Data       |
                |  |<----------------SFlow-2-|(Str.ID=5,
                |  |                         | Assoc=CFlow-0)
                |  |        Video Data       |
                |  |<----------------SFlow-3-|(Str.ID=5,
                |  |            :            | Assoc=CFlow-0)
                   |            :            |

Figure 5: Example NetConnection Message Exchange ๐Ÿ’–

5.4. Direct Peer-to-Peer Streams ๐Ÿฆ‹

okay so clients can connect straight to other clients for P2P streaming and data exchange ๐Ÿซถ a client MAY have multiple separate P2P NetStreams with a peer inside one RTMFP session, and each one is its own logical connection โœจ P2P NetStreams only go one way. the subscriber (the side sending the "play" command) starts things off, and the publisher is the other side. the subscriber gets a control flow to the publisher, and the publisher can have zero or more return flows back to the subscriber, tied to that control flow, carrying the stream media and data ๐Ÿ’…

5.4.1. Connecting ๐Ÿ“ž

so a client wants to subscribe directly to a stream that a publishing peer is putting out in P2P mode ๐ŸŽง the client finds out the publisher's peer ID and the stream name through application-specific means, which is just how the app does it ๐Ÿคทโ€โ™€๏ธ

if the client doesn't already have an RTMFP session with that peer ID, it starts a brand new one ๐ŸŒธ it makes an EPD with a Fingerprint option (Section 4.4.2.3) for the publisher's peer ID, and it uses the server session's DESTADDR as the first candidate address for the session to the peer. the server acts as an Introducer (Section 3.5.1.6 of RFC 7016), sending forward and redirect messages to help the client and the peer set up their session ๐Ÿงš

once an S_OPEN session to the peer you want exists, the client makes a new independent flow to that peer ๐ŸŽ€ the flow MUST have a non-zero Stream ID. the client sends an RTMP "play" command over that flow, naming the stream it wants at the publisher. this flow is the subscriber's control flow, and it's giving main character energy ๐Ÿ‘‘

5.4.2. Return Flows for Stream ๐ŸŒˆ

the publisher gets a new flow that doesn't point to a return association with any of its sending flows and has a non-zero Stream ID. it takes that flow and processes the "play" command ๐Ÿ“ฌ if the request is acceptable, the publisher opens one or more return flows back to the subscribing peer, tied to the subscriber's control flow and using the same Stream ID ๐Ÿ“ then it sends a StreamBegin User Control message, the appropriate RTMP status events, and the stream media over those return flows โญ

the subscriber looks at the return association on the media flows, which links them back to its control flow, and that's how it figures out which stream the media belongs to ๐Ÿ”

the publisher MAY open any number of media flows for the stream and close them whenever it wants ๐Ÿ™ˆ opening and closing media flows doesn't have any special meaning for the stream itself. the only thing that counts is that at least one flow has opened and at least one media message or a StreamBegin User Control message has come through, which means the publisher is publishing the requested stream to the subscriber ๐Ÿ”ฅ

         Subscriber                                     Publisher
         ------>--Subscriber-Control-Flow------------------>--+
                                                              |
               <------------------Publisher-Stream-Flow-1--<--+
                                              :               |
               <------------------Publisher-Stream-Flow-N--<--+

Figure 6: Schematic Flow Association Tree for a P2P Direct Connection ๐Ÿงฉ

5.4.3. Closing the Connection ๐Ÿ‘‹

either end can close the stream by closing or rejecting the subscriber's control flow ๐Ÿ’€ the publisher SHOULD close and unpublish to the subscriber once it gets a close/complete on the control flow. the subscriber SHOULD consider the stream closed when it gets notified of any exception on the control flow ๐Ÿ˜ญ


6. IANA Considerations ๐ŸŒท

this memo specifies option type code values for Certificate fields (Section 4.3.3), Endpoint Discriminator fields (Section 4.4.2), and Session Keying Component fields (Section 4.5.2) ๐ŸŽ€ it also specifies a flow metadata signature (Section 5.1.1). the type code values and signatures for this profile are assigned and maintained by Adobe, so IANA doesn't have to do anything for them โœจ

6.1. RTMFP URI Scheme Registration ๐Ÿ”—

this memo describes the use of an RTMFP URI scheme (Section 4.4.2.2, Section 5.3.1, Figure 5) ๐Ÿ’… and the "rtmfp" URI scheme has been registered by IANA ๐ŸŽ‰

the syntax and semantics of this URI scheme are described using the Augmented Backus-Naur Form (ABNF) [RFC5234] rules from RFC 3986 ๐Ÿ“

URI scheme name: rtmfp

Status: provisional

URI scheme syntax:

   rtmfp-uri-scheme = "rtmfp:"
                    / "rtmfp://" host [ ":" port ] path-abempty

URI scheme semantics: the first form is used in the APIs of some implementations to start an RTMFP client according to this memo, but without connecting to a server ๐Ÿงš that kind of setup might be used for pure peer-to-peer communication.

the second form gives the location info for the server to connect to, plus optional extra info to pass along to the server ๐Ÿซถ the only operation for this form is to connect to a server (the initial candidate addresses are named by host and port) according to Section 5.3. the UDP port for those initial candidate addresses is 1935 if you don't specify one. if the host is a reg-name, the initial candidate address set SHOULD comprise all IPv4 and IPv6 addresses that reg-name resolves to. what path-abempty means is up to the server, and connections are made using RTMFP as this memo specifies ๐ŸŒˆ

Encoding considerations: the path-abempty component is textual data made of characters from the Universal Character Set ๐Ÿ’œ this component SHOULD be encoded according to Section 2.5 of RFC 3986.

Applications/protocols that use this URI scheme name: the Flash runtime (including Flash Player) from Adobe Systems Incorporated, communication servers such as Adobe Media Server, and interoperable clients and servers from other parties, all using RTMFP according to this memo ๐Ÿฆ‹

Interoperability considerations: this scheme requires RTMFP as defined by RFC 7016, used in the way this memo describes โญ

Security considerations: see Security Considerations (Section 7) in this memo ๐Ÿ™ˆ seriously, go read it, it's not optional vibes ๐Ÿ’€

Contact: Michael Thornburgh, Adobe Systems Incorporated, <mthornbu@adobe.com>

Author/Change controller: Michael Thornburgh, Adobe Systems Incorporated, <mthornbu@adobe.com>

References:

Thornburgh, M., "Adobe's Secure Real-Time Media Flow Protocol", RFC 7016, November 2013.

This memo.

7. Security Considerations ๐Ÿ”

Okay bestie, this is the spicy part ๐Ÿ’… Section 4 is where all the crypto lives, so go check it out if you want the full tea ๐Ÿต

This profile does not define or use a Public Key Infrastructure (PKI) ๐Ÿ™…โ€โ™€๏ธ. Clients SHOULD use static Diffie-Hellman keys in their certificates (Section 4.3.3.5). Clients MUST create a new certificate with a distinct fingerprint for each new NetConnection (Section 5.3). These constraints make client identities ephemeral but impossible to forge ๐Ÿฆ‹. A man-in-the-middle can't successfully interpose itself in a connection to a target client addressed by its fingerprint/peer ID if that target client uses a static Diffie-Hellman public key ๐Ÿ”’.

Servers can have long-lived RTMFP instances, so they SHOULD use ephemeral Diffie-Hellman public keys for forward secrecy โœจ. This means server peer IDs can be forged, but clients don't connect to servers by peer ID, so it's irrelevant ๐Ÿ’€.

When a client connects to a server, the client will accept the response of any endpoint claiming to be "a server" ๐Ÿ‘€. It's assumed that an attacker who can passively observe traffic on a network segment can also inject their own packets with any source or destination and any payload ๐Ÿ˜ญ. An attacker can trick a client into connecting to a rogue server or man-in-the-middle, either by observing Initiator Hello packets from the client and responding earliest with a matching Responder Hello, or by using tricks like DNS spoofing or poisoning to direct a client straight to the rogue ๐Ÿ™ˆ. A TCP-based transport would be vulnerable to similar attacks ๐ŸŒˆ. Since there's no PKI, this profile gives no guarantee that the client has actually connected to the desired server, versus a rogue or man-in-the-middle ๐Ÿšจ.

When assurance is required that the connection is directly to the desired server, the client can use the Session Nonces (Section 4.6.5) to challenge the server ๐Ÿซถ. For example, it could do this over a different channel with acceptable security properties (such as an HTTPS) to transitively establish the server's identity and verify that the end-to-end communication is private and authentic ๐Ÿ”ฅ.

When session sequence numbers (Section 4.7.3.3) are not used, an attacker can use traffic analysis techniques to record encrypted packets containing the start of a new flow, then replay those packets after the flow has closed. To the receiver, that can look like a brand new flow ๐Ÿ‘ป. If that could be harmful, session sequence numbers SHOULD be used ๐Ÿ›ก๏ธ. Replaying packets for existing flows isn't harmful, since the receiver detects and discards duplicate flow sequence numbers, and flow sequence numbers don't wrap or otherwise repeat ๐Ÿ’œ.

Packet encryption uses CBC with the same (null) initialization vector for each packet ๐Ÿง. This can reveal to an observer whether two packets contain identical plaintext ๐Ÿ‘๏ธ. BUT, the maximum-length RTMFP common header and User Data or Data Acknowledgement header, including flow sequence number, always fit within the first 16-byte cipher block. So each initial cipher block for most packets will already be unique, even if timestamps are suppressed ๐ŸŒธ. Sending identical messages in a flow uses unique flow sequence numbers, so the cipher blocks are unique in that case too โœจ. Keepalive pings and retransmission of lost data can produce identical cipher blocks. However, traffic analysis can also reveal likely keepalives or retransmissions, and retransmission only happens as a result of observable network loss, so this is usually irrelevant ๐Ÿ™„. If any identical cipher block is unacceptable, session sequence numbers SHOULD be used, since they guarantee each initial cipher block will be unique ๐Ÿ”.

Packet verification can use a 16-bit simple checksum (Section 4.7.3.1) ๐Ÿงฎ. The checksum sits inside the encrypted packet, so for external packet modifications it's equivalent to a 16-bit cryptographic digest ๐Ÿค. If that's insufficient, HMAC verification (Section 4.7.3.2) SHOULD be used ๐Ÿ’–.


8. References ๐Ÿ“š

8.1. Normative References

[AES]      National Institute of Standards and Technology, "Advanced
           Encryption Standard (AES)", FIPS PUB 197, November 2001,
           <http://csrc.nist.gov/publications/fips/fips197/
           fips-197.pdf>.

[AMF0]     Adobe Systems Incorporated, "Action Message Format -- AMF
           0", December 2007, <http://www.adobe.com/go/spec_amf0>.

[AMF3]     Adobe Systems Incorporated, "Action Message Format -- AMF
           3", January 2013, <http://www.adobe.com/go/spec_amf3>.

[CBC]      Dworkin, M., "Recommendation for Block Cipher Modes of
           Operation", NIST Special Publication 800-38A, December
           2001, <http://csrc.nist.gov/publications/nistpubs/800-38a/
           sp800-38a.pdf>.

[DH]       Diffie, W. and M. Hellman, "New Directions in
           Cryptography", IEEE Transactions on Information Theory, V.
           IT-22, n. 6, June 1977.

[RFC2104]  Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-
           Hashing for Message Authentication", RFC 2104, February
           1997, <http://www.rfc-editor.org/info/rfc2104>.

[RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
           Requirement Levels", BCP 14, RFC 2119, March 1997,
           <http://www.rfc-editor.org/info/rfc2119>.

[RFC3526]  Kivinen, T. and M. Kojo, "More Modular Exponential (MODP)
           Diffie-Hellman groups for Internet Key Exchange (IKE)",
           RFC 3526, May 2003,
           <http://www.rfc-editor.org/info/rfc3526>.

[RFC3629]  Yergeau, F., "UTF-8, a transformation format of ISO
           10646", STD 63, RFC 3629, November 2003,
           <http://www.rfc-editor.org/info/rfc3629>.

[RFC3986]  Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
           Resource Identifier (URI): Generic Syntax", STD 66, RFC
           3986, January 2005,
           <http://www.rfc-editor.org/info/rfc3986>.

[RFC5234]  Crocker, D. and P. Overell, "Augmented BNF for Syntax
           Specifications: ABNF", STD 68, RFC 5234, January 2008,
           <http://www.rfc-editor.org/info/rfc5234>.

[RFC6234]  Eastlake, D. and T. Hansen, "US Secure Hash Algorithms
           (SHA and SHA-based HMAC and HKDF)", RFC 6234, May 2011,
           <http://www.rfc-editor.org/info/rfc6234>.

[RFC7016]  Thornburgh, M., "Adobe's Secure Real-Time Media Flow
           Protocol", RFC 7016, November 2013,
           <http://www.rfc-editor.org/info/rfc7016>.

[RFC7296]  Kaufman, C., Hoffman, P., Nir, Y., Eronen, P., and T.
           Kivinen, "Internet Key Exchange Protocol Version 2
           (IKEv2)", STD 79, RFC 7296, October 2014,
           <http://www.rfc-editor.org/info/rfc7296>.

[RTMP]     Adobe Systems Incorporated, "Real-Time Messaging Protocol
           (RTMP) specification", December 2012,
           <http://www.adobe.com/go/spec_rtmp>.

[SHA256]   National Institute of Standards and Technology, "Secure
           Hash Standard", FIPS PUB 180-4, March 2012,
           <http://csrc.nist.gov/publications/fips/fips180-4/
           fips-180-4.pdf>.

8.2. Informative References

[RFC1035]  Mockapetris, P., "Domain names - implementation and
           specification", STD 13, RFC 1035, November 1987,
           <http://www.rfc-editor.org/info/rfc1035>.

[RFC1071]  Braden, R., Borman, D., Partridge, C., and W. Plummer,
           "Computing the Internet checksum", RFC 1071, September
           1988, <http://www.rfc-editor.org/info/rfc1071>.

[RFC4302]  Kent, S., "IP Authentication Header", RFC 4302, December
           2005, <http://www.rfc-editor.org/info/rfc4302>.

[RFC4303]  Kent, S., "IP Encapsulating Security Payload (ESP)", RFC
           4303, December 2005,
           <http://www.rfc-editor.org/info/rfc4303>.

[RFC6479]  Zhang, X. and T. Tsou, "IPsec Anti-Replay Algorithm
           without Bit Shifting", RFC 6479, January 2012,
           <http://www.rfc-editor.org/info/rfc6479>.

Acknowledgements ๐Ÿ™Œ

Special thanks go to Glenn Eguchi, Matthew Kaufman, and Adam Lane for their contributions to the design of this profile ๐Ÿ‘‘.

Thanks to Philipp Hancke, Kevin Igoe, Paul Kyzivat, and Milos Trboljevac for their detailed reviews of this memo ๐ŸŽ€.

Author's Address ๐Ÿ–Š๏ธ

Michael C. Thornburgh
Adobe Systems Incorporated
345 Park Avenue
San Jose, CA  95110-2704
United States

Phone: +1 408 536 6000
EMail: mthornbu@adobe.com
URI:   http://www.adobe.com/
๐Ÿ“ฆ where this page comes from