MagicallMagicallTMALPHA
Why Magicall Pricing Compare About Login Get Started
Technical Deep-Dive

How Our Encryption Works

A plain-English explanation of Magicall's cryptographic architecture

The Short Version

  • Your video, audio, and chat are encrypted in your browser before they ever touch our servers.
  • Media is encrypted using SFrame (RFC 9605) with AES-256-GCM—standards used in secure communications worldwide.
  • Keys are exchanged using Elliptic Curve Diffie-Hellman (ECDH)—math that lets two strangers create a shared secret over an insecure channel.
  • You can verify participants using short codes to catch man-in-the-middle attacks.
  • Our servers cannot decrypt your calls. They relay encrypted blobs without any access to the content.
  • However, that assumes that you trust our servers to deliver to you the correct encryption code -- in theory, we could lie and deliver "bugged" cryptography code.

1. The Big Picture

When you make a Magicall video call, your browser does all the cryptographic heavy lifting. Before any video frame or chat message leaves your device, it gets encrypted with a key that only the participants in the room know. Our servers act as dumb relays—they pass encrypted data between participants but can never see inside it.

┌─────────────────────────────────────────────────────────────────────────────┐
│                         END-TO-END ENCRYPTION OVERVIEW                      │
└─────────────────────────────────────────────────────────────────────────────┘

  YOUR BROWSER                    OUR SERVERS                   THEIR BROWSER
 ┌────────────┐                  ┌────────────┐                 ┌────────────┐
 │            │                  │            │                 │            │
 │  Camera &  │   Encrypted      │   Relay    │   Encrypted     │  Decrypt   │
 │ Microphone ├─────────────────►│   Server   ├────────────────►│  & Display │
 │            │   (gibberish)    │   (SFU)    │   (gibberish)   │            │
 │  Encrypt   │                  │            │                 │            │
 │  locally   │                  │  Can NOT   │                 │  Decrypt   │
 │            │                  │  decrypt   │                 │  locally   │
 └────────────┘                  └────────────┘                 └────────────┘
       │                                                               │
       │                         ROOM KEY                              │
       └──────────────────────────────────────────────────────────────-┘
                    (shared secretly between participants)

This is called end-to-end encryption (E2EE). The "ends" are you and the other participants. Everyone in between—including us—sees only encrypted gibberish.

2. The Algorithms We Use

Cryptography is only as good as the algorithms behind it. We use well-established, peer-reviewed standards:

Purpose Algorithm What It Does
Encrypting video/audio SFrame (RFC 9605) Standard E2E media encryption format using AES-256-GCM with per-frame headers.
Encrypting chat AES-256-GCM Symmetric encryption with 256-bit keys. Authenticated—tampering is detected.
Key exchange ECDH (P-256) Lets two parties derive a shared secret without ever transmitting it.
Key derivation HKDF-SHA256 Derives multiple keys from a single secret with domain separation.
Verification codes SHA-256 Hashes the room key + public keys to generate human-readable verification codes.

All cryptography runs in your browser using the WebCrypto API—a native, audited implementation built into Chrome, Firefox, Safari, and Edge.

3. How Keys Are Exchanged

The magic trick of modern cryptography is that two strangers can create a shared secret over a public channel, and anyone listening in can't figure out what that secret is. This is called Diffie-Hellman key exchange.

┌─────────────────────────────────────────────────────────────────────────────┐
│                          ECDH KEY EXCHANGE                                  │
└─────────────────────────────────────────────────────────────────────────────┘

    ALICE (Room Owner)                              BOB (Guest)
   ┌─────────────────────┐                        ┌─────────────────────┐
   │ Generate key pair:  │                        │ Generate key pair:  │
   │  • Private key (a)  │                        │  • Private key (b)  │
   │  • Public key  (A)  │                        │  • Public key  (B)  │
   └─────────────────────┘                        └─────────────────────┘
            │                                               │
            │          ┌─────────────────────┐              │
            └─────────►│   Exchange public   │◄─────────────┘
                       │   keys (A and B)    │
                       │                     │
                       │  Anyone can see A   │
                       │  and B, but can't   │
                       │  compute the secret │
                       └─────────────────────┘
            │                                               │
            ▼                                               ▼
   ┌─────────────────────┐                        ┌─────────────────────┐
   │ Compute:            │                        │ Compute:            │
   │ shared = B^a mod p  │                        │ shared = A^b mod p  │
   │                     │                        │                     │
   │ (Using Alice's      │                        │ (Using Bob's        │
   │  private key + Bob's│                        │  private key +      │
   │  public key)        │                        │  Alice's public key)│
   └─────────────────────┘                        └─────────────────────┘
            │                                               │
            │        ┌───────────────────────┐              │
            └───────►│  SAME SHARED SECRET!  │◄─────────────┘
                     │      (256 bits)       │
                     └───────────────────────┘

