A858DE45F56D9BC9 archive

The count RNG and the runs

The number of GUIDs per post comes from one long-lived C# Random per app start, seeded with the machine's uptime. It identifies runs, restarts and reboots.

The mask digit comes from new Random(DateTime.Now.Millisecond).Next(9), a fresh Random for every post. The other number in each filler post, how many GUIDs it contains, comes from a second Random: one created once when A858's program starts and then asked for one number per scheduled post. With the right seed, the count of every post in a run can be reproduced, including runs that have never been decrypted.

74runs reproduced exactly by a seed
4runs with a probable seed, too few posts to be sure
6,477post counts regenerated from those seeds by this site's build
9,558of 9,558 filler posts assigned to a run or group

Reading the count without decrypting

For the 2012–2013 posts that decrypt, the count N is the number of GUIDs in the decrypted list. The later posts (from 2013-10-16) have never been decrypted, but N can still be read off their length. Every filler post is N GUIDs of 32 hex characters, encrypted with 3DES, plus one 8-byte padding block, then posted as hex. So the post is exactly 64·N + 16 hex characters long. All 6,120 filler posts after the cutover fit this exactly. The other posts from that period are the manual posts (hand-made, and the three account-setup posts) and one status marker. The tiny 16-character posts are the N = 0 case: an empty list, which is only padding.

So even undecrypted posts carry a number, and lined up in posting order those numbers can be matched against an RNG.

The last 16 characters are the padding. A GUID list is always a whole number of 8-byte blocks, so .NET's default PKCS7 padding adds one full block of eight 08 bytes, and every one of the 3,434 scheduled filler posts before the cutover that decrypts ends in exactly that block. Under one key and ECB mode that block always encrypts the same way: all 487 posts under MD5 of the name end in 5dacffba8ff64dbd, and all 268 under MD5 of the name twice share a tail too. After the cutover, none of the 6,010 posts checked shares its last 16 characters with another, so they were not encrypted with one fixed key in ECB mode, as the earlier filler was. That rules out the simplest kind of key, not the others: a different key for every post, or CBC mode with an IV that was never posted.

The count is Next(lo, hi) from a long-lived Random

Take the run that starts with 195ea8 (2013-02-24 20:51 UTC), which posted every 66 minutes. The GUID counts of its first posts are:

60 48 40 60 56 44 48 36 48 44 32 40 44 60 32 ...

Every count in this period is a multiple of 4 between 32 and 60. That is 4 * Next(8, 16), and the counts come out in order from:

var random = new Random(983934859);
random.Next();                          // one draw when the app starts
for (var i = 0; i < 15; ++i)
    Console.Write(4 * random.Next(8, 16) + " ");
// 60 48 40 60 56 44 48 36 48 44 32 40 44 60 32

It does not stop after 15. The run lasted until 2013-03-09 (267 timer slots), and every surviving post lands on the right number when placed in its 66-minute slot. Missing slots are posts that never made it to Reddit or were lost; each one still used up its draw. A single seed cannot fit hundreds of posts by chance: each post has 8 possible values, so 259 posts are worth about 777 bits against a search of about 231 seeds.

The seed is large here, not 0–999, because this Random is created with new Random() and no argument. In .NET Framework that uses Environment.TickCount: the number of milliseconds since Windows booted.

Different settings over time

The count code was rebuilt in December 2012 – January 2013, and it changed how many draws the program makes (see the draws that were never posted below). After that, the range changes whenever A858 restarted the program or changed its settings:

Period Count per post
Jun 2012 Next(8, 24)
Jun – Dec 2012 Next(8, 64)
Dec 2012 – Jan 2013 fixed 64
Jan – Aug 2013 4 * Next(8, 16)
Aug – Oct 2013 (into the first undecrypted week) 4 * Next(1, 12)
Nov 2013 – Aug 2014 Next(0, hi), hi between 20 and 184 per run
mid 2014 – mid 2015 mostly one fixed N per run (40, 42, 51, 80, …)
Aug 2015 Next(0, hi) with 2 extra hidden draws per post
Nov 2015 – Feb 2016 Next(10, hi), hi between 24 and 80 per run

