btcruby 1.3 → 1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/RELEASE_NOTES.md +6 -0
 - data/lib/btcruby.rb +1 -0
 - data/lib/btcruby/ssss.rb +265 -0
 - data/lib/btcruby/version.rb +1 -1
 - data/spec/ssss_spec.rb +62 -0
 - metadata +5 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 162d9f3dbb8974140831c1e831c72ffaa4ec0c01
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 6c429f7f67eb3fc066fb83e3501f9a26ea6d55a0
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: ff5d4970c74486586c1a4d0ea95b7b92eb8b635324bfc1a8bfbc5ee571af44ce079d381704a6b10a82946e920a2d84d7b6c6f7cfd17db5e518d148f4f651f3e0
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 3b7bd61ff37ba5d332b197b2503b5427be9ff3448298e7adcb79284373f9ba8a6d7dc6cf019f11d4d34ed164781907cba3e620eb0900d1ae2947a49706f448e0
         
     | 
    
        data/RELEASE_NOTES.md
    CHANGED
    
    | 
         @@ -2,6 +2,12 @@ 
     | 
|
| 
       2 
2 
     | 
    
         
             
            BTCRuby Release Notes
         
     | 
| 
       3 
3 
     | 
    
         
             
            =====================
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
      
 5 
     | 
    
         
            +
            1.4 (November 26, 2015)
         
     | 
| 
      
 6 
     | 
    
         
            +
            -----------------------
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            * `BTC::SecretSharing` implements Shamir's Secret Sharing Scheme (SSSS) for splitting 128-bit secrets up to 16 shares.
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
       5 
11 
     | 
    
         
             
            1.3 (November 24, 2015)
         
     | 
| 
       6 
12 
     | 
    
         
             
            -----------------------
         
     | 
| 
       7 
13 
     | 
    
         | 
    
        data/lib/btcruby.rb
    CHANGED
    
    | 
         @@ -51,6 +51,7 @@ require_relative 'btcruby/block_header.rb' 
     | 
|
| 
       51 
51 
     | 
    
         
             
            require_relative 'btcruby/block.rb'
         
     | 
| 
       52 
52 
     | 
    
         
             
            require_relative 'btcruby/merkle_tree.rb'
         
     | 
| 
       53 
53 
     | 
    
         
             
            require_relative 'btcruby/open_assets.rb'
         
     | 
| 
      
 54 
     | 
    
         
            +
            require_relative 'btcruby/ssss.rb'
         
     | 
| 
       54 
55 
     | 
    
         | 
| 
       55 
56 
     | 
    
         
             
            # TODO:
         
     | 
| 
       56 
57 
     | 
    
         
             
            # require_relative 'btcruby/curve_point.rb'
         
     | 
    
        data/lib/btcruby/ssss.rb
    ADDED
    
    | 
         @@ -0,0 +1,265 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Shamir's Secret Sharing Scheme (SSSS) with m-of-n rule for 128-bit numbers.
         
     | 
| 
      
 2 
     | 
    
         
            +
            # Author: Oleg Andreev <oleganza@gmail.com>
         
     | 
| 
      
 3 
     | 
    
         
            +
            #
         
     | 
| 
      
 4 
     | 
    
         
            +
            # * Deterministic, extensible algorithm: every combination of secret and threshold produces exactly the same shares on each run. More shares can be generated without invalidating the first ones.
         
     | 
| 
      
 5 
     | 
    
         
            +
            # * This algorithm splits and restores 128-bit secrets with up to 16 shares and up to 16 shares threshold.
         
     | 
| 
      
 6 
     | 
    
         
            +
            # * Secret is a binary 16-byte string below ffffffffffffffffffffffffffffff61.
         
     | 
| 
      
 7 
     | 
    
         
            +
            # * Shares are 17-byte binary strings with first byte indicating threshold and share index (these are necessary for recovery).
         
     | 
| 
      
 8 
     | 
    
         
            +
            # 
         
     | 
| 
      
 9 
     | 
    
         
            +
            # See also: https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing
         
     | 
| 
      
 10 
     | 
    
         
            +
            require 'digest/sha2'
         
     | 
| 
      
 11 
     | 
    
         
            +
            require 'securerandom'
         
     | 
| 
      
 12 
     | 
    
         
            +
            module BTC
         
     | 
| 
      
 13 
     | 
    
         
            +
              module SecretSharing
         
     | 
| 
      
 14 
     | 
    
         
            +
                extend self
         
     | 
| 
      
 15 
     | 
    
         
            +
                Order = 0xffffffffffffffffffffffffffffff61 # Largest prime below 2**128: (2**128 - 159)
         
     | 
| 
      
 16 
     | 
    
         
            +
              
         
     | 
| 
      
 17 
     | 
    
         
            +
                def random
         
     | 
| 
      
 18 
     | 
    
         
            +
                  be_from_int(SecureRandom.random_number(Order))
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
              
         
     | 
| 
      
 21 
     | 
    
         
            +
                def prng(seed)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  x = Order
         
     | 
| 
      
 23 
     | 
    
         
            +
                  s = nil
         
     | 
| 
      
 24 
     | 
    
         
            +
                  pad = "".b
         
     | 
| 
      
 25 
     | 
    
         
            +
                  while x >= Order
         
     | 
