Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run Scan×
Skip to content
World desk9 min

How to Stream Encrypted Files Peer-to-Peer with WebRTC DataChannels and Web Crypto in TypeScript

A practical architecture for sending encrypted file chunks peer-to-peer with WebRTC DataChannels and Web Crypto, including framing, unique AES-GCM IVs, backpressure, and reconstruction.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A browser can send an encrypted file peer-to-peer by reading it in bounded chunks, encrypting each chunk with Web Crypto, and sending framed binary messages over an RTCDataChannel. The sender and receiver still need an application protocol for signaling, key establishment, chunk identifiers, backpressure, authentication, recovery, and saving the reconstructed file. WebRTC’s DTLS protection encrypts channel traffic in transit; it does not define that file-level key protocol.

How does an encrypted WebRTC file transfer work?

An RTCDataChannel is a bidirectional channel associated with an RTCPeerConnection. It can carry arbitrary data, including binary file chunks. A practical transfer keeps several responsibilities separate:

As an Amazon Associate I earn from qualifying purchases.

  1. Connect: establish the peer connection and exchange the signaling information needed to open a data channel.
  2. Choose a key: obtain or establish an appropriate CryptoKey through a separately designed, authenticated key process.
  3. Read a bounded part: use a file slice rather than loading the entire file into memory.
  4. Encrypt and frame: encrypt that part, then include the metadata the receiver needs to identify and authenticate it.
  5. Send under backpressure: send only when the channel’s outgoing queue has room and the message fits the peer’s limit.
  6. Receive and validate: check framing and limits, authenticate and decrypt each chunk, then verify the expected set and order of chunks.
  7. Reconstruct and save: assemble or stream validated plaintext to the chosen destination, handling cancellation and save failures.

The signaling service is separate from the data channel. It may help peers find each other and exchange connection setup information; that does not mean it relays the file contents. The design must say what signaling carries and what parties can see it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What does WebRTC encryption protect?

WebRTC data-channel traffic is encrypted in transit with DTLS. MDN’s “Using WebRTC data channels” describes WebRTC data as encrypted. That protection addresses the transport path; it does not establish who is authorized to receive a file, how the peers authenticate one another, or how an application’s file key is shared.

Use application-level encryption when the file protocol requires a separate content-security boundary. Web Crypto supplies cryptographic primitives, not a complete identity, authorization, trust, or key-exchange system. The application must decide how a receiver obtains the right key and how the sender knows it belongs to the intended recipient. Do not treat possession of a data channel, or an unauthenticated key delivered through signaling, as proof of recipient identity.

How should AES-GCM protect file chunks?

AES-GCM can encrypt each bounded plaintext chunk and authenticate it. Each encryption operation needs an initialization vector (IV) that is unique for every use of the same key. The IV is not secret: MDN’s AesGcmParams documentation says it can be sent in the clear alongside the encrypted message. Uniqueness, rather than secrecy, is the critical requirement.

Assign a unique IV to every operation

Make IV allocation an explicit part of the protocol. For example, a design may allocate non-overlapping IVs for chunk operations under a key and maintain a counter that cannot reset or collide while that key remains in use. If transfers can share a key, IV allocation must cover all of them, not just the chunks within one file. A new key does not make accidental reuse under the old key safe. Document how the sender prevents reuse across retries, resumed transfers, parallel senders, and application restarts; never encrypt different plaintext with the same key-and-IV pair.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Retries need particular care. Re-sending the exact same authenticated ciphertext is different from encrypting new or changed plaintext again with a previously used IV. A protocol should retain the original ciphertext for retransmission or allocate a fresh IV for a new encryption operation.

Authenticate the metadata that gives a chunk meaning

Frame each encrypted chunk with enough information to associate it with a transfer and position, such as a transfer identifier, chunk index, plaintext length, and IV. Where appropriate, encode the security-relevant framing fields as AES-GCM additional authenticated data (AAD). The receiver must use the exact same AAD bytes during decryption. This binds those fields to the ciphertext, so an attacker cannot silently change an authenticated chunk’s position or claimed length.

The IV and ciphertext are not the whole frame: the receiver also needs the authenticated metadata and a defined representation for it. Specify a stable byte encoding, field lengths, and versioning rather than relying on ad hoc JSON stringification whose byte representation may vary. Treat headers as untrusted until authentication succeeds, and apply basic bounds checks before allocating memory or doing expensive work.

