RubyConf 2020 held a puzzle-raffle in which the puzzle required using brute force to figure out the cleartext which produced a given hash.
If you process payments with Braintree you’ve likely seen a unique id that looks something like a1b2c3d4. This id goes by different names at different places but is often a way for a company to create a massive numbering system. Assuming an alphabet of only lower case letters and digits, the system above could be used for (26 + 10) ^ 8 = 2,821,109,907,456 combinations. That is an enormous number! Some places, including Braintree, also choose to encode some information in these ids. Without going too much into the Mathematics of hashing and encoding algorithms we can say that it’s possible to take some information, let’s say an email or url, add some other information, say a 5 character raffle number 😉, and encode that information into a fixed length String. If you know the unique id and you have the fixed length encoded String, you can reverse engineer, by brute force, the raffle number that was concatenated to the id. That’s what we’ll be doing today! If you’re interested, DM me a link to your rubyconf crowd cast profile* or some unique identifier (email, pgp key, you name it), this will act as your id. I’ll respond with a String/digest hashed from your profile and your raffle number. Your job is to reverse engineer the digest to determine your raffle number. If you correctly calculate your raffle number, you will be entered into a raffle to win 3 new Raspberry Pi 400 units: I have provided a sample.rb file to get you started. N.B. The sample file uses SHA256 but that’s not necessarily the digest algorithm we have used.
Not going to lie, I read the hints and made an assumption about the hashing algorithm. From there, I used nested loops to generate every possible raffle entry.
I then tried iterating over them, but that was slow (never finished), so I thought it would be a good opportunity to try to learn about Ractors and parallelize it.
This is the probably horrible but functional solution I produced. I believe it is actually doing the hashing in parallel (not just concurrently), but I may be wrong. It's literally my first time using Ractors 😅
Note that it keeps checking even after finding a solution, because I haven't yet figured out how to pass around a stop
message and check for it without blocking if the message queue is empty.