summaryrefslogtreecommitdiff
path: root/ext/node/polyfills/internal_binding/_timingSafeEqual.ts
blob: 559b7685b8bcc3e0329a6a18d597835dfcc0fca2 (plain)
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
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

// TODO(petamoriken): enable prefer-primordials for node polyfills
// deno-lint-ignore-file prefer-primordials

import { Buffer } from "node:buffer";

function toDataView(ab: ArrayBufferLike | ArrayBufferView): DataView {
  if (ArrayBuffer.isView(ab)) {
    return new DataView(ab.buffer, ab.byteOffset, ab.byteLength);
  }
  return new DataView(ab);
}

/** Compare to array buffers or data views in a way that timing based attacks
 * cannot gain information about the platform. */
function stdTimingSafeEqual(
  a: ArrayBufferView | ArrayBufferLike | DataView,
  b: ArrayBufferView | ArrayBufferLike | DataView,
): boolean {
  if (a.byteLength !== b.byteLength) {
    return false;
  }
  if (!(a instanceof DataView)) {
    a = toDataView(a);
  }
  if (!(b instanceof DataView)) {
    b = toDataView(b);
  }
  const length = a.byteLength;
  let out = 0;
  let i = -1;
  while (++i < length) {
    out |= a.getUint8(i) ^ b.getUint8(i);
  }
  return out === 0;
}

export const timingSafeEqual = (
  a: Buffer | DataView | ArrayBuffer,
  b: Buffer | DataView | ArrayBuffer,
): boolean => {
  if (a instanceof Buffer) {
    a = new DataView(a.buffer, a.byteOffset, a.byteLength);
  }
  if (b instanceof Buffer) {
    b = new DataView(b.buffer, b.byteOffset, b.byteLength);
  }
  return stdTimingSafeEqual(a, b);
};