1/* 2 * Copyright (c) 2022-2025 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 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 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. 14 */ 15 16import { CustomTextDecoder } from "@koalaui/compat" 17import { int32 } from "@koalaui/compat" 18 19const K = [ 20 (0x5a827999 | 0) as int32, 21 (0x6ed9eba1 | 0) as int32, 22 (0x8f1bbcdc | 0) as int32, 23 (0xca62c1d6 | 0) as int32, 24] 25 26const inputBytes = 64 27const inputWords = inputBytes / 4 28const highIndex = inputWords - 2 29const lowIndex = inputWords - 1 30const workWords = 80 31const allocBytes = 80 32const allocWords = allocBytes / 4 33const allocTotal = allocBytes * 100 34 35export function createSha1(): SHA1Hash { 36 return new SHA1Hash() 37} 38 39export 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 47 private _size = 0 48 private _sp = 0 // surrogate pair 49 50 constructor() { 51 if (!sharedBuffer || sharedOffset >= allocTotal) { 52 sharedBuffer = new ArrayBuffer(allocTotal) 53 sharedOffset = 0 54 } 55 56 this._byte = new Uint8Array(sharedBuffer, sharedOffset, allocBytes) 57 this._word = new Int32Array(sharedBuffer, sharedOffset, allocWords) 58 sharedOffset += allocBytes 59 } 60 61 updateString(data: string, encoding?: string): SHA1Hash { 62 return this._utf8(data) 63 } 64 updateInt32(data: int32): SHA1Hash { 65 const buffer = new Int32Array(1) 66 buffer[0] = data 67 return this.update(buffer) 68 } 69 70 update(data: Int32Array | Float32Array | Uint32Array | Uint8Array): SHA1Hash { 71 if (data == null) { 72 throw new TypeError("SHA1Hash expected non-null data: ") 73 } 74 75 let byteOffset: int32 = 0 76 let length: int32 = 0 77 let buffer: ArrayBufferLike | undefined = undefined 78 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 84 buffer = data.buffer 85 } else if (data instanceof Uint32Array) { 86 byteOffset = data.byteOffset as int32 87 length = data.byteLength as int32 88 buffer = data.buffer 89 } else if (data instanceof Float32Array) { 90 byteOffset = data.byteOffset as int32 91 length = data.byteLength as int32 92 buffer = data.buffer 93 } else if (data instanceof Uint8Array) { 94 byteOffset = data.byteOffset as int32 95 length = data.byteLength as int32 96 buffer = data.buffer 97 } 98 99 let blocks: int32 = ((length / inputBytes) | 0) as int32 100 let offset: int32 = 0 101 102 // longer than 1 block 103 if ((blocks != 0) && !(byteOffset & 3) && !(this._size % inputBytes)) { 104 const block = new Int32Array(buffer!, byteOffset, blocks * inputWords) 105 while (blocks--) { 106 this._int32(block, offset >> 2) 107 offset += inputBytes 108 } 109 this._size += offset 110 } 111 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) 117 } 118 119 // no more bytes 120 if (offset == length) return this 121 122 return this._uint8(new Uint8Array(buffer!), offset) 123 } 124 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 130 131 while (offset < length) { 132 const start = this._size % inputBytes 133 let index = start 134 135 while (offset < length && index < inputBytes) { 136 _byte[index++] = data[offset++] 137 } 138 139 if (index >= inputBytes) { 140 this._int32(_word) 141 } 142 143 this._size += index - start 144 } 145 146 return this 147 } 148 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 154 155 for (let offset = 0; offset < length; ) { 156 const start = this._size % inputBytes 157 let index = start 158 159 while (offset < length && index < inputBytes) { 160 let code = text.charCodeAt(offset++) | 0 161 if (code < 0x80) { 162 // ASCII characters 163 _byte[index++] = code 164 } else if (code < 0x800) { 165 // 2 bytes 166 _byte[index++] = 0xC0 | (code >>> 6) 167 _byte[index++] = 0x80 | (code & 0x3F) 168 } else if (code < 0xD800 || code > 0xDFFF) { 169 // 3 bytes 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) 180 surrogate = 0 181 } else { 182 surrogate = code 183 } 184 } 185 186 if (index >= inputBytes) { 187 this._int32(_word) 188 _word[0] = _word[inputWords] 189 } 190 191 this._size += index - start 192 } 193 194 this._sp = surrogate 195 return this 196 } 197 198 private _int32(data: Int32Array, offset?: int32): void { 199 let A = this.A 200 let B = this.B 201 let C = this.C 202 let D = this.D 203 let E = this.E 204 let i = 0 205 offset = ((offset ?? 0) | 0) as int32 206 207 while (i < inputWords) { 208 W[i++] = swap32(data[offset++] as int32) 209 } 210 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)) 213 } 214 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 218 E = D 219 D = C 220 C = rotate30(B) 221 B = A 222 A = T 223 } 224 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 230 } 231 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 238 _byte[i++] = 0x80 239 240 // pad 0 for current word 241 while (i & 3) { 242 _byte[i++] = 0 243 } 244 i >>= 2 245 246 if (i > highIndex) { 247 while (i < inputWords) { 248 _word[i++] = 0 249 } 250 i = 0 251 this._int32(_word) 252 } 253 254 // pad 0 for rest words 255 while (i < inputWords) { 256 _word[i++] = 0 257 } 258 259 // input size 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 265 266 this._int32(_word) 267 268 return (encoding === "hex") ? this._hex() : this._bin() 269 } 270 271 private _hex(): string { 272 let A = this.A 273 let B = this.B 274 let C = this.C 275 let D = this.D 276 let E = this.E 277 278 return hex32Str(A, B, C, D, E) 279 } 280 281 private _bin(): Uint8Array { 282 let A = this.A 283 let B = this.B 284 let C = this.C 285 let D = this.D 286 let E = this.E 287 const _byte = this._byte 288 const _word = this._word 289 290 _word[0] = swap32(A) 291 _word[1] = swap32(B) 292 _word[2] = swap32(C) 293 _word[3] = swap32(D) 294 _word[4] = swap32(E) 295 296 return _byte.slice(0, 20) 297 } 298} 299 300type NS = (num: int32) => string 301type NN = (num: int32) => int32 302 303const W = new Int32Array(workWords) 304 305let sharedBuffer: ArrayBuffer 306let sharedOffset: int32 = 0 307 308const swapLE: NN = ((c:int32):int32 => (((c << 24) & 0xff000000) | ((c << 8) & 0xff0000) | ((c >> 8) & 0xff00) | ((c >> 24) & 0xff))) 309const swapBE: NN = ((c:int32):int32 => c) 310const swap32: NN = isBE() ? swapBE : swapLE 311const rotate1: NN = (num: int32): int32 => (num << 1) | (num >>> 31) 312const rotate5: NN = (num: int32): int32 => (num << 5) | (num >>> 27) 313const rotate30: NN = (num: int32): int32 => (num << 30) | (num >>> 2) 314 315function isBE(): boolean { 316 let a16 = new Uint16Array(1) 317 a16[0] = 0xFEFF 318 let a8 = new Uint8Array(a16.buffer) 319 return a8[0] == 0xFE // BOM 320} 321 322 323function 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) 326 return b ^ c ^ d 327} 328 329const hex32Decoder = new CustomTextDecoder() 330const hex32DecodeBuffer = new Uint8Array(40) 331function 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) 338} 339 340function 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) 349} 350 351function nibbleToHexCode(nibble: int32) { 352 return nibble > 9 ? nibble + 87 : nibble + 48 353} 354