4

I want to store some data in smart contract and then decrypt it on chain. Pseudocode:

contract EncryptedData {
   bytes data;
   string public decryptedData;

   constructor(bytes data_) public {
      data = data_;
   }

   function decrypt(bytes key) public {
      decryptedData = decryptDataSomehow(data, key);
   }
}

I know that when contract gets called the entire network gets the key and I'm fine with it.

The question is only "how can I decrypt data on chain"? I googled a lot but I didn't find any cryptographic primitives available. Only some EIPs like EIP-213 and so on, but nothing specific.

I'm not cryptographic PhD to implement cryptography myself (and it probably would burn bazillions of gas) so I need some builtins tools or at least already tested contracts.

What are my options here? I can't do it offchain because the entire idea of the whole thing is trust that only smart contract could have. I can't call oracles or something, the entire thing should be completely on-chain.


Edit: I actually have a little bit different situation. My contract is more like

contract EncryptedData {
   bytes[] data;
   string public decryptedData[];

   function addData(bytes data_) { 
       data.push(data_);
   }

   function decrypt(bytes key) public {
       for (uint i = 0; i < data.length; i++) {  
           decryptedData.push(decryptDataSomehow(data[i], key));
       }
   }
}
2
  • Why it has to be encrypted? See hashed timelocks github.com/ethereum/EIPs/pull/1630, for an idea using only hash functions. IMHO inventing a good encryption primitive is very hard even for people with knowledge in the subject.
    – Ismael
    Commented Apr 20, 2019 at 7:39
  • I'm not using etherum for payments, you know. I agree that inventing a good encryption is a hard thing but I didn't invent anything, I just implemented existing Vernam cipher Commented Apr 21, 2019 at 10:17

1 Answer 1

6

You may use the following simple function to encrypt/decrypt arbitrary data on-chain:

function encryptDecrypt (bytes memory data, bytes memory key)
  public pure returns (bytes memory result) {
  // Store data length on stack for later use
  uint256 length = data.length;

  assembly {
    // Set result to free memory pointer
    result := mload (0x40)
    // Increase free memory pointer by lenght + 32
    mstore (0x40, add (add (result, length), 32))
    // Set result length
    mstore (result, length)
  }

  // Iterate over the data stepping by 32 bytes
  for (uint i = 0; i < length; i += 32) {
    // Generate hash of the key and offset
    bytes32 hash = keccak256 (abi.encodePacked (key, i));

    bytes32 chunk;
    assembly {
      // Read 32-bytes data chunk
      chunk := mload (add (data, add (i, 32)))
    }
    // XOR the chunk with hash
    chunk ^= hash;
    assembly {
      // Write 32-byte encrypted chunk
      mstore (add (result, add (i, 32)), chunk)
    }
  }
}

It is symmetric, i.e. encryptDecrypt (encryptDecrypt (x, key), key) == x for any x and key. It is also secure enough.

Algorithm explanation: This function just XORs data with the following sequence of 32-byte hashes:

hash0 = keccak256 (key, 0)
hash1 = keccak256 (key, 32)
hash2 = keccak256 (key, 64)
...
hashN = keccak256 (key, N * 32)

This approach is known as Counter (CTR) encryption mode.

You may easily implement the same logic on any programming language that is able to calculate keccak256 on binary data.

Security Note: This schema assumes that each key is used only once and is hard to guess, preferably random. If you want to use one key several times, or use weak keys, you need to use initial vector like this:

For encryption:

  1. Generate random sequence of, say, 16 bytes, lets call it IV (initial vector)
  2. Append IV to the key (key = key + IV)
  3. Encrypt your data with this enforced key
  4. Prepend encrypted data with IV (encData = IV + encData)

For decryption:

  1. Cut first 16 bytes from encrypted data (these bytes are actually IV) and append them to the key
  2. Decrypt data using this enforced key
13
  • Looks good. What algorithm it is? Because I clearly should encrypt message offchain first, so I need to know how to do it. Another question about its reliability since I'm planning use it with some sensitive production data. I see your last line covers this a bit, but I think I need a bit more details. Maybe some link on the algorithm? Commented Apr 18, 2019 at 12:40
  • You may use this very function to encrypt. Commented Apr 18, 2019 at 12:44
  • 1
    I can't use it to encrypt since I cannot pass plaintext to the network. I am also not sure about XORing security. Commented Apr 18, 2019 at 12:45
  • 1
    By "data you XOR with" I meant hashes sequence, not the data. Sorry for confusion. The only property of the data that will not be hidden is its length, so make sure not to encrypt things like "yes"/"no", or "true"/"false" :) Commented Apr 18, 2019 at 12:52
  • 2
    You may have one master key, and generate one-time keys from it using keccak256 (masterKey, i), where i is the number of key. BTW, I updated hash generation logic in my solution to make it simpler and more secure. Commented Apr 18, 2019 at 13:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.