{"id":15452114,"url":"https://github.com/soatok/constant-time-js","last_synced_at":"2025-10-10T00:03:14.055Z","repository":{"id":54522189,"uuid":"299462939","full_name":"soatok/constant-time-js","owner":"soatok","description":"Constant-time JavaScript functions","archived":false,"fork":false,"pushed_at":"2022-10-02T23:03:29.000Z","size":203,"stargazers_count":58,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-10T00:03:09.498Z","etag":null,"topics":["constant-time","cryptography-algorithms","cryptography-utilities","javascript-library","typescript-library"],"latest_commit_sha":null,"homepage":"https://soatok.blog/2020/08/27/soatoks-guide-to-side-channel-attacks/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/soatok.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-09-29T00:24:55.000Z","updated_at":"2025-09-18T02:14:26.000Z","dependencies_parsed_at":"2022-08-13T18:31:14.031Z","dependency_job_id":null,"html_url":"https://github.com/soatok/constant-time-js","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/soatok/constant-time-js","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soatok%2Fconstant-time-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soatok%2Fconstant-time-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soatok%2Fconstant-time-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soatok%2Fconstant-time-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/soatok","download_url":"https://codeload.github.com/soatok/constant-time-js/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soatok%2Fconstant-time-js/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279002369,"owners_count":26083355,"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-10-09T02:00:07.460Z","response_time":59,"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":["constant-time","cryptography-algorithms","cryptography-utilities","javascript-library","typescript-library"],"created_at":"2024-10-01T21:41:18.117Z","updated_at":"2025-10-10T00:03:14.013Z","avatar_url":"https://github.com/soatok.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Constant-Time JavaScript\n\nConstant-time algorithms written in TypeScript.\n\n[![Build Status](https://github.com/soatok/constant-time-js/actions/workflows/ci.yml/badge.svg)](https://github.com/soatok/soatok/constant-time-js/actions)\n[![npm version](https://img.shields.io/npm/v/constant-time-js.svg)](https://npm.im/constant-time-js)\n\n**Important**: This Github repository is the companion to [Soatok's Guide to Side-Channel Attacks](https://soatok.blog/2020/08/27/soatoks-guide-to-side-channel-attacks/).\nDo not use this in production, especially if you don't have the budget for a cryptography audit.\n\n![Mind Blowing, right?](https://soatok.files.wordpress.com/2020/08/soatoktelegrams2020-01.png)\n\n## Installing and Usage\n\nSimply add `constant-time-js` to your dependencies section. One way to do this is with `npm`:\n\n```terminal\nnpm install --save constant-time-js\n```\n\nNext, you can import the modules you need.\n\nFor JavaScript users:\n\n```js\nconst { compare, bignum } = require('constant-time-js');\n```\n\nTor TypeScript users:\n\n```typescript\nimport { compare, bignum } from 'constant-time-js';\n```\n\nPlease refer to the documentation below for what each function/class does.\n\n## Documentation\n\nThis is just a quick outline of what each function does.\n\n* `compare(a, b)` - Compare two `Uint8Array` objects.\n  * [Explanation](https://soatok.blog/2020/08/27/soatoks-guide-to-side-channel-attacks/#string-inequality)\n  * Returns `-1` if `a \u003c b`\n  * Returns `1` if `a \u003e b`\n  * Returns `0` if `a === b`\n  * Throws an `Error` if `a.length !== b.length`\n* `compare_ints(a, b)` - Compare two integers.\n  * [Explanation](https://soatok.blog/2020/08/27/soatoks-guide-to-side-channel-attacks/#string-inequality)\n  * Returns `-1` if `a \u003c b`\n  * Returns `1` if `a \u003e b`\n  * Returns `0` if `a === b`\n* `equals(a, b)` - Are these `Uint8Array` objects equal?\n  * [Explanation](https://soatok.blog/2020/08/27/soatoks-guide-to-side-channel-attacks/#string-comparison)\n  * Returns `true` if they are equal.\n  * Returns `false` if they are not equal.\n  * Throws an `Error` if `a.length !== b.length`\n* `hmac_equals(a, b)` - Are these `Uint8Array` objects equal? (Using HMAC to compare.)\n  * [Explanation](https://soatok.blog/2020/08/27/soatoks-guide-to-side-channel-attacks/#double-hmac)\n  * Returns `true` if they are equal.\n  * Returns `false` if they are not equal.\n  * Throws an `Error` if `a.length !== b.length`\n* `intdiv(N, D)` - Divide `N` into `D`, discarding remainder.\n  * [Explanation](https://soatok.blog/2020/08/27/soatoks-guide-to-side-channel-attacks/#integer-division)\n  * Returns an integer.\n* `modulo(N, D)` - Divide `N` into `D`, return the remainder.\n  * [Explanation](https://soatok.blog/2020/08/27/soatoks-guide-to-side-channel-attacks/#integer-division)\n  * Returns an integer.\n* `resize(buf, size)` - Return a resized `Uint8Array` object (to side-step memory access leakage)\n* `select(x, a, b)` - Read it as a ternary. If `x` is true, returns `a`. Otherwise, returns `b`.\n  * [Explanation](https://soatok.blog/2020/08/27/soatoks-guide-to-side-channel-attacks/#conditional-select)\n  * `x` must be a `boolean`\n  * `a` must be a `Uint8Array`\n  * `b` must be a `Uint8Array`\n  * Throws an `Error` if `a.length !== b.length`\n* `select_ints(x, a, b)` - Read it as a ternary. If `x` is even, returns `a`. Otherwise, returns `b`. \n  (You should pass `1` or `0` for `x`).\n  * [Explanation](https://soatok.blog/2020/08/27/soatoks-guide-to-side-channel-attacks/#conditional-select)\n  * `x` must be a `boolean`\n  * `a` must be a `number`\n  * `b` must be a `number`\n* `trim_zeroes_left(buf)`\n  * [Explanation](https://soatok.blog/2020/08/27/soatoks-guide-to-side-channel-attacks/#null-byte-trimming)\n  * `buf` must be a `Uint8Array`\n  * Returns a `Uint8Array`\n* `trim_zeroes_right(buf)`\n  * [Explanation](https://soatok.blog/2020/08/27/soatoks-guide-to-side-channel-attacks/#null-byte-trimming)\n  * `buf` must be a `Uint8Array`\n  * Returns a `Uint8Array`\n\n### BigNumber\n\nOur BigNumber implementation aims to be constant-time for the magnitude of the numbers\n(i.e. number of limbs or bytes, regardless of how many bits are significant).\n\nUnless otherwise stated, all of our APIs expect `Uint8Array` objects (`Buffer` extends\nfrom `Uint8Array` and should work too, but we return `Uint8Array` objects, not `Buffer`\nobjects).\n\nUnless otherwise stated, all `Uint8Array` objects are **big-endian byte order**.\n\nUnless otherwise stated, all `Uint8Array` objects assume **unsigned integer** behavior.\n\nUnless otherwise stated, all of the `bignum` methods are immutable (meaning: they return\na new `Uint8Array` object rather than mutating the input arrays).  \n\n#### bignum.add()\n\nReturns `a + b`. Overflow is discarded.\n\n```typescript\n/** \n * @var {Uint8Array} a\n * @var {Uint8Array} b\n */\nconst c: Uint8Array = bignum.add(a, b);\n```\n#### bignum.and()\n\nReturns `a \u0026 b` (bitwise AND).\n\n```typescript\n/** \n * @var {Uint8Array} a\n * @var {Uint8Array} b\n */\nconst c: Uint8Array = bignum.and(a, b);\n```\n\n#### bignum.count_trailing_zero_bits()\n\nCounts the number of `0` bits beneath the most significant `1` bit.\n\nReturns a **BigInt** (the native JS type), since the number of bits may\nexceed 2^32 for an array that is less than 2^32 elements long.\n\n```typescript\n/** \n * @var {Uint8Array} a\n */\nconst c: bigint = bignum.count_trailing_zero_bits(a, b);\n```\n\n#### bignum.divide()\n\nCalculate `a / b`, discarding the remainder.\n\n```typescript\n/**\n * @var {Uint8Array} a\n * @var {Uint8Array} b\n */\nconst c: Uint8Array = bignum.divide(a, b);\n```\n\n#### bignum.gcd()\n\nCalculate the Greatest Common Denominator of two integers.\n\n```typescript\n/** \n * @var {Uint8Array} a\n * @var {Uint8Array} b\n */\nconst c: Uint8Array = bignum.gcd(a, b);\n```\n\n#### bignum.is_nonzero()\n\nReturns true if this number is not equal to zero?\n\n```typescript\n/**\n * @var {Uint8Array} x\n */\nconst check: boolean = bignum.is_nonzero(x);\n```\n\n#### bignum.lsb()\n\nReturns the least significant bit of a big number.\n(If `0`, this is a multiple of two.)\n\n```typescript\n/**\n * @var {Uint8Array} x\n */\nconst least: number = bignum.lsb(x);\n```\n\n#### bignum.lshift1()\n\n**Mutates the input array.**\n\nLeft-shift by 1. This is used internally in some algorithms.\n\n```typescript\n/** \n * @var {Uint8Array} a\n */\nlshift1(a);\n// `a` is now double its previous value\n```\n\n#### bignum.modulo()\n\nCalculate `a mod b`.\n\n```typescript\n/**\n * @var {Uint8Array} a\n * @var {Uint8Array} b\n */\nconst c: Uint8Array = bignum.modulo(a, b);\n```\n\n#### bignum.modInverse()\n\nCalculate the modular inverse of two integers.\n\n**Throws if `gcd(a, b)` is not equal to `1`.**\n\n```typescript\n/** \n * @var {Uint8Array} a\n * @var {Uint8Array} b\n */\nlet one_over_a: Uint8Array;\ntry {\n    one_over_a = bignum.modInverse(a, b);\n} catch (e) {\n    // Handle exception when 1/a is not defined (mod b).\n}\n```\n\n#### bignum.modPow()\n\nModular exponentiation.\n\n```typescript\n/**\n * @var {Uint8Array} base\n * @var {Uint8Array} exp\n * @var {Uint8Array} mod\n */\nconst out: Uint8Array = bignum.modPow(base, exp, mod);\n```\n\n#### bignum.msb()\n\nReturns the most significant bit of a big number.\n\n```typescript\n/**\n * @var {Uint8Array} x\n */\nconst most: number = bignum.msb(x);\n```\n\n#### bignum.multiply()\n\nMultiply two big numbers, return the product.\n\nThe output size will be larger than the inputs.\n\n```typescript\n/**\n * @var {Uint8Array} x\n * @var {Uint8Array} y\n */\nconst z: Uint8Array = bignum.multiply(x, y);\n```\n\n#### bignum.normalize()\n\nResize an Uint8Array to the desired length.\n\nThe default behavior is to treat the number as signed (thereby filling in the\nleft with 0xFF bytes if the most significant bit of the input Uint8Array is set).\n\nPass `true` to the optional third argument to always zero-fill this padding value.\n\n```typescript\n/** \n * @var {Uint8Array} a\n * @var {number} len\n */\nconst c: Uint8Array = bignum.normalize(a, len);\n```\n\n#### bignum.or()\n\nReturns `a | b` (bitwise OR).\n\n```typescript\n/** \n * @var {Uint8Array} a\n * @var {Uint8Array} b\n */\nconst c: Uint8Array = bignum.or(a, b);\n```\n\n\n#### bignum.pow()\n\nExponentiation.\n\n```typescript\n/** \n * @var {Uint8Array} a\n * @var {Uint8Array} n\n */\nconst c: Uint8Array = bignum.pow(a, n);\n```\n\n#### bignum.rshift1()\n\n**Mutates the input array.**\n\nRight-shift by 1. This is used internally in some algorithms.\n\n```typescript\n/** \n * @var {Uint8Array} a\n */\nrshift1(a);\n// `a` is half double its previous value\n```\n\nThe default behavior is congruent to JavaScript's `\u003e\u003e` operator.\nFor an unsigned right shift (`\u003e\u003e\u003e`), pass `true` as the second argument:\n\n```\nrshift1(a, true);\n```\n\n#### bignum.shift_left()\n\nShift left by an arbitrary amount.\n\n```typescript\n/**\n * @var {Uint8Array} x\n */\nconst y: Uint8Array = bignum.shift_left(x, 3n);\n// y :=  8 * x\n```\n\n#### bignum.shift_right()\n\nShift right by an arbitrary amount.\n\n```typescript\n/**\n * @var {Uint8Array} x\n */\nconst y: Uint8Array = bignum.shift_right(x, 3n);\n// y := x / 8\n```\n\n#### bignum.sub()\n\nReturns `a - b`. Use `msb()` to check if the output is negative.\n\n```typescript\n/** \n * @var {Uint8Array} a\n * @var {Uint8Array} b\n */\nconst c: Uint8Array = bignum.sub(a, b);\n```\n\n#### bignum.xor()\n\nReturns `a ^ b` (bitwise XOR).\n\n```typescript\n/** \n * @var {Uint8Array} a\n * @var {Uint8Array} b\n */\nconst c: Uint8Array = bignum.xor(a, b);\n```\n\n## Limitations\n\n### Potentially Dangerous on 32-bit Applications\n\n32-bit v8 (and, presumably, a lot of other 32-bit implementations) do things wrong, \nand our implementation might be variable-time on it.\n\nSpecifically, the most significant bit of a 32-bit integer distinguishes values from pointers.\nAs a result, accessing the highest bit of a 32-bit number in 32-bit JavaScript engines (such as v8)\nis potentially variable-time.\n\nTo mitigate this risk, the [`int32` class was created](https://github.com/soatok/constant-time-js/blob/master/lib/int32.ts)\nwhich wraps operates on 16-bit limbs (wrapping `Uint16Array`).\n\n## Frequently Asked Questions\n\n### But Why Though?\n\n![Mwahahahahahaha!](https://soatok.files.wordpress.com/2020/04/soatok_stickerpack-evil-laughter.png)\n\n**For science!**\n\nThis is a proof-of-concept library, that will eventually implement\nall of the algorithms described in [the accompanying blog post](https://soatok.blog/2020/08/27/soatoks-guide-to-side-channel-attacks/).\n\nThe main purpose of this library is to demonstrate the concepts in a programming\nlanguage widely accessible outside of the cryptography orthodoxy (which today is\nlargely C and sometimes Python, and hopefully soon Rust).\n\n### Can I use this in a project?\n\nHold off until v1.0.0 is tagged before you even *think* about relying on it for\nanything. APIs might break until then.\n\n### What's with the blue {fox, wolf}?\n\nMy fursona is a [dhole](https://soatok.blog/2020/08/10/all-about-dholes-and-dhole-fursonas/), not a wolf.\n\n### You should remove your fursona from this so my manager might take it seriously.\n\nI don't owe you anything. I don't owe your manager anything.\n\nBesides, if anyone is bigoted against a [predominantly LGBTQIA+ community](https://furscience.com/research-findings/sex-relationships-pornography/5-1-orientation/),\nthey're precisely the sort of person whose career I don't want to help.\n\nIn sum:\n\n[![I will increase the thing](https://soatok.files.wordpress.com/2020/07/increase-the-thing.png)](https://soatok.blog/2020/07/09/a-word-on-anti-furry-sentiments-in-the-tech-community/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoatok%2Fconstant-time-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsoatok%2Fconstant-time-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoatok%2Fconstant-time-js/lists"}