sframe 1.2.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 and 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.

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
    • Note: This backend uses unsafe code for in-place encryption/decryption to avoid memory allocations.
  • 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 the generic EncryptionKey<Aead, Kdf> / DecryptionKey<Aead, Kdf> with your types. See the caesar_cipher 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.

flowchart TD
    subgraph Sender
        A[MediaFrame<br/>Payload + Metadata] -->|new/with_metadata| B[MediaFrame/MediaFrameView]
        B --> EncryptJunction(( ))
        EncryptJunction -->|encrypt/encrypt_into| C[EncryptedFrame/EncryptedFrameView]
        FC[FrameCounter] --> EncryptJunction
        EK[EncryptionKey] --> EncryptJunction
    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[EncryptedFrame/EncryptedFrameView]
        E --> DecryptJunction(( ))
        DecryptJunction -->|decrypt/decrypt_into| F[MediaFrame/MediaFrameView]
        DK[DecryptionKey] --> KS[KeyStore]
        KS --> DecryptJunction
    end
    
    C -.->|Network| D
    
    classDef sframe fill:#6366f1,stroke:#a5b4fc,stroke-width:1px,color:#fff
    class Shared,B,C,E,F,EK,DK,FC,KS sframe
    style EncryptJunction fill:transparent,stroke:#888
    style DecryptJunction fill:transparent,stroke:#888

For example:


use sframe::{
    frame::{EncryptedFrameView, 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::new(&mut counter, payload);

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

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

assert_eq!(decrypted_media_frame, media_frame);

Additionally the library provides:

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 Receiver which decrypts them.
    • Shows how to use the ratchet mechanism and the frame validation (Reply Protection).
  • bip_frame_buffer
    • Demonstrates how to use the API with an arbitrary buffer implemetation with the FrameBuffer trait.
  • generate_headers
    • Serialize/Deserialize the plain SFrame headers.
  • caesar_cipher
    • Demonstrates how to plug in a custom crypto backend by implementing the AeadEncrypt, AeadDecrypt, and KeyDerivation traits.

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 continously 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