How to do 1-of-X or Y-of-X public key based encrypt/ decrypt in NodeJs?












3















I would like to be able to encrypt data using public keys, and decrypt the encrypted data using private keys.



Encryption essentially needs to accept inputs:




  1. Clear data to be encrypted

  2. A list of several public keys

  3. The minimum number of private keys corresponding to those public keys that are needed to decrypt the encrypted


How can this be done in NodeJs?





Scenarios



By way of concrete scenarios, where there are 5 users (A - E) with crypto key pairs in the system.



A 1-of-X scenario:





  • encrypted = crypto_encrypt(clearText, [A.publicKey, B.publicKey], 1) (1-of-2)


  • decrypted = crypto_decrypt(encrypted, [A.privateKey])



    • success: decrypted === clearText

    • because A.publicKey was used in encryption




  • decrypted = crypto_decrypt(encrypted, [C.privateKey])



    • failure: unable to decrypt

    • because C.publicKey was not used in encryption




A Y-of-X scenario:





  • encrypted = crypto_encrypt(clearText, [A.publicKey, B.publicKey, C.publicKey], 2) (2-of-3)


  • decrypted = crypto_decrypt(encrypted, [A.privateKey, C.privateKey])



    • success: decrypted === clearText

    • because both A.publicKey and C.publicKey was used in encryption




  • decrypted = crypto_decrypt(encrypted, [C.privateKey, E.privateKey])



    • failure: unable to decrypt

    • because while C.publicKey was used in encryption, E.publicKey was not






Ideally...




  • At minimum I need to be able to support the 1-of-X scenario, but if Y-of-X is also possible, that would be better

  • What the actual key pairs are is not so important here, could be RSA, could be any of the elliptic curves. If the method supports a number of different ones, and allows one to pick, that would be better

  • Preferably not tied to the use of any particular toolset or framework










share|improve this question




















  • 1





    Library recommendations are off-topic. Look at Shamirs Secret Sharing Algorithm. Not inherently asymmetric but you could make it so.

    – Luke Joshua Park
    Nov 13 '18 at 18:03











  • @LukeJoshuaPark Yup I have skimmed several papers, and it looks like this should be possible using many different types of schemes. However, there appears to be a gap between theoretically possible and concretely possible, which I am trying to fill.

    – bguiz
    Nov 13 '18 at 23:49











  • @LukeJoshuaPark speaking to your comment that "Library recommendations are off-topic", I'll amend the question to ask "how to do this" instead of "what libraries can be used to do this" - hopefully this falls within the purview of S/O. Otherwise LMK how else I should rephrase this question?

    – bguiz
    Nov 13 '18 at 23:50


















3















I would like to be able to encrypt data using public keys, and decrypt the encrypted data using private keys.



Encryption essentially needs to accept inputs:




  1. Clear data to be encrypted

  2. A list of several public keys

  3. The minimum number of private keys corresponding to those public keys that are needed to decrypt the encrypted


How can this be done in NodeJs?





Scenarios



By way of concrete scenarios, where there are 5 users (A - E) with crypto key pairs in the system.



A 1-of-X scenario:





  • encrypted = crypto_encrypt(clearText, [A.publicKey, B.publicKey], 1) (1-of-2)


  • decrypted = crypto_decrypt(encrypted, [A.privateKey])



    • success: decrypted === clearText

    • because A.publicKey was used in encryption




  • decrypted = crypto_decrypt(encrypted, [C.privateKey])



    • failure: unable to decrypt

    • because C.publicKey was not used in encryption




A Y-of-X scenario:





  • encrypted = crypto_encrypt(clearText, [A.publicKey, B.publicKey, C.publicKey], 2) (2-of-3)


  • decrypted = crypto_decrypt(encrypted, [A.privateKey, C.privateKey])



    • success: decrypted === clearText

    • because both A.publicKey and C.publicKey was used in encryption




  • decrypted = crypto_decrypt(encrypted, [C.privateKey, E.privateKey])



    • failure: unable to decrypt

    • because while C.publicKey was used in encryption, E.publicKey was not






Ideally...




  • At minimum I need to be able to support the 1-of-X scenario, but if Y-of-X is also possible, that would be better

  • What the actual key pairs are is not so important here, could be RSA, could be any of the elliptic curves. If the method supports a number of different ones, and allows one to pick, that would be better

  • Preferably not tied to the use of any particular toolset or framework










share|improve this question




















  • 1





    Library recommendations are off-topic. Look at Shamirs Secret Sharing Algorithm. Not inherently asymmetric but you could make it so.

    – Luke Joshua Park
    Nov 13 '18 at 18:03











  • @LukeJoshuaPark Yup I have skimmed several papers, and it looks like this should be possible using many different types of schemes. However, there appears to be a gap between theoretically possible and concretely possible, which I am trying to fill.

    – bguiz
    Nov 13 '18 at 23:49











  • @LukeJoshuaPark speaking to your comment that "Library recommendations are off-topic", I'll amend the question to ask "how to do this" instead of "what libraries can be used to do this" - hopefully this falls within the purview of S/O. Otherwise LMK how else I should rephrase this question?

    – bguiz
    Nov 13 '18 at 23:50
















3












3








3


2






I would like to be able to encrypt data using public keys, and decrypt the encrypted data using private keys.



Encryption essentially needs to accept inputs:




  1. Clear data to be encrypted

  2. A list of several public keys

  3. The minimum number of private keys corresponding to those public keys that are needed to decrypt the encrypted


How can this be done in NodeJs?





Scenarios



By way of concrete scenarios, where there are 5 users (A - E) with crypto key pairs in the system.



A 1-of-X scenario:





  • encrypted = crypto_encrypt(clearText, [A.publicKey, B.publicKey], 1) (1-of-2)


  • decrypted = crypto_decrypt(encrypted, [A.privateKey])



    • success: decrypted === clearText

    • because A.publicKey was used in encryption




  • decrypted = crypto_decrypt(encrypted, [C.privateKey])



    • failure: unable to decrypt

    • because C.publicKey was not used in encryption




A Y-of-X scenario:





  • encrypted = crypto_encrypt(clearText, [A.publicKey, B.publicKey, C.publicKey], 2) (2-of-3)


  • decrypted = crypto_decrypt(encrypted, [A.privateKey, C.privateKey])



    • success: decrypted === clearText

    • because both A.publicKey and C.publicKey was used in encryption




  • decrypted = crypto_decrypt(encrypted, [C.privateKey, E.privateKey])



    • failure: unable to decrypt

    • because while C.publicKey was used in encryption, E.publicKey was not






Ideally...




  • At minimum I need to be able to support the 1-of-X scenario, but if Y-of-X is also possible, that would be better

  • What the actual key pairs are is not so important here, could be RSA, could be any of the elliptic curves. If the method supports a number of different ones, and allows one to pick, that would be better

  • Preferably not tied to the use of any particular toolset or framework










share|improve this question
















I would like to be able to encrypt data using public keys, and decrypt the encrypted data using private keys.



Encryption essentially needs to accept inputs:




  1. Clear data to be encrypted

  2. A list of several public keys

  3. The minimum number of private keys corresponding to those public keys that are needed to decrypt the encrypted


How can this be done in NodeJs?





Scenarios



By way of concrete scenarios, where there are 5 users (A - E) with crypto key pairs in the system.



A 1-of-X scenario:





  • encrypted = crypto_encrypt(clearText, [A.publicKey, B.publicKey], 1) (1-of-2)


  • decrypted = crypto_decrypt(encrypted, [A.privateKey])



    • success: decrypted === clearText

    • because A.publicKey was used in encryption




  • decrypted = crypto_decrypt(encrypted, [C.privateKey])



    • failure: unable to decrypt

    • because C.publicKey was not used in encryption




A Y-of-X scenario:





  • encrypted = crypto_encrypt(clearText, [A.publicKey, B.publicKey, C.publicKey], 2) (2-of-3)


  • decrypted = crypto_decrypt(encrypted, [A.privateKey, C.privateKey])



    • success: decrypted === clearText

    • because both A.publicKey and C.publicKey was used in encryption




  • decrypted = crypto_decrypt(encrypted, [C.privateKey, E.privateKey])



    • failure: unable to decrypt

    • because while C.publicKey was used in encryption, E.publicKey was not






Ideally...




  • At minimum I need to be able to support the 1-of-X scenario, but if Y-of-X is also possible, that would be better

  • What the actual key pairs are is not so important here, could be RSA, could be any of the elliptic curves. If the method supports a number of different ones, and allows one to pick, that would be better

  • Preferably not tied to the use of any particular toolset or framework







node.js encryption cryptography public-key-encryption






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 13 '18 at 23:52







bguiz

















asked Nov 13 '18 at 15:55









bguizbguiz

12.6k29115193




12.6k29115193








  • 1





    Library recommendations are off-topic. Look at Shamirs Secret Sharing Algorithm. Not inherently asymmetric but you could make it so.

    – Luke Joshua Park
    Nov 13 '18 at 18:03











  • @LukeJoshuaPark Yup I have skimmed several papers, and it looks like this should be possible using many different types of schemes. However, there appears to be a gap between theoretically possible and concretely possible, which I am trying to fill.

    – bguiz
    Nov 13 '18 at 23:49











  • @LukeJoshuaPark speaking to your comment that "Library recommendations are off-topic", I'll amend the question to ask "how to do this" instead of "what libraries can be used to do this" - hopefully this falls within the purview of S/O. Otherwise LMK how else I should rephrase this question?

    – bguiz
    Nov 13 '18 at 23:50
















  • 1





    Library recommendations are off-topic. Look at Shamirs Secret Sharing Algorithm. Not inherently asymmetric but you could make it so.

    – Luke Joshua Park
    Nov 13 '18 at 18:03











  • @LukeJoshuaPark Yup I have skimmed several papers, and it looks like this should be possible using many different types of schemes. However, there appears to be a gap between theoretically possible and concretely possible, which I am trying to fill.

    – bguiz
    Nov 13 '18 at 23:49











  • @LukeJoshuaPark speaking to your comment that "Library recommendations are off-topic", I'll amend the question to ask "how to do this" instead of "what libraries can be used to do this" - hopefully this falls within the purview of S/O. Otherwise LMK how else I should rephrase this question?

    – bguiz
    Nov 13 '18 at 23:50










1




1





Library recommendations are off-topic. Look at Shamirs Secret Sharing Algorithm. Not inherently asymmetric but you could make it so.

– Luke Joshua Park
Nov 13 '18 at 18:03





Library recommendations are off-topic. Look at Shamirs Secret Sharing Algorithm. Not inherently asymmetric but you could make it so.

– Luke Joshua Park
Nov 13 '18 at 18:03













@LukeJoshuaPark Yup I have skimmed several papers, and it looks like this should be possible using many different types of schemes. However, there appears to be a gap between theoretically possible and concretely possible, which I am trying to fill.

– bguiz
Nov 13 '18 at 23:49





@LukeJoshuaPark Yup I have skimmed several papers, and it looks like this should be possible using many different types of schemes. However, there appears to be a gap between theoretically possible and concretely possible, which I am trying to fill.

– bguiz
Nov 13 '18 at 23:49













@LukeJoshuaPark speaking to your comment that "Library recommendations are off-topic", I'll amend the question to ask "how to do this" instead of "what libraries can be used to do this" - hopefully this falls within the purview of S/O. Otherwise LMK how else I should rephrase this question?

– bguiz
Nov 13 '18 at 23:50







@LukeJoshuaPark speaking to your comment that "Library recommendations are off-topic", I'll amend the question to ask "how to do this" instead of "what libraries can be used to do this" - hopefully this falls within the purview of S/O. Otherwise LMK how else I should rephrase this question?

– bguiz
Nov 13 '18 at 23:50














2 Answers
2






active

oldest

votes


















1














PGP can do this.



Specifically for node, openpgpjs has a section in the README - https://github.com/openpgpjs/openpgpjs#encrypt-and-decrypt-string-data-with-pgp-keys - which could be condensed into:



    const encryptedText = await openpgp.encrypt({ message: clearText, publicKeys });
const decryptedText = await openpgp.decrypt({ message: encryptedText, privateKeys });


However:




  • for number of keys required to decrypt, it only supports the 1 of many scenario, not the more general some of many scenario you'd ideally want

  • supports both RSA and many elliptic curve based keys, but the key format is designed for use by PGP, as the name of the library implies (so it is specific to the PGP toolchain)






share|improve this answer































    1














    As noted by Luke Joshua Park in the comments, this sounds like a textbook use case for a secret sharing scheme. Specifically, I would recommend that you:




    1. Generate a random AES (or other symmetric cipher) key. Make sure to use a cryptographically secure RNG (such as Crypto.randomBytes()) for this, since an attacker who can guess this key can also break the entire scheme!

    2. Encrypt the data with this key, using an authenticated encryption mode such as AES-SIV (as provided e.g. by miscreant).

    3. Split the AES key into multiple shares using Shamir's secret sharing scheme with the desired reconstruction threshold. (Some JS implementations I found with a quick Google search include secrets.js, jsss and ThresholdJS.)

    4. Encrypt each share using a different user's public key.

    5. Send each user their encrypted share and a copy of the AES-encrypted data.



    Disclaimer: I have not reviewed the security or correctness of any of the APIs or libraries linked above. The cryptographic techniques they claim to use appear to be sound and suitable for this task, but I cannot guarantee that they have been implemented safely and correctly. Caveat emptor.




    To decrypt the data, each user can first decrypt their share of the AES key using their private key, and a sufficient number of the decrypted shares can then be combined (using the same implementation of Shamir's secret sharing as used to create them) to reconstruct the original AES key, which can then be used to decrypt (and verify the integrity of) the data.



    Note that Shamir's secret sharing implicitly assumes that the users who combine their shares to reconstruct the secret will trust each other and not lie about their shares or otherwise misbehave. If that's not necessarily true, there are various ways for a malicious user to trick the others — perhaps most simply by waiting for everyone else to reveal their share to them and then refusing to reveal their own share to the others. In general, preventing such attacks is all but impossible without the help of some kind of a mutually trusted party.



    At the very least, though, using an encryption mode like AES-SIV with built-in authentication should ensure that users will detect if the reconstructed AES key is incorrect, since the decryption will then fail. If you want to be extra sure of this, you may wish to also send each of the users a secure cryptographic hash (e.g. SHA-512) of the AES key, so that they can verify its correctness before attempting decryption.






    share|improve this answer
























    • Thanks for such a comprehensive answer Ilmari! Unfortunately SSS does not look like it will work for my use case - for non-cryptographic reasons - because I cannot do this: 5. Send each user their encrypted share and a copy of the AES-encrypted data. The architecture does not support a means for the server to "push" shares to each client, rather the clients need to be able to retrieve the encrypted data, and be able to decrypt it using their own private key(s) - hence I've asked for a means to do this using public key encryption (and not secret key encryption).

      – bguiz
      Nov 15 '18 at 3:57













    • @bguiz: Does your server know the public keys of the clients in advance? If yes, you can just generate and encrypt the shares for each client, and store them on the server until the client connects and requests them. If not, you could still pre-generate the shares but store them unencrypted on the server until the client requests them; of course, this means that you'd be storing unencrypted shares on the server, which could be an issue if the server might get compromised.

      – Ilmari Karonen
      Nov 15 '18 at 13:31











    • It's also technically possible to dynamically create new shares on demand with SSS, but not all implementations may support it. Also, it requires storing the SSS polynomial coefficients, which include the secret itself (so, again, compromising the server could then compromise the data). And, of course, if the server doesn't have some way to reliably identify the clients (like, say, a public key for each of them), then you'd have to worry about the possibility of a malicious or compromised client tricking the server into giving it multiple shares.

      – Ilmari Karonen
      Nov 15 '18 at 13:35











    • Ah thanks for the suggestions Ilmari, but neither storing unencrypted shares on the server, nor sending private keys to the server are feasible options here. That's why I think a public key based encryption system is necessary here. Something similar to the PGP option presented in the other answer, but also supporting Y-of-X encryption would be great.

      – bguiz
      Nov 17 '18 at 2:42











    • @bguiz: I think there's some kind of a misunderstanding here. In the scheme I proposed above, there's no need for the server to know the clients' private keys. All it needs is the public keys, so that it can encrypt the shares.

      – Ilmari Karonen
      Nov 17 '18 at 10:54











    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53284796%2fhow-to-do-1-of-x-or-y-of-x-public-key-based-encrypt-decrypt-in-nodejs%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    1














    PGP can do this.



    Specifically for node, openpgpjs has a section in the README - https://github.com/openpgpjs/openpgpjs#encrypt-and-decrypt-string-data-with-pgp-keys - which could be condensed into:



        const encryptedText = await openpgp.encrypt({ message: clearText, publicKeys });
    const decryptedText = await openpgp.decrypt({ message: encryptedText, privateKeys });


    However:




    • for number of keys required to decrypt, it only supports the 1 of many scenario, not the more general some of many scenario you'd ideally want

    • supports both RSA and many elliptic curve based keys, but the key format is designed for use by PGP, as the name of the library implies (so it is specific to the PGP toolchain)






    share|improve this answer




























      1














      PGP can do this.



      Specifically for node, openpgpjs has a section in the README - https://github.com/openpgpjs/openpgpjs#encrypt-and-decrypt-string-data-with-pgp-keys - which could be condensed into:



          const encryptedText = await openpgp.encrypt({ message: clearText, publicKeys });
      const decryptedText = await openpgp.decrypt({ message: encryptedText, privateKeys });


      However:




      • for number of keys required to decrypt, it only supports the 1 of many scenario, not the more general some of many scenario you'd ideally want

      • supports both RSA and many elliptic curve based keys, but the key format is designed for use by PGP, as the name of the library implies (so it is specific to the PGP toolchain)






      share|improve this answer


























        1












        1








        1







        PGP can do this.



        Specifically for node, openpgpjs has a section in the README - https://github.com/openpgpjs/openpgpjs#encrypt-and-decrypt-string-data-with-pgp-keys - which could be condensed into:



            const encryptedText = await openpgp.encrypt({ message: clearText, publicKeys });
        const decryptedText = await openpgp.decrypt({ message: encryptedText, privateKeys });


        However:




        • for number of keys required to decrypt, it only supports the 1 of many scenario, not the more general some of many scenario you'd ideally want

        • supports both RSA and many elliptic curve based keys, but the key format is designed for use by PGP, as the name of the library implies (so it is specific to the PGP toolchain)






        share|improve this answer













        PGP can do this.



        Specifically for node, openpgpjs has a section in the README - https://github.com/openpgpjs/openpgpjs#encrypt-and-decrypt-string-data-with-pgp-keys - which could be condensed into:



            const encryptedText = await openpgp.encrypt({ message: clearText, publicKeys });
        const decryptedText = await openpgp.decrypt({ message: encryptedText, privateKeys });


        However:




        • for number of keys required to decrypt, it only supports the 1 of many scenario, not the more general some of many scenario you'd ideally want

        • supports both RSA and many elliptic curve based keys, but the key format is designed for use by PGP, as the name of the library implies (so it is specific to the PGP toolchain)







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 14 '18 at 1:51









        bojanglebojangle

        353213




        353213

























            1














            As noted by Luke Joshua Park in the comments, this sounds like a textbook use case for a secret sharing scheme. Specifically, I would recommend that you:




            1. Generate a random AES (or other symmetric cipher) key. Make sure to use a cryptographically secure RNG (such as Crypto.randomBytes()) for this, since an attacker who can guess this key can also break the entire scheme!

            2. Encrypt the data with this key, using an authenticated encryption mode such as AES-SIV (as provided e.g. by miscreant).

            3. Split the AES key into multiple shares using Shamir's secret sharing scheme with the desired reconstruction threshold. (Some JS implementations I found with a quick Google search include secrets.js, jsss and ThresholdJS.)

            4. Encrypt each share using a different user's public key.

            5. Send each user their encrypted share and a copy of the AES-encrypted data.



            Disclaimer: I have not reviewed the security or correctness of any of the APIs or libraries linked above. The cryptographic techniques they claim to use appear to be sound and suitable for this task, but I cannot guarantee that they have been implemented safely and correctly. Caveat emptor.




            To decrypt the data, each user can first decrypt their share of the AES key using their private key, and a sufficient number of the decrypted shares can then be combined (using the same implementation of Shamir's secret sharing as used to create them) to reconstruct the original AES key, which can then be used to decrypt (and verify the integrity of) the data.



            Note that Shamir's secret sharing implicitly assumes that the users who combine their shares to reconstruct the secret will trust each other and not lie about their shares or otherwise misbehave. If that's not necessarily true, there are various ways for a malicious user to trick the others — perhaps most simply by waiting for everyone else to reveal their share to them and then refusing to reveal their own share to the others. In general, preventing such attacks is all but impossible without the help of some kind of a mutually trusted party.



            At the very least, though, using an encryption mode like AES-SIV with built-in authentication should ensure that users will detect if the reconstructed AES key is incorrect, since the decryption will then fail. If you want to be extra sure of this, you may wish to also send each of the users a secure cryptographic hash (e.g. SHA-512) of the AES key, so that they can verify its correctness before attempting decryption.






            share|improve this answer
























            • Thanks for such a comprehensive answer Ilmari! Unfortunately SSS does not look like it will work for my use case - for non-cryptographic reasons - because I cannot do this: 5. Send each user their encrypted share and a copy of the AES-encrypted data. The architecture does not support a means for the server to "push" shares to each client, rather the clients need to be able to retrieve the encrypted data, and be able to decrypt it using their own private key(s) - hence I've asked for a means to do this using public key encryption (and not secret key encryption).

              – bguiz
              Nov 15 '18 at 3:57













            • @bguiz: Does your server know the public keys of the clients in advance? If yes, you can just generate and encrypt the shares for each client, and store them on the server until the client connects and requests them. If not, you could still pre-generate the shares but store them unencrypted on the server until the client requests them; of course, this means that you'd be storing unencrypted shares on the server, which could be an issue if the server might get compromised.

              – Ilmari Karonen
              Nov 15 '18 at 13:31











            • It's also technically possible to dynamically create new shares on demand with SSS, but not all implementations may support it. Also, it requires storing the SSS polynomial coefficients, which include the secret itself (so, again, compromising the server could then compromise the data). And, of course, if the server doesn't have some way to reliably identify the clients (like, say, a public key for each of them), then you'd have to worry about the possibility of a malicious or compromised client tricking the server into giving it multiple shares.

              – Ilmari Karonen
              Nov 15 '18 at 13:35











            • Ah thanks for the suggestions Ilmari, but neither storing unencrypted shares on the server, nor sending private keys to the server are feasible options here. That's why I think a public key based encryption system is necessary here. Something similar to the PGP option presented in the other answer, but also supporting Y-of-X encryption would be great.

              – bguiz
              Nov 17 '18 at 2:42











            • @bguiz: I think there's some kind of a misunderstanding here. In the scheme I proposed above, there's no need for the server to know the clients' private keys. All it needs is the public keys, so that it can encrypt the shares.

              – Ilmari Karonen
              Nov 17 '18 at 10:54
















            1














            As noted by Luke Joshua Park in the comments, this sounds like a textbook use case for a secret sharing scheme. Specifically, I would recommend that you:




            1. Generate a random AES (or other symmetric cipher) key. Make sure to use a cryptographically secure RNG (such as Crypto.randomBytes()) for this, since an attacker who can guess this key can also break the entire scheme!

            2. Encrypt the data with this key, using an authenticated encryption mode such as AES-SIV (as provided e.g. by miscreant).

            3. Split the AES key into multiple shares using Shamir's secret sharing scheme with the desired reconstruction threshold. (Some JS implementations I found with a quick Google search include secrets.js, jsss and ThresholdJS.)

            4. Encrypt each share using a different user's public key.

            5. Send each user their encrypted share and a copy of the AES-encrypted data.



            Disclaimer: I have not reviewed the security or correctness of any of the APIs or libraries linked above. The cryptographic techniques they claim to use appear to be sound and suitable for this task, but I cannot guarantee that they have been implemented safely and correctly. Caveat emptor.




            To decrypt the data, each user can first decrypt their share of the AES key using their private key, and a sufficient number of the decrypted shares can then be combined (using the same implementation of Shamir's secret sharing as used to create them) to reconstruct the original AES key, which can then be used to decrypt (and verify the integrity of) the data.



            Note that Shamir's secret sharing implicitly assumes that the users who combine their shares to reconstruct the secret will trust each other and not lie about their shares or otherwise misbehave. If that's not necessarily true, there are various ways for a malicious user to trick the others — perhaps most simply by waiting for everyone else to reveal their share to them and then refusing to reveal their own share to the others. In general, preventing such attacks is all but impossible without the help of some kind of a mutually trusted party.



            At the very least, though, using an encryption mode like AES-SIV with built-in authentication should ensure that users will detect if the reconstructed AES key is incorrect, since the decryption will then fail. If you want to be extra sure of this, you may wish to also send each of the users a secure cryptographic hash (e.g. SHA-512) of the AES key, so that they can verify its correctness before attempting decryption.






            share|improve this answer
























            • Thanks for such a comprehensive answer Ilmari! Unfortunately SSS does not look like it will work for my use case - for non-cryptographic reasons - because I cannot do this: 5. Send each user their encrypted share and a copy of the AES-encrypted data. The architecture does not support a means for the server to "push" shares to each client, rather the clients need to be able to retrieve the encrypted data, and be able to decrypt it using their own private key(s) - hence I've asked for a means to do this using public key encryption (and not secret key encryption).

              – bguiz
              Nov 15 '18 at 3:57













            • @bguiz: Does your server know the public keys of the clients in advance? If yes, you can just generate and encrypt the shares for each client, and store them on the server until the client connects and requests them. If not, you could still pre-generate the shares but store them unencrypted on the server until the client requests them; of course, this means that you'd be storing unencrypted shares on the server, which could be an issue if the server might get compromised.

              – Ilmari Karonen
              Nov 15 '18 at 13:31











            • It's also technically possible to dynamically create new shares on demand with SSS, but not all implementations may support it. Also, it requires storing the SSS polynomial coefficients, which include the secret itself (so, again, compromising the server could then compromise the data). And, of course, if the server doesn't have some way to reliably identify the clients (like, say, a public key for each of them), then you'd have to worry about the possibility of a malicious or compromised client tricking the server into giving it multiple shares.

              – Ilmari Karonen
              Nov 15 '18 at 13:35











            • Ah thanks for the suggestions Ilmari, but neither storing unencrypted shares on the server, nor sending private keys to the server are feasible options here. That's why I think a public key based encryption system is necessary here. Something similar to the PGP option presented in the other answer, but also supporting Y-of-X encryption would be great.

              – bguiz
              Nov 17 '18 at 2:42











            • @bguiz: I think there's some kind of a misunderstanding here. In the scheme I proposed above, there's no need for the server to know the clients' private keys. All it needs is the public keys, so that it can encrypt the shares.

              – Ilmari Karonen
              Nov 17 '18 at 10:54














            1












            1








            1







            As noted by Luke Joshua Park in the comments, this sounds like a textbook use case for a secret sharing scheme. Specifically, I would recommend that you:




            1. Generate a random AES (or other symmetric cipher) key. Make sure to use a cryptographically secure RNG (such as Crypto.randomBytes()) for this, since an attacker who can guess this key can also break the entire scheme!

            2. Encrypt the data with this key, using an authenticated encryption mode such as AES-SIV (as provided e.g. by miscreant).

            3. Split the AES key into multiple shares using Shamir's secret sharing scheme with the desired reconstruction threshold. (Some JS implementations I found with a quick Google search include secrets.js, jsss and ThresholdJS.)

            4. Encrypt each share using a different user's public key.

            5. Send each user their encrypted share and a copy of the AES-encrypted data.



            Disclaimer: I have not reviewed the security or correctness of any of the APIs or libraries linked above. The cryptographic techniques they claim to use appear to be sound and suitable for this task, but I cannot guarantee that they have been implemented safely and correctly. Caveat emptor.




            To decrypt the data, each user can first decrypt their share of the AES key using their private key, and a sufficient number of the decrypted shares can then be combined (using the same implementation of Shamir's secret sharing as used to create them) to reconstruct the original AES key, which can then be used to decrypt (and verify the integrity of) the data.



            Note that Shamir's secret sharing implicitly assumes that the users who combine their shares to reconstruct the secret will trust each other and not lie about their shares or otherwise misbehave. If that's not necessarily true, there are various ways for a malicious user to trick the others — perhaps most simply by waiting for everyone else to reveal their share to them and then refusing to reveal their own share to the others. In general, preventing such attacks is all but impossible without the help of some kind of a mutually trusted party.



            At the very least, though, using an encryption mode like AES-SIV with built-in authentication should ensure that users will detect if the reconstructed AES key is incorrect, since the decryption will then fail. If you want to be extra sure of this, you may wish to also send each of the users a secure cryptographic hash (e.g. SHA-512) of the AES key, so that they can verify its correctness before attempting decryption.






            share|improve this answer













            As noted by Luke Joshua Park in the comments, this sounds like a textbook use case for a secret sharing scheme. Specifically, I would recommend that you:




            1. Generate a random AES (or other symmetric cipher) key. Make sure to use a cryptographically secure RNG (such as Crypto.randomBytes()) for this, since an attacker who can guess this key can also break the entire scheme!

            2. Encrypt the data with this key, using an authenticated encryption mode such as AES-SIV (as provided e.g. by miscreant).

            3. Split the AES key into multiple shares using Shamir's secret sharing scheme with the desired reconstruction threshold. (Some JS implementations I found with a quick Google search include secrets.js, jsss and ThresholdJS.)

            4. Encrypt each share using a different user's public key.

            5. Send each user their encrypted share and a copy of the AES-encrypted data.



            Disclaimer: I have not reviewed the security or correctness of any of the APIs or libraries linked above. The cryptographic techniques they claim to use appear to be sound and suitable for this task, but I cannot guarantee that they have been implemented safely and correctly. Caveat emptor.




            To decrypt the data, each user can first decrypt their share of the AES key using their private key, and a sufficient number of the decrypted shares can then be combined (using the same implementation of Shamir's secret sharing as used to create them) to reconstruct the original AES key, which can then be used to decrypt (and verify the integrity of) the data.



            Note that Shamir's secret sharing implicitly assumes that the users who combine their shares to reconstruct the secret will trust each other and not lie about their shares or otherwise misbehave. If that's not necessarily true, there are various ways for a malicious user to trick the others — perhaps most simply by waiting for everyone else to reveal their share to them and then refusing to reveal their own share to the others. In general, preventing such attacks is all but impossible without the help of some kind of a mutually trusted party.



            At the very least, though, using an encryption mode like AES-SIV with built-in authentication should ensure that users will detect if the reconstructed AES key is incorrect, since the decryption will then fail. If you want to be extra sure of this, you may wish to also send each of the users a secure cryptographic hash (e.g. SHA-512) of the AES key, so that they can verify its correctness before attempting decryption.







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Nov 14 '18 at 17:41









            Ilmari KaronenIlmari Karonen

            37.2k566127




            37.2k566127













            • Thanks for such a comprehensive answer Ilmari! Unfortunately SSS does not look like it will work for my use case - for non-cryptographic reasons - because I cannot do this: 5. Send each user their encrypted share and a copy of the AES-encrypted data. The architecture does not support a means for the server to "push" shares to each client, rather the clients need to be able to retrieve the encrypted data, and be able to decrypt it using their own private key(s) - hence I've asked for a means to do this using public key encryption (and not secret key encryption).

              – bguiz
              Nov 15 '18 at 3:57













            • @bguiz: Does your server know the public keys of the clients in advance? If yes, you can just generate and encrypt the shares for each client, and store them on the server until the client connects and requests them. If not, you could still pre-generate the shares but store them unencrypted on the server until the client requests them; of course, this means that you'd be storing unencrypted shares on the server, which could be an issue if the server might get compromised.

              – Ilmari Karonen
              Nov 15 '18 at 13:31











            • It's also technically possible to dynamically create new shares on demand with SSS, but not all implementations may support it. Also, it requires storing the SSS polynomial coefficients, which include the secret itself (so, again, compromising the server could then compromise the data). And, of course, if the server doesn't have some way to reliably identify the clients (like, say, a public key for each of them), then you'd have to worry about the possibility of a malicious or compromised client tricking the server into giving it multiple shares.

              – Ilmari Karonen
              Nov 15 '18 at 13:35











            • Ah thanks for the suggestions Ilmari, but neither storing unencrypted shares on the server, nor sending private keys to the server are feasible options here. That's why I think a public key based encryption system is necessary here. Something similar to the PGP option presented in the other answer, but also supporting Y-of-X encryption would be great.

              – bguiz
              Nov 17 '18 at 2:42











            • @bguiz: I think there's some kind of a misunderstanding here. In the scheme I proposed above, there's no need for the server to know the clients' private keys. All it needs is the public keys, so that it can encrypt the shares.

              – Ilmari Karonen
              Nov 17 '18 at 10:54



















            • Thanks for such a comprehensive answer Ilmari! Unfortunately SSS does not look like it will work for my use case - for non-cryptographic reasons - because I cannot do this: 5. Send each user their encrypted share and a copy of the AES-encrypted data. The architecture does not support a means for the server to "push" shares to each client, rather the clients need to be able to retrieve the encrypted data, and be able to decrypt it using their own private key(s) - hence I've asked for a means to do this using public key encryption (and not secret key encryption).

              – bguiz
              Nov 15 '18 at 3:57













            • @bguiz: Does your server know the public keys of the clients in advance? If yes, you can just generate and encrypt the shares for each client, and store them on the server until the client connects and requests them. If not, you could still pre-generate the shares but store them unencrypted on the server until the client requests them; of course, this means that you'd be storing unencrypted shares on the server, which could be an issue if the server might get compromised.

              – Ilmari Karonen
              Nov 15 '18 at 13:31











            • It's also technically possible to dynamically create new shares on demand with SSS, but not all implementations may support it. Also, it requires storing the SSS polynomial coefficients, which include the secret itself (so, again, compromising the server could then compromise the data). And, of course, if the server doesn't have some way to reliably identify the clients (like, say, a public key for each of them), then you'd have to worry about the possibility of a malicious or compromised client tricking the server into giving it multiple shares.

              – Ilmari Karonen
              Nov 15 '18 at 13:35











            • Ah thanks for the suggestions Ilmari, but neither storing unencrypted shares on the server, nor sending private keys to the server are feasible options here. That's why I think a public key based encryption system is necessary here. Something similar to the PGP option presented in the other answer, but also supporting Y-of-X encryption would be great.

              – bguiz
              Nov 17 '18 at 2:42











            • @bguiz: I think there's some kind of a misunderstanding here. In the scheme I proposed above, there's no need for the server to know the clients' private keys. All it needs is the public keys, so that it can encrypt the shares.

              – Ilmari Karonen
              Nov 17 '18 at 10:54

















            Thanks for such a comprehensive answer Ilmari! Unfortunately SSS does not look like it will work for my use case - for non-cryptographic reasons - because I cannot do this: 5. Send each user their encrypted share and a copy of the AES-encrypted data. The architecture does not support a means for the server to "push" shares to each client, rather the clients need to be able to retrieve the encrypted data, and be able to decrypt it using their own private key(s) - hence I've asked for a means to do this using public key encryption (and not secret key encryption).

            – bguiz
            Nov 15 '18 at 3:57







            Thanks for such a comprehensive answer Ilmari! Unfortunately SSS does not look like it will work for my use case - for non-cryptographic reasons - because I cannot do this: 5. Send each user their encrypted share and a copy of the AES-encrypted data. The architecture does not support a means for the server to "push" shares to each client, rather the clients need to be able to retrieve the encrypted data, and be able to decrypt it using their own private key(s) - hence I've asked for a means to do this using public key encryption (and not secret key encryption).

            – bguiz
            Nov 15 '18 at 3:57















            @bguiz: Does your server know the public keys of the clients in advance? If yes, you can just generate and encrypt the shares for each client, and store them on the server until the client connects and requests them. If not, you could still pre-generate the shares but store them unencrypted on the server until the client requests them; of course, this means that you'd be storing unencrypted shares on the server, which could be an issue if the server might get compromised.

            – Ilmari Karonen
            Nov 15 '18 at 13:31





            @bguiz: Does your server know the public keys of the clients in advance? If yes, you can just generate and encrypt the shares for each client, and store them on the server until the client connects and requests them. If not, you could still pre-generate the shares but store them unencrypted on the server until the client requests them; of course, this means that you'd be storing unencrypted shares on the server, which could be an issue if the server might get compromised.

            – Ilmari Karonen
            Nov 15 '18 at 13:31













            It's also technically possible to dynamically create new shares on demand with SSS, but not all implementations may support it. Also, it requires storing the SSS polynomial coefficients, which include the secret itself (so, again, compromising the server could then compromise the data). And, of course, if the server doesn't have some way to reliably identify the clients (like, say, a public key for each of them), then you'd have to worry about the possibility of a malicious or compromised client tricking the server into giving it multiple shares.

            – Ilmari Karonen
            Nov 15 '18 at 13:35





            It's also technically possible to dynamically create new shares on demand with SSS, but not all implementations may support it. Also, it requires storing the SSS polynomial coefficients, which include the secret itself (so, again, compromising the server could then compromise the data). And, of course, if the server doesn't have some way to reliably identify the clients (like, say, a public key for each of them), then you'd have to worry about the possibility of a malicious or compromised client tricking the server into giving it multiple shares.

            – Ilmari Karonen
            Nov 15 '18 at 13:35













            Ah thanks for the suggestions Ilmari, but neither storing unencrypted shares on the server, nor sending private keys to the server are feasible options here. That's why I think a public key based encryption system is necessary here. Something similar to the PGP option presented in the other answer, but also supporting Y-of-X encryption would be great.

            – bguiz
            Nov 17 '18 at 2:42





            Ah thanks for the suggestions Ilmari, but neither storing unencrypted shares on the server, nor sending private keys to the server are feasible options here. That's why I think a public key based encryption system is necessary here. Something similar to the PGP option presented in the other answer, but also supporting Y-of-X encryption would be great.

            – bguiz
            Nov 17 '18 at 2:42













            @bguiz: I think there's some kind of a misunderstanding here. In the scheme I proposed above, there's no need for the server to know the clients' private keys. All it needs is the public keys, so that it can encrypt the shares.

            – Ilmari Karonen
            Nov 17 '18 at 10:54





            @bguiz: I think there's some kind of a misunderstanding here. In the scheme I proposed above, there's no need for the server to know the clients' private keys. All it needs is the public keys, so that it can encrypt the shares.

            – Ilmari Karonen
            Nov 17 '18 at 10:54


















            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53284796%2fhow-to-do-1-of-x-or-y-of-x-public-key-based-encrypt-decrypt-in-nodejs%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown