UNPKG

4.3 kBPlain TextView Raw
1import {
2 BLOCK_SIZE,
3 DIGEST_LENGTH,
4 INIT,
5 KEY,
6 MAX_HASHABLE_LENGTH
7} from "./constants";
8
9/**
10 * @internal
11 */
12export class RawSha256 {
13 private state: Int32Array = Int32Array.from(INIT);
14 private temp: Int32Array = new Int32Array(64);
15 private buffer: Uint8Array = new Uint8Array(64);
16 private bufferLength: number = 0;
17 private bytesHashed: number = 0;
18
19 /**
20 * @internal
21 */
22 finished: boolean = false;
23
24 update(data: Uint8Array): void {
25 if (this.finished) {
26 throw new Error("Attempted to update an already finished hash.");
27 }
28
29 let position = 0;
30 let { byteLength } = data;
31 this.bytesHashed += byteLength;
32
33 if (this.bytesHashed * 8 > MAX_HASHABLE_LENGTH) {
34 throw new Error("Cannot hash more than 2^53 - 1 bits");
35 }
36
37 while (byteLength > 0) {
38 this.buffer[this.bufferLength++] = data[position++];
39 byteLength--;
40
41 if (this.bufferLength === BLOCK_SIZE) {
42 this.hashBuffer();
43 this.bufferLength = 0;
44 }
45 }
46 }
47
48 digest(): Uint8Array {
49 if (!this.finished) {
50 const bitsHashed = this.bytesHashed * 8;
51 const bufferView = new DataView(
52 this.buffer.buffer,
53 this.buffer.byteOffset,
54 this.buffer.byteLength
55 );
56
57 const undecoratedLength = this.bufferLength;
58 bufferView.setUint8(this.bufferLength++, 0x80);
59
60 // Ensure the final block has enough room for the hashed length
61 if (undecoratedLength % BLOCK_SIZE >= BLOCK_SIZE - 8) {
62 for (let i = this.bufferLength; i < BLOCK_SIZE; i++) {
63 bufferView.setUint8(i, 0);
64 }
65 this.hashBuffer();
66 this.bufferLength = 0;
67 }
68
69 for (let i = this.bufferLength; i < BLOCK_SIZE - 8; i++) {
70 bufferView.setUint8(i, 0);
71 }
72 bufferView.setUint32(
73 BLOCK_SIZE - 8,
74 Math.floor(bitsHashed / 0x100000000),
75 true
76 );
77 bufferView.setUint32(BLOCK_SIZE - 4, bitsHashed);
78
79 this.hashBuffer();
80
81 this.finished = true;
82 }
83
84 // The value in state is little-endian rather than big-endian, so flip
85 // each word into a new Uint8Array
86 const out = new Uint8Array(DIGEST_LENGTH);
87 for (let i = 0; i < 8; i++) {
88 out[i * 4] = (this.state[i] >>> 24) & 0xff;
89 out[i * 4 + 1] = (this.state[i] >>> 16) & 0xff;
90 out[i * 4 + 2] = (this.state[i] >>> 8) & 0xff;
91 out[i * 4 + 3] = (this.state[i] >>> 0) & 0xff;
92 }
93
94 return out;
95 }
96
97 private hashBuffer(): void {
98 const { buffer, state } = this;
99
100 let state0 = state[0],
101 state1 = state[1],
102 state2 = state[2],
103 state3 = state[3],
104 state4 = state[4],
105 state5 = state[5],
106 state6 = state[6],
107 state7 = state[7];
108
109 for (let i = 0; i < BLOCK_SIZE; i++) {
110 if (i < 16) {
111 this.temp[i] =
112 ((buffer[i * 4] & 0xff) << 24) |
113 ((buffer[i * 4 + 1] & 0xff) << 16) |
114 ((buffer[i * 4 + 2] & 0xff) << 8) |
115 (buffer[i * 4 + 3] & 0xff);
116 } else {
117 let u = this.temp[i - 2];
118 const t1 =
119 ((u >>> 17) | (u << 15)) ^ ((u >>> 19) | (u << 13)) ^ (u >>> 10);
120
121 u = this.temp[i - 15];
122 const t2 =
123 ((u >>> 7) | (u << 25)) ^ ((u >>> 18) | (u << 14)) ^ (u >>> 3);
124
125 this.temp[i] =
126 ((t1 + this.temp[i - 7]) | 0) + ((t2 + this.temp[i - 16]) | 0);
127 }
128
129 const t1 =
130 ((((((state4 >>> 6) | (state4 << 26)) ^
131 ((state4 >>> 11) | (state4 << 21)) ^
132 ((state4 >>> 25) | (state4 << 7))) +
133 ((state4 & state5) ^ (~state4 & state6))) |
134 0) +
135 ((state7 + ((KEY[i] + this.temp[i]) | 0)) | 0)) |
136 0;
137
138 const t2 =
139 ((((state0 >>> 2) | (state0 << 30)) ^
140 ((state0 >>> 13) | (state0 << 19)) ^
141 ((state0 >>> 22) | (state0 << 10))) +
142 ((state0 & state1) ^ (state0 & state2) ^ (state1 & state2))) |
143 0;
144
145 state7 = state6;
146 state6 = state5;
147 state5 = state4;
148 state4 = (state3 + t1) | 0;
149 state3 = state2;
150 state2 = state1;
151 state1 = state0;
152 state0 = (t1 + t2) | 0;
153 }
154
155 state[0] += state0;
156 state[1] += state1;
157 state[2] += state2;
158 state[3] += state3;
159 state[4] += state4;
160 state[5] += state5;
161 state[6] += state6;
162 state[7] += state7;
163 }
164}