We use Elliptic Curve Diffie-Hellman (ECDH) with the P-256 curve. Each participant generates their own key pair when they join a room. Public keys are exchanged, and each person independently computes the same shared secret. This shared secret is then used to encrypt the actual room key.

4. The Room Key

Every Magicall room has a room key—a random 256-bit value generated by the room owner. This is the key that actually encrypts your video, audio, and chat.

┌─────────────────────────────────────────────────────────────────────────────┐
│                          ROOM KEY DISTRIBUTION                              │
└─────────────────────────────────────────────────────────────────────────────┘

                            ROOM OWNER
                       ┌─────────────────┐
                       │  Generates:     │
                       │  Room Key (32   │
                       │  random bytes)  │
                       └────────┬────────┘
                                │
           ┌────────────────────┼────────────────────┐
           │                    │                    │
           ▼                    ▼                    ▼
   ┌───────────────┐    ┌───────────────┐    ┌───────────────┐
   │ GUEST 1       │    │ GUEST 2       │    │ GUEST 3       │
   │               │    │               │    │               │
   │ 1. ECDH with  │    │ 1. ECDH with  │    │ 1. ECDH with  │
   │    owner      │    │    owner      │    │    owner      │
   │               │    │               │    │               │
   │ 2. Derive KEK │    │ 2. Derive KEK │    │ 2. Derive KEK │
   │    from ECDH  │    │    from ECDH  │    │    from ECDH  │
   │    secret     │    │    secret     │    │    secret     │
   │               │    │               │    │               │
   │ 3. Receive    │    │ 3. Receive    │    │ 3. Receive    │
   │    encrypted  │    │    encrypted  │    │    encrypted  │
   │    room key   │    │    room key   │    │    room key   │
   │               │    │               │    │               │
   │ 4. Decrypt    │    │ 4. Decrypt    │    │ 4. Decrypt    │
   │    with KEK   │    │    with KEK   │    │    with KEK   │
   └───────────────┘    └───────────────┘    └───────────────┘

   KEK = Key Encryption Key (derived via HKDF from the ECDH shared secret)

When a new participant joins:

  1. They exchange public keys with the room owner via ECDH
  2. Both sides compute a shared secret (different for each pair of participants)
  3. From this shared secret, a Key Encryption Key (KEK) is derived using HKDF
  4. The owner encrypts the room key with this KEK (using AES-256-GCM with the key epoch authenticated as additional data) and sends it to the new participant
  5. The participant decrypts it—now everyone shares the same room key

5. Encrypting Video and Audio

Each video or audio frame is encrypted individually using SFrame (RFC 9605)—a standard format designed specifically for end-to-end encrypted media in WebRTC. SFrame uses AES-256-GCM for authenticated encryption, which not only encrypts data but also detects if anyone tampers with it.

┌─────────────────────────────────────────────────────────────────────────────┐
│                     SFRAME ENCRYPTION (RFC 9605)                            │
└─────────────────────────────────────────────────────────────────────────────┘

   RAW VIDEO FRAME                               ENCRYPTED FRAME
  ┌─────────────────┐                          ┌──────────────────────────────┐
  │                 │                          │ SFRAME   │ ENCRYPTED PAYLOAD │
  │  Your face,     │     AES-256-GCM          │ HEADER   │  + AUTH TAG (16B) │
  │  in pixels      │ ───────────────────────► ├──────────┼───────────────────┤
  │                 │                          │ KID+CTR  │    Gibberish      │
  │                 │                          │ (1-17 B) │                   │
  └─────────────────┘                          └──────────┴───────────────────┘

   ┌───────────────────────────────────────────────────────────────────────────┐
   │                       SFRAME HEADER FORMAT                                │
   │                                                                           │
   │  [Config Byte][KID (0-8 bytes)][CTR (0-8 bytes)]                          │
   │                                                                           │
   │  Config Byte: [X:1][K:3][Y:1][C:3]                                        │
   │    X=0: K contains KID directly (0-7)                                     │
   │    X=1: K+1 = length of KID field in bytes                                │
   │    Y=0: C contains CTR directly (0-7)                                     │
   │    Y=1: C+1 = length of CTR field in bytes                                │
   │                                                                           │
   │  KID = Key ID (epoch + hash(trackId) — unique per sender)                 │
   │  CTR = Frame counter (unique per frame, starts at random 48-bit offset)   │
   └───────────────────────────────────────────────────────────────────────────┘

