Skip to content

Commit 88cdd82

Browse files
committed
Initial commit
0 parents  commit 88cdd82

19 files changed

+1770
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/.idea
2+
/node_modules
3+
/*.js
4+
/lib/*.js

README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Constant-Time JavaScript
2+
3+
Constant-time algorithms written in TypeScript.
4+
5+
[![Support on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.herokuapp.com%2Fsoatok&style=flat)](https://patreon.com/soatok)
6+
[![Linux Build Status](https://travis-ci.org/soatok/constant-time-js.svg?branch=master)](https://travis-ci.org/soatok/constant-time-js)
7+
[![Latest Stable Version](https://poser.pugx.org/soatok/constant-time-js/v/stable)](https://packagist.org/packages/soatok/constant-time-js)
8+
[![Latest Unstable Version](https://poser.pugx.org/soatok/constant-time-js/v/unstable)](https://packagist.org/packages/soatok/constant-time-js)
9+
[![License](https://poser.pugx.org/soatok/constant-time-js/license)](https://packagist.org/packages/soatok/constant-time-js)
10+
[![Downloads](https://img.shields.io/packagist/dt/soatok/constant-time-js.svg)](https://packagist.org/packages/soatok/constant-time-js)
11+
12+
**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/).
13+
14+
![Mind Blowing, right?](https://soatok.files.wordpress.com/2020/08/soatoktelegrams2020-01.png)
15+
16+
## Documentation
17+
18+
* `compare(a, b)` - Compare two `Uint8Array` objects.
19+
* Returns `-1` if `a < b`
20+
* Returns `1` if `a > b`
21+
* Returns `0` if `a === b`
22+
* Throws an `Error` if `a.length !== b.length`
23+
* `compare_ints(a, b)` - Compare two integers.
24+
* Returns `-1` if `a < b`
25+
* Returns `1` if `a > b`
26+
* Returns `0` if `a === b`
27+
* `equals(a, b)` - Are these `Uint8Array` objects equal?
28+
* Returns `true` if they are equal.
29+
* Returns `false` if they are not equal.
30+
* Throws an `Error` if `a.length !== b.length`
31+
* `hmac_equals(a, b)` - Are these `Uint8Array` objects equal? (Using HMAC to compare.)
32+
* Returns `true` if they are equal.
33+
* Returns `false` if they are not equal.
34+
* Throws an `Error` if `a.length !== b.length`
35+
* `intdiv(N, D)` - Divide `N` into `D`, discarding remainder.
36+
* Returns an integer.
37+
* `modulo(N, D)` - Divide `N` into `D`, return the remainder.
38+
* `resize(buf, size)` - Return a resized `Uint8Array` object (to side-step memory access leakage)
39+
* `select(x, a, b)` - Read it as a ternary. If `x` is true, returns `a`. Otherwise, returns `b`.
40+
* `x` must be a `boolean`
41+
* `a` must be a `Uint8Array`
42+
* `b` must be a `Uint8Array`
43+
* Throws an `Error` if `a.length !== b.length`
44+
* `select_ints(x, a, b)` - Read it as a ternary. If `x` is even, returns `a`. Otherwise, returns `b`.
45+
(You should pass `1` or `0` for `x`).
46+
* `x` must be a `boolean`
47+
* `a` must be a `number`
48+
* `b` must be a `number`
49+
* `trim_zeroes_left(buf)`
50+
* `buf` must be a `Uint8Array`
51+
* Returns a `Uint8Array`
52+
* `trim_zeroes_right(buf)`
53+
* `buf` must be a `Uint8Array`
54+
* Returns a `Uint8Array`

index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export { compare, compare_ints } from "./lib/compare";
2+
export { equals, hmac_equals } from "./lib/equals";
3+
export { intdiv, modulo } from './lib/intdiv';
4+
export { resize } from './lib/resize';
5+
export { select_ints, select } from './lib/select';
6+
export { trim_zeroes_left, trim_zeroes_right } from './lib/trim';

lib/compare.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
export function compare(left: Uint8Array, right: Uint8Array): number {
3+
if (left.length !== right.length) {
4+
throw new Error('Both arrays must be of equal length');
5+
}
6+
let gt: number = 0;
7+
let eq: number = 1;
8+
let i: number = left.length;
9+
while (i > 0) {
10+
i--;
11+
gt |= ((right[i] - left[i]) >>> 8) & eq;
12+
eq &= ((right[i] ^ left[i]) - 1) >>> 8;
13+
}
14+
return (gt + gt + eq) - 1;
15+
}
16+
17+
export function compare_ints(left: number, right: number): number {
18+
let gt: number = (right - left) >>> 31;
19+
let eq: number = ((right ^ left) - 1) >>> 31;
20+
return (gt + gt + eq) - 1;
21+
}

lib/equals.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import * as crypto from "crypto";
2+
3+
/**
4+
* Constant-time buffer comparison
5+
*
6+
* @param {Uint8Array} left
7+
* @param {Uint8Array} right
8+
*/
9+
export function equals(left: Uint8Array, right: Uint8Array): boolean {
10+
if (left.length !== right.length) {
11+
return false;
12+
}
13+
let d: number = 0;
14+
let i: number;
15+
for (i = 0; i < left.length; i++) {
16+
d |= left[i] ^ right[i];
17+
}
18+
return d === 0;
19+
}
20+
21+
/**
22+
* Constant-time equality even with an adversarial JIT compiler.
23+
*
24+
* @param {Uint8Array} left
25+
* @param {Uint8Array} right
26+
*/
27+
export function hmac_equals(left: Uint8Array, right: Uint8Array): boolean {
28+
return equals(
29+
crypto.createHmac('sha256', hmacKey).update(left).digest(),
30+
crypto.createHmac('sha256', hmacKey).update(right).digest()
31+
);
32+
}
33+
34+
const hmacKey = crypto.randomBytes(32);

