Giter Club home page Giter Club logo

shamir's Introduction

Shamir's Secret Sharing

CircleCI

A Java implementation of Shamir's Secret Sharing algorithm over GF(256).

Add to your project

<dependency>
  <groupId>com.codahale</groupId>
  <artifactId>shamir</artifactId>
  <version>0.7.0</version>
</dependency>

Note: module name for Java 9+ is com.codahale.shamir.

Use the thing

import com.codahale.shamir.Scheme;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Map;

class Example {
  void doIt() {
    final Scheme scheme = new Scheme(new SecureRandom(), 5, 3);
    final byte[] secret = "hello there".getBytes(StandardCharsets.UTF_8);
    final Map<Integer, byte[]> parts = scheme.split(secret);
    final byte[] recovered = scheme.join(parts);
    System.out.println(new String(recovered, StandardCharsets.UTF_8));
  } 
}

How it works

Shamir's Secret Sharing algorithm is a way to split an arbitrary secret S into N parts, of which at least K are required to reconstruct S. For example, a root password can be split among five people, and if three or more of them combine their parts, they can recover the root password.

Splitting secrets

Splitting a secret works by encoding the secret as the constant in a random polynomial of K degree. For example, if we're splitting the secret number 42 among five people with a threshold of three (N=5,K=3), we might end up with the polynomial:

f(x) = 71x^3 - 87x^2 + 18x + 42

To generate parts, we evaluate this polynomial for values of x greater than zero:

f(1) =   44
f(2) =  298
f(3) = 1230
f(4) = 3266
f(5) = 6822

These (x,y) pairs are then handed out to the five people.

Joining parts

When three or more of them decide to recover the original secret, they pool their parts together:

f(1) =   44
f(3) = 1230
f(4) = 3266

Using these points, they construct a Lagrange polynomial, g, and calculate g(0). If the number of parts is equal to or greater than the degree of the original polynomial (i.e. K), then f and g will be exactly the same, and f(0) = g(0) = 42, the encoded secret. If the number of parts is less than the threshold K, the polynomial will be different and g(0) will not be 42.

Implementation details

Shamir's Secret Sharing algorithm only works for finite fields, and this library performs all operations in GF(256). Each byte of a secret is encoded as a separate GF(256) polynomial, and the resulting parts are the aggregated values of those polynomials.

Using GF(256) allows for secrets of arbitrary length and does not require additional parameters, unlike GF(Q), which requires a safe modulus. It's also much faster than GF(Q): splitting and combining a 1KiB secret into 8 parts with a threshold of 3 takes single-digit milliseconds, whereas performing the same operation over GF(Q) takes several seconds, even using per-byte polynomials. Treating the secret as a single y coordinate over GF(Q) is even slower, and requires a modulus larger than the secret.

Performance

It's fast. Plenty fast.

For a 1KiB secret split with a n=4,k=3 scheme:

Benchmark         (n)  (secretSize)  Mode  Cnt     Score    Error  Units
Benchmarks.join     4          1024  avgt  200   196.787 ±  0.974  us/op
Benchmarks.split    4          1024  avgt  200   396.708 ±  1.520  us/op

N.B.: split is quadratic with respect to the number of shares being combined.

Tiered sharing

Some usages of secret sharing involve levels of access: e.g. recovering a secret requires two admin shares and three user shares. As @ba1ciu discovered, these can be implemented by building a tree of shares:

class BuildTree {
  public static void shareTree(String... args) {
    final byte[] secret = "this is a secret".getBytes(StandardCharsets.UTF_8);
    
    // tier 1 of the tree
    final Scheme adminScheme = new Scheme(new SecureRandom(), 5, 2);
    final Map<Integer, byte[]> admins = adminScheme.split(secret);

    // tier 2 of the tree
    final Scheme userScheme = Scheme.of(4, 3);
    final Map<Integer, Map<Integer, byte[]>> admins =
        users.entrySet()
            .stream()
            .collect(Collectors.toMap(Map.Entry::getKey, e -> userScheme.split(e.getValue())));
    
    System.out.println("Admin shares:");
    System.out.printf("%d = %s\n", 1, Arrays.toString(admins.get(1)));
    System.out.printf("%d = %s\n", 2, Arrays.toString(admins.get(2)));

    System.out.println("User shares:");
    System.out.printf("%d = %s\n", 1, Arrays.toString(users.get(3).get(1)));
    System.out.printf("%d = %s\n", 2, Arrays.toString(users.get(3).get(2)));
    System.out.printf("%d = %s\n", 3, Arrays.toString(users.get(3).get(3)));
    System.out.printf("%d = %s\n", 4, Arrays.toString(users.get(3).get(4)));
  }
}