Each encrypted frame has:

  • An SFrame header (1-17 bytes) containing the key ID (KID) and frame counter (CTR) in a compact variable-length encoding
  • The encrypted payload—your actual video/audio data, now unreadable
  • An authentication tag (16 bytes)—a cryptographic checksum that detects tampering

The Key ID (KID) is computed as: keyEpoch + hash(trackId). This gives each sender a unique KID, which means each sender derives a unique sframe_salt. Combined with the per-track random counter offset, this prevents nonce reuse even when multiple senders are transmitting simultaneously with the same room key.

The counter serves two purposes:

  1. It ensures each frame uses a unique nonce (AES-GCM requires this for security). Each track's counter starts at a random 48-bit offset to prevent nonce reuse if a track is removed and re-added.
  2. It enables replay protection—receivers reject frames with old counters

6. Encrypting Chat Messages

Chat messages are encrypted with a separate key derived from the room key. This is called domain separation—even if somehow the media key were compromised, chat would remain secure (and vice versa).

┌─────────────────────────────────────────────────────────────────────────────┐
│                          KEY DERIVATION (HKDF)                              │
└─────────────────────────────────────────────────────────────────────────────┘

                              ROOM KEY
                          (32 random bytes)
                                 │
       ┌────────────────────────┬┴───────────────────────┬─────────────────┐
       │                        │                        │                 │
       ▼                        ▼                        ▼                 ▼
┌─────────────┐          ┌─────────────┐          ┌─────────────┐   ┌───────────┐
│ SFRAME KEY  │          │ HKDF with   │          │ HKDF with   │   │ SHA-256   │
│ DERIVATION  │          │ salt=       │          │ salt=       │   │ hash with │
│ (RFC 9605)  │          │ "magicall-  │          │ "magicall-  │   │ room key  │
│             │          │  chat-v1"   │          │  room-key-  │   │ + pub keys│
│             │          │ info=       │          │  v1"        │   │ (sorted)  │
│             │          │ "chat-      │          │ info=       │   │           │
│             │          │  encryption"│          │ "key-       │   │           │
│             │          │             │          │  encryption"│   │           │
└──────┬──────┘          └──────┬──────┘          └──────┬──────┘   └─────┬─────┘
       │                        │                        │               │
       ▼                        ▼                        ▼               ▼
┌─────────────┐          ┌─────────────┐          ┌─────────────┐   ┌───────────┐
│ MEDIA KEY + │          │  CHAT KEY   │          │     KEK     │   │ SAS CODE  │
│ MEDIA SALT  │          │ (AES-256)   │          │ (AES-256)   │   │"ALFA-APEX │
│ (per KID)   │          └─────────────┘          └─────────────┘   │-ARCH-ATOM"│
└─────────────┘                                                     └───────────┘

   SFrame Key Derivation (RFC 9605 Section 4.4):
   ┌───────────────────────────────────────────────────────────────────────────┐
   │ sframe_key  = HKDF(room_key, info="SFrame 1.0 Secret key " || KID || 0005)│
   │ sframe_salt = HKDF(room_key, info="SFrame 1.0 Secret salt " || KID|| 0005)│
   │ nonce       = sframe_salt XOR counter (12 bytes)                          │
   │ (KID = 8-byte big-endian key ID, 0005 = AES_256_GCM cipher suite)         │
   └───────────────────────────────────────────────────────────────────────────┘

HKDF (HMAC-based Key Derivation Function) uses the room key as input and produces multiple independent keys. The "salt" and "info" parameters ensure that each derived key is cryptographically independent—knowing one tells you nothing about the others. For media encryption, we follow the SFrame RFC 9605 key derivation which derives both a key and a salt that is XORed with the counter to produce the nonce.

7. Verifying Participants (SAS)

End-to-end encryption protects your data in transit, but how do you know you're actually talking to who you think you are? An attacker could intercept the key exchange and substitute their own keys—a man-in-the-middle (MITM) attack.

Magicall provides Short Authentication Strings (SAS) to detect this. Each participant sees a four-word code (like "ALFA-APEX-ARCH-ATOM") derived from the room key and both participants' public keys. If everyone's codes match, you know the key exchange wasn't tampered with.

