I wouldn't worry too much.... but here's some more detailed info.
TL;DR: just read the bolded parts.Armory's KDF & encryption for SecurePrint looks approximately like this:
(
in short, it generates a random SecurePrint code, runs that through a KDF with good
key stretching, and then encrypts your master private key.)
constant_pi = SHA-256(SHA-256('3.1415926...')) # an arbitrary constant
constant_e = SHA-256(SHA-256('2.7182818...')) # an arbitrary constant
Step 1/3: create the SecurePrint code from the master privkey and chaincode
--------
ext_master_privkey = master_privkey + chaincode
# ext_master_privkey is now 64 bytes long and has
# 32 bytes of entropy (the chaincode contributes zero entropy)
code = PRF(key=ext_master_privkey, message=constant_e)
# where PRF is similar to HMAC
SHA-512; it requires 2 SHA-512 computations.
code = code[0..6] # truncate to 7 bytes long
code = code + SHA-256(SHA-256(code))[0] # append a single check byte
code = base58_encode(code)
# code now has
7 bytes of entropy (not 8 as the code comment says inside Armory)
Step 2/3: key stretching
--------
key = KDF(passphrase=code, salt=constant_e)
# where KDF is similar to ROMix
SHA-512 using ROMix
# (a memory-hard mixing function) as defined in the scrypt paper;
# it's hardcoded here to use 2
24 bytes (16 MiB) of RAM
# and require 2
24 / 64 * 1.5 = 393216 SHA-512 computations.
Step 3/3: encryption
--------
encrypted_privkey = CBC-Mode-Encrypt
AES-256(encryption_key=key, salt=constant_pi[0..15], plaintext=master_privkey)
So, brute-forcing a SecurePrint code would take, on average, about:
2
24 / 64 * 1.5 * 2
7*8 / 2 =
1.5 * 273 ≈ 1.4 * 1022 Total SHA-512 operationsWhat you'd probably like to do next is use these results to approximate how long / how much $ it would take to brute-force a SecurePrint code. Unfortunately, that's really hard (for me, anyways) to approximate.
You can't compare Armory's KDF to SHA-512 (Bitcoin) ASICs because Armory requires 16MiB of memory to do its work. Comparing Armory's KDF to scrypt (e.g. Litecoin) ASICs might seem better, but... Litecoin uses 128x less memory than Armory's KDF. It also uses the much faster Salsa20/8 hashing algorithm, somewhere around 15x faster per block (but it uses more of them). Finally, Armory doesn't use the full scrypt... it uses a portion of scrypt (ROMix), and a modified portion at that.
To make things
even more complicated, you
can tweak the numbers above via a space/time tradeoff according to this formula:
table_size = 2
24 / 64 # number of hashes stored in RAM when no space/time tradeoff is used
mem_required = 2
24-n where n is in 0..18 (mem_required is in bytes)
SHA-512 operations per guess = table_size + 0.5 * table_size * (1 + (2
n-1) / 2)
For the sake of a comparison to the Bitcoin network, let's pretend we can build an Armory KDF ASIC that holds just 16 hashes (has 1024 bytes of memory per parallel attempt, so not much more than 0), you set n=14, and get:
SHA-512 operations per guess = table_size + 0.5 * table_size * (1 + (2
14-1) / 2) = 262144 + 131072 * (1 + 8,191.5) = 1074069504
SHA-512 operations to brute-force = 1074069504 * 2
7*8 / 2 ≈ 2
86 ≈ 7.7 * 10
25If we apply the
entire Bitcoin network's hash capability of 3.5 * 10
17 H/s to this problem (and it's all been magically converted to Armory ASICs, and we assume that SHA-256d costs roughly the same to calculate as SHA-512), we get:
7.7 * 10
25 H / 3.5 * 10
17 H/s ≈ 2.2 * 10
8 s ≈
23 years (on average)Of course, maybe we can build ASICs with more RAM than this and speed things up, but presumable there'd be a point of diminishing returns; I really have absolutely no estimates on that topic, and
my guesses on time/spcace tradeoffs in ASICs are probably wildly off.
All of the above is missing several important assumptions: 1. The encrypted printout was stolen (e.g. by a snooping printer), but the SecurePrint code was not.
2. Either:
a. The encrypted wallet file was also stolen (we need access to the chaincode, which is always stored unencrypted, to figure out if a guess is correct), or
b. We can do EC scalar multiplications and address lookups in the blockchain at the same order of speed as the code guess rate (3.3 * 10
8/s in this wild example), which is yet another
very tall order; figure around 10,000 32-core servers, or additional specialized ASICs.
The reason for (2) above is that there's no way, just by looking at a decrypted private key, if the guess we tried for the code is correct unless we have something to check it against (either the chaincode or the blockchain, and the latter would require EC math which is also hard). Kudos to the Armory devs for not using any padding during private key encryption, which is different from every other wallet I've ever looked it.
I, for one, am not particularly concerned (but of course, keep your backups safe and offline!
)