-
Notifications
You must be signed in to change notification settings - Fork 55
/
equals.ts
55 lines (46 loc) · 2.89 KB
/
equals.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import isEqual from 'lodash/isEqual';
export const deepEquals = <T>(a: T, b: T) => isEqual(a, b);
export const strictEquals = <T>(a: T, b: T) => a === b;
export const sameArrayItems = <T>(arrayA: T[], arrayB: T[], itemEquals: (a: T, b: T) => boolean) =>
arrayA.length === arrayB.length && arrayA.every((a) => arrayB.some((b) => itemEquals(a, b)));
/**
* Performs a constant-time comparison of two number values to mitigate timing attacks (CWE-208).
*
* This function prevents timing attacks by ensuring that the time it takes to compare two number values
* is consistent, regardless of the values being compared.
*
* @param {number} a - The first number value to compare.
* @param {number} b - The second number value to compare.
* @returns {boolean} - Returns true if both number values are identical, otherwise returns false.
*/
export const areNumbersEqual = (a: number, b: number): boolean => (a ^ b) === 0; // This performs a constant-time comparison for number values
/**
* Performs a constant-time comparison of two strings to mitigate timing attacks (CWE-208).
*
* This function is designed to prevent timing attacks by ensuring that the time it takes to compare two strings
* does not depend on the contents of the strings themselves. It achieves this by comparing all characters
* up to the length of the longer string, treating out-of-bounds characters as zeros, and using bitwise operations
* to maintain constant time execution.
*
* @param {string} a - The first string to compare.
* @param {string} b - The second string to compare.
* @returns {boolean} - Returns true if the strings are identical, false otherwise.
*/
export const areStringsEqual = (a: string, b: string): boolean => {
if (a.length !== b.length) return false;
// Calculate the maximum length of the two strings. This value will be used to loop through each character.
const maxLength = Math.max(a.length, b.length);
// It loops through the maximum length of the two strings, ensuring that the iteration count doesn't leak information about the length difference.
const results: (0 | 1)[] = Array.from({ length: maxLength }, (_, i) => {
const charCodeA = i < a.length ? a.charCodeAt(i) : 0;
const charCodeB = i < b.length ? b.charCodeAt(i) : 0;
return charCodeA === charCodeB ? 1 : 0; // Return 1 if the characters are equal, otherwise return 0.
});
// Reduce the results array to a single value, using bitwise AND, explicitly casting the result as binary 0 | 1.
// We do not use booleans here (true/false) instead 0 and 1 because bitwise operations in JavaScript operate on 32-bit integers.
const areAllCharactersEqual = results.reduce(
(accumulator: 0 | 1, currentValue: 0 | 1) => (accumulator & currentValue) as 0 | 1, // Cast to 0 | 1 to satisfy TypeScript's strict type checking.
1 as 0 | 1
);
return areAllCharactersEqual === 1; // Return `true` if all characters matched, otherwise `false`.
};