Handle authentication failure as a failed chunk

Web Crypto rejects decryption when AES-GCM authentication fails. Do not append or expose unauthenticated plaintext, and do not silently substitute an empty chunk. Mark the transfer as failed or apply the protocol’s explicit recovery policy. Authentication failure can indicate corruption, mismatched keys or AAD, a framing bug, or tampering; it is not a reason to skip validation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How do you send a file over a WebRTC data channel?

Use reliable, ordered delivery as the straightforward default for a file protocol. The RTCDataChannel ordered property defaults to true. An application can configure other delivery behavior, but unordered or partially reliable delivery means the file protocol must identify chunks, detect missing or duplicate chunks, and define retransmission or failure behavior itself.

After the peers have negotiated a connection and opened the channel, the sender can read a slice as an ArrayBuffer, encrypt it, and send a binary message. Web Crypto’s SubtleCrypto.encrypt() operates on a bounded input; it is not a streaming operation that turns an arbitrarily large file into ciphertext in one call.

async function encryptChunk(
  key: CryptoKey,
  iv: BufferSource,
  plaintext: ArrayBuffer,
  additionalData: BufferSource,
): Promise<ArrayBuffer> {
  return crypto.subtle.encrypt(
    { name: "AES-GCM", iv, additionalData },
    key,
    plaintext,
  );
}

async function readFileChunk(file: File, start: number, end: number) {
  return file.slice(start, end).arrayBuffer();
}

This is a building block, not a complete wire protocol: the application still has to allocate a unique IV, produce the exact AAD and frame encoding expected by the receiver, enforce message limits, and manage the channel queue.

Choose a payload size from the negotiated limit

Do not hard-code one chunk size as safe for every browser, peer, and connection. Inspect RTCPeerConnection.sctp?.maxMessageSize where available and respect the negotiated receive limit. MDN documents a 64 KiB default when SDP omits max-message-size, and says modern browsers generally support at least 256 KiB. Those are protocol and documentation figures, not a universal recommended payload size. MDN also recommends moderately small messages because large messages can cause head-of-line blocking when message interleaving is unavailable.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The maximum message must accommodate the whole framed payload, not just plaintext. Subtract the encoded header, IV, and authentication overhead from the peer’s effective limit; leave margin for your protocol’s encoding and implementation constraints. If the limit is unavailable, do not assume it is unlimited: use a conservative application policy, handle send failures, and make the receiver reject oversized frames before expensive processing.

Smaller chunks reduce per-operation memory and can make queueing and recovery more manageable, but add framing and per-encryption overhead. Larger chunks reduce the number of operations, but hold more data at once and can occupy the channel longer. Tune within the negotiated limit and the memory budget instead of treating MDN’s minimum-support observation as a target.

Send only while the channel can accept more data

RTCDataChannel.send() accepts binary values such as Blob, ArrayBuffer, typed arrays, and DataView. Sending can fail if the channel is not ready, if its outgoing queue cannot accept more data, or if a message exceeds the peer’s receive limit. Check readyState, observe send failures, and make cancellation and retry behavior part of the protocol.

Use bufferedAmount and bufferedamountlow to keep the outgoing queue bounded. Set bufferedAmountLowThreshold to an application-chosen queue watermark, pause reading and encrypting when the queue is above it, then resume as it drains. Install the low-water listener before checking the current amount so a drain event is not missed between the check and the wait.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
function waitForQueueDrain(channel: RTCDataChannel): Promise<void> {
  if (channel.readyState !== "open") {
    return Promise.reject(new Error("Data channel is not open"));
  }

  if (channel.bufferedAmount <= channel.bufferedAmountLowThreshold) {
    return Promise.resolve();
  }

  return new Promise((resolve, reject) => {
    const cleanup = () => {
      channel.removeEventListener("bufferedamountlow", onLow);
      channel.removeEventListener("close", onClose);
      channel.removeEventListener("error", onError);
    };
    const onLow = () => {
      cleanup();
      resolve();
    };
    const onClose = () => {
      cleanup();
      reject(new Error("Data channel closed while waiting for queue drain"));
    };
    const onError = () => {
      cleanup();
      reject(new Error("Data channel failed while waiting for queue drain"));
    };

    channel.addEventListener("bufferedamountlow", onLow, { once: true });
    channel.addEventListener("close", onClose, { once: true });
    channel.addEventListener("error", onError, { once: true });

    // Recheck after registering listeners to avoid missing a drain in between.
    if (channel.bufferedAmount <= channel.bufferedAmountLowThreshold) {
      onLow();
    }
  });
}

