password-obscura
Version:
A lightweight NPM package to visually obscure passwords or strings using customizable shift-based and symbol-mapping logic. Inspired by Caesar Cipher — reimagined for modern devs.
383 lines (377 loc) • 11.9 kB
JavaScript
// src/lib/caesar.ts
function caesarCipher(text, shift) {
return text.replace(/[a-z]/gi, (char) => {
const base = char >= "a" && char <= "z" ? 97 : 65;
return String.fromCharCode((char.charCodeAt(0) - base + shift) % 26 + base);
});
}
function caesarDecipher(text, shift) {
return caesarCipher(text, 26 - shift % 26);
}
// src/lib/rot13.ts
function rot13(str) {
return str.replace(/[a-z]/gi, (char) => {
const base = char <= "Z" ? 65 : 97;
return String.fromCharCode((char.charCodeAt(0) - base + 13) % 26 + base);
});
}
// src/lib/symbolMap.ts
var DEFAULT_SYMBOL_MAP = {
// Lowercase letters
a: "\u{1F525}",
b: "\u2B50",
c: "\u{1F31F}",
d: "\u{1F4AB}",
e: "\u2728",
f: "\u{1F308}",
g: "\u{1F30A}",
h: "\u{1F32A}\uFE0F",
i: "\u2744\uFE0F",
j: "\u{1F319}",
k: "\u2600\uFE0F",
l: "\u{1F338}",
m: "\u{1F33A}",
n: "\u{1F33B}",
o: "\u{1F337}",
p: "\u{1F339}",
q: "\u{1F331}",
r: "\u{1F340}",
s: "\u{1F33F}",
t: "\u{1F33E}",
u: "\u{1F344}",
v: "\u{1F335}",
w: "\u{1F334}",
x: "\u{1F38B}",
y: "\u{1F332}",
z: "\u{1F333}",
// Uppercase letters
A: "\u{1F680}",
B: "\u26A1",
C: "\u{1F48E}",
D: "\u{1F3AF}",
E: "\u{1F3AA}",
F: "\u{1F3AD}",
G: "\u{1F3A8}",
H: "\u{1F3B5}",
I: "\u{1F3B8}",
J: "\u{1F3BA}",
K: "\u{1F3BB}",
L: "\u{1F941}",
M: "\u{1F3A4}",
N: "\u{1F3A7}",
O: "\u{1F3AE}",
P: "\u{1F579}\uFE0F",
Q: "\u{1F3B2}",
R: "\u{1F0CF}",
S: "\u{1F38A}",
T: "\u{1F381}",
U: "\u{1F388}",
V: "\u{1F380}",
W: "\u{1F48D}",
X: "\u{1F451}",
Y: "\u{1F531}",
Z: "\u2694\uFE0F",
// Numbers
"0": "\u{1F534}",
"1": "\u{1F7E0}",
"2": "\u{1F7E1}",
"3": "\u{1F7E2}",
"4": "\u{1F535}",
"5": "\u{1F7E3}",
"6": "\u{1F7E4}",
"7": "\u26AB",
"8": "\u26AA",
"9": "\u{1F536}"
};
function symbolMapEncode(text, symbolMap = DEFAULT_SYMBOL_MAP) {
return text.split("").map((char) => symbolMap[char] || char).join("");
}
function symbolMapDecode(text, symbolMap = DEFAULT_SYMBOL_MAP) {
const reverseMap = {};
Object.entries(symbolMap).forEach(([char, symbol]) => {
reverseMap[symbol] = char;
});
const chars = Array.from(text);
return chars.map((char) => reverseMap[char] || char).join("");
}
// src/lib/atbash.ts
function atbashCipher(text) {
return text.replace(/[a-zA-Z]/g, (char) => {
if (char >= "a" && char <= "z") {
return String.fromCharCode(25 - (char.charCodeAt(0) - 97) + 97);
} else {
return String.fromCharCode(25 - (char.charCodeAt(0) - 65) + 65);
}
});
}
var atbashDecipher = atbashCipher;
// src/lib/dynamicCipher.ts
var DEFAULT_TABLES = [
"abcdefghijklmnopqrstuvwxyz",
"zyxwvutsrqponmlkjihgfedcba",
"aeiouybcdfghjklmnpqrstvwxz",
"bcdefghijklmnopqrstuvwxyza"
];
var FIBONACCI_SEQUENCE = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144];
var PRIME_NUMBERS = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37];
function generateShift(position, pattern, baseShift = 3, customShifts) {
let shift;
switch (pattern) {
case "even-odd":
shift = position % 2 === 0 ? baseShift : baseShift + 1;
break;
case "fibonacci":
shift = FIBONACCI_SEQUENCE[position % FIBONACCI_SEQUENCE.length];
break;
case "prime":
shift = PRIME_NUMBERS[position % PRIME_NUMBERS.length];
break;
case "progressive":
shift = baseShift + position % 10;
break;
case "custom":
if (!customShifts || customShifts.length === 0) {
shift = baseShift;
} else {
shift = customShifts[position % customShifts.length];
}
break;
default:
shift = baseShift;
break;
}
return shift % 26;
}
function multiTableCaesar(text, config) {
const { tables, shiftPattern, customShifts, baseShift = 3 } = config;
const standardAlphabet = "abcdefghijklmnopqrstuvwxyz";
return text.split("").map((char, index) => {
if (!/[a-zA-Z]/.test(char)) return char;
const tableIndex = index % tables.length;
const table = tables[tableIndex];
const shift = generateShift(index, shiftPattern, baseShift, customShifts);
const standardIndex = standardAlphabet.indexOf(char.toLowerCase());
if (standardIndex === -1) return char;
const shiftedIndex = (standardIndex + shift) % standardAlphabet.length;
const newChar = table[shiftedIndex];
if (!newChar) return char;
return char === char.toUpperCase() ? newChar.toUpperCase() : newChar;
}).join("");
}
function multiTableCaesarDecrypt(text, config) {
const { tables, shiftPattern, customShifts, baseShift = 3 } = config;
const standardAlphabet = "abcdefghijklmnopqrstuvwxyz";
return text.split("").map((char, index) => {
if (!/[a-zA-Z]/.test(char)) return char;
const tableIndex = index % tables.length;
const table = tables[tableIndex];
const shift = generateShift(index, shiftPattern, baseShift, customShifts);
const tableIndex_char = table.indexOf(char.toLowerCase());
if (tableIndex_char === -1) return char;
const originalIndex = (tableIndex_char - shift + standardAlphabet.length) % standardAlphabet.length;
const originalChar = standardAlphabet[originalIndex];
if (!originalChar) return char;
return char === char.toUpperCase() ? originalChar.toUpperCase() : originalChar;
}).join("");
}
function polyalphabeticCipher(text, config) {
const { keyword, tables = DEFAULT_TABLES } = config;
const keywordRepeated = keyword.repeat(
Math.ceil(text.length / keyword.length)
);
return text.split("").map((char, index) => {
if (!/[a-zA-Z]/.test(char)) return char;
const keyChar = keywordRepeated[index];
const keyShift = keyChar.toLowerCase().charCodeAt(0) - 97;
const tableIndex = keyShift % tables.length;
const table = tables[tableIndex];
const charIndex = table.indexOf(char.toLowerCase());
if (charIndex === -1) return char;
const newIndex = (charIndex + keyShift) % table.length;
const newChar = table[newIndex];
return char === char.toUpperCase() ? newChar.toUpperCase() : newChar;
}).join("");
}
function polyalphabeticDecipher(text, config) {
const { keyword, tables = DEFAULT_TABLES } = config;
const keywordRepeated = keyword.repeat(
Math.ceil(text.length / keyword.length)
);
return text.split("").map((char, index) => {
if (!/[a-zA-Z]/.test(char)) return char;
const keyChar = keywordRepeated[index];
const keyShift = keyChar.toLowerCase().charCodeAt(0) - 97;
const tableIndex = keyShift % tables.length;
const table = tables[tableIndex];
const charIndex = table.indexOf(char.toLowerCase());
if (charIndex === -1) return char;
const newIndex = (charIndex - keyShift + table.length) % table.length;
const newChar = table[newIndex];
return char === char.toUpperCase() ? newChar.toUpperCase() : newChar;
}).join("");
}
function advancedSubstitution(text, layers) {
let result = text;
layers.forEach((layer) => {
var _a, _b;
switch (layer.type) {
case "table":
if (layer.config && layer.config.tables) {
result = multiTableCaesar(result, layer.config);
}
break;
case "shift":
const shift = ((_a = layer.config) == null ? void 0 : _a.shift) || 3;
result = result.replace(/[a-zA-Z]/g, (char) => {
const base = char >= "a" ? 97 : 65;
return String.fromCharCode(
(char.charCodeAt(0) - base + shift) % 26 + base
);
});
break;
case "reverse":
result = result.split("").reverse().join("");
break;
case "transpose":
const blockSize = ((_b = layer.config) == null ? void 0 : _b.blockSize) || 3;
const blocks = [];
for (let i = 0; i < result.length; i += blockSize) {
blocks.push(result.slice(i, i + blockSize));
}
result = blocks.map((block) => block.split("").reverse().join("")).join("");
break;
}
});
return result;
}
function advancedSubstitutionDecrypt(text, layers) {
let result = text;
[...layers].reverse().forEach((layer) => {
var _a, _b;
switch (layer.type) {
case "table":
if (layer.config && layer.config.tables) {
result = multiTableCaesarDecrypt(result, layer.config);
}
break;
case "shift":
const shift = ((_a = layer.config) == null ? void 0 : _a.shift) || 3;
result = result.replace(/[a-zA-Z]/g, (char) => {
const base = char >= "a" ? 97 : 65;
return String.fromCharCode(
(char.charCodeAt(0) - base - shift + 26) % 26 + base
);
});
break;
case "reverse":
result = result.split("").reverse().join("");
break;
case "transpose":
const blockSize = ((_b = layer.config) == null ? void 0 : _b.blockSize) || 3;
const blocks = [];
for (let i = 0; i < result.length; i += blockSize) {
blocks.push(result.slice(i, i + blockSize));
}
result = blocks.map((block) => block.split("").reverse().join("")).join("");
break;
}
});
return result;
}
// src/index.ts
function obscure(input, options) {
switch (options.method) {
case "caesar":
return caesarCipher(input, options.shift ?? 3);
case "rot13":
return rot13(input);
case "symbolMap":
return symbolMapEncode(input, options.symbolMap ?? DEFAULT_SYMBOL_MAP);
case "mirror":
return atbashCipher(input);
case "multiTable":
if (!options.tableConfig) {
options.tableConfig = {
tables: DEFAULT_TABLES,
shiftPattern: "even-odd",
baseShift: 3
};
}
return multiTableCaesar(input, options.tableConfig);
case "polyalphabetic":
if (!options.polyConfig) {
throw new Error("polyConfig is required for polyalphabetic method");
}
return polyalphabeticCipher(input, options.polyConfig);
case "advanced":
if (!options.layers || options.layers.length === 0) {
options.layers = [
{ type: "shift", config: { shift: 3 } },
{
type: "table",
config: {
tables: DEFAULT_TABLES,
shiftPattern: "fibonacci",
baseShift: 2
}
},
{ type: "reverse" }
];
}
return advancedSubstitution(input, options.layers);
default:
throw new Error(`Unsupported method: ${options.method}`);
}
}
function reveal(input, options) {
switch (options.method) {
case "caesar":
return caesarDecipher(input, options.shift ?? 3);
case "rot13":
return rot13(input);
// rot13 is symmetric
case "symbolMap":
return symbolMapDecode(input, options.symbolMap ?? DEFAULT_SYMBOL_MAP);
case "mirror":
return atbashDecipher(input);
// atbash is symmetric
case "multiTable":
if (!options.tableConfig) {
options.tableConfig = {
tables: DEFAULT_TABLES,
shiftPattern: "even-odd",
baseShift: 3
};
}
return multiTableCaesarDecrypt(input, options.tableConfig);
case "polyalphabetic":
if (!options.polyConfig) {
throw new Error("polyConfig is required for polyalphabetic method");
}
return polyalphabeticDecipher(input, options.polyConfig);
case "advanced":
if (!options.layers || options.layers.length === 0) {
options.layers = [
{ type: "shift", config: { shift: 3 } },
{
type: "table",
config: {
tables: DEFAULT_TABLES,
shiftPattern: "fibonacci",
baseShift: 2
}
},
{ type: "reverse" }
];
}
return advancedSubstitutionDecrypt(input, options.layers);
default:
throw new Error(`Unsupported method: ${options.method}`);
}
}
export {
DEFAULT_SYMBOL_MAP,
DEFAULT_TABLES,
obscure,
reveal
};