A Compressed OpenBSD Release Version Table

Last updated: 2019-10-09

Background

In October 2019, the OpenBSD project recently celebrated surpassing 400,000 commits to its codebase that has grown and matured for over 24 years. This historic achievement is based on commits made to the repository since it was originally created by project leader Theo de Raadt on 1995-10-18.

In July 2019, Mastodon user @ephemeris@bsd.network posted a simple algorithm for computing an OpenBSD release version based on your target year:

current_version - ((current_year - target_year) * 2)

This is a pretty neat trick, but it wasn't accurate enough for me. OpenBSD follows a rigorous release schedule and has consistently put out two releases per year without fail since 1996!

In my excitement for the upcoming OpenBSD release, I began to wonder more about ephemeris's algorithm and OpenBSD's release schedule.

The history of OpenBSD's releases is well-documented on Wikipedia. A new release comes approximately every six months, and OpenBSD's versioning has been remarkably consistent since its first official release, "2.0". From that release forward, we know that OpenBSD releases:

  1. have always been fewer than 255 days apart
  2. have always been versioned as "previous_version + 0.1"

Inspired by the libtai date and time management library, I know that libtai processes a compressed table of when to apply leap seconds to Coordinated Universal Time so your computer's clock can always stay accurate.

OpenBSD's release schedule seemed simple enough for me to describe in a very small amount of data in a similar way. Because each OpenBSD release is typically about 180 days apart, and has never been more than 255 days apart, each release can be established as a difference in days that can be represented in one byte of data.

At that point, all you need to know is the date of the first OpenBSD release, and you can then read a string of bytes and add those byte values to that date to establish OpenBSD's release version history.

Generating the Compressed OpenBSD Release Version Table

Starting from release version "2.0" dated 1996-10-01, the OpenBSD release version table converts this into its Modified Julian Date. For our purposes, we can simply define a date's MJD value as "days elapsed since 1858-11-17".

1996-10-01 is day 50,357 using the Modified Julian Date method. 50,357 can be represented in hexadecimal as 0xc4b5, and this is the first value in our OpenBSD table: "c4b5".

OpenBSD release version "2.1" occurred on 1997-06-01, 243 days after version "2.0", so the next release can be represented by converting 243 into hexadecimal: 0xf3, so the next value in the OpenBSD table is "f3".

Each OpenBSD release is computed in this manner. Determine the number of days between releases, convert it to hexadecimal, and append that byte to the list. This creates a simple, compressed release history for OpenBSD versions. The only exception to this rule is the first date, which takes two bytes. I chose to make this one exception because using Modified Julian Dates is standard in performing datetime calculations. It would be verging on irony if the method of computing the OpenBSD release table was OpenBSD-specific.

To reduce parsing problems and transmission errors, the table is then encoded in Netstring format:

<length of table data in decimal> : <table data> ,

In Netstring format, the colon is octal byte \072 and the comma is octal byte \054. So to encode the ASCII string "Secure by default" in Perl:

$data = 'Secure by default';
$dlen = length($data);
print $dlen, ':', $data, ',';

The resulting Netstring is:

17:Secure by default,

Obtaining the Compressed OpenBSD Release Version Table

I've assembled the OpenBSD release table into a compressed format and put it into DNS. You may query it by doing a TXT DNS resource record lookup on:

openbsdversion.bze.ro

In dig format, for example:

dig +short openbsdversion.bze.ro txt
"94:c4b5f3b7a9c4a9c4c5a9b6b7a9a6b5b8b6b8c7a6b5b8b5b8b6b8b5aad5a6b5b8b6b8b5b8b5b8b5aaa39cdeb5afc7bc,"

If using djbdns:

dnstxt openbsdversion.bze.ro
94:c4b5f3b7a9c4a9c4c5a9b6b7a9a6b5b8b6b8c7a6b5b8b5b8b6b8b5aad5a6b5b8b6b8b5b8b5b8b5aaa39cdeb5afc7bc,

Reading the Compressed OpenBSD Release Version Table

The method of reading this table is simple.
  1. The first date is the first four hexadecimal characters — representing two bytes — of the table data, expressed as a Modified Julian Date. This is the date of release for OpenBSD version "2.0".
  2. Each subsequent two hexadecimal characters — one byte — of table data is the days to add to the previous release date to obtain the next release date. To compute the release version add 0.1 to the previous release version.

I have written a Perl script to demonstrate how to extract the information in this table after fetching it from DNS. This script requires the Net::DNS Perl module.

The output of this script should look like this:

$ ./openbsd-release-version.pl
1996-10-01 2.0
1997-06-01 2.1
1997-12-01 2.2
1998-05-19 2.3
1998-12-01 2.4
1999-05-19 2.5
1999-12-01 2.6
2000-06-15 2.7
2000-12-01 2.8
2001-06-01 2.9
2001-12-01 3.0
2002-05-19 3.1
...[snip]...
2014-11-01 5.6
2015-05-01 5.7
2015-10-18 5.8
2016-03-29 5.9
2016-09-01 6.0
2017-04-11 6.1
2017-10-09 6.2
2018-04-02 6.3
2018-10-18 6.4
2019-04-24 6.5

Each line of output is a date in YYYY-MM-DD format, followed by a three-character, two-digit decimal version string in an "A.B" format.

It would be easy enough to extract this table as a pre-sorted array of dates and compute the latest OpenBSD release for any given date by comparing it with entries in the array and determining the version based on the index of the date in the array where it falls chronologically. The array index is assumed to be zero-based.

For example, a date of "2005-01-01" comes after entry #16 (2004-11-01), but before entry #17 (2005-05-19), so the latest OpenBSD release available as of 2005-01-01 was (2.0 + 0.1 * 16), or version "3.6".

I look forward to seeing many, many more OpenBSD releases added to this list.