scintia-96 0.3.2

A lightweight, keyed 96-bit permutation based on the Speck cipher, designed for unique ID derivation.
Documentation

🌌 Scintia-96

Crates.io Documentation License Rust

Scintia-96 is a lightweight, keyed 96-bit permutation built for situations where you need guaranteed uniqueness for a 96-bit input. ⚑


πŸš€ Quick Start

Getting started is easy! Add it to your project:

cargo add scintia-96

Features

  • cipher (optional): Enables the BlockCipher implementation for server-side environments. This allows use with the cipher crate's traits, provides a precomputed key schedule for better performance, and enables reversing (decrypting) the permutation.

Basic Usage

use scintia_96::Scintia96;

// 128-bit key (4 x u32)
const KEY: [u32; 4] = [0x01020304, 0x05060708, 0x090a0b0c, 0x0d0e0f10];
const PERMUTATION: Scintia96 = Scintia96::new(KEY);

fn main() {
    // 96-bit block (3 x u32)
    let block = [0xdeadbeef, 0xcafebabe, 0xfacefeed];
    
    // Permute it!
    let permuted = PERMUTATION.permute(block);
    
    println!("Original: {:x?}", block);
    println!("Permuted: {:x?}", permuted);
}

🎯 Why Scintia-96?

The Use Cases

Scintia-96 is perfect for situations where you need to transform a 96-bit value while preserving its uniqueness:

  • Chip ID Masking: Scintia-96 was originally created to derive a USB serial number from a 96-bit unique chip ID (such as those on STM32 microcontrollers) while preserving uniqueness.
  • ID Obfuscation: MongoDB BSON ObjectIds are 96-bit values containing metadata such as timestamps and process identifiers. Scintia-96 can mask this information while preserving the useful properties of the ID. πŸƒ

While you could use a hash function, a permutation is often more suitable for cases like this. Since it's a one-to-one mapping, the output inherits the guaranteed uniqueness of the input, meaning there can never be a collision.

Why a Keyed Permutation?

By using a key, you ensure that your derived result is unique to your use case. Even if other apps use Scintia-96 for the same purpose, their results will differ from yours. πŸ§‚


πŸ› οΈ The Algorithm

Scintia-96 is based on the Speck block cipher (specifically Speck-64/128) with a few tweaks:

  • 3-word GFN: It operates on three 32-bit words using a Generalized Feistel Network.
  • 32 Rounds: Since we have 3 lanes and leave one out each round, diffusion is slightly slower. We bumped the round count to 32 to compensate and ensure robust mixing. πŸŒͺ️

πŸ”’ Security & Promises

I'm a developer, not a cryptographer, so I can't vouch for its resistance to cryptanalysis. But here’s what Scintia-96 brings to the table:

  • βœ… No Collisions: It's a true permutation: 96-bit input, 96-bit output, with a one-to-one mapping.
  • βœ… Statistically Sound: Passes avalanche and random distribution tests.
  • βœ… Hard to Reverse: Prevents recovery of the input without the key.
  • ❌ Not Side-Channel Resistant: If an attacker can perform side-channel analysis on your hardware, they can probably just as well extract the key from flash memory. πŸ€·β€β™‚οΈ

⚠️ A Note on Safety

Please don't use this to encrypt sensitive data. For AEAD, KDF, or MAC use cases, stick to well-established primitives like Xoodyak.

While we provide an optional BlockCipher implementation for convenience in server environments, Scintia-96 is designed primarily as a keyed permutation for identity masking. It has not undergone the rigorous cryptanalysis required for a general-purpose block cipher.


πŸ”‘ Key Generation

We provide a handy script to generate keys for your project:

python3 scripts/keygen.py

Quick One-Liners

Random Generation (via /dev/urandom):

python3 -c "import os; d=os.urandom(16); print('const KEY: [u32; 4] = [%s];' % ', '.join('0x%08x' % int.from_bytes(d[i:i+4], 'big') for i in range(0, 16, 4)))"

Deterministic Derivation (via SHA-512):

key_material="my-unique-variant"
python3 -c "import hashlib; m='$key_material'; h=hashlib.sha512(('scintia-96:key:'+m).encode()).digest(); print('// derive_key(\"%s\")\nconst KEY: [u32; 4] = [%s];' % (m, ', '.join('0x%08x' % int.from_bytes(h[i:i+4], 'big') for i in range(0, 16, 4))))"

πŸ“œ License

This project is licensed under the BSD 2-Clause License. See LICENSE.md for the full text. πŸ“„