Analysis of the GitLab Ciphercube

Shortly after I joined GitLab in Sep 2022, GitLab’s security team opened up a Swag initiative for a “laser-engraved cube made from sustainably sourced wood with a message on each side”.
Up for such an offer and challenge, I subscribed and received on the 7th Dec 2022 a small wooden cube.

CipherCube Portrait

In my lunch break today, 8th Dec 2022, I started to decipher the cube.
On first glance 1 side showed the GitLab Tanuki and 5 sides had small shields standing out of the engraving in patterns.

CipherCube Shield Side

I inspected those that the shields were structured on each of the five sides in an 8x8 grid, with the shield giving a clear orientation where the bottom of the grid should be. 8x8 on a side, so one byte per row, 8 byte per side.

CipherCube Fold

Next I translated the patterns into binary for all the sides as well as into decimal side, leaving us with 8 binary/decimal numbers per side.

Dice sideRowBinaryDecimal
Up111111111255
Up20000110113
Up3000001015
Up4000001015
Up5000001117
Up6000001004
Up70001000117
Up811111111255
----
left111111111255
left2000001015
left30001000117
left40000111115
left5000001117
left6000001004
left70001000117
left811111111255
----
down111111111255
down20001001119
down30001010121
down4000001106
down50001100024
down60000110113
down70000111014
down811111111255
----
bottom1000000102
bottom2000001004
bottom3000000011
bottom4000001106
bottom50001000117
bottom60000111115
bottom7000001106
bottom811111111255
----
right1
right2
right3
right4
right5
right6
right7
right8

Note: The “Down” and “Bottom” data above was decoded from close-up photos using image analysis. The side names are tentative, I’ll correct them once I re-examine the physical cube. The “Right” side remains unread from the available photos.

The cipher

The raw decimal values looked odd at first. No printable ASCII characters. But looking at the first two sides together, the pattern clicked.

Each byte encodes a letter position (A=0, B=1, … Z=25), shifted by 13, ROT13. The decoding formula: take the byte value, add 13, modulo 26, map to a letter.

Byte 13 → (13+13) % 26 = 0 → A
Byte 5 → (5+13) % 26 = 18 → S

Applied to the Up side (inner rows): 13, 5, 5, 7, 4, 17 → A-S-S-U-R-E
Left side: 5, 17, 15, 7, 4, 17 → S-E-C-U-R-E

The border rows (all shields, value 255) also decode: (255+13) % 26 = 8 → I. The full row of shields that frames each side doubles as the word “I” in the message.

CipherCube GITLAB side

Applying the same decode to the remaining sides:

SideBytes (inner rows)Decoded
Up13, 5, 5, 7, 4, 17ASSURE
Left5, 17, 15, 7, 4, 17SECURE
Down19, 21, 6, 24, 13, 14GITLAB
Bottom2, 4, 1, 6, 17, 15, 6PROTECT
Right?unread

The “Bottom” side is special: it has 7 data rows instead of 6, with only one border row at the bottom. Row 1 starts directly with P (byte 2), no leading “I” border.

CipherCube PROTECT side

The message

Reading the full 8-byte sequence per side, borders included:

I ASSURE I · I SECURE I · PROTECT I · I GITLAB I · I ??? I

The border rows encode “I”, making each side read as a pledge: “I ASSURE”, “I SECURE”, “I PROTECT”, “I GITLAB” (as in “I am GitLab” or “I represent GitLab”).

CipherCube 3D overview

One side’s word is still missing. Given the security theme, candidates like DEFEND, DETECT, or SHIELD would fit. The encoding for each:

  • DEFEND → bytes: 16, 17, 18, 17, 0, 16
  • DETECT → bytes: 16, 17, 6, 17, 15, 6
  • SHIELD → bytes: 5, 20, 21, 17, 24, 16

I’ll need the physical cube in hand to read that last side and complete the message.

CipherCube Tanuki side

The sixth face carries the GitLab Tanuki, no cipher. Just the logo, watching over the pledge engraved into the other five sides.