PHPackages                             niktomo/kasumi - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. niktomo/kasumi

ActiveLibrary

niktomo/kasumi
==============

Reversible 63-bit integer scrambling using modular multiplicative inverse

v1.0.2(1mo ago)00MITPHPPHP ^8.2CI passing

Since Mar 28Pushed 1mo agoCompare

[ Source](https://github.com/niktomo/kasumi)[ Packagist](https://packagist.org/packages/niktomo/kasumi)[ Docs](https://github.com/niktomo/kasumi)[ RSS](/packages/niktomo-kasumi/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (6)Versions (4)Used By (0)

Kasumi 霞
========

[](#kasumi-霞)

[日本語](README-ja.md) | English

Reversible 63-bit integer scrambling for Laravel — no bcmath, no GMP, no extra extensions.

```
$scrambler->scramble(12345);          // ScrambledValue → "00000001x73riz"
$scrambler->scramble($scrambled);     // back to 12345
```

The same `scramble()` call encodes **and** decodes — no separate methods needed.

Why
---

[](#why)

Sequential integer IDs are a liability when exposed in URLs or API responses:

- **Enumeration** — an attacker iterates `/users/1`, `/users/2`, … to harvest data or probe for IDOR vulnerabilities.
- **Business intelligence leakage** — order ID `5983` tells a competitor "this store has ~6000 orders." Two observations an hour apart reveal the transaction rate.
- **User-count estimation** — in social games and SaaS products, sequential user IDs let rivals track your growth in real time.

Kasumi scrambles IDs into opaque, fixed-length strings at the application layer, keeping your database schema and indexes untouched.

> **Obfuscation, not encryption.** Kasumi hides the structure of IDs but does not provide cryptographic security. The salt space is 32 bits (~2 billion values), which is brute-forceable given enough known plaintext pairs. Do not rely on Kasumi as the sole defence against unauthorized access. Always enforce server-side authorization checks independently.

How it works
------------

[](#how-it-works)

Based on [this algorithm](https://cs.hatenablog.jp/entry/2013/06/19/135527), applied independently to the upper 32 bits and lower 32 bits of the input:

```
scramble32(x) = inverseSalt × reverseBits32(salt × x mod 2³²) mod 2³²

```

The function is **involutory** — applying it twice returns the original value:

```
f(f(x)) = x  for all x in [0, PHP_INT_MAX]

```

- `salt × x mod 2³²` — multiplication disperses bits across the 32-bit space
- `reverseBits32(…)` — bit reversal mixes upper and lower halves
- `inverseSalt × …` — multiplication by the modular inverse makes the whole operation self-inverse

All arithmetic uses native PHP integers. No bcmath or GMP required.

Compared to alternatives
------------------------

[](#compared-to-alternatives)

**Kasumi**jenssegers/optimushashids / sqidsMax input**63-bit** (PHP\_INT\_MAX)31-bit only63-bit (needs bcmath/GMP)Extensions**none**optional GMPbcmath or GMP requiredAPI**f(f(x)) = x**encode + decodeencode + decodeOutputinteger or Base36 stringintegerstring onlyLaravel**built-in**third-party wrapperthird-party wrapperRequirements
------------

[](#requirements)

- PHP ^8.2
- Laravel ^12.0

Installation
------------

[](#installation)

```
composer require niktomo/kasumi
```

Generate a salt and write it to `.env`:

```
php artisan kasumi:salt:generate
```

This adds `KASUMI_SCRAMBLE_SALT=` to your `.env`. Keep this value secret and stable — **changing it invalidates all existing scrambled values**.

Usage
-----

[](#usage)

### Facade

[](#facade)

```
use Kasumi\Laravel\Facades\Kasumi;

// Scramble
$result = Kasumi::scramble(12345);
echo $result;           // "00000001x73riz"  (base36, always 14 chars, salt-dependent)

// Unscramble — pass the ScrambledValue directly (no toInt() on intermediate values)
$original = Kasumi::scramble($result)->toInt(); // 12345
```

### Dependency Injection

[](#dependency-injection)

```
use Kasumi\Scrambler;

class UserController
{
    public function __construct(private Scrambler $scrambler) {}

    public function show(string $encoded): Response
    {
        $encoder         = new \Kasumi\Base36Encoder();
        [$upper, $lower] = $encoder->decode($encoded);
        $scrambled       = new \Kasumi\ScrambledValue($upper, $lower, $encoder);
        $id              = $this->scrambler->scramble($scrambled)->toInt();

        $user = User::findOrFail($id);
        // …
    }
}
```

### Standalone (without Laravel)

[](#standalone-without-laravel)

```
use Kasumi\Scrambler;
use Kasumi\Base36Encoder;

$scrambler = Scrambler::fromSalt(
    salt: 1234567891,   // must be odd
    encoder: new Base36Encoder(),
);

$result = $scrambler->scramble(12345);
echo $result;           // "00000001x73riz"  (with salt 1234567891)

// Unscramble
$original = $scrambler->scramble($result)->toInt(); // 12345
```

Custom Encoder
--------------

[](#custom-encoder)

Implement `Kasumi\Encoder` to use a different string representation:

```
use Kasumi\Encoder;

class Base62Encoder implements Encoder
{
    public function encode(int $upper, int $lower): string { /* … */ }
    /** @return array{0: int, 1: int} */
    public function decode(string $s): array { /* … */ }
}

$scrambler = Scrambler::fromSalt($salt, new Base62Encoder());
```

ChecksumEncoder
---------------

[](#checksumencoder)

`ChecksumEncoder` is a decorator that wraps any `Encoder` and adds tamper detection. It appends a 5-character base36 checksum derived from the inner encoding:

- **2-character prefix** — prepended to the output for a quick validity check
- **3 filler characters** — inserted at fixed positions inside the body as noise

Output length is `2 + innerLength + 3`. With `Base36Encoder` (14 chars), the total is **19 characters**.

`decode()` throws `\InvalidArgumentException` for any string that was not produced by this encoder — wrong length, tampered prefix, or tampered body all fail.

```
use Kasumi\Base36Encoder;
use Kasumi\ChecksumEncoder;
use Kasumi\Scrambler;

$encoder  = new ChecksumEncoder(new Base36Encoder());
$scrambler = Scrambler::fromSalt(salt: 1234567891, encoder: $encoder);

$result = $scrambler->scramble(12345);
echo $result;           // "0b00u000i001gx73riz"  (19 chars, with salt 1234567891)

$original = $scrambler->scramble($result)->toInt(); // 12345

// Decoding a tampered string throws \InvalidArgumentException
$encoder->decode('zzzzzzzzzzzzzzzzzzzz'); // throws
```

`ChecksumEncoder` can also wrap a custom encoder:

```
$encoder = new ChecksumEncoder(new Base62Encoder());
```

Artisan Commands
----------------

[](#artisan-commands)

```
# Generate a new salt and write to .env
php artisan kasumi:salt:generate

# Display without writing
php artisan kasumi:salt:generate --show

# Overwrite existing salt (with warning)
php artisan kasumi:salt:generate --force
```

Config
------

[](#config)

Publish the config file:

```
php artisan vendor:publish --tag=kasumi-config
```

`config/kasumi.php`:

```
return [
    'scramble_salt' => env('KASUMI_SCRAMBLE_SALT'),

    // Encoder to use. Switch to ChecksumEncoder to add tamper detection (19-char output).
    'encoder' => \Kasumi\Base36Encoder::class,
    // 'encoder' => \Kasumi\ChecksumEncoder::class,
];
```

Notes
-----

[](#notes)

- `scramble(0)` returns `0` (trivial fixed point). Avoid passing `0` if this is a concern.
- Valid input range: `[0, PHP_INT_MAX]` (63-bit non-negative integers).
- The salt must be an **odd integer**. `kasumi:salt:generate` guarantees this.
- No bcmath or GMP extension required — all arithmetic uses native PHP integers.
- The encoded string is always exactly **14 characters** (two zero-padded base36 halves) when using `Base36Encoder`, or **19 characters** when wrapped with `ChecksumEncoder`.
- To unscramble, pass the `ScrambledValue` directly to `scramble()`. Avoid calling `toInt()` on the scrambled value before passing it back, as the intermediate value may exceed `PHP_INT_MAX`.

License
-------

[](#license)

MIT

###  Health Score

38

—

LowBetter than 85% of packages

Maintenance90

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

3

Last Release

46d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/1d5f48320c88e00c997df26416eb613aa49554a6d1e719552a4e00fd83bffec5?d=identicon)[tomonori855-hub](/maintainers/tomonori855-hub)

---

Top Contributors

[![niktomo](https://avatars.githubusercontent.com/u/266456726?v=4)](https://github.com/niktomo "niktomo (15 commits)")

---

Tags

laravelobfuscateintegerbijectionidscramble

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/niktomo-kasumi/health.svg)

```
[![Health](https://phpackages.com/badges/niktomo-kasumi/health.svg)](https://phpackages.com/packages/niktomo-kasumi)
```

###  Alternatives

[cybercog/laravel-optimus

An Optimus bridge for Laravel. Id obfuscation based on Knuth's multiplicative hashing method.

192564.1k](/packages/cybercog-laravel-optimus)[cviebrock/eloquent-typecast

Trait for Eloquent models to force type-casting on retrieved values

2468.0k](/packages/cviebrock-eloquent-typecast)[matriphe/laraciproid

Indonesia city and province data migration and seeder for Laravel.

232.5k](/packages/matriphe-laraciproid)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
