Published using Google Docs
Trustless Time-Locked Encryption
Updated automatically every 5 minutes

Trustless Time-Locked Encryption Protocol

Under review

Abstract

This proposal makes it possible for content to be shared with another party at a later date, without a trusted third party. The timed encryption is implemented using the blockchain and its underlying coin is used as an incentive for decryption.

The following diagram illustrates the goal behind the proposal. Alice is unable to unlock Bob's documents anytime before the encryption time which Bob set.


Overview

Bob will generate a pair of keys, using the private key to encrypt the file. The encryption scheme used to encrypt the files is out of scope of this document. For the sake of argument let's use AES[1] with a key size of 256 bits. We just need to make sure that Alice can't decrypt it until both keys have been revealed in their entirety.

This diagram shows the steps that Bob takes to generate the keys necessary to perform the encryption.

The key splitting mechanism, Shamir's Secret Sharing[2], just splits the key to a 2-2 scheme. However, splitting to any n-m scheme where m is at most n-1 would suffice.

Time delay

To ensure data stays hidden for a predetermined amount of time we can use proof of work duration prediction. Similar to how the duration between bitcoin blocks averages to ten minutes, we can estimate how much work is needed to prove a puzzle.

In this case we shall make use of a Puzzle Transaction to incentivize and publish Key02.

Hash with a Hint

We don't want Key02 to be public immediately but it is safe to publicize its hash. The SHA-256 hash algorithm returns a 256 bit hash, meaning there are at least 2256 possible hash. To convert a hash into the original value would require more computing power than earth has (possibly would ever have). To make undoing a hash possible we can provide a 'hint', or better known as a bit mask.

For example, if the value of private Key02  is "secret" it has a binary representation of

"secret"                        01110011 01100101 01100011 01110010 01100101 01110100

Let's say we want to give a hint of 16 (number of unset bits), this leaves a possible bitmask of

hint                                11111111 11111111 11111111 11111111 00000000 00000000

Without giving away Key02 we will publicize the hash, bitmask, and the logical and between Key02 & bitmask.

hash             0x2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b

mask                                11111111 11111111 11111111 11111111 00000000 00000000

hint                                01110011 01100101 01100011 01110010 00000000 00000000

To force a delay of approximately 1 second, hint should be set to . The '+ 1' is used since the hint is just an average estimated time taken to calculate. Each time the hint is incremented, the time required to determine the original value is doubled.

To restore the Key02 with these values will take, on average, 215 or 32768 hashes. Even with a CPU this would be resolved in approximately a second. Generating a better Hint & Time for sender's needs can be predicted before the first transaction is sent.

Puzzle Transaction

Each bitcoin transaction is a program that conforms to Script[3] instructions. The transaction needed here will be similar to the transaction puzzle[4] example mentioned in the article. It is a Pay-to-Script[5] hash transaction with an unset recipient.

Breakdown

Let's walk through an example to see how a transaction can be used to hide a key.

  1. Key0 = password
  2. Encrypt files with Key0
  3. Require 2 parts from 2 to reconstruct Key0. Shamir Secret Sharing Scheme Playground
  4. Generate Transaction script with a hint set to your liking, i.e. hint = 64
  5. Choose an nLocktime for when miners should start working towards puzzle reward
  6. Send Transaction and Share Data Transfer with Recipient

Generation

Script

This TypeScript code snippet will generate the OP Codes for the transaction. See the full script in this Github Gist.

const secret = 0x12312312n // bigint that contains the entire secret to hide

const hint   = 8

/** Creates OP Codes to obfuscate the secret using the given hint. */

async function buildOpCodes(secret: bigint, hint: number) {

  const secretBytes = toBytes(secret)

  if (hint * 8 > secretBytes.byteLength)

    throw new Error('Secret is too short to be hidden sufficiently.')

                                                                 // hint = 8

  const hidden   = 2n ** BigInt(hint) - 1n                       // 0x000000ff

  let secretMask = 2n ** BigInt(secretBytes.byteLength * 8) - 1n // 0xffffffff

      secretMask -= hidden                                       // 0xffffff00

  const visible  = secret & secretMask                           // 0x12312300

  const hiddenMask = toBytes(hidden, secretBytes.byteLength)

  const visibleMask = toBytes(visible, secretBytes.byteLength)

  const solutionHash = await hash256(secretBytes)

  // sha256(sha256((<input> & `hidden`) | `visible`)) == `solution`

  return [

    solutionHash,

    OP_SWAP,

    hiddenMask,

    OP_AND,

    visibleMask,

    OP_OR,

    OP_HASH256,

    OP_EQUALVERIFY,

  ]

}

OP Codes

Test the generated bitcoin script in the bitauth IDE. In this example, the success test case shows that the Key02 must be the check / scriptPubKey inorder for the transaction to compile.

OP_PUSHBYTES_32 0x7698649eb600fe7fd3ccab9f3e6dbaec56ff6031903659dac0c1bb7af2520287

OP_SWAP

