RFC 7574

Peer-to-Peer Streaming Peer Protocol (PPSPP)

๐Ÿ“… published July 2015, from ietf

RFC 7574: Peer-to-Peer Streaming Peer Protocol (PPSPP) ๐ŸŽ€

okay bestie, this is the title block tea โ˜• ๐Ÿ’…

  • Publisher: the Internet Engineering Task Force (IETF) ๐ŸŒ
  • RFC number: 7574
  • Title: Peer-to-Peer Streaming Peer Protocol (PPSPP)
  • Authors: A. Bakker (Vrije Universiteit Amsterdam), R. Petrocco, and V. Grishchenko (Technische Universiteit Delft) ๐Ÿ‘ฏ
  • Category: Standards Track ๐Ÿ”ฅ
  • ISSN: 2070-1721
  • Date: July 2015 ๐Ÿ—“๏ธ

Abstract โœจ

so PPSPP is a protocol for getting the same content to a whole group of people who are interested in it, and it does this in a streaming way ๐ŸŒŠ it works for prerecorded on-demand stuff AND live audio/video, which is so iconic ๐Ÿ“บ

it's built on the peer-to-peer vibe, where the people watching are on equal footing with the servers that first had the content. everyone can potentially give upload bandwidth, so it's a whole community energy thing ๐Ÿซถ it's designed for short time-till-playback so you're not sitting there waiting forever ๐Ÿ˜ญ, and it's built to stop malicious peers from wrecking the streams ๐Ÿ›ก๏ธ

it's also super flexible and extensible ๐Ÿฆ‹ it can use different methods to optimize peer uploading, stop freeriding, and work with different peer discovery schemes, like centralized trackers or Distributed Hash Tables (DHTs) ๐Ÿงš. it supports multiple methods for content integrity protection and chunk addressing too, which is giving precision ๐Ÿ’œ

it's a generic protocol that can run on top of different transport protocols. right now it runs on UDP and uses Low Extra Delay Background Transport (LEDBAT) for congestion control ๐ŸŒธ honestly the whole thing is so well thought out, I'm obsessed ๐Ÿ˜ญ๐Ÿ”ฅ


Status of This Memo โญ

this one is a Standards Track document, so it's an official Internet Standards Track document ๐ŸŽ€

it's a product of the Internet Engineering Task Force (IETF), and it represents the consensus of the IETF community. it got public review and was approved for publication by the Internet Engineering Steering Group (IESG). if you want more info on Internet Standards, check Section 2 of RFC 5741 ๐Ÿ“

for the current status of this doc, any errata, and how to send feedback, go to http://www.rfc-editor.org/info/rfc7574 ๐ŸŒˆ


Copyright (c) 2015 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) as they stand on the date of publication. please read these carefully, bestie, because they explain your rights and restrictions for this document ๐Ÿ“œ

one important bit: Code Components pulled out of this document must include Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions. they come without warranty, as described in the Simplified BSD License ๐Ÿ’œ

1. Introduction โœจ

bestie ๐Ÿฅบ the paste stops right after the "1. Introduction" heading, so there's no actual Section 1 text for me to rewrite yet. I'm not going to invent spec wording, because implementers rely on every word and a made-up requirement would be a disaster ๐Ÿ˜ญ. Could you paste the body of Section 1 (starting at 1.1. Purpose) and any remaining text from the Introduction? Then I'll pick up exactly where we left off ๐Ÿ’–

1.1. Purpose ๐Ÿ’–

Okay bestie, let's talk about PPSPP, the Peer-to-Peer Streaming Peer Protocol ๐ŸŽ€ It's built for getting the same content to a whole group of interested people, streaming style. It handles both prerecorded (on-demand) and live audio/video โœจ The big idea is peer-to-peer: the people watching are on equal footing with the servers that started out holding the content, so everyone can potentially contribute upload bandwidth ๐Ÿซถ

PPSPP is designed for short time-till-playback so the end user isn't stuck waiting ๐Ÿ˜ญ It also protects streams from malicious peers, and the key to that is a simple way of identifying content through self-certification ๐Ÿ”ฅ Content in PPSPP is identified by one cryptographic hash, the root hash of a Merkle hash tree that gets calculated recursively from the content [MERKLE] [ABMRKL]. This self-certifying hash tree means every peer can directly detect when a malicious peer is trying to spread fake content ๐Ÿ•ต๏ธโ€โ™€๏ธ The tree works for both static and live content, and it means only a small amount of information is needed to start a download and to verify incoming chunks. That's how start-up stays fast ๐Ÿš€

PPSPP is also built to be extensible across different transports and use cases ๐Ÿฆ‹ It's a generic protocol that can run directly on top of UDP, TCP, or other protocols. It defines a common set of messages, and those messages can look different on the wire depending on which lower-level protocol is in use ๐ŸŒˆ When the lower-level transport allows it, PPSPP can also use different congestion control algorithms.

Right now PPSPP is set to run on top of UDP using LEDBAT for congestion control [RFC6817] โญ LEDBAT is what lets PPSPP keep serving the content after playback finishes (this is called seeding) without disrupting the user, who may have moved on to other tasks on the same network connection ๐Ÿ’…

PPSPP is also flexible about how it encourages clients to contribute and prevents freeriding, which means dealing with peers that only download and never upload to anyone ๐Ÿ‘€ If the defaults don't fit a particular use case, it also allows different schemes for chunk addressing and content integrity protection. It can work with different peer discovery schemes too, such as centralized trackers or fast Distributed Hash Tables [JIM11]. And in this default setup, PPSPP keeps only a small amount of state per peer ๐Ÿงš A reference implementation of PPSPP over UDP is available [SWIFTIMPL].

The protocol in this document assumes a peer has already discovered a list of (initial) peers, for example through a centralized tracker [PPSP-TP] ๐Ÿ“ Once a peer has that list, PPSPP lets it connect to other peers, request chunks of content, and discover other peers distributing the same content.

PPSPP's design comes from our research into making BitTorrent [BITTORRENT] suitable for streaming content [P2PWIKI] ๐Ÿ’œ Most PPSPP messages have BitTorrent counterparts and vice versa. But PPSPP is specifically targeted at streaming audio/video and optimizes time-till-playback, and it was designed to be more flexible and extensible too ๐ŸŽฌ


1.2. Requirements Language ๐Ÿ“œ

Here's the spicy bit about the key words. In this document, "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" are to be interpreted as described in RFC 2119 [RFC2119] ๐Ÿซถ These are the words that set exactly how strict a rule is, so read them carefully ๐Ÿ™ˆ


1.3. Terminology ๐Ÿ“–

Okay, time for the vocab list ๐Ÿ’… These are the terms the rest of the spec leans on, so let's get them down:

  • message: The basic unit of PPSPP communication. A message looks different on the wire depending on the transport protocol used. Messages are typically multiplexed into a datagram for transmission.
  • datagram: A sequence of messages offered as one unit to the underlying transport protocol (UDP, etc.). The datagram is PPSPP's Protocol Data Unit (PDU).
  • content: Either a live transmission or a prerecorded multimedia file.
  • chunk: The basic unit the content is divided into. For example, a block of N kilobytes. A chunk may be of variable size.
  • chunk ID: A unique identifier for a chunk of content (e.g., an integer). Its type depends on the chunk addressing scheme used.
  • chunk specification: An expression that denotes one or more chunk IDs.
  • chunk addressing scheme: A scheme for identifying chunks and expressing a peer's chunk availability map in a compact way.
  • chunk availability map: The set of chunks a peer has successfully downloaded and checked the integrity of.
  • bin: A number denoting a specific binary interval of the content (i.e., one or more consecutive chunks) in the bin numbers chunk addressing scheme (see Section 4).
  • content integrity protection scheme: A scheme for protecting the integrity of the content while it's being distributed over the peer-to-peer network. It gives receiving peers ways to detect whether a requested chunk has been modified, either maliciously by the sending peer or accidentally in transit.
  • hash: The result of applying a cryptographic hash function, more specifically a Modification Detection Code (MDC) [HAC01], such as SHA-256 [FIPS180-4], to a piece of data.
  • Merkle hash tree: A tree of hashes whose base is formed by the hashes of the chunks of content. Its higher nodes are calculated by recursively computing the hash of the concatenation of the two child hashes (see Section 5.1).
  • root hash: The root in a Merkle hash tree calculated recursively from the content (see Section 5.1).
  • munro hash: The hash of a subtree that is the unit of signing in the Unified Merkle Tree content authentication scheme for live streaming (see Section 6.1.2.1).
  • swarm: A group of peers participating in the distribution of the same content.
  • swarm ID: A unique identifier for a swarm of peers. In PPSPP it's a sequence of bytes. For video on demand with content integrity protection enabled, the identifier is the so-called root hash of a Merkle hash tree over the content. For live streaming, the swarm ID is a public key.
  • tracker: An entity that records the addresses of peers participating in a swarm (usually for a set of swarms) and makes this membership information available to other peers on request.
  • choking: When Peer A is choking Peer B, it means A is currently not willing to accept requests for content from B.
  • seeding: Peer A is said to be seeding when A has downloaded a static content file completely and is now offering it for others to download.
  • leeching: Peer A is said to be leeching when A has not completely downloaded a static content file yet, or is not offering to upload it to others.
  • channel: A logical connection between two peers. The channel concept lets peers use the same transport address for communicating with different peers.
  • channel ID: A unique, randomly chosen identifier for a channel, local to each peer. So the two peers logically connected by a channel each have a different channel ID for that channel.
  • heavy payload: A datagram has a heavy payload when it contains DATA messages, SIGNED_INTEGRITY messages, or a large number of smaller messages.
Quick aside: in this document, the prefixes kilo-, mega-, etc., denote base 1024 ๐Ÿ“

2. Overall Operation ๐ŸŒธ

The basic unit of communication in PPSPP is the message โœจ Multiple messages get multiplexed into a single datagram for transmission. A datagram (and therefore the messages inside it) will look different on the wire depending on the transport protocol used (see Section 8) ๐Ÿฆ‹

The overall operation of PPSPP is shown in the examples below. They assume the content is static, UDP is used for transport, the Merkle Hash Tree scheme is used for content integrity protection, and a specific policy is used for choosing which chunks to download ๐Ÿง 

2.1. Example: Joining a Swarm ๐ŸŽ€

Picture this: a user wants to watch a video ๐Ÿฟ To play it, they click the play button on a HTML5 <video> element in their PPSPP-enabled browser. Imagine this element has a PPSPP URL (to be defined elsewhere) that identifies the video as its source. The browser passes this URL to its peer-to-peer streaming protocol handler. Let's call that handler Peer A ๐Ÿ’– Peer A parses the URL to get the transport address of a peer-to-peer streaming protocol tracker, plus the swarm metadata for the content. The tracker address may be optional when a decentralized tracking mechanism is in place. How peers get tracked is outside the scope of this document ๐Ÿ™ˆ

Next, Peer A registers with the tracker following the peer-to-peer streaming protocol tracker specification [PPSP-TP] and receives the IP address and port of peers already in the swarm. Let's say Peers B, C, and D ๐Ÿซถ Now PPSPP starts operating. Peer A sends a datagram containing a PPSPP HANDSHAKE message to Peers B, C, and D. This message conveys protocol options. Specifically, Peer A includes the ID of the swarm (part of the swarm metadata) as a protocol option, because the destination peers can listen for multiple swarms on the same transport address ๐Ÿ”ฅ

Peers B and C reply with datagrams containing a PPSPP HANDSHAKE message and one or more HAVE messages. A HAVE message conveys (part of) a peer's chunk availability, so it contains a chunk specification that denotes which chunks of the content Peers B and C have, respectively ๐Ÿ“ฆ Peer D sends a datagram with a HANDSHAKE and HAVE messages, but also a CHOKE message. That CHOKE tells Peer A that Peer D isn't willing to upload chunks to it right now ๐Ÿฅบ

2.2. Example: Exchanging Chunks ๐Ÿฆ‹

okay so picture this ๐Ÿ’… Peer A sends fresh datagrams to Peers B and C, and each one has REQUEST messages in it. A REQUEST tells the other peer which chunks you want to download, so it carries a chunk specification. The REQUESTs to B and C point at disjoint sets of chunks, so nobody's asking for the same stuff twice ๐Ÿ™ˆ

Peers B and C answer with datagrams holding HAVE, DATA, and, in this example, INTEGRITY messages. In the Merkle hash tree content protection scheme (see Section 5.1), the INTEGRITY messages contain all the cryptographic hashes Peer A needs to verify the integrity of the content chunk sent in the DATA message. ๐Ÿ” With those hashes, Peer A checks that the chunks from B and C are correct against the trusted swarm ID. It's giving security check โœ… Peer A also updates its picture of which chunks B and C have, using the info in their HAVE messages. And then it hands the video chunks over to the user's browser for rendering ๐ŸŽฌ

After all that processing, Peer A sends a datagram with HAVE messages for the chunks it just got to all its peers ๐Ÿ“ฃ The datagram going to B and C also has an ACK message that acknowledges the chunks received, plus new REQUESTs for more chunks. ACK messages are not used when a reliable transport protocol is used. Now say Peer C notices that Peer A got a chunk from Peer B that Peer C didn't have yet. Peer C's next datagram includes a REQUEST for that chunk. Main character energy for sure ๐Ÿ‘‘

Peer D also sends HAVE messages to Peer A whenever it downloads chunks from other peers ๐ŸŒธ When Peer D is willing to accept REQUESTs from Peer A, Peer D sends a datagram with an UNCHOKE message to tell Peer A. If Peer B or C decides to choke Peer A, they send a CHOKE message, and then Peer A should re-request from other peers ๐Ÿ”„ Peers B and C may keep sending HAVE, REQUEST, or periodic keep-alive messages, which means Peer A keeps getting HAVE messages from them. Lowkey so wholesome ๐Ÿซถ

Once Peer A has received all the content (the video-on-demand use case), it stops sending messages to all the other peers that have all the content (a.k.a. seeders) ๐Ÿ Peer A can also contact the tracker or another source again to get more peer addresses ๐Ÿงš

2.3. Example: Leaving a Swarm ๐Ÿ‘‹

To leave a swarm gracefully, Peer A sends a specific HANDSHAKE message to all its peers (see Section 8.4) and deregisters from the tracker following the tracker specification [PPSP-TP]. โœจ Peers that get the datagram should remove Peer A from their current peer list. Now if Peer A crashes ungracefully, peers should remove Peer A from their peer list once they detect it no longer sends messages (see Section 3.12). Ghosting is not it, bestie ๐Ÿ’€

3. Messages ๐Ÿ’Œ

No error codes or responses are used in the protocol. Absence of any response indicates an error. Invalid messages are discarded, and further communication with the peer SHOULD be stopped. ๐Ÿ›‘ The rationale is that it's enough to sort peers into good or bad and then only use the good ones ๐Ÿ’– A good peer is one that responds with chunks. A peer that does not respond, or does not respond in time, gets classified as bad. The idea is that, in PPSPP, the content is available from multiple sources (unlike HTTP), so a peer should not invest too much effort trying to get it from one particular source ๐Ÿ“ This good/bad split lets a peer deal with slow, crashed, and (silent) malicious peers. Iconic filtering ๐Ÿ’…

Multiple messages MUST be multiplexed into a single datagram for transmission. Messages in a single datagram MUST be processed in the strict order in which they appear in the datagram. If an invalid message is found in a datagram, the remaining messages MUST be discarded. โš ๏ธ

For the sake of simplicity, one swarm of peers deals with one content file or stream only ๐ŸŽž๏ธ There is a single division of the content into chunks that all peers in the swarm adhere to, determined by the content publisher. Distributing a collection of files can be done either by using multiple swarms or by using an external storage mapping from the linear byte space of a single swarm to different files, which is transparent to the protocol. In other words, the audio/video container format used is outside the scope of this document ๐Ÿ“ฆ

3.1. HANDSHAKE ๐Ÿค

For Peer P to establish communication with Peer Q in Swarm S, the peers must first exchange HANDSHAKE messages through a handshake procedure. The initiating Peer P needs to know the metadata of Swarm S, which consists of:

  • (a) the swarm ID of the content (see Sections 5.1 and 6),
  • (b) the chunk size used,
  • (c) the chunk addressing method used,
  • (d) the content integrity protection method used, and
  • (e) the Merkle hash tree function used (if applicable).
  • (f) If automatic content size detection (see Section 5.6) is not used, the content length is also part of the metadata (for static content.)

This document assumes the swarm metadata is obtained from a trusted source ๐Ÿ”’ Peer P also needs to know a transport address for Peer Q, which it gets from a peer discovery/tracking protocol.

The payload of the HANDSHAKE message contains a sequence of protocol options. The protocol options encode the swarm metadata just described, so the peers can do an end-to-end check that they're in the right swarm ๐Ÿ•ต๏ธโ€โ™€๏ธ The options also encode a number of per-peer configuration parameters. The complete set of protocol options is specified in Section 7. The HANDSHAKE message also contains a channel ID for multiplexing communication and security (see Sections 3.11 and 12.1). A HANDSHAKE message MUST always be the first message in a datagram. ๐Ÿฅ‡

3.1.1. Handshake Procedure ๐ŸŽ€

The handshake procedure for a peer, Peer P, to start communication with another peer, Peer Q, in Swarm S goes like this:

  1. The first datagram the initiating Peer P sends to Peer Q MUST start with a HANDSHAKE message. This HANDSHAKE message MUST contain:
    • A channel ID, chanP, randomly chosen as specified in Section 12.1.
    • The metadata of Swarm S, encoded as protocol options, as specified in Section 7. In particular, the initiating Peer P MUST include the swarm ID.
    • The capabilities of Peer P, in particular, its supported protocol versions, "Live Discard Window" (in case of a live swarm), and "Supported Messages", encoded as protocol options.

This first datagram MUST be prefixed with the (destination) channel ID 0; see Section 3.11. So the datagram ends up with two channel IDs: the destination channel ID prefixed to the datagram, and the channel ID chanP included in the HANDSHAKE message inside the datagram ๐Ÿ‘ฏ This datagram MAY also contain some minor additional payload, e.g., HAVE messages to show Peer P's current progress, but it MUST NOT include any heavy payload (defined in Section 1.3), such as a DATA message. Allowing minor payload cuts down the number of initialization round trips, which improves time-till-playback โฉ Forbidding heavy payload prevents an amplification attack (see Section 12.1). Smart and safe ๐Ÿ›ก๏ธ

  1. The receiving Peer Q checks the HANDSHAKE message from Peer P. If any check by Peer Q fails, or if Peers P and Q are not in the same swarm, Peer Q MUST NOT send a HANDSHAKE (or any other) message back, because the message from Peer P may have been spoofed (see Section 12.1) ๐Ÿ™…โ€โ™€๏ธ Otherwise, if Peer Q is interested in communicating with Peer P, Peer Q MUST send a datagram to Peer P that starts with a HANDSHAKE message. This reply HANDSHAKE MUST contain:
    • A channel ID, chanQ, randomly chosen as specified in Section 12.1.
    • The metadata of Swarm S, encoded as protocol options, as specified in Section 7. In particular, the responding Peer Q MAY include the swarm ID.
    • The capabilities of Peer Q, in particular, its supported protocol versions, its "Live Discard Window" (in case of a live swarm), and "Supported Messages", encoded as protocol options.

