forked from soulseekah/py-libgfshare
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgfshare.py
More file actions
156 lines (116 loc) · 4.85 KB
/
gfshare.py
File metadata and controls
156 lines (116 loc) · 4.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import ctypes
import random
from ctypes import *
random.seed( None )
class GFShareContext( Structure ):
_fields_ = \
[
( 'sharecount', c_uint ),
( 'threshold', c_uint ),
( 'size', c_uint ),
( 'sharenrs', POINTER( c_ubyte ) ),
( 'buffer', POINTER( c_ubyte ) ),
( 'buffersize', c_uint ),
]
libgfshare = ctypes.cdll.LoadLibrary( 'libgfshare.so' )
libgfshare.gfshare_ctx_init_enc.argtypes = \
[ POINTER( c_ubyte ), c_uint, c_ubyte, c_uint ]
libgfshare.gfshare_ctx_init_enc.restype = POINTER( GFShareContext )
libgfshare.gfshare_ctx_enc_setsecret.argtypes = \
[ POINTER( GFShareContext ), POINTER( c_ubyte ) ]
libgfshare.gfshare_ctx_enc_getshare.argtypes = \
[ POINTER( GFShareContext ), c_ubyte, POINTER( c_ubyte ) ]
libgfshare.gfshare_ctx_init_dec.argtypes = \
[ POINTER( c_ubyte ), c_uint, c_uint ]
libgfshare.gfshare_ctx_init_dec.restype = POINTER( GFShareContext )
libgfshare.gfshare_ctx_dec_giveshare.argtypes = \
[ POINTER( GFShareContext ), c_ubyte, POINTER( c_ubyte ) ]
libgfshare.gfshare_ctx_dec_extract.argtypes = \
[ POINTER( GFShareContext ), POINTER( c_ubyte ) ]
libgfshare.gfshare_ctx_free.argtypes = [ POINTER( GFShareContext ) ]
def _fill_random( buffer, count ):
""" By default libgfshare uses the libc random() function to fill the
buffer with random data. This is not ideal, so we have to provide a
much better and secure implementation.
As an example this implementation uses Python's random library. Feel
free to redefine this function as it offers little to no advantage over
the internal implementation.
A better but slow implementation would use /dev/random, for example.
"""
for x in range( count ):
buffer[x] = random.randint( 0, 255 )
def split( sharecount, threshold, data ):
""" Split `data` into `sharecount` shares, with any `threshold`
shares required for recombination. Returns a list of tuples:
[
( sharenr, data ),
( sharenr, data ),
...
]
Note: the `data` is not destroyed or cleared from memory, this is
left up to the client to do after calling the function. Keep your memory
safe.
"""
if sharecount > 255:
raise ValueError( 'Amount of shares cannot be larger than 255 as we are working in GF(2**8)' )
if sharecount < 2:
raise ValueError( 'Amount of shares cannot be less than 2, they are called shares for a reason' )
if threshold > sharecount:
raise ValueError( 'Threshold cannot be more than the amount of shares' )
if threshold < 2 :
raise ValueError( 'Threshold cannot be less than 2, single shares do not contain enough information' )
# Set the fill_random callback
fill_random = CFUNCTYPE( None, POINTER( c_ubyte ), c_uint )( _fill_random )
gfshare_fill_rand = c_void_p.in_dll( libgfshare, 'gfshare_fill_rand' )
gfshare_fill_rand.value = cast( fill_random, c_void_p ).value
# Generate the needed unique share numbers at random
sharenrs = random.sample( range( 1, 256 ), sharecount )
sharenrs = list( map( lambda x: c_ubyte( x ), sharenrs ) )
sharenrs = ( c_ubyte * len( sharenrs ) )( *sharenrs )
# Setup the context
ctx = libgfshare.gfshare_ctx_init_enc( \
sharenrs, c_uint( sharecount ), c_ubyte( threshold ), c_uint( len( data ) ) )
data = list( map( lambda x: ord( x ), data ) )
data = ( c_ubyte * len( data ) )( *data )
libgfshare.gfshare_ctx_enc_setsecret( ctx, data )
shares = []
# Retrieve the shares
for sharenr in range( sharecount ):
share = ( c_ubyte * len( data ) )()
libgfshare.gfshare_ctx_enc_getshare( ctx, sharenr, share )
shares.append( ( sharenrs[sharenr], ''.join( list( map( lambda x: chr( x ), share ) ) ) ) )
libgfshare.gfshare_ctx_free( ctx )
return shares
def combine( shares ):
""" Combine `shares` back into the secret. Expects a list of tuples:
[
( sharenr, data ),
( sharenr, data ),
...
]
Make sure to wipe the memory after you get the secret.
"""
if len( shares ) > 255:
raise ValueError( 'There can never be more than 255 shares as we are working in GF(2**8)' )
if len( shares ) < 2:
raise ValueError( 'There has to be at least 2 shares to reconstruct the secret' )
sharenrs = list( map( lambda x: x[0], shares ) )
sharenrs = list( map( lambda x: c_ubyte( x ), sharenrs ) )
sharenrs = ( c_ubyte * len( sharenrs ) )( *sharenrs )
size = len( shares[0][1] )
for share in shares:
if len( share[1] ) != size:
raise ValueError( 'Shares are not of the same size, cannot mix and match' )
# Setup the context
ctx = libgfshare.gfshare_ctx_init_dec( \
sharenrs, c_uint( len( shares ) ), c_uint( size ) )
# Recombine the shares
for sharenr, share in enumerate( shares ):
_, share = share
share = list( map( lambda x: ord( x ), share ) )
share = ( c_ubyte * size )( *share )
libgfshare.gfshare_ctx_dec_giveshare( ctx, sharenr, share )
secret = ( c_ubyte * size )()
libgfshare.gfshare_ctx_dec_extract( ctx, secret )
libgfshare.gfshare_ctx_free( ctx )
return ''.join( list( map( lambda x: chr( x ), secret ) ) )