| 
      
 26 
     | 
    
         
            +
                    s = Digest::SHA2.digest(Digest::SHA2.digest(seed + pad))[0,16]
         
     | 
| 
      
 27 
     | 
    
         
            +
                    x = int_from_be(s)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    pad = pad + "\x00".b
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
                  s
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
              
         
     | 
| 
      
 33 
     | 
    
         
            +
                # Returns N strings, any M of them are enough to retrieve a secret.
         
     | 
| 
      
 34 
     | 
    
         
            +
                # Each string encodes X and Y coordinates and also M. X & M takes one byte, Y takes 16 bytes:
         
     | 
| 
      
 35 
     | 
    
         
            +
                # MMMMXXXX YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY
         
     | 
| 
      
 36 
     | 
    
         
            +
                def split(secret, m, n)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  prime = Order
         
     | 
| 
      
 38 
     | 
    
         
            +
                  secret_num = int_from_be(secret)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  if secret_num >= Order
         
     | 
| 
      
 40 
     | 
    
         
            +
                    raise "Secret cannot be encoded with 128-bit SSSS"
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  if !(n >= 1 && n <= 16)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    raise "N must be between 1 and 16"
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                  if !(m >= 1 && m <= n)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    raise "M must be between 1 and N"
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
                  shares = []
         
     | 
| 
      
 49 
     | 
    
         
            +
                  coef = [secret_num] # polynomial coefficients
         
     | 
| 
      
 50 
     | 
    
         
            +
                  (m - 1).times do |i|
         
     | 
| 
      
 51 
     | 
    
         
            +
                    # Generate unpredictable yet deterministic coefficients for each secret and M.
         
     | 
| 
      
 52 
     | 
    
         
            +
                    coef << int_from_be(prng(secret + m.chr + i.chr))
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
                  n.times do |i|
         
     | 
| 
      
 55 
     | 
    
         
            +
                    x = i + 1
         
     | 
| 
      
 56 
     | 
    
         
            +
                    exp = 1
         
     | 
| 
      
 57 
     | 
    
         
            +
                    y = coef[0]
         
     | 
| 
      
 58 
     | 
    
         
            +
                    while exp < m
         
     | 
| 
      
 59 
     | 
    
         
            +
                      y = (y + (coef[exp] * ((x**exp) % prime) % prime)) % prime
         
     | 
| 
      
 60 
     | 
    
         
            +
                      exp += 1
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
                    # Encode share
         
     | 
| 
      
 63 
     | 
    
         
            +
                    shares << string_from_point(m, x, y)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end
         
     | 
| 
      
 65 
     | 
    
         
            +
                  shares
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
              
         
     | 
| 
      
 68 
     | 
    
         
            +
                # Transforms M 17-byte binary strings into original secret 16-byte binary string.
         
     | 
| 
      
 69 
     | 
    
         
            +
                # Each share string must be well-formed.
         
     | 
| 
      
 70 
     | 
    
         
            +
                def restore(shares)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  prime = Order
         
     | 
| 
      
 72 
     | 
    
         
            +
                  shares = shares.dup.uniq
         
     | 
| 
      
 73 
     | 
    
         
            +
                  raise "No shares provided" if shares.size == 0
         
     | 
| 
      
 74 
     | 
    
         
            +
                  points = shares.map{|s| point_from_string(s) } # [[m,x,y],...]
         
     | 
| 
      
 75 
     | 
    
         
            +
                  ms = points.map{|p| p[0]}.uniq
         
     | 
| 
      
 76 
     | 
    
         
            +
                  xs = points.map{|p| p[1]}.uniq
         
     | 
| 
      
 77 
     | 
    
         
            +
                  raise "Shares do not use the same M value" if ms.size > 1
         
     | 
| 
      
 78 
     | 
    
         
            +
                  m = ms.first
         
     | 
| 
      
 79 
     | 
    
         
            +
                  raise "All shares must have unique X values" if xs.size != points.size
         
     | 
| 
      
 80 
     | 
    
         
            +
                  raise "Number of shares should be M or more" if points.size < m
         
     | 
| 
      
 81 
     | 
    
         
            +
                  points = points[0, m] # make sure we have exactly M points
         
     | 
| 
      
 82 
     | 
    
         
            +
                  y = 0
         
     | 
| 
      
 83 
     | 
    
         
            +
                  points.size.times do |formula| # 0..(m-1)
         
     | 
| 
      
 84 
     | 
    
         
            +
                    # Multiply the numerator across the top and denominators across the bottom to do Lagrange's interpolation
         
     | 
| 
      
 85 
     | 
    
         
            +
                    numerator = 1
         
     | 
| 
      
 86 
     | 
    
         
            +
                    denominator = 1
         
     | 
| 
      
 87 
     | 
    
         
            +
                    points.size.times do |count| # 0..(m-1)
         
     | 
| 
      
 88 
     | 
    
         
            +
                      if formula != count # skip element with i == j
         
     | 
| 
      
 89 
     | 
    
         
            +
                        startposition = points[formula][1]
         
     | 
| 
      
 90 
     | 
    
         
            +
                        nextposition = points[count][1]
         
     | 
| 
      
 91 
     | 
    
         
            +
                        numerator = (numerator * -nextposition) % prime
         
     | 
| 
      
 92 
     | 
    
         
            +
                        denominator = (denominator * (startposition - nextposition)) % prime
         
     | 
| 
      
 93 
     | 
    
         
            +
                      end
         
     | 
| 
      
 94 
     | 
    
         
            +
                    end
         
     | 
| 
      
 95 
     | 
    
         
            +
                    value = points[formula][2]
         
     | 
| 
      
 96 
     | 
    
         
            +
                    y = (prime + y + (value * numerator * modinv(denominator, prime))) % prime
         
     | 
| 
      
 97 
     | 
    
         
            +
                  end
         
     | 
| 
      
 98 
     | 
    
         
            +
                  return be_from_int(y)
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
              
         
     | 
| 
      
 101 
     | 
    
         
            +
                # Returns mmmmxxxx yyyyyyyy yyyyyyyy ... (16 bytes of y)
         
     | 
| 
      
 102 
     | 
    
         
            +
                def string_from_point(m, x, y)
         
     | 
| 
      
 103 
     | 
    
         
            +
                  m = to_nibble(m)
         
     | 
| 
      
 104 
     | 
    
         
            +
                  x = to_nibble(x)
         
     | 
| 
      
 105 
     | 
    
         
            +
                  byte = [(m << 4) + x].pack("C")
         
     | 
| 
      
 106 
     | 
    
         
            +
                  byte + be_from_int(y)
         
     | 
| 
      
 107 
     | 
    
         
            +
                end
         
     | 
| 
      
 108 
     | 
    
         
            +
              
         
     | 
| 
      
 109 
     | 
    
         
            +
                # returns [m, x, y]
         
     | 
| 
      
 110 
     | 
    
         
            +
                def point_from_string(s)
         
     | 
| 
      
 111 
     | 
    
         
            +
                  byte = s.bytes.first
         
     | 
| 
      
 112 
     | 
    
         
            +
                  m = from_nibble(byte >> 4)
         
     | 
| 
      
 113 
     | 
    
         
            +
                  x = from_nibble(byte & 0x0f)
         
     | 
| 
      
 114 
     | 
    
         
            +
                  y = int_from_be(s[1..-1])
         
     | 
| 
      
 115 
     | 
    
         
            +
                  [m, x, y]
         
     | 
| 
      
 116 
     | 
    
         
            +
                end
         
     | 
| 
      
 117 
     | 
    
         
            +
              
         
     | 
| 
      
 118 
     | 
    
         
            +
                # Encodes values in range 1..16 to one nibble where all values are encoded as-is, 
         
     | 
| 
      
 119 
     | 
    
         
            +
                # except for 16 which becomes 0. This is to make strings look friendly for common cases when M,N < 16
         
     | 
| 
      
 120 
     | 
    
         
            +
                def to_nibble(x)
         
     | 
| 
      
 121 
     | 
    
         
            +
                  x == 16 ? 0 : x
         
     | 
| 
      
 122 
     | 
    
         
            +
                end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                def from_nibble(x)
         
     | 
| 
      
 125 
     | 
    
         
            +
                  x == 0 ? 16 : x
         
     | 
| 
      
 126 
     | 
    
         
            +
                end
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                # Gives the decomposition of the gcd of a and b.  Returns [x,y,z] such that x = gcd(a,b) and y*a + z*b = x
         
     | 
| 
      
 129 
     | 
    
         
            +
                def gcd_decomposition(a,b)
         
     | 
| 
      
 130 
     | 
    
         
            +
                  if b == 0
         
     | 
| 
      
 131 
     | 
    
         
            +
                    [a, 1, 0]
         
     | 
| 
      
 132 
     | 
    
         
            +
                  else
         
     | 
| 
      
 133 
     | 
    
         
            +
                    n = a/b
         
     | 
| 
      
 134 
     | 
    
         
            +
                    c = a % b
         
     | 
| 
      
 135 
     | 
    
         
            +
                    r = gcd_decomposition(b,c)
         
     | 
| 
      
 136 
     | 
    
         
            +
                    [r[0], r[2], r[1]-r[2]*n]
         
     | 
| 
      
 137 
     | 
    
         
            +
                  end
         
     | 
| 
      
 138 
     | 
    
         
            +
                end
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                # Gives the multiplicative inverse of k mod prime. In other words (k * modInverse(k)) % prime = 1 for all prime > k >= 1  
         
     | 
| 
      
 141 
     | 
    
         
            +
                def modinv(k, prime)
         
     | 
| 
      
 142 
     | 
    
         
            +
                  k = k % prime
         
     | 
| 
      
 143 
     | 
    
         
            +
                  r = (k < 0) ? -gcd_decomposition(prime,-k)[2] : gcd_decomposition(prime,k)[2]
         
     | 
| 
      
 144 
     | 
    
         
            +
                  return (prime + r) % prime
         
     | 
| 
      
 145 
     | 
    
         
            +
                end
         
     | 
| 
      
 146 
     | 
    
         
            +
             
     | 
| 
      
 147 
     | 
    
         
            +
                def int_from_be(data)
         
     | 
| 
      
 148 
     | 
    
         
            +
                  r = data.unpack("C*").reverse.inject({pos:0, total:0}) do |ctx, c|
         
     | 
| 
      
 149 
     | 
    
         
            +
                    c = c << ctx[:pos]
         
     | 
| 
      
 150 
     | 
    
         
            +
                    ctx[:pos] += 8
         
     | 
| 
      
 151 
     | 
    
         
            +
                    ctx[:total] += c
         
     | 
