{"id":27044874,"url":"https://github.com/kensnyder/poly-crypto","last_synced_at":"2025-07-28T02:02:48.219Z","repository":{"id":34529731,"uuid":"167722886","full_name":"kensnyder/poly-crypto","owner":"kensnyder","description":"Encrypt and decrypt data with AES-256 GCM; interoperable with Node and PHP 7.1+","archived":false,"fork":false,"pushed_at":"2025-01-22T05:10:38.000Z","size":889,"stargazers_count":13,"open_issues_count":1,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-07-07T18:07:49.247Z","etag":null,"topics":["aes","aes-256","aes-256-gcm","bcrypt","cipher","crypto","cryptography","decrypt","decryption","digest","encrypt","encryption","random"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kensnyder.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-01-26T18:20:47.000Z","updated_at":"2025-04-17T13:44:20.000Z","dependencies_parsed_at":"2024-12-24T06:23:53.543Z","dependency_job_id":"df222654-ca01-427d-98a3-f4c122b15d05","html_url":"https://github.com/kensnyder/poly-crypto","commit_stats":{"total_commits":65,"total_committers":4,"mean_commits":16.25,"dds":"0.10769230769230764","last_synced_commit":"7df53b646e9d89dd776e71194f8eaf4a1c09c9d9"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/kensnyder/poly-crypto","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kensnyder%2Fpoly-crypto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kensnyder%2Fpoly-crypto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kensnyder%2Fpoly-crypto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kensnyder%2Fpoly-crypto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kensnyder","download_url":"https://codeload.github.com/kensnyder/poly-crypto/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kensnyder%2Fpoly-crypto/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267451492,"owners_count":24089312,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-28T02:00:09.689Z","response_time":68,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aes","aes-256","aes-256-gcm","bcrypt","cipher","crypto","cryptography","decrypt","decryption","digest","encrypt","encryption","random"],"created_at":"2025-04-05T05:30:31.303Z","updated_at":"2025-07-28T02:02:48.151Z","avatar_url":"https://github.com/kensnyder.png","language":"TypeScript","readme":"# poly-crypto\n\n**Poly**glot **Crypto**graphy. High-level cryptographic functions that are\ninteroperable between NodeJS and PHP 7.2+ (and 8.0+).\n\n[![NPM Link](https://badgen.net/npm/v/poly-crypto?v=2.3.0)](https://npmjs.com/package/poly-crypto)\n[![Packagist Link](https://img.shields.io/packagist/php-v/poly-crypto/poly-crypto/2.1.0)](https://packagist.org/packages/poly-crypto/poly-crypto)\n[![Language](https://badgen.net/static/language/TS?v=2.3.0)](https://github.com/search?q=repo:kensnyder/poly-crypto++language:TypeScript\u0026type=code)\n[![Build Status](https://github.com/kensnyder/poly-crypto/actions/workflows/workflow.yml/badge.svg?v=2.3.0)](https://github.com/kensnyder/poly-crypto/actions)\n[![Code Coverage](https://codecov.io/gh/kensnyder/poly-crypto/branch/main/graph/badge.svg?v=2.3.0)](https://codecov.io/gh/kensnyder/poly-crypto)\n[![Gzipped Size](https://badgen.net/bundlephobia/minzip/poly-crypto?label=minzipped\u0026v=2.3.0)](https://bundlephobia.com/package/poly-crypto@2.3.0)\n[![Dependency details](https://badgen.net/bundlephobia/dependency-count/poly-crypto?v=2.3.0)](https://www.npmjs.com/package/poly-crypto?activeTab=dependencies)\n[![Tree shakeable](https://badgen.net/bundlephobia/tree-shaking/poly-crypto?v=2.3.0)](https://www.npmjs.com/package/poly-crypto)\n[![ISC License](https://badgen.net/github/license/kensnyder/poly-crypto?v=2.3.0)](https://opensource.org/licenses/ISC)\n\n## Project Goals\n\n1. APIs that work exactly the same on NodeJS and PHP 7.2+ (and 8.0+)\n2. Package for Node that can be used on serverless functions without external C\n   bindings\n3. Two-way symmetric encryption with a key or with password and salt\n4. Password hashing\n5. Support ESM with tree shaking; support CommonJS; Written in TypeScript\n\n## Installation\n\nYou can use PolyCrypto in JavaScript, PHP, or both.\n\n```bash\n# NodeJS\nnpm install poly-crypto\n\n# PHP\ncomposer require poly-crypto/poly-crypto\n```\n\n## Cheatsheet\n\n| Section                                                     | NodeJS                                                  | PHP                                                          |\n| ----------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------ |\n| [Encrypt with key](#encrypt-and-decrypt-with-key)           | PolyAES.withKey(key).encrypt(data)                      | PolyAES::withKey($key)-\u003eencrypt($data)                       |\n| [Decrypt with key](#encrypt-and-decrypt-with-key)           | PolyAES.withKey(key).decrypt(encrypted)                 | PolyAES::withKey($key)-\u003edecrypt($encrypted)                  |\n| [Encrypt with password](#encrypt-and-decrypt-with-password) | PolyAES.withPassword(password, salt).encrypt(data)      | PolyAES::withPassword($password, $salt)-\u003eencrypt($data)      |\n| [Decrypt with password](#encrypt-and-decrypt-with-password) | PolyAES.withPassword(password, salt).decrypt(encrypted) | PolyAES::withPassword($password, $salt)-\u003edecrypt($encrypted) |\n| [Bcrypt hash](#password-hashing)                            | PolyBcrypt.hash(password)                               | PolyBcrypt::hash($password)                                  |\n| [Bcrypt verify](#password-hashing)                          | PolyBcrypt.verify(password, hash)                       | PolyBcrypt::verify($password, $hash)                         |\n| [Digest functions](#digest-functions)                       | PolyDigest.sha256(data)                                 | PolyDigest::sha256($data)                                    |\n| [Random functions](#random-functions)                       | PolyRand.slug(length)                                   | PolyRand::slug($length)                                      |\n| [Base conversion](#base-conversion)                         | PolyConvert.base(digits, fromBase, toBase)              | PolyRand::base($digits, $fromBase, $toBase)                  |\n\n## Table of Contents\n\n- [poly-crypto](#poly-crypto)\n  - [Project Goals](#project-goals)\n  - [Installation](#installation)\n  - [Cheatsheet](#cheatsheet)\n  - [Table of Contents](#table-of-contents)\n  - [Technology choices](#technology-choices)\n    - [AES-256 GCM](#aes-256-gcm)\n    - [Bcrypt](#bcrypt)\n    - [Randomness](#randomness)\n  - [Use cases](#use-cases)\n  - [Misuse](#misuse)\n    - [AES Encryption](#aes-encryption)\n      - [Encrypt and decrypt with key](#encrypt-and-decrypt-with-key)\n      - [Encrypt and decrypt with password](#encrypt-and-decrypt-with-password)\n    - [Password hashing](#password-hashing)\n    - [Digest functions](#digest-functions)\n    - [Random functions](#random-functions)\n    - [Base conversion](#base-conversion)\n  - [Command line utilities](#command-line-utilities)\n    - [Global install of poly-crypto](#global-install-of-poly-crypto)\n  - [Browser usage](#browser-usage)\n  - [JavaScript direct import](#javascript-direct-import)\n  - [Unit tests](#unit-tests)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n## Technology choices\n\n### AES-256 GCM\n\nAs of December 2022, AES-256 Encryption with GCM block mode is a reputable and\nsecure method that is available across PHP and NodeJS without any extensions.\nWith the right arguments and options, these 2 languages can decrypt one\nanother's encrypted strings using PHP's openssl\\_\\* functions and npm's\nnode-forge.\n\n### Bcrypt\n\nAs of January 2025, Bcrypt password hashing is reputable and secure. These 2\nlanguages can hash and verify one another's hashes: npm's bcrypt-js and PHP's\npassword_hash function.\n\n### Randomness\n\nCryptographic randomness is tricky. These 2 languages can provide secure\nrandomness:\nPHP's random_bytes() and Node's crypto.randomBytes() functions.\n\n## Use cases\n\npoly-crypto's basic use cases:\n\n|     | Case                                                     | Input                                | Output                          | NodeJS                                             |\n| --- | -------------------------------------------------------- | ------------------------------------ | ------------------------------- | -------------------------------------------------- |\n| 1.  | Encrypt data that you can to decrypt later               | Encryption key string                | base-64 encoded string          | PolyAES.withKey(hexKey).encrypt(data)              |\n| 2.  | Encrypt data for a user that he or she can decrypt later | User-supplied password \u0026 system salt | base-64 encoded string          | PolyAES.withPassword(password, salt).encrypt(data) |\n| 3.  | Hash passwords with bcrypt                               | Password string                      | bcrypt hash                     | PolyBcrypt.hash(password)                          |\n| 4.  | Check if a password matches the given bcrypt hash        | Password string \u0026 bcrypt hash        | True if password matches        | PolyBcrypt.verify(password, hash)                  |\n| 5.  | Calculate digests (e.g. sha256)                          | String data                          | digest string                   | PolyDigest.sha256(data)                            |\n| 6.  | Generate random slugs                                    | number of characters                 | a string with random characters | PolyRand.slug(numCharacters)                       |\n| 7.  | Convert numbers between bases                            | number to convert                    | converted number                | PolyConvert.base(input, from, to)                  |\n\n## Misuse\n\n1. **File encryption.** poly-crypto modules are not meant to be used to encrypt\n   entire files. You'll want to use a C-based library that is designed to\n   encrypt large amounts of data quickly. For example, consider the following:\n    1. poly-crypto is not fast for large files.\n    1. AES-256 GCM encryption can be parallelized in languages that support\n       threading for faster processing\n1. **Streaming data.** PolyAES is not designed to encrypt streaming data.\n1. **Secure key storage.** If you store encryption keys or user passwords in\n   plain text, encryption will not provide protection. You'll want to store keys\n   in a secure parameter store.\n1. **Digests for passwords.** Do not use md5 or any sha digest for hashing\n   passwords, even if you use salt. PolyBcrypt is the only poly-crypto module\n   designed for hashing passwords.\n\n### AES Encryption\n\n#### Encrypt and decrypt with key\n\n**Note:** key should be a 64-character hex-encoded string stored in a secure\nparam store. To generate a cryptographically secure random key,\nuse `PolyAES.generateKey(64)`.\n\nNodeJS:\n\n```js\nimport { PolyAES } from 'poly-crypto';\n\nconst hexKey = '64-char hex encoded string from secure param store';\nconst encrypted = PolyAES.withKey(hexKey).encrypt(data);\nconst decrypted = PolyAES.withKey(hexKey).decrypt(encrypted);\n```\n\nPHP:\n\n```php\n\u003c?php\n\nrequire_once('vendor/autoload.php');\nuse PolyCrypto\\PolyAES;\n\n$hexKey = '64-char hex encoded string from secure param store';\n$encrypted = PolyAES::withKey($hexKey)-\u003eencrypt($data);\n$decrypted = PolyAES::withKey($hexKey)-\u003edecrypt($encrypted);\n```\n\n**Note:** You can re-use the \"cipher\" object. For example:\n\nNodeJS:\n\n```js\nimport { PolyAES } from 'poly-crypto';\n\nconst hexKey = '64-char hex encoded string from secure param store';\nconst cipher = PolyAES.withKey(hexKey);\nconst encrypted = cipher.encrypt(data);\nconst decrypted = cipher.decrypt(encrypted);\n```\n\nPHP:\n\n```php\n\u003c?php\n\nrequire_once('vendor/autoload.php');\nuse PolyCrypto\\PolyAES;\n\n$hexKey = '64-char hex encoded string from secure param store';\n$cipher = PolyAES::withKey($hexKey);\n$encrypted = $cipher-\u003eencrypt($data);\n$decrypted = $cipher-\u003edecrypt($encrypted);\n```\n\n#### Encrypt and decrypt with password\n\nNodeJS:\n\n```js\nimport { PolyAES } from 'poly-crypto';\n\nconst password = 'String from user';\nconst salt = 'String from secure param store';\nconst encrypted = PolyAES.withPassword(password, salt).encrypt(data);\nconst decrypted = PolyAES.withPassword(password, salt).decrypt(encrypted);\n```\n\nPHP:\n\n```php\n\u003c?php\n\nrequire_once('vendor/autoload.php');\nuse PolyCrypto\\PolyAES;\n\n$password = 'String from user';\n$salt = 'String from secure param store';\n$encrypted = PolyAES::withPassword($password, $salt)-\u003eencrypt($data);\n$decrypted = PolyAES::withPassword($password, $salt)-\u003edecrypt($encrypted);\n```\n\n**Note:** You can re-use the \"cipher\" as an object.\n\n### Password hashing\n\nBcrypt hashes are designed to store user passwords with a max length of 72\nbytes. If a longer string is passed, an exception will be thrown. Keep in mind\nthat Unicode characters require multiple bytes.\n\nBcrypt conveniently stores salt along with the password. That ensures that\nidentical passwords will get different hashes. As such, you cannot compare two\nhashes, you must use the `PolyBcrypt.verify()` function to see if the given\npassword matches the hash you have on record.\n\nNodeJS:\n\n```js\nimport { PolyBcrypt } from 'poly-crypto';\n\nconst password = 'Password from a user';\nconst hash = PolyBcrypt.hash(password);\nconst isCorrect = PolyBcrypt.verify(password, hash);\n```\n\nPHP:\n\n```php\n\u003c?php\n\nrequire_once('vendor/autoload.php');\nuse PolyCrypto\\PolyBcrypt;\n\n$password = 'Password from a user';\n$hash = PolyBcrypt::hash($password);\n$isCorrect = PolyBcrypt::verify($password, $hash);\n```\n\n### Digest functions\n\nStandard one-way digest functions.\n\nNodeJS:\n\n```js\nimport { PolyDigest } from 'poly-crypto';\n\nPolyDigest.sha512(data);\nPolyDigest.sha256(data);\nPolyDigest.sha1(data);\nPolyDigest.md5(data);\n```\n\nPHP:\n\n```php\n\u003c?php\n\nrequire_once('vendor/autoload.php');\nuse PolyCrypto\\PolyDigest;\n\nPolyDigest::sha512($data);\nPolyDigest::sha256($data);\nPolyDigest::sha1($data);\nPolyDigest::md5($data);\n```\n\n### Random functions\n\nSimple functions to generate random values synchronously.\n\nNodeJS:\n\n```js\nimport { PolyRand } from 'poly-crypto';\n\n// generate a string containing numbers and letters minus vowels\n// suitable for resources such as URLs with random strings\nPolyRand.slug(length);\n\n// generate a string containing hexadecimal characters\nPolyRand.hex(length);\n\n// generate a string containing numbers and lowercase letters\n// that are unambiguous when written down\nPolyRand.fax(length);\n\n// generate a string containing lowercase letters minus vowels\nconst symbolList = 'bcdfghjklmnpqrstvwxyz'.split('');\nPolyRand.string(length, symbolList);\n\n// generate random bytes in binary form\nPolyRand.bytes(length);\n\n// generate a uuid v4\nPolyRand.uuidv4();\n```\n\nPHP:\n\n```php\n\u003c?php\n\nrequire_once('vendor/autoload.php');\nuse PolyCrypto\\PolyRand;\n\n// generate a string containing numbers and letters minus vowels\n// suitable for resources such as URLs with random strings\nPolyRand::slug($length);\n\n// generate a string containing hexadecimal characters\nPolyRand::hex($length);\n\n// generate a string containing numbers and lowercase letters\n// that are unambiguous when written down\nPolyRand::fax($length);\n\n// generate a string containing lowercase letters minus vowels\n$symbolList = explode('', 'bcdfghjklmnpqrstvwxyz');\nPolyRand::string($length, $symbolList);\n\n// generate random bytes in binary form\nPolyRand::bytes($length);\n\n// generate a uuid v4\nPolyRand::uuidv4();\n```\n\n### Base conversion\n\nSimple functions to convert numbers from one base to another, up to base 95.\n\nUseful in some situations:\n\n- You have a long string in a low base but want fewer characters\n- You need to limit to a smaller character set but don't care about string length\n- You want output to contain no vowels, ensuring no swear words are present\n\nNodeJS:\n\n```js\nimport { PolyConvert } from 'poly-crypto';\n\nPolyConvert.base('1011', 2, 10); // '11'\nPolyConvert.base('FF', 16, 10); // '255'\nPolyConvert.base('18446744073709551615', 10, 62); // 'lYGhA16ahyf'\nPolyConvert.base('And_TypeScript_too!', 92, 62); // 'btjYsDwwuWrElSt7WRf2g'\n\nPolyConvert.fax.applyBase('4BD3DBDFCBCJDBJCD737BC6H43', 10, 21); // '467BCDFHJKMNPQRTVWXY'\n```\n\nPHP:\n\n```php\n\u003c?php\n\nrequire_once('vendor/autoload.php');\nuse PolyCrypto\\PolyConvert;\n\nPolyConvert::base('1011', 2, 10); // '11'\nPolyConvert::base('FF', 16, 10); // '255'\nPolyConvert::base('18446744073709551615', 10, 62); // 'lYGhA16ahyf'\nPolyConvert::base('And_TypeScript_too!', 92, 62); // 'btjYsDwwuWrElSt7WRf2g'\n```\n\n## Command line utilities\n\npoly-crypto functions can be used from the command line if Node JS is installed.\n\n### Global install of poly-crypto\n\nYou'll have the following commands as symlinks:\n\n```bash\n# Global install command and arguments        # JavaScript equivalent\n# ------------------------------------------- # ---------------------\nnpx key-encrypt $hexKey $plaintext            # PolyAES.withKey(hexKey).encrypt(plaintext)\nnpx key-decrypt $hexKey $ciphertext           # PolyAES.withKey(hexKey).decript(ciphertext)\nnpx pass-encrypt $password $salt $plaintext   # PolyAES.withPassword(password, salt).encrypt(plaintext)\nnpx pass-decrypt $password $salt $ciphertext  # PolyAES.withPassword(password, salt).decrypt(plaintext)\nnpx bcrypt-hash $password                     # PolyBcrypt.hash(password)\nnpx bcrypt-verify $password $againstHash      # PolyBcrypt.verify(password, againstHash)\nnpx poly-digest $algo $string                 # PolyDigest[algo](data) where algo is one of: sha1, sha256, sha512, md5\nnpx poly-rand $type $length                   # PolyRand[type](length) where type is one of: slug, hex, fax, bytes, uuidv4\nnpx poly-rand-string $length $symbolString    # PolyRand.string(length, symbolList) where symbolList is a string containing allowed characters\nnpx poly-convert-base $input $from $to        # PolyConvert.base(input, from, to)\n```\n\n## Browser usage\n\nAll poly-crypto modules do indeed function in the browser. There are only a few use\ncases where encrypting in the browser is a good idea. If you have a good reason to\nuse poly-crypto in the browser, see the following section for instructions on\ndirectly importing a Poly\\* module.\n\n## JavaScript direct import\n\nIf you are using ESM or a bundler such as\n[vite](https://vitejs.dev) or [esbuild](https://esbuild.github.io)\nyou will benefit from tree shaking by using an import statement.\n\n```js\nimport { PolyBcrypt } from 'poly-crypto';\n```\n\n## Unit tests\n\n```bash\n# test both languages\nnpm run test:all\n\n# PHP\n./vendor/bin/kahlan --spec=php/tests\n\n# NodeJS\nnpm test\n```\n\n## Contributing\n\nContributions welcome! See\n[CONTRIBUTING.md](https://github.com/kensnyder/poly-crypto/blob/master/CONTRIBUTING.md)\n.\n\n## License\n\nOpen Source, under the [ISC License](https://opensource.org/licenses/ISC).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkensnyder%2Fpoly-crypto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkensnyder%2Fpoly-crypto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkensnyder%2Fpoly-crypto/lists"}