2024-01-01 21:11:32 +00:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2023-03-18 12:36:00 +00:00
|
|
|
// This module is browser compatible.
|
2022-08-11 10:06:26 +00:00
|
|
|
|
2024-05-30 10:16:58 +00:00
|
|
|
function toDataView(
|
|
|
|
value: ArrayBufferView | ArrayBufferLike | DataView,
|
|
|
|
): DataView {
|
|
|
|
if (value instanceof DataView) {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
return ArrayBuffer.isView(value)
|
|
|
|
? new DataView(value.buffer, value.byteOffset, value.byteLength)
|
|
|
|
: new DataView(value);
|
|
|
|
}
|
2022-08-11 10:06:26 +00:00
|
|
|
|
2023-09-01 02:35:37 +00:00
|
|
|
/**
|
|
|
|
* When checking the values of cryptographic hashes are equal, default
|
|
|
|
* comparisons can be susceptible to timing based attacks, where attacker is
|
|
|
|
* able to find out information about the host system by repeatedly checking
|
|
|
|
* response times to equality comparisons of values.
|
|
|
|
*
|
|
|
|
* It is likely some form of timing safe equality will make its way to the
|
|
|
|
* WebCrypto standard (see:
|
2024-01-31 22:19:46 +00:00
|
|
|
* {@link https://github.com/w3c/webcrypto/issues/270 | w3c/webcrypto#270}), but until
|
2023-09-01 02:35:37 +00:00
|
|
|
* that time, `timingSafeEqual()` is provided:
|
|
|
|
*
|
2024-05-31 02:39:47 +00:00
|
|
|
* @example Usage
|
2023-09-01 02:35:37 +00:00
|
|
|
* ```ts
|
2024-04-29 02:57:30 +00:00
|
|
|
* import { timingSafeEqual } from "@std/crypto/timing-safe-equal";
|
refactor(assert,async,bytes,cli,collections,crypto,csv,data-structures,datetime,dotenv,encoding,expect,fmt,front-matter,fs,html,http,ini,internal,io,json,jsonc,log,media-types,msgpack,net,path,semver,streams,testing,text,toml,ulid,url,uuid,webgpu,yaml): import from `@std/assert` (#5199)
* refactor: import from `@std/assert`
* update
2024-06-30 08:30:10 +00:00
|
|
|
* import { assert } from "@std/assert";
|
2023-09-01 02:35:37 +00:00
|
|
|
*
|
|
|
|
* const a = await crypto.subtle.digest(
|
|
|
|
* "SHA-384",
|
|
|
|
* new TextEncoder().encode("hello world"),
|
|
|
|
* );
|
|
|
|
* const b = await crypto.subtle.digest(
|
|
|
|
* "SHA-384",
|
|
|
|
* new TextEncoder().encode("hello world"),
|
|
|
|
* );
|
|
|
|
*
|
|
|
|
* assert(timingSafeEqual(a, b));
|
|
|
|
* ```
|
2024-05-31 02:39:47 +00:00
|
|
|
*
|
|
|
|
* @param a The first value to compare.
|
|
|
|
* @param b The second value to compare.
|
|
|
|
* @returns `true` if the values are equal, otherwise `false`.
|
2023-09-01 02:35:37 +00:00
|
|
|
*/
|
2022-08-11 10:06:26 +00:00
|
|
|
export function timingSafeEqual(
|
|
|
|
a: ArrayBufferView | ArrayBufferLike | DataView,
|
|
|
|
b: ArrayBufferView | ArrayBufferLike | DataView,
|
|
|
|
): boolean {
|
2024-05-30 10:16:58 +00:00
|
|
|
if (a.byteLength !== b.byteLength) return false;
|
|
|
|
const dataViewA = toDataView(a);
|
|
|
|
const dataViewB = toDataView(b);
|
2022-08-11 10:06:26 +00:00
|
|
|
const length = a.byteLength;
|
|
|
|
let out = 0;
|
|
|
|
let i = -1;
|
|
|
|
while (++i < length) {
|
2024-05-30 10:16:58 +00:00
|
|
|
out |= dataViewA.getUint8(i) ^ dataViewB.getUint8(i);
|
2022-08-11 10:06:26 +00:00
|
|
|
}
|
|
|
|
return out === 0;
|
|
|
|
}
|