lib/intdiv.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { compare_ints } from './compare';
2+
import { select_ints } from './select';
3+
4+
export function intdiv(numerator: number, denominator: number): number {
5+
return divide(numerator, denominator)[0];
6+
}
7+
8+
export function modulo(numerator: number, denominator: number): number {
9+
return divide(numerator, denominator)[1];
10+
}
11+
12+
function divide(N: number, D: number): number[] {
13+
if (D === 0) {
14+
throw new Error('Division by zero');
15+
}
16+
let Q: number = 0;
17+
let R: number = 0;
18+
let Qprime: number = 0;
19+
let Rprime: number = 0;
20+
let i: number = 0;
21+
let compared: number;
22+
let swap: number;
23+
let bits: number = Math.ceil(Math.log2(N)) | 0;
24+
for (i = 0; i <= bits; i++) {
25+
// R := R << 1
26+
// R(0) := N(i)
27+
R = (R << 1) | ((N >> (bits - i)) & 1);
28+
29+
/*
30+
-- if R > D then compared == 1, swap = 1
31+
-- if R == D then compared == 0, swap = 1
32+
-- if R < D then compared == -1, swap = 0
33+
*/
34+
compared = compare_ints(R, D);
35+
swap = (1 - ((compared >>> 31) & 1));
36+
37+
/*
38+
Rprime := R - D
39+
Qprime := Q
40+
Qprime(i) := 1
41+
*/
42+
Rprime = R - D;
43+
Qprime = Q | (1 << (bits - i));
44+
45+
R = select_ints(swap, Rprime, R);
46+
Q = select_ints(swap, Qprime, Q);
47+
}
48+
return [Q, R];
49+
}

lib/resize.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { modulo } from './intdiv';
2+
import { select_ints } from './select';
3+
4+
/**
5+
* Iterate over every byte of the input array, returning a new object
6+
* (so as to minimize memory access leakage outside).
7+
*
8+
* This is useful for building functions to trim trailing or leading zero
9+
* bytes without leakage.
10+
*
11+
* @param {Uint8Array} input
12+
* @param {number} desired
13+
*/
14+
export function resize(input: Uint8Array, desired: number): Uint8Array {
15+
if (desired === 0) {
16+
return new Uint8Array(0);
17+
}
18+
const output: Uint8Array = new Uint8Array(desired);
19+
let x: number;
20+
let y: number;
21+
let z: number;
22+
for (x = 0; x < input.length; x++) {
23+
/*
24+
if (x <= desired) y = 0;
25+
else y = 1;
26+
*/
27+
y = ((x - desired) >>> 31) & 1;
28+
29+
z = modulo(x, desired);
30+
output[z] = select_ints(y & 1, input[z], output[z]);
31+
}
32+
33+
return output;
34+
}

lib/select.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* If TRUE, return left; else, return right.
3+
*
4+
* @param {boolean} returnLeft
5+
* @param {Uint8Array} left
6+
* @param {Uint8Array} right
7+
* @returns {Uint8Array}
8+
*/
9+
export function select(returnLeft: boolean, left: Uint8Array, right: Uint8Array): Uint8Array {
10+
if (left.length !== right.length) {
11+
throw new Error('select() expects two Uint8Array objects of equal length');
12+
}
13+
/*
14+
If returnLeft, mask = 0xFF; else, mask = 0x00;
15+
*/
16+
const mask: number = (-!!returnLeft) & 0xff;
17+
const out: Uint8Array = new Uint8Array(left.length);
18+
for (let i: number = 0; i < left.length; i++) {
19+
out[i] = right[i] ^ ((left[i] ^ right[i]) & mask);
20+
}
21+
return out;
22+
}
23+
24+
/**
25+
* If TRUE, return left; else, return right.
26+
*
27+
* @param {number} returnLeft
28+
* @param {number} left
29+
* @param {number} right
30+
* @returns {number}
31+
*/
32+
export function select_ints(returnLeft: number, left: number, right: number): number {
33+
/*
34+
If returnLeft, mask = 0xFFFFFFFF; else, mask = 0x00000000;
35+
*/
36+
const mask: number = (-(returnLeft & 1)) & 0xfffffffff;
37+
return right ^ ((left ^ right) & mask);
38+
}

lib/trim.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { resize } from './resize';
2+
3+
/**
4+
* @param {Uint8Array} buf
5+
* @returns {Uint8Array}
6+
*/
7+
export function trim_zeroes_right(buf: Uint8Array): Uint8Array {
8+
let foundNonZero: number = 0;
9+
let i: number;
10+
let index: number = 1;
11+
let isNonZero: number = 0;
12+
let found: number = 0;
13+
for (i = buf.length - 1; i >= 0; i--) {
14+
/* if foundNonZero === 0 && buf[i] !== 0, index := i */
15+
isNonZero = ((buf[i] - 1) >>> 8) & 0xff;
16+
found = (foundNonZero - 1) & 0xff;
17+
index = (i & ~foundNonZero) ^ (foundNonZero & index);
18+
foundNonZero |= (~isNonZero) & 0xff;
19+
}
20+
return resize(buf, index + 1);
21+
}
22+
23+
/**
24+
* @param {Uint8Array} buf
25+
* @returns {Uint8Array}
26+
*/
27+
export function trim_zeroes_left(buf: Uint8Array): Uint8Array {
28+
buf.reverse();
29+
buf = trim_zeroes_right(buf);
30+
buf.reverse();
31+
return buf;
32+
}

0 commit comments

Comments
 (0)