The first undecrypted stage (1okyzu, 2013-10-16) uses exactly the same 4 * Next(1, 12) as the decrypted posts just before it. The "new era" after the October 2013 outage is the same program, restarted with a new key, not a different program.

Draws that were never posted

The count stream also draws for some slots that never produce a post, and how many depends on the build:

These readings come from the timing and the draw positions, not from anything the program shows directly. A few runs do not fit: 1okyzu starts at draw 2, and one 2012 run starts at draw 1. What the startup draw is used for is still unknown. Its value does not show in the counts, the GUID seeds, the timing, the title clock, the archive metadata or the post-cutover salts tested so far.

The seed tells you when the machine booted

Since the seed is milliseconds since boot, app start − seed gives the boot time. For the Feb 2013 run, 983,934,859 ms is 11 days 9 h 19 min, which puts the boot at 2013-02-13 10:26–11:32 UTC (the range comes from not knowing exactly when in the first timer slot the app started).

2013201420152016Run 1 (seed 1,054,668,128) 2012-06-26T15:25 → 2012-06-26T15:27 11 posts Next(8,24) boot 2012-06-14 10:27–10:27Run 2 (seed 1,054,905,639) 2012-06-26T15:32 → 2012-06-26T17:04 22 posts Next(8,24) boot 2012-06-14 10:26–10:31Run 3 (seed 1,063,142,882) 2012-06-26T17:45 → 2012-06-26T19:41 77 posts Next(8,24) boot 2012-06-14 10:25–10:26Run 4 (seed 664,106,302) 2012-06-26T19:47 → 2012-06-26T20:42 11 posts Next(8,24) boot F 2012-05-15 19:07–19:12Run 5 (seed 659,062,118) 2012-06-26T21:11 → 2012-06-26T22:51 21 posts Next(8,24) boot F 2012-05-15 19:11–19:13Run 6 (seed 652,173,660) 2012-06-26T23:09 → 2012-06-27T05:14 42 posts Next(8,24) boot F 2012-05-15 19:06–19:16Run 7 (seed 629,367,141) 2012-06-27T05:26 → 2012-06-27T15:00 30 posts Next(8,24) boot F 2012-05-15 18:57–19:13Run 8 (seed 593,707,075) 2012-06-27T15:27 → 2012-06-27T19:47 14 posts Next(8,24) boot F 2012-05-15 18:59–19:19Run 9 (seed 449,725,219) 2012-06-29T07:20 → 2012-06-29T23:31 43 posts Next(8,64) boot F 2012-05-15 19:12–19:12Probable run 1 (seed 293,378,646) 2012-07-01T02:49 → 2012-07-01T02:49 5 posts Next(8,64) boot F 2012-05-15 19:11–19:12Run 10 (seed 38,548,093) 2012-07-02T21:33 → 2012-07-04T09:34 37 posts Next(8,64) boot 2012-07-02 09:51–10:51Run 11 (seed 37,057,156) 2012-07-05T21:03 → 2012-07-07T16:34 40 posts Next(8,64) boot 2012-07-05 09:16–10:46Run 12 (seed 36,542,234) 2012-07-05T21:04 → 2012-07-06T10:34 20 posts Next(8,64) boot 2012-07-05 10:25–10:55Run 13 (seed 19,267,593) 2012-07-06T17:37 → 2012-07-07T16:07 16 posts Next(8,64) boot 2012-07-06 10:46–12:16Run 14 (seed 205,976,312) 2012-07-07T21:41 → 2012-07-11T04:53 21 posts Next(8,64) boot 2012-07-05 08:28–12:28Run 15 (seed 117,733,640) 2012-07-07T21:41 → 2012-07-11T04:53 21 posts Next(8,64) boot 2012-07-06 08:59–12:59Run 17 (seed 38,757,453) 2012-07-12T03:27 → 2012-07-21T04:22 37 posts Next(8,64) boot before 2012-07-11 16:41Run 16 (seed 39,036,328) 2012-07-12T04:00 → 2012-07-21T04:27 37 posts Next(8,64) boot 2012-07-11 17:05–17:10Run 18 (seed 6,051,781) 2012-07-27T20:57 → 2012-07-27T21:41 44 posts Next(8,64) boot 2012-07-27 19:15–19:16Run 19 (seed 257,878,531) 2012-07-30T20:58 → 2012-07-30T21:10 5 posts Next(8,64) boot 2012-07-27 21:17–21:20Run 20 (seed 267,961,078) 2012-07-31T03:39 → 2012-07-31T04:19 6 posts Next(8,64) boot ambiguous: 2012-07-27 ~21:13 or F 06-14 12:54 (probable run, 6 posts)Probable run 2 (seed 377,361,921) 2012-08-01T04:02 → 2012-08-01T04:35 4 posts Next(8,64) boot 2012-07-27 19:10–19:12Run 21 (seed 452,527,359) 2012-08-02T00:55 → 2012-08-02T02:40 8 posts Next(8,64) boot 2012-07-27 18:58–19:13Run 22 (seed 528,385,484) 2012-08-02T21:59 → 2012-08-13T10:17 85 posts Next(8,64) boot 2012-07-27 19:02–19:13Run 23 (seed 21,083,000) 2012-08-15T18:16 → 2012-08-15T18:20 5 posts Next(8,64) boot 2012-08-15 12:23–12:24Run 24 (seed 39,416,093) 2012-08-15T21:18 → 2012-08-16T22:56 101 posts Next(8,64) boot 2012-08-15 10:18–10:21Run 25 (seed 158,297,437) 2012-08-17T06:23 → 2012-08-17T09:43 21 posts Next(8,64) boot 2012-08-15 10:14–10:24Run 26 (seed 1,159,248,375) 2012-08-28T20:25 → 2012-08-28T20:55 11 posts Next(8,64) boot 2012-08-15 10:21–10:24Run 27 (seed 1,337,299,625) 2012-08-30T21:53 → 2012-09-04T14:00 67 posts Next(8,64) boot 2012-08-15 10:07–10:24Run 28 (seed 1,774,293,828) 2012-09-05T01:30 → 2012-09-05T05:00 8 posts Next(8,64) boot 2012-08-15 12:08–12:38Run 29 (seed 1,844,859,734) 2012-09-05T20:00 → 2012-09-06T05:00 10 posts Next(8,64) boot 2012-08-15 10:32–11:32Run 30 (seed 1,932,335,625) 2012-09-06T19:30 → 2012-09-07T06:30 12 posts Next(8,64) boot 2012-08-15 09:44–10:44Run 31 (seed 15,917,281) 2012-09-08T05:00 → 2012-09-18T18:30 26 posts Next(8,64) boot 2012-09-07 23:34–00:34Run 32 (seed 945,377,703) 2012-09-19T21:00 → 2012-09-20T02:00 21 posts Next(8,64) boot 2012-09-08 22:08–22:23Run 33 (seed 1,220,410,968) 2012-09-22T01:00 → 2012-09-22T02:50 12 posts Next(8,64) boot 2012-09-07 21:49–21:59Run 34 (seed 83,105,000) 2012-09-23T09:30 → 2012-09-28T00:30 275 posts Next(8,64) boot 2012-09-22 10:23–10:24Run 35 (seed 854,622,578) 2012-10-02T08:00 → 2012-10-10T20:00 535 posts Next(8,64) boot 2012-09-22 10:16–10:36Run 36 (seed 335,015) 2012-10-10T21:00 → 2012-10-15T17:00 30 posts Next(8,64) boot 2012-10-10 20:54–00:54Run 37 (seed 425,468,968) 2012-10-15T21:00 → 2012-10-22T13:00 21 posts Next(8,64) boot 2012-10-10 14:48–22:48Run 38 (seed 1,045,884,937) 2012-10-23T06:23 → 2012-11-10T23:00 141 posts Next(8,64) boot 2012-10-11 03:41–03:51Run 39 (seed 1,106,402,812) 2012-12-03T19:23 → 2012-12-12T06:00 104 posts Next(8,64) boot 2012-11-20 22:02–00:02Run 40 (seed 1,069,639,703) 2013-01-22T14:59 → 2013-02-05T15:00 15 posts 4·Next(8,16) boot 2013-01-09 05:52–05:52Run 41 (seed 1,681,282,046) 2013-02-08T18:00 → 2013-02-13T05:59 13 posts 4·Next(8,16) boot 2013-01-19 21:58–06:58Probable run 3 (seed 33,600,187) 2013-02-13T21:00 → 2013-02-21T20:59 9 posts 4·Next(8,16) boot 2013-02-13 10:24–11:32Run 42 (seed 983,934,859) 2013-02-24T20:51 → 2013-03-09T01:26 267 posts 4·Next(8,16) boot 2013-02-13 10:26–11:32Run 43 (seed 120,022,078) 2013-03-22T20:00 → 2013-03-31T00:34 104 posts 4·Next(8,16) boot 2013-03-21 09:44–10:39Run 44 (seed 1,087,967,875) 2013-04-03T00:32 → 2013-04-05T19:29 93 posts 4·Next(8,16) boot 2013-03-21 09:35–10:19Run 45 (seed 51,450,859) 2013-04-12T04:00 → 2013-05-03T22:50 153 posts 4·Next(8,16) boot 2013-04-10 12:46–16:39Run 46 (seed 39,043,781) 2013-05-15T21:22 → 2013-05-29T17:31 72 posts 4·Next(8,16) boot 2013-05-15 03:07–10:31Run 47 (seed 1,776,839,421) 2013-06-05T01:00 → 2013-06-13T00:59 17 posts 4·Next(8,16) boot 2013-05-14 23:26–11:26Run 48 (seed 387,048,281) 2013-06-17T22:00 → 2013-07-09T21:59 23 posts 4·Next(8,16) boot 2013-06-12 10:29–10:29Run 49 (seed 822,652,546) 2013-07-20T00:00 → 2013-08-02T00:00 147 posts 4·Next(8,16) boot 2013-07-10 11:27–11:29Run 50 (seed 2,278,890) 2013-08-06T00:07 → 2013-08-14T00:06 65 posts 4·Next(8,16) boot 2013-08-05 20:29–23:29Run 51 (seed 714,271,718) 2013-08-22T17:00 → 2013-08-28T15:59 14 posts 4·Next(8,16) boot 2013-08-13 23:35–10:35Run 52 (seed 1,430,705,843) 2013-08-31T00:00 → 2013-09-10T23:59 89 posts 4·Next(1,12) boot 2013-08-14 07:34–10:34Run 53 (seed 26,950,390) 2013-09-12T18:00 → 2013-10-08T11:15 159 posts 4·Next(1,12) boot 2013-09-12 09:30–10:30Run 54 (seed 625,529,093) 2013-10-16T16:50 → 2013-10-23T16:49 43 posts 4·Next(1,12) boot 2013-10-09 03:04–07:04Run 55 (seed 135,104,921) 2013-11-16T01:00 → 2013-12-11T10:59 333 posts Next(0,184), then Next(0,124) from draw 193 boot 2013-11-14 10:28–11:28Run 56 (seed 561,089,375) 2013-12-18T11:33 → 2014-01-16T23:32 237 posts Next(0,98) boot 2013-12-11 20:41–23:41Run 57 (seed 758,305,750) 2014-02-07T23:15 → 2014-02-13T11:14 34 posts Next(0,44) boot 2014-01-30 00:36–04:36Run 58 (seed 115,354,343) 2014-02-14T20:00 → 2014-02-17T19:59 25 posts Next(0,44) boot 2014-02-13 08:57–11:57Run 59 (seed 1,704,050,750) 2014-03-05T06:00 → 2014-03-12T09:59 44 posts Next(0,36) boot 2014-02-13 08:39–12:39Run 60 (seed 19,858,187) 2014-03-12T16:00 → 2014-03-28T15:59 193 posts Next(0,50) boot 2014-03-12 08:29–10:29Run 61 (seed 2,007,067,968) 2014-04-07T22:13 → 2014-04-10T10:14 56 posts Next(0,20) boot F 2014-03-12 08:00–09:06Run 62 (seed 507,820,296) 2014-04-16T07:31 → 2014-05-02T20:29 398 posts Next(0,55) boot 2014-04-10 09:27–10:27Run 63 (seed 1,506,676,046) 2014-05-12T18:11 → 2014-05-15T09:59 59 posts Next(0,33) boot F 2014-04-10 10:33–11:39Run 64 (seed 367,071,562) 2014-05-19T16:33 → 2014-05-27T22:32 199 posts Next(0,33) boot 2014-05-15 09:35–10:35Run 65 (seed 1,582,093,843) 2014-06-02T19:00 → 2014-06-11T08:59 104 posts Next(0,22) boot 2014-05-15 09:31–11:31Run 66 (seed 36,611,781) 2014-08-28T20:30 → 2014-09-09T08:29 24 posts Next(0,65) boot 2014-08-27 22:19–10:19Run 67 (seed 466,795,333) 2015-08-18T20:41 → 2015-08-19T05:31 11 posts Next(0,25) +2 boot 2015-08-13 10:08–11:01Run 68 (seed 316,235,227) 2015-08-25T18:02 → 2015-08-31T22:03 75 posts Next(0,22) +2 boot 2015-08-22 00:11–02:11Probable run 4 (seed 200,662,541) 2015-11-13T20:02 → 2015-11-14T00:02 5 posts Next(10,50) boot 2015-11-11 11:56–12:01Run 69 (seed 314,488,327) 2015-11-15T03:22 → 2015-11-17T21:23 67 posts Next(10,50) boot 2015-11-11 11:01–12:01Run 70 (seed 6,038,486) 2015-11-17T23:27 → 2015-11-30T04:25 704 posts Next(10,40) boot 2015-11-17 21:21–21:46Run 71 (seed 1,220,915,585) 2015-12-02T01:00 → 2015-12-10T11:01 203 posts Next(10,24) boot 2015-11-17 20:51–21:51Run 72 (seed 1,847,828,239) 2016-01-01T00:00 → 2016-01-13T00:06 97 posts Next(10,50) boot 2015-12-10 11:43–14:43Run 73 (seed 546,388,634) 2016-01-19T20:05 → 2016-01-25T17:08 142 posts Next(10,35) boot 2016-01-13 11:18–12:18Run 74 (seed 78,575,145) 2016-02-11T09:30 → 2016-02-16T23:32 135 posts Next(10,80) boot 2016-02-10 10:40–11:40
The seed runs, one row each (hover for details, click to open). Each hollow circle is the boot time implied by the seed (app start minus uptime), joined to the run by a dotted line; runs that share a boot line up vertically. All runs.

