2
* Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3
* Licensed under the Apache License, Version 2.0 (the "License");
4
* you may not use this file except in compliance with the License.
5
* You may obtain a copy of the License at
7
* http://www.apache.org/licenses/LICENSE-2.0
9
* Unless required by applicable law or agreed to in writing, software
10
* distributed under the License is distributed on an "AS IS" BASIS,
11
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
* See the License for the specific language governing permissions and
13
* limitations under the License.
16
import { CustomTextDecoder } from "@koalaui/compat"
17
import { int32 } from "@koalaui/compat"
20
(0x5a827999 | 0) as int32,
21
(0x6ed9eba1 | 0) as int32,
22
(0x8f1bbcdc | 0) as int32,
23
(0xca62c1d6 | 0) as int32,
27
const inputWords = inputBytes / 4
28
const highIndex = inputWords - 2
29
const lowIndex = inputWords - 1
32
const allocWords = allocBytes / 4
33
const allocTotal = allocBytes * 100
35
export function createSha1(): SHA1Hash {
39
export class SHA1Hash {
40
private A = (0x67452301 | 0) as int32
41
private B = (0xefcdab89 | 0) as int32
42
private C = (0x98badcfe | 0) as int32
43
private D = (0x10325476 | 0) as int32
44
private E = (0xc3d2e1f0 | 0) as int32
45
private readonly _byte: Uint8Array
46
private readonly _word: Int32Array
48
private _sp = 0 // surrogate pair
51
if (!sharedBuffer || sharedOffset >= allocTotal) {
52
sharedBuffer = new ArrayBuffer(allocTotal)
56
this._byte = new Uint8Array(sharedBuffer, sharedOffset, allocBytes)
57
this._word = new Int32Array(sharedBuffer, sharedOffset, allocWords)
58
sharedOffset += allocBytes
61
updateString(data: string, encoding?: string): SHA1Hash {
62
return this._utf8(data)
64
updateInt32(data: int32): SHA1Hash {
65
const buffer = new Int32Array(1)
67
return this.update(buffer)
70
update(data: Int32Array | Float32Array | Uint32Array | Uint8Array): SHA1Hash {
72
throw new TypeError("SHA1Hash expected non-null data: ")
75
let byteOffset: int32 = 0
77
let buffer: ArrayBufferLike | undefined = undefined
79
// TODO: an attempt to wrie this in a generic form causes
80
// es2panda to segfault.
81
if (data instanceof Int32Array) {
82
byteOffset = data.byteOffset as int32
83
length = data.byteLength as int32
85
} else if (data instanceof Uint32Array) {
86
byteOffset = data.byteOffset as int32
87
length = data.byteLength as int32
89
} else if (data instanceof Float32Array) {
90
byteOffset = data.byteOffset as int32
91
length = data.byteLength as int32
93
} else if (data instanceof Uint8Array) {
94
byteOffset = data.byteOffset as int32
95
length = data.byteLength as int32
99
let blocks: int32 = ((length / inputBytes) | 0) as int32
100
let offset: int32 = 0
102
// longer than 1 block
103
if ((blocks != 0) && !(byteOffset & 3) && !(this._size % inputBytes)) {
104
const block = new Int32Array(buffer!, byteOffset, blocks * inputWords)
106
this._int32(block, offset >> 2)
112
// data: TypedArray | DataView
113
const BYTES_PER_ELEMENT = (data as Uint8Array).BYTES_PER_ELEMENT as int32
114
if ((BYTES_PER_ELEMENT != 1) && buffer != undefined) {
115
const rest = new Uint8Array(buffer, byteOffset + offset, length - offset)
116
return this._uint8(rest)
120
if (offset == length) return this
122
return this._uint8(new Uint8Array(buffer!), offset)
125
private _uint8(data: Uint8Array, offset?: int32): SHA1Hash {
126
const _byte = this._byte
127
const _word = this._word
128
const length = data.length
129
offset = ((offset ?? 0) | 0) as int32
131
while (offset < length) {
132
const start = this._size % inputBytes
135
while (offset < length && index < inputBytes) {
136
_byte[index++] = data[offset++]
139
if (index >= inputBytes) {
143
this._size += index - start
149
private _utf8(text: string): SHA1Hash {
150
const _byte = this._byte
151
const _word = this._word
152
const length = text.length
153
let surrogate = this._sp
155
for (let offset = 0; offset < length; ) {
156
const start = this._size % inputBytes
159
while (offset < length && index < inputBytes) {
160
let code = text.charCodeAt(offset++) | 0
163
_byte[index++] = code
164
} else if (code < 0x800) {
166
_byte[index++] = 0xC0 | (code >>> 6)
167
_byte[index++] = 0x80 | (code & 0x3F)
168
} else if (code < 0xD800 || code > 0xDFFF) {
170
_byte[index++] = 0xE0 | (code >>> 12)
171
_byte[index++] = 0x80 | ((code >>> 6) & 0x3F)
172
_byte[index++] = 0x80 | (code & 0x3F)
173
} else if (surrogate) {
174
// 4 bytes - surrogate pair
175
code = ((surrogate & 0x3FF) << 10) + (code & 0x3FF) + 0x10000
176
_byte[index++] = 0xF0 | (code >>> 18)
177
_byte[index++] = 0x80 | ((code >>> 12) & 0x3F)
178
_byte[index++] = 0x80 | ((code >>> 6) & 0x3F)
179
_byte[index++] = 0x80 | (code & 0x3F)
186
if (index >= inputBytes) {
188
_word[0] = _word[inputWords]
191
this._size += index - start
198
private _int32(data: Int32Array, offset?: int32): void {
205
offset = ((offset ?? 0) | 0) as int32
207
while (i < inputWords) {
208
W[i++] = swap32(data[offset++] as int32)
211
for (i = inputWords; i < workWords; i++) {
212
W[i] = rotate1((W[i - 3] as int32) ^ (W[i - 8] as int32) ^ (W[i - 14] as int32) ^ (W[i - 16] as int32))
215
for (i = 0; i < workWords; i++) {
216
const S = (i / 20) | 0
217
const T = ((rotate5(A) + ft(S, B, C, D) + E + W[i] + K[S]) as int32) | 0
225
this.A = (A + this.A) | 0
226
this.B = (B + this.B) | 0
227
this.C = (C + this.C) | 0
228
this.D = (D + this.D) | 0
229
this.E = (E + this.E) | 0
232
// digest(): Uint8Array
233
// digest(encoding: string): string
234
digest(encoding?: string): Uint8Array | string {
235
const _byte = this._byte
236
const _word = this._word
237
let i = (this._size % inputBytes) | 0
240
// pad 0 for current word
247
while (i < inputWords) {
254
// pad 0 for rest words
255
while (i < inputWords) {
260
const bits64: int32 = this._size * 8
261
const low32: int32 = ((bits64 & 0xffffffff) as int32 >>> 0) as int32
262
const high32: int32 = ((bits64 - low32) as int32 / 0x100000000) as int32
263
if (high32) _word[highIndex] = swap32(high32) as int32
264
if (low32) _word[lowIndex] = swap32(low32) as int32
268
return (encoding === "hex") ? this._hex() : this._bin()
271
private _hex(): string {
278
return hex32Str(A, B, C, D, E)
281
private _bin(): Uint8Array {
287
const _byte = this._byte
288
const _word = this._word
296
return _byte.slice(0, 20)
300
type NS = (num: int32) => string
301
type NN = (num: int32) => int32
303
const W = new Int32Array(workWords)
305
let sharedBuffer: ArrayBuffer
306
let sharedOffset: int32 = 0
308
const swapLE: NN = ((c:int32):int32 => (((c << 24) & 0xff000000) | ((c << 8) & 0xff0000) | ((c >> 8) & 0xff00) | ((c >> 24) & 0xff)))
309
const swapBE: NN = ((c:int32):int32 => c)
310
const swap32: NN = isBE() ? swapBE : swapLE
311
const rotate1: NN = (num: int32): int32 => (num << 1) | (num >>> 31)
312
const rotate5: NN = (num: int32): int32 => (num << 5) | (num >>> 27)
313
const rotate30: NN = (num: int32): int32 => (num << 30) | (num >>> 2)
315
function isBE(): boolean {
316
let a16 = new Uint16Array(1)
318
let a8 = new Uint8Array(a16.buffer)
319
return a8[0] == 0xFE // BOM
323
function ft(s: int32, b: int32, c: int32, d: int32) {
324
if (s == 0) return (b & c) | ((~b) & d)
325
if (s == 2) return (b & c) | (b & d) | (c & d)
329
const hex32Decoder = new CustomTextDecoder()
330
const hex32DecodeBuffer = new Uint8Array(40)
331
function hex32Str(A: int32, B: int32, C: int32, D: int32, E: int32): string {
332
writeIntAsHexUTF8(A, hex32DecodeBuffer, 0)
333
writeIntAsHexUTF8(B, hex32DecodeBuffer, 8)
334
writeIntAsHexUTF8(C, hex32DecodeBuffer, 16)
335
writeIntAsHexUTF8(D, hex32DecodeBuffer, 24)
336
writeIntAsHexUTF8(E, hex32DecodeBuffer, 32)
337
return hex32Decoder.decode(hex32DecodeBuffer)
340
function writeIntAsHexUTF8(value: int32, buffer: Uint8Array, byteOffset: int32) {
341
buffer[byteOffset++] = nibbleToHexCode((value >> 28) & 0xF)
342
buffer[byteOffset++] = nibbleToHexCode((value >> 24) & 0xF)
343
buffer[byteOffset++] = nibbleToHexCode((value >> 20) & 0xF)
344
buffer[byteOffset++] = nibbleToHexCode((value >> 16) & 0xF)
345
buffer[byteOffset++] = nibbleToHexCode((value >> 12) & 0xF)
346
buffer[byteOffset++] = nibbleToHexCode((value >> 8 ) & 0xF)
347
buffer[byteOffset++] = nibbleToHexCode((value >> 4 ) & 0xF)
348
buffer[byteOffset++] = nibbleToHexCode((value >> 0 ) & 0xF)
351
function nibbleToHexCode(nibble: int32) {
352
return nibble > 9 ? nibble + 87 : nibble + 48