mtgsig

#!/usr/bin/env node 'use strict'; const crypto = require('crypto'); const fs = require('fs'); const path = require('path'); /** * 完整的单文件mtgsig纯算离线签名器 ...
mtgsig
mtgsig
#!/usr/bin/env node
'use strict';

const crypto = require('crypto');
const fs = require('fs');
const path = require('path');

/**
 * 完整的单文件mtgsig纯算离线签名器
 * 支持离线生成mtgsig签名,使用预设的profile参数
 */

// ============= 基础MD5和XOR工具 =============
function md5Hex(input) {
    return crypto.createHash('md5').update(input).digest('hex');
}

function xorHex(buffer, xorKeyHex) {
    const xorKey = Buffer.from(xorKeyHex, 'hex');
    const out = Buffer.alloc(16);
    for (let i = 0; i < 16; i++) {
        out[i] = buffer[i] ^ xorKey[i];
    }
    return out.toString('hex');
}

function getSaltFromA6(a6) {
    const s = String(a6 || '');
    if (!s.startsWith('h1.9') || s.length < 10) {
        throw new Error('Invalid a6 payload: expected prefix h1.9 and at least 6 salt chars');
    }
    return s.slice(4, 10);
}

// ============= A3生成(从WEBDFPID) =============
function deriveA3FromWebdfpid(webdfpid) {
    const s = String(webdfpid || '').trim();
    if (!s) {
        return '';
    }
    return s.split('-')[0] || '';
}

function parseCookieHeader(cookieHeader) {
    if (!cookieHeader) {
        return {};
    }

    return String(cookieHeader)
        .split(';')
        .map((part) => part.trim())
        .filter(Boolean)
        .reduce((cookies, part) => {
            const idx = part.indexOf('=');
            if (idx === -1) {
                return cookies;
            }

            const key = part.slice(0, idx).trim();
            const value = part.slice(idx + 1).trim();
            if (key) {
                cookies[key] = value;
            }
            return cookies;
        }, {});
}

function pickCookieHeader(input) {
    if (!input) {
        return '';
    }

    return input.cookie
        || input.Cookie
        || (input.headers && (input.headers.Cookie || input.headers.cookie))
        || '';
}

function resolveA3(input = {}) {
    if (input.a3) {
        return String(input.a3);
    }

    const cookies = parseCookieHeader(pickCookieHeader(input));
    return deriveA3FromWebdfpid(input.webdfpid || input.dfpid || cookies.WEBDFPID);
}

// ============= A8生成(核心签名) =============
function generateA8({ a6, timestamp, xorKeyHex, suffix = '2' }) {
    const salt = getSaltFromA6(a6);
    const input = `h1.9${salt}${String(timestamp)}${suffix}`;
    const digest = crypto.createHash('md5').update(input).digest();
    return xorHex(digest, xorKeyHex);
}

// ============= A6Key生成 =============
function generateA6Key(a8, a9, a10) {
    return md5Hex(`${a8}${a9}${a10}`);
}

// ============= D1生成 =============
function generateD1({ d1, sessionId, payload }) {
    if (d1 && typeof d1 === 'string' && d1.length === 32) {
        return d1;
    }
    if (sessionId) {
        return md5Hex(String(sessionId));
    }
    return md5Hex(String(payload || ''));
}

// ============= A5生成(假设使用默认值或提供) =============
function generateA5({ a5, timestamp }) {
    if (a5) {
        return String(a5);
    }
    // 如果没有提供,返回空白或使用placeholder
    return '';
}

// ============= 默认Profile(从浏览器捕获) =============
const DEFAULT_PROFILE = {
    a9: '4.2.0,7,39',
    a10: '10',
    a8Suffix: '2',
    xorKeyHex: '19f02f45fb1dee6ce3e613b8aefd6d13',  // 从latest_request_20260420.json推导
    description: 'Default MTGSig profile for offline signing'
};

// ============= 签名生成器 =============
function createOfflineMtgsigSigner(profile = {}) {
    const resolvedProfile = {
        ...DEFAULT_PROFILE,
        ...profile
    };

    function sign(request = {}, options = {}) {
        const timestamp = options.clock || request.a2 || request.timestamp || Date.now();
        const a9 = options.a9 || request.a9 || resolvedProfile.a9;
        const a10 = options.a10 || request.a10 || resolvedProfile.a10;
        const suffix = options.suffix || resolvedProfile.a8Suffix;

        // 需要的数据:a6payload或从请求推导
        const a6 = request.a6 || request.payload;
        if (!a6) {
            throw new Error('Missing a6 payload in request or profile');
        }

        // 生成各个字段
        const a8 = generateA8({
            a6,
            timestamp,
            xorKeyHex: resolvedProfile.xorKeyHex,
            suffix
        });

        const a3 = resolveA3(request);
        const d1 = generateD1({
            d1: request.d1,
            sessionId: request.sessionId || request.arg23,
            payload: a6
        });

        const a6Key = generateA6Key(a8, a9, a10);

        const a5 = generateA5({
            a5: request.a5,
            timestamp
        });

        const a1 = '1.2';
        const a2 = String(timestamp);

        return {
            a1,
            a2,
            a3,
            a5,
            a6,
            a8,
            a9,
            a10,
            d1,
            a6Key,
            x0: 4  // 固定值
        };
    }

    return { sign };
}

// ============= 使用工具和CLI =============
function loadJsonFile(filePath) {
    if (!filePath) {
        return null;
    }
    try {
        const content = fs.readFileSync(path.resolve(filePath), 'utf8');
        return JSON.parse(content);
    } catch (err) {
        console.error(`Failed to load ${filePath}:`, err.message);
        return null;
    }
}

