UNPKG

lambda-live-debugger

Version:

Debug Lambda functions locally like it is running in the cloud

313 lines (308 loc) 10.2 kB
const { toUint8Array, concatBytes } = require("@smithy/core/serde"); const { createHmac, createHash } = require("node:crypto"); const { Sha256, Sha256Js, Sha256Node } = require("@smithy/core/checksum"); exports.Sha256 = Sha256; exports.Sha256Js = Sha256Js; exports.Sha256Node = Sha256Node; const BLOCK = 64; const DIGEST_LENGTH = 20; const INIT = new Int32Array([0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]); const K = new Int32Array([0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6]); class Sha1Js { digestLength = DIGEST_LENGTH; state = Int32Array.from(INIT); w; buffer = new Uint8Array(BLOCK); bufferLength = 0; bytesHashed = 0; finished = false; inner; outer; constructor(secret) { if (secret) { const key = Sha1Js.normalizeKey(secret); this.inner = new Sha1Js(); this.outer = new Sha1Js(); const pad = new Uint8Array(BLOCK * 2); for (let i = 0; i < BLOCK; ++i) { pad[i] = 0x36 ^ key[i]; pad[i + BLOCK] = 0x5c ^ key[i]; } this.inner.update(pad.subarray(0, BLOCK)); this.outer.update(pad.subarray(BLOCK)); } } update(data) { if (this.finished) { throw new Error("Attempted to update an already finished HMAC."); } if (this.inner) { this.inner.update(data); return; } let pos = 0; let { length } = data; this.bytesHashed += length; if (this.bufferLength > 0) { while (length > 0 && this.bufferLength < BLOCK) { this.buffer[this.bufferLength++] = data[pos++]; --length; } if (this.bufferLength === BLOCK) { this.hashBuffer(this.buffer, 0); this.bufferLength = 0; } } while (length >= BLOCK) { this.hashBuffer(data, pos); pos += BLOCK; length -= BLOCK; } while (length > 0) { this.buffer[this.bufferLength++] = data[pos++]; --length; } } async digest() { if (this.inner && this.outer) { if (this.finished) { throw new Error("Attempted to digest an already finished HMAC."); } this.finished = true; const innerDigest = this.inner.digestSync(); this.outer.update(innerDigest); return this.outer.digestSync(); } return this.digestSync(); } reset() { this.state = Int32Array.from(INIT); this.buffer = new Uint8Array(BLOCK); this.bufferLength = 0; this.bytesHashed = 0; } digestSync() { const state = this.state.slice(); const buffer = this.buffer.slice(); let bufferLength = this.bufferLength; const bitsHi = (this.bytesHashed / 0x20000000) | 0; const bitsLo = this.bytesHashed << 3; buffer[bufferLength++] = 0x80; if (bufferLength > BLOCK - 8) { for (let i = bufferLength; i < BLOCK; ++i) { buffer[i] = 0; } this.hashBufferWith(state, buffer, 0); bufferLength = 0; } for (let i = bufferLength; i < BLOCK - 8; ++i) { buffer[i] = 0; } const v = new DataView(buffer.buffer, buffer.byteOffset, BLOCK); v.setUint32(BLOCK - 8, bitsHi, false); v.setUint32(BLOCK - 4, bitsLo, false); this.hashBufferWith(state, buffer, 0); const out = new Uint8Array(DIGEST_LENGTH); out[0] = (state[0] >>> 24) & 0xff; out[1] = (state[0] >>> 16) & 0xff; out[2] = (state[0] >>> 8) & 0xff; out[3] = state[0] & 0xff; out[4] = (state[1] >>> 24) & 0xff; out[5] = (state[1] >>> 16) & 0xff; out[6] = (state[1] >>> 8) & 0xff; out[7] = state[1] & 0xff; out[8] = (state[2] >>> 24) & 0xff; out[9] = (state[2] >>> 16) & 0xff; out[10] = (state[2] >>> 8) & 0xff; out[11] = state[2] & 0xff; out[12] = (state[3] >>> 24) & 0xff; out[13] = (state[3] >>> 16) & 0xff; out[14] = (state[3] >>> 8) & 0xff; out[15] = state[3] & 0xff; out[16] = (state[4] >>> 24) & 0xff; out[17] = (state[4] >>> 16) & 0xff; out[18] = (state[4] >>> 8) & 0xff; out[19] = state[4] & 0xff; return out; } static normalizeKey(secret) { const key = toUint8Array(secret); if (key.byteLength > BLOCK) { const h = new Sha1Js(); h.update(key); const digest = h.digestSync(); const padded = new Uint8Array(BLOCK); padded.set(digest); return padded; } const padded = new Uint8Array(BLOCK); padded.set(key); return padded; } hashBuffer(data, offset) { this.hashBufferWith(this.state, data, offset); } hashBufferWith(state, data, offset) { const w = (this.w ??= new Int32Array(80)); let s0 = state[0], s1 = state[1], s2 = state[2], s3 = state[3], s4 = state[4]; for (let t = 0; t < 16; ++t) { w[t] = ((data[offset + t * 4] & 0xff) << 24) | ((data[offset + t * 4 + 1] & 0xff) << 16) | ((data[offset + t * 4 + 2] & 0xff) << 8) | (data[offset + t * 4 + 3] & 0xff); } for (let t = 16; t < 80; ++t) { const x = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16]; w[t] = (x << 1) | (x >>> 31); } for (let t = 0; t < 80; ++t) { const r = t < 20 ? 0 : t < 40 ? 1 : t < 60 ? 2 : 3; const temp = (((((s0 << 5) | (s0 >>> 27)) + (r === 0 ? (s1 & s2) ^ (~s1 & s3) : r === 2 ? (s1 & s2) ^ (s1 & s3) ^ (s2 & s3) : s1 ^ s2 ^ s3)) | 0) + ((s4 + ((K[r] + w[t]) | 0)) | 0)) | 0; s4 = s3; s3 = s2; s2 = (s1 << 30) | (s1 >>> 2); s1 = s0; s0 = temp; } state[0] = (state[0] + s0) | 0; state[1] = (state[1] + s1) | 0; state[2] = (state[2] + s2) | 0; state[3] = (state[3] + s3) | 0; state[4] = (state[4] + s4) | 0; } } const hasNativeCrypto = (() => { try { createHash("sha1"); return true; } catch { return false; } })(); const Sha1Node = hasNativeCrypto ? buildNativeClass() : Sha1Js; function buildNativeClass() { return class Sha1Node { digestLength = 20; secret; hash; isHmac; finished = false; constructor(secret) { this.secret = secret; this.isHmac = !!secret; this.hash = this.createHash(); } update(data) { if (this.finished) { throw new Error("Attempted to update an already finished hash."); } this.hash.update(data); } async digest() { let buf; if (this.isHmac) { this.finished = true; buf = this.hash.digest(); } else { buf = this.hash.copy().digest(); } return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength); } reset() { this.hash = this.createHash(); this.finished = false; } createHash() { return this.secret ? createHmac("sha1", toBuffer(this.secret)) : createHash("sha1"); } }; } function toBuffer(data) { if (typeof data === "string") { return data; } if (ArrayBuffer.isView(data)) { return Buffer.from(data.buffer, data.byteOffset, data.byteLength); } return Buffer.from(data); } const { digest, sign, importKey } = globalThis?.crypto?.subtle ?? {}; const subtle = typeof digest === "function" && typeof sign === "function" && typeof importKey === "function" ? globalThis.crypto.subtle : undefined; const MAX_PENDING_BYTES = 8 * 1024 * 1024; class Sha1WebCrypto { digestLength = 20; secret; pending = []; pendingBytes = 0; fallback; finished = false; constructor(secret) { if (secret) { this.secret = toUint8Array(secret); } } update(data) { if (this.finished) { throw new Error("Attempted to update an already finished HMAC."); } if (this.fallback) { this.fallback.update(data); return; } this.pending.push(data.slice()); this.pendingBytes += data.byteLength; if (this.pendingBytes >= MAX_PENDING_BYTES) { this.switchToFallback(); } } async digest() { if (this.fallback) { return this.fallback.digest(); } if (this.secret && this.finished) { throw new Error("Attempted to digest an already finished HMAC."); } const data = concatBytes(this.pending); if (subtle) { if (this.secret) { this.finished = true; const key = await subtle.importKey("raw", this.secret, { name: "HMAC", hash: "SHA-1" }, false, ["sign"]); const sig = await subtle.sign("HMAC", key, data); return new Uint8Array(sig); } const hash = await subtle.digest("SHA-1", data); return new Uint8Array(hash); } const sha1 = new Sha1Js(this.secret); sha1.update(data); return sha1.digest(); } reset() { this.pending = []; this.pendingBytes = 0; this.fallback = undefined; this.finished = false; } switchToFallback() { const sha1Js = new Sha1Js(this.secret); for (const chunk of this.pending) { sha1Js.update(chunk); } this.fallback = sha1Js; this.pending = []; this.pendingBytes = 0; } } exports.Sha1 = Sha1Node; exports.Sha1Js = Sha1Js; exports.Sha1Node = Sha1Node; exports.Sha1WebCrypto = Sha1WebCrypto;