OP_PUSHDATA_1 0x82 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff

OP_AND

OP_PUSHDATA_1 0x82 0x0801d06ddc9ed804bc3af6e90d493ba5b776383a468ee241a43257fa9a9ff13c09ab0a96d82d3072c113e3920e080e5deaa4c13fa84825281e5b9653461014931bc35aa9a29d1a624cf773851e81f4b0dd723aec85d96bb1dfee651478af664d930b0b3d53836d95dbc880815fff1b02143de18e18e7d0fcdab80000000000000000

OP_OR

OP_HASH256

OP_EQUALVERIFY

Create Transaction

The compiled bitcoin script is used to create the raw hex for the transaction.[6] This hex will be broadcast, and used to generate a Transaction ID to give to the recipient.

bitcoin-cli createrawtransaction "[

  <inputs>,

  [{ "data": <script> }, <other_outputs>],

  <nLocktime>,

  false

]"

Transaction ID

The ID of a transaction is used as reference on the blockchain. This ID is computed by concatenating the following components of the transaction,[7] then computing the sha-256 result twice. Finally, reverse bytes to little-endian.[8]

nVersion

This is always 0x1

inputs

Hex encoded value of the puzzle script

outputs

Set to unknown in P2SH, since anyone can claim the transaction

nLockTime

Earliest the transaction can be redeemed, set by the encryptor

The sender has access to the Key02, so they know the only solution to making the input valid. They can safely share the transaction ID with the recipient without revealing anything to anyone.

Data Transfer

Initial

As shown in the first diagram, the encryptor will share a few things with the recipient.

This could be done by email, or any communication protocol out of scope of this proposal. Ideally, a program would generate an archive file type which includes all in one.

Publicly the sender shares the hints for the aforementioned bitcoin transactions. A Third Party can be used, but the sender doesn't need to trust the third party since the shared data has no revealing information.

Lock time has been reached

Once the current time has passed the nLocktime defined in the transaction puzzle, the transaction can be taken by anyone with a solution to the puzzle. Working on solving the puzzle could result in wasted hash power since the sender could have spent the coins marking this transaction as invalid. However, if the transaction is still valid the transaction is fair game for all miners.

Unlocking once miners have solved the puzzle

The recipient should be able to see the solution in its entirety on the blockchain. First, get the transaction data from the blockchain by querying your local bitcoin RPC instance. Decode it, and the solution to the puzzle will be the script for of the first input in the transaction.

bitcoin-cli getrawtransaction <puzzle transaction ID> |

bitcoin-cli decoderawtransaction |

serde --query "$.vin[0].script"

Serde[9] is used to query the returned JSON for an exact value, in this case the `scriptPubKey` which is the answer.

Technically, the miner can fill the scriptPubKey which is not covered by the hint with anything. However, since the transaction can only be spent with a proper final value, the recipient can just perform the bitwise calculations which the miner performed a single time.

Sender Predictions

Price

As the sender, there are a couple predictions that must be made to ensure the decryption key gets found, for as cheap as possible, at the optimal time.

On average, the cost of a puzzle transaction should incentivize miners away from mining towards the blockchain. An estimate of the price needed to convince miners to unlock the puzzle.

Hint & Time

Using the same 'total hashing power of miners' value used to calculate the Price, the hint can be set to resolve in 10 minutes (the same duration it takes to block rewards to be dispensed).

As of 2023, the Antminer S19 Pro[10] is a popular bitcoin miner which can hit roughly 110 trillion hashes per second. A hint of  would take approximately 1 second for an Antminer S19 to unlock. To extend this to a day the hint will be increased by , to a final hint value of 58.

In this example, the key of "secret" is only 6 bytes, so the max hint is 48 (6*8), fortunately, the length of the keys after splitting are still 256 bits, leaving plenty of room to increase if needed for more time.

Third Party

The OP_AND & OP_OR opcodes are disabled, due to vulnerabilities found with OP_LSHIFT.[11] Unfortunately, this means a third party must be used to store hints and their corresponding transactions. Fortunately, this third party doesn't have to be trusted in any way. Any platform, as long as it does not remove the hint over the course of the lock time, will suffice.

