I have been working with the libsodium library to implement Shamir secret sharing and trying to test the implementation done by dark crystal
https://gitlab.com/dark-crystal-javascript/key-backup-crypto/-/blob/master/example.js
Implementation is something like this
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.toString('hex'));
console.log('curvePublicKey', curvePublicKey.length);
console.log('publicKey', publicKey.length);
sodium.crypto_sign_ed25519_pk_to_curve25519(curvePublicKey, publicKey)
// console.log('curvePublicKey', curvePublicKey.toString('hex'));
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])
},
below is 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())
})
})
Now the problem is when I am using encryptionKeypair function to generate key pair for and then after that when I am trying to generate to do crypto_sign_ed25519_sk_to_curve25519 using the key pair generated in this encryptionKeypair function I am getting
UnhandledPromiseRejectionWarning: Error: ENOMEM, Cannot allocate memory
I have checked my swap space it is completely free
total used free shared buff/cache available
Mem: 3138 83 2896 0 158 2908
Swap: 5119 0 5119
I am not able to understand what is the issue.
It's not clear to me why you want to convert a key pair created with
encryptionKeypair()withcrypto_sign_ed25519_sk_to_curve25519()orcrypto_sign_ed25519_pk_to_curve25519().The latter two methods convert a secret or public Ed25519 key (used in the context of signing) to a secret or public X25519 key (used in the context of key exchange).
encryptionKeypair()appliescrypto_box_keypair()and thus already creates an X25519 key pair, so conversion is not necessary (and not possible).A working use of the conversion methods would be, e.g. using
crypto_sign_keypair(), which generates an Ed25519 key pair:Also, I can't reproduce the posted error message. When using
encryptionKeypair()instead ofsigningKeypair(), I get the following error message Error: public key conversion failed.Edit:
In the 2nd code snippet, the
custodians' key pairs are created withs.encryptionKeypair(), which produces X25519 key pairs. In the later calleds.oneWayBox()it is then tried to convert the public keys withcrypto_sign_ed25519_pk_to_curve25519(), which must fail as described above.Presumably this is a bug! A possible fix is to generate the
custodians' key pairs withs.keypair()(orsigningKeypair()), which creates Ed25519 key pairs. The public keys can then be successfully converted to X25519 keys ins.oneWayBox(). With this change,oneWayBox()runs without any errors on my machine.This change to Ed25519 key pairs is also consistent with the description of
encryptionKeypair(), which states that this method is only used when generating ephemeral keys, e.g. inoneWayBox(). In all other cases it is internally derived from Ed25519 keys.