Bitcoin Forum
May 09, 2024, 02:34:15 PM *
News: Latest Bitcoin Core release: 27.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: 1 2 [All]
  Print  
Author Topic: Password-protected private key export format  (Read 7395 times)
samr7 (OP)
Full Member
***
Offline Offline

Activity: 140
Merit: 430

Firstbits: 1samr7


View Profile
August 10, 2011, 04:10:03 PM
Last edit: August 17, 2011, 06:57:46 PM by samr7
 #1

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.
The block chain is the main innovation of Bitcoin. It is the first distributed timestamping system.
Advertised sites are not endorsed by the Bitcoin Forum. They may be unsafe, untrustworthy, or illegal in your jurisdiction.
1715265255
Hero Member
*
Offline Offline

Posts: 1715265255

View Profile Personal Message (Offline)

Ignore
1715265255
Reply with quote  #2

1715265255
Report to moderator
error
Hero Member
*****
Offline Offline

Activity: 588
Merit: 500



View Profile
August 10, 2011, 06:32:58 PM
 #2

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?

3KzNGwzRZ6SimWuFAgh4TnXzHpruHMZmV8
dogisland
Sr. Member
****
Offline Offline

Activity: 262
Merit: 250



View Profile
August 10, 2011, 07:20:21 PM
 #3

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.
error
Hero Member
*****
Offline Offline

Activity: 588
Merit: 500



View Profile
August 10, 2011, 07:31:49 PM
 #4

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.

3KzNGwzRZ6SimWuFAgh4TnXzHpruHMZmV8
kjj
Legendary
*
Offline Offline

Activity: 1302
Merit: 1025



View Profile
August 10, 2011, 07:44:59 PM
 #5

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.

17Np17BSrpnHCZ2pgtiMNnhjnsWJ2TMqq8
I routinely ignore posters with paid advertising in their sigs.  You should too.
samr7 (OP)
Full Member
***
Offline Offline

Activity: 140
Merit: 430

Firstbits: 1samr7


View Profile
August 10, 2011, 07:54:41 PM
 #6

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.
casascius
Mike Caldwell
VIP
Legendary
*
Offline Offline

Activity: 1386
Merit: 1136


The Casascius 1oz 10BTC Silver Round (w/ Gold B)


View Profile WWW
August 10, 2011, 07:56:16 PM
 #7

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.

Companies claiming they got hacked and lost your coins sounds like fraud so perfect it could be called fashionable.  I never believe them.  If I ever experience the misfortune of a real intrusion, I declare I have been honest about the way I have managed the keys in Casascius Coins.  I maintain no ability to recover or reproduce the keys, not even under limitless duress or total intrusion.  Remember that trusting strangers with your coins without any recourse is, as a matter of principle, not a best practice.  Don't keep coins online. Use paper or hardware wallets instead.
samr7 (OP)
Full Member
***
Offline Offline

Activity: 140
Merit: 430

Firstbits: 1samr7


View Profile
August 10, 2011, 09:05:24 PM
Last edit: August 10, 2011, 09:47:07 PM by samr7
 #8

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.
pixelglow
Newbie
*
Offline Offline

Activity: 26
Merit: 0


View Profile
August 11, 2011, 02:01:40 AM
 #9

The scheme that I proposed to dogisland of the StrongCoin project is as follows.

privkey = 32-byte EC private key, big-endian form
salt = 4-byte random salt value
symkey = PBKDF(password, salt, SHA256, 4096)
pwcheck = SHA256(SHA256(privkey | salt))
protkey = AES-CBC-256(privkey, symkey) | pwcheck[0:64] | salt

The 44-byte protkey value then has a type byte (136) prefixed to the beginning, and is base-58 encoded as per the bitcoin function EncodeBase58Check().  

Interesting, although your layering/composing of different cryptographic schemes may come back to bite you.

As I see it, you want to encrypt the password and also verify that the encrypted bits are unchanged. Here's a slight modification of the above:

