node-fpe
Version:
Format-preserving encryption implementation
59 lines (50 loc) • 1.36 kB
JavaScript
const crypto = require('crypto');
const digits = '1234567890'.split('');
module.exports = function ({ secret, domain = digits }) {
if (!secret) {
throw new Error('`secret` is required');
}
function enc(text) {
return crypto.createHmac('sha256', secret).update(text).digest('hex');
}
// create a permutation of domain
const sorted = domain
.map((c) => c)
.sort((c1, c2) => enc(c1).localeCompare(enc(c2)));
const encTable = {};
const decTable = {};
for (let i in domain) {
encTable[domain[i]] = sorted[i];
decTable[sorted[i]] = domain[i];
}
function validate(text, result) {
if (text.length !== result.length) {
throw new Error(
`some of the input characters are not in the cipher's domain: [${domain}]`,
);
}
}
function encrypt(text) {
if (typeof text !== 'string') {
throw new Error('input is not a string');
}
const encrypted = text
.split('')
.map((c) => encTable[c])
.join('');
validate(text, encrypted);
return encrypted;
}
function decrypt(text) {
if (typeof text !== 'string') {
throw new Error('input is not a string');
}
const decrypted = text
.split('')
.map((c) => decTable[c])
.join('');
validate(text, decrypted);
return decrypted;
}
return { encrypt, decrypt };
};