You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Buffer中正确提取2048位Diffie-Hellman质数?

问题描述

我正尝试使用Telegram API实现系统内直接发起Telegram通话,该库要求端到端加密,因此调用Api.messages.GetDhConfig方法从API获取2048位Diffie-Hellman质数。但该质数以Buffer形式返回,我尝试转换时得到负数,结果明显错误,转字符串再转数字也无效。以下是我的代码:

async requestCall(userId: string) {
    try {
      const dhConfig = await this.invoke(
        new Api.messages.GetDhConfig({
          randomLength: 32
        })
      );

      // convert p to number
      // @ts-expect-error ignore
      const p = dhConfig.p as Buffer;
      // convert p to integer

      const pInt = p.readInt32BE(0);

      // @ts-expect-error ignore
      const g = dhConfig.g;

      // chose random value between 1 and p-1
      const a = Math.floor(Math.random() * (pInt - 1)) + 1;

      // calculate g^a mod p
      const ga = Math.pow(g, a) % pInt;
      this.ga = ga;

      // get the ga hash SHA256 of g^a mod p as 32 bytes
      const gaHash = crypto.createHash('sha256').update(ga.toString()).digest();

      // save in the session
      this.a = a;
      this.p = pInt;

      // request the call
      await this.invoke(
        new Api.phone.RequestCall({
          userId,
          randomId: Math.floor(Math.random() * 100000000),
          gAHash: gaHash,
          protocol: new Api.PhoneCallProtocol({
            udpP2p: true,
            udpReflector: true,
            minLayer: 65,
            maxLayer: 65,
            libraryVersions: ['1.0.0', '2.0.0', '3.0.0', '4.0.0']
          })
        })
      );
    } catch (err) {
      console.log('[Telegram Client] [call] error: ', err);
    }
  }

请问该如何从这个Buffer中正确提取出质数?


解决方案

你的问题核心在于2048位的质数远超出了JavaScript原生Number类型的精度范围(Number最多支持53位精度),而且readInt32BE仅读取Buffer前4个字节,完全没拿到完整质数,自然得到错误结果。

下面提供两种可行的处理方案:

方案1:使用原生BigInt

直接将Buffer转为十六进制字符串,再用BigInt解析完整的超大整数:

// 将Buffer转为十六进制字符串,用BigInt解析完整质数
const pBigInt = BigInt('0x' + p.toString('hex'));
const gBigInt = BigInt(dhConfig.g);

生成密码学安全的随机数a(不能用Math.random(),精度不足且不安全):

// 生成1到pBigInt-1之间的安全随机大数
function generateRandomBigInt(max: bigint): bigint {
  const byteLength = max.toString(16).length / 2;
  let randomBytes;
  let randomBigInt;
  do {
    randomBytes = crypto.randomBytes(byteLength);
    randomBigInt = BigInt('0x' + randomBytes.toString('hex'));
  } while (randomBigInt === 0n || randomBigInt >= max);
  return randomBigInt;
}

const a = generateRandomBigInt(pBigInt - 1n);

计算g^a mod p时,用BigInt原生的幂取模运算(避免Math.pow的精度丢失):

const ga = gBigInt ** a % pBigInt;

生成哈希时,要基于ga的原始字节而不是字符串:

// 将BigInt转回Buffer
function bigIntToBuffer(num: bigint): Buffer {
  let hex = num.toString(16);
  // 补前导零确保字节数为偶数
  if (hex.length % 2 !== 0) hex = '0' + hex;
  return Buffer.from(hex, 'hex');
}

const gaBuffer = bigIntToBuffer(ga);
const gaHash = crypto.createHash('sha256').update(gaBuffer).digest();

方案2:使用bn.js库

如果项目依赖bn.js(多数Telegram API库会自带),处理更简洁:

import BN from 'bn.js';

// 将Buffer转为BN实例
const pBN = new BN(p);
const gBN = new BN(dhConfig.g);

// 生成安全随机数a
const a = new BN(crypto.randomBytes(32)).mod(pBN.subn(1)).addn(1);

// 计算g^a mod p
const gaBN = gBN.pow(a, pBN);

// 转Buffer生成哈希
const gaBuffer = gaBN.toBuffer();
const gaHash = crypto.createHash('sha256').update(gaBuffer).digest();

关键注意事项

  • 密码学场景下,绝对不能用Math.random()生成随机数,必须用crypto.randomBytes这类安全随机源。
  • 超大整数运算必须用BigInt或专门的大数库,原生Number会丢失精度导致加密逻辑完全失效。
  • 生成哈希时要基于原始字节(Buffer),转字符串会改变哈希结果,无法通过Telegram的校验。

内容的提问来源于stack exchange,提问作者mohammad hanafi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 15:25:24