FISE rules define how data is transformed and protected. The beauty of FISE is its simplicity - you only need to define 3 security points to create unique, secure rules.
Purpose: Calculates where to insert the encoded salt length metadata into the ciphertext.
Why it matters: This is the primary security point. Different apps use different offset calculations, creating unique "encryption dialects."
Example:
offset(cipherText: string, ctx: FiseContext): number {
const t = ctx.timestamp ?? 0;
const len = cipherText.length || 1;
return (len * 7 + (t % 11)) % len;
}Key Points:
- ✅ Must vary per app - This is what makes each deployment unique
- ✅ Can use timestamp for temporal rotation
- ✅ Can use ciphertext length, salt length, or any combination
- ✅ Simple math is enough - no complex cryptography needed
Purpose: Encodes the salt length as a string representation.
Why it matters: Different encodings create format diversity - the metadata looks different across apps.
Example:
encodeLength(len: number, ctx: FiseContext): string {
return len.toString(36).padStart(2, "0"); // Base36 encoding
}Common Encodings:
- Base36 (default):
len.toString(36).padStart(2, "0") - Hex:
len.toString(16).padStart(2, "0") - Base62: Custom implementation
- Custom: Any character set you want
Purpose: Decodes the encoded salt length back to a number.
Why it matters: Must match encodeLength - decode(encode(len)) === len.
Example:
decodeLength(encoded: string, ctx: FiseContext): number {
return parseInt(encoded, 36); // Base36 decoding
}Purpose: Define the search range for salt length (default: 10-99).
When to use: If you want a different range for your salt lengths.
saltRange: { min: 20, max: 150 }Note: This is NOT a security point - the range doesn't create diversity, but a wider range increases brute-force search space.
Purpose: Custom salt extraction logic (default: tail-based).
When to use: If you want head-based or custom salt placement.
// Head-based salt
extractSalt(envelope, saltLen) {
return envelope.slice(0, saltLen);
}
stripSalt(envelope, saltLen) {
return envelope.slice(saltLen);
}The simplest approach - just copy defaultRules and modify what you need:
import { defaultRules } from "fise";
const myRules = {
...defaultRules,
offset(c, ctx) {
// Your unique offset - just change the multiplier/modulo!
const t = ctx.timestamp ?? 0;
return (c.length * 13 + (t % 17)) % c.length;
}
};Quick presets for common patterns:
import { FiseBuilder } from "fise";
// Use a preset
const rules = FiseBuilder.defaults().build();
const rules = FiseBuilder.hex().build();
const rules = FiseBuilder.timestamp(13, 17).build();Fluent API for building rules:
import { FiseBuilder } from "fise";
const rules = FiseBuilder.create()
.withOffset((c, ctx) => {
const t = ctx.timestamp ?? 0;
return (c.length * 7 + (t % 11)) % c.length;
})
.withEncodeLength((len) => len.toString(36).padStart(2, "0"))
.withDecodeLength((encoded) => parseInt(encoded, 36))
.withSaltRange(10, 99)
.build();Define rules directly as an object:
const rules: FiseRules = {
offset(cipherText, ctx) {
const t = ctx.timestamp ?? 0;
return (cipherText.length * 7 + (t % 11)) % cipherText.length;
},
encodeLength(len) {
return len.toString(36).padStart(2, "0");
},
decodeLength(encoded) {
return parseInt(encoded, 36);
}
};-
Always customize
offset()- This is your primary security point- Use different multipliers/modulos per app
- Consider using app-specific constants
-
Use timestamp for rotation - Pass
timestampin options (backend only):// backend.ts fiseEncrypt(text, cipher, rules, { timestamp: Math.floor(Date.now() / 60000) });
-
Keep it simple - The 3 security points are enough for most use cases
- Don't overcomplicate - simple math is powerful
- Focus on making
offset()unique per app
-
Rotate rules periodically - Change your offset function over time
- This makes reverse-engineered decoders obsolete quickly
-
Test your rules - Ensure
decodeLength(encodeLength(len)) === len
offset(c, ctx) {
const t = ctx.timestamp ?? 0;
return (c.length * 7 + (t % 11)) % c.length;
}offset(c) {
return Math.floor(c.length / 2);
}offset(c) {
return c.length % 7;
}offset(c, ctx) {
const t = ctx.timestamp ?? 0;
const saltLen = ctx.saltLength ?? 10;
return (c.length * 7 + (t % 11) + (saltLen * 3)) % c.length;
}FISE automatically handles:
- ✅ Salt extraction (default: tail-based)
- ✅ Brute-force search for salt length
- ✅ Metadata size inference
- ✅ All internal validation logic
You only need to focus on the 3 security points!
- Quick Start Guide - Get started in 30 seconds
- Quick Start - Examples of unique rules and developer variations
- Whitepaper - Complete technical specification and architecture