File size: 1,617 Bytes
0ed8124
 
 
 
 
 
 
 
 
 
 
 
 
c3633b1
0ed8124
 
 
 
 
 
 
 
 
 
c3633b1
 
 
0ed8124
 
 
 
 
 
 
 
 
 
 
 
 
c3633b1
 
 
 
 
 
0ed8124
c3633b1
0ed8124
 
 
 
 
 
 
 
 
 
 
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
56
57
58
59
60
import { sha256 } from '@noble/hashes/sha2.js';
import { bytesToHex } from '@noble/hashes/utils.js';
import { abortError, throwIfAborted } from './errors';

export interface BlobIntegrityResult {
  actualSha256: string;
  matches: boolean;
}

export async function verifyBlobSha256(
  blob: Blob,
  expectedSha256: string,
  signal?: AbortSignal,
  onProgress?: (loadedBytes: number, totalBytes: number) => void,
): Promise<BlobIntegrityResult> {
  if (signal) {
    throwIfAborted(signal);
  }
  const hasher = sha256.create();
  const reader = blob.stream().getReader();
  const cancelRead = (): void => {
    void reader.cancel(abortError()).catch(() => undefined);
  };
  signal?.addEventListener('abort', cancelRead, { once: true });
  let loadedBytes = 0;
  let lastProgressAt = 0;
  onProgress?.(0, blob.size);
  try {
    while (true) {
      if (signal) {
        throwIfAborted(signal);
      }
      const { done, value } = await reader.read();
      if (signal) {
        throwIfAborted(signal);
      }
      if (done) {
        break;
      }
      hasher.update(value);
      loadedBytes += value.byteLength;
      const now = performance.now();
      if (now - lastProgressAt >= 100) {
        lastProgressAt = now;
        onProgress?.(Math.min(loadedBytes, blob.size), blob.size);
      }
    }
    onProgress?.(blob.size, blob.size);
    const actualSha256 = bytesToHex(hasher.digest());
    return {
      actualSha256,
      matches: actualSha256 === expectedSha256,
    };
  } finally {
    signal?.removeEventListener('abort', cancelRead);
    reader.releaseLock();
    hasher.destroy();
  }
}