Beeminder Forum

How to lock your device if you don't have any friends

I have come to realize that my only contributions to this forum are off-topic rants and reviving threads from 2019. To contribute at least a little, I want to share a script that I have started to use recently and enjoy a lot.

The fundamental problem that I wanted to solve is “how can I lock myself out of websites and programs without permanently locking myself out”. For example, there are neat apps that can monitor my YouTube consumption and close the app when I exceed my budget. Unfortunately, the purpose of these apps can be simply defeated by uninstalling them.

I use “Lock Me Out” on my Android phone, and they provide a feature to password lock the app while blocking is active. That prevents me from uninstalling the app, but of course setting a password is not too helpful if the person who you want to lock out is yourself. The developers suggest giving the password to a friend so that you cannot work around this mechanism. That’s a good idea, but I am weirdly individualistic sometimes, and I prefer to keep the keys in my hand.

Ideally, I would have something like a timelock where I write down the password, lock it in with a timer, and not access it until the time is over. People in this forum have suggested such a device. And again, while it is a good idea, I wouldn’t say I like the idea of having an additional piece of hardware to solve a digital problem.

Luckily, there is a straightforward digital solution. I want to think that I first come up with this idea by myself, but when I googled it a couple of years ago, I found that the legendary Gwern had already written about it in 2011. So, if you would like an in-depth explanation, click on that link (it’s the section about chained-hashes that is relevant for this post).

One way to implement a timelock is to use hash functions. Hash functions take input and compute an output deterministically. The beauty is that you cannot predict the result. You have to do the work and execute the function. For example, you could start with the phrase “secret-password,” put it into your hash function and get the output “oN5CKH6G” in a certain amount of time, say one second.

By repeatedly using the output as the input for the same function, we can spend an arbitrary amount of time (or iterations) until we get a final string, our time password. We can use this password like a regular password, except we throw it away once we have used it to lock the app (or whatever our use-case is).

Lastly, to restore the password, we need to remember our initial input and the number of iterations we have executed. For example, here I am running one hundred million iterations which take about 90 seconds on my machine.

2021-04-25_22-47

And at the end, I get a nice random time password. One could post-process the final string to allow only certain characters or shorten the length, as some websites require it.

Now, you can see whatever amount of time I am willing to spend initially; I will later have that same amount as a buffer to defeat Akrasia. I have noticed that waiting ten minutes to open up Reddit is already more than enough in practice.

2021-04-25_22-49

Lastly, here is the code if anybody wants to try it. It requires Python 3. I am using the code with 3.9, but it should work with 3.4 too. I have replaced the f-strings with format.

hash.py
#!/usr/bin/env python3

import hashlib
import sys
import time
import getpass


def get_input():
    total_count = 0
    while not total_count:
        try:
            # We do the double conversion thing to allow 10ex notation
            total_count = int(float(input("Enter count: ")))
        except:
            print("Enter a positive integer.")

    seed, seed_check = "a", "b"
    while seed != seed_check:
        seed = getpass.getpass()
        seed_check = getpass.getpass(prompt="Confirm password: ")
        if seed != seed_check:
            print("Passwords must match.")
    return total_count, seed


def print_progress(total_count, passed_time, current_count):
    projected_time = total_count * passed_time / current_count
    progress_time = "[{:.2f} / {:.2f}s".format(passed_time, projected_time)
    progress_percent = current_count / total_count * 100
    progress_percent = "- {:.2f}%]".format(progress_percent)
    print(progress_time, progress_percent, "   ", end='\r')


if __name__ == "__main__":
    total_count, seed = get_input()
    start_time = time.time()
    current_time = start_time
    m = hashlib.sha256()
    m.update(seed.encode())

    for current_count in range(total_count):
        if time.time() - current_time > 1:
            current_time = time.time()
            passed_time = current_time - start_time
            print_progress(total_count, passed_time, current_count)
        m.update(m.digest())

    print("{} hashes done. Final result:".format(total_count))
    print(m.hexdigest())
5 Likes

Wow that’s super clever.

FYI there are also services that will let you email yourself later, Google “email me later” to see some options. Still, this hashing solution feels like it has some cool potential for a more streamlined or automated solution to specific use cases.

2 Likes

Great post!

Tongue in cheek: with my level of akrasia, I’d rather port the code to c so I’d have to wait less than the 10 minutes i initially set. :smile:

On the plus side that trick only works once, because then I’d use the c version to generate hashes…

2 Likes

Thanks for mentioning that!

[Phone conversation]

Greenidge plant operator: Hello Greenidge operation speaking, how may I help you today?

eugeniobruno: Yeah, hello… ah so I really would like to browse some Reddit now, but I SHA256-hashed my password ten Trillion times. Is there anything you can do about that?

Greenidge: Let me check quickly… Yeah we have an ASIC available. Just send that initial string over and we’ll return the result in a couple of seconds.

eugeniobruno: kthxbye!

3 Likes

Excellent idea, thanks for sharing your code! Will apply it if I have chance, sadly I’m using iOS and can’t find any ways to set text password for Screen Time. The current 4-number password is too easy to remember.

3 Likes

I read a sci-fi story years ago that featured a genius/addict character who did something similar. The potentially fatal (highly addictive but don’t take too much too often, though you’ll want to) doses were in a locked cabinet where the combination required him to calculate (or decrypt or something) a value that took long enough and enough concentration that he was forced to sober up between doses.

Probably an old enough story that they didn’t imagine computers being able to do one hundred million iterations in ninety seconds…

But it’s an interesting twist, effectively a context-aware algorithm: the more that he wanted to unlock it, the longer it took, and only when he cared less about unlocking it was he able to compute the key

4 Likes

Is there a way to use at least six or possibly eight numbers? If so, I could change the script so that it outputs that instead of the full hex-digits string of the final hash.

That’s pretty cool. I have to read more Sci-Fi again. I started Snow Crash yesterday, and the first chapter already blew my mind.

Maybe you could implement a similar algorithm using a problem that is not yet solved (or at least not fully solved) by computers. For example, in Super Mario 64, finish the first Bowser stage in under 90 seconds. By adjusting the time, you could make it so that the required level of soberness is necessary to be achievable.

On the other hand, there are anecdotes of prodigies in their respective field that perform at their best when they are slightly intoxicated, so maybe it wouldn’t work.

1 Like