Skip to content

Secure binary (de)serialization routines

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

stepfunc/scursor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

31 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

scursor

Secure cursor library with support for read and write transactions.

Panic-free design

scursor is designed to be strictly panic-free. This makes it suitable for parsing untrusted input in security-sensitive contexts, embedded systems with panic = "abort", or anywhere predictable failure handling is required.

The ReadCursor uses a consumption model where each read operation advances an internal position within a borrowed byte slice. The key insight is that all operations use inherently safe methods. For example, the core read_array routine is implemented as:

pub fn read_array<const N: usize>(&mut self) -> Result<[u8; N], ReadError> {
    let chunk = self.input.get(self.pos..)
        .and_then(|s| s.first_chunk::<N>()) // non-panicking bounds check
        .ok_or(ReadError)?;
    
    self.pos = self.pos.checked_add(N).ok_or(ReadError)?; // overflow-safe arithmetic
    Ok(*chunk)
}

Higher-level routines are composed from these primitives. There are no direct slice indexing operations (slice[i]), no .unwrap() or .expect() calls, and no arithmetic that could overflow. Every failure path returns a Result.

Safety by Composition

The core philosophy of scursor is that complexity should be built from verified, safe foundations. You can build complex, multi-step parsers that inherit the library's panic-free guarantees:

use scursor::{ReadCursor, ReadError};

struct Packet {
    id: u32,
    payload: [u8; 4],
    checksum: u16,
}

fn parse_packet(cursor: &mut ReadCursor) -> Result<Packet, ReadError> {
    // All of these calls are panic-free and bounds-checked
    Ok(Packet {
        id: cursor.read_u32_le()?,
        payload: cursor.read_array()?,
        checksum: cursor.read_u16_le()?,
    })
}

fn main() {
    let data = [0x01, 0x02, 0x03, 0x04, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
    let mut cursor = ReadCursor::new(&data);
    
    // The transaction API rolls back the cursor if any part of the parser fails
    let packet = cursor.transaction(|cur| parse_packet(cur));
}

Formal Verification

This library is formally verified to be panic-free using the Kani Rust Verifier. Kani uses bit-precise model checking to mathematically prove the absence of panics, out-of-bounds accesses, and overflows across all possible execution paths and inputs.

To run the mathematical proofs yourself:

  1. Install Kani:

    cargo install --locked kani-verifier
    cargo kani setup
  2. Run Verification:

    cargo kani

License

Licensed under the terms of the MIT or Apache v2 licenses at your choice.

About

Secure binary (de)serialization routines

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages