Pages:
Author

Topic: Password-protected private key export format - page 2. (Read 7550 times)

full member
Activity: 140
Merit: 430
Firstbits: 1samr7
The difference between an 8 byte salt and a 4 byte salt isn't 4 bytes, and it isn't a factor of 2.  It is a factor of over 4 billion.  It is the difference between "Got a minute?" and "Sometime after the end of the universe".

In reality, it is probably not that big of a deal.  Precomputing AES tables isn't a trivial task, and even 4 or 5 bits of salt is probably enough to make it impractical for well beyond the potential lifetime of one of these protected keys.

But cryptosystems are designed with defense in depth for very good reasons.  When a weakness in one part is discovered, it is usually not a catastrophe because we have added redundant security measures.

You make some very good points about this.

In terms of the purpose of the salt, what would be the usage model for any sort of precomputed table for this problem?  It seems like it would be limited to creating a list of AES keys derived from possible passwords, each of which would have to be tested, still requiring exponential time in the length of the password, but allowing the attacker to circumvent running the expensive PBKDF function for each test.  Is there something sneaker and more devious that an attacker could do with this particular problem?  I don't think this is like trying to build a table for a hash function, where the outputs depend on only the password and the salt, in which case the attacker can index the outputs to the inputs and build a true lookup table or a rainbow table.

Anyway, if that's true, then at 48 bytes per AES key + IV, and 2^32 salt variations of each, the attacker would need to be able to store 192GB per precomputed password.  At least, without some clever way of compressing the keys.  That is a lot easier than 2^64 variants of each, but still seems intractable.

The tradeoff to having four extra bytes of hash would be six extra characters in the encoded output.  Example side-by-side is below:

PAYxy8B9zhSGpbyC7Z29KRS7rsPzAcTF3zSPmyAigbAJYamt9GmNwsFT9E95dGfp6Ri
3W3qzi2Sow4o7LP7jwwnrmqiiGxyMcwLdubKxYhNy4J63eigN3jz7avYqSWfGwxz3nMDDXwwc


Edit: It's possible, using a smaller value for the type byte, to reduce it to five extra characters.

Your point about weakness mitigation is well taken though, and the tradeoff may be worth it.

EDIT: I would even recommend that fewer bits went into the password check.  Like, maybe as few as 8.  This will make it even more expensive to brute force passwords, as it will result in an enormous number of false positives that can only be checked by doing a time-consuming EC multiply (to derive the bitcoin address) and a database lookup (to see if there are any coins at that address, only to find there are none).  In the event of a typo, even with 8 bits, still a >99% chance it will still be caught.  The password check as it stands is probably just a gift to an attacker.

The PBKDF function here requires 8192 rounds of the SHA256 hash function, so it should already be a lot more expensive than computing the bitcoin address and looking it up in a list.  Even if the attacker was using a table, having to do an EC operation as often as every 2^8 keys probably won't cause a big slowdown.  But, it will make the password cracker a bit more complicated.

For the one person who gets a letter of their password wrong, and is unlucky enough to have it silently accepted and mapped to the wrong account, and either the bitcoin address isn't known or isn't compared to the expected value, they better hope the password-protected version of their key wasn't discarded.
vip
Activity: 1386
Merit: 1140
The Casascius 1oz 10BTC Silver Round (w/ Gold B)
The difference between an 8 byte salt and a 4 byte salt isn't 4 bytes, and it isn't a factor of 2.  It is a factor of over 4 billion.  It is the difference between "Got a minute?" and "Sometime after the end of the universe".

In reality, it is probably not that big of a deal.  Precomputing AES tables isn't a trivial task, and even 4 or 5 bits of salt is probably enough to make it impractical for well beyond the potential lifetime of one of these protected keys.

But cryptosystems are designed with defense in depth for very good reasons.  When a weakness in one part is discovered, it is usually not a catastrophe because we have added redundant security measures.

So, the question is:  Is saving a few keystrokes, maybe several times per year for maybe a couple billion users, worth the slight risk that an AES break won't be "phew, gotta upgrade", but instead be "All your addresses are belong to us"?  I would vote for the extra keystrokes, but that might just be me.

By the way, this is a nifty idea.  One thought that came to mind was embedding metadata into the key itself using DER.  Since these keys are not usable on the network, they should maybe not be assigned a key version prefix.

I think this argument sort of sidesteps the purpose of salt however.  The salt is not secret - it's coded plainly into the encrypted key and freely available to an attacker - so beyond a certain point, it doesn't really matter how many bits it is.  The whole point of salt if I understand correctly is just to make it infeasible to crack a whole batch at the same time with the same effort (such as with a pre-generated rainbow table), forcing the attacker to concentrate on cracking one key at a time.  If I am right, then the salt needs only enough bits so as to be unlikely to collide with other codes that might be acquired by the same attacker and cracked within the same batch.

EDIT: I would even recommend that fewer bits went into the password check.  Like, maybe as few as 8.  This will make it even more expensive to brute force passwords, as it will result in an enormous number of false positives that can only be checked by doing a time-consuming EC multiply (to derive the bitcoin address) and a database lookup (to see if there are any coins at that address, only to find there are none).  In the event of a typo, even with 8 bits, still a >99% chance it will still be caught.  The password check as it stands is probably just a gift to an attacker.
full member
Activity: 140
Merit: 430
Firstbits: 1samr7
Hm, in this case the overall length isn't as important as having visual breaks. Think about the way Microsoft prints its product keys that people have to type in to activate Windows.

This is a very good point.  Segmentation like this is something that regular bitcoin addresses could benefit from as well -- typing in 34 characters is difficult enough.

So, the password-protected key above could look something like:

PAYxy8-B9zhSG-pbyC7Z-29KRS7-rsPzAc-TF3zSP-myAigb-AJYamt-9GmNws-FT9E95-dGfp6Ri

And the address:

1Hackn-YhLRpt-tqGAiB-ew1fQA-KRGnMo-rkxk

The breaks sure make it easy to remember where you left off, and they're a heck of a lot easier to transcribe to and from paper, or between screens!  But, the password-protected key now only barely fits into an 80-column terminal, and with anything prefixed at the beginning of the line, it will overflow.   Sad

Maybe this idea deserves its own thread.
kjj
legendary
Activity: 1302
Merit: 1026
The difference between an 8 byte salt and a 4 byte salt isn't 4 bytes, and it isn't a factor of 2.  It is a factor of over 4 billion.  It is the difference between "Got a minute?" and "Sometime after the end of the universe".

In reality, it is probably not that big of a deal.  Precomputing AES tables isn't a trivial task, and even 4 or 5 bits of salt is probably enough to make it impractical for well beyond the potential lifetime of one of these protected keys.

But cryptosystems are designed with defense in depth for very good reasons.  When a weakness in one part is discovered, it is usually not a catastrophe because we have added redundant security measures.

So, the question is:  Is saving a few keystrokes, maybe several times per year for maybe a couple billion users, worth the slight risk that an AES break won't be "phew, gotta upgrade", but instead be "All your addresses are belong to us"?  I would vote for the extra keystrokes, but that might just be me.

By the way, this is a nifty idea.  One thought that came to mind was embedding metadata into the key itself using DER.  Since these keys are not usable on the network, they should maybe not be assigned a key version prefix.
hero member
Activity: 588
Merit: 500
NPHard. Funny.

My first question is, why are you bothering to save 4 bytes here and there? It's not like we're running out of space on our floppy drives?

It's useful if you need to type the private key into an import dialog. For example you may keep your private keys in a  paper wallet.

Hm, in this case the overall length isn't as important as having visual breaks. Think about the way Microsoft prints its product keys that people have to type in to activate Windows.
sr. member
Activity: 262
Merit: 250
NPHard. Funny.

My first question is, why are you bothering to save 4 bytes here and there? It's not like we're running out of space on our floppy drives?

It's useful if you need to type the private key into an import dialog. For example you may keep your private keys in a  paper wallet.
hero member
Activity: 588
Merit: 500
NPHard. Funny.

My first question is, why are you bothering to save 4 bytes here and there? It's not like we're running out of space on our floppy drives?
full member
Activity: 140
Merit: 430
Firstbits: 1samr7
Private keys in base-58 export format are great for moving between bitcoin wallets, or for safe keeping outside of wallet.dat.  They are currently used by several 3rd party systems, including BitBills, vanitygen, and pywallet.  However, they are very vulnerable to being leaked or stolen if certain security measures aren't taken.  While the built-in bitcoin wallet.dat encryption is able to encrypt private keys stored in wallet.dat, this protection does not extend to exported keys.  The format proposed here is an extension to the base-58 export format with integrated password protection.

This format uses strong encryption and multi-iteration password-based key derivation to encrypt a bitcoin private key.  As with any format of this type, the password can be cracked.  However, by current standards, it is very difficult, of similar difficulty to cracking passwords for WiFi WPA2-PSK.  Complex passwords with no dictionary words of at least nine characters will require more than three years to crack for a very resourceful attacker.

Updated: The current scheme is as follows.  Thanks go to pixelglow for smart suggestions.

privkey = 32-byte EC private key, big-endian form
param = Parameter descriptor byte.
  • 0: Brief format -- unpadded cipher, 4-byte salt, HMAC-SHA256 password check value
    • pbhash = HMAC-SHA256
    • pbiter = 4096
    • cipher = AES-256-CBC
  • 16: PKCS#7-compliant format -- padded cipher, 8-byte salt
    • pbhash = HMAC-SHA256
    • pbiter = 4096
    • cipher = AES-256-CBC

For Brief format:
salt = 4-byte random value
key = PBKDF2(password, salt, pbhash, iter)
(key is then split into cipherkeyiv and hmackey.  hmackey is 16 bytes long.)
pwcheck = HMAC-SHA256(hmackey, privkey)
protkey = param | cipher(privkey, cipherkeyiv, unpadded) | pwcheck[0:64] | salt
Result is 45 bytes for most ciphers

For PKCS#7-compliant format:
salt = 8-byte random value
cipherkey = PBKDF2(password, salt, pbhash, iter)
protkey = param | cipher(privkey, cipherkey) | salt
Result is 58 bytes for most ciphers

The protkey value then has a data class byte (32 or 79) prefixed to the beginning, and is base-58 encoded as per the bitcoin function EncodeBase58Check().  Below is an example of a private key password-protected using this scheme, with a test password password1:

Address: 1HacknYhLRpttqGAiBew1fQAKRGnMorkxk
Privkey: 5JmsuCoDBDTPT8oBxx1Vv4cy9Fw14456rt7hvMKmvQY1DES2w6z

Protkey: PsQg61gLtNX6bg7PG8Kw9bpdFEfuP8h1Ri8dAoBjRpV1i1rPPx72EYrGgi7CfWWkbutH
Protkey: 2uhT7zkgeGXpVEw4aYTnCyAvTQ9G1hqSGPPNS5EpC4W62J28euHT8o9CQnKrZCqYwhcHgrxBASsWzYT bF3Qcx
(Careful: the long form above has an extra space added by the forum)

The Brief representation makes some specific compromises to maintain a short representation.  In this mode, the block cipher is performed with no PKCS#7 padding at the end.  Since the padding provides a way to verify the correctness of the password, the HMAC value pwcheck is computed and added to the output.  The pwcheck value is computed using the HMAC-SHA256 hash of the plaintext private key, using 16 bytes of key material taken from the PBKDF2 function as the message key.  This saves a few bytes in the final representation.  The salt value is also only four bytes long, shorter than the recommended eight.

This scheme, unless somebody can poke a big hole in it or recommend a useful change, will show up as an optional output format in the next release of vanitygen, along with a password codec.

A JavaScript example of the password codec is available here.  It does run in your browser but does not transmit keys or passwords over the network.

Bounty: I don't have many bitcoins to throw at this, but 5 BTC go to the first person who can remove the protection from the key below:

1NPHardFPnejsycvne2gvirm5MCr9gnAbf
PsV8XM6n5FvjonHPwwjzqvzA6UXruAAJfQ5VwbXFCJrSQEiQ4gyNNDhuYfL8JUBt6mxt


To sweeten the deal: the password is 14 characters long, with upper and lower case letters only, and contains at least one dictionary word.
Pages:
Jump to: