Can I encrypt a file with multiple public address such that each of them can decrypt the file using their private keys? Thus achieving a system to securely store file on IPFS which can be accessed by only a selected number of personals. How exactly can I achieve this? How can I achieve this in Solidity and JS.
2 Answers
You should create a symmetric key for encryption and encrypt IPFS file with it, and then encrypt this symmetric key with different public keys so that each recipient can decrypt the symmetric key and in turn decrypt IPFS file.
This script lets you generate a PGP KeyPair from an Ethereum Private Key using npm install openpgp:
const openpgp = require('openpgp'); const ethereumPrivateKey = 'e9ee206196eb26b92b937b83b7a44974b1c3d995b9e61ca65d2c5760a97250af'; const buffKey = Buffer.from(ethereumPrivateKey); const privateKey = openpgp.util.Uint8Array_to_str(buffKey); const options = { curve: 'secp256k1', userIds: {name: 'Tiago', email: '[email protected]'}, numBits: 2048, material: { key: privateKey, } }; openpgp.generateKey(options).then(function(key) { console.log(key.privateKeyArmored); }).catch(function(error) { console.log(error); }); We are going to assume you generated two different keys key1 and key2 using that method and now you would like to encrypt some data with key1.publicKeyArmored and key2.publicKeyArmored (Stack syntax highlighting is pretty bad right now, but it works, I promise) :
const openpgp = require('openpgp'); openpgp.initWorker({ path: 'openpgp.worker.js' }); const keys = [`-----BEGIN PGP PUBLIC KEY BLOCK----- Version: OpenPGP.js v4.4.10 Comment: https://openpgpjs.org xk8EXJ8SUxMFK4EEAAoCAwQRvMNE18QPFsBLIw0yWHfNZUVtOyPVXplzqhPy DslTKxjj3PE2s15vbVDNNOpmmzgIlRUEbyYFcDfhdCCXXscxzRtUaWFnbyA8 Z2l0aHViQHRsb3JpYXRvLmNvbT7CdwQQEwgAHwUCXJ8SUwYLCQcIAwIEFQgK AgMWAgECGQECGwMCHgEACgkQnCIDAkHMdM8czwEA2JOPG6zaNsa1D9M2a3U8 gl5R8S1F1lOELJtlVSH1tWAA/RRPREctK8pxkCKs5NuICnx9FIQ1WUsyulG+ 08xZY5CZzlMEXJ8SUxIFK4EEAAoCAwQ9aBjd/ntB+uqOUZgpzaNdtE6O2cQd l3DnZ3k2tEngIWKve+5d7woMd6idEdk2AkAvyhmqYMeiaCkI3LuNzjr3AwEI B8JhBBgTCAAJBQJcnxJTAhsMAAoJEJwiAwJBzHTP1IABAMNLXoEyrW7+5/ty bG8u5uIGP/0M+EDnELpDpUWMBa8dAP9MtynJ0qHbov6so3+9jA5Nug0s7Ywq 8NDcfsq8u367Tg== =67V7 -----END PGP PUBLIC KEY BLOCK-----`, `-----BEGIN PGP PUBLIC KEY BLOCK----- Version: OpenPGP.js v4.4.10 Comment: https://openpgpjs.org xk8EXJ8SKRMFK4EEAAoCAwTlZPr0xaQXWs3+2Q2at+OgE1VevOeyq0TXOMw2 BhFUTOF49sY3mJ9tA0jtHYeRG/BwXJlO7ycC6pc8a04LHeYWzRtUaWFnbyA8 Z2l0aHViQHRsb3JpYXRvLmNvbT7CdwQQEwgAHwUCXJ8SKQYLCQcIAwIEFQgK AgMWAgECGQECGwMCHgEACgkQ0ZFQ34vxIBgpCgD9Hbz/W+mQDLFAUulQ0pgO UBKuNdPraXpU419kIXZzl3QA/RTaX8cbQTHiuDy63tiwRZqZSiiuBiYZaZ0G S9p+k2UWzlMEXJ8SKRIFK4EEAAoCAwQu4w+JVtCeXxuaZIWg+r6FvjURLMqN gK9czwBYZlsWHZgXiiAI7aVgDtoPVcm1wYByMV7Ut6eUvzaDOdTwS/KrAwEI B8JhBBgTCAAJBQJcnxIpAhsMAAoJENGRUN+L8SAYsoYA/ilPvNJmlbpC187c LJXqKxdppUlb6DISS8gq4Moin/BdAQClCGwqtyovA7Et7MjkCxiW0P5xiaTn qENz9YJFSRBEAQ== =bUeW -----END PGP PUBLIC KEY BLOCK-----` ]; openpgp.key.readArmored(keys[0]) .then((firstKey) => { openpgp.key.readArmored(keys[1]) .then((secondKey) => { const options = { message: openpgp.message.fromText(`I just discovered the keys to Satoshi's Wallet! Meet @ Liberty Sq., 20:30pm`), // You can also call fromFile()! publicKeys: [firstKey.keys[0], secondKey.keys[0]] }; openpgp.encrypt(options).then(ciphertext => { encrypted = ciphertext.data; console.log(encrypted); }); }); }); Now I assume that Ethereum comes into play? You want like to verify the PGP Public Keys with Ethereum Holders in Solidity. You can do it from two "keys", you can ask them to sign:
- The PGP Fingerprint (way smaller, but you have to get the full key somewhere)
- The PGP Public Key (bigger, so more expansive)
First you need your participants to sign the one of the above keys:
const ethers = require('ethers'); // The message... const message = PGP_PUBLIC_KEY||PG_FINGERPRINT; // Sign the message (this could also come from eth_signMessage) const wallet = new ethers.Wallet(privateKey); const signature = wallet.signMessage(message) // Split the signature into its r, s and v (Solidity's format) const sig = ethers.utils.splitSignature(signature); // Call the contract with the message and signature const promise = contract.verifyMessage(message, sig.v, sig.r, sig.s); promise.then(function(signer) { // Check the computed signer matches the actual signer console.log(signer === wallet.address); }); Now you just have to create your Solidity Contract to verify a certain message:
contract Verifier { // Returns the address that signed a given string message function verifyString(string message, uint8 v, bytes32 r, bytes32 s) public pure returns (address signer) { // The message header; we will fill in the length next string memory header = "\x19Ethereum Signed Message:\n000000"; uint256 lengthOffset; uint256 length; assembly { // The first word of a string is its length length := mload(message) // The beginning of the base-10 message length in the prefix lengthOffset := add(header, 57) } // Maximum length we support require(length <= 999999); // The length of the message's length in base-10 uint256 lengthLength = 0; // The divisor to get the next left-most message length digit uint256 divisor = 100000; // Move one digit of the message length to the right at a time while (divisor != 0) { // The place value at the divisor uint256 digit = length / divisor; if (digit == 0) { // Skip leading zeros if (lengthLength == 0) { divisor /= 10; continue; } } // Found a non-zero digit or non-leading zero digit lengthLength++; // Remove this digit from the message length's current value length -= digit * divisor; // Shift our base-10 divisor over divisor /= 10; // Convert the digit to its ASCII representation (man ascii) digit += 0x30; // Move to the next character and write the digit lengthOffset++; assembly { mstore8(lengthOffset, digit) } } // The null string requires exactly 1 zero (unskip 1 leading 0) if (lengthLength == 0) { lengthLength = 1 + 0x19 + 1; } else { lengthLength += 1 + 0x19; } // Truncate the tailing zeros from the header assembly { mstore(header, lengthLength) } // Perform the elliptic curve recover operation bytes32 check = keccak256(header, message); return ecrecover(check, v, r, s); } } Solidity's part with the help of RicMoo
-
numBits: 2048could (and should) have been 4096Tiago Loriato Simões– Tiago Loriato Simões2019-03-30 07:51:34 +00:00Commented Mar 30, 2019 at 7:51 - This doesn't answer the question, OP wants to use public keys to encryptVinnie James– Vinnie James2019-04-04 11:22:08 +00:00Commented Apr 4, 2019 at 11:22
- The only way it seems possible to me is doing so through PGP Public Keys. If they derive from the same private keys than an ethereum address, it makes them a public key to me.Tiago Loriato Simões– Tiago Loriato Simões2019-04-04 11:37:47 +00:00Commented Apr 4, 2019 at 11:37