┌─────────────────────────────────────────────────────────────────────────────┐
│                          SAS VERIFICATION                                   │
└─────────────────────────────────────────────────────────────────────────────┘

   NORMAL KEY EXCHANGE (SECURE):

   Alice ◄────────────────────────────────────────────────────────► Bob
              Both compute same shared secret
              Both derive same SAS: "ALFA-APEX-ARCH-ATOM"

   ✓ Alice sees "ALFA-APEX-ARCH-ATOM"   ✓ Bob sees "ALFA-APEX-ARCH-ATOM"
   ✓ They match!                        ✓ No attacker in the middle


   MAN-IN-THE-MIDDLE ATTACK (DETECTED):

   Alice ◄─────────────► ATTACKER ◄─────────────► Bob
           Secret #1                 Secret #2 (different!)

   ✗ Alice sees "ALFA-APEX-ARCH-ATOM"   ✗ Bob sees "KILO-NEST-LIMA-PAPA"
   ✗ Different codes!                   ✗ MITM detected!

   The attacker can't make both sides see the same SAS because
   they have different shared secrets with each participant.

The SAS is generated by hashing the room key along with both participants' public keys (sorted lexicographically) using SHA-256, then mapping the first four bytes to a wordlist of 256 phonetically distinct words. This gives over 4 billion possible codes—an attacker has only a 1-in-4-billion chance of guessing correctly. Including the public keys in the hash ensures the SAS is bound to the specific key exchange, not just the room key.

Verify out-of-band: For SAS verification to work, you need to compare codes through a separate channel—verbally during the call, via text message, or in person. If you only compare codes through Magicall itself, a MITM could manipulate what you see.

8. What Our Servers See

Our servers (specifically, the SFU—Selective Forwarding Unit) route encrypted streams between participants. Here's exactly what they can and cannot access:

Data Server Access
Your face and voice Cannot see — Encrypted E2E
Chat messages Cannot see — Encrypted E2E
Room key Cannot see — Never transmitted in the clear
ECDH shared secrets Cannot see — Computed locally, never transmitted
Public keys Can see — But useless without private keys
Encrypted room key blobs Can see — But cannot decrypt (no KEK)
Your nickname Can see — Sent in the clear for display to others
Call timing and duration Can see — When you join/leave, how long you stay
Packet sizes and timing Can see — Metadata, reveals nothing about content
IP addresses Can see — Necessary for routing packets

Even if our servers were compromised, attackers would only get encrypted blobs. The keys to decrypt them exist only in participants' browsers.

9. Double Encryption

Your media actually goes through two layers of encryption:

┌─────────────────────────────────────────────────────────────────────────────┐
│                          ENCRYPTION LAYERS                                  │
└─────────────────────────────────────────────────────────────────────────────┘

                          YOUR VIDEO FRAME
                                 │
                                 ▼
                    ┌────────────────────────┐
                    │   E2E ENCRYPTION       │
                    │   SFrame (RFC 9605)    │
                    │   with AES-256-GCM     │
                    │                        │
                    │   Key: Derived from    │
                    │   room key via HKDF    │
                    │   (only participants   │
                    │    know room key)      │
                    └───────────┬────────────┘
                                │
                                ▼
                    ┌────────────────────────┐
                    │   TRANSPORT ENCRYPTION │
                    │   (SRTP)               │
                    │                        │
                    │   Key: Session Key     │
                    │   (negotiated per      │
                    │    WebRTC connection)  │
                    └───────────┬────────────┘
                                │
                                ▼
                    ┌────────────────────────┐
                    │   SENT OVER INTERNET   │
                    │                        │
                    │   (doubly encrypted)   │
                    └────────────────────────┘

   Even if SRTP were somehow broken, the E2E layer remains.
   The server terminates SRTP but CANNOT see inside the E2E layer.
  1. E2E Encryption (our layer): SFrame (RFC 9605) with AES-256-GCM using keys derived from the room key. This happens before data leaves your browser.
  2. SRTP (WebRTC transport): Encrypts RTP packets on the wire. This is standard WebRTC security.

The SFU terminates SRTP (it needs to read packet headers to route them), but it cannot pierce the E2E layer—it just sees encrypted blobs inside the SRTP packets.

10. Encryption States

The Magicall UI shows your current encryption status:

