1 | import { Hash, SourceData } from "@aws-sdk/types";
|
2 | import { isEmptyData, convertToBuffer } from "@aws-crypto/util";
|
3 | import {
|
4 | EMPTY_DATA_SHA_256,
|
5 | SHA_256_HASH,
|
6 | SHA_256_HMAC_ALGO,
|
7 | } from "./constants";
|
8 | import { locateWindow } from "@aws-sdk/util-locate-window";
|
9 |
|
10 | export class Sha256 implements Hash {
|
11 | private readonly key: Promise<CryptoKey> | undefined;
|
12 | private toHash: Uint8Array = new Uint8Array(0);
|
13 |
|
14 | constructor(secret?: SourceData) {
|
15 | if (secret !== void 0) {
|
16 | this.key = new Promise((resolve, reject) => {
|
17 | locateWindow()
|
18 | .crypto.subtle.importKey(
|
19 | "raw",
|
20 | convertToBuffer(secret),
|
21 | SHA_256_HMAC_ALGO,
|
22 | false,
|
23 | ["sign"]
|
24 | )
|
25 | .then(resolve, reject);
|
26 | });
|
27 | this.key.catch(() => {});
|
28 | }
|
29 | }
|
30 |
|
31 | update(data: SourceData): void {
|
32 | if (isEmptyData(data)) {
|
33 | return;
|
34 | }
|
35 |
|
36 | const update = convertToBuffer(data);
|
37 | const typedArray = new Uint8Array(
|
38 | this.toHash.byteLength + update.byteLength
|
39 | );
|
40 | typedArray.set(this.toHash, 0);
|
41 | typedArray.set(update, this.toHash.byteLength);
|
42 | this.toHash = typedArray;
|
43 | }
|
44 |
|
45 | digest(): Promise<Uint8Array> {
|
46 | if (this.key) {
|
47 | return this.key.then((key) =>
|
48 | locateWindow()
|
49 | .crypto.subtle.sign(SHA_256_HMAC_ALGO, key, this.toHash)
|
50 | .then((data) => new Uint8Array(data))
|
51 | );
|
52 | }
|
53 |
|
54 | if (isEmptyData(this.toHash)) {
|
55 | return Promise.resolve(EMPTY_DATA_SHA_256);
|
56 | }
|
57 |
|
58 | return Promise.resolve()
|
59 | .then(() =>
|
60 | locateWindow().crypto.subtle.digest(SHA_256_HASH, this.toHash)
|
61 | )
|
62 | .then((data) => Promise.resolve(new Uint8Array(data)));
|
63 | }
|
64 | }
|