Shamir's Secret Sharing in three forms:
- A GUI app
- A standard Java API
- A reactive Java API
All forms use finite field arithmetic to prevent geometric attacks.
Having at least a cursory understanding of Shamir's Secret Sharing is beneficial before using the APIs. The Wikipedia entry is recommended as a good starting point.
The GUI app provides a simple way to use Shamir's Secret Sharing.
Native packages are available for MacOS and Windows, and a JAR is available for all other cases.
Latest releases:
Each release is PGP signed for security, and the signer's public key can be found here.
You can also build the releases from the source for added security.
Start by getting a copy of the master branch. You can download it from Github or clone it by running:
git clone -b master https://github.com/MatthewTamlin/Shamir
The native releases for MacOS and Windows come bundled with a minified JRE, however for technical and legal reasons the JREs aren't included in this repository. To install the JREs:
- Download the MacOS and Windows TAR releases from here.
- Unzip the MacOS JRE and copy the contents of the unzipped
Home
directory to/app/deployment/JREs/macos
. - Unzip the windows JRE to
/app/deployment/JREs/windows
.
Next open the command line in the repository's root directory and run the following commands:
# On Unix style command lines
chmod +x gradlew
./gradlew cleanAllModules buildAllModules :app:buildAllReleases
# On Windows
gradlew.bat cleanAllModules buildAllModules :app:buildAllReleases
If you only want to build the release for a specific platform, replace :app:buildAllReleases
with :app:buildMacOsRelease
or :app:buildWindowsRelease
.
The release artifacts are deployed to the relevant subdirectories of /app/build/
.
A 4096 bit prime is used as the basis of the finite field, therefore the GUI can only be used to share files which are at most 510 bytes long (two bytes are reserved for safety and encoding). To share larger files, first use a symmetric encryption protocol to encrypt the payload, and then use the GUI app to convert the key into shares. If you use a well-known protocol such as AES, then it should be safe to distribute the encrypted payload to each shareholder.
The next steps for the GUI app are:
- Randomise the prime per installation.
- Provide better descriptions when files are selected.
The standard Java API provides Shamir's Secret Sharing using standard Java patterns.
To add the standard API to your project, add the following to you gradle build file:
repositories {
jcenter()
}
dependencies {
implementation 'com.matthew-tamlin:shamir:2.0.1'
}
Older versions are available in the Maven repo.
This example demonstrates how to use the standard API to share and recover a secret. The example uses a K=3 N=5 scheme, meaning that 5 shares are created in total and a minimum of 3 are needed to reconstruct the secret.
For the example let the secret be 973490247382347
.
The sharing/recovery operations are provided by the Shamir
class. To instantiate the class:
Shamir shamir = new Shamir(new SecureRandom());
Alternatively:
Shamir shamir = Shamir.create(new SecureRandom());
To share the secret:
BigInteger secret = new BigInteger("973490247382347");
// The prime must be greater than the secret and the total number of shares
BigInteger prime = new BigInteger("2305843009213693951");
CreationScheme creationScheme = CreationScheme
.builder()
.setRequiredShareCount(3)
.setTotalShareCount(5)
.setPrime(prime)
.build();
Set<Share> shares = shamir.createShares(secret, creationScheme);
Each share contains an index and a value. The example yields the following shares:
- index = 1, value = 1007431061686543935
- index = 2, value = 1805108382619357109
- index = 3, value = 88162443832127918
- index = 4, value = 468279263752244264
- index = 5, value = 639615833166012196
To recover the secret:
// Must contain the same values as the creation scheme (excluding the total share count)
RecoveryScheme recoveryScheme = RecoveryScheme
.builder()
.setRequiredShareCount(3)
.setPrime(prime)
.build();
// For brevity, assume there's some method that takes the first three shares
Set<Share> threeShares = takeFirstThree(shares);
BigInteger recoveredSecret = shamir.recoverSecret(threeShares, recoveryScheme);
The example yields a recovered secret of 973490247382347
which matches the original secret.
The standard API is compatible with Java 1.8 and up.
The reactive API is functionally the same as the standard API, but the interface has been changed to use reactive types from RxJava.
To add the reactive API to your project, add the following to you gradle build file:
repositories {
jcenter()
}
dependencies {
implementation 'com.matthew-tamlin:rxshamir:2.0.1'
}
Older versions are available in the Maven repo.
This example demonstrates how to use the reactive API to share and recover a secret. For brevity, the example will use the definitions for the secret, the prime, the creation scheme and the recovery scheme from the previous example.
The sharing/recovery operations are provided by the RxShamir
class. To instantiate the class:
RxShamir shamir = new RxShamir(new SecureRandom());
Alternatively:
RxShamir shamir = RxShamir.create(new SecureRandom());
To share the secret:
Observable<Share> shares = rxShamir.createShares(secret, creationScheme);
Each share contains an index and a value. The example yields an observable which emits the following shares:
- index = 1, value = 1007431061686543935
- index = 2, value = 1805108382619357109
- index = 3, value = 88162443832127918
- index = 4, value = 468279263752244264
- index = 5, value = 639615833166012196
To recover the secret:
Single<BigInteger> recoveredSecret = shares
.take(3)
.collectInto(new HashSet<Share>(), Set::add)
.flatMap(threeShares -> rxShamir.recoverSecret(threeShares, recoveryScheme));
The example yields a single that emits 973490247382347
. Thus the recovered secret is equal to the original secret.
The reactive API is compatible with Java 1.8 and up.
The standard API and the reactive API produce the same results given the same inputs, therefore the APIs can be used interchangeably without migration/conversion.