Revealer.cc — Deepdive and warning

Alan Evans
7 min readAug 18, 2018

--

Revealer is a physical tool for hiding your secrets, such as a mnemonic.

You can order the physical card (or print your own) and then you print your own secrets at home by entering a code on the front on the revealer.

The source code is available here:

I’ve been reviewing the source code and here’s a deep dive in to how it all works.

Revealer noise generation

The code on the front of the physical revealer is a hex number.

e.g:

0874 D129 F482 0E65 C819 87B5 2425 9409 1A01

The last 3 digits being the first 3 of a sha-256 checksum, used to ensure you typed it in correctly. The first is a version number, currently 0. The rest is treated as a number which is used to seed pythons random number generator random.seed(self.noise_seed).

So a revealer code is:

<0: version><noise_seed: 16 hex digits><checksum: 3 hex digits>

That’s 16 x 8 = 128 bits of entropy in the revealer code being fed to the random seed, 0x874D129F4820E65C81987B5242594091 in our case.

Then the random number generator is used to generate a field of random noise by calling randInt(0, 1) and drawing either black or white on a bitmap of 159 x 97 pixels.

Raw random noise

It doesn’t look like a revealer though, with large patches of black and white, it is not very even. In order to produce a printable revealer, or secret for that matter, it needs a bit of post processing.

Producing a printable output

Each pixel is converted to one of two patterns:

Pixel patterns

Now applying that to the 159 x 97 image, we get an image that’s twice the size but that has a perfectly even distribution of black and white pixels.

Random but even noise

This will enable the magic to happen when you overlay revealers and secrets as opposing pixel patterns produce black, and same pixel patterns produce 50% grey.

Secret generation

A secret is any 159 x 97, 1-bit bitmap that is XORed with the random noise of the seed.

My secret
XORed with Revealer

Then the same process is applied to get the even, magic pattern.

My dolphin secret

Viewing the secret

Now when I hold the revealer over the secret, I can make out the original image.

Revealer held over the secret

This works because where the secret image was white, the pattern was kept the same as the revealer. And where it was black the pattern was reversed and so white -> 50% grey, black -> black.

Here comes the issue and warning, to repeat “where the secret image was white, the pattern was kept the same as the revealer.” i.e.

Most of your secret is the same as your revealer.

Warning: Never reuse a revealer

Here’s a new secret mnemonic created with the same revealer as my dolphin pic:

New secret

So suppose I have my dolphin secret kept along side a mnemonic, keeping them away from the revealer I’m pretty confident my secrets are safe.

However, if I held the two secrets over a bright light source:

Or if I XOR the two secrets, I can see even clearer:

Exclusion layer mode

Both methods are very clear and readable because the words don’t clash but if you actually reused a revealer for two 12 word secrets, you might only reveal this:

(The following examples are generated directly from PNGs from the Revealer Electrum plugin)

2 x 12 word secrets XORed

This isn’t as readable, but it would not be hard to brute force both sets of 12 words, given the known result of their XOR, and a lot of clues:

  • the pixel patterns of all 2048 words is easy to produce
  • the widths of those patterns would exclude a lot of combinations
  • each line is a separate puzzle of up to 4 words

If you used differing word counts it would be even easier as they don’t line up:

12 and 24 word secrets XORed

Or if you just used the revealer for a short “Hello, World!” one liner, experiment type secret:

One liner experiment XORed with a 12 word mnemonic

Random security

Note: This section caused revealer to change their random number generator choice.

Despite getting someone with experience in hacking MT to weigh in on the debate who said “when starting with a small seed (small entropy in comparison to MTs 19936 bit seed) MT takes a long time to “warm up” and start actually producing random numbers. There will be easily identifiable patterns in the output.” revealer.cc never agreed it was possible to hack the original V0 card, they also declined my suggestion to put some funds on the line and let people try to hack it.

Revealer say in the V1 of revealer, they use a secure random number generator, HMAC_DRBG (SHA-512), though I have not had time to review this.

So this section only applies to the older V0 which people will still have, this cannot be solved by the electrum software update and reprinting, you need the V1 physical revealer card.

Revealer uses the regular Mersenne Twister random of python. It does use all 128 bits of entropy, but the documentation page does carry the warning:

The pseudo-random generators of this module should not be used for security purposes.

Not to scaremonger, but the question I have is, given at least the first 24 columns of a secret are the same as the revealer, and so we know at least the first 24 x 97, 0,1 random integer outputs it is producing, could someone determine enough information about the initial state of the random generator to reproduce the rest of the sequence, and thus reproduce the whole revealer from a single secret?

Edit: Previously this section referred to knowing the first rows, but the algorithm fills the random seed column first.

The answer to my question is that you need 624 x 32 bit results to know the exact state of the twister, but how much improvement can be made on the 2¹²⁸ brute force given those 2,328 bits of output and the plenty of other known places elsewhere on the secret that can be assumed to match the revealer, other margins for example, and in between sentences. I’d be surprised if none of these clues can simplify the brute force at all.

For a 12 word seed, approximate area safe to assume directly matching revealer and so random output. 10,143 / 15,423 pixels. Colour bands highlight the 624 value period of the random number generator.

There is no question that Mersenne Twister is not a cryptographically secure random, it isn’t. The question is:

Does a secret leak enough information to weaken the security of revealer below 128-bits, and if so, by how much?

If you’re not convinced there is a case to answer here, here is some essential reading on MT Cracking Random Number Generators Part 3 and Part 4. Note that all operations in MT are reversible, it is not a one way hash function in anyway.

I would have been much happier to see HMAC-SHA512 in use as the pseudo-random function, just like it is in BIP32 HD wallets.

Conclusion

First a disclaimer, I don’t own a revealer, so I’m unaware of any warnings you get if you actually order one, however the wording on the dialog seems to encourage reuse.

Edit: Revealer does mention possible attacks stemming from reuse on the FAQ and agree that below wording could be improved.

“And” suggests reuse is OK

Revealer appears pretty robust for one time use, but it could give you a false sense of security if you don’t know about the risks of reuse.

But I think I would like to get an answer about the random implementation used before I actually entrusted my mnemonics to it.

--

--

Alan Evans

British Canadian Software Developer living in Connecticut, Staff Android Engineer at The New York Times