2013-02-13 is the day after Patch Tuesday, and that happens again and again: boots come out, for example, as 2012-07-11, 2012-08-15, 2012-10-10, 2013-01-09, 2013-02-13, 2013-06-12, 2013-11-14, 2013-12-11, 2014-03-12, 2014-04-10 and 2014-05-15, each one or two days after Microsoft's monthly update Tuesday, as expected for a Windows machine that reboots for updates. Several runs in a row often share the same boot; some runs end when the machine reboots, others when only the app is restarted.

Two more details back this up:

A prediction that came true

On Reddit, the 25-minute run of November 2015 appeared to start with 3tjz5s on 2015-11-20 11:03. Its counts fit Next(10, 40) with seed 6,038,486, but only if 144 draws had already happened before that post. The next run's seed (December 2015, 3v2sif) independently puts a reboot at Nov 17 20:51–21:51, and 6,038,486 ms is 1 h 41 min, so this app started on Nov 17 roughly between 22:30 and 23:30. Counting back 143 slots of 25 minutes from Nov 20 11:03 lands on Nov 17 23:28, inside that window: the app had made 143 posts nobody had seen.

Those posts turned up later in the Arctic Shift archive: 143 posts from 2015-11-17 23:27 to 11-20 10:38, every 25 minutes, with exactly the predicted counts. The full run (run 70) is 704 posts long and every one of them matches.

