使用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
相关产品推荐
相关产品推荐

