{"id":13438078,"url":"https://github.com/ricmoo/scrypt-js","last_synced_at":"2025-04-04T13:09:00.631Z","repository":{"id":45255697,"uuid":"53655519","full_name":"ricmoo/scrypt-js","owner":"ricmoo","description":"Pure JavaScript implementation of the scrypt password-based key derivation function.","archived":false,"fork":false,"pushed_at":"2022-12-30T19:03:46.000Z","size":138,"stargazers_count":143,"open_issues_count":12,"forks_count":27,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-28T12:06:25.316Z","etag":null,"topics":["javascript","scrypt"],"latest_commit_sha":null,"homepage":"http://ricmoo.github.io/scrypt-js/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ricmoo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null}},"created_at":"2016-03-11T09:27:23.000Z","updated_at":"2025-03-28T06:02:17.000Z","dependencies_parsed_at":"2023-01-31T13:45:26.120Z","dependency_job_id":null,"html_url":"https://github.com/ricmoo/scrypt-js","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricmoo%2Fscrypt-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricmoo%2Fscrypt-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricmoo%2Fscrypt-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricmoo%2Fscrypt-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ricmoo","download_url":"https://codeload.github.com/ricmoo/scrypt-js/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247182342,"owners_count":20897379,"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","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":["javascript","scrypt"],"created_at":"2024-07-31T03:01:02.733Z","updated_at":"2025-04-04T13:09:00.614Z","avatar_url":"https://github.com/ricmoo.png","language":"JavaScript","readme":"scrypt\n======\n\nThe [scrypt](https://en.wikipedia.org/wiki/Scrypt) password-base key derivation\nfunction (pbkdf) is an algorithm designed to be brute-force resistant that\nconverts human readable passwords into fixed length arrays of bytes, which can\nthen be used as a key for symmetric block ciphers, private keys, et cetera.\n\n### Features:\n- **Non-blocking** - Gives other events in the event loop opportunities to run (asynchronous)\n- **Cancellable** - If the key is no longer required, the computation can be cancelled\n- **Progress Callback** - Provides the current progress of key derivation as a percentage complete\n\nTuning\n------\n\nThe scrypt algorithm is, by design, expensive to execute, which increases the amount of time an attacker requires in order to brute force guess a password, adjustable by several parameters which can be tuned:\n- **N** - The CPU/memory cost; increasing this increases the overall difficulty\n- **r** - The block size; increasing this increases the dependency on memory latency and bandwidth\n- **p** - The parallelization cost; increasing this increases the dependency on multi-processing\n\n\n\nInstalling\n----------\n\n**node.js**\n\nIf you do not require the progress callback or cancellable features, and your application\nis specific to *node.js*, you should likely use the\n[built-in crypto package](https://nodejs.org/api/crypto.html#crypto_crypto_scrypt_password_salt_keylen_options_callback).\n\nOtherwise, to install in node.js, use:\n\n```\nnpm install scrypt-js\n```\n\n\n**browser**\n\n```html\n\u003cscript src=\"https://raw.githubusercontent.com/ricmoo/scrypt-js/master/scrypt.js\" type=\"text/javascript\"\u003e\u003c/script\u003e\n```\n\nAPI\n---\n\n**scrypt . scrypt ( password , salt , N , r , p , dkLen [ , progressCallback ] )** *=\u003e Promise\u003cUint8Array\u003e*\n\nCompute the scrypt PBKDF asynchronously using a Promise. If *progressCallback* is\nprovided, it is periodically called with a single parameter, a number between 0 and\n1 (inclusive) indicating the completion progress; it will **always** emit 0 at the\nbeginning and 1 at the end, and numbers between may repeat.\n\n**scrypt . syncScrypt ( password , salt , N , r , p , dkLen )** *=\u003e Uint8Array*\n\nCompute the scrypt PBKDF synchronously. Keep in mind this may stall UI and other tasks and the\nasynchronous version is highly preferred.\n\n\nExample\n-------\n\n```html\n\u003chtml\u003e\n  \u003cbody\u003e\n    \u003cdiv\u003e\u003cspan id=\"progress\"\u003e\u003c/span\u003e% complete...\u003c/div\u003e\n    \u003c!-- These two libraries are highly recommended for encoding password/salt --\u003e\n    \u003cscript src=\"libs/buffer.js\" type=\"text/javascript\"\u003e\u003c/script\u003e\n\n    \u003c!-- This shim library greatly improves performance of the scrypt algorithm --\u003e\n    \u003cscript src=\"libs/setImmediate.js\" type=\"text/javascript\"\u003e\u003c/script\u003e\n\n    \u003cscript src=\"index.js\" type=\"text/javascript\"\u003e\u003c/script\u003e\n    \u003cscript type=\"text/javascript\"\u003e\n\n      // See the section below: \"Encoding Notes\"\n      const password = new buffer.SlowBuffer(\"anyPassword\".normalize('NFKC'));\n      const salt = new buffer.SlowBuffer(\"someSalt\".normalize('NFKC'));\n\n      const N = 1024, r = 8, p = 1;\n      const dkLen = 32;\n\n      function updateInterface(progress) {\n          document.getElementById(\"progress\").textContent = Math.trunc(100 * progress);\n      }\n\n      // Async\n      const keyPromise = scrypt.scrypt(password, salt, N, r, p, dkLen, updateInterface);\n\n      keyPromise.then(function(key) {\n          console.log(\"Derived Key (async): \", key);\n      });\n\n      // Sync\n      const key = scrypt.syncScrypt(password, salt, N, r, p, dkLen);\n      console.log(\"Derived Key (sync): \", key);\n    \u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\nEncoding Notes\n--------------\n\n```\nTL;DR - either only allow ASCII characters in passwords, or use\n        String.prototype.normalize('NFKC') on any password\n```\n\nIt is *HIGHLY* recommended that you do **NOT** pass strings into this (or any password-base key derivation function) library without careful consideration; you should convert your strings to a canonical format that you will use consistently across all platforms.\n\nWhen encoding passwords with UTF-8, it is important to realize that there may be multiple UTF-8 representations of a given string. Since the key generated by a password-base key derivation function is *dependent on the specific bytes*, this matters a great deal.\n\n**Composed vs. Decomposed**\n\nCertain UTF-8 code points can be combined with other characters to create composed characters. For example, the letter *a with the umlaut diacritic mark* (two dots over it) can be expressed two ways; as its composed form, U+00FC; or its decomposed form, which is the letter \"u\" followed by U+0308 (which basically means modify the previous character by adding an umlaut to it).\n\n```javascript\n// In the following two cases, a \"u\" with an umlaut would be seen\n\u003e '\\u00fc'\n\u003e 'u\\u0308'\n\n\n// In its composed form, it is 2 bytes long\n\u003e new Buffer('u\\u0308'.normalize('NFKC'))\n\u003cBuffer c3 bc\u003e\n\u003e new Buffer('\\u00fc')\n\u003cBuffer c3 bc\u003e\n\n// Whereas the decomposed form is 3 bytes, the letter u followed by U+0308\n\u003e new Buffer('\\u00fc'.normalize('NFKD'))\n\u003cBuffer 75 cc 88\u003e\n\u003e new Buffer('u\\u0308')\n\u003cBuffer 75 cc 88\u003e\n```\n\n\n**Compatibility equivalence mode**\n\nCertain strings are often displayed the same, even though they may have different semantic means. For example, UTF-8 provides a code point for the roman number for one, which appears as the letter I, in most fonts identically. Compatibility equivalence will fold these two cases into simply the capital letter I.\n\n```\n\u003e '\\u2160'\n'I'\n\u003e 'I'\n'I'\n\u003e '\\u2160' === 'I'\nfalse\n\u003e '\\u2160'.normalize('NFKC') === 'I'\ntrue\n```\n\n\n**Normalizing**\n\nThe `normalize()` method of a string can be used to convert a string to a\nspecific form. Without going into too much detail, I generally recommend\n`NFKC`, however if you wish to dive deeper into this, a nice short summary\ncan be found in Pythons [unicodedata module](https://docs.python.org/2/library/unicodedata.html#unicodedata.normalize)'s\ndocumentation.\n\nFor browsers without `normalize()` support, the [npm unorm module](https://www.npmjs.com/package/unorm)\ncan be used to polyfill strings.\n\n\n**Another example of encoding woes**\n\nOne quick story I will share is a project which used the `SHA256(encodeURI(password))` as\na key, which (ignoring [rainbow table attacks](https://en.wikipedia.org/wiki/Rainbow_table))\nhad an unfortunate consequence of old web browsers replacing spaces with `+` while on new web\nbrowsers, replacing it with a `%20`, causing issues for anyone who used spaces in their password.\n\n\n### Suggestions\n\n- While it may be inconvenient to many international users, one option is to restrict passwords to a safe subset of ASCII, for example: `/^[A-Za-z0-9!@#$%^\u0026*()]+$/`.\n- My personal recommendation is to normalize to the NFKC form, however, one could imagine setting their password to a Chinese phrase on one computer, and then one day using a computer that does not have Chinese input capabilities and therefore be unable to log in.\n\n**See:** [Unicode Equivalence](https://en.wikipedia.org/wiki/Unicode_equivalence)\n\n\nTests\n-----\n\nThe test cases from the [scrypt whitepaper](http://www.tarsnap.com/scrypt/scrypt.pdf) are included in `test/test-vectors.json` and can be run using:\n\n```javascript\nnpm test\n```\n\nSpecial Thanks\n--------------\n\nI would like to thank @dchest for his [scrypt-async](https://github.com/dchest/scrypt-async-js)\nlibrary and for his assistance providing feedback and optimization suggestions.\n\n\nLicense\n-------\n\nMIT license.\n\n\nReferences\n----------\n\n- [scrypt white paper](http://www.tarsnap.com/scrypt/scrypt.pdf)\n- [Wikipedia](https://en.wikipedia.org/wiki/Scrypt)\n- [scrypt-async npm module](https://www.npmjs.com/package/scrypt-async)\n- [scryptsy npm module](https://www.npmjs.com/package/scryptsy)\n- [Unicode Equivalence](https://en.wikipedia.org/wiki/Unicode_equivalence)\n\n\nDonations\n---------\n\nObviously, it's all licensed under the MIT license, so use it as you wish;\nbut if you'd like to buy me a coffee, I won't complain. =)\n\n- Ethereum - `ricmoo.eth`\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fricmoo%2Fscrypt-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fricmoo%2Fscrypt-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fricmoo%2Fscrypt-js/lists"}