nostr[12] seems like a viable option, but even twitter (if you're okay with revealing your identity) would work. This decreases the size of the script and also decreases the mining fee as well. The puzzle transactions scrabble just include the final, and require a minor to on assets. Since the chance of finding random data that would get this to fit is .

The public hint is just a bitmask of the solution.  

It seems like these OP codes could be readded in a future version if implemented in a way which doesn't allocate more memory.[13]

Vulnerabilities

Premature decryption

With enough hashing power, the document recipient could work out the message before the nLocktime has hit.

Unfortunately, the encryptor has to predict the receiver's hashing power by the time the nLocktime has hit. They could increase the hint, just to be safe, but since that increases difficulty, they will probably have to pay more to ensure the recipient can still unlock the message eventually.

Recipient can "speed" up the transaction

If the recipient knows the transaction script, they recreate a similar one with different nLocktime. This would still incur costs for the sender and recipient, maybe

One possible resolution is to include the sending address into the 'puzzle'. Not sure how this is possible, but believe digital signatures can help somehow.

Alternatives

Key Splitting

Rather than using Shamir's Secret Sharing Alice could share a public key with Bob. Then Bob can encrypt the file with her key. Next Bob generates his own public private key pair and encrypts the file again with his public key.

He then uses the private key as the solution to the puzzle transaction for Alice to decrypt the files.

This would mimic a 2-1 split, but using Shamir's Secret Sharing on top of this would enable multiple transactions to be used for a single encryption key.

Sarcophagus[14]

This is a decentralized dead man's switch building on ethereum and Arweave. There are a couple downsides to this protocol.

First, it requires the encryptor to pay for the size of the files to encrypt. This is due to the fact that Sarcophagus stores the files on the Arweave[15] chain. In this proposal, the encryptor sharing their file with the decryptor is out of scope.

Second, this uses a second layer token to incentivize the miners into decrypting locked files. Financially, this means the value of the token is directly tied to how often users need the service. This proposal uses bitcoin as the underlying token so the price of encrypting is not tied to the popularity of this protocol's usage; but instead volatility of the price of bitcoin. Miners will always be around to solve the transaction puzzles for as long as they are incentivized to mine bitcoin.

Proposals

There are a few proposals for time-locked encryption, but seems like most require a trusted[16] third[17] party.[18] Other solutions require the recipient to do the PoW to unlock the files[19] themselves, which is a bit unrealistic for most users.

Thoughts

Why is a Trustless Solution useful?

Whistleblowers can encrypt and share their documents and findings without the contents being revealed for a set amount of time. The initiator will have time to prepare before the documents are unlocked to their target, and they can know that no middle man can compromise them by unlocking the documents prematurely.

Future transactions can be revoked prematurely, is that a problem?

No, this is a benefit by design.

By having transactions be revocable, miners have no incentive to solve puzzles in which the reward can be claimed. Miners can focus on securing the network, or solving puzzles with a claimable reward.

The encryptor can undo their transaction anytime before the transaction is unlocked, allowing them to extend the time, or cancel the decryption process entirely.

Puzzles without an answer

This sucks, but it means that the sender destroyed their own coin to DOS the network. Maybe digitally signing[20][21] can be used to ensure that there is an answer to the puzzle.

If the network is spammed with puzzles without an answer, miners wouldn't want to waste time trying to solve any puzzle, real or fake. Maybe the hash can be proved to fit the hint and provide a valid transaction, without revealing the original key.[22]

Can OPCodes be removed in the future?

Yes.[23] However, OP_HASH256, or any other hash related OP codes will probably not get removed.

Nested Transactions

Any message could be encrypted and stored in the puzzle transaction. This message could be another puzzle, not sure what the use case is at the moment, but it is there…

Encoding Data in the blockchain

Many interesting approaches to doing this.[24] Recently, Ordinals[25] have allowed images, and various files to be fully included in the blockchain, but this has been congesting the network a bit. All seem fairly expensive to try and do at a realistic scale. The best to handle data off chain (or on another chain, like IPFS[26] / Arweave), and only keep the (partial) decryption keys on chain.

Transaction Security

Note that while transactions like this are fun, they are not secure, because they do not contain any signatures and thus any transaction attempting to spend them can be replaced with a different transaction sending the funds somewhere else.[27]

This isn't actually a big deal. Since miners are the ones to solve the puzzles, they should just include the answer in the next block they mine. Unfortunately, this may limit the puzzle unlocking to large scale miners. Small miners would have to team up with the ones finding rewards to ensure their solved transaction doesn't get stolen.


References

Changelog


[1] Advanced Encryption Standard

[2] Shamir's Secret Sharing

[3] Bitcoin Script

[4] Transaction puzzle

[5] Learn me a bitcoin

[6] createrawtransaction — Bitcoin

[7] How is the transaction hash determined?

[8] How are TXIDs determined from the raw hex data of a block?

[9] serdejsonpath.live

[10] Bitmain - Antminer S19 Pro

[11] CVE-2010-5137

[12] nostr.com

[13] Are disabled OpCodes considered invalid or are they treated as NOP? - BitcoinTalk Forum Post

[14] sarcophagus.io 

[15] arweave.org

[16] time-based encryption algorithm?

[17] Is it possible to make time-locked encryption algorithm?

[18] Time-Lapse Cryptography

[19] PulpSpy's answer

[20] Digital signature

[21] Understanding Digital Signatures

[22] Hashing and Digital Signatures – CompTIA Security+ SY0-501 – 6.1

[23] Reliance upon transactions .vs. reactions to incidents - BitcoinTalk Forum Post

[24] Hidden surprises in the Bitcoin blockchain and how they are stored

[25] ordinals.com

[26] ipfs.tech

[27] The first successful Zero-Knowledge Contingent Payment