1 | import { isEmptyData } from "./isEmptyData";
|
2 | import { SHA_256_HMAC_ALGO } from "./constants";
|
3 | import { Hash, SourceData } from "@aws-sdk/types";
|
4 | import { fromUtf8 } from "@aws-sdk/util-utf8-browser";
|
5 | import { CryptoOperation, Key, MsWindow } from "@aws-crypto/ie11-detection";
|
6 | import { locateWindow } from "@aws-sdk/util-locate-window";
|
7 |
|
8 | export class Sha256 implements Hash {
|
9 | private operation: Promise<CryptoOperation>;
|
10 |
|
11 | constructor(secret?: SourceData) {
|
12 | if (secret) {
|
13 | this.operation = getKeyPromise(secret).then(keyData =>
|
14 | (locateWindow() as MsWindow).msCrypto.subtle.sign(
|
15 | SHA_256_HMAC_ALGO,
|
16 | keyData
|
17 | )
|
18 | );
|
19 | this.operation.catch(() => {});
|
20 | } else {
|
21 | this.operation = Promise.resolve(
|
22 | (locateWindow() as MsWindow).msCrypto.subtle.digest("SHA-256")
|
23 | );
|
24 | }
|
25 | }
|
26 |
|
27 | update(toHash: SourceData): void {
|
28 | if (isEmptyData(toHash)) {
|
29 | return;
|
30 | }
|
31 |
|
32 | this.operation = this.operation.then(operation => {
|
33 | operation.onerror = () => {
|
34 | this.operation = Promise.reject(
|
35 | new Error("Error encountered updating hash")
|
36 | );
|
37 | };
|
38 | operation.process(toArrayBufferView(toHash));
|
39 |
|
40 | return operation;
|
41 | });
|
42 | this.operation.catch(() => {});
|
43 | }
|
44 |
|
45 | digest(): Promise<Uint8Array> {
|
46 | return this.operation.then<Uint8Array>(
|
47 | operation =>
|
48 | new Promise((resolve, reject) => {
|
49 | operation.onerror = () => {
|
50 | reject(new Error("Error encountered finalizing hash"));
|
51 | };
|
52 | operation.oncomplete = () => {
|
53 | if (operation.result) {
|
54 | resolve(new Uint8Array(operation.result));
|
55 | }
|
56 | reject(new Error("Error encountered finalizing hash"));
|
57 | };
|
58 |
|
59 | operation.finish();
|
60 | })
|
61 | );
|
62 | }
|
63 | }
|
64 |
|
65 | function getKeyPromise(secret: SourceData): Promise<Key> {
|
66 | return new Promise((resolve, reject) => {
|
67 | const keyOperation = (locateWindow() as MsWindow).msCrypto.subtle.importKey(
|
68 | "raw",
|
69 | toArrayBufferView(secret),
|
70 | SHA_256_HMAC_ALGO,
|
71 | false,
|
72 | ["sign"]
|
73 | );
|
74 |
|
75 | keyOperation.oncomplete = () => {
|
76 | if (keyOperation.result) {
|
77 | resolve(keyOperation.result);
|
78 | }
|
79 |
|
80 | reject(new Error("ImportKey completed without importing key."));
|
81 | };
|
82 | keyOperation.onerror = () => {
|
83 | reject(new Error("ImportKey failed to import key."));
|
84 | };
|
85 | });
|
86 | }
|
87 |
|
88 | function toArrayBufferView(data: SourceData): Uint8Array {
|
89 | if (typeof data === "string") {
|
90 | return fromUtf8(data);
|
91 | }
|
92 |
|
93 | if (ArrayBuffer.isView(data)) {
|
94 | return new Uint8Array(
|
95 | data.buffer,
|
96 | data.byteOffset,
|
97 | data.byteLength / Uint8Array.BYTES_PER_ELEMENT
|
98 | );
|
99 | }
|
100 |
|
101 | return new Uint8Array(data);
|
102 | }
|