1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| export default { async fetch(request, env, ctx) { const url = new URL(request.url); const secret = url.pathname.slice(1).replace(/\//g, ""); if (!secret || secret === "") { return new Response("用法: https://your-worker.url/YOUR_SECRET_HERE", { status: 400 }); }
try { const { token } = await getTOTP(secret); return new Response(token, { headers: { "Content-Type": "text/plain;charset=UTF-8", "Access-Control-Allow-Origin": "*" }, }); } catch (e) { return new Response("错误: 无效的 Base32 密钥", { status: 400 }); } } };
async function getTOTP(secret) { const keyBuf = base32ToBuf(secret); const epoch = Math.floor(Date.now() / 1000); const counter = Math.floor(epoch / 30);
const counterBuf = new ArrayBuffer(8); const view = new DataView(counterBuf); view.setUint32(4, counter, false);
const cryptoKey = await crypto.subtle.importKey( "raw", keyBuf, { name: "HMAC", hash: "SHA-1" }, false, ["sign"] ); const signature = await crypto.subtle.sign("HMAC", cryptoKey, counterBuf); const sigBytes = new Uint8Array(signature);
const offset = sigBytes[sigBytes.length - 1] & 0xf; const code = ((sigBytes[offset] & 0x7f) << 24) | ((sigBytes[offset + 1] & 0xff) << 16) | ((sigBytes[offset + 2] & 0xff) << 8) | (sigBytes[offset + 3] & 0xff);
return { token: (code % 1e6).toString().padStart(6, "0") }; }
function base32ToBuf(base32) { const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; const str = base32.toUpperCase().replace(/=+$/, ""); const buf = new Uint8Array(Math.floor((str.length * 5) / 8)); let bits = 0, value = 0, index = 0; for (let i = 0; i < str.length; i++) { const idx = alphabet.indexOf(str[i]); if (idx === -1) continue; value = (value << 5) | idx; bits += 5; if (bits >= 8) { buf[index++] = (value >> (bits - 8)) & 255; bits -= 8; } } return buf; }
|