Comparing version 1.0.0 to 2.0.0
@@ -1,47 +0,58 @@ | ||
const crypto = require('crypto') | ||
const numbers = '1234567890'.split('') | ||
const crypto = require('crypto'); | ||
const digits = '1234567890'.split(''); | ||
module.exports = function ({ password, algorithm = 'aes-256-cbc', domain = numbers }) { | ||
if (!password) { | ||
throw new Error('`password` is required') | ||
module.exports = function ({ secret, domain = digits }) { | ||
if (!secret) { | ||
throw new Error('`secret` is required'); | ||
} | ||
function enc (text) { | ||
const cipher = crypto.createCipher(algorithm, password) | ||
let crypted = cipher.update(text, 'utf8', 'hex') | ||
crypted += cipher.final('hex') | ||
return crypted | ||
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 = {} | ||
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] | ||
encTable[domain[i]] = sorted[i]; | ||
decTable[sorted[i]] = domain[i]; | ||
} | ||
function validate (text, result) { | ||
function validate(text, result) { | ||
if (text.length !== result.length) { | ||
throw new Error(`some of the input characters are not in the cipher's domain: [${numbers}]`) | ||
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 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 | ||
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 } | ||
} | ||
return { encrypt, decrypt }; | ||
}; |
{ | ||
"name": "node-fpe", | ||
"version": "1.0.0", | ||
"version": "2.0.0", | ||
"description": "Format-preserving encryption implementation", | ||
"main": "lib", | ||
"scripts": { | ||
"test": "standard && tape test" | ||
"test:prettier": "prettier --single-quote --list-different **/*.js", | ||
"fix:prettier": "prettier --write '**/*.js'", | ||
"test": "npm run test:prettier && tape test" | ||
}, | ||
"engines": { | ||
"node": ">=6.6.0" | ||
"node": ">=12.0.0" | ||
}, | ||
"author": "Michael Derazon <mderazon@hotmail.com>", | ||
"bugs:": "https://github.com/mderazon/node-fpe/issues", | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/mderazon/node-fpe.git" | ||
}, | ||
"author": "Michael DeRazon <mderazon@hotmail.com>", | ||
"homepage": "https://github.com/mderazon/node-fpe", | ||
@@ -29,5 +36,5 @@ "keywords": [ | ||
"devDependencies": { | ||
"standard": "^8.5.0", | ||
"tape": "^4.6.2" | ||
"prettier": "^2.0.5", | ||
"tape": "^5.0.1" | ||
} | ||
} |
@@ -1,10 +0,10 @@ | ||
# node-fpe [![Build Status](https://travis-ci.org/mderazon/node-fpe.svg?branch=master)](https://travis-ci.org/mderazon/node-fpe) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/) | ||
# node-fpe | ||
[![Build Status](https://travis-ci.org/mderazon/node-fpe.svg?branch=master)](https://travis-ci.org/mderazon/node-fpe) [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) | ||
> Simple format-preserving encryprion implementation | ||
> Format preserving string substitution encryption | ||
In general, [format-preserving encryption](https://en.wikipedia.org/wiki/Format-preserving_encryption) is a type of encryption such that the output (the ciphertext) is in the same format as the input (the plaintext). | ||
This library uses a simple FPE from a [prefix cipher](https://en.wikipedia.org/wiki/Format-preserving_encryption#FPE_from_a_prefix_cipher). The method is only useful for a small domains, for example numbers or alphanumeric. | ||
This library uses a simple [substitution cipher](https://en.wikipedia.org/wiki/Substitution_cipher) algorithm. Read more about the security of this library in the dedicated section below. | ||
@@ -14,25 +14,26 @@ ## Usage | ||
### Example: | ||
cipher with default domain ([0-9]) and default encryption alg (aes-256-cbc): | ||
cipher with default domain ([0-9]) | ||
```js | ||
const fpe = require(node-fpe) | ||
const cipher = fpe({password: 'secret'}) | ||
const fpe = require('node-fpe'); | ||
const cipher = fpe({ secret: 'secret!' }); | ||
cipher.encrypt('1234567') | ||
// '4185730' | ||
cipher.encrypt('1234567'); | ||
// '7130548' | ||
cipher.decrypt('4185730') | ||
cipher.decrypt('7130548'); | ||
// '1234567' | ||
``` | ||
cipher with custom domain ([A-E]) and default encryption alg (aes-256-cbc): | ||
cipher with a custom domain ([A-E]) | ||
```js | ||
const fpe = require(node-fpe) | ||
const cipher = fpe({password: 'secret', domain: ['A', 'B', 'C', 'D', 'E']}) | ||
const fpe = require('node-fpe'); | ||
const cipher = fpe({ secret: 'secret!', domain: ['A', 'B', 'C', 'D', 'E'] }); | ||
cipher.encrypt('BEEBEE') | ||
// 'CBBCBB' | ||
cipher.encrypt('BEEBEE'); | ||
// 'ABBABB' | ||
cipher.decrypt('CBBCBB') | ||
cipher.decrypt('ABBABB'); | ||
// 'BEEBEE' | ||
@@ -42,6 +43,14 @@ ``` | ||
### Options | ||
Options to pass on to *node-fpe* are: | ||
- `password`: **mandatory**. a secret used in the underlying block cipher. | ||
- `algorithm`: **optional**. the underlying block cipher used. similar to the input to node's [crypto.createCipher()](https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password). **default**: *aes-256-cbc* | ||
Options to pass on to _node-fpe_ are: | ||
- `secret`: **mandatory**. a secret used in the underlying hash function. | ||
- `domain`: **optional**. an array of characters used as the FPE domain. **default**: 0-9 digits | ||
## Security | ||
This module is using the term _format-preserving encryption_, however it is **not** a proper fpe implementation. It is basically a [substitution cipher](https://en.wikipedia.org/wiki/Substitution_cipher), you can use it to scramble and de-scramble strings but it is **not recommended to use it with anything sensitive** as the encryption is weak. | ||
For fpe, there are other libraries available: | ||
- https://github.com/eCollect/node-fe1-fpe |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
5210
50
55
0