privkey = 32-byte EC private key, big-endian form
salt = 4-byte random salt value
symkey = PBKDF2(HMAC-SHA1, password, salt, 4096, 64)
cryptedprivkey = (privkey XOR symkey[0:32])
pwcheck = HMAC-SHA1(symkey[33:64],cryptedprivkey)
protkey = cryptedprivkey | pwcheck | salt

  • You should use the more recent PBKDF2 (http://www.di-mgt.com.au/cryptoKDFs.html), which allows you to produce arbitrary length keys. In this case we're generating a 64-byte length key, half of which we'll use for encryption and the other half for authentication.
  • Next we encrypt the privkey with half the symkey by XOR'ing them together, a simple stream cipher. This avoids the use of AES in client code (slow in Javascript?, missing an initialization vector, plaintext length < AES block size of 128 bits) and should be pretty safe. Stream ciphers like RC4 are simply a random keystream XOR with the plaintext and are difficult to break, provided the random keystream is truly unpredictable and you never repeat the key. The trouble with stream ciphers then manifests when you have a large enough chunk of data to encode, then without a proper CPRNG like RC4 the keystream may repeat etc. and thus block ciphers under CBC become useful. Since we have a 32-byte private key and a 32-byte symkey to XOR together, this should not be the issue.
  • We then use a MAC, specifically a HMAC-SHA1, to act as the password check against modification. We use the second half of the symkey as the secret. HMAC's are particularly designed for message authentication.

Alternatively (safer/more paranoid) you could use two salts to produce two 32-byte keys, one for the encryption, one for the authentication.
samr7 (OP)
Full Member
***
Offline Offline

Activity: 140
Merit: 430

Firstbits: 1samr7


View Profile
August 11, 2011, 04:19:24 PM
 #10

Interesting, although your layering/composing of different cryptographic schemes may come back to bite you.

As I see it, you want to encrypt the password and also verify that the encrypted bits are unchanged. Here's a slight modification of the above:

privkey = 32-byte EC private key, big-endian form
salt = 4-byte random salt value
symkey = PBKDF2(HMAC-SHA1, password, salt, 4096, 64)
cryptedprivkey = (privkey XOR symkey[0:32])
pwcheck = HMAC-SHA1(symkey[33:64],cryptedprivkey)
protkey = cryptedprivkey | pwcheck | salt

  • You should use the more recent PBKDF2 (http://www.di-mgt.com.au/cryptoKDFs.html), which allows you to produce arbitrary length keys. In this case we're generating a 64-byte length key, half of which we'll use for encryption and the other half for authentication.
  • Next we encrypt the privkey with half the symkey by XOR'ing them together, a simple stream cipher. This avoids the use of AES in client code (slow in Javascript?, missing an initialization vector, plaintext length < AES block size of 128 bits) and should be pretty safe. Stream ciphers like RC4 are simply a random keystream XOR with the plaintext and are difficult to break, provided the random keystream is truly unpredictable and you never repeat the key. The trouble with stream ciphers then manifests when you have a large enough chunk of data to encode, then without a proper CPRNG like RC4 the keystream may repeat etc. and thus block ciphers under CBC become useful. Since we have a 32-byte private key and a 32-byte symkey to XOR together, this should not be the issue.
  • We then use a MAC, specifically a HMAC-SHA1, to act as the password check against modification. We use the second half of the symkey as the secret. HMAC's are particularly designed for message authentication.

Alternatively (safer/more paranoid) you could use two salts to produce two 32-byte keys, one for the encryption, one for the authentication.


Excellent!!

Pixelglow, you really seem to know your stuff, are you a pro??

The PBKDF that I've been using isn't exactly PKCS#5 v1, it's actually OpenSSL's EVP_BytesToKey().  It's similar but will produce arbitrarily large key material.  Otherwise, there certainly wouldn't be enough key material for an AES IV using SHA256.

Regardless, using PBKDF2 with an HMAC function would seem to be a very desirable change, and also appears to be very easy to get with OpenSSL.

Regarding pwcheck, the use of an HMAC function there with some additional key material also seems like the appropriate tool for the job, more so than my hacked-together mess.  Can't really say no to that either.

Would you recommend HMAC-SHA1 over HMAC-SHA256?

Somebody smart said that a work is finished not when everything that can be added to it has been added, but when everything that can be removed has been removed.  AES may fit into this category.  However, other similar password-protection schemes, including bitcoin's built-in wallet encryption, also use AES for fixed-size private keys, and it's nerve-racking to replace it with XOR, even though it may be the correct thing to do.

Thank you very much!!
pixelglow
Newbie
*
Offline Offline

Activity: 26
Merit: 0


View Profile
August 12, 2011, 02:17:19 AM
 #11

Pixelglow, you really seem to know your stuff, are you a pro??

I'm only a regular programmer with a math background, hence the interest in cryptography. I once worked on a font DRM scheme which used crypto, and I'm currently working on an Australian bitcoin exchange that will feature crypto as well. The rest of it is just google  Wink.

Quote
The PBKDF that I've been using isn't exactly PKCS#5 v1, it's actually OpenSSL's EVP_BytesToKey().  It's similar but will produce arbitrarily large key material.  Otherwise, there certainly wouldn't be enough key material for an AES IV using SHA256.

http://www.openssl.org/docs/crypto/EVP_BytesToKey.html seems to indicate you should move to PKCS#5 v2.0, which is PBKDF2.

Quote
Regardless, using PBKDF2 with an HMAC function would seem to be a very desirable change, and also appears to be very easy to get with OpenSSL.

Indeed, there is the PKCS5_PBKDF2_HMAC_SHA1 function in OpenSSL. I just submitted a patch to node.js to incorporate this: https://github.com/joyent/node/pull/1491

Quote
Regarding pwcheck, the use of an HMAC function there with some additional key material also seems like the appropriate tool for the job, more so than my hacked-together mess.  Can't really say no to that either.

Would you recommend HMAC-SHA1 over HMAC-SHA256?

RFC 2104 (http://tools.ietf.org/html/rfc2104) seems to think that any half-decent cryptohash will do, even MD5 (!), since HMAC has a secret key which greatly expands the amount of computation you need to do to break it. I suggested HMAC-SHA1 since the PBKDF2 in OpenSSL is based on it (symmetry!), it seems to be used often enough (newer schemes have less cryptanalysis done, could have flaws you don't anticipate esp. used in tandem like with HMAC) and produces a smaller output.

Quote
Somebody smart said that a work is finished not when everything that can be added to it has been added, but when everything that can be removed has been removed.  AES may fit into this category.  However, other similar password-protection schemes, including bitcoin's built-in wallet encryption, also use AES for fixed-size private keys, and it's nerve-racking to replace it with XOR, even though it may be the correct thing to do.

Indeed. Simple schemes work best sometimes in crypto since less can go wrong. As for using XOR, check out wikipedia's article on Vernam ciphers: http://en.wikipedia.org/wiki/Vernam_cipher. TL;DR: so long as the key is truly random, as large as the plaintext and never reused in whole or part, it should be unbreakable. Obviously we can't get the first one 100% since we're starting with a human password and PBKDF2 I think doesn't make any guarantees about the randomness of the result. The second requirement is true. The last requirement should be ensured by the use of random salts.

Does bitcoin encryption use AES with a single key on the entire wallet, or AES on individual keys (without an IV or starting at the same point)? The former is pretty sensible. The latter can be dangerous since you are effectively reusing the key.
Pieter Wuille
Legendary
*
qt
Offline Offline

Activity: 1072
Merit: 1174


View Profile WWW
August 12, 2011, 11:36:38 AM
 #12

I'm not sure what your use case is here. Encrypted dumps of the wallet are obviously useful, but I think they come in two categories:

1) combined with encrypted wallet, just a dump of the encrypted keys therein. This would allow you to export and import dumps without providing a password. I believe this is the safest option, but it would be inherently implementation-specific (the encryption inside bitcoin, as planned for 0.4.0, uses EVP-SHA512 keys derived from a password, used to encrypt a 256-bit master key using AES256-CBC, the wallet keys themselves are encrypted using this master key using AES256-CBC, with the hash of their pubkey as IV).

2) a portable format for exchanging wallets (hopefully between several applications). if the encryption is independent from that of the wallet storage itself, that means exporting requires knowledge of the wallet passphrase + another key to encrypt the dump with. Although there is definitely merit in doing this inside bitcoin itself, i don't think there is anything better than doing a normal wallet dump + GPG/OpenSSL/... encrypting it.

I do Bitcoin stuff.
dogisland
Sr. Member
****
Offline Offline

Activity: 262
Merit: 250



View Profile
August 12, 2011, 01:10:49 PM
 #13

I'm not sure what your use case is here. Encrypted dumps of the wallet are obviously useful, but I think they come in two categories:



It's not about dumping the wallet but a shared scheme for encrypting private keys.

A possible use case

You could generate an offline paper wallet containing a number of public Bitcoin addresses and their corresponding private keys. The private keys will be encrypted with a password only you know but in a standard format.

The main reason to have a standard encryption for the private key is that when you want to redeem coins against those addresses the bitcoin client or an online wallet will be able to decrypt them for you when you give your password.

Also an online wallet could store the encrypted keys for you. You don't have to worry about the service being attacked, the private keys would be worthless without the passwords. (assuming strong passwords).

Hope this helps.
Pieter Wuille
Legendary
*
qt
Offline Offline

Activity: 1072
Merit: 1174


View Profile WWW
August 12, 2011, 01:51:15 PM
 #14

Right, of course. I just generalized things a bit.

If you're talking about private keys, the distiction remains: either you want to export/import what is in an already encrypted wallet (useful for backup), or you want to do encryption separately. The intent here seems to be the second case, which is fine. It just means you'll need to both the wallet passphrase and the key password ready if you want to import.

Also, considering the used version byte, could you follow this thread? I'd say encrypted privkeys form a new data class.

I do Bitcoin stuff.
samr7 (OP)
Full Member
***
Offline Offline

Activity: 140
Merit: 430

Firstbits: 1samr7


View Profile
August 12, 2011, 04:04:23 PM
 #15

If you're talking about private keys, the distiction remains: either you want to export/import what is in an already encrypted wallet (useful for backup), or you want to do encryption separately. The intent here seems to be the second case, which is fine. It just means you'll need to both the wallet passphrase and the key password ready if you want to import.

Absolutely!  The goal is to handle the keying independently and not depend on the master key of a specific encrypted wallet.  So, if it were used for exporting, it wouldn't be as convenient as "backupwallet," and at least one export password would be required.  But it could be implemented to be more convenient than dumpwallet + GPG.

Quote
Also, considering the used version byte, could you follow this thread? I'd say encrypted privkeys form a new data class.

Thanks!  I posted a note to the list and referenced that thread.
Gavin Andresen
Legendary
*
qt
Offline Offline

Activity: 1652
Merit: 2216


Chief Scientist


View Profile WWW
August 12, 2011, 06:28:32 PM
 #16

Two different encryption schemes and/or passwords seems like a bad idea to me, both from a code maintenance/security point of view and from a usability point-of-view.

Can you expand on "private keys in base-58 export format are great for swapping around" -- what's the use case?  Who are you swapping with, and how?

How often do you get the chance to work on a potentially world-changing project?
kjj
Legendary
*
Offline Offline

Activity: 1302
Merit: 1025



View Profile
August 12, 2011, 06:50:23 PM
 #17

We are at the point where outside utilities are starting to need more access to the wallet.  See pywallet, for an example of a very useful tool that I wish I didn't have to shut down my client to use.

One option would be to have the client open the wallet database without a total lock, so that other things could use it at the same time.  In that case, using per-key encryption inside the wallet would be a good thing for sure.

But most of the things that would be enabled by allowing concurrent wallet access could also be done through RPC, if we can just make the right RPC commands.  Oh, and we'll probably need at least a little bit of granularity in the RPC security system.

At any rate, these encrypted single keys would always be useful as a transfer format for moving keys between servers, archiving onto paper or CDs, offline generation, etc.  Whether they are useful as an alternate storage format inside the wallet will depend on whether we go with RPC or unlocking the database.

17Np17BSrpnHCZ2pgtiMNnhjnsWJ2TMqq8
I routinely ignore posters with paid advertising in their sigs.  You should too.
error
Hero Member
*****
Offline Offline

Activity: 588
Merit: 500



View Profile
August 12, 2011, 06:54:09 PM
 #18

I'm convinced at this point that the on-disk wallet format needs to be completely scrapped and redesigned from scratch. Unfortunately, I'm not likely to have the spare time to devote to working on this.

3KzNGwzRZ6SimWuFAgh4TnXzHpruHMZmV8
kinlo
Sr. Member
****
Offline Offline

Activity: 263
Merit: 250


Pool operator of Triplemining.com


View Profile
August 12, 2011, 06:55:44 PM
 #19

If special attention should go to entering the private key by keyboard, making it short and so on, shouldn't the format foresee multiple checksums to detect typing errors instead of the standard hash check provided by EncodeBase58Check?

If you would split the long private key in - let's say -  6 blocks, a checksum per block instead of one on the whole string as EncodeBase58Check does right now, gui's that allow entering the key in parts  would be capable of hilighting the specific part in red that contains a typing error, instead of invalidating the whole input.
samr7 (OP)
Full Member
***
Offline Offline

Activity: 140
Merit: 430

Firstbits: 1samr7


View Profile
August 12, 2011, 07:00:49 PM
 #20

Two different encryption schemes and/or passwords seems like a bad idea to me, both from a code maintenance/security point of view and from a usability point-of-view.

Can you expand on "private keys in base-58 export format are great for swapping around" -- what's the use case?  Who are you swapping with, and how?

Swapping around means storing a private key outside of a wallet.dat, either to be imported into another wallet.dat or another bitcoin client implementation.  It's an interchange format, not a specific wallet implementation.

The format proposed here is an extension that allows an exported private key to be password-protected.  Naturally, if it is to be interchangeable with another bitcoin wallet or another implementation, it shouldn't/can't be protected using the same password or master key as the wallet from which it came (if that wallet is encrypted).  The initial use case is to allow someone to use vanitygen to generate an address, and securely store the private key until it is later imported into a client.

Anyway, I think this proposal is orthogonal to the encryption of wallet.dat.

If you would rather this format follow the same password-derivation and encryption scheme as the master keys of the bitcoin wallet encryption system, that's certainly possible.  They're already quite similar, and until pixelglow came out and recommended PBKDF2, they both used EVP_BytesToKey().  I don't think the use cases between the wallet master key and a password-protected export format are quite the same though.
2112
Legendary
*
Offline Offline

Activity: 2128
Merit: 1068



View Profile
August 12, 2011, 09:08:33 PM
 #21

I'm convinced at this point that the on-disk wallet format needs to be completely scrapped and redesigned from scratch. Unfortunately, I'm not likely to have the spare time to devote to working on this.
I'll say don't give up on that goal. There may be an incremental way to redesign it.

1) Create a separate DB_ENV just for the wallet.
2) Change single B-tree DB into three RecNo DB-s backed by flat-text files. This is quite decently supported by the Berkeley DB, especially because the wallet database is essentially an append-only. The 3 files would be public-keys, private-keys and transaction-log.
3) I see lot of people here proposing public-key operation only for shared/managed hosts. But I have yet to see anyone discussing proper transaction auditing as done by the accountants. This is why I was thinking about 3 separate databases, the 3rd being the one given to the auditors.
4) During the transition (and possibly afterwards) the default client will continue to use Oracle BDB as its only database interface.

This incremental approach could assuage Gavin's worries that the patches/pull would try to surreptitiously change the basic protocol and principles of operation for Bitcoin.

Please comment, critique, criticize or ridicule BIP 2112: https://bitcointalk.org/index.php?topic=54382.0
Long-term mining prognosis: https://bitcointalk.org/index.php?topic=91101.0
samr7 (OP)
Full Member
***
Offline Offline

Activity: 140
Merit: 430

Firstbits: 1samr7


View Profile
August 15, 2011, 07:25:39 PM
 #22

The primary goal of this format is to create something like PKCS#8/RFC5958.  PKCS#8 is the format commonly used to store SSL/TLS private keys, in password-protected form or otherwise.  It is a very flexible format with metadata describing of the type of private key, and the PBKDF parameters and block cipher used to encrypt it.

It's possible to convert an exported bitcoin private key in the base-58 format to and from PKCS#8.  In PKCS#8, the built-in password-protection feature can be applied to the private key.  Also, if a bitcoin key were to be imported into some other non-bitcoin application for whatever reason, perhaps as a signing key, PKCS#8 would be the best bet as an import format.

It's also possible to take a PKCS#8 representation and produce a reduced mapping that assumes certain parameters.  If we assume the use of the SECG 256k1 EC curve, a set of of PBKDF2 parameters, and the use of AES with a specific key size, the representation can be smaller.  Unfortunately, due to RFC5915, there is a practical lower limit on the size of any reduced format.  For bitcoin private keys, this is close to 135 bytes, and it includes numerous details that, for our purposes, aren't necessary or can be assumed.  This is unwieldy large.

The next step of evolution from PKCS#8 might work as follows.

privkey = Minimal EC private key representation, 32 bytes long
param = Parameter descriptor byte.  This is an index into a predefined list of parameter groups, perhaps like:
  • 0: SECG 256k1 key, PBKDF2, HMAC-SHA256, 8-byte salt, 4096 iterations, AES-256-CBC
  • 1: SECG 256k1 key, PBKDF2, HMAC-SHA256, 8-byte salt, 4096 iterations, Camellia-256-CBC
  • 2: SECG 256k1 key, PBKDF2, HMAC-SHA256, 8-byte salt, 16384 iterations, AES-256-CBC
  • etc..

pbhash = Hash function from parameter list
cipher = Block cipher function from parameter list
salt = Random salt value of size described in parameter list
iter = PBKDF iteration count described in parameter list
pbkey = PBKDF2(password, salt, pbhash, iter)
protkey = param | cipher(privkey, pbkey) | salt

This would handle the cryptography in the most conservative, commonly-used way, and should not make anyone nervous, or at least, not any more nervous than PKCS#8.  It would also make the format easily adaptable to new block ciphers and key derivation functions.  The smallest representation using AES would be 58 bytes long, and would convert to an 83-character base-58 string.  This is much more compact than a PKCS#8 interoperable format, but I still think it's too long.
jmarquess
Newbie
*
Offline Offline

Activity: 2
Merit: 0


View Profile
August 15, 2011, 08:45:26 PM
 #23

It's great to see that someone is trying to establish standards in bitcoin community. A while ago I was doing a bit of research regarding the private key export and came to a conclusion that OpenPGP message format would suit this use case perfectly. I encourage you to take look into RFC 4880 if you haven't already, quite a brilliant piece of work. What I advocate is to use the existing, vast system that OpenPGP is and try fitting bitcoin into it.

Advantages to taking this approach are numerous.
  • OpenPGP is a well known and tested standard
  • Security is built in
  • There is room for growth and modifications
  • It's very robust and covers almost all use cases
  • Possible use of existing OpenPGP compliant architecture

There is one great disadvantage, though. With robustness comes complexity - it's not a quick and easy fix to the problem of key sharing but rather a lengthy process of integrating bitcoin into OpenPGP ecosystem.
samr7 (OP)
Full Member
***
Offline Offline

Activity: 140
Merit: 430

Firstbits: 1samr7


View Profile
August 17, 2011, 11:38:16 PM
 #24

It's great to see that someone is trying to establish standards in bitcoin community. A while ago I was doing a bit of research regarding the private key export and came to a conclusion that OpenPGP message format would suit this use case perfectly. I encourage you to take look into RFC 4880 if you haven't already, quite a brilliant piece of work. What I advocate is to use the existing, vast system that OpenPGP is and try fitting bitcoin into it.

Advantages to taking this approach are numerous.
  • OpenPGP is a well known and tested standard
  • Security is built in
  • There is room for growth and modifications
  • It's very robust and covers almost all use cases
  • Possible use of existing OpenPGP compliant architecture

There is one great disadvantage, though. With robustness comes complexity - it's not a quick and easy fix to the problem of key sharing but rather a lengthy process of integrating bitcoin into OpenPGP ecosystem.

This sounds very interesting.  I can see simple use cases like using one EC key both as a bitcoin account and for signing PGP messages.  Possibly using a bitcoin-disclosed public key to encrypt messages, if this is safe.  How else would integration be possible?

Another factor, GnuPG has yet to produce a stable release with support for EC keys.
Red Emerald
Hero Member
*****
Offline Offline

Activity: 742
Merit: 500



View Profile WWW
September 28, 2011, 09:30:40 AM
 #25

Keeping it short is also important if you want to store it in a QR code

kjj
Legendary
*
Offline Offline

Activity: 1302
Merit: 1025



View Profile
March 29, 2013, 07:58:49 PM
Last edit: April 17, 2013, 01:12:05 PM by kjj
 #26

FYI, this is the format used by vanitygen 0.22 when you specify -e.

I had a hell of a time sorting out how to decode them, thought this might help someone else.  I'm using PHP, but PHP is easy to read.  I'm using an external PBKDF2 function.  If you use the function included with newer PHP versions, you may need to adjust the parameter order.  The mcrypt_cbc() is old too, check with the PHP manual for updated information, or use phpseclib.

The $input value is the raw base58coded string.  My base58decode function returns the binary output without verifying the check value.  If yours checks it before returning, you can skip that part.

The output value is either -1 (bad 58 check code), -2 (wrong password), or the private key as a raw binary string.

Code:
<?php
function keydecrypt($input,$password){
 
$data=base58decode($input);
 
$style=substr($data,0,1);
 
$params=substr($data,1,1);
 
$ciphertext=substr($data,2,32);
 
$pwcheck=substr($data,2+32,8);
 
$salt=substr($data,2+32+8,4);
 
$code58=substr($data,2+32+8+4,4);
 
$check58=substr(hash("sha256",hash("sha256",$style.$params.$ciphertext.$pwcheck.$salt,TRUE),TRUE),0,4);
 if(
$code58!=$check58)return -1;        // Invalid 58 check code
 
$key=pbkdf2("sha256",$password,$salt,4096,64,TRUE);    // 4096 is iterations, 64 is output length
 
$cipherkey=substr($key,0,32);
 
$iv=substr($key,32,16);
 
$hmac_key=substr($key,48,16);
 
$unprot=mcrypt_cbc(MCRYPT_RIJNDAEL_128,$cipherkey,$ciphertext,MCRYPT_DECRYPT,$iv);
 
$hmac=substr(hash_hmac("sha256",$unprot,$hmac_key,TURE),0,8);
 if(
$hmac!=$pwcheck)return -2;  // Invalid password
 
return $unprot;
}
?>


If using phpseclib, use this instead of the mcrypt_cbc call:

Code:
<?php
require_once("Crypt/AES.php");

  
$cipher = new Crypt_AES(CRYPT_AES_MODE_CBC);
  
$cipher->setKey($cipherkey);
  
$cipher->setIV($iv);
  
$unprot=$cipher->decrypt($cb);
?>


17Np17BSrpnHCZ2pgtiMNnhjnsWJ2TMqq8
I routinely ignore posters with paid advertising in their sigs.  You should too.
fecell
Jr. Member
*
Offline Offline

Activity: 134
Merit: 2


View Profile
July 10, 2019, 02:47:15 AM
 #27

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.
someone claim prize. can you give a solution?
Pages: 1 2 [All]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!