summaryrefslogtreecommitdiff
path: root/std/hash/_sha3/sponge.ts
diff options
context:
space:
mode:
authorskdltmxn <supershop@naver.com>2020-06-05 04:14:36 +0900
committerGitHub <noreply@github.com>2020-06-04 15:14:36 -0400
commite05ed537130eff116b48af02d1eb6ce65953e7d8 (patch)
tree0bd1ca235ad9ed89ca602c384ab39301346afe24 /std/hash/_sha3/sponge.ts
parent430beebc46be1dc87a993c2f7442c5c22076f458 (diff)
feat(std/hash): add sha3 (#5558)
Diffstat (limited to 'std/hash/_sha3/sponge.ts')
-rw-r--r--std/hash/_sha3/sponge.ts111
1 files changed, 111 insertions, 0 deletions
diff --git a/std/hash/_sha3/sponge.ts b/std/hash/_sha3/sponge.ts
new file mode 100644
index 000000000..a5705e13e
--- /dev/null
+++ b/std/hash/_sha3/sponge.ts
@@ -0,0 +1,111 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
+import * as hex from "../../encoding/hex.ts";
+
+type SpongePermutator = (data: Uint8Array) => void;
+
+/** Sponge construction option */
+export interface SpongeOption {
+ bitsize: number;
+ rate: number;
+ dsbyte: number;
+ permutator: SpongePermutator;
+}
+
+export type Message = string | ArrayBuffer;
+
+const STATE_SIZE = 200;
+const TYPE_ERROR_MSG = "sha3: `data` is invalid type";
+
+/** Sponge construction */
+export class Sponge {
+ #option: SpongeOption;
+ #state: Uint8Array;
+ #rp: number;
+ #absorbing: boolean;
+
+ constructor(option: SpongeOption) {
+ this.#option = option;
+ this.#state = new Uint8Array(STATE_SIZE);
+ this.#rp = 0;
+ this.#absorbing = true;
+ }
+
+ /** Applies padding to internal state */
+ private pad(): void {
+ this.#state[this.#rp] ^= this.#option.dsbyte;
+ this.#state[this.#option.rate - 1] ^= 0x80;
+ }
+
+ /** Squeezes internal state */
+ protected squeeze(length: number): Uint8Array {
+ if (length < 0) {
+ throw new Error("sha3: length cannot be negative");
+ }
+
+ this.pad();
+
+ const hash = new Uint8Array(length);
+ let pos = 0;
+ while (length > 0) {
+ const r = length > this.#option.rate ? this.#option.rate : length;
+ this.#option.permutator(this.#state);
+ hash.set(this.#state.slice(0, r), pos);
+ length -= r;
+ pos += r;
+ }
+
+ this.#absorbing = false;
+ return hash;
+ }
+
+ /** Updates internal state by absorbing */
+ update(data: Message): this {
+ if (!this.#absorbing) {
+ throw new Error("sha3: cannot update already finalized hash");
+ }
+
+ let msg: Uint8Array;
+
+ if (typeof data === "string") {
+ msg = new TextEncoder().encode(data as string);
+ } else if (typeof data === "object") {
+ if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {
+ msg = new Uint8Array(data);
+ } else {
+ throw new Error(TYPE_ERROR_MSG);
+ }
+ } else {
+ throw new Error(TYPE_ERROR_MSG);
+ }
+
+ let rp = this.#rp;
+
+ for (let i = 0; i < msg.length; ++i) {
+ this.#state[rp++] ^= msg[i];
+ if (rp >= this.#option.rate) {
+ this.#option.permutator(this.#state);
+ rp = 0;
+ }
+ }
+
+ this.#rp = rp;
+ return this;
+ }
+
+ /** Returns the hash in ArrayBuffer */
+ digest(): ArrayBuffer {
+ return this.squeeze(this.#option.bitsize >> 3);
+ }
+
+ /** Returns the hash in given format */
+ toString(format: "hex" = "hex"): string {
+ const rawOutput = this.squeeze(this.#option.bitsize >> 3);
+ switch (format) {
+ case "hex":
+ return hex.encodeToString(rawOutput);
+ default:
+ throw new Error("sha3: invalid output format");
+ }
+ }
+}