This reply datagram MUST be prefixed with the channel ID chanP that Peer P sent in its first HANDSHAKE message (see Section 3.11). This reply datagram MAY also contain some minor additional payload, e.g., HAVE messages to show Peer Q's current progress, or REQUEST messages (see Section 3.7), but it MUST NOT include any heavy payload. ๐Ÿ“จ

  1. The initiating Peer P checks the reply datagram from Peer Q. If the reply datagram is not prefixed with (destination) channel ID chanP, Peer P MUST discard the datagram. Peer P SHOULD continue to process datagrams from Peer Q that do meet this requirement. This check prevents interference by spoofing (see Section 12.1) ๐Ÿฅท If Peer P's channel ID is echoed back correctly, the initiator Peer P knows the addressed Peer Q really responds. Receipt confirmed ๐ŸŽ‰
  2. Next, Peer P checks the HANDSHAKE message in the datagram from Peer Q. If any check by Peer P fails, or Peer P is no longer interested in communicating with Peer Q, Peer P MAY send a HANDSHAKE message to tell Peer Q it will cease communication. This closing HANDSHAKE message MUST contain an all zeros channel ID and a list of protocol options. The list MUST either be empty or contain the maximum version number Peer P supports, following the min/max versioning scheme defined in [RFC6709], Section 4.1 ๐Ÿ“š

The datagram containing this closing HANDSHAKE message MUST be prefixed with the (destination) channel ID chanQ. Peer P MAY also simply cease communication. Ghost mode, activated ๐Ÿ‘ป

  1. If the addressed peer, Peer Q, does not respond to initiating Peer P's first datagram, Peer P MAY resend that datagram until Peer Q is considered dead, according to the rules in Section 3.12 ๐Ÿ”
  2. If the reply datagram by Peer Q does pass the checks by Peer P, and Peer P wants to keep interacting with Peer Q, Peer P can now send REQUEST, PEX_REQ, and other messages to Peer Q. Datagrams carrying these messages MUST be prefixed with the channel ID chanQ sent by Peer Q. More specifically, because Peer P knows that Peer Q really responds, Peer P MAY start sending Peer Q messages with heavy payload. That means Peer P MAY start responding to any REQUEST messages Peer Q may have sent in this first reply datagram with DATA messages. Hence, chunk transfer can start soon in PPSPP ๐Ÿš€
  3. If Peer Q receives any datagram (apparently) from Peer P that does not contain channel ID chanQ, Peer Q MUST discard the datagram, but SHOULD continue to process datagrams from Peer P that do meet this requirement. Once Peer Q receives a datagram from Peer P that does contain channel ID chanQ, Peer Q knows that Peer P really received its reply datagram, and the three-way handshake and channel establishment is complete โœ… Peer Q MAY now also start sending messages with heavy payload to Peer P. Full handshake energy ๐Ÿ’ž
  4. If Peer P decides it no longer wants to communicate with Peer Q, or vice versa, the peer SHOULD send a closing HANDSHAKE message to the other, as described above. Bye bye, bestie ๐Ÿซถ

3.2. HAVE ๐Ÿ’–

okay so the HAVE message is how a peer tells everyone which chunks it has ready to download ๐Ÿฆ‹. The chunks can be described using different chunk addressing and availability map compression schemes, which are covered in Section 4 โœจ. A HAVE message can give a full overview of a peer's chunk availability, or it can send updates to that set ๐Ÿซถ.

Here's the big rule. Whenever a receiving Peer P has successfully checked the integrity of a chunk, or an interval of chunks, it MUST send a HAVE message to all peers Q1..Qn it wants to let download those chunks ๐Ÿ”ฅ. Peer P's policy decides when the HAVE goes out. Peer P can send it right away, or it can wait until it has other data to send to Peer Qi, or until it has received and checked multiple chunks โญ. How urgent it is to spread the news depends on the chunk picking policy (see Section 9.1), so that's what sets the timing ๐Ÿ’…. HAVE messages can also hitch a ride on other messages (piggybacked). Peers that never get a HAVE basically can't download the new chunks, so the HAVE message can act as a method of choking ๐Ÿ™ˆ.

The HAVE message MUST contain the chunk specification of the received and verified chunks ๐Ÿ’œ. A receiving peer MUST NOT send a HAVE message to peers whose handshake procedure is still incomplete (see Section 12.1) ๐Ÿšซ. A peer SHOULD NOT send a HAVE message to peers that already have the complete content, like in video-on-demand scenarios ๐Ÿ“.


3.3. DATA ๐ŸŽ€

The DATA message is how chunks of content actually get transferred ๐Ÿ“ฆ. It MUST contain the chunk ID of the chunk and the chunk itself. A peer MAY send DATA messages for multiple chunks in the same datagram ๐ŸŒˆ. The DATA message MAY also contain extra info if the specific congestion control mechanism needs it. Right now PPSPP uses LEDBAT [RFC6817] for congestion control, and LEDBAT needs the current system time sent along with the DATA message, so the current system time MUST be included โฐโœจ.


3.4. ACK ๐Ÿซง

ACK messages MUST be sent to acknowledge received chunks if PPSPP is run over an unreliable transport protocol ๐Ÿšจ. If a reliable transport protocol is used, ACK messages MAY be sent ๐Ÿ’–. In the unreliable case, a receiving peer that has successfully checked the integrity of a chunk, or an interval of chunks C, MUST send an ACK message containing a chunk specification for C ๐Ÿงš. Since LEDBAT is in play, an ACK message MUST contain the one-way delay, computed from the peer's current system time that came in the DATA message โฑ๏ธ. A peer MAY delay sending ACK messages as defined in the LEDBAT specification [RFC6817] ๐ŸŒธ.


3.5. INTEGRITY ๐Ÿ”

The INTEGRITY message carries whatever info the receiver needs to verify a chunk's integrity ๐Ÿ•ต๏ธโ€โ™€๏ธ. Its payload depends on the content integrity protection scheme being used. When the Merkle Hash Tree scheme is used, an INTEGRITY message MUST contain a cryptographic hash of a subtree of the Merkle hash tree, plus the chunk specification that identifies that subtree ๐Ÿ’….

Here's a typical example. When a peer wants to send a chunk and Merkle hash trees are in use, it builds a datagram with several INTEGRITY messages holding the hashes the receiver needs to verify the chunk, and then the actual chunk, encoded in a DATA message ๐ŸŽ. What the necessary hashes are, and the exact rules for encoding them into datagrams, are specified in Sections 5.3, and 5.4, respectively ๐Ÿงฉ.


3.6. SIGNED_INTEGRITY โœ๏ธ

The SIGNED_INTEGRITY message carries digitally signed info the receiver needs to verify a chunk in live streaming ๐Ÿ“ก. Logically, it contains a chunk specification, a timestamp, and a digital signature ๐Ÿ’œ. Its exact payload depends on the live content integrity protection scheme in use, which is covered in Section 6.1 ๐Ÿ‘‘.


3.7. REQUEST ๐Ÿ™‹โ€โ™€๏ธ

Bulk download protocols usually make explicit requests for certain ranges of data. That's the pull model, and BitTorrent [BITTORRENT] is a classic example ๐Ÿ“ฅ. Live streaming protocols often go with a push model instead, with no requests, to save round trips โšก. PPSPP supports both models, so it's giving versatility ๐ŸŒˆ.

The REQUEST message is how a peer asks another peer for one or more chunks. A REQUEST message MUST contain the specification of the chunks the requester wants to download ๐Ÿซถ. A peer that gets a REQUEST message MAY send out the requested chunks (using DATA messages) ๐Ÿ“จ. When Peer Q gets multiple REQUESTs from the same Peer P, Peer Q SHOULD process them in the order they were received ๐Ÿ”ข. Multiple REQUEST messages MAY be sent in one datagram, for example when a peer wants to request several rare chunks at once ๐Ÿ’Ž.

With a live push model, a peer that gets REQUESTs also MAY send some other chunks in case it runs out of requests or for some other reason ๐ŸŒŠ. In that case, the only job of REQUEST messages is to give hints and help peers coordinate, so nobody retransmits data for no reason ๐Ÿง .


3.8. CANCEL ๐Ÿ›‘

When downloading on-demand or live streaming content, a peer can ask multiple peers for urgent data to boost the odds it arrives on time ๐Ÿƒโ€โ™€๏ธ๐Ÿ’จ. Specifically, if the chunk picking algorithm (see Section 9.1) notices that a request for urgent data might not get served on time, the same request can be sent to a different peer ๐Ÿ”„. When a Peer P decides to request urgent data from a Peer Q, Peer P SHOULD send a CANCEL message to all the peers the data was previously requested from ๐Ÿซฃ. The CANCEL message has the specification of the chunks Peer P no longer wants ๐Ÿšฎ.

There's also an implicit cancel. When Peer Q receives a HAVE message for that urgent data from Peer P, Peer Q MUST also cancel the previous REQUEST(s) from Peer P ๐Ÿ’…. In other words, the HAVE message acts as an implicit CANCEL ๐Ÿช„.


3.9. CHOKE and UNCHOKE ๐Ÿšง

Peer A can send a CHOKE message to Peer B to say it won't be responding to REQUEST messages from Peer B anymore, for example because Peer A's upload capacity is maxed out ๐Ÿ˜ฎโ€๐Ÿ’จ. Peer A MAY later send an UNCHOKE message to say it'll answer new REQUESTs from Peer B again (Peer A SHOULD discard old requests) ๐ŸŽ‰. When Peer B gets a CHOKE message from Peer A, it MUST NOT send new REQUEST messages, and it can't count on answers to any outstanding ones, since the chunk transfer is choked ๐Ÿฅฒ. If Peer B is choked but gets a HAVE message from Peer A, it does NOT get automatically unchoked, so it MUST NOT send any new REQUEST messages either ๐Ÿ™…โ€โ™€๏ธ. CHOKE and UNCHOKE are informational, because responding to REQUESTs is OPTIONAL (see Section 3.7) ๐Ÿ’ญ.


3.10. Peer Address Exchange ๐Ÿ—บ๏ธ

3.10.1. PEX_REQ and PEX_RES Messages ๐Ÿ’Œ

Peer Exchange (PEX) messages are everywhere in peer-to-peer protocols ๐ŸŒ. They let peers swap the transport addresses of the peers they're currently talking to, which cuts down on how often they need to contact a central tracker (or a Distributed Hash Table) to discover new peers ๐Ÿ”. The strength here is that it enables decentralized peer discovery: after an initial bootstrap, you don't need a central tracker anymore ๐Ÿ‘‘. The weakness is that it opens the door to a bunch of attacks, so it should not be used on the Internet unless extra security measures are in place ๐Ÿšจ๐Ÿ’€.

PPSPP supports peer-address exchange on the Internet and in benign private networks, as an OPTIONAL feature (not mandatory to implement), under certain conditions ๐Ÿซถ. Here's the general mechanism. To get some peer addresses, a Peer A MAY send a PEX_REQ message to Peer B. Peer B MAY respond with one or more PEX_REScert messages. Logically, a PEX_REScert reply contains the address of a single peer Ci. Peer B MUST have exchanged messages with Peer Ci in the last 60 seconds to guarantee liveliness โณ. Once Peer A gets them, it may contact any, all, or none of the returned peers Ci ๐Ÿคทโ€โ™€๏ธ. Or, peers MAY ignore PEX_REQ and PEX_REScert messages if they're not interested in finding new peers, because of security considerations (rate limiting), or for any other reason ๐Ÿ™ˆ.

To use PEX in PPSPP on the Internet, two conditions must be met ๐Ÿง:

  1. Peer transport addresses must be relatively stable.
  2. A peer must not obtain all its peer addresses through PEX.

The full security analysis for PEX messages is in Section 12.2 ๐Ÿ”’. Physically, a PEX_REScert message carries a swarm-membership certificate rather than an IP address and port ๐Ÿ“œ. A membership certificate for Peer C says that Peer C at address (ipC,portC) is part of Swarm S at Time T, and it's cryptographically signed by an issuer โœ๏ธ. The receiver, Peer A, can check the certificate for a valid signature from a trusted issuer, the right swarm, and liveliness, and only then consider contacting C โœ…. These swarm-membership certificates line up with signed node descriptors in secure decentralized peer sampling services [SPS] ๐Ÿงš.

There are several possible designs for the security setup around these membership certificates ๐Ÿ› ๏ธ. Different designs can decide who signs the membership certificates and how public keys get distributed. Section 12.2.2 describes an example where a central tracker acts as the Certification Authority ๐Ÿ›๏ธ.

In a hostile environment like the Internet, peers also have to make sure they don't end up talking only to malicious peers when they use peer-address exchange ๐Ÿ˜ฑ. So peers MUST ensure that part of their connections are to peers whose addresses came from a trusted and secured tracker (see Section 12.2.3) ๐Ÿ›ก๏ธ.

Besides the PEX_REScert, there are two other PEX reply messages ๐Ÿ‘ฏ. The PEX_RESv4 message contains a single IPv4 address and port. The PEX_RESv6 message contains a single IPv6 address and port. They MUST only be used in a benign environment, like a private network, because they give no guarantees that the host addressed actually takes part in a PPSPP swarm ๐Ÿ™…โ€โ™€๏ธ.

Once a PPSPP implementation has a list of peers (from PEX, from a central tracker, or from a Distributed Hash Table (DHT)), it has to figure out which peers to actually contact ๐Ÿงญ. This is where it can benefit from information provided by network or content providers, which helps improve network usage and boost PPSPP performance ๐Ÿš€. How a peer-to-peer (P2P) system like PPSPP can do these optimizations using the Application-Layer Traffic Optimization (ALTO) protocol is described in detail in [RFC7285], Section 7 ๐ŸŒŸ.

3.11. Channels ๐ŸŽ€

okay so peers have a MAJOR problem: NATs and firewalls make it SO hard for them to talk to each other ๐Ÿ˜ญ so PPSPP uses this multiplexing move called channels, which lets multiple swarms share the same transport address ๐Ÿฆ‹ channels are kinda like TCP connections, and each channel belongs to a single swarm (see Figure 1 โœจ). just like TCP, every channel gets an identifier that's local to the peer at each end (think TCP port energy), and that identifier MUST be randomly chosen ๐ŸŽฒ so the two peers on one channel use different IDs to name the same channel, and that's the point! the IDs are different and random for security reasons, see Section 12.1 ๐Ÿ” literally iconic.

In the PPSP-over-UDP encapsulation (Section 8.3), when a Channel C has been set up between Peer A and Peer B, the datagrams carrying messages from Peer A to Peer B get a four-byte channel ID prefix that Peer B picked, and it's the other way around for datagrams from Peer B to Peer A ๐Ÿ’œ the channel IDs get swapped during the handshake procedure (see Section 8.4), and the handshake's starting datagram uses channel ID 0 ๐Ÿงš also, PPSPP can team up with Session Traversal Utilities for NAT (STUN) [RFC5389] ๐Ÿซถ

              _________    _________          _________
              |       |    |       |          |       |
              | Swarm |    | Swarm |          | Swarm |
              |  Mgr  |    |   A   |          |   B   |
              |_______|    |_______|          |_______|
                  |            |                /   \
                  |            |               /     \
              ____|____    ____|____    ______/__    _\_______
              |       |    |       |    |       |    |       |
              | Chan  |    | Chan  |    | Chan  |    | Chan  |
              |   0   |    |  481  |    |  836  |    |  372  |
              |_______|    |_______|    |_______|    |_______|
                  |            |            |            |
                  |            |            |            |
              ____|____________|____________|____________|____
              |                                              |
              |                      UDP                     |
              |                   port 6778                  |
              |______________________________________________|

Figure 1 is a peer's network stack, and it's giving organized ๐Ÿ’… this peer is reachable on UDP port 6778 and talks over channel 481 to one peer in Swarm A, and over channels 836 and 372 to two peers in Swarm B. channel ID 0 is the special one, reserved for handshaking ๐Ÿซถ

3.12. Keep Alive Signaling ๐Ÿ’œ