Status What It Means
Transport Only SRTP is active, but E2E key exchange hasn't completed yet. This is a brief transitional state when joining.
E2E Encrypted (Unverified) Full E2E encryption is active, but you haven't verified participants via SAS yet. Secure against passive eavesdroppers.
E2E Encrypted (Verified) Full E2E encryption plus you've verified the SAS codes match. This is the highest security level—MITM attacks ruled out.
E2E Failed SAS verification failed—the codes didn't match. This indicates a potential man-in-the-middle attack. You should disconnect immediately.

11. Browser Requirements

E2E encryption requires your browser to support the RTCRtpScriptTransform API (part of the WebRTC Encoded Transform specification). This lets us intercept video/audio frames and encrypt them before they leave your device.

Supported browsers:

  • Chrome/Edge 86+ — Full support
  • Firefox 117+ — Full support
  • Safari 15.4+ — Full support

On older browsers without this API, Magicall falls back to transport-only encryption (SRTP). You'll still have secure transport, but the server could theoretically see your media.

12. The Trust Model

No security system is perfect. Here's what you're trusting when you use Magicall:

  • The code we serve: Like all browser-based encryption, you trust that we serve you the correct JavaScript. A malicious insider or server compromise could theoretically serve modified code. This is a fundamental limitation of web-based E2E encryption.
  • Your browser's WebCrypto: We use your browser's native cryptographic implementation. If there's a bug in Chrome/Firefox/Safari's crypto, it affects everyone.
  • The algorithms: AES-256, P-256, SHA-256, and HKDF are well-studied standards with decades of cryptanalysis. No practical attacks are known.
  • Your device: If your computer has malware, it could capture video before encryption or keylog your passwords. E2E encryption can't protect against endpoint compromise.

13. Summary

Here's the complete flow of what happens when you join a Magicall room:

┌─────────────────────────────────────────────────────────────────────────────┐
│                          COMPLETE E2E FLOW                                  │
└─────────────────────────────────────────────────────────────────────────────┘

 1. JOIN ROOM
    └─► Generate ECDH key pair (P-256)
    └─► If owner: generate 32-byte random room key

 2. KEY EXCHANGE
    └─► Broadcast public key to all participants
    └─► Receive others' public keys
    └─► Compute ECDH shared secret with each peer

 3. ROOM KEY DISTRIBUTION (owner only)
    └─► For each peer:
        └─► Derive KEK from shared secret (HKDF)
        └─► Encrypt room key with KEK (AES-256-GCM, key epoch in AAD)
        └─► Send encrypted room key + epoch to peer

 4. ROOM KEY RECEPTION (guests)
    └─► Receive encrypted room key from owner
    └─► Derive same KEK from ECDH shared secret
    └─► Decrypt room key

 5. DERIVE WORKING KEYS
    └─► KID = keyEpoch + hash(trackId) — unique per sender
    └─► Media key + salt = SFrame HKDF derivation (RFC 9605) per KID
    └─► Chat key = HKDF(room key, salt="magicall-chat-v1", info="chat-encryption")
    └─► SAS code = first 4 bytes of SHA-256(room key || pubKey1 || pubKey2) → wordlist

 6. VERIFY PARTICIPANTS
    └─► Compare SAS codes out-of-band
    └─► If match: mark as verified (MITM ruled out)
    └─► If mismatch: potential attack, disconnect

 7. ENCRYPT MEDIA (SFrame RFC 9605)
    └─► Initialize counter with random 48-bit offset per track
    └─► Each frame: AES-256-GCM(sframe_key, frame, nonce=sframe_salt XOR counter)
    └─► Increment counter for each frame
    └─► Prepend SFrame header with key ID (KID) + counter (CTR)

 8. ENCRYPT CHAT
    └─► Each message: AES-256-GCM(chat key, message, random IV)
    └─► Send via signaling channel

 9. ON PEER JOIN
    └─► Owner sends existing room key to new peer (encrypted with peer-specific KEK)
    └─► Unique KID per sender prevents nonce collisions
    └─► Counters continue from random offsets (no reset needed)

Questions?

We're cryptographers. We love talking about this stuff. If you have questions about our encryption architecture, want to report a security issue, or just want to geek out about cryptography:

  • Email: hello@magicall.online
  • Company: Symbolic Software, Paris, France
Why Magicall Pricing Compare About Privacy Cryptography Support

Symbolic SoftwareBuilt by your friendly cryptographers at Symbolic Software

© 2026 Symbolic Software. All rights reserved.

This is alpha software. Features may change and bugs may occur.