sframe 2.1.0

pure rust implementation of SFrame (RFC 9605)
Documentation

Secure Frame (SFrame)

build version Crates.io license documentation maintenance

This library is an implementation of Sframe (RFC 9605) and provides an end-to-end encryption mechanism for media frames that is suited for WebRTC conferences. It was forked from the original goto-opensource/secure-frame-rs and is continued here.

Migrating from 1.x? 2.0 moves the ratcheting and validation state changes behind authentication, and replaces the bare integers with types. MIGRATION.md walks through it module by module.

Supported crypto libraries

Three crypto libraries are supported out of the box, selectable via mutually exclusive features:

  • ring
    • is enabled per default with the feature ring
    • supports compilation to Wasm32
    • Aes-CTR mode ciphers are not supported
  • openssl
    • is enabled with the feature openssl
      • To build e.g. use cargo build --features openssl --no-default-features
    • uses rust bindings to OpenSSL.
    • Per default the OpenSSL library is locally compiled and then statically linked. The build process requires a C compiler, perl (and perl-core), and make. For further options see the openssl crate documentation.
    • Compilation to Wasm32 is not yet supported
  • rust crypto
    • is enabled with the feature rust-crypto
      • to build e.g. use cargo build --features rust-crypto --no-default-features
    • pure rust implementation of the necessary crypto primitives (AES-GCM, SHA-512, HKDF, AES-CTR)
    • Compilation to Wasm32 is supported

These cannot be enabled at the same time, thus on conflict sframe issues a compiler error.

Custom crypto backend

If none of the backend features is enabled, sframe exposes only the generic crypto traits and lets you plug in your own implementation. Implement AeadEncrypt/AeadDecrypt and KeyDerivation from the crypto module, then parameterize key::GenericEncryptionKey / key::GenericDecryptionKey with your types. With a backend feature enabled, key::EncryptionKey / key::DecryptionKey are aliases of those pinned to it, so the type parameters never have to be spelled out. See the custom-crypto-backend example for a full walkthrough.

Usage

The API provides low-level access to encryption and decryption at the frame level.

It allows the use of arbitrary buffers, enabling the creation of views to avoid unnecessary copies:

  • MediaFrameView for unencrypted data
  • EncryptedFrameView for encrypted data

For encryption and decryption, a buffer must be provided implementing the FrameBuffer trait to allocate the necessary memory. For convenience, this trait has already been implemented for Vec<u8>.

There is also a variant which allocates the necessary memory and owns the buffers:

  • MediaFrame for unencrypted data
  • EncryptedFrame for encrypted data

To convert between MediaFrame(View) and EncryptedFrame(View), an EncryptionKey or DecryptionKey is needed, which needs to be derived from a shared and secret key material.

The diagram below follows the borrowing views. The owning MediaFrame/EncryptedFrame offer the same conversions via encrypt/decrypt, which allocate instead of writing into a supplied buffer.

flowchart TD
    subgraph Sender
        A[Payload + Metadata] --> NewJunction(( ))
        FC[FrameCounter] --> NewJunction
        NewJunction -->|try_new/try_with_meta_data| B[MediaFrameView]
        B --> EncryptJunction(( ))
        OBE["Output Buffer<br/>FrameBuffer"] --> EncryptJunction
        EK[EncryptionKey] --> EncryptJunction
        EncryptJunction -->|encrypt_into| C[EncryptedFrameView]
    end
    
    Shared[Key Material<br/>CipherSuite]
    Shared -->|derive_from| EK
    Shared -->|derive_from| DK
    
    subgraph Receiver
        D[Incoming Frame Buffer] -->|try_new/try_with_meta_data| E[EncryptedFrameView]
        E --> DecryptJunction(( ))
        OBD["Output Buffer<br/>FrameBuffer"] --> DecryptJunction
        FV[FrameValidation<br/>ReplayAttackProtection] -.->|optional| DecryptJunction
        DK[DecryptionKey<br/>as KeyStore] --> DecryptJunction
        DecryptJunction -->|"decrypt_into/validated_decrypt_into"| F[MediaFrameView]
        E ~~~ F
    end
    
    C -.->|Network| D
    
    classDef sframe fill:#6366f1,stroke:#a5b4fc,stroke-width:1px,color:#fff
    class Shared,B,C,E,F,EK,DK,FC,FV sframe
    style NewJunction fill:transparent,stroke:#888
    style EncryptJunction fill:transparent,stroke:#888
    style DecryptJunction fill:transparent,stroke:#888

A FrameValidation screens a frame before decryption and is only updated once decryption authenticated it, so a forged header cannot poison it:

screen(frame)? -> Token -> decrypt()? -> record(token)

Additionally the library provides:

Examples

Encrypting and decrypting a frame with caller supplied buffers:

use sframe::{
    frame::{MediaFrameView, MonotonicCounter},
    key::{DecryptionKey, EncryptionKey},
    CipherSuite,
};

let key_id = 42u64;
let enc_key = EncryptionKey::derive_from(CipherSuite::AesGcm256Sha512, key_id, "pw123").unwrap();
let mut counter = MonotonicCounter::default();
let payload = "Something secret";

let mut encrypt_buffer = Vec::new();
let mut decrypt_buffer = Vec::new();
let media_frame = MediaFrameView::try_new(&mut counter, payload).unwrap();

let encrypted_frame = media_frame.encrypt_into(&enc_key, &mut encrypt_buffer).unwrap();

let dec_key = DecryptionKey::derive_from(CipherSuite::AesGcm256Sha512, key_id, "pw123").unwrap();
let decrypted_media_frame = encrypted_frame
    .decrypt_into(&dec_key, &mut decrypt_buffer)
    .unwrap();

assert_eq!(decrypted_media_frame, media_frame);

Runnable examples:

  • sender_receiver
    • Demonstrates how the API can be used in an application to encrypt and decrypt frames between two parties.
    • Implements a Sender which encrypts frames and a Receiver which decrypts them.
    • Shows how to use the ratchet mechanism and the frame validation (replay protection).
  • bip_frame_buffer
    • Demonstrates how to use the API with an arbitrary buffer implementation with the FrameBuffer trait.
  • generate_headers
    • Serialize/Deserialize the plain SFrame headers.
  • custom-crypto-backend
    • Demonstrates how to plug in a custom crypto backend by implementing the AeadEncrypt, AeadDecrypt, and KeyDerivation traits.
  • wasm-webrtc
    • End-to-end encrypted video in the browser, encrypts VP8 frames in a WebRTC Encoded Transform.

Benchmarks

The criterion benchmarks located at ./benches currently test

  • encryption/decryption with all available cipher suites and different frame size
  • key derivation with all available cipher suites
  • header (de)serialization

They are tracked continuously with a Bencher Perf Page:

Decryption (AES-GCM) Decrypt (AES-CTR)
Encrypt (AES-GCM) Encrypt (AES-CTR)

Contribution

Any help in form of descriptive and friendly issues or comprehensive pull requests are welcome!

The Changelog of this library is generated from its commit log, there any commit message must conform with https://www.conventionalcommits.org/en/v1.0.0/. For simplicity you could make your commits with convco.

License