By discarding the third admin share and the first two sets of user shares, we have a set of shares which can be used to recover the original secret as long as either two admins or one admin and three users agree.

License

Copyright © 2017 Coda Hale

Distributed under the Apache License 2.0.

shamir's People

Contributors

codahale avatar kocakosm avatar ryantenney avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

shamir's Issues

use case: given points

I have this use case where I want to construct a polynomial where I have as input the secret and some additional predefined points. The idea is that each "application" (whatever that is) can present its own point(s), and that the secret remains the same, independent of the "application". Hence you can have a single AES encryption (key) shared by all "applications".

readme example for Tiered Sharing looks buggy

The example code on the readme declares admin twice and doesn't declare 'users`.

    final Map<Integer, byte[]> admins = adminScheme.split(secret);

    final Map<Integer, Map<Integer, byte[]>> admins =
        users.entrySet()
            .stream()
            .collect(Collectors.toMap(Map.Entry::getKey, e -> userScheme.split(e.getValue())));

then we see users used but not declared.

    System.out.printf("%d = %s\n", 1, Arrays.toString(users.get(3).get(1)));

I have sent a PR to fix.

Issue in combining the shares generated on iOS Platform #13

Hi

I've been trying to use this library to work with iOS. I keep facing an issue in combining the shares which are generated on iOS platform. Each share generated on iOS is an unsigned integer array, whereas in Java it's in form of signed bytes. I have been converting the unsigned integer array from iOS to signed bytes.

The Scheme.join() method works fine if I provide all the shares generated on iOS but fails if the number of shares are less than the total number number of shares.

public void testCrossPlatform() {
	String secretString = "Hi";
    final byte[] secret = secretString.getBytes(StandardCharsets.UTF_8);
	final Map<Integer, byte[]> parts = scheme.split(secret);
	
	HashMap<Integer, byte[]> allParts = new HashMap<>();
	allParts.put(1, new byte[]{(byte)43, (byte)9});
	allParts.put(2, new byte[]{(byte)37, (byte)145});
	allParts.put(3, new byte[]{(byte)70, (byte)241});

        HashMap<Integer, byte[]> halfParts = new HashMap<>();
	halfParts.put(1, new byte[]{(byte)43, (byte)9});
	halfParts.put(2, new byte[]{(byte)37, (byte)145});
	
	final byte[] recovered = scheme.join(halfParts);
    Log.d("msg", "Recovered String : " + new String(recovered,StandardCharsets.UTF_8));
}

I have been using this library in iOS.

Could you please help me with resolving the issue. Thanks in advance.

Go version

Hi,
I saw that you have archived the GO version. Do you have any recommendation for GO version of this alg implementation?

Thanks.

generator test uses random source

The following test:

 @Test
  void generate() {
    final SecureRandom random = new SecureRandom(); // <- random 
    final byte[] p = GF256.generate(random, 5, (byte) 20);
   // removed for brevity
   assertThat(p[p.length - 1]).isNotZero();
  }

Will find it hard to catch a bug where generate doesn't loop on the random source until p[p.length - 1] != (byte) 0. If a regression is introduced it is unlikely to be caught before the code is released.

original secret validation

If scheme.join() doesn't tell you if returned value is actually the original secret, how do you know if you actually got original secret back? If whole idea is to form original secret on the fly and not having to store it, how do people go about it? Store hash of the original secret and find hash of the value returns by scheme.join() and compare?

Gradle build?

Hello @codahale ,
I would appreciate if you could provide a gradle build for the library. You could use jitpack.io

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.