You need to enable JavaScript to run this app. 使用libsodium实现Shamir秘密共享时调用密钥转换接口报ENOMEM错误
优惠活动
大模型
产品
解决方案
定价
更多

使用libsodium实现Shamir秘密共享时调用密钥转换接口报ENOMEM错误

问题场景
  • 基于libsodium库实现Shamir秘密共享,开发过程中遇到接口报错问题
  • 核心加解密实现逻辑如下:
function encryptionKeypair () {
    const keypair = {
      publicKey: sodium.sodium_malloc(sodium.crypto_box_PUBLICKEYBYTES),
      secretKey: sodium.sodium_malloc(sodium.crypto_box_SECRETKEYBYTES)
    }
    sodium.crypto_box_keypair(keypair.publicKey, keypair.secretKey)
    return keypair
  },

 function oneWayBox (message, publicKey) {
    console.log('in one way box');
    const curvePublicKey = sodium.sodium_malloc(sodium.crypto_box_PUBLICKEYBYTES)
    console.log('curvePublicKey', curvePublicKey.length);
    console.log('publicKey', publicKey.length);
    
    sodium.crypto_sign_ed25519_pk_to_curve25519(curvePublicKey, publicKey)

    console.log('in one way box');
    console.log('\n');

    const ephemeral = this.encryptionKeypair()
    const nonce = this.randomBytes(sodium.crypto_box_NONCEBYTES)
    const cipherText = sodium.sodium_malloc(message.length + sodium.crypto_box_MACBYTES)
  
    sodium.crypto_box_easy(cipherText, message, nonce, curvePublicKey, ephemeral.secretKey)
    zero(ephemeral.secretKey)
    zero(message)
    return Buffer.concat([nonce, ephemeral.publicKey, cipherText])
  },
  • 秘密分片生成(Secret-sharing-generation.js)实现代码如下:
const secrets = require('secret-sharing')
const s = require('.')

const secret = 'My secret key'
const label = ''

console.log('Secret to share:', secret.toString('hex'))

console.log(`Packing with label: '${label}'`)
const packedSecret = s.packLabel(secret, label)
console.log(`Packed secret: ${packedSecret.toString('hex')}`)
console.log(`Length of packed secret is ${packedSecret.length} bytes.`)
const signingKeypair = s.keypair()
const encryptionKeypair = s.signingKeypairToEncryptionKeypair(signingKeypair)

const custodians = []
for (let i = 0; i < 5; i++) {
  custodians.push(s.encryptionKeypair())
}


console.log('custodians', custodians);

console.log('Creating 5 shares, 3 needed to recover')
secrets.share(packedSecret, 5, 3).then((shards) => {
  console.log('Shards:')
  console.log(shards.map(s => s.toString('hex')))
  console.log('Signed shards:')
  const signedShards = s.signShards(shards, signingKeypair)
  console.log(signedShards.map(s => s.toString('hex')))

  const boxedShards = signedShards.map((shard, i) => {
    return s.oneWayBox(shard, custodians[i].publicKey)
  })

  console.log('Boxed shards:')
  console.log(boxedShards.map(s => s.toString('hex')))
  console.log(`Length of boxed shards are ${boxedShards[0].length} bytes.`)
  secrets.combine(shards.slice(2)).then((result) => {
    console.log('Result of recombining 3 shares:', result.toString())
  })
})

故障现象

使用encryptionKeypair函数生成密钥对后,调用crypto_sign_ed25519_sk_to_curve25519接口对密钥做格式转换时,程序抛出未处理的Promise拒绝警告,报错信息为:
UnhandledPromiseRejectionWarning: Error: ENOMEM, Cannot allocate memory
已核查系统Swap与内存空间,资源完全空闲,系统状态如下:

total        used        free      shared  buff/cache   available
Mem:           3138          83        2896           0         158        2908
Swap:          5119           0        5119

根因定位

该ENOMEM报错与系统物理内存完全无关,触发原因是libsodium接口的入参类型不匹配:

  • encryptionKeypair函数通过sodium.crypto_box_keypair生成的是原生Curve25519格式的加密密钥,本身就可以直接用于crypto_box_easy加解密流程,不需要做任何格式转换
  • crypto_sign_ed25519_pk_to_curve25519、crypto_sign_ed25519_sk_to_curve25519两个接口的入参,仅支持接收sodium.crypto_sign_keypair生成的Ed25519签名密钥,不能接收已经是Curve25519格式的加密密钥
  • 当传入的密钥不符合接口预期格式时,libsodium的敏感内存校验机制不会抛出明确的参数格式错误,而是直接抛出ENOMEM类异常,导致排查方向偏离

修复方案

  • 调整oneWayBox函数的公钥处理逻辑:如果传入的公钥是encryptionKeypair生成的Curve25519格式公钥,直接跳过Ed25519转Curve25519的转换步骤,将公钥直接作为加密目标公钥传入加解密接口
  • 只有当传入的公钥是Ed25519格式的签名公钥时,才调用格式转换接口完成转换后再用于加解密
  • 开发调试阶段可在转换步骤前增加密钥长度、类型的校验日志,避免不同类型的密钥混用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.23 20:24:03