The earliest memory I have of ‘programming’ is in the early/mid 90s when my father brought home a computer from work. We could play games on it … so of course I took the spreadsheet program he used (LOTUS 123, did I date myself with that?) and tried to modify it to print out a helpful message for him. It … halfway worked? At least I could undo it so he could get back to work…

After that, I picked up programming for real in QBASIC (I still have a few of those programs lying around), got my own (junky) Linux desktop from my cousin, tried to learn VBasic (without a Windows machine), and eventually made it to high school… In college, I studied computer science and mathematics, mostly programming in Java/.NET, although with a bit of everything in the mix. A few of my oldest programming posts on this blog are from that time.

After that, on to grad school! Originally, I was going to study computational linguistics, but that fell through. Then programming languages (the school’s specialty). And finally I ended up studying censorship and computer security… before taking a hard turn into the private sector to follow my PhD advisor.

Since then, I’ve worked in the computer security space at a couple of different companies. Some don’t exist any more, some you’ve probably heard of. I still program for fun too, and not just in security.

But really, I still have a habit of doing a little bit of everything. Whatever seems interesting at the time!

Popular Github Repos

Posts

WebCrypto SHA-256 HMAC

A quick random thing I learned that I found helpful (and you might too!):

async function hmac(text, secret) {
    let enc = new TextEncoder("utf-8");
    let algorithm = { name: "HMAC", hash: "SHA-256" };
    
    let key = await crypto.subtle.importKey("raw", enc.encode(secret), algorithm, false, ["sign", "verify"]);
    let signature = await crypto.subtle.sign(algorithm.name, key, enc.encode(text));
    let digest = btoa(String.fromCharCode(...new Uint8Array(signature)));

    return digest;
}

This is a function that uses the Web Crypto API to calculate a SHA-256 HMAC of a string given a secret value.

I mostly worked this out so that I could figure out how exactly TextEncoder worked, along with importKey (to turn a secret into proper key material) and also how to convert that back into a hex digest.

>> await hmac("lorem ipsum", "super secret")
"qArFX93Zi83ccIayhYnuFDpd4pk3eB4rZYDvNteobSU="

>> await hmac("lorem ipsum doler sit amet", "super secret")
"klTAioH5nNkguNhU2YcJshaZZtJW9DEb+MTqz4NWq8E="

>> await hmac("lorem ipsum", "even more super secret!")
"RoQLg2uz5KWLMJM72VExH5gZOls5bdZZyzHi678eDWs=" 

>> await hmac("lorem ipsum", "super secret")
"qArFX93Zi83ccIayhYnuFDpd4pk3eB4rZYDvNteobSU="

Disclaimer: This totally counts as rolling your own crypto. Don’t do this unless you know what you’re doing. 😄

Disclaimer disclaimer: I only rarely know what I’m doing. 😄 😄

Also, for what it’s worth, this is equivalent to the Python standard libraries’ hmac + base64 :

>>> base64.b64encode(hmac.digest(b'super secret', b'lorem ipsum', 'SHA256')).decode()
'qArFX93Zi83ccIayhYnuFDpd4pk3eB4rZYDvNteobSU='

Freshly (Frosted) Solved

And so it begins. Freshly Frosted It’s a cute little puzzle game about making a donut factory. It’s a lot like Solving Cosmic Express in that it’s a ‘puzzle on rails’, you are basically routing around the grid from source to target. In the way, we have to go to certain tiles in a certain order (in this case, to apply toppings to our donuts). Let’s do it! The first section (starting with Basic layout) is the final state of the solution.

read more...


AoC 2024 Day 25: Christmas Lockpickinator

Source: Day 25: Code Chronicle

Full solution for today (spoilers!).

Part 1

You are given a series of locks and keys (see below). How many unique (lock, key) are there that do not overlap (they do not have to fit perfectly).

A lock starts from the top. The entire top row is # and the entire bottom row is ..

#####
.####
.####
.####
.#.#.
.#...
.....

A key is the opposite:

.....
.....
.....
#....
#.#..
#.#.#
#####

read more...


AoC 2024 Day 23: LAN Partinator

Source: Day 23: LAN Party

Full solution for today (spoilers!).

Part 1

You are given the edges of an undirected graph. Count how many complete subgraphs of size three exist that contain one or more starting with the letter t.

Aside: Games with local (but not hotseat) multiplayer have gotten rather rarer over the years… how many people still know what a LAN party is/was?

read more...


AoC 2024 Day 22: Xorshiftinator

Source: Day 22: Monkey Market

Full solution for today (spoilers!).

Part 1

Implement a PRNG with the following update function:

  1. Multiply by 64, xor with the previous value, modulo 16777216
  2. Divide by 32, xor with the previous value (from step 1), modulo 16777216
  3. Multiply by 2048, xor with the previous value (from step 2), module 16777216

For each of a series of seeds, sum the 2000th generated number.

read more...


AoC 2024 Day 21: Busy Workinator

Source: Day 21: Keypad Conundrum

Full solution for today (spoilers!).

Part 1

You are trying to type a code on a keypad:

+---+---+---+
| 7 | 8 | 9 |
+---+---+---+
| 4 | 5 | 6 |
+---+---+---+
| 1 | 2 | 3 |
+---+---+---+
    | 0 | A |
    +---+---+

But you cannot type directly. Instead, you can control a pointer on the keypad with arrow keys:

    +---+---+
    | ^ | A |
+---+---+---+
| < | v | > |
+---+---+---+

Whenever you type a ^ on the arrow keys, the pointer on the keypad will move up one, etc. When you type A, then the pointer on the keypad will type whatever it is pointing at.

But that’s not enough either. Add a second keypad. And then a third, that is the one you are actually controlling.

For each output sequence multiple the length of the minimum input sequence needed to generate it by the numeric value of the input sequence (ignoring any A); sum these.

Note: Moving off any keypad or into the blank spaces is an error.

read more...


AoC 2024 Day 20: Shadow Catinator

Source: Day 20: Race Condition

Full solution for today (spoilers!).

Part 1

Given a maze with exactly one path, find how many single walls you can walk through (remove) that shorten the best path by at least 100 units.

read more...