Skip to content

Commit ee0ba04

Browse files
committed
added 2016/14
1 parent d45144b commit ee0ba04

File tree

3 files changed

+86
-0
lines changed

3 files changed

+86
-0
lines changed

2016/14/README.md

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# 2016, Day 14: One-Time Pad
2+
3+
In order to communicate securely with Santa while you're on this mission, you've been using a [one-time pad](https://en.wikipedia.org/wiki/One-time_pad) that you [generate](https://en.wikipedia.org/wiki/Security_through_obscurity) using a pre-agreed algorithm. Unfortunately, you've run out of keys in your one-time pad, and so you need to generate some more.
4+
5+
## Part 1
6+
7+
To generate keys, you first get a stream of random data by taking the [MD5](https://en.wikipedia.org/wiki/MD5) of a pre-arranged [salt](https://en.wikipedia.org/wiki/Salt_(cryptography)) (your puzzle input) and an increasing integer index (starting with `0`, and represented in decimal); the resulting MD5 hash should be represented as a string of _lowercase_ hexadecimal digits.
8+
9+
However, not all of these MD5 hashes are _keys_, and you need `64` new keys for your one-time pad. A hash is a key _only if_:
10+
11+
* It contains _three_ of the same character in a row, like `777`. Only consider the first such triplet in a hash.
12+
* One of the next `1000` hashes in the stream contains that same character _five_ times in a row, like `77777`.
13+
14+
Considering future hashes for five-of-a-kind sequences does not cause those hashes to be skipped; instead, regardless of whether the current hash is a key, always resume testing for keys starting with the very next hash.
15+
16+
For example, if the pre-arranged salt is `abc`:
17+
18+
* The first index which produces a triple is `18`, because the MD5 hash of `abc18` contains `...cc38887a5...`. However, index `18` does not count as a key for your one-time pad, because none of the next thousand hashes (index `19` through index `1018`) contain `88888`.
19+
* The next index which produces a triple is `39`; the hash of `abc39` contains `eee`. It is also the first key: one of the next thousand hashes (the one at index 816) contains `eeeee`.
20+
* None of the next six triples are keys, but the one after that, at index `92`, is: it contains `999` and index `200` contains `99999`.
21+
* Eventually, index `22728` meets all of the criteria to generate the `64`th key.
22+
23+
So, using our example salt of `abc`, index `22728` produces the `64`th key.
24+
25+
Given the actual salt in your puzzle input, _what index_ produces your `64`th one-time pad key?
26+
27+
Your puzzle input was `ihaygndm`.
28+
29+
Your puzzle answer was `15035`.
30+
31+
## Part 2
32+
33+
Of course, in order to make this process [even more secure](https://en.wikipedia.org/wiki/MD5#Security), you've also implemented [key stretching](https://en.wikipedia.org/wiki/Key_stretching).
34+
35+
Key stretching forces attackers to spend more time generating hashes. Unfortunately, it forces everyone else to spend more time, too.
36+
37+
To implement key stretching, whenever you generate a hash, before you use it, you first find the MD5 hash of that hash, then the MD5 hash of _that_ hash, and so on, a total of _`2016` additional hashings_. Always use lowercase hexadecimal representations of hashes.
38+
39+
For example, to find the stretched hash for index `0` and salt `abc`:
40+
41+
* Find the MD5 hash of `abc0`: `577571be4de9dcce85a041ba0410f29f`.
42+
* Then, find the MD5 hash of that hash: `eec80a0c92dc8a0777c619d9bb51e910`.
43+
* Then, find the MD5 hash of that hash: `16062ce768787384c81fe17a7a60c7e3`.
44+
* ...repeat many times...
45+
* Then, find the MD5 hash of that hash: `a107ff634856bb300138cac6568c0f24`.
46+
47+
So, the stretched hash for index `0` in this situation is `a107ff...`. In the end, you find the original hash (one use of MD5), then find the hash-of-the-previous-hash `2016` times, for a total of `2017` uses of MD5.
48+
49+
The rest of the process remains the same, but now the keys are entirely different. Again for salt `abc`:
50+
51+
* The first triple (`222`, at index `5`) has no matching `22222` in the next thousand hashes.
52+
* The second triple (`eee`, at index `10`) hash a matching `eeeee` at index `89`, and so it is the first key.
53+
* Eventually, index `22551` produces the `64`th key (triple `fff` with matching `fffff` at index `22859`.
54+
55+
Given the actual salt in your puzzle input and using `2016` extra MD5 calls of key stretching, _what index_ now produces your `64`th one-time pad key?
56+
57+
Your puzzle input was still `ihaygndm`.
58+
59+
Your puzzle answer was `19968`.
60+
61+
62+
## Solution Notes
63+
64+
The only way I can think of solving this puzzle is the straightforward one, possibly with caching (which is pretty much obligatory in part 2 and "nice to have" in part 1). Even so, part 2 takes a quite long.
65+
66+
* Part 1, Python: 198 bytes, ~2 s
67+
* Part 2, Python: 271 bytes, ~30 s

2016/14/aoc2016_14_part1.py

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import re,hashlib as H
2+
K=lambda i:H.md5("ihaygndm"+str(i)).hexdigest()
3+
n,i=64,0
4+
while n:
5+
i+=1;m=re.search(r'(.)\1\1',K(i))
6+
if m and any(5*m.group(1)in K(j)for j in range(i+1,i+1001)):n-=1
7+
print i

2016/14/aoc2016_14_part2.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import re,hashlib as H
2+
c={}
3+
def K(i):
4+
if i in c:return c[i]
5+
h="ihaygndm"+str(i)
6+
for k in range(2017):h=H.md5(h).hexdigest()
7+
c[i]=h;return h
8+
n,i=64,0
9+
while n:
10+
i+=1;m=re.search(r'(.)\1\1',K(i))
11+
if m and any(5*m.group(1)in K(j)for j in range(i+1,i+1001)):n-=1
12+
print i

0 commit comments

Comments
 (0)