| 
      
 152 
     | 
    
         
            +
                    ctx
         
     | 
| 
      
 153 
     | 
    
         
            +
                  end
         
     | 
| 
      
 154 
     | 
    
         
            +
                  r[:total]
         
     | 
| 
      
 155 
     | 
    
         
            +
                end
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                def be_from_int(i, pad = 128)
         
     | 
| 
      
 158 
     | 
    
         
            +
                  a = []
         
     | 
| 
      
 159 
     | 
    
         
            +
                  while i > 0
         
     | 
| 
      
 160 
     | 
    
         
            +
                    a.unshift(i % 256)
         
     | 
| 
      
 161 
     | 
    
         
            +
                    i /= 256
         
     | 
| 
      
 162 
     | 
    
         
            +
                  end
         
     | 
| 
      
 163 
     | 
    
         
            +
                  a.pack("C*").rjust(pad / 8, "\x00")
         
     | 
| 
      
 164 
     | 
    
         
            +
                end
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
              end
         
     | 
| 
      
 167 
     | 
    
         
            +
            end
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
            if $0 == __FILE__
         
     | 
| 
      
 170 
     | 
    
         
            +
              
         
     | 
| 
      
 171 
     | 
    
         
            +
              SSSS = BTC::SecretSharing
         
     | 
| 
      
 172 
     | 
    
         
            +
              require_relative 'data.rb'
         
     | 
| 
      
 173 
     | 
    
         
            +
              
         
     | 
| 
      
 174 
     | 
    
         
            +
              # Usage 
         
     | 
| 
      
 175 
     | 
    
         
            +
              secret = SSSS.random
         
     | 
| 
      
 176 
     | 
    
         
            +
              puts "Secret: #{BTC.to_hex(secret)}"
         
     | 
| 
      
 177 
     | 
    
         
            +
              shares = SSSS.split(secret, 2, 3)
         
     | 
| 
      
 178 
     | 
    
         
            +
              shares.each do |share|
         
     | 
| 
      
 179 
     | 
    
         
            +
                puts "Share:  #{BTC.to_hex(share)}"
         
     | 
| 
      
 180 
     | 
    
         
            +
              end
         
     | 
| 
      
 181 
     | 
    
         
            +
              restored_secret = SSSS.restore([shares[1], shares[0]])
         
     | 
| 
      
 182 
     | 
    
         
            +
              puts "Recovered secret with shares 2 and 1: #{BTC.to_hex(restored_secret)}"
         
     | 
| 
      
 183 
     | 
    
         
            +
              restored_secret = SSSS.restore([shares[0], shares[2]])
         
     | 
| 
      
 184 
     | 
    
         
            +
              puts "Recovered secret with shares 1 and 3: #{BTC.to_hex(restored_secret)}"
         
     | 
| 
      
 185 
     | 
    
         
            +
              restored_secret = SSSS.restore([shares[1], shares[2]])
         
     | 
| 
      
 186 
     | 
    
         
            +
              puts "Recovered secret with shares 2 and 3: #{BTC.to_hex(restored_secret)}"
         
     | 
| 
      
 187 
     | 
    
         
            +
              
         
     | 
| 
      
 188 
     | 
    
         
            +
              # Output:
         
     | 
| 
      
 189 
     | 
    
         
            +
              # Secret: d881c6f74ccac24997bb27040640a8eb
         
     | 
| 
      
 190 
     | 
    
         
            +
              # Share:  2147018841997da8d92211ad7590f85754
         
     | 
| 
      
 191 
     | 
    
         
            +
              # Share:  22b581498be6308f68ac6833e71bb0051e
         
     | 
| 
      
 192 
     | 
    
         
            +
              # Share:  2324010ad632e375f836beba58a667b387
         
     | 
| 
      
 193 
     | 
    
         
            +
              # Recovered secret with shares 2 and 1: d881c6f74ccac24997bb27040640a8eb
         
     | 
| 
      
 194 
     | 
    
         
            +
              # Recovered secret with shares 1 and 3: d881c6f74ccac24997bb27040640a8eb
         
     | 
| 
      
 195 
     | 
    
         
            +
              # Recovered secret with shares 2 and 3: d881c6f74ccac24997bb27040640a8eb
         
     | 
| 
      
 196 
     | 
    
         
            +
              
         
     | 
| 
      
 197 
     | 
    
         
            +
              # Test Vectors
         
     | 
| 
      
 198 
     | 
    
         
            +
              
         
     | 
| 
      
 199 
     | 
    
         
            +
              test_vectors = [
         
     | 
| 
      
 200 
     | 
    
         
            +
                {
         
     | 
| 
      
 201 
     | 
    
         
            +
                  "secret" => "31415926535897932384626433832795",
         
     | 
| 
      
 202 
     | 
    
         
            +
                  "1-of-1" => ["1131415926535897932384626433832795"],
         
     | 
| 
      
 203 
     | 
    
         
            +
                  "1-of-2" => ["1131415926535897932384626433832795", "1231415926535897932384626433832795"],
         
     | 
| 
      
 204 
     | 
    
         
            +
                  "2-of-2" => ["215af384f05d9b45f0e4e348f95b371acd", "2284a5b0ba67ddf44ea6422f8e82eb0e05"],
         
     | 
| 
      
 205 
     | 
    
         
            +
                  "1-of-3" => ["1131415926535897932384626433832795", "1231415926535897932384626433832795", "1331415926535897932384626433832795"],
         
     | 
| 
      
 206 
     | 
    
         
            +
                  "2-of-3" => ["215af384f05d9b45f0e4e348f95b371acd", "2284a5b0ba67ddf44ea6422f8e82eb0e05", "23ae57dc847220a2ac67a11623aa9f013d"],
         
     | 
| 
      
 207 
     | 
    
         
            +
                  "3-of-3" => ["316cb005ab037e85ed9c8befbe72fef75c", "321387c8a1b34863197fae486ca60c1b97", "3325c8a20a62b62f16cceb6c6eccaa93a7"],
         
     | 
| 
      
 208 
     | 
    
         
            +
                  "4-of-6" => ["416c4b3a8dc218696f8b1aed23385496eb", "429b14a744ce462bdc71b910b5cf0890ba", "4384d4d7881b01db3881cd0f17457112c8", 
         
     | 
| 
      
 209 
     | 
    
         
            +
                               "44f0c303944b6b73e265c52a42e9601a3c", "45a61663a602a2f238c80fa43408a7a57b", "466c062ff9e3c8529a531abee5f119b1ac"],
         
     | 
| 
      
 210 
     | 
    
         
            +
                  "10-of-16"=>["a1a8b4077b75b0b18aefa63399d0b8d749", "a2e015e817190296d9ebe29f1c8cdc21c7", "a3c65760010c358c9760cece5da815edb4", "a4129891c5efd375a8367c854ab08010d6",
         
     | 
| 
      
 211 
     | 
    
         
            +
                               "a53c138386a55b0b35447ca03e44ab4eeb", "a6182993f21038c5d3bf548dac9dee7e20", "a769f010c04a4996b471a82addd4ea05d4", "a88e27a316dda9822f81616b2d48cb5e23",
         
     | 
| 
      
 212 
     | 
    
         
            +
                               "a9b0298820dc8c26989b6f8a2e8b00c3c4", "aa98042e1bcdf63b7283503ac4ad364380", "ab27bed0235b651dd92e764fa8cea25ba8", "ac05890d2177c48f4ec6cabd1047d9dbdc",
         
     | 
| 
      
 213 
     | 
    
         
            +
                               "adba7838775b82e4022af68f19d9985368", "aeb96045352c20fd24c6de8563cb2446f2", "af4f51af0a774592f9eabb71aaf0348def", "a06f50a680d22280f31b853d941c7eb158"],
         
     | 
| 
      
 214 
     | 
    
         
            +
                },
         
     | 
| 
      
 215 
     | 
    
         
            +
                {
         
     | 
| 
      
 216 
     | 
    
         
            +
                  "secret" => "deadbeefcafebabedeadbeefcafebabe",
         
     | 
| 
      
 217 
     | 
    
         
            +
                  "1-of-1" => ["11deadbeefcafebabedeadbeefcafebabe"],
         
     | 
| 
      
 218 
     | 
    
         
            +
                  "2-of-2" => ["217f21b8a8329e69ea75a518485c8da19d", "221f95b2609a3e19160c9c71a0ee1c887c"],
         
     | 
| 
      
 219 
     | 
    
         
            +
                  "2-of-3" => ["217f21b8a8329e69ea75a518485c8da19d", "221f95b2609a3e19160c9c71a0ee1c887c", "23c009ac1901ddc841a393caf97fab6ebc"],
         
     | 
| 
      
 220 
     | 
    
         
            +
                  "3-of-3" => ["31d6b7c83a2587dd06be735c2ba5c719c0", "32762d76edcca00dd227bccb825a8daa75", "33bd0ecb0ac0474d211a8a0cf3e9526c3e"],
         
     | 
| 
      
 221 
     | 
    
         
            +
                },
         
     | 
| 
      
 222 
     | 
    
         
            +
                {
         
     | 
| 
      
 223 
     | 
    
         
            +
                  "secret" => "ffffffffffffffffffffffffffffff60",
         
     | 
| 
      
 224 
     | 
    
         
            +
                  "1-of-1" => ["11ffffffffffffffffffffffffffffff60"],
         
     | 
| 
      
 225 
     | 
    
         
            +
                  "2-of-2" => ["21375c71bcaf077f5946f9e901efb9cf70", "226eb8e3795e0efeb28df3d203df739ee1"],
         
     | 
| 
      
 226 
     | 
    
         
            +
                  "2-of-3" => ["21375c71bcaf077f5946f9e901efb9cf70", "226eb8e3795e0efeb28df3d203df739ee1", "23a61555360d167e0bd4edbb05cf2d6e52"],
         
     | 
| 
      
 227 
     | 
    
         
            +
                  "3-of-3" => ["3112dac40bb910928263e5cf3971c39c8b", "32dec3f6359b1f7671aa60dd821c4969d3", "3363bb967da62cabcdd3712ad9ff916915"],
         
     | 
| 
      
 228 
     | 
    
         
            +
                },
         
     | 
| 
      
 229 
     | 
    
         
            +
                {
         
     | 
| 
      
 230 
     | 
    
         
            +
                  "secret" => "00000000000000000000000000000000",
         
     | 
| 
      
 231 
     | 
    
         
            +
                  "1-of-1" => ["1100000000000000000000000000000000"],
         
     | 
| 
      
 232 
     | 
    
         
            +
                  "2-of-2" => ["2125df3f1da76af07c37689382bc8201a6", "224bbe7e3b4ed5e0f86ed127057904034c"],
         
     | 
| 
      
 233 
     | 
    
         
            +
                  "2-of-3" => ["2125df3f1da76af07c37689382bc8201a6", "224bbe7e3b4ed5e0f86ed127057904034c", "23719dbd58f640d174a639ba88358604f2"],
         
     | 
| 
      
 234 
     | 
    
         
            +
                  "3-of-3" => ["31651161eeddabb39134be97908f0d7d9e", "32671d1a7e6d7ef24037990a5285a75164", "33062329aeaf79bc0d088f5845e3cd7b52"],
         
     | 
| 
      
 235 
     | 
    
         
            +
                }
         
     | 
| 
      
 236 
     | 
    
         
            +
              ]
         
     | 
| 
      
 237 
     | 
    
         
            +
              
         
     | 
| 
      
 238 
     | 
    
         
            +
              test_vectors.each do |test|
         
     | 
| 
      
 239 
     | 
    
         
            +
                hexsecret = test.delete("secret")
         
     | 
| 
      
 240 
     | 
    
         
            +
                secret = BTC.from_hex(hexsecret)
         
     | 
| 
      
 241 
     | 
    
         
            +
                test.each do |rule, defined_shares|
         
     | 
| 
      
 242 
     | 
    
         
            +
                  m, n = rule.split("-of-").map{|x|x.to_i}
         
     | 
| 
      
 243 
     | 
    
         
            +
                  puts "Testing #{hexsecret} #{rule}:"
         
     | 
| 
      
 244 
     | 
    
         
            +
                  shares = SSSS.split(secret, m, n)
         
     | 
| 
      
 245 
     | 
    
         
            +
                  hexshares = shares.map{|s| BTC.to_hex(s)}
         
     | 
| 
      
 246 
     | 
    
         
            +
                  failed = false
         
     | 
| 
      
 247 
     | 
    
         
            +
                  if hexshares != defined_shares
         
     | 
| 
      
 248 
     | 
    
         
            +
                    failed = true
         
     | 
| 
      
 249 
     | 
    
         
            +
                    puts "Failed test:"
         
     | 
| 
      
 250 
     | 
    
         
            +
                    puts "            Expected:  #{defined_shares.inspect}"
         
     | 
| 
      
 251 
     | 
    
         
            +
                    puts "            Generated: #{hexshares.inspect}"
         
     | 
| 
      
 252 
     | 
    
         
            +
                  end
         
     | 
| 
      
 253 
     | 
    
         
            +
                  subshares = hexshares[0...m] # TODO: iterate over various combinations
         
     | 
| 
      
 254 
     | 
    
         
            +
                  restored_secret = SSSS.restore(subshares.map{|s| BTC.from_hex(s)})
         
     | 
| 
      
 255 
     | 
    
         
            +
                  if restored_secret != secret
         
     | 
| 
      
 256 
     | 
    
         
            +
                    failed = true
         
     | 
| 
      
 257 
     | 
    
         
            +
                    puts "Failed #{hexsecret} #{rule} test: failed to restore secret using #{subshares.inspect}"
         
     | 
| 
      
 258 
     | 
    
         
            +
                  end
         
     | 
| 
      
 259 
     | 
    
         
            +
                  if !failed
         
     | 
| 
      
 260 
     | 
    
         
            +
                    puts "Ok."
         
     | 
| 
      
 261 
     | 
    
         
            +
                  end
         
     | 
| 
      
 262 
     | 
    
         
            +
                end
         
     | 
| 
      
 263 
     | 
    
         
            +
              end
         
     | 
| 
      
 264 
     | 
    
         
            +
              
         
     | 
| 
      
 265 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/btcruby/version.rb
    CHANGED
    
    
    
        data/spec/ssss_spec.rb
    ADDED
    
    | 
         @@ -0,0 +1,62 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
            describe BTC::SecretSharing do
         
     | 
| 
      
 3 
     | 
    
         
            +
              
         
     | 
| 
      
 4 
     | 
    
         
            +
              SSSS = BTC::SecretSharing
         
     | 
| 
      
 5 
     | 
    
         
            +
              
         
     | 
| 
      
 6 
     | 
    
         
            +
              test_vectors = [
         
     | 
| 
      
 7 
     | 
    
         
            +
                {
         
     | 
| 
      
 8 
     | 
    
         
            +
                  "secret" => "31415926535897932384626433832795",
         
     | 
| 
      
 9 
     | 
    
         
            +
                  "1-of-1" => ["1131415926535897932384626433832795"],
         
     | 
| 
      
 10 
     | 
    
         
            +
                  "1-of-2" => ["1131415926535897932384626433832795", "1231415926535897932384626433832795"],
         
     | 
| 
      
 11 
     | 
    
         
            +
                  "2-of-2" => ["215af384f05d9b45f0e4e348f95b371acd", "2284a5b0ba67ddf44ea6422f8e82eb0e05"],
         
     | 
| 
      
 12 
     | 
    
         
            +
                  "1-of-3" => ["1131415926535897932384626433832795", "1231415926535897932384626433832795", "1331415926535897932384626433832795"],
         
     | 
| 
      
 13 
     | 
    
         
            +
                  "2-of-3" => ["215af384f05d9b45f0e4e348f95b371acd", "2284a5b0ba67ddf44ea6422f8e82eb0e05", "23ae57dc847220a2ac67a11623aa9f013d"],
         
     | 
| 
      
 14 
     | 
    
         
            +
                  "3-of-3" => ["316cb005ab037e85ed9c8befbe72fef75c", "321387c8a1b34863197fae486ca60c1b97", "3325c8a20a62b62f16cceb6c6eccaa93a7"],
         
     | 
| 
      
 15 
     | 
    
         
            +
                  "4-of-6" => ["416c4b3a8dc218696f8b1aed23385496eb", "429b14a744ce462bdc71b910b5cf0890ba", "4384d4d7881b01db3881cd0f17457112c8", 
         
     | 
| 
      
 16 
     | 
    
         
            +
                               "44f0c303944b6b73e265c52a42e9601a3c", "45a61663a602a2f238c80fa43408a7a57b", "466c062ff9e3c8529a531abee5f119b1ac"],
         
     | 
| 
      
 17 
     | 
    
         
            +
                  "10-of-16"=>["a1a8b4077b75b0b18aefa63399d0b8d749", "a2e015e817190296d9ebe29f1c8cdc21c7", "a3c65760010c358c9760cece5da815edb4", "a4129891c5efd375a8367c854ab08010d6",
         
     | 
| 
      
 18 
     | 
    
         
            +
                               "a53c138386a55b0b35447ca03e44ab4eeb", "a6182993f21038c5d3bf548dac9dee7e20", "a769f010c04a4996b471a82addd4ea05d4", "a88e27a316dda9822f81616b2d48cb5e23",
         
     | 
| 
      
 19 
     | 
    
         
            +
                               "a9b0298820dc8c26989b6f8a2e8b00c3c4", "aa98042e1bcdf63b7283503ac4ad364380", "ab27bed0235b651dd92e764fa8cea25ba8", "ac05890d2177c48f4ec6cabd1047d9dbdc",
         
     | 
| 
      
 20 
     | 
    
         
            +
                               "adba7838775b82e4022af68f19d9985368", "aeb96045352c20fd24c6de8563cb2446f2", "af4f51af0a774592f9eabb71aaf0348def", "a06f50a680d22280f31b853d941c7eb158"],
         
     | 
| 
      
 21 
     | 
    
         
            +
                },
         
     | 
| 
      
 22 
     | 
    
         
            +
                {
         
     | 
| 
      
 23 
     | 
    
         
            +
                  "secret" => "deadbeefcafebabedeadbeefcafebabe",
         
     | 
| 
      
 24 
     | 
    
         
            +
                  "1-of-1" => ["11deadbeefcafebabedeadbeefcafebabe"],
         
     | 
| 
      
 25 
     | 
    
         
            +
                  "2-of-2" => ["217f21b8a8329e69ea75a518485c8da19d", "221f95b2609a3e19160c9c71a0ee1c887c"],
         
     | 
| 
      
 26 
     | 
    
         
            +
                  "2-of-3" => ["217f21b8a8329e69ea75a518485c8da19d", "221f95b2609a3e19160c9c71a0ee1c887c", "23c009ac1901ddc841a393caf97fab6ebc"],
         
     | 
| 
      
 27 
     | 
    
         
            +
                  "3-of-3" => ["31d6b7c83a2587dd06be735c2ba5c719c0", "32762d76edcca00dd227bccb825a8daa75", "33bd0ecb0ac0474d211a8a0cf3e9526c3e"],
         
     | 
| 
      
 28 
     | 
    
         
            +
                },
         
     | 
| 
      
 29 
     | 
    
         
            +
                {
         
     | 
| 
      
 30 
     | 
    
         
            +
                  "secret" => "ffffffffffffffffffffffffffffff60",
         
     | 
| 
      
 31 
     | 
    
         
            +
                  "1-of-1" => ["11ffffffffffffffffffffffffffffff60"],
         
     | 
| 
      
 32 
     | 
    
         
            +
                  "2-of-2" => ["21375c71bcaf077f5946f9e901efb9cf70", "226eb8e3795e0efeb28df3d203df739ee1"],
         
     | 
| 
      
 33 
     | 
    
         
            +
                  "2-of-3" => ["21375c71bcaf077f5946f9e901efb9cf70", "226eb8e3795e0efeb28df3d203df739ee1", "23a61555360d167e0bd4edbb05cf2d6e52"],
         
     | 
| 
      
 34 
     | 
    
         
            +
                  "3-of-3" => ["3112dac40bb910928263e5cf3971c39c8b", "32dec3f6359b1f7671aa60dd821c4969d3", "3363bb967da62cabcdd3712ad9ff916915"],
         
     | 
| 
      
 35 
     | 
    
         
            +
                },
         
     | 
| 
      
 36 
     | 
    
         
            +
                {
         
     | 
| 
      
 37 
     | 
    
         
            +
                  "secret" => "00000000000000000000000000000000",
         
     | 
| 
      
 38 
     | 
    
         
            +
                  "1-of-1" => ["1100000000000000000000000000000000"],
         
     | 
| 
      
 39 
     | 
    
         
            +
                  "2-of-2" => ["2125df3f1da76af07c37689382bc8201a6", "224bbe7e3b4ed5e0f86ed127057904034c"],
         
     | 
| 
      
 40 
     | 
    
         
            +
                  "2-of-3" => ["2125df3f1da76af07c37689382bc8201a6", "224bbe7e3b4ed5e0f86ed127057904034c", "23719dbd58f640d174a639ba88358604f2"],
         
     | 
| 
      
 41 
     | 
    
         
            +
                  "3-of-3" => ["31651161eeddabb39134be97908f0d7d9e", "32671d1a7e6d7ef24037990a5285a75164", "33062329aeaf79bc0d088f5845e3cd7b52"],
         
     | 
| 
      
 42 
     | 
    
         
            +
                }
         
     | 
| 
      
 43 
     | 
    
         
            +
              ]
         
     | 
| 
      
 44 
     | 
    
         
            +
              
         
     | 
| 
      
 45 
     | 
    
         
            +
              test_vectors.each do |test|
         
     | 
| 
      
 46 
     | 
    
         
            +
                hexsecret = test.delete("secret")
         
     | 
| 
      
 47 
     | 
    
         
            +
                secret = BTC.from_hex(hexsecret)
         
     | 
| 
      
 48 
     | 
    
         
            +
                test.each do |rule, defined_shares|
         
     | 
| 
      
 49 
     | 
    
         
            +
                  m, n = rule.split("-of-").map{|x|x.to_i}
         
     | 
| 
      
 50 
     | 
    
         
            +
                  it "Should split and restore #{rule} shares for #{hexsecret}" do
         
     | 
| 
      
 51 
     | 
    
         
            +
                    shares = SSSS.split(secret, m, n)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    hexshares = shares.map{|s| BTC.to_hex(s)}
         
     | 
| 
      
 53 
     | 
    
         
            +
                    failed = false
         
     | 
| 
      
 54 
     | 
    
         
            +
                    hexshares.must_equal defined_shares
         
     | 
| 
      
 55 
     | 
    
         
            +
                    subshares = hexshares[0...m] # TODO: iterate over various combinations
         
     | 
| 
      
 56 
     | 
    
         
            +
                    restored_secret = SSSS.restore(subshares.map{|s| BTC.from_hex(s)})
         
     | 
| 
      
 57 
     | 
    
         
            +
                    BTC.to_hex(restored_secret).must_equal hexsecret
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: btcruby
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: '1. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: '1.4'
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Oleg Andreev
         
     | 
| 
         @@ -9,7 +9,7 @@ authors: 
     | 
|
| 
       9 
9 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       10 
10 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       11 
11 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       12 
     | 
    
         
            -
            date: 2015-11- 
     | 
| 
      
 12 
     | 
    
         
            +
            date: 2015-11-26 00:00:00.000000000 Z
         
     | 
| 
       13 
13 
     | 
    
         
             
            dependencies:
         
     | 
| 
       14 
14 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       15 
15 
     | 
    
         
             
              name: ffi
         
     | 
| 
         @@ -119,6 +119,7 @@ files: 
     | 
|
| 
       119 
119 
     | 
    
         
             
            - lib/btcruby/script/test_signature_checker.rb
         
     | 
| 
       120 
120 
     | 
    
         
             
            - lib/btcruby/script/transaction_signature_checker.rb
         
     | 
| 
       121 
121 
     | 
    
         
             
            - lib/btcruby/secp256k1.rb
         
     | 
| 
      
 122 
     | 
    
         
            +
            - lib/btcruby/ssss.rb
         
     | 
| 
       122 
123 
     | 
    
         
             
            - lib/btcruby/transaction.rb
         
     | 
| 
       123 
124 
     | 
    
         
             
            - lib/btcruby/transaction_builder.rb
         
     | 
| 
       124 
125 
     | 
    
         
             
            - lib/btcruby/transaction_builder/errors.rb
         
     | 
| 
         @@ -160,6 +161,7 @@ files: 
     | 
|
| 
       160 
161 
     | 
    
         
             
            - spec/script_spec.rb
         
     | 
| 
       161 
162 
     | 
    
         
             
            - spec/secp256k1_spec.rb
         
     | 
| 
       162 
163 
     | 
    
         
             
            - spec/spec_helper.rb
         
     | 
| 
      
 164 
     | 
    
         
            +
            - spec/ssss_spec.rb
         
     | 
| 
       163 
165 
     | 
    
         
             
            - spec/transaction_builder_spec.rb
         
     | 
| 
       164 
166 
     | 
    
         
             
            - spec/transaction_spec.rb
         
     | 
| 
       165 
167 
     | 
    
         
             
            - spec/wire_format_spec.rb
         
     | 
| 
         @@ -211,6 +213,7 @@ test_files: 
     | 
|
| 
       211 
213 
     | 
    
         
             
            - spec/script_number_spec.rb
         
     | 
| 
       212 
214 
     | 
    
         
             
            - spec/script_spec.rb
         
     | 
| 
       213 
215 
     | 
    
         
             
            - spec/secp256k1_spec.rb
         
     | 
| 
      
 216 
     | 
    
         
            +
            - spec/ssss_spec.rb
         
     | 
| 
       214 
217 
     | 
    
         
             
            - spec/transaction_builder_spec.rb
         
     | 
| 
       215 
218 
     | 
    
         
             
            - spec/transaction_spec.rb
         
     | 
| 
       216 
219 
     | 
    
         
             
            - spec/wire_format_spec.rb
         
     |