function normalizeInput(input) {
    // 支持多种输入格式
    if (typeof input === 'object' && input.request) {
        return input.request;
    }
    return input || {};
}

function main() {
    const args = process.argv.slice(2);
    const requestPath = args[0];
    const profilePath = args[1];

    // 如果没有参数,显示帮助和演示
    if (!requestPath) {
        console.log('══════════════════════════════════════════════════════');
        console.log('  mtgsig 离线签名生成器');
        console.log('══════════════════════════════════════════════════════');
        console.log('');
        console.log('用法:');
        console.log('  node mtgsig_pure_offline_signer.js [request.json] [profile.json]');
        console.log('');
        console.log('示例 1 - 使用默认参数(旧版本 4.2.0,7,39):');
        console.log('  node mtgsig_pure_offline_signer.js request.json');
        console.log('');
        console.log('示例 2 - 使用新版本参数(4.2.0,7,8):');
        console.log('  node mtgsig_pure_offline_signer.js request.json new_version_profile.json');
        console.log('');
        console.log('request.json 格式:');
        console.log(JSON.stringify({
            a2: '当前时间戳(毫秒),如: ' + Date.now(),
            a6: 'h1.9HK1PkF...(真实的payload)',
            a3: '372887v7w5yu5282067xww2y1463u8wy80yw0z8xu4z9795881u44vw7',
            webdfpid: '372887v7w5yu5282067xww2y1463u8wy80yw0z8xu4z9795881u44vw7-...'
        }, null, 2));
        console.log('');
        console.log('══════════════════════════════════════════════════════');
        console.log('执行演示(使用测试数据):');
        console.log('══════════════════════════════════════════════════════');
        console.log('');

        // 演示模式:使用测试数据
        const demoProfile = {
            a9: '4.2.0,7,8',
            a10: '98',
            a8Suffix: '2',
            xorKeyHex: 'b5ba5e10d16d19d1f2858b6497caabdd'
        };

        const demoRequest = {
            a2: Date.now(),  // 使用当前时间戳而不是写死的值
            a6: 'h1.9HK1PkFlf4Ob8girMsbirMgPXRSgRpTkkG6ek+9XOfrTyBfz/ElmPja0cIA5BI+MVJTPmY38oUCtvWO2Caf4Eu9TsvpzGWF1/sc0go/luDfjsSIp8WN3Ngl6rBYvMeOggErpTlqvfDmGOpZ2wc+hVEHJxt5Q2kKZnPxI8E0IIdELX4+gNL0a4AF5p76g2Wn+bBLVnpvytVNKpqL/Lfr3y2Mk0asGbuqHNzmV3ivHSxnNui3ltOUB7HUGQYhMonCY0kzHlNGhCbTw3/7DQvQgck4UERlc3UrVukC9cES1kIYjxf52Rj1j4V0kfo1cf9OnWYRd9fCWmgXOIoJi5J9Da2+6R+pUtNhnnAkdgxVWWuQGieJQAOFStvImm6qjKvgPpCmBalIVenyKm79g1e6nF5w==',
            a3: '372887v7w5yu5282067xww2y1463u8wy80yw0z8xu4z9795881u44vw7',
            webdfpid: '372887v7w5yu5282067xww2y1463u8wy80yw0z8xu4z9795881u44vw7-1776843046392-1768882819070QUEUOWCfd79fef3d01d5e9aadc18ccd4d0c95072412'
        };

        console.log('📋 演示配置 (新版本):');
        console.log(`  a9: ${demoProfile.a9}`);
        console.log(`  a10: ${demoProfile.a10}`);
        console.log(`  xorKeyHex: ${demoProfile.xorKeyHex}`);
        console.log('');
        console.log('📋 演示请求数据:');
        console.log(`  timestamp: ${demoRequest.a2}`);
        console.log(`  a3: ${demoRequest.a3.substring(0, 30)}...`);
        console.log('');

        const signer = createOfflineMtgsigSigner(demoProfile);
        try {
            const signature = signer.sign(demoRequest);
            console.log('✅ 生成成功!');
            console.log('');
            console.log('📤 生成的签名:');
            console.log(JSON.stringify(signature, null, 2));
            console.log('');
            console.log(`✓ a8 (关键签名): ${signature.a8}`);
            console.log(`✓ a6Key (验证码): ${signature.a6Key}`);
        } catch (err) {
            console.error('❌ 生成失败:', err.message);
            process.exitCode = 1;
        }
        return;
    }

    // 加载请求数据
    const request = loadJsonFile(requestPath) || {};
    console.error(`[INFO] Loaded request from: ${requestPath}`);

    // 加载自定义profile
    let customProfile = null;
    if (profilePath) {
        customProfile = loadJsonFile(profilePath);
        console.error(`[INFO] Loaded profile from: ${profilePath}`);
    }

    // 如果输入JSON包含request字段,使用它
    const normalizedRequest = normalizeInput(request);

    // 创建签名器
    const signer = createOfflineMtgsigSigner(customProfile);

    // 生成签名
    try {
        const signature = signer.sign(normalizedRequest);
        console.log(JSON.stringify(signature, null, 2));
    } catch (err) {
        console.error('[ERROR]', err.message);
        process.exitCode = 1;
    }
}

// ============= 导出 =============
if (require.main === module) {
    main();
}

module.exports = {
    md5Hex,
    xorHex,
    getSaltFromA6,
    generateA8,
    generateA6Key,
    generateD1,
    generateA5,
    resolveA3,
    createOfflineMtgsigSigner,
    DEFAULT_PROFILE
};

大佬勿喷,AI撕的“纯算”,大家参考,我调用了一下没问题 :rofl:

2 个帖子 - 2 位参与者

阅读完整话题

来源: linux.do查看原文