c-SSS  0.2.1
Implementation of Shamir's Secret Sharing in C.
shamir.c
1 /*
2 
3  shamir.c -- Shamir's Secret Sharing
4 
5  Inspired by:
6 
7  http://en.wikipedia.org/wiki/Shamir's_Secret_Sharing#Javascript_example
8 
9 
10  Compatible with:
11 
12  http://www.christophedavid.org/w/c/w.php/Calculators/ShamirSecretSharing
13 
14  Notes:
15 
16  * The secrets start with 'AABBCC'
17  * 'AA' is the hex encoded share # (1 - 255)
18  * 'BB' is the threshold # of shares, also in hex
19  * 'CC' is fake for compatibility with the web implementation above (= 'AA')
20  * Remaining characters are encoded 1 byte = 2 hex characters, plus
21  'G0' = 256 (since we use 257 prime modulus)
22 
23  Limitations:
24 
25  * rand() needs to be seeded before use (see below)
26 
27 
28  Copyright © 2015 Fletcher T. Penney. Licensed under the MIT License.
29 
30  ## The MIT License ##
31 
32  Permission is hereby granted, free of charge, to any person obtaining a copy
33  of this software and associated documentation files (the "Software"), to deal
34  in the Software without restriction, including without limitation the rights
35  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
36  copies of the Software, and to permit persons to whom the Software is
37  furnished to do so, subject to the following conditions:
38 
39  The above copyright notice and this permission notice shall be included in
40  all copies or substantial portions of the Software.
41 
42  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
43  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
44  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
45  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
46  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
47  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
48  THE SOFTWARE.
49 */
50 
51 #include <stdlib.h>
52 #include <stdio.h>
53 #include <time.h>
54 #include <string.h>
55 #include <math.h>
56 #include <unistd.h>
57 
58 #include "shamir.h"
59 
60 
61 static int prime = 257;
62 
63 
64 /*
65  http://stackoverflow.com/questions/322938/recommended-way-to-initialize-srand
66 
67  http://www.concentric.net/~Ttwang/tech/inthash.htm
68 
69  Need a less predictable way to seed rand().
70 */
71 
72 unsigned long mix(unsigned long a, unsigned long b, unsigned long c)
73 {
74  a=a-b; a=a-c; a=a^(c >> 13);
75  b=b-c; b=b-a; b=b^(a << 8);
76  c=c-a; c=c-b; c=c^(b >> 13);
77  a=a-b; a=a-c; a=a^(c >> 12);
78  b=b-c; b=b-a; b=b^(a << 16);
79  c=c-a; c=c-b; c=c^(b >> 5);
80  a=a-b; a=a-c; a=a^(c >> 3);
81  b=b-c; b=b-a; b=b^(a << 10);
82  c=c-a; c=c-b; c=c^(b >> 15);
83  return c;
84 }
85 
86 void seed_random(void) {
87  unsigned long seed = mix(clock(), time(NULL), getpid());
88  srand(seed);
89 }
90 
91 int nonzero_random(int modulo) {
92  int range = modulo - 1;
93  /*-- use arc4random if available */
94 #if defined (HAVE_ARC4RANDOM)
95  return arc4random_uniform(range) + 1;
96 #else
97  return rand() % range + 1;
98 #endif
99 }
100 
101 /*
102  from http://stackoverflow.com/questions/18730071/c-programming-to-calculate-using-modular-exponentiation
103 
104  Allows working with larger numbers (e.g. 255 shares, with a threshold of 200)
105 */
106 
107 int modular_exponentiation(int base,int exp,int mod)
108 {
109  if (exp == 0)
110  return 1;
111  else if (exp%2 == 0) {
112  int mysqrt = modular_exponentiation(base, exp/2, mod);
113  return (mysqrt*mysqrt)%mod;
114  }
115  else
116  return (base * modular_exponentiation(base, exp-1, mod))%mod;
117 }
118 
119 
120 
121 /*
122  split_number() -- Split a number into shares
123  n = the number of shares
124  t = threshold shares to recreate the number
125 */
126 
127 int * split_number(int number, int n, int t) {
128  int * shares = malloc(sizeof(int)*n);
129 
130  int coef[t];
131  int x;
132  int i;
133 
134  coef[0] = number;
135 
136  for (i = 1; i < t; ++i)
137  {
138  /* prevent degraded polynomials by using non-zero random coefficients */
139  coef[i] = nonzero_random(prime - 1);
140  }
141 
142  for (x = 0; x < n; ++x)
143  {
144  int y = coef[0];
145 
146  /* Calculate the shares */
147  for (i = 1; i < t; ++i)
148  {
149  int temp = modular_exponentiation(x+1, i, prime);
150 
151  y = (y + (coef[i] * temp % prime)) % prime;
152  }
153 
154  /* Sometimes we're getting negative numbers, and need to fix that */
155  y = (y + prime) % prime;
156 
157  shares[x] = y;
158  }
159 
160  return shares;
161 }
162 
163 #ifdef TEST
164 void Test_split_number(CuTest* tc) {
165 
166  seed_random();
167 
168  int * test = split_number(1234, 50, 20);
169 
170  //printf("Split\n1: %d\n2: %d\n3: %d\n4: %d\n5: %d\n6: %d\n", *test, *(test+1), *(test+2),
171  // *(test+3),*(test+4),*(test+5));
172 
173  free(test);
174 
175  CuAssertIntEquals(tc, 0, 0);
176 }
177 #endif
178 
179 
180 /*
181  Math stuff
182 */
183 
184 int * gcdD(int a, int b) {
185  int * xyz = malloc(sizeof(int) * 3);
186 
187  if (b == 0) {
188  xyz[0] = a;
189  xyz[1] = 1;
190  xyz[2] = 0;
191  } else {
192  int n = floor(a/b);
193  int c = a % b;
194  int *r = gcdD(b,c);
195 
196  xyz[0] = r[0];
197  xyz[1] = r[2];
198  xyz[2] = r[1]-r[2]*n;
199 
200  free(r);
201  }
202 
203  return xyz;
204 }
205 
206 
207 /*
208  More math stuff
209 */
210 
211 int modInverse(int k) {
212  k = k % prime;
213 
214  int r;
215  int * xyz;
216 
217  if (k < 0) {
218  xyz = gcdD(prime,-k);
219  r = -xyz[2];
220  } else {
221  xyz = gcdD(prime, k);
222  r = xyz[2];
223  }
224 
225  free(xyz);
226 
227  return (prime + r) % prime;
228 }
229 
230 
231 /*
232  join_shares() -- join some shares to retrieve the secret
233  xy_pairs is array of int pairs, first is x, second is y
234  n is number of pairs submitted
235 */
236 
237 int join_shares(int *xy_pairs, int n) {
238  int secret = 0;
239  long numerator;
240  long denominator;
241  long startposition;
242  long nextposition;
243  long value;
244  int i;
245  int j;
246 
247  for (i = 0; i < n; ++i)
248  {
249  numerator = 1;
250  denominator = 1;
251  for (j = 0; j < n; ++j)
252  {
253  if(i != j) {
254  startposition = xy_pairs[i*2];
255  nextposition = xy_pairs[j*2];
256  numerator = (numerator * -nextposition) % prime;
257  denominator = (denominator * (startposition - nextposition)) % prime;
258  //fprintf(stderr, "Num: %lli\nDen: %lli\n", numerator, denominator);
259  }
260  }
261 
262  value = xy_pairs[i * 2 + 1];
263 
264  secret = (secret + (value * numerator * modInverse(denominator))) % prime;
265  }
266 
267  /* Sometimes we're getting negative numbers, and need to fix that */
268  secret = (secret + prime) % prime;
269 
270  return secret;
271 }
272 
273 
274 #ifdef TEST
275 void Test_join_shares(CuTest* tc) {
276  int n = 200;
277  int t = 100;
278 
279  int shares[n*2];
280 
281  int count = 255; /* How many times should we test it? */
282  int j;
283 
284  for (j = 0; j < count; ++j)
285  {
286  int * test = split_number(j, n, t);
287  int i;
288 
289  for (i = 0; i < n; ++i)
290  {
291  shares[i*2] = i + 1;
292  shares[i*2 + 1] = test[i];
293  }
294 
295  /* Send all n shares */
296  int result = join_shares(shares, n);
297 
298  free(test);
299 
300  CuAssertIntEquals(tc, j, result);
301  }
302 }
303 #endif
304 
305 
306 /*
307  split_string() -- Divide a string into shares
308  return an array of pointers to strings;
309 */
310 
311 char ** split_string(char * secret, int n, int t) {
312  int len = strlen(secret);
313 
314  char ** shares = malloc (sizeof(char *) * n);
315  int i;
316 
317  for (i = 0; i < n; ++i)
318  {
319  /* need two characters to encode each character */
320  /* Need 4 character overhead for share # and quorum # */
321  /* Additional 2 characters are for compatibility with:
322 
323  http://www.christophedavid.org/w/c/w.php/Calculators/ShamirSecretSharing
324  */
325  shares[i] = (char *) malloc(2*len + 6 + 1);
326 
327  sprintf(shares[i], "%02X%02XAA",(i+1),t);
328  }
329 
330  /* Now, handle the secret */
331 
332  for (i = 0; i < len; ++i)
333  {
334  // fprintf(stderr, "char %c: %d\n", secret[i], (unsigned char) secret[i]);
335  int letter = secret[i]; // - '0';
336 
337  if (letter < 0)
338  letter = 256 + letter;
339 
340  //fprintf(stderr, "char: '%c' int: '%d'\n", secret[i], letter);
341 
342  int * chunks = split_number(letter, n, t);
343  int j;
344 
345  for (j = 0; j < n; ++j)
346  {
347  if (chunks[j] == 256) {
348  sprintf(shares[j] + 6+ i * 2, "G0"); /* Fake code */
349  } else {
350  sprintf(shares[j] + 6 + i * 2, "%02X", chunks[j]);
351  }
352  }
353 
354  free(chunks);
355  }
356 
357  // fprintf(stderr, "%s\n", secret);
358 
359  return shares;
360 }
361 
362 
363 void free_string_shares(char ** shares, int n) {
364  int i;
365 
366  for (i = 0; i < n; ++i)
367  {
368  free(shares[i]);
369  }
370 
371  free(shares);
372 }
373 
374 
375 char * join_strings(char ** shares, int n) {
376  /* TODO: Check if we have a quorum */
377 
378  if (n == 0)
379  return NULL;
380 
381  int len = (strlen(shares[0]) - 6) / 2;
382 
383  char * result = malloc(len + 1);
384  char codon[3];
385  codon[2] = '\0'; // Must terminate the string!
386 
387  int x[n];
388  int i;
389  int j;
390 
391  for (i = 0; i < n; ++i)
392  {
393  codon[0] = shares[i][0];
394  codon[1] = shares[i][1];
395 
396  x[i] = strtol(codon, NULL, 16);
397  }
398 
399  for (i = 0; i < len; ++i)
400  {
401  int *chunks = malloc(sizeof(int) * n * 2);
402 
403  for (j = 0; j < n; ++j)
404  {
405  chunks[j*2] = x[j];
406 
407  codon[0] = shares[j][6 + i * 2];
408  codon[1] = shares[j][6 + i * 2 + 1];
409 
410  if (memcmp(codon,"G0",2) == 0) {
411  chunks[j*2 + 1] = 256;
412  } else {
413  chunks[j*2 + 1] = strtol(codon, NULL, 16);
414  }
415  }
416 
417  //unsigned char letter = join_shares(chunks, n);
418  char letter = join_shares(chunks, n);
419 
420  free(chunks);
421 
422  // fprintf(stderr, "char %c: %d\n", letter, (unsigned char) letter);
423 
424  sprintf(result + i, "%c",letter);
425  }
426 
427  return result;
428 }
429 
430 
431 #ifdef TEST
432 void Test_split_string(CuTest* tc) {
433  int n = 255; /* Maximum n = 255 */
434  int t = 254; /* t <= n, we choose less than that so we have two tests */
435 
436  char * phrase = "This is a test of Bücher and Später.";
437 
438  int count = 10;
439  int i;
440 
441  for (i = 0; i < count; ++i)
442  {
443  char ** result = split_string(phrase, n, t);
444 
445  /* Extract secret using first t shares */
446  char * answer = join_strings(result, t);
447  CuAssertStrEquals(tc, phrase, answer);
448  free(answer);
449 
450  /* Extract secret using all shares */
451  answer = join_strings(result, n);
452  CuAssertStrEquals(tc, phrase, answer);
453  free(answer);
454 
455  free_string_shares(result, n);
456  }
457 }
458 #endif
459 
460 
461 /*
462  generate_share_strings() -- create a string of the list of the generated shares,
463  one per line
464 */
465 
466 char * generate_share_strings(char * secret, int n, int t) {
467  char ** result = split_string(secret, n, t);
468 
469  int len = strlen(secret);
470  int key_len = 6 + 2 * len + 1;
471  int i;
472 
473  char * shares = malloc(key_len * n + 1);
474 
475  for (i = 0; i < n; ++i)
476  {
477  sprintf(shares + i * key_len, "%s\n", result[i]);
478  }
479 
480  free_string_shares(result, n);
481 
482  return shares;
483 }
484 
485 
486 /* Trim spaces at end of string */
487 void trim_trailing_whitespace(char *str) {
488  unsigned long l;
489 
490  if (str == NULL)
491  return;
492 
493  l = strlen(str);
494 
495  if (l < 1)
496  return;
497 
498  while ( (l > 0) && (( str[l - 1] == ' ' ) ||
499  ( str[l - 1] == '\n' ) ||
500  ( str[l - 1] == '\r' ) ||
501  ( str[l - 1] == '\t' )) ) {
502  str[l - 1] = '\0';
503  l = strlen(str);
504  }
505 }
506 
507 
508 /*
509  extract_secret_from_share_strings() -- get a raw string, tidy it up
510  into individual shares, and then extract secret
511 */
512 
513 char * extract_secret_from_share_strings(const char * string) {
514  char ** shares = malloc(sizeof(char *) * 255);
515 
516  char * share;
517  char * saveptr = NULL;
518  int i = 0;
519 
520  /* strtok_rr modifies the string we are looking at, so make a temp copy */
521  char * temp_string = strdup(string);
522 
523  /* Parse the string by line, remove trailing whitespace */
524  share = strtok_rr(temp_string, "\n", &saveptr);
525 
526  shares[i] = strdup(share);
527  trim_trailing_whitespace(shares[i]);
528 
529  while ( (share = strtok_rr(NULL, "\n", &saveptr))) {
530  i++;
531 
532  shares[i] = strdup(share);
533 
534  trim_trailing_whitespace(shares[i]);
535 
536  if ((shares[i] != NULL) && (strlen(shares[i]) == 0)) {
537  /* Ignore blank lines */
538  free(shares[i]);
539  i--;
540  }
541  }
542 
543  i++;
544 
545  char * secret = join_strings(shares, i);
546 
547 
548 /*
549  fprintf(stdout, "count: %d\n", i);
550 
551  for (int j = 0; j < i; ++j)
552  {
553  fprintf(stderr, "'%s'\n", shares[j]);
554  }
555 */
556 
557  free_string_shares(shares, i);
558 
559  return secret;
560 }
char * extract_secret_from_share_strings(const char *string)
Given a list of shares (\n separated without leading whitespace), recreate the original secret...
Definition: shamir.c:513
char * generate_share_strings(char *secret, int n, int t)
Given a secret, n, and t, create a list of shares (\n separated).
Definition: shamir.c:466
void seed_random(void)
Seed the random number generator. MUST BE CALLED before using the library (unless on arc4random() sys...
Definition: shamir.c:86
Simple library for providing SSS functionality.