Skip to content

Instantly share code, notes, and snippets.

@PWrzesinski
Created January 21, 2025 10:42
Show Gist options
  • Save PWrzesinski/da156caead56116cf638f045c96d010f to your computer and use it in GitHub Desktop.
Save PWrzesinski/da156caead56116cf638f045c96d010f to your computer and use it in GitHub Desktop.
// I put this together from 2 sources:
// - https://github.com/cyrildever/feistel-cipher
// - https://stackoverflow.com/a/12590064/2786606
//
// In particular, Daniel Vérité wrote a great answer on SO.
//
// I save this for myself, there are no guarantees about correctness, quality, or anything else.
import 'dart:convert';
import 'package:crypto/crypto.dart';
class _SplitString {
late String left;
late String right;
_SplitString(String data) {
final half = data.length ~/ 2;
left = data.substring(0, half);
right = data.substring(half);
}
}
class FeistelCipher {
static const _paddingCharacter = '0';
static const _visuallyDistinctAlphabet = "abcdefghkmnoprstuwxzACDEFGHJKLMNPQRTUWXY3469";
final String key;
final int rounds;
const FeistelCipher(this.key, this.rounds);
// String implementation
/// Obfuscate the passed data.
String encrypt(String data) {
if (data.length % 2 == 1) {
data = data.padLeft(data.length + 1, _paddingCharacter);
}
// Apply the balanced Feistel cipher.
final parts = _SplitString(data);
for (var i = 0; i < rounds; ++i) {
final tmp = xor(parts.left, round(parts.right, i));
parts.left = parts.right;
parts.right = tmp;
}
return parts.left + parts.right;
}
/// Deobfuscate the passed data.
String decrypt(String obfuscated) {
if (obfuscated.length % 2 != 0) {
throw Exception('Invalid obfuscated data');
}
// Apply the balanced Feistel cipher.
final parts = _SplitString(obfuscated);
for (var i = 0; i < rounds; ++i) {
final tmp = xor(parts.right, round(parts.left, rounds - i - 1));
parts.right = parts.left;
parts.left = tmp;
}
final result = parts.left + parts.right;
if (result.startsWith(_paddingCharacter)) {
return result.substring(1);
} else {
return result;
}
}
String xor(String a, String b) {
final result = StringBuffer();
for (var i = 0; i < a.length; ++i) {
result.write(String.fromCharCode(a.codeUnitAt(i) ^ b.codeUnitAt(i)));
}
return result.toString();
}
String add(String a, String b) {
final result = StringBuffer();
for (var i = 0; i < a.length; ++i) {
result.write(String.fromCharCode(a.codeUnitAt(i) + b.codeUnitAt(i)));
}
return result.toString();
}
/// Returns an extraction of the passed string of the desired length from the passed start index.
/// If the desired length is too long, the key string is repeated.
String extract(String data, int index, int length) {
final startIndex = index % data.length;
final lengthNeeded = startIndex + length;
final repeatCount = (lengthNeeded / data.length).ceil();
return (data * repeatCount).substring(startIndex, lengthNeeded);
}
String getSha256(String value) => sha256.convert(utf8.encode(value)).toString().toLowerCase();
/// Round is the function applied at each round of the obfuscation process to the right side of the Feistel cipher.
String round(String item, int index) {
final addition = add(item, extract(key, index, item.length));
final hashed = getSha256(addition);
return extract(hashed, index, item.length);
}
// int implementation
int roundInt(int value) {
return ((((1366 * value + 150889) % 714025) / 714025) * 32767).round();
}
int encryptInt(int data) {
int left = data >> 16 & 0xFFFF;
int right = data & 0xFFFF;
int leftTemp, rightTemp;
for (var i = 0; i < rounds; ++i) {
leftTemp = right;
rightTemp = left ^ roundInt(right);
left = leftTemp;
right = rightTemp;
}
return (right << 16) + left;
}
String intToVisuallyDistinctString(int value, bool padTo3) {
const alphabet = _visuallyDistinctAlphabet;
const alphabetLength = alphabet.length;
int valueTemp = value;
final result = StringBuffer();
while (valueTemp > 0) {
result.write(alphabet[valueTemp % alphabetLength]);
valueTemp ~/= alphabetLength;
}
final tempResult = result.toString();
if (padTo3) {
return tempResult.padLeft(3, "7");
} else {
return tempResult;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment