Diffinery

codician/diffinerypublic

Open-source, agent-first GitHub and GitHub Actions alternative that runs on your own Cloudflare account: git hosting, pull requests, code review and drop-in GitHub Actions YAML on Workers, Durable Objects, R2 and Containers.

98 lines · 3.4 KBRaw History
1import { toHex } from "./bytes.js";
2
3/** SHA-1 using WebCrypto (available in Workers and Node 20+). */
4export async function sha1(data: Uint8Array): Promise<Uint8Array> {
5 const digest = await crypto.subtle.digest("SHA-1", data as BufferSource);
6 return new Uint8Array(digest);
7}
8
9export async function sha1Hex(data: Uint8Array): Promise<string> {
10 return toHex(await sha1(data));
11}
12
13export async function sha256Hex(data: Uint8Array): Promise<string> {
14 return toHex(new Uint8Array(await crypto.subtle.digest("SHA-256", data as BufferSource)));
15}
16
17/**
18 * Incremental SHA-1, for checksums over data that is never held whole (a pack streamed to a client).
19 * WebCrypto only hashes a complete buffer.
20 */
21export class Sha1 {
22 private h = new Uint32Array([0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]);
23 private block = new Uint8Array(64);
24 private used = 0;
25 private total = 0;
26 private w = new Uint32Array(80);
27
28 update(data: Uint8Array): this {
29 this.total += data.length;
30 let i = 0;
31 if (this.used) {
32 const take = Math.min(64 - this.used, data.length);
33 this.block.set(data.subarray(0, take), this.used);
34 this.used += take;
35 i = take;
36 if (this.used < 64) return this;
37 this.compress(this.block, 0);
38 this.used = 0;
39 }
40 for (; i + 64 <= data.length; i += 64) this.compress(data, i);
41 if (i < data.length) {
42 this.block.set(data.subarray(i), 0);
43 this.used = data.length - i;
44 }
45 return this;
46 }
47
48 digest(): Uint8Array {
49 const bits = this.total * 8;
50 const pad = new Uint8Array((this.used < 56 ? 56 : 120) - this.used + 8);
51 pad[0] = 0x80;
52 const n = pad.length;
53 pad[n - 8] = Math.floor(bits / 2 ** 56) & 0xff;
54 pad[n - 7] = Math.floor(bits / 2 ** 48) & 0xff;
55 pad[n - 6] = Math.floor(bits / 2 ** 40) & 0xff;
56 pad[n - 5] = Math.floor(bits / 2 ** 32) & 0xff;
57 pad[n - 4] = (bits >>> 24) & 0xff;
58 pad[n - 3] = (bits >>> 16) & 0xff;
59 pad[n - 2] = (bits >>> 8) & 0xff;
60 pad[n - 1] = bits & 0xff;
61 const total = this.total;
62 this.update(pad);
63 this.total = total;
64 const out = new Uint8Array(20);
65 for (let j = 0; j < 5; j++) {
66 out[j * 4] = this.h[j]! >>> 24;
67 out[j * 4 + 1] = (this.h[j]! >>> 16) & 0xff;
68 out[j * 4 + 2] = (this.h[j]! >>> 8) & 0xff;
69 out[j * 4 + 3] = this.h[j]! & 0xff;
70 }
71 return out;
72 }
73
74 private compress(b: Uint8Array, o: number) {
75 const w = this.w;
76 for (let t = 0; t < 16; t++) w[t] = (b[o + t * 4]! << 24) | (b[o + t * 4 + 1]! << 16) | (b[o + t * 4 + 2]! << 8) | b[o + t * 4 + 3]!;
77 for (let t = 16; t < 80; t++) {
78 const x = w[t - 3]! ^ w[t - 8]! ^ w[t - 14]! ^ w[t - 16]!;
79 w[t] = (x << 1) | (x >>> 31);
80 }
81 let [a, bb, c, d, e] = this.h as unknown as [number, number, number, number, number];
82 for (let t = 0; t < 80; t++) {
83 const f = t < 20 ? (bb & c) | (~bb & d) : t < 40 ? bb ^ c ^ d : t < 60 ? (bb & c) | (bb & d) | (c & d) : bb ^ c ^ d;
84 const k = t < 20 ? 0x5a827999 : t < 40 ? 0x6ed9eba1 : t < 60 ? 0x8f1bbcdc : 0xca62c1d6;
85 const tmp = (((a << 5) | (a >>> 27)) + f + e + k + w[t]!) | 0;
86 e = d;
87 d = c;
88 c = (bb << 30) | (bb >>> 2);
89 bb = a;
90 a = tmp;
91 }
92 this.h[0] = (this.h[0]! + a) | 0;
93 this.h[1] = (this.h[1]! + bb) | 0;
94 this.h[2] = (this.h[2]! + c) | 0;
95 this.h[3] = (this.h[3]! + d) | 0;
96 this.h[4] = (this.h[4]! + e) | 0;
97 }
98}