User:Vbuterin/K of N redundant offline private key proposal

From Bitcoin Wiki
Revision as of 11:49, 5 August 2012 by Vbuterin (talk | contribs) (Created page with "There is an important tradeoff between security and recoverability when storing an offline wallet. The simplest setup, storing the entire private key in a single place in a lo...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

There is an important tradeoff between security and recoverability when storing an offline wallet. The simplest setup, storing the entire private key in a single place in a lockbox, provides medium security but is vulnerable to accidental loss or destruction through accidents such as fire. Storing a simple backup of the key mitigates this risk, but at the same time decreases security, and the opposite strategy, splitting the key into multiple pieces, all of which must somehow be combined to retrieve the original key, increases security but makes the accidental loss problem even worse. The optimal solution is a setup which combines the two strategies, allowing the key to easily be split up into n pieces, any k of which can be used to recover the original key. The simplest such setup is storing k parts of the key and then an XOR of all k parts, but that approach is limiting as it only allows for n = k+1. What this document proposes is a setup which allows any n and k to be used. The steps are the following:

1. K-piece key splitting

Take your private key, P, and find k values (which we'll call v(1) to v(k)) which meet the following conditions:

  1. hex(SHA256(i)) starts with '00' for all i 1 to k - this is the checksum
  2. XOR(v(i) for all i 1 to k) = the original private key

Pseudocode implementation:

def generate(P,k):
  v = array(k)
  for i in range(0,k-2):
    while hex(sha256(v[i]))[0:2] != '00':
      v[i] = random(1,2^256)
  remainder = P
  for i in range(0,k-2):
    remainder = remainder XOR v[i]
  while hex(sha256(v[k-2]))[0:2] != '00' or hex(sha256(v[k-1]))[0:2] != '00':
    v[k-2] = random(1,2^256)
    v[k-1] = remainder XOR v[k-2]

2. N piece generation

Piece i will have the following format:

  • Byte 0 = 0x86 (unique "format" identifier to make keyparts in base 58 recognizable to the untrained eye)
  • Byte 1 = i
  • Byte 2 = byte length of everything coming after it (leave this blank at first, and calculate it at the end).
  • Bytes 3-whatever = v(1) + v(2)*2^i + v(3)*3^i + v(4)*4^i + ...

For example, if you want your private key to require three out of five pieces to reconstitute, the final pieces will be (string lengths will depend on exactly what v(1), v(2) and v(3) are):

  1. 0x86 1 45 v(1) + v(2)*2 + v(3)*3
  2. 0x86 2 45 v(1) + v(2)*4 + v(3)*9
  3. 0x86 3 46 v(1) + v(2)*8 + v(3)*27
  4. 0x86 4 46 v(1) + v(2)*16 + v(3)*81
  5. 0x86 5 46 v(1) + v(2)*32 + v(3)*243

When you're done, the pieces can be kept as hex of base58 - either format works fine.

To reconstitute the private key, simply solve the linear system from any three pieces, inferring from the second byte of the pieces what all the coefficients are, and XOR all the results. You actually don't have to know what n is because you can simply assume that n is the number of pieces that you have, and if you have too many pieces solving the linear system will simply lead you to discover that the n+1st, n+2nd, etc pieces are all equal to zero.

The scheme can easily be adapted to make the private key the EC product of v(1), v(2), etc rather than an XOR, and even the linear systems can be changed to an multiplicative/exponential equivalent if desired, so it's pretty adaptable.