aldenml / ecc

elliptic-curve cryptography
MIT License
28 stars 1 forks source link

Proxy re encryption with 3rd party encrypting message #130

Closed perki closed 1 year ago

perki commented 1 year ago

Hello,

I'm trying to achieve the following scheme C encrypt a message with A public key and send it to the Proxy A creates a re-encryption key that the proxy can use to share data with B

I managed to achieve it only if the re-encryption is signed by C pre_schema1_ReKeyGen(keysA.sk, keysB.pk, signingC);

This is not very practical in the following situation A has a data repository, managed by Proxy anyone can contribute to this data repository by positing messages encrypted with A.pk A select who can read these data by creating reEncKey for each recipient.

In this situation it would mean that A would have to create n reEncKeys for each Contributor/Recipient pair.

Do I miss a point that would allow to have only one reEncKey per recipient ?

Here is a sample working code

let ecc;

async function main() {

  // client A setup public/private keys and signing keys
  const keysA = await ecc.pre_schema1_KeyGen();
  const signingA = await ecc.pre_schema1_SigningKeyGen();

  // client B setup public/private keys (signing keys are not used here)
  const keysB = await ecc.pre_schema1_KeyGen();

  // proxy server setup signing keys
  const signingProxy = await ecc.pre_schema1_SigningKeyGen();

  // client C is the origin encrypting party
  const signingC = await ecc.pre_schema1_SigningKeyGen();

  // client C select a plaintext message
  const message = await ecc.pre_schema1_MessageGen();
  const messageS = uintArrayToString(message);

  // client C encrypts the message with A key
  const ciphertextLevel1 = await ecc.pre_schema1_Encrypt(
    message,
    keysA.pk,
    signingC);

  // client A is able to decrypt ciphertextLevel1 
  const messageDecrypted1 = await ecc.pre_schema1_DecryptLevel1(
    ciphertextLevel1,
    keysA.sk,
    signingC.spk
  );

  console.log(' Message decrypted == message ', (messageS == uintArrayToString(messageDecrypted1)));

  // client A allows client B to see the encrypted

  // client A creates a re-encryption key that the proxy can use
  // to re-encrypt the ciphertext (ciphertextLevel1) in order for
  // client B be able to recover the original message
  const reEncKey = await ecc.pre_schema1_ReKeyGen(
    keysA.sk, 
    keysB.pk, 
    signingC // ⚠️ HERE I WOULD LIKE TO AVOID USING `C` signing Key
  );

  // the proxy re-encrypt the ciphertext ciphertextLevel1 with such
  // a key that allows client B to recover the original message
  const ciphertextLevel2 = await ecc.pre_schema1_ReEncrypt(
    ciphertextLevel1,
    reEncKey,
    signingC.spk, // ⚠️ HERE I WOULD LIKE TO AVOID USING `C` signing Key
    keysB.pk, 
    signingProxy
  );

  // client B is able to decrypt ciphertextLevel2 and the result
  // is the original plaintext message
  const messageDecrypted2 = await ecc.pre_schema1_DecryptLevel2(
    ciphertextLevel2,
    keysB.sk, signingProxy.spk
  );

  // now both client A and client B share the same plaintext message
  // messageDecrypted is equal to message

  console.log(' Message recrypted/decrypted == message ', (messageS == uintArrayToString(messageDecrypted2)));

}

function uintArrayToString(u8) {
  return Buffer.from(u8).toString('base64');
}

function stringToUintArray(b64) {
  return new Uint8Array(Buffer.from(b64, 'base64'))
}

(async () => {
  ecc = await import('@aldenml/ecc');
  await main();
})();
aldenml commented 1 year ago

Hi @perki, I'm glad you are giving this library a try. You are right, the primitives in this "schema1" are not adequate for your use case, it does not scale.

I think your problem can be solved with an additional clever trick on the keys of contributors and recipients, and I believe this is solved in the paper "Cryptographically Enforced Orthogonal Access Control at Scale" by Bob Wall and Patric Walsh.

...allows the decision about the groups to which to encrypt a piece of data to be made independently and asynchronously from the decision about who belongs to a group and can therefore decrypt the data.

That's the foundation of their product of group management and PRE.

Still, there is a cost when an entity is added or removed from the group since keys need to be regenerated iirc.

perki commented 1 year ago

Hello @aldenml,

Thank you for your answer. My first successful implementation is with Ironcore's library from Bob Wall and Patric Walsh. Now I start testing others PRE flavors such as your implementation.

For my use case, I don't need to "add" / "remove" entities from a group. But I need anyone to able to contribute.

A not fully end-end to encryption, but could be acceptable for my use case, is then to let the proxy encrypt plain text messages with A pk. Then A could generate a reEncryption key (implying that A knows the proxy's secret signing).

Thanks again and kind regards, Pierre-Mikael

aldenml commented 1 year ago

Feel free to re-open if you want to continue the thread