Every filler post is accounted for

Before the cutover (3,438 GUID-list filler posts, 2011 → 2013-10-08):

What Posts
Reproduced by a seed 3,296
Body removed, placed by its count (1158zr) 1
Fixed N = 64 (Dec 13 2012 – Jan 1 2013) 91
Short app starts, too few values to pin a seed 46
Single posts just before an app start 4

Not counted here: 42 operator test posts (bursts seconds or a minute apart, often with Pacific-time titles), 32 hand or tool lists from before the scheduled app and one single test post. They are manual posts, listed on the manual posts page.

After the cutover (6,120 length-law filler posts, 2013-10-16 → 2016):

What Posts
Reproduced by a seed (21 runs) 3,175
Same, but posted with an empty body (the seed still drew a count) 8
Fixed N (41 runs, one per app start) 2,921
Probable seed (Nov 13 2015) 5
Short pieces and single test posts, each explained 11

The three account-setup posts of 2014-02-25 are manual posts, not filler, so they are not in this table.

Details from these passes:

What this means

Check it yourself

This script ports the .NET Framework Random (including its int32 overflow, which matters for large seeds), regenerates the start of the Nov 2015 run and compares it with the posts from Arctic Shift, using the length of each post to get N. No decryption is involved.

import json, re, urllib.request

def net_random(seed):
    # .NET Framework System.Random(seed), including its int32 overflow
    M = 2147483647
    w = lambda x: (x + 2**31) % 2**32 - 2**31
    sa = [0] * 56
    mj = w(161803398 - seed)
    sa[55], mk = mj, 1
    for i in range(1, 55):
        ii = 21 * i % 55
        sa[ii], mk, mj = mk, w(mj - mk), mk
        if mk < 0: mk = w(mk + M)
    for _ in range(4):
        for i in range(1, 56):
            sa[i] = w(sa[i] - sa[1 + (i + 30) % 55])
            if sa[i] < 0: sa[i] = w(sa[i] + M)
    a, b = 0, 21
    while True:
        a = a % 55 + 1
        b = b % 55 + 1
        r = w(sa[a] - sa[b])
        if r == M: r -= 1
        if r < 0: r = w(r + M)
        sa[a] = r
        yield r / M

def next_range(rng, lo, hi):
    return int(next(rng) * (hi - lo)) + lo

# Nov 2015 run: seed 6,038,486, one draw at startup, then Next(10, 40) per 25-minute slot
rng = net_random(6038486)
next(rng)
counts = [next_range(rng, 10, 40) for _ in range(40)]

url = ('https://arctic-shift.photon-reddit.com/api/posts/search?subreddit=A858DE45F56D9BC9'
       '&after=2015-11-17T23:00:00&before=2015-11-18T12:00:00&limit=100&sort=asc'
       '&fields=id,created_utc,selftext')
req = urllib.request.Request(url, headers={'User-Agent': 'a858-count-check'})
posts = json.load(urllib.request.urlopen(req))['data']
t0 = posts[0]['created_utc']
for p in posts:
    n = (len(re.sub(r'[^0-9A-Fa-f]', '', p['selftext'])) - 16) // 64
    slot = round((p['created_utc'] - t0) / 1500)
    print(p['id'], 'slot', slot, 'N =', n, 'predicted', counts[slot])
3t82b2 slot 0 N = 10 predicted 10
3t8601 slot 1 N = 38 predicted 38
3t89g7 slot 2 N = 31 predicted 31
...