okay this part is about staying in touch with the peers you care about ๐Ÿฅน a peer SHOULD send a "keep alive" message periodically to each peer it's interested in, as long as it has no other messages to send them right now ๐Ÿซถ the whole goal is to keep a signaling channel open to the peers who matter. which peers count as "of interest" is decided by a policy about who's useful now and soon. this document doesn't prescribe a policy, but some examples are (a) peers that have chunks on offer that this client needs, or (b) peers that don't have interesting chunks on offer right now (because they're still downloading themselves, or doing live streaming) but gave good performance in the past ๐Ÿ“ when those peers get new chunks, the peer that kept the channel open can use them again โœจ periodic keep alives also stop other peers from declaring you dead, which is a whole mood ๐Ÿ’€

A guideline for declaring a peer dead when using UDP is a three minute delay since the last packet was received from that peer, plus at least three datagrams sent to that peer during the same period ๐Ÿ˜ญ when a peer is declared dead, the channel to it closes, no more messages get sent to that peer, and the local administration about it is discarded ๐Ÿ™ˆ busy servers can also force idle clients to disconnect by just not sending keep alives. PPSPP doesn't define an explicit message type for "keep alive" messages. in the PPSP-over-UDP encapsulation, they're simply datagrams made of a four-byte channel ID only (see Sections 8.3 and 8.4) ๐ŸŒˆ


4. Chunk Addressing Schemes ๐Ÿ’–

okay now we're talking about how chunks get addressed ๐ŸŽ€ PPSPP can use different methods of chunk addressing, meaning it can support different ways of identifying chunks and different ways of squishing a peer's chunk availability map into something compact ๐Ÿงš

All peers in a swarm MUST use the same chunk addressing method. no mixing and matching, bestie ๐Ÿ™…โ€โ™€๏ธ

4.1. Start-End Ranges ๐ŸŒธ

a chunk specification is a single (start specification, end specification) pair that identifies a range of chunks, with the end inclusive โœจ both the start and end specifications can use one of several addressing schemes. two schemes are defined right now: chunk ranges and byte ranges ๐Ÿ’…

4.1.1. Chunk Ranges

here, the start and end specifications are both chunk identifiers. chunk identifiers are 32-bit or 64-bit unsigned integers ๐Ÿ”ข a PPSPP peer MUST support this scheme, so it's the non-negotiable one ๐Ÿ’œ

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~                    Start chunk (32 or 64)                     ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~                    End chunk (32 or 64)                       ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

4.1.2. Byte Ranges

this time the start and end specifications are 64-bit byte offsets in the content ๐Ÿฆ‹ support for this scheme is OPTIONAL, so it's a bonus, not a must ๐ŸŽ

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Start byte offset (64)                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    End byte offset (64)                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

4.2. Bin Numbers ๐Ÿ“

PPSPP brings a novel way of addressing chunks of content called "bin numbers" (or "bins" for short) ๐Ÿง  bin numbers let you address a binary interval of data with a single integer. this cuts down the state that has to be recorded per peer and the space needed to denote intervals on the wire, so the protocol stays lightweight ๐Ÿชถ basically, this numbering system lets PPSPP work with simpler data structures, like arrays instead of binary trees, which means less complexity overall ๐Ÿ‘‘ the support for this scheme is OPTIONAL.

in bin addressing, the smallest binary interval is a single chunk (for example a block of bytes that may be of variable size), and the largest interval is a complete range of 2**63 chunks ๐ŸŒŒ the novel part is that these intervals get numbered so they lay out into a vector nicely, and that's called bin numbering. here's how it works. take a chunk interval of width W. to derive the bin numbers of the complete interval and its subintervals, you build a minimal balanced binary tree that's at least W chunks wide at the base. the leaves, read left to right, correspond to chunks 0..W-1 in the interval, and each leaf has bin number I*2, where I is the index of the chunk (counting beyond W-1 to balance the tree) โœจ the bin number of a higher-level node P in the tree is calculated like this:

binP = (binL + binR) / 2

where binL is the bin of node P's left-hand child and binR is the bin of node P's right-hand child ๐Ÿ’– since every node in the tree stands for a subinterval of the original interval, each subinterval can be addressed by one single integer. so cute ๐Ÿฅน here's the bin number tree for an interval of width W=8:

                               7
                              / \
                            /     \
                          /         \
                        /             \
                       3                11
                      / \              / \
                     /   \            /   \
                    /     \          /     \
                   1       5        9       13
                  / \     / \      / \      / \
                 0   2   4   6    8   10  12   14

                 C0  C1  C2  C3   C4  C5  C6   C7

          The bin number tree of an interval of width W=8

Figure 2 is that tree, and it's the blueprint for everything bin-related ๐Ÿ“ bin 7 covers the complete interval, bin 3 covers chunks C0..C3, bin 1 covers chunks C0 and C1, and bin 2 is just chunk C1 ๐Ÿง the special numbers 0xFFFFFFFF (32-bit) or 0xFFFFFFFFFFFFFFFF (64-bit) mean an empty interval, and 0x7FFF...FFF means "everything" ๐ŸŒˆ

when bin numbering is in use, a chunk's ID is its corresponding (leaf) bin number in the tree, and the chunk specification in HAVE and ACK messages is just a single bin number (32-bit or 64-bit), like this:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~                    Bin number (32 or 64)                      ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

4.3. In Messages ๐Ÿ’œ

4.3.1. In HAVE Messages

when a receiving peer has successfully checked the integrity of a chunk or interval of chunks, it MUST send a HAVE message to all the peers it wants to let download those chunks from ๐Ÿ”ฅ being able to hold back HAVE messages is what makes them usable as a method of choking, which is kinda genius ๐Ÿ˜ค the HAVE message MUST contain the chunk specification of the biggest complete interval of all chunks the receiver has received and checked so far that fully includes the interval just received ๐Ÿงฉ so the chunk specification MUST denote at least the interval received, but the receiver is supposed to aggregate and acknowledge bigger intervals when possible ๐Ÿซถ

as a result, every single chunk gets acknowledged a logarithmic number of times ๐ŸŒธ that gives some necessary redundancy in acknowledgements, which sufficiently compensates for unreliable transport protocols ๐Ÿ›Ÿ

Implementation note:

To record which chunks a peer has in the state that an implementation keeps for each peer, an implementation MAY use the efficient "binmap" data structure, which is a hybrid of a bitmap and a binary tree, discussed in detail in [BINMAP].

4.3.2. In ACK Messages

PPSPP peers MUST use ACK messages to acknowledge received chunks if an unreliable transport protocol is used ๐Ÿ™Œ when a receiving peer has successfully checked the integrity of a chunk or interval of chunks C, it MUST send an ACK message to the sending peer, containing the chunk specification of its biggest, complete interval covering C (see HAVE) ๐Ÿฆ‹ this is the same vibe as HAVE, just for acknowledging what you received ๐Ÿ’…

5. Content Integrity Protection โœจ

okay so PPSPP can protect the integrity of content while it's zipping around the peer-to-peer network ๐Ÿซถ basically it gives receiving peers ways to check whether a chunk was maliciously changed by the peer that sent it. in benign environments, content integrity protection can be disabled ๐ŸŒธ

For static content, PPSPP currently defines just one method for protecting integrity, called the Merkle Hash Tree scheme. If PPSPP operates over the Internet, this scheme MUST be used. If PPSPP operates in a benign environment, this scheme MAY be used. so it's mandatory to implement, which is what you need for strong security in an IETF protocol [RFC3365] ๐Ÿ’… An extended version of the scheme protects dynamically generated content (live streams), as explained below and in Section 6.1.

The Merkle Hash Tree scheme works with different chunk addressing schemes. all it needs is the ability to address a range of chunks. In the description below, abstract node IDs are used to identify nodes in the tree. On the wire, those get translated into the matching range of chunks in whatever chunk addressing scheme you picked ๐Ÿงš

5.1. Merkle Hash Tree Scheme

PPSPP names content using self-certification, and it's honestly so clean ๐Ÿ˜ญ content is identified by a single cryptographic hash, the root hash of a Merkle hash tree calculated recursively from the content [ABMRKL]. this self-certifying hash tree lets every peer directly detect when a malicious peer tries to distribute fake content. it also means only a small amount of information is needed to start a download (the root hash and some peer addresses) ๐ŸŽ€ For live streaming, a dynamic tree and a public key are used, see below.

okay so here's how the tree gets built for a content file split into N chunks. the construction doesn't assume chunks have to be a fixed size, which is a big vibe ๐Ÿฆ‹ Given a cryptographic hash function, more specifically an MDC [HAC01], such as SHA-256, you calculate the hashes of all the chunks. Next, you create a binary tree of sufficient height. Sufficient height means the lowest level in the tree has enough nodes to hold all chunk hashes in the set, as with bin numbering. The figure below shows the tree for a content file consisting of 7 chunks. Just like the content addressing scheme, the leaves of the tree correspond to a chunk and, in this case, are assigned the hash of that chunk, starting at the leftmost leaf. Since the base of the tree may be wider than the number of chunks, any remaining leaves are assigned an empty hash value of all zeros. Finally, the hash values of the higher levels get calculated by concatenating the hash values of the two children (again left to right) and computing the hash of that aggregate. If the two children are empty hashes, the parent is an empty all-zeros hash as well (to save computation) ๐Ÿงฎ This process ends in a hash value for the root node, which is called the "root hash". Note the root hash only depends on the content, so any modification of the content will result in a different root hash ๐Ÿ”ฅ

                               7 = root hash
                              / \
                            /     \
                          /         \
                        /             \
                      3*               11
                     / \              / \
                    /   \            /   \
                   /     \          /     \
                  1       5        9       13* = uncle hash
                 / \     / \      / \      / \
                0   2   4   6    8   10* 12   14

                C0  C1  C2  C3   C4  C5  C6   E
                =chunk index     ^^           = empty hash

            Merkle hash tree of a content file with N=7 chunks

                                 Figure 3

5.2. Content Integrity Verification

say a peer gets the root hash of the content it wants from a trusted source ๐Ÿซถ it can then check the integrity of any chunk it receives like this. First, it calculates the hash of the chunk it received, for example, chunk C4 in the previous figure. Along with this chunk, it MUST receive the hashes required to check that chunk's integrity. In principle, these are the hash of the chunk's sibling (C5) and those of its "uncles". A chunk's uncles are the sibling Y of its parent X, and the uncle of that Y, recursively until the root is reached. For chunk C4, the uncles are nodes 13 and 3 and the sibling is 10; all of them are marked with a * in the figure ๐Ÿ‘€ Using this information, the peer recalculates the root hash of the tree and compares it to the root hash it got from the trusted source. If they match, the chunk has been positively verified as the requested part of the content โœจ Otherwise, the sending peer sent either the wrong content or the wrong sibling or uncle hashes. For simplicity, the set of sibling and uncle hashes is collectively referred to as the "uncle hashes".

In the case of live streaming, the tree of chunks grows dynamically, and the root hash is undefined or, more precisely, transient, for as long as new data is being generated by the live source ๐ŸŒŠ Section 6.1.2 defines a method for content integrity verification for live streams that works with such a dynamic tree. Even though the tree is dynamic, content verification works the same for both live and predefined content, so you get one unified method for both types of streaming ๐Ÿ’–

5.3. The Atomic Datagram Principle

as explained above, a datagram is a sequence of messages. Ideally, every datagram sent is independent of the others: each datagram SHOULD be processed separately, and losing one datagram must not disrupt the flow of datagrams between two peers ๐Ÿงš Since a datagram carries zero or more messages, both messages and message interdependencies SHOULD NOT span multiple datagrams.

this principle means that as any chunk is verified using its uncle hashes, the necessary hashes SHOULD be put into the same datagram as the chunk's data. If that's not possible because of a limitation on datagram size, the necessary hashes MUST be sent first in one or more datagrams. As a general rule, if some additional data is still missing to process a message within a datagram, the message SHOULD be dropped ๐Ÿ™ˆ

The hashes needed to verify a chunk are, in principle, its sibling's hash and all its uncle hashes, but the set of hashes to send can be optimized ๐Ÿ’… Before sending a packet of data to the receiver, the sender inspects the receiver's previous acknowledgements (HAVE or ACK) to work out which hashes the receiver already has for sure. Suppose the receiver had acknowledged chunks C0 and C1 (the first two chunks of the file). Then it must already have uncle hashes 5, 11, and so on, because those hashes are necessary to check C0 and C1 against the root hash. Then, hashes 3, 7, and so on must also be known, since they're calculated in the process of checking the uncle hash chain. So, to send chunk C7, the sender only needs to include the hashes for nodes 14 and 9, which let the data be checked against hash 11, which the receiver already knows ๐Ÿ“

The sender MAY optimistically skip hashes that were sent out in previous, still-unacknowledged datagrams. It's an optimization trade-off between redundant hash transmission and the risk of collateral data loss. That risk applies when some necessary hashes are lost in the network, so some delivered data can't be verified and thus has to be dropped ๐Ÿ˜ญ Either way, the receiver builds the Merkle hash tree on-demand, incrementally, starting from the root hash, and uses it for data validation.

In short, the sender MUST put into the datagram the hashes it believes are necessary for the receiver to verify the chunk ๐Ÿซถ The receiver MUST remember all the hashes it needs to verify missing chunks that it still wants to download. Note that the latter implies a hardware-limited receiver MAY forget some hashes if it does not plan to announce possession of those chunks to others (i.e., does not plan to send HAVE messages.) ๐ŸŒˆ

5.4. INTEGRITY Messages

Concretely, a peer that wants to send a chunk of content creates a datagram that MUST consist of a list of INTEGRITY messages followed by a DATA message ๐ŸŽ€ If the INTEGRITY messages and the DATA message can't fit into a single datagram because of a limitation on datagram size, the INTEGRITY messages MUST be sent first in one or more datagrams. The list of INTEGRITY messages sent MUST contain an INTEGRITY message for each hash the receiver misses for integrity checking. An INTEGRITY message for a hash MUST contain the chunk specification corresponding to the node ID of the hash, plus the hash data itself. The chunk specification for a node ID is the range of chunks formed by the leaves of the subtree rooted at that node. For example, node 3 in Figure 3 denotes chunks 0, 2, 4, and 6, so the chunk specification should denote that interval ๐Ÿงš The list of INTEGRITY messages MUST be sorted in order of the tree height of the nodes, descending (the leaves are at height 0). The DATA message MUST contain the chunk specification of the chunk and the chunk itself. A peer MAY send the required messages for multiple chunks in the same datagram, depending on the encapsulation ๐Ÿ’œ

5.5. Discussion and Overhead ๐Ÿ’…

okay so the way BitTorrent [BITTORRENT] handles content integrity is literally not built for streaming ๐Ÿ˜ญ it hands clients the hashes of every chunk up front, through metadata files (called .torrent files in BitTorrent). but when chunks are tiny, like in the current UDP encapsulation of PPSPP, that means downloading a huge pile of hashes before any content can even start. so time-till-playback goes way up for end users, and that's a major vibe killer for streaming ๐Ÿ™ˆ

the overhead of Merkle hash trees is actually lowkey pretty chill ๐ŸŒธ the size of the tree (counted as total nodes) depends on how many chunks the content gets divided into, which means it depends on the chunk size too. the formula is:

nnodes = math.pow(2,math.log(nchunks,2)+1)

in principle, a peer needs the hash values of all these nodes sent to it once so it can verify every chunk. so the worst-case on-the-wire overhead is hashsize * nnodes. but the real number of hashes sent can be optimized, as described in Section 5.3 โœจ

now, to see how a peer can verify all the chunks without getting every hash, check out the example tree from Section 5.1 ๐Ÿฆ‹ in a simple progressive download of chunks 0, 2, 4, 6, and so on, the sending peer sends these hashes:

+-------+---------------------------------------------+
| Chunk | Node IDs of hashes sent                     |
+-------+---------------------------------------------+
|   0   | 2,5,11                                      |
|   2   | - (receiver already knows all)              |
|   4   | 6                                           |
|   6   | -                                           |
|   8   | 10,13 (hash 3 can be calculated from 0,2,5) |
|   10  | -                                           |
|   12  | 14                                          |
|   14  | -                                           |
| Total | # hashes        7                           |
+-------+---------------------------------------------+

                Table 1: Overhead for the Example Tree

so the total number of hashes sent (7) is less than the total number of hashes in the tree (16) ๐ŸŽ€ that's because a peer doesn't need hashes that were already calculated and verified as part of earlier chunks. main character efficiency, honestly ๐Ÿ‘‘


5.6. Automatic Detection of Content Size ๐Ÿงš

okay this one is so cute ๐Ÿฅน in PPSPP, the size of a static content file (think a video file) can be reliably and automatically worked out from info received from the network, as long as fixed-size chunks are used. so you don't have to put the file size in the content's metadata for these files. implementations of PPSPP MAY use this automatic detection feature ๐Ÿ’œ and fyi, this feature is the only thing in PPSPP that requires a fixed-size chunk. it's built on the Merkle hash tree and the trusted root hash (the swarm ID), like this:

5.6.1. Peak Hashes ๐Ÿ”ฅ

the newcomer peer's ability to detect the content size really depends on peak hashes ๐Ÿ’– and peak hashes depend on the ideas of filled and incomplete nodes. remember when we built the binary trees for content verification? the base of the tree can have more leaves than the content has chunks. those extra leaves got empty all-zero hashes so the higher-level hashes could still be calculated ๐Ÿ“ so now a filled node is one whose interval of leaves contains only hashes of content chunks, no empty hashes. an incomplete (not filled) node covers an interval that also has empty hashes, usually one that runs past the end of the file. in the figure below, nodes 7, 11, 13, and 14 are incomplete, and everything else is filled โœจ

formally, a peak hash is the hash of a filled node whose sibling is an incomplete node. practically, say a file is 7162 bytes long and a chunk is 1 kilobyte. that file fits into 7 chunks, and the tail chunk is 1018 bytes long. the Merkle hash tree for that file is shown in Figure 4. following the definition, the peak hashes of this file are in nodes 3, 9, and 12, marked with an *. E marks an empty hash ๐Ÿฆ‹

                               7
                              / \
                            /     \
                          /         \
                        /             \
                      3*               11
                     / \              / \
                    /   \            /   \
                   /     \          /     \
                  1       5        9*      13
                 / \     / \      / \      / \
                0   2   4   6    8   10  12*  14

                C0  C1  C2  C3   C4  C5  C6   E
                                         = 1018 bytes

                  Peak hashes in a Merkle hash tree

                              Figure 4

peak hashes can be explained through the binary representation of the number of chunks the file takes up ๐Ÿง  the binary representation of 7 is 111. every "1" in the binary representation of the file's packet length corresponds to a peak hash. for this file there are indeed three peaks: nodes 3, 9, and 12. so the number of peak hashes for a file is also, at most, logarithmic in its size ๐ŸŒˆ

a peer that knows which nodes hold the peak hashes can work out how many chunks the file has, which gives an estimate of the file size (since all chunks but the last are a fixed size) ๐Ÿซถ the peak node IDs can be securely shared from one (untrusted) peer, Peer A, to another peer, Peer B, by having Peer A send the peak hashes and their node IDs to Peer B. it can be shown that the root hash Peer B got from a trusted source is enough to verify these are the right peak hashes, and here's why ๐Ÿ‘‘

Lemma: Peak hashes can be checked against the root hash.

Proof: (a) Any peak hash is always the left sibling. Otherwise, if it is the right sibling, its left neighbor/sibling must also be a filled node, because of the way chunks are laid out in the leaves, which contradicts the definition of a peak hash. (b) For the rightmost peak hash, its right sibling is zero. (c) For any peak hash, the right sibling might be calculated using peak hashes to the left and zeros for empty nodes. (d) Once the right sibling of the leftmost peak hash is calculated, its parent might be calculated. (e) Once that parent is calculated, we might trivially get to the root hash by concatenating the hash with zeros and hashing it repeatedly.

informally, the Lemma basically says: peak hashes cover all the data, so the remaining hashes are either trivial (zeros) or can be calculated from peak hashes and zero hashes ๐Ÿ’…

finally, once Peer B has the number of chunks in the content, it can work out the exact file size ๐Ÿ˜ญ since all chunks except the last are a fixed size, Peer B only needs to know the size of the last chunk. knowing the number of chunks, Peer B can calculate the node ID of the last chunk and download it. as always, Peer B verifies this chunk's integrity against the trusted root hash. since only one chunk of data leads to a successful verification, the size of that chunk has to be correct. then Peer B can determine the exact file size as:

(number of chunks -1) * fixed chunk size + size of last chunk

5.6.2. Procedure ๐ŸŽ€

a PPSPP implementation that wants to use automatic size detection MUST operate as follows. when Peer A sends a DATA message to Peer B for the first time, Peer A MUST first send all the peak hashes for the content, in INTEGRITY messages, unless Peer B has already signaled that it knows the peak hashes by having acknowledged any chunk. if they're needed, the peak hashes MUST be sent as an extra list of uncle hashes for the chunk, before the list of actual uncle hashes of the chunk as described in Section 5.3. the receiver, Peer B, MUST check the peak hashes against the root hash to determine the approximate content size. to get the definite content size, Peer B MUST download the last chunk of the content from any peer that offers it ๐Ÿ”ฅ

let's run through an example ๐Ÿงš say a 7162-byte file that fits in 7 chunks of 1 kilobyte is distributed by Peer A. Figure 4 shows the relevant Merkle hash tree. Peer B only knows the root hash of the file after successfully connecting to Peer A, and it requests the first chunk of data, C0 in Figure 4. Peer A replies by putting these messages in the datagram, in this exact order: first, the three peak hashes of this file, the hashes of nodes 3, 9, and 12; second, the uncle hashes of C0; and then the DATA message with the actual content of C0 โœจ when Peer B gets the peak hashes, it checks them against the root hash and figures out the file is 7 chunks long. to pin down the exact size, Peer B needs to request and retrieve the last chunk containing data, C6 in Figure 4. once that last chunk is retrieved and verified, Peer B concludes it's 1018 bytes long, which means the file is exactly 7162 bytes long ๐ŸŽ‰


6. Live Streaming ๐ŸŒธ

the messages defined above can be used for live streaming too ๐Ÿ’– in a pull-based model, a live streaming injector can announce the chunks it generates via HAVE messages, and peers can retrieve them via REQUEST messages. the areas that need special attention are content authentication and chunk addressing (to get an infinite stream of chunks) ๐Ÿฆ‹

6.1. Content Authentication โœจ

okay so for live streaming, PPSPP gives you two ways for a peer to check that the content it gets from another peer is legit: "Sign All" and "Unified Merkle Tree" ๐Ÿ”๐Ÿ’–

In the "Sign All" method, the live injector signs every single chunk with a private key. When a peer gets a chunk, it checks the signature against the matching public key, which it got from a trusted source. Supporting this method is OPTIONAL ๐ŸŒธ

In the "Unified Merkle Tree" method, PPSPP mixes the Merkle Hash Tree scheme for static content with signatures, so video-on-demand and live streaming end up working the same way ๐Ÿฆ‹ The Merkle hash trees mean fewer signing and verification operations, which gives you similar signature amortization to what's described in [SIGMCAST]. If PPSPP runs over the Internet, the "Unified Merkle Tree" method MUST be used. If the protocol runs in a benign environment, the "Unified Merkle Tree" method MAY be used. So yes, this method is mandatory to implement, bestie ๐Ÿ‘‘

In both methods, the swarm ID is a public key, encoded the way a DNSSEC DNSKEY resource record is encoded, minus the Base64 encoding [RFC4034] ๐Ÿงš

Specifically, the swarm ID is a 1-byte Algorithm field, which says what cryptographic algorithm the public key uses and decides the format of the Public Key field that comes after it. The value of that Algorithm field has to be one of the values in the "Domain Name System Security (DNSSEC) Algorithm Numbers" registry [IANADNSSECALGNUM]. The RSASHA1 [RFC4034], RSASHA256 [RFC5702], ECDSAP256SHA256 and ECDSAP384SHA384 [RFC6605] algorithms are mandatory to implement ๐Ÿ“

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | Algo Number(8)|                                               ~
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   ~                DNSSEC Public Key (variable)                   ~
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

6.1.1. Sign All ๐Ÿ’…

In the "Sign All" method, the live injector signs every chunk with a private key, and peers, once they've got a chunk, check the signature using the matching public key from a trusted source. The vibe here is that, in PPSPP, the swarm ID of the live stream is that public key ๐Ÿ”‘

A peer that wants to send a chunk builds a datagram that MUST contain a SIGNED_INTEGRITY message with the chunk's signature, followed by a DATA message holding the actual chunk. If the SIGNED_INTEGRITY message and the DATA message can't fit in a single datagram because of the datagram size limit, the SIGNED_INTEGRITY message MUST be sent first, in its own separate datagram ๐Ÿ“ฆ The SIGNED_INTEGRITY message is made up of the chunk specification, the timestamp, and the digital signature โœจ

The digital signature algorithm is picked by the Live Signature Algorithm protocol option, see Section 7.7. The signature is computed over a concatenation of the on-the-wire representation of the chunk specification, a 64-bit timestamp in NTP Timestamp format [RFC5905], and the chunk, in that order. The timestamp is the time signature that was made at the injector in UTC ๐Ÿ•ฐ๏ธ


6.1.2. Unified Merkle Tree ๐ŸŒˆ

In this method, the chunks are used as the basis for a Merkle hash tree, same as for static content. The twist is that chunks keep getting generated, so the tree isn't static, it's dynamic ๐ŸŒ€ That means the tree doesn't have a root hash, or, to be more precise, it has a transient root hash. So a public key ends up serving as the swarm ID of the content. The key is used to digitally sign updates to the tree, which lets peers grow it based on trusted information using the process below ๐Ÿซถ

6.1.2.1. Signed Munro Hashes

The live injector generates a number of chunks, called NCHUNKS_PER_SIG, which is a fixed power of 2 (NCHUNKS_PER_SIG>=2). These chunks get added as new leaves to the existing hash tree. Because of that expansion, the tree gets a new subtree that is NCHUNKS_PER_SIG chunks wide at the base. The root of that new subtree is called the munro of the subtree, and its hash is the munro hash of the subtree. Check out Figure 5 ๐Ÿฆ‹ Here, node 5 is the new munro, marked with a $ sign.

                                     3
                                    / \
                                   /   \
                                  /     \
                                 1       5$
                                / \     / \
                               0   2   4   6

   Expanded live tree.  With NCHUNKS_PER_SIG=2, node 5 is the munro for
      the new subtree spanning 4 and 6.  Node 1 is the munro for the
    subtree spanning chunks 0 and 2, created in the previous iteration.

                                 Figure 5

Informally, here's how it goes ๐Ÿ’– The injector signs only the munro hash of the new subtree with its private key. Then it tells its peers the new subtree exists, using HAVE messages. When a peer answers those HAVE messages by requesting a chunk from the new subtree, the injector first sends the signed munro hash for the requested chunk. After that, a lot like static content, the injector sends the uncle hashes needed to verify that chunk, as in Section 5.1. The difference is that the uncle hashes are used to verify the requested chunk against the munro hash, not the root hash like with static content. Last of all, the injector sends the actual chunk ๐ŸŽ€

The receiving peer checks the signature on the signed munro using the swarm ID (a public key), then updates its hash tree. Once the peer knows the munro hash is trusted, it can verify every chunk in the subtree against that munro hash, using the accompanying uncle hashes as in Section 5.1 ๐Ÿ˜ญโœจ

Let's run through the next round so it clicks. The injector has the tree from Figure 5, and it's connected to a bunch of peers who have that same tree and all have chunks 0, 2, 4, and 6. When the injector generates two new chunks (NCHUNKS_PER_SIG=2), the tree grows the way Figure 6 shows. The two new chunks, 8 and 10, extend the tree on the right, and to fit them a new root is made: node 7. Since this tree is wider at the base than the number of chunks, there are two empty leaves right now. The munro node for the new subtree is 9, marked with a $ sign ๐Ÿงš

                                     7
                                    / \
                                  /     \
                                /         \
                              /             \
                            3               11
                           / \              / \
                          /   \            /   \
                         /     \          /     \
                        1       5        9$      13
                       / \     / \      / \      / \
                      0   2   4   6    8   10   E   E

    Expanded live tree.  With NCHUNKS_PER_SIG=2, node 9 is the munro of
             the newly added subtree spanning chunks 8 and 10.

                                 Figure 6

Now the injector has to tell its peers about the updated tree, including the new munro hash 9. So it sends a HAVE message with a chunk specification for nodes 8 + 10 to its peers. Peer P responds by asking for the new chunk, say chunk 8, from the injector with a REQUEST message ๐Ÿ™ˆ In reply, the injector sends the signed munro hash of node 9 as an INTEGRITY message carrying the hash of node 9, plus a SIGNED_INTEGRITY message carrying the signature of the hash of node 9. Those are followed by an INTEGRITY message with the hash of node 10 and a DATA message with chunk 8 ๐Ÿ’…

When Peer P gets all that, it verifies the signature on the munro and expands its view of the tree. Then the peer computes the hash of chunk 8 and combines it with the received hash of node 10, which gives the expected hash of node 9. Next, it checks chunk 8 by comparing the computed hash of node 9 with the munro hash of that same node it just received. Boom, Peer P has successfully verified the integrity of chunk 8 ๐Ÿ”ฅ

This procedure needs just one signing operation for every NCHUNKS_PER_SIG chunks created, and one verification operation for every NCHUNKS_PER_SIG received, which makes it way cheaper than "Sign All" ๐Ÿ’œ A receiving peer does also need to check one or more hashes per chunk through the Merkle Hash Tree scheme, but that needs fewer hardware resources than a signature verification for every chunk. The approach is similar to signature amortization through Merkle Tree Chaining [SIGMCAST]. The catch is higher latency. A peer can't download the new chunks until the injector has computed the signature and announced the subtree. A peer MUST check the signature before forwarding the chunks to other peers [POLLIVE] ๐Ÿซ 

The number of chunks per signature, NCHUNKS_PER_SIG, MUST be a fixed power of 2 for simplicity. NCHUNKS_PER_SIG MUST be larger than 1 for performance reasons. Two things matter when picking a value. First, the CPU load clients can afford for signature verifications, given the stream's bitrate. To keep CPU load low on a high bitrate stream, NCHUNKS_PER_SIG should be high. Second, the effect on latency, which goes up as NCHUNKS_PER_SIG gets higher, as we just talked about. Also note that the procedure doesn't rule out variable-size chunks ๐ŸŒธ

This integrity method has an extra perk ๐ŸŽ If some peers in the system saved the whole broadcast, then the moment the broadcast ends, the content is available as a video-on-demand download, using the now stabilized tree and the final root hash as the swarm identifier. Peers that saved all the chunks can announce the root hash to the tracking infrastructure and instantly seed the content ๐ŸŒˆ

6.1.2.2. Munro Signature Calculation โœจ

The digital signature algorithm is determined by the Live Signature Algorithm protocol option, see Section 7.7. The signature is computed over a concatenation of the on-the-wire representation of the chunk specification of the munro node (see Section 6.1.2.1), a timestamp in 64-bit NTP Timestamp format [RFC5905], and the hash associated with the munro node, in that order. The timestamp is the time signature that was made at the injector in UTC โญ

6.1.2.3. Procedure ๐ŸŽ€

okay so bestie, the injector MUST NOT send a HAVE message for chunks in the new subtree until it has computed the signed munro hash for that subtree ๐Ÿ”ฅ no shortcuts, no cap!!

when Peer B asks Peer A for a chunk C (whether Peer A is the injector or just some other peer), and Peer A decides to reply, it has to follow these steps ๐Ÿ’…. First, Peer A MUST send an INTEGRITY message with the chunk specification for the munro of chunk C plus the munro's hash ๐Ÿฆ‹. Then it MUST send a SIGNED_INTEGRITY message with the chunk specification for the munro, the timestamp, and its signature, all bundled into a single datagram โœจ. The only time this gets skipped is if Peer B already said it has a chunk with the same corresponding munro earlier in the exchange, using HAVE or ACK messages ๐Ÿ™ˆ.

After those two messages (if there are any), Peer A MUST send the missing uncles hashes it needs to verify the chunk against its munro hash, and then the chunk itself, as described in Section 5.4 ๐ŸŒธ. Sharing datagrams is encouraged whenever it's possible ๐Ÿซถ.

6.1.2.4. Secure Tune In ๐Ÿงš

okay so when a peer tunes in to a live stream, it has to figure out what the last chunk the injector made is ๐Ÿ‘‘. To make this easier in the Unified Merkle Tree scheme, every peer shares what it knows about the injector's chunks by swapping their latest signed munro hashes ๐Ÿ’œ. Here's how it goes ๐Ÿ˜ญ.

Quick recap from PPSPP: when Peer A starts a channel with Peer B, Peer A sends a first datagram with a HANDSHAKE message, and Peer B answers with a second datagram that also has a HANDSHAKE message (see Section 3.1) ๐ŸŒˆ. When Peer A sends a third datagram to Peer B and Peer B gets it, both peers know the other is listening on its stated transport address ๐ŸŽ€. Peer B is then allowed to send heavy payload like DATA messages in the fourth datagram ๐Ÿ’–. Peer A can already safely do that in the third datagram โœจ.

In the Unified Merkle Tree scheme, Peer A MUST send its rightmost signed munro hash to Peer B in the third datagram, and in every datagram after that to Peer B, until Peer B indicates it has a chunk with the same corresponding munro or a more recent munro (by means of a HAVE or ACK message) ๐Ÿฆ‹. Peer B might already have said this through HAVE messages in the second datagram ๐Ÿ™ˆ. Going the other way, when Peer B sends the fourth datagram or any later datagram to Peer A, Peer B MUST send its rightmost signed munro hash, unless Peer A already indicated it knows that munro or a more recent one ๐Ÿ’…. The rightmost signed munro hash of a peer is defined as the munro hash signed by the injector of the rightmost subtree of width NCHUNKS_PER_SIG chunks in the peer's Merkle hash tree ๐Ÿ”ฅ. Peer A MUST NOT send the signed munro hash in the first datagram of the HANDSHAKE procedure, and Peer B MUST NOT send it in the second datagram, because it counts as heavy payload ๐Ÿ˜ญ๐Ÿ’€.

When a peer gets a SIGNED_INTEGRITY message with a signed munro hash but the timestamp is too old, the peer MUST discard the message ๐Ÿ™…โ€โ™€๏ธ. Otherwise, it SHOULD use the signed munro to update its hash tree and pick a tune-in point in the live stream ๐Ÿ‘‘. A peer may combine information from multiple peers to choose that tune-in point ๐Ÿซถ.

6.2. Forgetting Chunks ๐Ÿ“

okay so as a live broadcast goes on, a peer might want to toss the chunks it already played out ๐Ÿงš. Ideally, other peers should know this so they don't keep asking that peer for those chunks ๐Ÿ’œ. This can happen when viewers pause a live stream, or when they start late, like joining a broadcast at 20:35 when it actually began at 20:30 ๐Ÿ™ˆ.

PPSPP has a simple fix so peers can keep up with what a discarding peer has available ๐ŸŒธ. A discarding peer in a live stream MUST enable the Live Discard Window protocol option, which says how many chunks/bytes it caches before the last chunk/byte it advertised as available (see Section 7.9) โœจ. Its peers SHOULD apply this number as a sliding window filter over the peer's chunk availability, as shared through its HAVE messages ๐ŸŽ€.

Three things matter when picking a value for this option: the playback buffer peers want, the bitrate of the stream, and the resources the peer has available ๐Ÿ’…. Think about a fresh peer joining the stream ๐Ÿฆ‹. The size of the discard window on the peers it connects to decides how much it can download straight away to build its prebuffer ๐Ÿ”ฅ. If the window is smaller than the buffer it wants, the fresh peer has to wait until the peers have downloaded more of the stream before it can start playback ๐Ÿ˜ญ. Media buffers are usually measured in seconds, so the discard window size also depends on the (average) bitrate of the stream ๐Ÿ’–. Finally, if a peer has few resources for storing chunks and metadata, it SHOULD choose a small discard window ๐Ÿซถ.

  1. Protocol Options ๐ŸŒˆ

okay so the HANDSHAKE message in PPSPP can carry the following protocol options โœจ. Unless stated otherwise, a protocol option is an 8-bit code followed by an 8-bit value ๐ŸŽ€. Larger values are all encoded big-endian ๐Ÿ’…. Each protocol option gets explained in the subsections below ๐Ÿฆ‹. The list of protocol options MUST be sorted on code value (ascending) in a HANDSHAKE message ๐Ÿ‘‘.

          +--------+-------------------------------------+
          | Code   | Description                         |
          +--------+-------------------------------------+
          | 0      | Version                             |
          | 1      | Minimum Version                     |
          | 2      | Swarm Identifier                    |
          | 3      | Content Integrity Protection Method |
          | 4      | Merkle Hash Tree Function           |
          | 5      | Live Signature Algorithm            |
          | 6      | Chunk Addressing Method             |
          | 7      | Live Discard Window                 |
          | 8      | Supported Messages                  |
          | 9      | Chunk Size                          |
          | 10-254 | Unassigned                          |
          | 255    | End Option                          |
          +--------+-------------------------------------+

                       Table 2: PPSPP Options

7.1. End Option ๐Ÿ’–

okay so a peer MUST end the list of protocol options with the end option ๐Ÿ™Œ. Anything after that should be treated as protocol messages ๐ŸŒธ. The code for the end option is 255, and unlike the others it has no value octet, so the option is just 1 octet long ๐Ÿซถ.

 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|1 1 1 1 1 1 1 1|
+-+-+-+-+-+-+-+-+

7.2. Version โœจ

okay so a peer MUST include the maximum PPSPP version it supports as the first protocol option in the list ๐ŸŽ€. The code for this option is 0 ๐Ÿ’…. Defined values are in Table 3 ๐Ÿฆ‹.

        +---------+----------------------------------------+
        | Version | Description                            |
        +---------+----------------------------------------+
        | 0       | Reserved                               |
        | 1       | Protocol as described in this document |
        | 2-255   | Unassigned                             |
        +---------+----------------------------------------+

                   Table 3: PPSPP Version Numbers
 0                   1
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 0 0|  Version (8)  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

7.3. Minimum Version ๐ŸŒˆ

okay so when a peer starts the handshake, it MUST include the minimum PPSPP version it supports in the list of protocol options ๐Ÿ”ฅ. This follows the min/max versioning scheme from [RFC6709], Section 4.1, strategy 5 ๐Ÿ’œ. The code for this option is 1 ๐Ÿ“. Defined values are in Table 3 ๐Ÿงš.

 0                   1
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 0 1| Min. Ver. (8) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

7.4. Swarm Identifier ๐Ÿฆ‹

okay so when a peer starts the handshake, it MUST include exactly one swarm identifier option ๐Ÿ‘‘. If the peer is not the initiator, it MAY include a swarm identifier option as an end-to-end check ๐Ÿซถ. This option looks like this ๐Ÿ’–:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 1 0|     Swarm ID Length (16)      |               ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~                       Swarm Identifier (variable)             ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The Swarm ID Length field holds the length of the single Swarm Identifier that follows, in bytes ๐Ÿงš. The Length field is 16 bits wide so it can handle big public keys as identifiers in live streaming ๐ŸŒธ.

Each PPSPP peer knows the IDs of the swarms it joins, so this info can be checked the moment it arrives โœจ.

7.5. Content Integrity Protection Method ๐Ÿ’…

okay so a peer MUST include the content integrity method used by a swarm ๐Ÿ”ฅ. The code for this option is 3 ๐Ÿ’œ. Defined values are in Table 4 ๐ŸŽ€.

                +--------+-------------------------+
                | Method | Description             |
                +--------+-------------------------+
                | 0      | No integrity protection |
                | 1      | Merkle Hash Tree        |
                | 2      | Sign All                |
                | 3      | Unified Merkle Tree     |
                | 4-255  | Unassigned              |
                +--------+-------------------------+

         Table 4: PPSPP Content Integrity Protection Methods

The "Merkle Hash Tree" method is the default for static content, see Section 5.1 ๐Ÿฆ‹. "Sign All" and "Unified Merkle Tree" are for live content, see Section 6.1 ๐ŸŒˆ, and "Unified Merkle Tree" is the default ๐Ÿ‘‘.

 0                   1
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 1 1|   CIPM (8)    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

7.6. Merkle Tree Hash Function ๐Ÿ“

okay so when the content integrity protection method is "Merkle Hash Tree", this option that defines which hash function the tree uses MUST be included ๐Ÿงš. The code for this option is 4 ๐ŸŒธ. Defined values are in Table 5 (see [FIPS180-4] for the function semantics) ๐Ÿ’–.

                     +----------+-------------+
                     | Function | Description |
                     +----------+-------------+
                     | 0        | SHA-1       |
                     | 1        | SHA-224     |
                     | 2        | SHA-256     |
                     | 3        | SHA-384     |
                     | 4        | SHA-512     |
                     | 5-255    | Unassigned  |
                     +----------+-------------+

                Table 5: PPSPP Merkle Hash Functions

Implementations MUST support SHA-1 (see Section 12.5) and SHA-256 โœจ. SHA-256 is the default ๐Ÿซถ.

 0                   1
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 0 1 0 0|    MHF (8)    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

7.7. Live Signature Algorithm ๐Ÿ”

okay so when the content integrity protection method is "Sign All" or "Unified Merkle Tree", this option MUST be defined ๐Ÿ’…. Its code is 5, and the 8-bit value is one of the entries in the "Domain Name System Security (DNSSEC) Algorithm Numbers" registry [IANADNSSECALGNUM]. The RSASHA1 [RFC4034], RSASHA256 [RFC5702], ECDSAP256SHA256 and ECDSAP384SHA384 [RFC6605] algorithms are mandatory to implement ๐Ÿซถ. If nothing is set, the default is ECDSAP256SHA256 โœจ.

    0                   1
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 1 0 1|    LSA (8)    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

7.8. Chunk Addressing Method ๐Ÿงฉ

a peer MUST include the chunk addressing method it uses ๐ŸŽ€. The code for this option is 6, and the defined values are listed in Table 6 below ๐Ÿ“.

                     +--------+---------------------+
                     | Method | Description         |
                     +--------+---------------------+
                     | 0      | 32-bit bins         |
                     | 1      | 64-bit byte ranges  |
                     | 2      | 32-bit chunk ranges |
                     | 3      | 64-bit bins         |
                     | 4      | 64-bit chunk ranges |
                     | 5-255  | Unassigned          |
                     +--------+---------------------+

                  Table 6: PPSPP Chunk Addressing Methods

implementations MUST support "32-bit chunk ranges" and "64-bit chunk ranges" ๐ŸŒˆ. The default is "32-bit chunk ranges" ๐Ÿฆ‹.

    0                   1
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 1 1 0|    CAM (8)    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

7.9. Live Discard Window ๐ŸชŸ

a peer in a live swarm MUST include the discard window it uses ๐Ÿ‘‘. The code for this option is 7. The unit of the discard window depends on the chunk addressing method, so check Table 6 ๐Ÿง. For bins and chunk ranges, it counts chunks. For byte ranges, it counts bytes. Its data type is the same as for a bin, or one value in a range specification. Basically, the value is a 32-bit or 64-bit integer in big-endian format ๐Ÿ”ข. One important thing: if this option is used, the Chunk Addressing Method MUST appear before it in the list ๐Ÿšจ. The structure looks like this:

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 1 1 1|       Live Discard Window (32 or 64)          ~
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   ~                                                               ~
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

a peer that does not, under normal circumstances, discard chunks MUST set this option to the special value 0xFFFFFFFF (32-bit) or 0xFFFFFFFFFFFFFFFF (64-bit) ๐Ÿซง. For example, peers that record a complete broadcast and then offer it directly as a static file after the broadcast ends use these values (see Section 6.1.2) ๐Ÿ“ผ. Section 6.2 explains how to work out a value for this option. ๐Ÿ’œ

7.10. Supported Messages ๐Ÿ“ฌ

peers may support just a subset of the PPSPP messages ๐Ÿ™ˆ. For example, peers running over TCP may not accept ACK messages, or peers used with a centralized tracking infrastructure may not accept PEX messages. Because of that, peers who support only a proper subset of the PPSPP messages MUST signal which subset they support using this protocol option ๐Ÿ“ฃ. The code for this option is 8. Its value is a length octet (SupMsgLen) that gives the length, in bytes, of the compressed bitmap right after it ๐Ÿงƒ.

the set of supported messages can be read from the compressed bitmap by padding it with bytes of value 0 until it is 256 bits long ๐Ÿงฎ. Then a 1 bit at position X (counting left to right) means the peer supports message type X, see Table 7. To build the compressed bitmap, make a bitmap with a 1 for each supported message type and a 0 for each one that isn't, store it as an array of bytes, and cut it off after the last non-zero byte โœ‚๏ธ. For example, the first 16 bits of the compressed bitmap for a peer that supports every message except ACKs and PEXs look like this: 11011001 11110000 ๐ŸŒธ.

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 1 0 0 0| SupMsgLen (8) |                               ~
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   ~            Supported Messages Bitmap (variable, max 256)      ~
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

7.11. Chunk Size ๐Ÿ“

a peer in a swarm MUST include the chunk size the swarm uses ๐Ÿฐ. The code for this option is 9. Its value is a 32-bit integer giving the size of the chunks in bytes, in big-endian format. When variable chunk sizes are used, this option MUST be set to the special value 0xFFFFFFFF ๐ŸŒ€. Section 8.1 explains how content publishers can work out a value for this option. ๐Ÿง

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 1 0 0 1|       Chunk Size (32)                         ~
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   ~               |
   +-+-+-+-+-+-+-+-+

8. UDP Encapsulation ๐ŸŒŠ

PPSPP implementations MUST use UDP as the transport protocol, and MUST use LEDBAT for congestion control [RFC6817] ๐Ÿš€. Using LEDBAT lets PPSPP keep serving content after playback (seeding) without wrecking the experience of the user, who may have moved on to other tasks that use the same network connection ๐Ÿซถ. Future PPSPP versions could also run over other transport protocols or use different congestion control algorithms ๐Ÿ”ฎ.

8.1. Chunk Size ๐Ÿ•

in general, a UDP datagram carrying PPSPP messages SHOULD fit inside a single IP packet, so its maximum size depends on the MTU of the network ๐Ÿ“. If the datagram doesn't fit, it's more likely to get lost, because losing one fragment means losing the whole datagram ๐Ÿ’€.

The biggest message in a PPSPP datagram is the DATA message carrying a chunk of content. So the (maximum) size of chunk you pick for a particular swarm depends mainly on the expected MTU ๐Ÿง . The chunk size should be chosen so that a chunk and its required INTEGRITY messages can usually fit inside a single datagram, following the Atomic Datagram Principle (Section 5.3) ๐Ÿ’–. Hardware capabilities of the peers matter too. Having large chunks, and therefore fewer chunks per megabyte of content, cuts down on processing costs ๐Ÿ’ช. The chunk addressing schemes can all work with different chunk sizes, see Section 4 ๐Ÿงฉ.

The RECOMMENDED approach is to use fixed-size chunks of 1024 bytes, because this size has a high chance of travelling end-to-end across the Internet without any fragmentation ๐Ÿฆ‹. In particular, with this size, a UDP datagram with a DATA message can go out as a single IP packet over an Ethernet network with 1500-byte frames ๐ŸŒˆ.

A PPSPP implementation MAY use a variant of the Packetization Layer Path MTU Discovery (PLPMTUD), described in [RFC4821], to discover the optimal MTU between sender and destination ๐Ÿ”. Like PLPMTUD, it sends progressively larger probing packets to find the best MTU for a given path. The twist in PPSPP is that probe packets SHOULD contain actual messages, in particular multiple DATA messages ๐ŸŽฏ. Since the probes are real DATA messages, the returning ACK messages confirm that the probe arrived, which updates the MTU estimate at both ends of the link โœ…. To scale up probe packets in sensible steps, a minimum chunk size of 512 bytes SHOULD be used, because smaller chunk sizes make the protocol inefficient ๐Ÿ™…โ€โ™€๏ธ. The upshot is that PPSPP only supports IPv4 datagrams of 576 bytes or more. This variant is not mandatory to implement ๐Ÿซง.

The chunk size used for a particular swarm, or the fact that it is variable, MUST be part of the swarm's metadata (which then minimally consists of the swarm ID and the chunk nature and size). ๐Ÿ’…โœจ

8.2. Datagrams and Messages ๐Ÿ’Œ

when using UDP, the abstract datagram described above maps directly onto a UDP datagram ๐ŸŽ. Most messages inside a datagram have a fixed length, which usually depends on the message type. The first byte of a message tells you its type. The currently defined types are:

                      +----------+------------------+
                      | Msg Type | Description      |
                      +----------+------------------+
                      | 0        | HANDSHAKE        |
                      | 1        | DATA             |
                      | 2        | ACK              |
                      | 3        | HAVE             |
                      | 4        | INTEGRITY        |
                      | 5        | PEX_RESv4        |
                      | 6        | PEX_REQ          |
                      | 7        | SIGNED_INTEGRITY |
                      | 8        | REQUEST          |
                      | 9        | CANCEL           |
                      | 10       | CHOKE            |
                      | 11       | UNCHOKE          |
                      | 12       | PEX_RESv6        |
                      | 13       | PEX_REScert      |
                      | 14-254   | Unassigned       |
                      | 255      | Reserved         |
                      +----------+------------------+

                       Table 7: PPSPP Message Types

integers are serialised in network (big-endian) byte order ๐ŸŒ. Take a HAVE message (Section 3.2) using bin chunk addressing as an example. It has a message type of 0x03 and a payload that's a bin number, a 4-byte integer (say, 1). So its on-the-wire representation for UDP can be written in hex as "0300000001" ๐Ÿค“.

all messages are idempotent or recognisable as duplicates ๐Ÿ”. Idempotent means that processing a message more than once doesn't leave you in a different state than processing it just once ๐Ÿ’œ. In particular, a peer MAY resend DATA, ACK, HAVE, INTEGRITY, PEX_*, SIGNED_INTEGRITY, REQUEST, CANCEL, CHOKE, and UNCHOKE messages without problems when loss is suspected ๐Ÿ™Œ. When a peer resends a HANDSHAKE message, the receiver can recognise it as a duplicate, because it already recorded the first connection attempt, and deal with it that way ๐Ÿงƒ.

8.3. Channels ๐Ÿ“ก

as described in Section 3.11, PPSPP uses a multiplexing scheme called channels, which lets multiple swarms share the same UDP port ๐ŸŽ€. In the UDP encapsulation, each datagram from Peer A to Peer B is prefixed with the channel ID that Peer B allocated. The peers learn each other's channel IDs during the handshake, as explained in Section 3.1.1 ๐Ÿค. A channel ID is 4 bytes long and MUST be generated following the requirements in [RFC4960] (Section 5.1.3) ๐Ÿ”.

8.4. HANDSHAKE ๐Ÿค

okay so a channel gets set up with a handshake ๐Ÿซถ to start one, the initiating peer needs two things: the swarm metadata from Section 3.1, and the IP address and UDP port of the peer it wants to talk to. the datagram carrying a HANDSHAKE message looks like this:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                  Destination Channel ID (32)                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 0 0|            Source Channel ID (32)             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |                                               ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
~                     Protocol Options                          ~
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

where's the breakdown, bestie ๐Ÿ’œ

  • Destination Channel ID:
    • If the datagram is sent by the initiating peer, then it MUST be an all-zeros channel ID.
    • If the datagram is sent by the responding peer, then it MUST consist of the Source Channel ID from the sender's HANDSHAKE message.
  • The octet 0x00: The HANDSHAKE message type
  • Source Channel ID: A locally unused channel ID
  • Protocol Options: A list of protocol options encoding the swarm's metadata, as defined in Section 7.

okay and when you wanna close a channel ๐Ÿšช, a peer SHOULD send a HANDSHAKE message that MUST contain an all zeros Source Channel ID and a list of protocol options. the list MUST either be empty or contain the maximum version number the sender supports, following the min/max versioning scheme from [RFC6709], Section 4.1. literally so clean ๐Ÿ’…


8.5. HAVE ๐ŸŒธ

a HAVE message (type 0x03) is giving "I've got these chunks and I checked them, they're good" โœจ it has one chunk specification, which stands for a consecutive range of verified chunks. a bin is a single integer, or a chunk or byte range of two integers, and the width comes from the Chunk Addressing protocol options. everything gets encoded big-endian ๐Ÿ“

here's a HAVE message using 32-bit chunk ranges as the Chunk Addressing method:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 1 1|                 Start chunk (32)              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |                  End chunk (32)               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |
+-+-+-+-+-+-+-+-+

so the first octet is the HAVE message (0x03), then the start chunk and the end chunk that describe the chunk range ๐Ÿฆ‹ this diagram is a message, not a datagram, so there's no destination Channel ID stuck on the front. same goes for every message diagram after this one, so don't be confused, bestie ๐Ÿ™ˆ


8.6. DATA ๐Ÿ’–

a DATA message (type 0x01) is a chunk specification, a timestamp, and the actual chunk ๐Ÿ“ฆ if a datagram has one DATA message, a sender MUST always put that DATA message at the tail of the datagram. a datagram MAY contain multiple DATA messages when the chunk size is fixed and none of them carry the last chunk, if that one is smaller than the chunk size. since LEDBAT congestion control is in use, a sender MUST include a timestamp, specifically a 64-bit integer for the current system time with microsecond accuracy. the timestamp MUST be included between the chunk specification and the actual chunk. the timing is SO important, it's the whole vibe ๐Ÿ”ฅ

here's a DATA message using 32-bit chunk ranges as the Chunk Addressing method:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 0 1|                 Start chunk (32)              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |                  End chunk (32)               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Timestamp (64)                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
~                            Data                               ~
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

so the first octet is the DATA message (0x01), followed by the start chunk and the end chunk describing the single chunk, then the timestamp, then the actual data ๐Ÿงš


8.7. ACK ๐ŸŽ€

an ACK message (type 0x02) acknowledges data that its addressee received ๐Ÿซก to play nice with the LEDBAT delay-based congestion control, an ACK message has a chunk specification and a timestamp that represents a one-way delay sample. that sample is a 64-bit integer with microsecond accuracy, and it's computed from the timestamp in the previous DATA message that carried the chunk being acknowledged, following the LEDBAT specification. the math is giving precision ๐Ÿค“

here's an ACK message using 32-bit chunk ranges as the Chunk Addressing method:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 1 0|                 Start chunk (32)              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |                  End chunk (32)               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                  One-way delay sample (64)                    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |
+-+-+-+-+-+-+-+-+

where the first octet is the ACK message (0x02) followed by the start chunk and the end chunk describing the chunk range and the one-way delay sample.


8.8. INTEGRITY ๐Ÿ”

an INTEGRITY message (type 0x04) has a chunk specification and the cryptographic hash for the specified chunk or node ๐Ÿง the type and format of the hash depend on the protocol options.

here's an INTEGRITY message using 32-bit chunk ranges as the Chunk Addressing method and a SHA-256 hash:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 0 1 0 0|                 Start chunk (32)              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |                  End chunk (32)               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
~                            Hash (256)                         ~
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |
+-+-+-+-+-+-+-+-+

where the first octet is the INTEGRITY message (0x04) followed by the start chunk and the end chunk describing the chunk range and the hash.


8.9. SIGNED_INTEGRITY โœ๏ธ

a SIGNED_INTEGRITY message (type 0x07) is a chunk specification, a 64-bit timestamp in NTP Timestamp format [RFC5905], and a digital signature encoded the way a Signature field is in an RRSIG record in DNSSEC, just without the Base64 encoding [RFC4034] ๐Ÿ’œ the signature algorithm is defined by the Live Signature Algorithm protocol option, see Section 7.7. the plaintext the signature is taken over depends on the content integrity protection method being used, see Section 6.1.

here's a SIGNED_INTEGRITY message using 32-bit chunk ranges as the Chunk Addressing method:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 0 1 1 1|                 Start chunk (32)              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |                  End chunk (32)               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Timestamp (64)                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
~                       Signature                               ~
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

where the first octet is the SIGNED_INTEGRITY message (0x07) followed by the start chunk and the end chunk describing the chunk range, the timestamp, and the Signature.

okay the signature length is the part I'm obsessed with ๐Ÿ˜ญ you can work it out from the Live Signature Algorithm protocol option and the swarm ID. the first mandatory algorithms are RSASHA1 and RSASHA256. for those, the swarm ID has a 1-byte Algorithm field followed by an RSA public key stored as a tuple (exponent length, exponent, modulus) [RFC3110]. given the exponent length and the length of the public key tuple in the swarm ID, you can calculate the length of the modulus in bytes. that gives you the signature length, since in RSA the signature is the length of the modulus [HAC01]. the other mandatory algorithms are ECDSAP256SHA256 and ECDSAP384SHA384 [RFC6605]. for those, the digital signature is 64 and 96 bytes, respectively. ๐Ÿซถ๐Ÿ’…

8.10. REQUEST ๐Ÿ“ฅ

okay so a REQUEST message (type 0x08) is how you tell your peer which chunks you're hoping to download ๐Ÿฅบ it carries a chunk specification for them.

here's a REQUEST using 32-bit chunk ranges as the Chunk Addressing method:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 1 0 0 0|                 Start chunk (32)              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |                  End chunk (32)               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |
+-+-+-+-+-+-+-+-+

the first octet is the REQUEST message (0x08), then comes the start chunk and the end chunk, which together describe the chunk range ๐ŸŽ€

8.11. CANCEL ๐Ÿ™…โ€โ™€๏ธ

a CANCEL message (type 0x09) is the vibe check of "nvm, I'm no longer interested" ๐Ÿ’€ it has a chunk specification for the chunks the requester doesn't care about anymore.

a CANCEL using 32-bit chunk ranges as the Chunk Addressing method:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 1 0 0 1|                 Start chunk (32)              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |                  End chunk (32)               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |
+-+-+-+-+-+-+-+-+

the first octet is the CANCEL message (0x09), followed by the start chunk and the end chunk describing the chunk range. bye bye chunks ๐Ÿ‘‹

8.12. CHOKE and UNCHOKE ๐Ÿšง๐Ÿ”“

CHOKE and UNCHOKE messages (types 0x0a and 0x0b, respectively) are giving minimalist ๐Ÿค neither one carries a payload.

a CHOKE message:

 0
 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|0 0 0 0 1 0 1 0|
+-+-+-+-+-+-+-+-+

the first octet is the CHOKE message (0x0a).

an UNCHOKE message:

 0
 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|0 0 0 0 1 0 1 1|
+-+-+-+-+-+-+-+-+

the first octet is the UNCHOKE message (0x0b). ๐Ÿฅณ

8.13. PEX_REQ, PEX_RESv4, PEX_RESv6, and PEX_REScert ๐ŸŒ๐Ÿซถ

PEX stands for peer exchange, and it's the whole "hey, do you know anyone else in this swarm?" energy โœจ

a PEX_REQ (0x06) message has no payload. a PEX_RESv4 (0x05) message contains an IPv4 address in big-endian format, followed by a UDP port number, also in big-endian format. a PEX_RESv6 (0x0c) message is the same idea, except it has a 128-bit IPv6 address instead of an IPv4 one.

now the important bit ๐Ÿšจ if a PEX_REQ doesn't come from a private, unique-local, link-local, or multicast address [RFC1918] [RFC4193] [RFC4291], then the PEX_RES* messages sent back in reply MUST NOT contain such addresses. the whole point is to stop internal addresses from leaking out to external peers ๐Ÿ”’ no oversharing, bestie.

a PEX_REQ message:

 0
 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|0 0 0 0 0 1 1 0|
+-+-+-+-+-+-+-+-+

the first octet is the PEX_REQ message (0x06).

a PEX_RESv4 message:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 0 1 0 1|              IPv4 Address (32)                |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |             Port (16)         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

the first octet is the PEX_RESv4 message (0x05), followed by the IPv4 address and the port number.

a PEX_RESv6 message:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 1 1 0 0|                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                   IPv6 Address (128)                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |             Port (16)         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

the first octet is the PEX_RESv6 message (0x0c), followed by the IPv6 address and the port number. ๐ŸŒˆ

a PEX_REScert (0x0d) message is about membership certs ๐Ÿชช it starts with a 16-bit integer in big-endian that gives the size of the membership certificate that follows (see Section 12.2.1). this certificate says that Peer P at Time T is a member of Swarm S. it's an X.509v3 certificate [RFC5280], encoded with the ASN.1 distinguished encoding rules (DER) [CCITT.X690.2002]. the certificate MUST contain a "Subject Alternative Name" extension, marked as critical, of type uniformResourceIdentifier. no skipping that part ๐Ÿ’…

a PEX_REScert message:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 1 1 0 1|   Size of Memb. Cert. (16)    |               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
~                    Membership Certificate                     ~
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

the first octet is the PEX_REScert message (0x0d), followed by the size of the membership certificate and then the membership certificate itself.

the URL in the name extension MUST follow the generic URL syntax [RFC3986]. its scheme component is "file", the host in the authority component is the DNS name or IP address of Peer P, the port in the authority component is Peer P's port, and the path holds the swarm identifier for Swarm S in hex. the preferred form of the swarm identifier is xxyyzz..., where each x, y, and z is 2 hexadecimal digits from the 8-bit pieces of the identifier. the certificate's validity time is set with notBefore UTCTime set to T, and notAfter UTCTime set to T plus some expiry time defined by the issuer โฐ an example URL:

file://192.0.2.0:6778/e5a12c7ad2d8fab33c699d1e198d66f79fa610c3

8.14. KEEPALIVE ๐Ÿ’“

keep alives don't have a message type on UDP ๐Ÿ™ˆ they're just simple datagrams made of the 4-byte channel ID of the destination, and that's it. so minimal, so iconic.

a keep-alive datagram:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Channel ID (32)                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

8.15. Flow and Congestion Control ๐ŸŒŠ

okay this one is lowkey my favourite ๐Ÿฆ‹ explicit flow control is not required for PPSPP over UDP. for video on demand, the receiver explicitly asks for the content from peers, so the receiver is the one in charge of how much data comes in ๐Ÿ‘‘ for live streaming, where a push model may be used, the amount of incoming data is limited to the stream bitrate. the receiver has to be able to process that to keep playback smooth. if the receiver somehow gets saturated with data anyway, the congestion control on the sender side will notice and adjust the sending rate accordingly. the sender has its own back, honestly ๐Ÿซถ

PPSPP over UDP can support different congestion control algorithms. right now it uses the LEDBAT congestion control algorithm [RFC6817]. LEDBAT is delay-based, and millions of people use it every day as part of the uTP transmission protocol of BitTorrent [LBT] [LCOMPL]. it's also suitable for P2P streaming [PPSPPERF] ๐Ÿ”ฅ

LEDBAT keeps an eye on the delay of packets on the data path. it uses the one-way delay variations to react early and limit how much congestion the stream might cause in the network [RFC6817]. this is what lets PPSPP keep serving content to other interested peers after playback has finished (seeding) without disrupting the user ๐ŸŒธ after playback, the user might switch to other tasks that use their network link, and those get priority over PPSPP traffic. so the user doesn't even notice the background PPSPP traffic, and that makes seeding the content for a longer time way more likely โœจ

reacting early isn't a problem in a peer-to-peer system where multiple sources offer the content. if congestion happens near the sender, LEDBAT's early reaction does affect chunk transmission to the receiver. but honestly, the receiver benefits from learning early that transmission from a particular source is impacted. the receiver can then pick time-critical chunks from other sources during its chunk picking phase ๐Ÿงš

if the bottleneck is near the receiver, the receiver is unlucky, because any transmission from any source running through that bottleneck will back off pretty fast thanks to LEDBAT ๐Ÿฅฒ still, for the rest of the network (and the network operator), this is a win, since the video-streaming system backs off early enough and doesn't add too much congestion.

the power of LEDBAT is that its behaviour can be configured ๐Ÿ’… for live streaming, a PPSPP deployer may want more aggressive behaviour to keep quality of service up. in that case, LEDBAT can be made more aggressive. specifically, LEDBAT's queuing target delay value (TARGET in [RFC6817]) and other parameters can be adjusted so it acts as aggressive as TCP (or even more). so yeah, LEDBAT is an algorithm that works for a lot of scenarios in a peer-to-peer context, and it's giving reliable ๐ŸŒˆ

8.16. Example of Operation โœจ

okay bestie, let's watch a leecher and a seeder chat ๐Ÿซถ we're sending the file "Hello World!", which fits in one 1024-byte chunk. To keep it easy to follow, we use the message names from Table 7 and the protocol option names from Table 2 instead of the raw binary values ๐Ÿ’…

The handshake starts with the initiating peer sending a datagram that MUST start with an all-zeros channel ID (0x00000000) ๐Ÿงš Then comes a HANDSHAKE message, whose payload is locally unused, plus a random channel ID (in this case 0x00000001) and a list of protocol options. Channel IDs MUST be randomly chosen, as described in Section 12.1 ๐ŸŽ€

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |   HANDSHAKE   |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 1|    Version    |0 0 0 0 0 0 0 1|  Min Version  |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 1|   Swarm ID    |0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 1 0 0 0 1 1 1 1 0 1 0 0 0 0 0 0 0 0 1 0 0 1 1 1 1 1 0 0 1 1 0|
   ~                             .....                             ~
   |1 0 0 0 0 1 1 0 1 0 1 0 1 0 1 0 1 0 1 1 0 0 0 0 0 0 1 1 1 0 1 1|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |   Cont. Int.  |0 0 0 0 0 0 0 1| Mer.H.Tree F. |0 0 0 0 0 0 1 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |   Chunk Add.  |0 0 0 0 0 0 1 0|   Chunk Size  |0 0 0 0 0 0 0 0~
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   ~0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0|      End      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

the protocol options in that handshake are:

  • Version: 1
  • Minimum supported Version: 1
  • Swarm Identifier: A 32-byte root hash (47a0...b03b) identifying the content
  • Content Integrity Protection Method: Merkle Hash Tree
  • Merkle Tree Hash Function: SHA-256
  • Chunk Addressing Method: 32-bit chunk ranges
  • Chunk Size: 1024

the receiving peer MAY respond ๐Ÿ‘€ and if it does, the returned datagram MUST consist of the channel ID from the sender's HANDSHAKE message (0x00000001), then a HANDSHAKE message whose payload is locally unused, then its own random channel ID (0x00000008) and a list of protocol options. After that it can send any other messages it wants ๐Ÿฆ‹

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |   HANDSHAKE   |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 1 0 0 0|    Version    |0 0 0 0 0 0 0 1|   Cont. Int.  |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 1| Mer.H.Tree F. |0 0 0 0 0 0 1 0|   Chunk Add.  |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 1 0|  Chunk Size   |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0~
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   ~0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0|      End      |      HAVE     |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

with those protocol options, the receiving peer agrees to speak protocol version 1 ๐Ÿ™Œ It also picks the Merkle Hash Tree as the Content Integrity Protection Method, SHA-256 as the Merkle Tree Hash Function, 32-bit chunk ranges as the Chunk Addressing Method, and Chunk Size 1024. And it slides a HAVE message into the same datagram, announcing it already has the first chunk of content ๐Ÿ“

At this point the initiator knows the peer really responded ๐Ÿฅน For that to mean anything, channel IDs MUST be random enough that nobody can easily guess them. So the third datagram of a handshake MAY already contain some heavy payload โœจ To cut down on initialization round trips, the first two datagrams MAY also carry some minor payload, e.g., the HAVE message.

The initiating peer MAY send a request for the chunks it wants from the receiving peer, e.g., the first chunk announced during the handshake. It always puts the channel ID of the peer it's talking to in front of the message (0x00000008 in our example), as described in Section 3.11 ๐Ÿ’– It MAY also tack on extra messages, such as a PEX_REQ.

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |    REQUEST    |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0|    PEX_REQ    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

when the third datagram arrives, both peers have proof they're really talking to each other ๐Ÿค The three-way handshake is complete! The receiving peer answers the request by sending a DATA message with the requested content ๐ŸŽ‰

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     DATA      |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0 1 0 0 1|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 0 1 1 0 1 1 1 1 1 0 1 1 0 1 1|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 1 0 0 0 1 0 0|0 1 0 0 1 0 0 0 0 1 1 0 0 1 0 1 0 1 1 0 1 1 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   ~                           .....                               ~
   |0 1 1 0 1 1 0 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 1 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

the DATA message is made up of:

  • The 32-bit chunk range: 0,0 (the first chunk)
  • The timestamp value: 0004e94180b7db44
  • The data: 48656c6c6f20776f726c6421 (the "Hello world!" file)
the datagram above doesn't include an INTEGRITY message ๐Ÿ‘€ That's because the whole content fits in one message, so the initiating peer can verify it straight against the root hash.

also, in this example the peer doesn't answer the PEX_REQ, since it doesn't know any third peer in the swarm ๐Ÿ™ˆ

once the requested data arrives, the initiating peer replies with an ACK message for the first chunk, carrying a one-way delay sample (100 ms) โฑ๏ธ It also adds a HAVE message for that chunk ๐Ÿ’œ

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |      ACK      |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 1 1 0 0 1 0 0|      HAVE     |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

at this point the initiating peer has successfully grabbed the entire file ๐ŸŽ‰๐ŸŽ‰ Then it explicitly closes the connection by sending a HANDSHAKE message that has an all-zeros Source Channel ID ๐Ÿ‘‹

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |   HANDSHAKE   |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |0 0 0 0 0 0 0 0|      End      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

9. Extensibility โœจ

9.1. Chunk Picking Algorithms

okay so chunk (or piece) picking is totally up to the receiving peer ๐Ÿซถ The sending peer only finds out which chunks are preferred through REQUEST messages. In some live scenarios, it may be beneficial to let the sender ignore those hints and send unrequested data too ๐ŸŒˆ

the chunk picking algorithm sits outside PPSPP. It's usually a pluggable policy that uses the mechanisms PPSPP provides ๐Ÿง  It handles the choices the user makes while consuming the content, like seeking or switching audio tracks or subtitles. Example policies for P2P streaming can be found in [BITOS], and [EPLIVEPERF] ๐Ÿ’…

9.2. Reciprocity Algorithms

the whole point of reciprocity algorithms in peer-to-peer systems is to encourage clients to contribute and stop freeriding ๐Ÿ˜ค A peer counts as freeriding if it only downloads and never uploads to anyone else. Examples include tit-for-tat, as used in BitTorrent [TIT4TAT], and Give-to-Get [GIVE2GET]. In PPSPP, enforcing reciprocity is the sole responsibility of the sending peer ๐Ÿ’–

10. IANA Considerations ๐Ÿ“‹

IANA has created a brand new top-level registry called "Peer-to-Peer Streaming Peer Protocol (PPSPP)" ๐ŸŽ€ It holds the six new sub-registries defined below, so the protocol can be extended later. For every registry, an assignment is a name plus its associated value. Also, for every registry, the "Unassigned" ranges are governed by the policy "IETF Review", as described in [RFC5226] โœจ

10.1. PPSPP Message Type Registry ๐ŸŽ€

okay so the registry name is "PPSPP Message Type Registry" ๐Ÿ’… Values are integers in the range 0-255, and the initial assignments and reservations are listed in Table 7. literally just a little number list for message types, iykyk ๐Ÿฆ‹

10.2. PPSPP Option Registry ๐ŸŒธ

the registry name is "PPSPP Option Registry" โœจ Values are integers in the range 0-255, with initial assignments and reservations given in Table 2. same energy as the message type list, just for options ๐Ÿซถ

10.3. PPSPP Version Number Registry ๐Ÿ’œ

registry name: "PPSPP Version Number Registry" ๐Ÿงš Values are integers in the range 0-255, and the initial assignments and reservations live in Table 3. the version numbers get their own little home, bestie ๐Ÿ“

10.4. PPSPP Content Integrity Protection Method Registry ๐Ÿ”ฅ

this one is "PPSPP Content Integrity Protection Method Registry" ๐Ÿ‘‘ Values are integers in the range 0-255, with initial assignments and reservations given in Table 4. these are the integrity protection methods, and they're giving organized ๐Ÿ’–

10.5. PPSPP Merkle Hash Tree Function Registry ๐ŸŒˆ

the registry name is "PPSPP Merkle Hash Tree Function Registry" ๐Ÿฆ‹ Values are integers in the range 0-255, and you'll find the initial assignments and reservations in Table 5. the hash tree functions get their own registry, and I'm obsessed โœจ

10.6. PPSPP Chunk Addressing Method Registry ๐Ÿ’…

last one! the registry name is "PPSPP Chunk Addressing Method Registry" ๐ŸŽ€ Values are integers in the range 0-255, with initial assignments and reservations given in Table 6. the chunk addressing methods are sorted, and that's the whole IANA moment ๐Ÿซถ


11. Manageability Considerations ๐Ÿ› ๏ธโœจ

this section goes over operations and management stuff, following the checklist in [RFC5706], Appendix A. it's giving very organised ops energy ๐Ÿ’…

in this section, "PPSPP client" means a PPSPP peer acting on behalf of an end user who may not have a copy of the content yet ๐Ÿงš and "PPSPP server" means a PPSPP peer that provides the initial copies of the content to the swarm on behalf of a content provider ๐Ÿ’–

11.1. Operations ๐ŸŒธ

11.1.1. Installation and Initial Setup ๐ŸŽ€

a content provider who wants to distribute content with PPSPP should set up at least one PPSPP server ๐Ÿฆ‹ PPSPP servers need access to either some static content or some live audio/video sources. the setup process isn't standardized, to give implementors some flexibility, so do your thing ๐Ÿ’…

the output of that process is a list of metadata records, one for each swarm ๐Ÿ“‹ A metadata record consists of the swarm ID, the chunk size used, the chunk addressing method used, the content integrity protection method used, and the Merkle hash tree function used (if applicable). if automatic content size detection (see Section 5.6) isn't used, the content length is also part of the metadata record for static content. also, the swarm ID already contains the Live Signature Algorithm used, in case of a live stream ๐Ÿ”ฅ

a content provider should also set up a tracking facility for the content, for example by configuring a peer-to-peer streaming protocol tracker [PPSP-TP] or a Distributed Hash Table ๐ŸŒˆ the output of the latter process is a list of transport addresses for the tracking facility โœจ

the list of metadata records for available content, plus the transport address for the tracking facility, can be shared with users in different ways. typically they get published on a website as links ๐Ÿ“ when a user clicks one, the PPSPP client launches, either as a standalone app or by invoking the browser's internal PPSPP protocol handler, as shown in Section 2 ๐Ÿ’œ the clients then use the tracking facility to find the transport address of the PPSPP server(s) and other peers in the swarm, and they run the peer protocol to retrieve and redistribute the content ๐Ÿงš the format of PPSPP URLs should be defined in an extension document. the default protocol options should be used to keep the URLs small ๐Ÿ’…

the minimum info a tracking facility has to return when it's asked for a list of peers in a swarm is this. assuming the communication between the tracking facility and the requester is protected, the facility must at least return, for each peer in the list, its IP address, transport protocol identifier (i.e., UDP), and transport protocol port number ๐Ÿ”ฅ

11.1.2. Migration Path ๐Ÿฆ‹

this document doesn't detail a migration path, because there's no previous standard protocol that provides similar functionality ๐Ÿซถ

11.1.3. Requirements on Other Protocols and Functional Components ๐Ÿ’–

when using the peer-to-peer streaming protocol tracker, PPSPP requires a specific behavior from that protocol for security reasons, as detailed in Section 12.2 ๐Ÿ›ก๏ธ๐Ÿ‘‘

11.1.4. Impact on Network Operation ๐ŸŒ

PPSPP is a peer-to-peer protocol that takes advantage of the fact that content is available from multiple sources, which improves robustness, scalability, and performance ๐ŸŒˆ at the same time, poor choices about which exact sources to use can lead to a bad experience for the end user and high costs for network operators ๐Ÿ˜ญ so PPSPP can benefit from the ALTO protocol to steer peer selection, as described in Section 3.10.1 โœจ

11.1.5. Verifying Correct Operation ๐Ÿ”

PPSPP is operating correctly when all peers get the desired content on time ๐ŸŽ€ so the PPSPP client is the ideal place to check that it's working. however, it isn't feasible to require logging of PPSPP peer behavior in all implementations and deployments, for example for privacy reasons ๐Ÿ™ˆ there are two alternative options:

  • Monitoring the PPSPP servers that initially provide the content, using standard metrics such as bandwidth usage, peer connections, and activity, can help identify trouble, see next section and [RFC2564]. ๐Ÿงš
  • The tracker protocol [PPSP-TP] may be used to gather information about all peers in a swarm, to get a global view of operation, according to PPSP.OAM.REQ-3 in [RFC6972]. ๐Ÿ’œ

basic operation is easy to verify when a tracker and swarm metadata are known: just start a PPSPP download ๐Ÿฆ‹ deep packet inspection of DATA and ACK messages helps establish that actual content transfer is happening and that the chunk availability signaling and integrity checking are working ๐Ÿ”ฅ

11.1.6. Configuration โš™๏ธ

Table 8 shows the PPSPP parameters, their defaults, and where each parameter is defined ๐ŸŒธ for parameters with no default, the table row contains the word "var" and refers to the section that discusses what to think about when choosing a value ๐Ÿ’…

+-------------------------+-----------------------+-----------------+
| Name                    | Default               | Definition      |
+-------------------------+-----------------------+-----------------+
| Chunk Size              | var, 1024 bytes       | Section 8.1     |
|                         | recommended           |                 |
|                         |                       |                 |
| Static Content          | 1 (Merkle Hash Tree)  | Section 7.5     |
| Integrity Protection    |                       |                 |
| Method                  |                       |                 |
|                         |                       |                 |
| Live Content Integrity  | 3 (Unified Merkle     | Section 7.5     |
| Protection Method       | Tree)                 |                 |
|                         |                       |                 |
| Merkle Hash Tree        | 2 (SHA-256)           | Section 7.6     |
| Function                |                       |                 |
|                         |                       |                 |
| Live Signature          | 13 (ECDSAP256SHA256)  | Section 7.7     |
| Algorithm               |                       |                 |
|                         |                       |                 |
| Chunk Addressing Method | 2 (32-bit chunk       | Section 7.8     |
|                         | ranges)               |                 |
|                         |                       |                 |
| Live Discard Window     | var                   | Section 6.2,    |
|                         |                       | Section 7.9     |
|                         |                       |                 |
| NCHUNKS_PER_SIG         | var                   | Section 6.1.2.1 |
|                         |                       |                 |
| Dead peer detection     | No reply in 3 minutes | Section 3.12    |
|                         | + 3 datagrams         |                 |
+-------------------------+-----------------------+-----------------+

                          Table 8: PPSPP Defaults

11.2. Management Considerations ๐Ÿง‘โ€๐Ÿ’ป

the management stuff for PPSPP is very similar to other protocols used for large-scale content distribution, especially HTTP ๐ŸŒˆ how do you manage huge numbers of servers? how do you push new content out to a server farm and allow staged releases? how are faults detected, and how are servers and end-user performance measured? ngl these challenges still have standard solutions in development, so this section can't give a definitive recommendation on how PPSPP should be managed ๐Ÿ˜ญ instead, it describes the standard solutions available right now and assumes a future extension document will provide more complete guidelines ๐Ÿ”ฎ

11.2.1. Management Interoperability and Information ๐Ÿ“Š

like we just said, PPSPP servers that provide initial copies of content are basically like WWW and FTP servers ๐Ÿ“ they can also be deployed in large numbers, so they can benefit from standard management facilities ๐Ÿ’œ therefore, PPSPP servers may implement an SNMP management interface based on the APPLICATION-MIB [RFC2564], where the file object can be used to report on swarms ๐Ÿงš

what's missing is the ability to remove or rate limit specific PPSPP swarms on a server ๐Ÿ˜ค this is like removing or limiting specific virtual servers on a web server. in other words, when multiple pieces of content (swarms, or virtual WWW servers) are multiplexed onto a single server process, you need more fine-grained management of that process, and that functionality is currently missing ๐Ÿซ 

logging is important for PPSPP servers and, depending on the deployment, PPSPP clients too ๐Ÿ“ logging should be done via syslog [RFC5424] ๐Ÿ’…

11.2.2. Fault Management ๐Ÿšจ

the facilities for verifying correct operation and server management (just covered) seem sufficient for PPSPP fault monitoring ๐Ÿ‘‘ this can be supplemented with host resource monitoring [RFC2790] and UDP/IP network monitoring [RFC4113], since PPSPP server failures can generally be traced directly to conditions on the host or network ๐Ÿ–ฅ๏ธ

PPSPP was designed to work in a hostile environment, so many benign faults get handled by the mechanisms used for managing attacks ๐Ÿ›ก๏ธ for example, when a malfunctioning peer starts sending the wrong chunks, the content integrity protection mechanism detects it and another source is sought ๐Ÿฆ‹

11.2.3. Configuration Management ๐ŸŽ€

large-scale deployments may benefit from a standard way of replicating a new piece of content across a set of initial PPSPP servers ๐ŸŒธ this functionality may need controlled releasing, so content only becomes available at a specific point in time (e.g., the release of a movie trailer) ๐Ÿฟ this could be provided via NETCONF [RFC6241], to enable atomic configuration updates over a set of servers โœจ uploading the new content could be one configuration change, and making it available for the public to download could be another ๐Ÿ’–

11.2.4. Accounting Management โœจ

okay so content providers might host PPSPP for different customers and want to bill them, like based on how much bandwidth they used ๐Ÿ’ธ. this is basically the same vibe as billing per virtual server for web servers, so PPSPP can totally benefit from the general standardization work in this area [RFC2975] once that's actually done ๐Ÿซถ

11.2.5. Performance Management ๐Ÿ“ˆ

depending on the deployment scenario, you can use the application performance measurement stuff from [RFC3729] and the related [RFC4150] with PPSPP. no cap, it's giving useful ๐Ÿ”ฅ

also, when the PPSPP tracker protocol is in use, it comes with its own built-in, application-level performance measurement setup for different metrics ๐Ÿงš see PPSP.OAM.REQ-3 in [RFC6972] โœจ

11.2.6. Security Management ๐Ÿ”’

malicious peers should ideally be locked out for the long term ๐Ÿ™…โ€โ™€๏ธ this is mostly about performance, since the protocol is already pretty robust against attacks (more on that in the next section ๐Ÿ’…). Section 12.7 describes a procedure for long-term exclusion.

12. Security Considerations ๐Ÿ›ก๏ธ

okay bestie, this part is SERIOUS ๐Ÿ˜ญ. like any other network protocol, PPSPP has a bunch of common security challenges to deal with. an implementation has to think about buffer overruns, DoS attacks, and manipulation (aka reflection attacks) ๐Ÿ’€. privacy is honestly not really guaranteed, because the user is exposing their IP address to the peers. the one probable exception is when the user is hidden behind a public NAT or proxy ๐Ÿ™ˆ this section digs into the protocol's security considerations in detail, so get ready ๐ŸŽ€

12.1. Security of the Handshake Procedure ๐Ÿค

so the analysis in [RFC5971] is the basis here, and it says PPSPP could get attacked with three kinds of denial-of-service attacks ๐Ÿ˜ฑ

  1. DoS amplification attack: attackers try to use a PPSPP peer to generate more traffic aimed at a victim ๐Ÿ“ฃ
  2. DoS flood attack: attackers try to deny service to other peers by making a PPSPP peer allocate lots of state ๐Ÿ—‘๏ธ
  3. Disrupt service to an individual peer: attackers send bogus messages, like REQUEST and HAVE, that look like they come from victim Peer A to the Peers B1..Bn serving that peer ๐Ÿ‘€ this makes Peer A receive chunks it never asked for, or miss the chunks it did ask for ๐Ÿ’”

the basic plan to protect against all of this is a secure handshake procedure ๐Ÿ” in the UDP encapsulation, the handshake gets secured by using randomly chosen channel IDs, like this. the channel IDs must be generated following the requirements in [RFC4960] (Section 5.1.3) โœจ

when UDP is used, every datagram carrying PPSPP messages is prefixed with a 4-byte channel ID. these are random numbers, set up during the handshake phase like so ๐Ÿงš Peer A starts an exchange with Peer B by sending a datagram with a HANDSHAKE message, prefixed with the channel ID made of all zeros. Peer A's HANDSHAKE includes a randomly chosen channel ID, chanA:

A->B: chan0 + HANDSHAKE(chanA) + ...

when Peer B gets this datagram, it creates some state for Peer A, which at least contains the channel ID chanA ๐Ÿ—‚๏ธ then Peer B sends a response to Peer A: a datagram with a HANDSHAKE message, prefixed with the chanA channel ID. Peer B's HANDSHAKE has a randomly chosen channel ID too, called chanB ๐Ÿฆ‹

B->A: chanA + HANDSHAKE(chanB) + ...

now Peer A knows Peer B is really responding, because it echoed chanA back ๐Ÿฅน so the next datagram Peer A sends can already carry heavy payload, aka a chunk ๐Ÿ“ฆ this next datagram to Peer B gets prefixed with the chanB channel ID. once Peer B receives it, both peers have proof that they're really talking to each other, and the three-way handshake is complete ๐ŸŽ‰ basically, the randomly chosen channel IDs act as tags (cf. [RFC4960] (Section 5.1)) ๐Ÿท๏ธ

A->B: chanB + HAVE + DATA + ...

12.1.1. Protection against Attack 1 ๐Ÿ›ก๏ธ

short version: PPSPP does a so-called return routability check before any heavy payload gets sent ๐Ÿ” this means attack 1 gets fended off. PPSPP doesn't send back much more data than it received, unless it knows it's talking to a live peer ๐Ÿ™Œ an attacker who sends a spoofed HANDSHAKE to Peer B while pretending to be Peer A would also need to intercept the message from Peer B to Peer A, so that Peer B sends heavy payload and that payload goes to the victim. that's assumed to be too hard to be a practical attack ๐Ÿ˜ค

note, the rule is that no heavy payload may be sent until the third datagram ๐Ÿšซ this has implications for PPSPP implementations that use verbose chunk addressing schemes. if an implementation uses large bitmaps to convey chunk availability, those may not be sent by Peer B in the second datagram ๐Ÿง

12.1.2. Protection against Attack 2 ๐Ÿšจ

when Peer B receives the first datagram, it records some state about Peer A โœ๏ธ right now that state is the chanA channel ID, plus the results of processing the other messages in that first datagram. specifically, if Peer A included some HAVE messages, Peer B may add a chunk availability map to Peer A's state. Peer B may also request some chunks from Peer A in the second datagram, and in that case Peer B keeps state about those outgoing requests ๐Ÿ“‹

so honestly, PPSPP is somewhat vulnerable to attack 2 ๐Ÿฅฒ an attacker could send lots of datagrams with HANDSHAKEs and HAVEs and allocate state at the PPSPP peer that way. that's why Peer A MUST respond immediately to the second datagram, if it's still interested in Peer B โšก

why use this slightly vulnerable three-way handshake instead of the safer handshake procedure from Stream Control Transmission Protocol (SCTP) [RFC4960] (Section 5.1)? speed! โฉ it gives the user a quicker response time. in the SCTP procedure, Peers A and B can't request chunks until datagrams 3 and 4 respectively, whereas in the proposed procedure it's datagrams 2 and 1. so the user waits less time between starting the video stream and seeing the first images ๐ŸŽฌโœจ

12.1.3. Protection against Attack 3 ๐Ÿ•ต๏ธโ€โ™€๏ธ

in general, channel IDs serve to authenticate a peer ๐Ÿชช so to pull off an attack, a malicious Peer T would need to eavesdrop on conversations between victim A and a benign Peer B, to get the channel ID Peer B assigned to Peer A, chanB ๐Ÿ‘€ on top of that, attacker Peer T would need to be able to spoof messages like REQUEST and HAVE coming from Peer A, to make Peer B send heavy DATA messages to Peer A, or to stop Peer B from sending them, respectively ๐Ÿ˜ฌ

the capability to eavesdrop isn't common, so channel IDs will be enough protection in most cases ๐Ÿ’– if not, point-to-point encryption of traffic should be used, see below ๐Ÿ”

12.2. Secure Peer Address Exchange ๐Ÿ—บ๏ธ

as described in Section 3.10, Peer A can send Peer-Exchange messages PEX_RES to Peer B. these contain the IP address and port of other peers that are supposedly also in the current swarm ๐Ÿ the strength of this mechanism is that it allows decentralized tracking. after an initial bootstrap, no central tracker is needed ๐Ÿ™Œ the vulnerability of this mechanism (and DHTs) is that malicious peers can abuse it for an Amplification attack ๐Ÿ˜ˆ

here's the sketchy part: a malicious Peer T could send PEX_RES messages to well-behaved Peer A with the addresses of Peers B1..Bn. when Peer A gets those, it could send a HANDSHAKE to all of them. so in the worst case, a single datagram results in N datagrams ๐Ÿ’ฅ how bad it actually gets depends on what Peer A does. if Peer A already has sufficient connections, it may not connect to the offered ones at all ๐Ÿ˜Œ but if it's a fresh peer, it may connect to all of them directly ๐Ÿ˜ฑ

PEX can also be used in Eclipse attacks [ECLIPSE], where malicious peers try to isolate a particular peer so it only interacts with malicious peers ๐ŸŒ‘ let's split this into two specific attacks:

  • E1. Malicious peers try to eclipse the single injector in live streaming.
  • E2. Malicious peers try to eclipse a specific consumer peer.

attack E1 has the biggest impact on the system, since it would disrupt all peers ๐Ÿšจ

12.2.1. Protection against the Amplification Attack ๐Ÿ”

if peer addresses are relatively stable, you can get strong protection against this attack by using public key cryptography and certification ๐Ÿ’Ž specifically, a PEX_REScert message carries swarm-membership certificates instead of IP address and port. a membership certificate for Peer B states that Peer B at address (ipB,portB) is part of Swarm S at Time T, and it's cryptographically signed โœ๏ธ the receiver, Peer A, can check the certificate for a valid signature, the right swarm, and liveliness, and only then consider contacting Peer B ๐Ÿซก these swarm-membership certificates correspond to signed node descriptors in secure decentralized peer sampling services [SPS] ๐Ÿงฉ

there are several possible designs for the security environment around these membership certificates. there are different options for who signs the membership certificates and how public keys get distributed ๐Ÿ”‘ as an example, we describe a design where the peer-to-peer streaming protocol tracker acts as the certification authority ๐Ÿ›๏ธ

12.2.2. Example: Tracker as Certification Authority ๐Ÿ›๏ธ

here's how it goes ๐Ÿ’… Peer A wants to join Swarm S, so it sends a certificate request message to Tracker X for that swarm. once the tracker gets it, it creates a membership certificate from the request, with Swarm ID S, a Timestamp T, and the external IP and port it received the message from. it signs this with the tracker's private key, and the certificate goes back to Peer A ๐Ÿ“œ

then Peer A includes this certificate whenever it sends a PEX_REScert to Peer B. Peer B verifies it against the tracker public key. this tracker public key should be part of the swarm's metadata, which Peer B got from a trusted source ๐Ÿ” after that, Peer B can pass Peer A's member certificate along to other peers in PEX_REScert messages ๐Ÿ”

Peer A can send the certification request when it first contacts the tracker, or at a later time โฐ the responses the tracker sends could also contain membership certificates instead of plain addresses, so they can be gossiped securely too ๐Ÿ—ฃ๏ธ

we assume the tracker is protected against attacks and does a return routability check. the check makes sure malicious peers can't get a certificate for a random host, only for hosts where they can eavesdrop on incoming traffic ๐Ÿ‘๏ธ

the load on the tracker depends on churn and on how long a certificate lives. certificates can be pretty long lived, since the main goal of membership certificates is to stop malicious Peer T from making good Peer A contact random hosts ๐Ÿ™…โ€โ™€๏ธ the freshness of the timestamp just adds extra protection on top of that goal. it protects against malicious hosts making a good Peer A contact hosts that were previously in the swarm ๐Ÿ•ฐ๏ธ

the membership certificate mechanism can itself be used for a kind of amplification attack against good peers ๐Ÿ˜ค malicious Peer T can make Peer A spend some CPU verifying the signatures on the membership certificates Peer T sends. to counter this, Peer A SHOULD check a few of the certificates sent and discard the rest if they're defective ๐Ÿงน

the same membership certificates described above can be registered in a Distributed Hash Table that has been secured against the well-known DHT specific attacks [SECDHTS] ๐Ÿ—„๏ธ

note that this scheme doesn't work for peers behind a symmetric Network Address Translator, but normal tracker registration doesn't work for them either ๐Ÿซ 

12.2.3. Protection against Eclipse Attacks โœจ

okay so before we can even talk about Eclipse attacks, we need to figure out the security vibes of the central tracker ๐Ÿ•ต๏ธโ€โ™€๏ธ. A tracker is vulnerable to Amplification attacks too, bestie ๐Ÿ˜ญ. A malicious Peer T could register a victim Peer B with the tracker, and then a bunch of peers joining the swarm would all contact Peer B. Trackers can also be used for Eclipse attacks. If lots of malicious peers register themselves at the tracker, the percentage of bad peers in the returned address list could get really high ๐Ÿ’€. We're leaving the protection of the tracker to the peer-to-peer streaming protocol tracker specification [PPSP-TP], so for the rest of this discussion we assume it returns a true random sample of the actual swarm membership (achieved via Sybil attack protection) ๐Ÿงš. That means if 50% of the peers are bad, you'll still get 50% good addresses from the tracker. iykyk ๐Ÿ’œ

Attack E1 on PEX can be fended off by letting live injectors disable PEX ๐Ÿ™…โ€โ™€๏ธ, or at least letting live injectors make sure part of their connections go to peers whose addresses came from the trusted tracker. The same measures defend against attack E2 on PEX too ๐ŸŽ€. They can also be used dynamically. When the current set of Peers B that Peer A is connected to isn't giving good quality of service, Peer A can contact the tracker to find new candidates. main character energy ๐Ÿ‘‘

12.3. Support for Closed Swarms ๐Ÿ”

Regarding PPSP.SEC.REQ-1 in [RFC6972], the Closed Swarms [CLOSED] and Enhanced Closed Swarms [ECS] mechanisms give you swarm-level access control ๐Ÿฆ‹. The basic idea is that a peer cannot download from another peer unless it shows a Proof-of-Access. Enhanced Closed Swarms level up the original Closed Swarms by adding on-the-wire encryption against man-in-the-middle attacks and more flexible access control rules ๐Ÿ”ฅ.

The exact mapping of ECS to PPSPP is defined in [ECS-protocol] ๐Ÿ“–.

12.4. Confidentiality of Streamed Content ๐Ÿคซ

Regarding PPSP.SEC.REQ-1 in [RFC6972], no extra mechanism is needed for confidentiality in PPSPP ๐Ÿ™Œ. A content publisher who wants confidentiality should just distribute content in ciphertext, and/or in a format where Digital Rights Management (DRM) techniques have been applied ๐Ÿ’…. In that case, it's assumed a higher layer handles key management out-of-band. Or, if you want pure point-to-point encryption of content and traffic, you can use the proposed Closed Swarms access control mechanism, DTLS [RFC6347], or IPsec [RFC4301] ๐ŸŽ€.

When transmitting over DTLS, PPSPP can grab the PMTU estimate maintained by the IP layer to figure out how much payload fits in a single datagram without fragmentation ([RFC6347], Section 4.1.1.1) ๐Ÿง . If the PMTU changes and the chunk size gets too large to fit in a single datagram, PPSPP can choose to allow fragmentation by clearing the Don't Fragment (DF) bit ๐Ÿซฃ. The other option is for the content publisher to use smaller chunks and pack multiple into the same datagram when the MTU allows โœจ.

12.5. Strength of the Hash Function for Merkle Hash Trees ๐ŸŒณ

Implementations MUST support SHA-1 as the hash function for content integrity protection via Merkle hash trees ๐Ÿšจ. Content providers may prefer SHA-1 over stronger hash functions because it reduces on-the-wire overhead ๐Ÿ“. So honestly, it's a trade-off between performance and security โš–๏ธ. The security considerations for SHA-1 are discussed in [RFC6194].

Also, the hash function is used in a hash tree, which makes collisions way harder to create ๐Ÿงฉ. If attackers manage to find a collision for a hash, it can only replace one chunk, so the impact is limited ๐Ÿฅน. If fixed-size chunks are used, the collision even has to be the same size as the original chunk. For hashes higher up in the tree, a collision must be a concatenation of two hashes. Basically, finding collisions that fit the hash tree is generally harder than finding regular collisions ๐Ÿ’ช.

12.6. Limit Potential Damage and Resource Exhaustion by Bad or Broken Peers ๐Ÿšง

Regarding PPSP.SEC.REQ-2 in [RFC6972], this section analyzes the potential damage a malicious peer can do with each message in the protocol, and how the protocol (implementation) prevents it ๐Ÿ›ก๏ธ.

12.6.1. HANDSHAKE ๐Ÿค

  • Secured against DoS Amplification attacks as described in Section 12.1 ๐Ÿ”’.
  • Threat HS.1: An Eclipse attack where Peers T1..Tn fill all connection slots of Peer A by initiating the connection to Peer A ๐Ÿ˜ฑ.

Solution: Peer A must not let other peers fill all its available connection slots. Peer A must initiate connections itself too, to prevent isolation ๐Ÿƒโ€โ™€๏ธ.

12.6.2. HAVE ๐Ÿ™‹โ€โ™€๏ธ

  • Threat HAVE.1: Malicious Peer T can claim to have content it doesn't have. Then Peer T won't respond to requests ๐Ÿ™„.

Solution: Peer A will consider Peer T a slow peer and won't ask it again ๐Ÿข.

  • Threat HAVE.2: Malicious Peer T can claim not to have content. Hence, it won't contribute ๐Ÿ˜ค.

Solution: Peer and chunk selection algorithms external to the protocol will implement fairness and provide sharing incentives ๐ŸŽ.

12.6.3. DATA ๐Ÿ“ฆ

  • Threat DATA.1: Peer T sends bogus chunks ๐Ÿ—‘๏ธ.

Solution: The content integrity protection schemes defend against this ๐Ÿซถ.

  • Threat DATA.2: Peer T sends Peer A unrequested chunks ๐Ÿ™….

To protect against this threat, we need network-level DoS prevention ๐Ÿงฑ.

12.6.4. ACK โœ…

  • Threat ACK.1: Peer T acknowledges the wrong chunks ๐Ÿคก.

Solution: Peer A will detect inconsistencies with the data it sent to Peer T ๐Ÿ”.

  • Threat ACK.2: Peer T modifies the timestamp in an ACK to Peer A, which is used for time-based congestion control โฑ๏ธ.

Solution: In theory, by decreasing the timestamp, Peer T could fake that there's no congestion when there actually is, making Peer A send more data than it should ๐Ÿ˜ฌ. [RFC6817] does not list this as a security consideration. Possibly, this attack can be detected by the large resulting asymmetry between round-trip time and measured one-way delay ๐Ÿ•ต๏ธโ€โ™€๏ธ.

12.6.5. INTEGRITY and SIGNED_INTEGRITY ๐Ÿงพ

  • Threat INTEGRITY.1: An amplification attack where Peer T sends bogus INTEGRITY or SIGNED_INTEGRITY messages, causing Peer A to check hashes or signatures and waste CPU for nothing ๐Ÿ”ฅ๐Ÿ’ป.

Solution: If the hashes or signatures don't check out, Peer A will stop asking Peer T because of the atomic datagram principle and the content integrity protection. Subsequent unsolicited traffic from Peer T will be ignored ๐Ÿšซ.

  • Threat INTEGRITY.2: An attack where Peer T sends old SIGNED_INTEGRITY messages in the Unified Merkle Tree scheme, trying to make Peer A tune in at a past point in the live stream ๐Ÿ“ผ.

Solution: The timestamp in the SIGNED_INTEGRITY message protects against such replays โฐ. Subsequent traffic from Peer T will be ignored ๐Ÿ™…โ€โ™€๏ธ.

12.6.6. REQUEST ๐Ÿ“ฅ

  • Threat REQUEST.1: Peer T could request lots from Peer A, leaving Peer A without resources for others ๐Ÿ˜ค.

Solution: A limit is imposed on the upload capacity a single peer can consume, for example by using an upload bandwidth scheduler that takes into account the needs of multiple peers ๐Ÿ“Š. A natural upper limit for this upload quotum is the bitrate of the content, taking into account that this may be variable ๐ŸŽš๏ธ.

12.6.7. CANCEL โŒ

  • Threat CANCEL.1: Peer T sends CANCEL messages for content it never requested to Peer A ๐Ÿคจ.

Solution: Peer A will detect the inconsistency of the messages and ignore them. Note that CANCEL messages may be received unexpectedly when a transport is used where REQUEST messages may be lost or reordered with respect to the subsequent CANCELs ๐ŸŒ€.

12.6.8. CHOKE ๐Ÿšซ

  • Threat CHOKE.1: Peer T sends REQUEST messages after Peer A has sent Peer B a CHOKE message ๐Ÿ™ƒ.

Solution: Peer A will just discard the unwanted REQUESTs and resend the CHOKE, assuming it got lost ๐Ÿ“จ.

12.6.9. UNCHOKE ๐ŸŸข

  • Threat UNCHOKE.1: Peer T sends an UNCHOKE message to Peer A without having sent a CHOKE message before ๐Ÿซ .

Solution: Peer A can easily detect this violation of protocol state and ignore it. Note this can also happen due to loss of a CHOKE message sent by a benign peer ๐Ÿซถ.

  • Threat UNCHOKE.2: Peer T sends an UNCHOKE message to Peer A, but then doesn't respond to its REQUESTs ๐Ÿ‘ป.

Solution: Peer A will consider Peer T a slow peer and won't ask it again ๐ŸŒ.

12.6.10. PEX_RES ๐Ÿงƒ

  • Secured against amplification and Eclipse attacks as described in Section 12.2 ๐Ÿ›ก๏ธโœจ.

12.6.11. Unsolicited Messages in General ๐Ÿ“ฌ

  • Threat: Peer T could send a spoofed PEX_REQ or REQUEST from Peer B to Peer A, causing Peer A to send a PEX_RES/DATA to Peer B ๐ŸŽญ.

Solution: The message from Peer T won't be accepted unless Peer T does a handshake first. If it does, the reply goes to Peer T, not to victim Peer B ๐Ÿ’….

12.7. Exclude Bad or Broken Peers ๐Ÿšช

This section is regarding PPSP.SEC.REQ-2 in [RFC6972]. A receiving peer can detect malicious or faulty senders as just described, and then ignore them ๐Ÿ‘€. However, kicking a bad peer out of the system completely is complex ๐Ÿ˜ฉ. Random monitoring by trusted peers that blacklist bad peers, as described in [DETMAL], is one option ๐Ÿ•ถ๏ธ. This mechanism does require extra capacity to run such trusted peers, which must be indistinguishable from regular peers, and it requires a solution for timely, scalable distribution of this blacklist to peers ๐Ÿ“ข.

13. References ๐Ÿ“š

13.1. Normative References ๐Ÿ“šโœจ

okay so these are the references the spec needs you to have read, bestie ๐Ÿซถ no skipping!! ๐Ÿ’… (the rest of the list is the optional reading, iykyk ๐Ÿ™ˆ)

[CCITT.X690.2002]
           International Telephone and Telegraph Consultative
           Committee, "ASN.1 encoding rules: Specification of basic
           encoding Rules (BER), Canonical encoding rules (CER) and
           Distinguished encoding rules (DER)", CCITT Recommendation
           X.690, July 2002.

[FIPS180-4]
           National Institute of Standards and Technology,
           Information Technology Laboratory, "Federal Information
           Processing Standards: Secure Hash Standard (SHS)", FIPS
           PUB 180-4, March 2012.

[IANADNSSECALGNUM]
           IANA, "Domain Name System Security (DNSSEC) Algorithm
           Numbers", March 2014,
           <http://www.iana.org/assignments/dns-sec-alg-numbers>.

[RFC1918]  Rekhter, Y., Moskowitz, B., Karrenberg, D., J. de Groot,
           G., and E. Lear, "Address Allocation for Private
           Internets", BCP 5, RFC 1918, DOI 10.17487/RFC1918,
           February 1996, <http://www.rfc-editor.org/info/rfc1918>.

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

[RFC3110]  Eastlake 3rd, D., "RSA/SHA-1 SIGs and RSA KEYs in the
           Domain Name System (DNS)", RFC 3110, DOI 10.17487/RFC3110,
           May 2001, <http://www.rfc-editor.org/info/rfc3110>.

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

[RFC4034]  Arends, R., Austein, R., Larson, M., Massey, D., and S.
           Rose, "Resource Records for the DNS Security Extensions",
           RFC 4034, DOI 10.17487/RFC4034, March 2005,
           <http://www.rfc-editor.org/info/rfc4034>.

[RFC4291]  Hinden, R. and S. Deering, "IP Version 6 Addressing
           Architecture", RFC 4291, DOI 10.17487/RFC4291, February
           2006, <http://www.rfc-editor.org/info/rfc4291>.

[RFC5280]  Cooper, D., Santesson, S., Farrell, S., Boeyen, S.,
           Housley, R., and W. Polk, "Internet X.509 Public Key
           Infrastructure Certificate and Certificate Revocation List
           (CRL) Profile", RFC 5280, DOI 10.17487/RFC5280, May 2008,
           <http://www.rfc-editor.org/info/rfc5280>.

[RFC5702]  Jansen, J., "Use of SHA-2 Algorithms with RSA in DNSKEY
           and RRSIG Resource Records for DNSSEC", RFC 5702,
           DOI 10.17487/RFC5702, October 2009,
           <http://www.rfc-editor.org/info/rfc5702>.

[RFC5905]  Mills, D., Martin, J., Ed., Burbank, J., and W. Kasch,
           "Network Time Protocol Version 4: Protocol and Algorithms
           Specification", RFC 5905, DOI 10.17487/RFC5905, June 2010,
           <http://www.rfc-editor.org/info/rfc5905>.

[RFC6605]  Hoffman, P. and W. Wijngaards, "Elliptic Curve Digital
           Signature Algorithm (DSA) for DNSSEC", RFC 6605,
           DOI 10.17487/RFC6605, April 2012,
           <http://www.rfc-editor.org/info/rfc6605>.

[RFC6817]  Shalunov, S., Hazel, G., Iyengar, J., and M. Kuehlewind,
           "Low Extra Delay Background Transport (LEDBAT)", RFC 6817,
           DOI 10.17487/RFC6817, December 2012,
           <http://www.rfc-editor.org/info/rfc6817>.
these are the big-deal refs ๐Ÿ”ฅ the bits you really need to know before you touch the protocol ๐Ÿ’œ

13.2. Informative References โœจ

okay bestie, these are the extra reads ๐Ÿ“š๐Ÿซถ the spec doesn't need them to work, but they're where all the good background lives ๐Ÿฆ‹

  • [ABMRKL] Bakker, A., "Merkle hash torrent extension", BitTorrent Enhancement Proposal 30, March 2009, <http://bittorrent.org/beps/bep_0030.html>.
  • [BINMAP] Grishchenko, V. and J. Pouwelse, "Binmaps: Hybridizing Bitmaps and Binary Trees", Delft University of Technology Parallel and Distributed Systems Report Series, Report number PDS-2011-005, ISSN 1387-2109, April 2009.
  • [BITOS] Vlavianos, A., Iliofotou, M., Mathieu, F., and M. Faloutsos, "BiToS: Enhancing BitTorrent for Supporting Streaming Applications", IEEE INFOCOM Global Internet Symposium, Barcelona, Spain, April 2006.
  • [BITTORRENT] Cohen, B., "The BitTorrent Protocol Specification", BitTorrent Enhancement Proposal 3, February 2008, <http://bittorrent.org/beps/bep_0003.html>.
  • [CLOSED] Borch, N., Mitchell, K., Arntzen, I., and D. Gabrijelcic, "Access Control to BitTorrent Swarms Using Closed Swarms", ACM workshop on Advanced Video Streaming Techniques for Peer-to-Peer Networks and Social Networking (AVSTP2P '10), Florence, Italy, October 2010, <http://doi.acm.org/10.1145/1877891.1877898>.
  • [DETMAL] Shetty, S., Galdames, P., Tavanapong, W., and Ying. Cai, "Detecting Malicious Peers in Overlay Multicast Streaming", IEEE Conference on Local Computer Networks, (LCN'06), Tampa, FL, USA, November 2006.
  • [ECLIPSE] Sit, E. and R. Morris, "Security Considerations for Peer-to-Peer Distributed Hash Tables", IPTPS '01: Revised Papers from the First International Workshop on Peer-to-Peer Systems, pp. 261-269, Springer-Verlag, 2002.
  • [ECS] Jovanovikj, V., Gabrijelcic, D., and T. Klobucar, "Access Control in BitTorrent P2P Networks Using the Enhanced Closed Swarms Protocol", International Conference on Emerging Security Information, Systems and Technologies (SECURWARE 2011), pp. 97-102, Nice, France, August 2011.
  • [ECS-protocol] Gabrijelcic, D., "Enhanced Closed Swarm protocol", Work in Progress, draft-ppsp-gabrijelcic-ecs-01, June 2013.
  • [EPLIVEPERF] Bonald, T., Massoulie, L., Mathieu, F., Perino, D., and A. Twigg, "Epidemic live streaming: optimal performance trade-offs", Proceedings of the 2008 ACM SIGMETRICS International Conference on Measurement and Modeling of Computer Systems, Annapolis, MD, USA, June 2008.
  • [GIVE2GET] Mol, J., Pouwelse, J., Meulpolder, M., Epema, D., and H. Sips, "Give-to-Get: Free-riding-resilient Video-on-Demand in P2P Systems", Proceedings Multimedia Computing and Networking conference (Proceedings of SPIE, Vol. 6818), San Jose, CA, USA, January 2008.
  • [HAC01] Menezes, A., van Oorschot, P., and S. Vanstone, "Handbook of Applied Cryptography", CRC Press, (Fifth Printing, August 2001), October 1996.
  • [JIM11] Jimenez, R., Osmani, F., and B. Knutsson, "Sub-Second Lookups on a Large-Scale Kademlia-Based Overlay", IEEE International Conference on Peer-to-Peer Computing (P2P'11), Kyoto, Japan, August 2011.
  • [LBT] Rossi, D., Testa, C., Valenti, S., and L. Muscariello, "LEDBAT: the new BitTorrent congestion control protocol", Computer Communications and Networks (ICCCN), Zurich, Switzerland, August 2010.
  • [LCOMPL] Testa, C. and D. Rossi, "On the impact of uTP on BitTorrent completion time", IEEE International Conference on Peer-to-Peer Computing (P2P'11), Kyoto, Japan, August 2011.
  • [MERKLE] Merkle, R., "Secrecy, Authentication, and Public Key Systems", Ph.D. thesis, Dept. of Electrical Engineering, Stanford University, CA, USA, pp 40-45, 1979.
  • [P2PWIKI] Bakker, A., Petrocco, R., Dale, M., Gerber, J., Grishchenko, V., Rabaioli, D., and J. Pouwelse, "Online video using BitTorrent and HTML5 applied to Wikipedia", IEEE International Conference on Peer-to-Peer Computing (P2P'10), Delft, The Netherlands, August 2010.
  • [POLLIVE] Dhungel, P., Hei, Xiaojun., Ross, K., and N. Saxena, "Pollution in P2P Live Video Streaming", International Journal of Computer Networks & Communications (IJCNC) Vol. 1, No. 2, Jul 2009.
  • [PPSP-TP] Cruz, R., Nunes, M., Yingjie, G., Xia, J., Huang, R., Taveira, J., and D. Lingli, "PPSP Tracker Protocol-Base Protocol (PPSP-TP/1.0)", Work in Progress, draft-ietf-ppsp-base-tracker-protocol-09, March 2015.
  • [PPSPPERF] Petrocco, R., Pouwelse, J., and D. Epema, "Performance Analysis of the Libswift P2P Streaming Protocol", IEEE International Conference on Peer-to-Peer Computing (P2P'12), Tarragona, Spain, September 2012.
  • [RFC2564] Kalbfleisch, C., Krupczak, C., Presuhn, R., and J. Saperia, "Application Management MIB", RFC 2564, DOI 10.17487/RFC2564, May 1999, <http://www.rfc-editor.org/info/rfc2564>.
  • [RFC2790] Waldbusser, S. and P. Grillo, "Host Resources MIB", RFC 2790, DOI 10.17487/RFC2790, March 2000, <http://www.rfc-editor.org/info/rfc2790>.
  • [RFC2975] Aboba, B., Arkko, J., and D. Harrington, "Introduction to Accounting Management", RFC 2975, DOI 10.17487/RFC2975, October 2000, <http://www.rfc-editor.org/info/rfc2975>.
  • [RFC3365] Schiller, J., "Strong Security Requirements for Internet Engineering Task Force Standard Protocols", BCP 61, RFC 3365, DOI 10.17487/RFC3365, August 2002, <http://www.rfc-editor.org/info/rfc3365>.
  • [RFC3729] Waldbusser, S., "Application Performance Measurement MIB", RFC 3729, DOI 10.17487/RFC3729, March 2004, <http://www.rfc-editor.org/info/rfc3729>.
  • [RFC4113] Fenner, B. and J. Flick, "Management Information Base for the User Datagram Protocol (UDP)", RFC 4113, DOI 10.17487/RFC4113, June 2005, <http://www.rfc-editor.org/info/rfc4113>.
  • [RFC4150] Dietz, R. and R. Cole, "Transport Performance Metrics MIB", RFC 4150, DOI 10.17487/RFC4150, August 2005, <http://www.rfc-editor.org/info/rfc4150>.
  • [RFC4193] Hinden, R. and B. Haberman, "Unique Local IPv6 Unicast Addresses", RFC 4193, DOI 10.17487/RFC4193, October 2005, <http://www.rfc-editor.org/info/rfc4193>.
  • [RFC4301] Kent, S. and K. Seo, "Security Architecture for the Internet Protocol", RFC 4301, DOI 10.17487/RFC4301, December 2005, <http://www.rfc-editor.org/info/rfc4301>.
  • [RFC4821] Mathis, M. and J. Heffner, "Packetization Layer Path MTU Discovery", RFC 4821, DOI 10.17487/RFC4821, March 2007, <http://www.rfc-editor.org/info/rfc4821>.
  • [RFC4960] Stewart, R., Ed., "Stream Control Transmission Protocol", RFC 4960, DOI 10.17487/RFC4960, September 2007, <http://www.rfc-editor.org/info/rfc4960>.
  • [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 5226, DOI 10.17487/RFC5226, May 2008, <http://www.rfc-editor.org/info/rfc5226>.
  • [RFC5389] Rosenberg, J., Mahy, R., Matthews, P., and D. Wing, "Session Traversal Utilities for NAT (STUN)", RFC 5389, DOI 10.17487/RFC5389, October 2008, <http://www.rfc-editor.org/info/rfc5389>.
  • [RFC5424] Gerhards, R., "The Syslog Protocol", RFC 5424, DOI 10.17487/RFC5424, March 2009, <http://www.rfc-editor.org/info/rfc5424>.
  • [RFC5706] Harrington, D., "Guidelines for Considering Operations and Management of New Protocols and Protocol Extensions", RFC 5706, DOI 10.17487/RFC5706, November 2009, <http://www.rfc-editor.org/info/rfc5706>.
  • [RFC5971] Schulzrinne, H. and R. Hancock, "GIST: General Internet Signalling Transport", RFC 5971, DOI 10.17487/RFC5971, October 2010, <http://www.rfc-editor.org/info/rfc5971>.
  • [RFC6194] Polk, T., Chen, L., Turner, S., and P. Hoffman, "Security Considerations for the SHA-0 and SHA-1 Message-Digest Algorithms", RFC 6194, DOI 10.17487/RFC6194, March 2011, <http://www.rfc-editor.org/info/rfc6194>.
  • [RFC6241] Enns, R., Ed., Bjorklund, M., Ed., Schoenwaelder, J., Ed., and A. Bierman, Ed., "Network Configuration Protocol (NETCONF)", RFC 6241, DOI 10.17487/RFC6241, June 2011, <http://www.rfc-editor.org/info/rfc6241>.
  • [RFC6347] Rescorla, E. and N. Modadugu, "Datagram Transport Layer Security Version 1.2", RFC 6347, DOI 10.17487/RFC6347, January 2012, <http://www.rfc-editor.org/info/rfc6347>.
  • [RFC6709] Carpenter, B., Aboba, B., Ed., and S. Cheshire, "Design Considerations for Protocol Extensions", RFC 6709, DOI 10.17487/RFC6709, September 2012, <http://www.rfc-editor.org/info/rfc6709>.
  • [RFC6972] Zhang, Y. and N. Zong, "Problem Statement and Requirements of the Peer-to-Peer Streaming Protocol (PPSP)", RFC 6972, DOI 10.17487/RFC6972, July 2013, <http://www.rfc-editor.org/info/rfc6972>.
  • [RFC7285] Alimi, R., Ed., Penno, R., Ed., Yang, Y., Ed., Kiesel, S., Previdi, S., Roome, W., Shalunov, S., and R. Woundy, "Application-Layer Traffic Optimization (ALTO) Protocol", RFC 7285, DOI 10.17487/RFC7285, September 2014, <http://www.rfc-editor.org/info/rfc7285>.
  • [SECDHTS] Urdaneta, G., Pierre, G., and M. van Steen, "A Survey of DHT Security Techniques", ACM Computing Surveys, vol. 43(2), January 2011.
  • [SIGMCAST] Wong, C. and S. Lam, "Digital Signatures for Flows and Multicasts", IEEE/ACM Transactions on Networking 7(4), pp. 502-513, August 1999.
  • [SPS] Jesi, G., Montresor, A., and M. van Steen, "Secure Peer Sampling", Computer Networks vol. 54(12), pp. 2086-2098, Elsevier, August 2010.
  • [SWIFTIMPL] Grishchenko, V., Paananen, J., Pronchenkov, A., Bakker, A., and R. Petrocco, "Swift reference implementation", 2015, <https://github.com/libswift/libswift>.
  • [TIT4TAT] Cohen, B., "Incentives Build Robustness in BitTorrent", 1st Workshop on Economics of Peer-to-Peer Systems, Berkeley, CA, USA, May 2003.

Acknowledgements ๐ŸŽ€

okay bestie, this is the thank-you section and it's giving gratitude ๐Ÿซถ Arno Bakker, Riccardo Petrocco, and Victor Grishchenko were partially supported by the P2P-Next project, which is a research project at http://www.p2p-next.org/. It was funded by the European Community under its 7th Framework Programme (grant agreement no. 216217) โœจ The views and conclusions in the document are the authors' own. They should not be read as the official policies or endorsements, expressed or implied, of the P2P-Next project or the European Commission ๐Ÿ’…

PPSPP was designed by Victor Grishchenko at Technische Universiteit Delft, under supervision of Johan Pouwelse ๐Ÿงš The authors also want to thank the chairs (Martin Stiemerling, Yunfei Zhang, Stefano Previdi, and Ning Zong) and the members of the IETF PPSP working group. They also shout out Mihai Capota, Raul Jimenez, Flutra Osmani, and Raynor Vliegendhart for their contributions to this document ๐ŸŒˆ literally everyone who helped, iconic ๐Ÿ‘‘


Authors' Addresses ๐Ÿฆ‹

Here are the three authors and where to find them ๐Ÿ’œ

Arno Bakker
Vrije Universiteit Amsterdam
De Boelelaan 1081
Amsterdam  1081HV
The Netherlands

Email: arno@cs.vu.nl


Riccardo Petrocco
Technische Universiteit Delft
Mekelweg 4
Delft  2628CD
The Netherlands

Email: r.petrocco@gmail.com


Victor Grishchenko
Technische Universiteit Delft
Mekelweg 4
Delft  2628CD
The Netherlands

Email: victor.grishchenko@gmail.com
๐Ÿ“ฆ where this page comes from