Production code should also define what happens if a queue wait lasts too long and ensure that a failed or cancelled transfer does not leave listeners or pending work behind. The sender should wait for queue capacity before reading and encrypting another chunk; otherwise backpressure may stop network sends while the application continues accumulating plaintext and ciphertext in memory.

Best Value
Baby Einstein Curious Explorers Teether Book Take-Along Toy, Ages Newborn +, Multicolored
  • Discover a tale of teething
  • Little hands can easily grip this take-along toy
  • Teething corners help soothe sore gums
  • Soft pages are easy to flip
  • Use handle to create a new carrier toy
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

How should the receiver decrypt and reconstruct the file?

The receiver should parse each binary frame under strict size limits, identify its transfer and chunk, and reject malformed or unexpected data. It then reconstructs the authenticated metadata, decrypts with the expected key, IV, and AAD, and accepts the plaintext only if authentication succeeds.

  1. Validate the frame shape: check the protocol version, field lengths, transfer identifier, and total message size before trusting any field.
  2. Check chunk identity: detect duplicate indices, unexpected indices, and chunks outside the declared file or transfer bounds.
  3. Authenticate and decrypt: call crypto.subtle.decrypt() with the matching AES-GCM parameters and the identical AAD bytes used for encryption.
  4. Check recovered length: compare the authenticated plaintext length with the protocol’s expected length for that chunk.
  5. Commit validated data: store or append only authenticated plaintext, tracking which expected chunks are present.
  6. Finish only when complete: verify that every required chunk has arrived and that the final length matches the authenticated transfer description before presenting the reconstructed file as complete.

For a small file, an application may retain validated chunk buffers and create a final Blob. That still uses memory proportional to the file size. For a large file, design a destination that can accept incremental writes rather than collecting the entire plaintext in memory; the destination and save workflow have their own failure and permission cases. In either approach, do not declare success merely because the channel closed or a final-looking chunk arrived.

What should happen when a transfer is interrupted?

WebRTC transport does not define your application’s resume behavior. A useful protocol distinguishes transfer state from channel state and specifies how peers react to interruption.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Connection drop: mark the transfer interrupted, preserve only validated chunks, and reconnect or cancel according to user choice.
  • Resume: exchange authenticated transfer identity and missing chunk identifiers; never accept a chunk as belonging to a resumed transfer solely because its index matches.
  • Duplicate or missing chunks: reject or deduplicate according to explicit rules and request retransmission where the reliability policy supports it.
  • Authentication failure: reject the chunk and fail or retry only under a defined protocol, without exposing unverified plaintext.
  • Unsupported message limit: stop cleanly, renegotiate or choose smaller frames if possible, and report that peers could not agree on a usable size.
  • Cancellation: stop scheduling new reads and encryptions, stop or close the channel as appropriate, and discard incomplete output unless the user explicitly chooses to keep resumable state.
  • Save or storage failure: distinguish a successfully received transfer from a successfully written file, and report which operation failed.

Which responsibilities belong to the application?

Concern Provided by the platform Still the application’s responsibility
Peer transport RTCPeerConnection and RTCDataChannel provide peer connection and data-channel APIs. Signaling design, channel setup, user-visible connection state, and interruption handling.
Transport confidentiality WebRTC data-channel traffic is protected in transit with DTLS. Whether to add file-level encryption and how file keys, identity, authorization, and trust are established.
Cryptographic operations Web Crypto provides encryption and decryption primitives such as AES-GCM. Unique IV allocation, key lifecycle, authenticated framing, failure handling, and the protocol that peers agree to use.
Binary sending send() accepts binary payloads. Message framing, peer-limit adaptation, queue bounds, retries, resume, and cancellation.
File reconstruction Browser APIs provide file data and binary containers. Ordering and completeness checks, memory or disk strategy, and confirming that the final save succeeded.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.