diff options
author | HappyLeslieAlexander <HappyLeslieAlexander@duck.com> | 2025-01-25 14:39:31 +0100 |
---|---|---|
committer | HappyLeslieAlexander <HappyLeslieAlexander@duck.com> | 2025-01-25 14:39:31 +0100 |
commit | 5ee2fd508d36eac0c22b1ab82e2f299d408c56e5 (patch) | |
tree | 63e578ab15c37333c369d1aa586ab326ab5a5ef6 /_worker.js |
Diffstat (limited to '_worker.js')
-rw-r--r-- | _worker.js | 985 |
1 files changed, 985 insertions, 0 deletions
diff --git a/_worker.js b/_worker.js new file mode 100644 index 0000000..cab2f2e --- /dev/null +++ b/_worker.js @@ -0,0 +1,985 @@ +// src/worker.js +import { connect } from "cloudflare:sockets"; + +let Pswd = 'LeslieAlexanderTrojan88c8292f-d679-41a6-913e-335546702bdf'; +const proxyIPs = ["cdn-all.xn--b6gac.eu.org"]; // https://github.com/HappyLeslieAlexander/Cloudflare_Trojan/blob/main/proxyip.txt 中的地址也可以 +let hostnames = ['www.visa.com.hk']; + +let sha224Password ; +let proxyIP = proxyIPs[Math.floor(Math.random() * proxyIPs.length)]; +const worker_default = { + /** + * @param {import("@cloudflare/workers-types").Request} request + * @param {proxyip: string, pswd: string} env + * @param {import("@cloudflare/workers-types").ExecutionContext} ctx + * @returns {Promise<Response>} + */ + async fetch(request, env, ctx) { + try { + proxyIP = env.proxyip || proxyIP; + Pswd = env.pswd || Pswd + sha224Password = sha256.sha224(Pswd); + const upgradeHeader = request.headers.get("Upgrade"); + if (!upgradeHeader || upgradeHeader !== 'websocket') { + const url = new URL(request.url); + switch (url.pathname) { + case '/cf': + return new Response(JSON.stringify(request.cf, null, 4), { + status: 200, + headers: { + "Content-Type": "application/json;charset=utf-8", + }, + }); + + case `/${Pswd}`: { + const trojanConfig = gettrojanConfig(Pswd, request.headers.get('Host')); + return new Response(`${trojanConfig}`, { + status: 200, + headers: { + "Content-Type": "text/plain;charset=utf-8", + } + }); + } + default: + // return new Response('Not found', { status: 404 }); + // For any other path, reverse proxy to 'ramdom website' and return the original response, caching it in the process + const randomHostname = hostnames[Math.floor(Math.random() * hostnames.length)]; + const newHeaders = new Headers(request.headers); + newHeaders.set('cf-connecting-ip', '1.2.3.4'); + newHeaders.set('x-forwarded-for', '1.2.3.4'); + newHeaders.set('x-real-ip', '1.2.3.4'); + newHeaders.set('referer', 'https://www.google.com/search?q=edtunnel'); + // Use fetch to proxy the request to 15 different domains + const proxyUrl = 'https://' + randomHostname + url.pathname + url.search; + let modifiedRequest = new Request(proxyUrl, { + method: request.method, + headers: newHeaders, + body: request.body, + redirect: 'manual', + }); + const proxyResponse = await fetch(modifiedRequest, { redirect: 'manual' }); + // Check for 302 or 301 redirect status and return an error response + if ([301, 302].includes(proxyResponse.status)) { + return new Response(`Redirects to ${randomHostname} are not allowed.`, { + status: 403, + statusText: 'Forbidden', + }); + } + // Return the response from the proxy server + return proxyResponse; + } + } else { + return await trojanOverWSHandler(request); + } + } catch (err) { + /** @type {Error} */ let e = err; + return new Response(e.toString()); + } + }, +}; + +async function trojanOverWSHandler(request) { + const webSocketPair = new WebSocketPair(); + const [client, webSocket] = Object.values(webSocketPair); + webSocket.accept(); + let address = ""; + let portWithRandomLog = ""; + const log = (info, event) => { + console.log(`[${address}:${portWithRandomLog}] ${info}`, event || ""); + }; + const earlyDataHeader = request.headers.get("sec-websocket-protocol") || ""; + const readableWebSocketStream = makeReadableWebSocketStream(webSocket, earlyDataHeader, log); + let remoteSocketWapper = { + value: null + }; + let udpStreamWrite = null; + readableWebSocketStream.pipeTo(new WritableStream({ + async write(chunk, controller) { + if (udpStreamWrite) { + return udpStreamWrite(chunk); + } + if (remoteSocketWapper.value) { + const writer = remoteSocketWapper.value.writable.getWriter(); + await writer.write(chunk); + writer.releaseLock(); + return; + } + const { + hasError, + message, + portRemote = 443, + addressRemote = "", + rawClientData + } = await parseTrojanHeader(chunk); + address = addressRemote; + portWithRandomLog = `${portRemote}--${Math.random()} tcp`; + if (hasError) { + throw new Error(message); + return; + } + handleTCPOutBound(remoteSocketWapper, addressRemote, portRemote, rawClientData, webSocket, log); + }, + close() { + log(`readableWebSocketStream is closed`); + }, + abort(reason) { + log(`readableWebSocketStream is aborted`, JSON.stringify(reason)); + } + })).catch((err) => { + log("readableWebSocketStream pipeTo error", err); + }); + return new Response(null, { + status: 101, + // @ts-ignore + webSocket: client + }); +} + +async function parseTrojanHeader(buffer) { + if (buffer.byteLength < 56) { + return { + hasError: true, + message: "invalid data" + }; + } + let crLfIndex = 56; + if (new Uint8Array(buffer.slice(56, 57))[0] !== 0x0d || new Uint8Array(buffer.slice(57, 58))[0] !== 0x0a) { + return { + hasError: true, + message: "invalid header format (missing CR LF)" + }; + } + const password = new TextDecoder().decode(buffer.slice(0, crLfIndex)); + if (password !== sha224Password) { + return { + hasError: true, + message: "invalid password" + }; + } + + const socks5DataBuffer = buffer.slice(crLfIndex + 2); + if (socks5DataBuffer.byteLength < 6) { + return { + hasError: true, + message: "invalid SOCKS5 request data" + }; + } + + const view = new DataView(socks5DataBuffer); + const cmd = view.getUint8(0); + if (cmd !== 1) { + return { + hasError: true, + message: "unsupported command, only TCP (CONNECT) is allowed" + }; + } + + const atype = view.getUint8(1); + // 0x01: IPv4 address + // 0x03: Domain name + // 0x04: IPv6 address + let addressLength = 0; + let addressIndex = 2; + let address = ""; + switch (atype) { + case 1: + addressLength = 4; + address = new Uint8Array( + socks5DataBuffer.slice(addressIndex, addressIndex + addressLength) + ).join("."); + break; + case 3: + addressLength = new Uint8Array( + socks5DataBuffer.slice(addressIndex, addressIndex + 1) + )[0]; + addressIndex += 1; + address = new TextDecoder().decode( + socks5DataBuffer.slice(addressIndex, addressIndex + addressLength) + ); + break; + case 4: + addressLength = 16; + const dataView = new DataView(socks5DataBuffer.slice(addressIndex, addressIndex + addressLength)); + const ipv6 = []; + for (let i = 0; i < 8; i++) { + ipv6.push(dataView.getUint16(i * 2).toString(16)); + } + address = ipv6.join(":"); + break; + default: + return { + hasError: true, + message: `invalid addressType is ${atype}` + }; + } + + if (!address) { + return { + hasError: true, + message: `address is empty, addressType is ${atype}` + }; + } + + const portIndex = addressIndex + addressLength; + const portBuffer = socks5DataBuffer.slice(portIndex, portIndex + 2); + const portRemote = new DataView(portBuffer).getUint16(0); + return { + hasError: false, + addressRemote: address, + portRemote, + rawClientData: socks5DataBuffer.slice(portIndex + 4) + }; +} + +async function handleTCPOutBound(remoteSocket, addressRemote, portRemote, rawClientData, webSocket, log) { + async function connectAndWrite(address, port) { + const tcpSocket2 = connect({ + hostname: address, + port + }); + remoteSocket.value = tcpSocket2; + log(`connected to ${address}:${port}`); + const writer = tcpSocket2.writable.getWriter(); + await writer.write(rawClientData); + writer.releaseLock(); + return tcpSocket2; + } + async function retry() { + const tcpSocket2 = await connectAndWrite(proxyIP || addressRemote, portRemote); + tcpSocket2.closed.catch((error) => { + console.log("retry tcpSocket closed error", error); + }).finally(() => { + safeCloseWebSocket(webSocket); + }); + remoteSocketToWS(tcpSocket2, webSocket, null, log); + } + const tcpSocket = await connectAndWrite(addressRemote, portRemote); + remoteSocketToWS(tcpSocket, webSocket, retry, log); +} + +function makeReadableWebSocketStream(webSocketServer, earlyDataHeader, log) { + let readableStreamCancel = false; + const stream = new ReadableStream({ + start(controller) { + webSocketServer.addEventListener("message", (event) => { + if (readableStreamCancel) { + return; + } + const message = event.data; + controller.enqueue(message); + }); + webSocketServer.addEventListener("close", () => { + safeCloseWebSocket(webSocketServer); + if (readableStreamCancel) { + return; + } + controller.close(); + }); + webSocketServer.addEventListener("error", (err) => { + log("webSocketServer error"); + controller.error(err); + }); + const { earlyData, error } = base64ToArrayBuffer(earlyDataHeader); + if (error) { + controller.error(error); + } else if (earlyData) { + controller.enqueue(earlyData); + } + }, + pull(controller) {}, + cancel(reason) { + if (readableStreamCancel) { + return; + } + log(`readableStream was canceled, due to ${reason}`); + readableStreamCancel = true; + safeCloseWebSocket(webSocketServer); + } + }); + return stream; +} + +async function remoteSocketToWS(remoteSocket, webSocket, retry, log) { + let hasIncomingData = false; + await remoteSocket.readable.pipeTo( + new WritableStream({ + start() {}, + /** + * + * @param {Uint8Array} chunk + * @param {*} controller + */ + async write(chunk, controller) { + hasIncomingData = true; + if (webSocket.readyState !== WS_READY_STATE_OPEN) { + controller.error( + "webSocket connection is not open" + ); + } + webSocket.send(chunk); + }, + close() { + log(`remoteSocket.readable is closed, hasIncomingData: ${hasIncomingData}`); + }, + abort(reason) { + console.error("remoteSocket.readable abort", reason); + } + }) + ).catch((error) => { + console.error( + `remoteSocketToWS error:`, + error.stack || error + ); + safeCloseWebSocket(webSocket); + }); + if (hasIncomingData === false && retry) { + log(`retry`); + retry(); + } +} + +function base64ToArrayBuffer(base64Str) { + if (!base64Str) { + return { error: null }; + } + try { + base64Str = base64Str.replace(/-/g, "+").replace(/_/g, "/"); + const decode = atob(base64Str); + const arryBuffer = Uint8Array.from(decode, (c) => c.charCodeAt(0)); + return { earlyData: arryBuffer.buffer, error: null }; + } catch (error) { + return { error }; + } +} + +let WS_READY_STATE_OPEN = 1; +let WS_READY_STATE_CLOSING = 2; + +function safeCloseWebSocket(socket) { + try { + if (socket.readyState === WS_READY_STATE_OPEN || socket.readyState === WS_READY_STATE_CLOSING) { + socket.close(); + } + } catch (error) { + console.error("safeCloseWebSocket error", error); + } +} +export { + worker_default as + default +}; + +//# sourceMappingURL=worker.js.map +function gettrojanConfig(Pswd, hostName) { + const wtrojanwstls = `trojan://${Pswd}\u0040www.visa.com.hk:443?security=tls&type=ws&host=${hostName}&path=%2F%3Fed%3D2560#Leslie-Workers/Pages`; + const ptrojanwstls = `trojan://${Pswd}\u0040www.visa.com.hk:443?security=tls&type=ws&host=${hostName}&sni=${hostName}&fp=random&path=%2F%3Fed%3D2560#Leslie-Workers/Pages-TLS`; + const note = `正在使用的ProxyIP:${proxyIP}`; + + if (hostName.includes('pages.dev')) { + return ` +==========================配置详解============================== + +${note} + +################################################################ +Cloudflare-Pages-Trojan+Websocket+TLS节点,分享链接如下: + +${ptrojanwstls} + +--------------------------------------------------------------- +注意:如果 ${hostName} 在本地网络打不开,客户端必须开启TLS切片功能 +--------------------------------------------------------------- +客户端必要文明参数如下: +客户端地址(address):自定义的域名 或者 优选域名 或者 优选IP +端口(port):6个TLS端口可任意选择(443、8443、2053、2083、2087、2096) +密码:${Pswd} +传输协议(network):Websocket +伪装域名(host):${hostName} +路径(path):/?ed=2560 +传输安全(TLS):开启 +跳过证书验证(allowlnsecure):false +################################################################ +捐赠 +加密货币 +TRON +TY7n1xwiHCBqcQqGH1cxjTQqZTuTXbzB4S +Ethereum +0xed57e7237e88cec19d3fd12a0d26bacb1dcc247b +Polygon +0xed57e7237e88cec19d3fd12a0d26bacb1dcc247b +TON +UQC4r4gxAIbOTEEZGG-C1Ffn9inRo24J7qw3U0dFfaIfKyFr +--------------------------------------------------------------- +联系 +Telegram: https://t.me/Depressed_LeslieAlexander/ +项目地址: https://github.com/HappyLeslieAlexander/Cloudflare_Trojan/ +By Leslie Alexander +`; + } else { + return ` +==========================配置详解============================== + +${note} + +################################################################ +一、Cloudflare-Workers-Trojan+Websocket+TLS节点,分享链接如下: + +${wtrojanwstls} + +--------------------------------------------------------------- +注意:如果 ${hostName} 在本地网络打不开,客户端必须开启TLS切片功能 +--------------------------------------------------------------- +客户端必要文明参数如下: +客户端地址(address):自定义的域名 或者 优选域名 或者 优选IP +端口(port):6个TLS端口可任意选择(443、8443、2053、2083、2087、2096) +密码:${Pswd} +传输协议(network):Websocket +伪装域名(host):${hostName} +路径(path):/?ed=2560 +################################################################ +捐赠 +加密货币 +TRON +TY7n1xwiHCBqcQqGH1cxjTQqZTuTXbzB4S +Ethereum +0xed57e7237e88cec19d3fd12a0d26bacb1dcc247b +Polygon +0xed57e7237e88cec19d3fd12a0d26bacb1dcc247b +TON +UQC4r4gxAIbOTEEZGG-C1Ffn9inRo24J7qw3U0dFfaIfKyFr +--------------------------------------------------------------- +联系 +Telegram: https://t.me/Depressed_LeslieAlexander/ +E-mail: https://github.com/HappyLeslieAlexander/ +项目地址: https://github.com/HappyLeslieAlexander/Cloudflare_Trojan/ +By Leslie Alexander +`; + } +} + +/** + * [js-sha256]{@link https://github.com/emn178/js-sha256} + * + * @version 0.11.0 + * @author Chen, Yi-Cyuan [emn178@gmail.com] + * @copyright Chen, Yi-Cyuan 2014-2024 + * @license MIT + */ +/*jslint bitwise: true */ +(function () { + 'use strict'; + + var ERROR = 'input is invalid type'; + var WINDOW = typeof window === 'object'; + var root = WINDOW ? window : {}; + if (root.JS_SHA256_NO_WINDOW) { + WINDOW = false; + } + var WEB_WORKER = !WINDOW && typeof self === 'object'; + var NODE_JS = !root.JS_SHA256_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node; + if (NODE_JS) { + root = global; + } else if (WEB_WORKER) { + root = self; + } + var COMMON_JS = !root.JS_SHA256_NO_COMMON_JS && typeof module === 'object' && module.exports; + var AMD = typeof define === 'function' && define.amd; + var ARRAY_BUFFER = !root.JS_SHA256_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined'; + var HEX_CHARS = '0123456789abcdef'.split(''); + var EXTRA = [-2147483648, 8388608, 32768, 128]; + var SHIFT = [24, 16, 8, 0]; + var K = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + ]; + var OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer']; + + var blocks = []; + + if (root.JS_SHA256_NO_NODE_JS || !Array.isArray) { + Array.isArray = function (obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; + }; + } + + if (ARRAY_BUFFER && (root.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) { + ArrayBuffer.isView = function (obj) { + return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer; + }; + } + + var createOutputMethod = function (outputType, is224) { + return function (message) { + return new Sha256(is224, true).update(message)[outputType](); + }; + }; + + var createMethod = function (is224) { + var method = createOutputMethod('hex', is224); + if (NODE_JS) { + method = nodeWrap(method, is224); + } + method.create = function () { + return new Sha256(is224); + }; + method.update = function (message) { + return method.create().update(message); + }; + for (var i = 0; i < OUTPUT_TYPES.length; ++i) { + var type = OUTPUT_TYPES[i]; + method[type] = createOutputMethod(type, is224); + } + return method; + }; + + var nodeWrap = function (method, is224) { + var crypto = require('crypto') + var Buffer = require('buffer').Buffer; + var algorithm = is224 ? 'sha224' : 'sha256'; + var bufferFrom; + if (Buffer.from && !root.JS_SHA256_NO_BUFFER_FROM) { + bufferFrom = Buffer.from; + } else { + bufferFrom = function (message) { + return new Buffer(message); + }; + } + var nodeMethod = function (message) { + if (typeof message === 'string') { + return crypto.createHash(algorithm).update(message, 'utf8').digest('hex'); + } else { + if (message === null || message === undefined) { + throw new Error(ERROR); + } else if (message.constructor === ArrayBuffer) { + message = new Uint8Array(message); + } + } + if (Array.isArray(message) || ArrayBuffer.isView(message) || + message.constructor === Buffer) { + return crypto.createHash(algorithm).update(bufferFrom(message)).digest('hex'); + } else { + return method(message); + } + }; + return nodeMethod; + }; + + var createHmacOutputMethod = function (outputType, is224) { + return function (key, message) { + return new HmacSha256(key, is224, true).update(message)[outputType](); + }; + }; + + var createHmacMethod = function (is224) { + var method = createHmacOutputMethod('hex', is224); + method.create = function (key) { + return new HmacSha256(key, is224); + }; + method.update = function (key, message) { + return method.create(key).update(message); + }; + for (var i = 0; i < OUTPUT_TYPES.length; ++i) { + var type = OUTPUT_TYPES[i]; + method[type] = createHmacOutputMethod(type, is224); + } + return method; + }; + + function Sha256(is224, sharedMemory) { + if (sharedMemory) { + blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = + blocks[4] = blocks[5] = blocks[6] = blocks[7] = + blocks[8] = blocks[9] = blocks[10] = blocks[11] = + blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + this.blocks = blocks; + } else { + this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + } + + if (is224) { + this.h0 = 0xc1059ed8; + this.h1 = 0x367cd507; + this.h2 = 0x3070dd17; + this.h3 = 0xf70e5939; + this.h4 = 0xffc00b31; + this.h5 = 0x68581511; + this.h6 = 0x64f98fa7; + this.h7 = 0xbefa4fa4; + } else { // 256 + this.h0 = 0x6a09e667; + this.h1 = 0xbb67ae85; + this.h2 = 0x3c6ef372; + this.h3 = 0xa54ff53a; + this.h4 = 0x510e527f; + this.h5 = 0x9b05688c; + this.h6 = 0x1f83d9ab; + this.h7 = 0x5be0cd19; + } + + this.block = this.start = this.bytes = this.hBytes = 0; + this.finalized = this.hashed = false; + this.first = true; + this.is224 = is224; + } + + Sha256.prototype.update = function (message) { + if (this.finalized) { + return; + } + var notString, type = typeof message; + if (type !== 'string') { + if (type === 'object') { + if (message === null) { + throw new Error(ERROR); + } else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) { + message = new Uint8Array(message); + } else if (!Array.isArray(message)) { + if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) { + throw new Error(ERROR); + } + } + } else { + throw new Error(ERROR); + } + notString = true; + } + var code, index = 0, i, length = message.length, blocks = this.blocks; + while (index < length) { + if (this.hashed) { + this.hashed = false; + blocks[0] = this.block; + this.block = blocks[16] = blocks[1] = blocks[2] = blocks[3] = + blocks[4] = blocks[5] = blocks[6] = blocks[7] = + blocks[8] = blocks[9] = blocks[10] = blocks[11] = + blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + } + + if (notString) { + for (i = this.start; index < length && i < 64; ++index) { + blocks[i >>> 2] |= message[index] << SHIFT[i++ & 3]; + } + } else { + for (i = this.start; index < length && i < 64; ++index) { + code = message.charCodeAt(index); + if (code < 0x80) { + blocks[i >>> 2] |= code << SHIFT[i++ & 3]; + } else if (code < 0x800) { + blocks[i >>> 2] |= (0xc0 | (code >>> 6)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } else if (code < 0xd800 || code >= 0xe000) { + blocks[i >>> 2] |= (0xe0 | (code >>> 12)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | ((code >>> 6) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } else { + code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff)); + blocks[i >>> 2] |= (0xf0 | (code >>> 18)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | ((code >>> 12) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | ((code >>> 6) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } + } + } + + this.lastByteIndex = i; + this.bytes += i - this.start; + if (i >= 64) { + this.block = blocks[16]; + this.start = i - 64; + this.hash(); + this.hashed = true; + } else { + this.start = i; + } + } + if (this.bytes > 4294967295) { + this.hBytes += this.bytes / 4294967296 << 0; + this.bytes = this.bytes % 4294967296; + } + return this; + }; + + Sha256.prototype.finalize = function () { + if (this.finalized) { + return; + } + this.finalized = true; + var blocks = this.blocks, i = this.lastByteIndex; + blocks[16] = this.block; + blocks[i >>> 2] |= EXTRA[i & 3]; + this.block = blocks[16]; + if (i >= 56) { + if (!this.hashed) { + this.hash(); + } + blocks[0] = this.block; + blocks[16] = blocks[1] = blocks[2] = blocks[3] = + blocks[4] = blocks[5] = blocks[6] = blocks[7] = + blocks[8] = blocks[9] = blocks[10] = blocks[11] = + blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + } + blocks[14] = this.hBytes << 3 | this.bytes >>> 29; + blocks[15] = this.bytes << 3; + this.hash(); + }; + + Sha256.prototype.hash = function () { + var a = this.h0, b = this.h1, c = this.h2, d = this.h3, e = this.h4, f = this.h5, g = this.h6, + h = this.h7, blocks = this.blocks, j, s0, s1, maj, t1, t2, ch, ab, da, cd, bc; + + for (j = 16; j < 64; ++j) { + // rightrotate + t1 = blocks[j - 15]; + s0 = ((t1 >>> 7) | (t1 << 25)) ^ ((t1 >>> 18) | (t1 << 14)) ^ (t1 >>> 3); + t1 = blocks[j - 2]; + s1 = ((t1 >>> 17) | (t1 << 15)) ^ ((t1 >>> 19) | (t1 << 13)) ^ (t1 >>> 10); + blocks[j] = blocks[j - 16] + s0 + blocks[j - 7] + s1 << 0; + } + + bc = b & c; + for (j = 0; j < 64; j += 4) { + if (this.first) { + if (this.is224) { + ab = 300032; + t1 = blocks[0] - 1413257819; + h = t1 - 150054599 << 0; + d = t1 + 24177077 << 0; + } else { + ab = 704751109; + t1 = blocks[0] - 210244248; + h = t1 - 1521486534 << 0; + d = t1 + 143694565 << 0; + } + this.first = false; + } else { + s0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10)); + s1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7)); + ab = a & b; + maj = ab ^ (a & c) ^ bc; + ch = (e & f) ^ (~e & g); + t1 = h + s1 + ch + K[j] + blocks[j]; + t2 = s0 + maj; + h = d + t1 << 0; + d = t1 + t2 << 0; + } + s0 = ((d >>> 2) | (d << 30)) ^ ((d >>> 13) | (d << 19)) ^ ((d >>> 22) | (d << 10)); + s1 = ((h >>> 6) | (h << 26)) ^ ((h >>> 11) | (h << 21)) ^ ((h >>> 25) | (h << 7)); + da = d & a; + maj = da ^ (d & b) ^ ab; + ch = (h & e) ^ (~h & f); + t1 = g + s1 + ch + K[j + 1] + blocks[j + 1]; + t2 = s0 + maj; + g = c + t1 << 0; + c = t1 + t2 << 0; + s0 = ((c >>> 2) | (c << 30)) ^ ((c >>> 13) | (c << 19)) ^ ((c >>> 22) | (c << 10)); + s1 = ((g >>> 6) | (g << 26)) ^ ((g >>> 11) | (g << 21)) ^ ((g >>> 25) | (g << 7)); + cd = c & d; + maj = cd ^ (c & a) ^ da; + ch = (g & h) ^ (~g & e); + t1 = f + s1 + ch + K[j + 2] + blocks[j + 2]; + t2 = s0 + maj; + f = b + t1 << 0; + b = t1 + t2 << 0; + s0 = ((b >>> 2) | (b << 30)) ^ ((b >>> 13) | (b << 19)) ^ ((b >>> 22) | (b << 10)); + s1 = ((f >>> 6) | (f << 26)) ^ ((f >>> 11) | (f << 21)) ^ ((f >>> 25) | (f << 7)); + bc = b & c; + maj = bc ^ (b & d) ^ cd; + ch = (f & g) ^ (~f & h); + t1 = e + s1 + ch + K[j + 3] + blocks[j + 3]; + t2 = s0 + maj; + e = a + t1 << 0; + a = t1 + t2 << 0; + this.chromeBugWorkAround = true; + } + + this.h0 = this.h0 + a << 0; + this.h1 = this.h1 + b << 0; + this.h2 = this.h2 + c << 0; + this.h3 = this.h3 + d << 0; + this.h4 = this.h4 + e << 0; + this.h5 = this.h5 + f << 0; + this.h6 = this.h6 + g << 0; + this.h7 = this.h7 + h << 0; + }; + + Sha256.prototype.hex = function () { + this.finalize(); + + var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5, + h6 = this.h6, h7 = this.h7; + + var hex = HEX_CHARS[(h0 >>> 28) & 0x0F] + HEX_CHARS[(h0 >>> 24) & 0x0F] + + HEX_CHARS[(h0 >>> 20) & 0x0F] + HEX_CHARS[(h0 >>> 16) & 0x0F] + + HEX_CHARS[(h0 >>> 12) & 0x0F] + HEX_CHARS[(h0 >>> 8) & 0x0F] + + HEX_CHARS[(h0 >>> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] + + HEX_CHARS[(h1 >>> 28) & 0x0F] + HEX_CHARS[(h1 >>> 24) & 0x0F] + + HEX_CHARS[(h1 >>> 20) & 0x0F] + HEX_CHARS[(h1 >>> 16) & 0x0F] + + HEX_CHARS[(h1 >>> 12) & 0x0F] + HEX_CHARS[(h1 >>> 8) & 0x0F] + + HEX_CHARS[(h1 >>> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] + + HEX_CHARS[(h2 >>> 28) & 0x0F] + HEX_CHARS[(h2 >>> 24) & 0x0F] + + HEX_CHARS[(h2 >>> 20) & 0x0F] + HEX_CHARS[(h2 >>> 16) & 0x0F] + + HEX_CHARS[(h2 >>> 12) & 0x0F] + HEX_CHARS[(h2 >>> 8) & 0x0F] + + HEX_CHARS[(h2 >>> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] + + HEX_CHARS[(h3 >>> 28) & 0x0F] + HEX_CHARS[(h3 >>> 24) & 0x0F] + + HEX_CHARS[(h3 >>> 20) & 0x0F] + HEX_CHARS[(h3 >>> 16) & 0x0F] + + HEX_CHARS[(h3 >>> 12) & 0x0F] + HEX_CHARS[(h3 >>> 8) & 0x0F] + + HEX_CHARS[(h3 >>> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] + + HEX_CHARS[(h4 >>> 28) & 0x0F] + HEX_CHARS[(h4 >>> 24) & 0x0F] + + HEX_CHARS[(h4 >>> 20) & 0x0F] + HEX_CHARS[(h4 >>> 16) & 0x0F] + + HEX_CHARS[(h4 >>> 12) & 0x0F] + HEX_CHARS[(h4 >>> 8) & 0x0F] + + HEX_CHARS[(h4 >>> 4) & 0x0F] + HEX_CHARS[h4 & 0x0F] + + HEX_CHARS[(h5 >>> 28) & 0x0F] + HEX_CHARS[(h5 >>> 24) & 0x0F] + + HEX_CHARS[(h5 >>> 20) & 0x0F] + HEX_CHARS[(h5 >>> 16) & 0x0F] + + HEX_CHARS[(h5 >>> 12) & 0x0F] + HEX_CHARS[(h5 >>> 8) & 0x0F] + + HEX_CHARS[(h5 >>> 4) & 0x0F] + HEX_CHARS[h5 & 0x0F] + + HEX_CHARS[(h6 >>> 28) & 0x0F] + HEX_CHARS[(h6 >>> 24) & 0x0F] + + HEX_CHARS[(h6 >>> 20) & 0x0F] + HEX_CHARS[(h6 >>> 16) & 0x0F] + + HEX_CHARS[(h6 >>> 12) & 0x0F] + HEX_CHARS[(h6 >>> 8) & 0x0F] + + HEX_CHARS[(h6 >>> 4) & 0x0F] + HEX_CHARS[h6 & 0x0F]; + if (!this.is224) { + hex += HEX_CHARS[(h7 >>> 28) & 0x0F] + HEX_CHARS[(h7 >>> 24) & 0x0F] + + HEX_CHARS[(h7 >>> 20) & 0x0F] + HEX_CHARS[(h7 >>> 16) & 0x0F] + + HEX_CHARS[(h7 >>> 12) & 0x0F] + HEX_CHARS[(h7 >>> 8) & 0x0F] + + HEX_CHARS[(h7 >>> 4) & 0x0F] + HEX_CHARS[h7 & 0x0F]; + } + return hex; + }; + + Sha256.prototype.toString = Sha256.prototype.hex; + + Sha256.prototype.digest = function () { + this.finalize(); + + var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5, + h6 = this.h6, h7 = this.h7; + + var arr = [ + (h0 >>> 24) & 0xFF, (h0 >>> 16) & 0xFF, (h0 >>> 8) & 0xFF, h0 & 0xFF, + (h1 >>> 24) & 0xFF, (h1 >>> 16) & 0xFF, (h1 >>> 8) & 0xFF, h1 & 0xFF, + (h2 >>> 24) & 0xFF, (h2 >>> 16) & 0xFF, (h2 >>> 8) & 0xFF, h2 & 0xFF, + (h3 >>> 24) & 0xFF, (h3 >>> 16) & 0xFF, (h3 >>> 8) & 0xFF, h3 & 0xFF, + (h4 >>> 24) & 0xFF, (h4 >>> 16) & 0xFF, (h4 >>> 8) & 0xFF, h4 & 0xFF, + (h5 >>> 24) & 0xFF, (h5 >>> 16) & 0xFF, (h5 >>> 8) & 0xFF, h5 & 0xFF, + (h6 >>> 24) & 0xFF, (h6 >>> 16) & 0xFF, (h6 >>> 8) & 0xFF, h6 & 0xFF + ]; + if (!this.is224) { + arr.push((h7 >>> 24) & 0xFF, (h7 >>> 16) & 0xFF, (h7 >>> 8) & 0xFF, h7 & 0xFF); + } + return arr; + }; + + Sha256.prototype.array = Sha256.prototype.digest; + + Sha256.prototype.arrayBuffer = function () { + this.finalize(); + + var buffer = new ArrayBuffer(this.is224 ? 28 : 32); + var dataView = new DataView(buffer); + dataView.setUint32(0, this.h0); + dataView.setUint32(4, this.h1); + dataView.setUint32(8, this.h2); + dataView.setUint32(12, this.h3); + dataView.setUint32(16, this.h4); + dataView.setUint32(20, this.h5); + dataView.setUint32(24, this.h6); + if (!this.is224) { + dataView.setUint32(28, this.h7); + } + return buffer; + }; + + function HmacSha256(key, is224, sharedMemory) { + var i, type = typeof key; + if (type === 'string') { + var bytes = [], length = key.length, index = 0, code; + for (i = 0; i < length; ++i) { + code = key.charCodeAt(i); + if (code < 0x80) { + bytes[index++] = code; + } else if (code < 0x800) { + bytes[index++] = (0xc0 | (code >>> 6)); + bytes[index++] = (0x80 | (code & 0x3f)); + } else if (code < 0xd800 || code >= 0xe000) { + bytes[index++] = (0xe0 | (code >>> 12)); + bytes[index++] = (0x80 | ((code >>> 6) & 0x3f)); + bytes[index++] = (0x80 | (code & 0x3f)); + } else { + code = 0x10000 + (((code & 0x3ff) << 10) | (key.charCodeAt(++i) & 0x3ff)); + bytes[index++] = (0xf0 | (code >>> 18)); + bytes[index++] = (0x80 | ((code >>> 12) & 0x3f)); + bytes[index++] = (0x80 | ((code >>> 6) & 0x3f)); + bytes[index++] = (0x80 | (code & 0x3f)); + } + } + key = bytes; + } else { + if (type === 'object') { + if (key === null) { + throw new Error(ERROR); + } else if (ARRAY_BUFFER && key.constructor === ArrayBuffer) { + key = new Uint8Array(key); + } else if (!Array.isArray(key)) { + if (!ARRAY_BUFFER || !ArrayBuffer.isView(key)) { + throw new Error(ERROR); + } + } + } else { + throw new Error(ERROR); + } + } + + if (key.length > 64) { + key = (new Sha256(is224, true)).update(key).array(); + } + + var oKeyPad = [], iKeyPad = []; + for (i = 0; i < 64; ++i) { + var b = key[i] || 0; + oKeyPad[i] = 0x5c ^ b; + iKeyPad[i] = 0x36 ^ b; + } + + Sha256.call(this, is224, sharedMemory); + + this.update(iKeyPad); + this.oKeyPad = oKeyPad; + this.inner = true; + this.sharedMemory = sharedMemory; + } + HmacSha256.prototype = new Sha256(); + + HmacSha256.prototype.finalize = function () { + Sha256.prototype.finalize.call(this); + if (this.inner) { + this.inner = false; + var innerHash = this.array(); + Sha256.call(this, this.is224, this.sharedMemory); + this.update(this.oKeyPad); + this.update(innerHash); + Sha256.prototype.finalize.call(this); + } + }; + + var exports = createMethod(); + exports.sha256 = exports; + exports.sha224 = createMethod(true); + exports.sha256.hmac = createHmacMethod(); + exports.sha224.hmac = createHmacMethod(true); + + if (COMMON_JS) { + module.exports = exports; + } else { + root.sha256 = exports.sha256; + root.sha224 = exports.sha224; + if (AMD) { + define(function () { + return exports; + }); + } + } +})(); |