https://github.com/kensnyder/poly-crypto
Encrypt and decrypt data with AES-256 GCM; interoperable with Node and PHP 7.1+
https://github.com/kensnyder/poly-crypto
aes aes-256 aes-256-gcm bcrypt cipher crypto cryptography decrypt decryption digest encrypt encryption random
Last synced: 7 months ago
JSON representation
Encrypt and decrypt data with AES-256 GCM; interoperable with Node and PHP 7.1+
- Host: GitHub
- URL: https://github.com/kensnyder/poly-crypto
- Owner: kensnyder
- License: isc
- Created: 2019-01-26T18:20:47.000Z (about 7 years ago)
- Default Branch: main
- Last Pushed: 2025-01-22T05:10:38.000Z (about 1 year ago)
- Last Synced: 2025-07-07T18:07:49.247Z (7 months ago)
- Topics: aes, aes-256, aes-256-gcm, bcrypt, cipher, crypto, cryptography, decrypt, decryption, digest, encrypt, encryption, random
- Language: TypeScript
- Homepage:
- Size: 868 KB
- Stars: 13
- Watchers: 2
- Forks: 4
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE.md
- Roadmap: ROADMAP.md
Awesome Lists containing this project
README
# poly-crypto
**Poly**glot **Crypto**graphy. High-level cryptographic functions that are
interoperable between NodeJS and PHP 7.2+ (and 8.0+).
[](https://npmjs.com/package/poly-crypto)
[](https://packagist.org/packages/poly-crypto/poly-crypto)
[](https://github.com/search?q=repo:kensnyder/poly-crypto++language:TypeScript&type=code)
[](https://github.com/kensnyder/poly-crypto/actions)
[](https://codecov.io/gh/kensnyder/poly-crypto)
[](https://bundlephobia.com/package/poly-crypto@2.3.0)
[](https://www.npmjs.com/package/poly-crypto?activeTab=dependencies)
[](https://www.npmjs.com/package/poly-crypto)
[](https://opensource.org/licenses/ISC)
## Project Goals
1. APIs that work exactly the same on NodeJS and PHP 7.2+ (and 8.0+)
2. Package for Node that can be used on serverless functions without external C
bindings
3. Two-way symmetric encryption with a key or with password and salt
4. Password hashing
5. Support ESM with tree shaking; support CommonJS; Written in TypeScript
## Installation
You can use PolyCrypto in JavaScript, PHP, or both.
```bash
# NodeJS
npm install poly-crypto
# PHP
composer require poly-crypto/poly-crypto
```
## Cheatsheet
| Section | NodeJS | PHP |
| ----------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------ |
| [Encrypt with key](#encrypt-and-decrypt-with-key) | PolyAES.withKey(key).encrypt(data) | PolyAES::withKey($key)->encrypt($data) |
| [Decrypt with key](#encrypt-and-decrypt-with-key) | PolyAES.withKey(key).decrypt(encrypted) | PolyAES::withKey($key)->decrypt($encrypted) |
| [Encrypt with password](#encrypt-and-decrypt-with-password) | PolyAES.withPassword(password, salt).encrypt(data) | PolyAES::withPassword($password, $salt)->encrypt($data) |
| [Decrypt with password](#encrypt-and-decrypt-with-password) | PolyAES.withPassword(password, salt).decrypt(encrypted) | PolyAES::withPassword($password, $salt)->decrypt($encrypted) |
| [Bcrypt hash](#password-hashing) | PolyBcrypt.hash(password) | PolyBcrypt::hash($password) |
| [Bcrypt verify](#password-hashing) | PolyBcrypt.verify(password, hash) | PolyBcrypt::verify($password, $hash) |
| [Digest functions](#digest-functions) | PolyDigest.sha256(data) | PolyDigest::sha256($data) |
| [Random functions](#random-functions) | PolyRand.slug(length) | PolyRand::slug($length) |
| [Base conversion](#base-conversion) | PolyConvert.base(digits, fromBase, toBase) | PolyRand::base($digits, $fromBase, $toBase) |
## Table of Contents
- [poly-crypto](#poly-crypto)
- [Project Goals](#project-goals)
- [Installation](#installation)
- [Cheatsheet](#cheatsheet)
- [Table of Contents](#table-of-contents)
- [Technology choices](#technology-choices)
- [AES-256 GCM](#aes-256-gcm)
- [Bcrypt](#bcrypt)
- [Randomness](#randomness)
- [Use cases](#use-cases)
- [Misuse](#misuse)
- [AES Encryption](#aes-encryption)
- [Encrypt and decrypt with key](#encrypt-and-decrypt-with-key)
- [Encrypt and decrypt with password](#encrypt-and-decrypt-with-password)
- [Password hashing](#password-hashing)
- [Digest functions](#digest-functions)
- [Random functions](#random-functions)
- [Base conversion](#base-conversion)
- [Command line utilities](#command-line-utilities)
- [Global install of poly-crypto](#global-install-of-poly-crypto)
- [Browser usage](#browser-usage)
- [JavaScript direct import](#javascript-direct-import)
- [Unit tests](#unit-tests)
- [Contributing](#contributing)
- [License](#license)
## Technology choices
### AES-256 GCM
As of December 2022, AES-256 Encryption with GCM block mode is a reputable and
secure method that is available across PHP and NodeJS without any extensions.
With the right arguments and options, these 2 languages can decrypt one
another's encrypted strings using PHP's openssl\_\* functions and npm's
node-forge.
### Bcrypt
As of January 2025, Bcrypt password hashing is reputable and secure. These 2
languages can hash and verify one another's hashes: npm's bcrypt-js and PHP's
password_hash function.
### Randomness
Cryptographic randomness is tricky. These 2 languages can provide secure
randomness:
PHP's random_bytes() and Node's crypto.randomBytes() functions.
## Use cases
poly-crypto's basic use cases:
| | Case | Input | Output | NodeJS |
| --- | -------------------------------------------------------- | ------------------------------------ | ------------------------------- | -------------------------------------------------- |
| 1. | Encrypt data that you can to decrypt later | Encryption key string | base-64 encoded string | PolyAES.withKey(hexKey).encrypt(data) |
| 2. | Encrypt data for a user that he or she can decrypt later | User-supplied password & system salt | base-64 encoded string | PolyAES.withPassword(password, salt).encrypt(data) |
| 3. | Hash passwords with bcrypt | Password string | bcrypt hash | PolyBcrypt.hash(password) |
| 4. | Check if a password matches the given bcrypt hash | Password string & bcrypt hash | True if password matches | PolyBcrypt.verify(password, hash) |
| 5. | Calculate digests (e.g. sha256) | String data | digest string | PolyDigest.sha256(data) |
| 6. | Generate random slugs | number of characters | a string with random characters | PolyRand.slug(numCharacters) |
| 7. | Convert numbers between bases | number to convert | converted number | PolyConvert.base(input, from, to) |
## Misuse
1. **File encryption.** poly-crypto modules are not meant to be used to encrypt
entire files. You'll want to use a C-based library that is designed to
encrypt large amounts of data quickly. For example, consider the following:
1. poly-crypto is not fast for large files.
1. AES-256 GCM encryption can be parallelized in languages that support
threading for faster processing
1. **Streaming data.** PolyAES is not designed to encrypt streaming data.
1. **Secure key storage.** If you store encryption keys or user passwords in
plain text, encryption will not provide protection. You'll want to store keys
in a secure parameter store.
1. **Digests for passwords.** Do not use md5 or any sha digest for hashing
passwords, even if you use salt. PolyBcrypt is the only poly-crypto module
designed for hashing passwords.
### AES Encryption
#### Encrypt and decrypt with key
**Note:** key should be a 64-character hex-encoded string stored in a secure
param store. To generate a cryptographically secure random key,
use `PolyAES.generateKey(64)`.
NodeJS:
```js
import { PolyAES } from 'poly-crypto';
const hexKey = '64-char hex encoded string from secure param store';
const encrypted = PolyAES.withKey(hexKey).encrypt(data);
const decrypted = PolyAES.withKey(hexKey).decrypt(encrypted);
```
PHP:
```php
encrypt($data);
$decrypted = PolyAES::withKey($hexKey)->decrypt($encrypted);
```
**Note:** You can re-use the "cipher" object. For example:
NodeJS:
```js
import { PolyAES } from 'poly-crypto';
const hexKey = '64-char hex encoded string from secure param store';
const cipher = PolyAES.withKey(hexKey);
const encrypted = cipher.encrypt(data);
const decrypted = cipher.decrypt(encrypted);
```
PHP:
```php
encrypt($data);
$decrypted = $cipher->decrypt($encrypted);
```
#### Encrypt and decrypt with password
NodeJS:
```js
import { PolyAES } from 'poly-crypto';
const password = 'String from user';
const salt = 'String from secure param store';
const encrypted = PolyAES.withPassword(password, salt).encrypt(data);
const decrypted = PolyAES.withPassword(password, salt).decrypt(encrypted);
```
PHP:
```php
encrypt($data);
$decrypted = PolyAES::withPassword($password, $salt)->decrypt($encrypted);
```
**Note:** You can re-use the "cipher" as an object.
### Password hashing
Bcrypt hashes are designed to store user passwords with a max length of 72
bytes. If a longer string is passed, an exception will be thrown. Keep in mind
that Unicode characters require multiple bytes.
Bcrypt conveniently stores salt along with the password. That ensures that
identical passwords will get different hashes. As such, you cannot compare two
hashes, you must use the `PolyBcrypt.verify()` function to see if the given
password matches the hash you have on record.
NodeJS:
```js
import { PolyBcrypt } from 'poly-crypto';
const password = 'Password from a user';
const hash = PolyBcrypt.hash(password);
const isCorrect = PolyBcrypt.verify(password, hash);
```
PHP:
```php