Initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
nb-configuration.xml
|
||||
target
|
||||
@@ -0,0 +1 @@
|
||||
language: java
|
||||
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Marko Zivanovic
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>rs.in.zivanovic</groupId>
|
||||
<artifactId>sss</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.groupId}:${project.artifactId}</name>
|
||||
<description>Implementation of the Shamir's Secret Sharing algorithm.</description>
|
||||
<url>https://github.com/zmarko/sss</url>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>MIT license</name>
|
||||
<url>http://opensource.org/licenses/MIT</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>marko</id>
|
||||
<name>Marko Zivanovic</name>
|
||||
<email>marko@zivanovic.in.rs</email>
|
||||
<url>http://marko.zivanovic.in.rs</url>
|
||||
<roles>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
<timezone>+1</timezone>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:git@github.com:zmarko/sss.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:zmarko/sss.git</developerConnection>
|
||||
<url>git@github.com:zmarko/sss.git</url>
|
||||
</scm>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.10</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.10.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
<version>2.5.1</version>
|
||||
<configuration>
|
||||
<autoVersionSubmodules>true</autoVersionSubmodules>
|
||||
<useReleaseProfile>false</useReleaseProfile>
|
||||
<releaseProfiles>release</releaseProfiles>
|
||||
<goals>deploy</goals>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2014 Marko Zivanovic.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package rs.in.zivanovic.sss;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Utility methods for generating, transforming, encoding and decoding input data into format that we can do the math
|
||||
* with.
|
||||
*/
|
||||
public final class SasUtils {
|
||||
|
||||
private static final Random RANDOM = new SecureRandom();
|
||||
private static final byte[] SIGNATURE = "SS".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
/**
|
||||
* Encode any valid Unicode string to integer.
|
||||
*
|
||||
* @param str string to encode as integer.
|
||||
* @return integer representation of the secret string.
|
||||
*/
|
||||
public static BigInteger encodeStringToInteger(String str) {
|
||||
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] res;
|
||||
if ((bytes[0] & 0b10000000) >> 7 == 1) {
|
||||
res = new byte[bytes.length + 1];
|
||||
res[0] = 0;
|
||||
System.arraycopy(bytes, 0, res, 1, bytes.length);
|
||||
} else {
|
||||
res = bytes;
|
||||
}
|
||||
BigInteger r = new BigInteger(res);
|
||||
assert r.compareTo(BigInteger.ZERO) > 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode integer encoded with {@link #encodeStringToInteger(java.lang.String) } back to Unicode string form.
|
||||
*
|
||||
* @param num integer to decode back to string.
|
||||
* @return decoded string.
|
||||
*/
|
||||
@SuppressWarnings("empty-statement")
|
||||
public static String decodeIntegerToString(BigInteger num) {
|
||||
byte[] bytes = num.toByteArray();
|
||||
int count;
|
||||
for (count = 0; count < bytes.length && bytes[count] == 0; count++);
|
||||
byte[] trimmed = new byte[bytes.length - count];
|
||||
System.arraycopy(bytes, count, trimmed, 0, trimmed.length);
|
||||
return new String(trimmed, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate first probable prime greater than the number specified.
|
||||
*
|
||||
* @param num lower bound for the generated probable prime
|
||||
* @return probable prime greater than the specified number
|
||||
*/
|
||||
public static BigInteger generateFirstPrimeGreaterThan(BigInteger num) {
|
||||
return num.nextProbablePrime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random probable prime guaranteed to be greater than the number specified.
|
||||
*
|
||||
* @param num lower bound for the generated probable prime
|
||||
* @return random probable prime greater than the specified number
|
||||
*/
|
||||
public static BigInteger generateRandomPrimeGreaterThan(BigInteger num) {
|
||||
BigInteger res = BigInteger.ZERO;
|
||||
while (res.compareTo(num) <= 0) {
|
||||
res = BigInteger.probablePrime(num.bitLength(), RANDOM);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random number guaranteed to be less than the number specified.
|
||||
*
|
||||
* @param num upper bound of the generated random number
|
||||
* @return random number guaranteed to be less than the specified number
|
||||
*/
|
||||
public static BigInteger generateRandomIntegerLessThan(BigInteger num) {
|
||||
BigInteger r = null;
|
||||
while (r == null || r.compareTo(num) >= 0) {
|
||||
r = new BigInteger(num.bitLength(), RANDOM);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random coefficients for the Shamir's Secret Sharing algorithm. First coefficient is filled with a known
|
||||
* value and the rest of the coefficients are randomly generated, keeping them less than the specified prime.
|
||||
*
|
||||
* @param n number of coefficients to generate
|
||||
* @param elementZero value of the first coefficient
|
||||
* @param prime upper bound for randomly generated coefficients
|
||||
* @return array of generated coefficients
|
||||
*/
|
||||
public static BigInteger[] generateRandomCoefficients(int n, BigInteger elementZero, BigInteger prime) {
|
||||
BigInteger[] res = new BigInteger[n];
|
||||
res[0] = elementZero;
|
||||
for (int i = 1; i < n; i++) {
|
||||
res[i] = SasUtils.generateRandomIntegerLessThan(prime);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize secret share to binary message format. The message consists of the following fields:
|
||||
* <ul>
|
||||
* <li>header (ASCII string "SS")</li>
|
||||
* <li>single byte indicating the ordinal number of this specific share in the series</li>
|
||||
* <li>single integer (four bytes) indicating the length of the share data</li>
|
||||
* <li>variable number of bytes (see previous item) representing share data</li>
|
||||
* <li>single integer (four bytes) indicating the length of the prime data</li>
|
||||
* <li>variable number of bytes (see previous item) representing prime data</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param share secret share to serialize
|
||||
* @return byte array representing serialized secret share
|
||||
*/
|
||||
public static byte[] encodeToBinary(SecretShare share) {
|
||||
if (share.getN() < 0 || share.getN() > 255) {
|
||||
throw new IllegalArgumentException("Invalid share number, must be between 0 and 255");
|
||||
}
|
||||
|
||||
byte[] shareData = share.getShare().toByteArray();
|
||||
byte[] primeData = share.getPrime().toByteArray();
|
||||
byte n = new Integer(share.getN()).byteValue();
|
||||
|
||||
int len = 9 + SIGNATURE.length + shareData.length + primeData.length;
|
||||
|
||||
ByteBuffer bb = ByteBuffer.allocate(len);
|
||||
bb.put(SIGNATURE);
|
||||
bb.put(n);
|
||||
bb.putInt(shareData.length);
|
||||
bb.put(shareData);
|
||||
bb.putInt(primeData.length);
|
||||
bb.put(primeData);
|
||||
|
||||
assert bb.position() == bb.capacity();
|
||||
assert bb.hasArray();
|
||||
|
||||
return bb.array();
|
||||
}
|
||||
|
||||
/**
|
||||
* De-serialize secret share from binary message data. Serialization mechanism is detailed in {@link #encodeToBinary(rs.in.zivanovic.sss.SecretShare)
|
||||
* }
|
||||
*
|
||||
* @param data binary data to de-serialize
|
||||
* @return secret share data
|
||||
*/
|
||||
public static SecretShare decodeFromBinary(byte[] data) {
|
||||
ByteBuffer bb = ByteBuffer.wrap(data);
|
||||
try {
|
||||
byte[] signature = new byte[SIGNATURE.length];
|
||||
bb.get(signature);
|
||||
if (!Arrays.equals(SIGNATURE, signature)) {
|
||||
throw new IllegalArgumentException("signature missing");
|
||||
}
|
||||
byte n = bb.get();
|
||||
int shareDataLen = bb.getInt();
|
||||
byte[] shareData = new byte[shareDataLen];
|
||||
bb.get(shareData);
|
||||
int primeDataLen = bb.getInt();
|
||||
byte[] primeData = new byte[primeDataLen];
|
||||
bb.get(primeData);
|
||||
|
||||
BigInteger share = new BigInteger(shareData);
|
||||
BigInteger prime = new BigInteger(primeData);
|
||||
if (share.compareTo(BigInteger.ZERO) <= 0) {
|
||||
throw new IllegalArgumentException("invalid share number");
|
||||
}
|
||||
if (prime.compareTo(BigInteger.ZERO) <= 0) {
|
||||
throw new IllegalArgumentException("invalid prime");
|
||||
}
|
||||
return new SecretShare(n, share, prime);
|
||||
} catch (BufferUnderflowException ex) {
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private SasUtils() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2014 Marko Zivanovic.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package rs.in.zivanovic.sss;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Value class containing data necessary to fully describe single secret share in the series.
|
||||
*/
|
||||
public final class SecretShare {
|
||||
|
||||
private final int n;
|
||||
private final BigInteger share;
|
||||
private final BigInteger prime;
|
||||
|
||||
/**
|
||||
* Construct secret share object with the specified data.
|
||||
*
|
||||
* @param n ordinal number of this specific share in the series
|
||||
* @param share specific share data
|
||||
* @param prime prime number used for the series
|
||||
*/
|
||||
public SecretShare(int n, BigInteger share, BigInteger prime) {
|
||||
this.n = n;
|
||||
this.share = share;
|
||||
this.prime = prime;
|
||||
}
|
||||
|
||||
public int getN() {
|
||||
return n;
|
||||
}
|
||||
|
||||
public BigInteger getShare() {
|
||||
return share;
|
||||
}
|
||||
|
||||
public BigInteger getPrime() {
|
||||
return prime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 79 * hash + this.n;
|
||||
hash = 79 * hash + Objects.hashCode(this.share);
|
||||
hash = 79 * hash + Objects.hashCode(this.prime);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final SecretShare other = (SecretShare) obj;
|
||||
if (this.n != other.n) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(this.share, other.share)) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(this.prime, other.prime)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SecretShare{" + "n=" + n + ", share=" + share + ", prime=" + prime + '}';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2014 Marko Zivanovic.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package rs.in.zivanovic.sss;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implementation of the Shamir's Secret Sharing algorithm. Both splitting the secret into shares and joining the shares
|
||||
* back into the secret are supported. Since arbitrary-precision integers are used, there is, effectively, no limit on
|
||||
* the length of the secret data.
|
||||
*
|
||||
*/
|
||||
public final class ShamirSecretSharing {
|
||||
|
||||
/**
|
||||
* Split the secret into the specified total number of shares. Specified minimum threshold of shares will need to be
|
||||
* present in order to join them back into the secret. Polynomial coefficients and prime modulo will be randomly
|
||||
* chosen.
|
||||
*
|
||||
* @param secretString string to convert into number and split into shares
|
||||
* @param total number of shares to generate
|
||||
* @param threshold minimum number of shares required to successfully join back the secret
|
||||
* @return list of shares
|
||||
*/
|
||||
public static List<SecretShare> split(String secretString, int total, int threshold) {
|
||||
BigInteger secret = SasUtils.encodeStringToInteger(secretString);
|
||||
return split(secret, total, threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the secret into the specified total number of shares. Specified minimum threshold of shares will need to be
|
||||
* present in order to join them back into the secret. Polynomial coefficients and prime modulo will be randomly
|
||||
* chosen.
|
||||
*
|
||||
* @param secret number to split into shares
|
||||
* @param total number of shares to generate
|
||||
* @param threshold minimum number of shares required to successfully join back the secret
|
||||
* @return list of shares
|
||||
*/
|
||||
public static List<SecretShare> split(BigInteger secret, int total, int threshold) {
|
||||
BigInteger prime = SasUtils.generateFirstPrimeGreaterThan(secret);
|
||||
BigInteger[] coeffs = SasUtils.generateRandomCoefficients(total, secret, prime);
|
||||
return split(secret, coeffs, total, threshold, prime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the secret into the specified total number of shares. Specified minimum threshold of shares will need to be
|
||||
* present in order to join them back into the secret.
|
||||
*
|
||||
* @param secret number to split into shares
|
||||
* @param coefficients random coefficients of the underlying polynomial
|
||||
* @param total number of shares to generate
|
||||
* @param threshold minimum number of shares required to successfully join back the secret
|
||||
* @param prime to be used for finite field arithmetic
|
||||
* @return list of shares
|
||||
*/
|
||||
public static List<SecretShare> split(BigInteger secret, BigInteger[] coefficients, int total, int threshold,
|
||||
BigInteger prime) {
|
||||
|
||||
if (secret.compareTo(BigInteger.ZERO) <= 0) {
|
||||
throw new IllegalArgumentException("secret must be positive integer");
|
||||
}
|
||||
|
||||
if (prime.compareTo(secret) <= 0) {
|
||||
throw new IllegalArgumentException("prime must be greater than secret");
|
||||
}
|
||||
|
||||
if (coefficients.length < threshold) {
|
||||
throw new IllegalArgumentException("not enough coefficients, need " + threshold + ", have " +
|
||||
coefficients.length);
|
||||
}
|
||||
|
||||
if (total < threshold) {
|
||||
throw new IllegalArgumentException("total number of shares must be greater than or equal threshold");
|
||||
}
|
||||
|
||||
List<SecretShare> shares = new ArrayList<>();
|
||||
|
||||
for (int i = 1; i <= total; i++) {
|
||||
BigInteger x = BigInteger.valueOf(i);
|
||||
BigInteger v = coefficients[0];
|
||||
for (int c = 1; c < threshold; c++) {
|
||||
v = v.add(x.modPow(BigInteger.valueOf(c), prime).multiply(coefficients[c]).mod(prime)).mod(prime);
|
||||
}
|
||||
shares.add(new SecretShare(i, v, prime));
|
||||
}
|
||||
return shares;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join shares back into the secret.
|
||||
*
|
||||
* @param shares to join
|
||||
* @return secret string
|
||||
*/
|
||||
public static String joinToUtf8String(List<SecretShare> shares) {
|
||||
return SasUtils.decodeIntegerToString(join(shares));
|
||||
}
|
||||
|
||||
/**
|
||||
* Join shares back into the secret.
|
||||
*
|
||||
* @param shares to join
|
||||
* @return secret number
|
||||
*/
|
||||
public static BigInteger join(List<SecretShare> shares) {
|
||||
if (!checkSamePrimes(shares)) {
|
||||
throw new IllegalArgumentException("shares not from the same series");
|
||||
}
|
||||
BigInteger res = BigInteger.ZERO;
|
||||
for (int i = 0; i < shares.size(); i++) {
|
||||
BigInteger n = BigInteger.ONE;
|
||||
BigInteger d = BigInteger.ONE;
|
||||
BigInteger prime = shares.get(i).getPrime();
|
||||
for (int j = 0; j < shares.size(); j++) {
|
||||
if (i != j) {
|
||||
BigInteger sp = BigInteger.valueOf(shares.get(i).getN());
|
||||
BigInteger np = BigInteger.valueOf(shares.get(j).getN());
|
||||
n = n.multiply(np.negate()).mod(prime);
|
||||
d = d.multiply(sp.subtract(np)).mod(prime);
|
||||
}
|
||||
}
|
||||
BigInteger v = shares.get(i).getShare();
|
||||
res = res.add(prime).add(v.multiply(n).multiply(d.modInverse(prime))).mod(prime);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if all shares have the same prime. If they do not, then they are not from the same series and cannot
|
||||
* possibly be joined.
|
||||
*
|
||||
* @param shares to check
|
||||
* @return true if all shares have the same prime, false if not.
|
||||
*/
|
||||
private static boolean checkSamePrimes(List<SecretShare> shares) {
|
||||
boolean ret = true;
|
||||
BigInteger prime = null;
|
||||
for (SecretShare share : shares) {
|
||||
if (prime == null) {
|
||||
prime = share.getPrime();
|
||||
} else if (!prime.equals(share.getPrime())) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private ShamirSecretSharing() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2014 Marko Zivanovic.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package rs.in.zivanovic.sss;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SasUtilsTest {
|
||||
|
||||
private static final String s1 = "Hello World!";
|
||||
|
||||
public SasUtilsTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateFirstPrimeGreaterThan() {
|
||||
System.out.println("generateFirstPrimeGreaterThan");
|
||||
BigInteger si1 = SasUtils.encodeStringToInteger(s1);
|
||||
BigInteger p1 = SasUtils.generateFirstPrimeGreaterThan(si1);
|
||||
assertTrue(p1.compareTo(si1) > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateRandomPrimeGreaterThan() {
|
||||
System.out.println("generateRandomPrimeGreaterThan");
|
||||
BigInteger si1 = SasUtils.encodeStringToInteger(s1);
|
||||
BigInteger p1 = SasUtils.generateRandomPrimeGreaterThan(si1);
|
||||
assertTrue(p1.compareTo(si1) > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateRandomIntegerLessThan() {
|
||||
System.out.println("generateRandomIntegerLessThan");
|
||||
BigInteger i = BigInteger.valueOf(100);
|
||||
BigInteger r = SasUtils.generateRandomIntegerLessThan(i);
|
||||
assertTrue(r.compareTo(i) < 0);
|
||||
i = BigInteger.valueOf(100_000_000);
|
||||
r = SasUtils.generateRandomIntegerLessThan(i);
|
||||
assertTrue(r.compareTo(i) < 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBinaryCodec() {
|
||||
System.out.println("binaryCodec");
|
||||
SecretShare src = new SecretShare(3, new BigInteger("100"), new BigInteger("200"));
|
||||
byte[] data = SasUtils.encodeToBinary(src);
|
||||
SecretShare dst = SasUtils.decodeFromBinary(data);
|
||||
assertTrue(src.equals(dst));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecretCodec() {
|
||||
System.out.println("secretCodec");
|
||||
BigInteger bi = SasUtils.encodeStringToInteger(s1);
|
||||
String d = SasUtils.decodeIntegerToString(bi);
|
||||
assertTrue(s1.equals(d));
|
||||
}
|
||||
|
||||
@Test(expected = java.lang.IllegalArgumentException.class)
|
||||
public void testDecodeEmptyMessage() {
|
||||
System.out.println("decodeEmptyMessage");
|
||||
byte[] b = new byte[0];
|
||||
SecretShare ss = SasUtils.decodeFromBinary(b);
|
||||
}
|
||||
|
||||
@Test(expected = java.lang.IllegalArgumentException.class)
|
||||
public void testDecodePartialMessage() {
|
||||
System.out.println("decodePartialMessage");
|
||||
SecretShare src = new SecretShare(3, new BigInteger("100"), new BigInteger("200"));
|
||||
byte[] buff = SasUtils.encodeToBinary(src);
|
||||
byte[] partial = new byte[buff.length / 2];
|
||||
System.arraycopy(buff, 0, partial, 0, partial.length);
|
||||
SecretShare ss = SasUtils.decodeFromBinary(partial);
|
||||
}
|
||||
|
||||
@Test(expected = java.lang.IllegalArgumentException.class)
|
||||
public void testDecodeDamagedHeaderMessage() {
|
||||
System.out.println("decodeDamagedHeader");
|
||||
SecretShare src = new SecretShare(3, new BigInteger("100"), new BigInteger("200"));
|
||||
byte[] buff = SasUtils.encodeToBinary(src);
|
||||
buff[0] = 'X';
|
||||
SecretShare ss = SasUtils.decodeFromBinary(buff);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2014 Marko Zivanovic.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package rs.in.zivanovic.sss;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ShamirSecretSharingTest {
|
||||
|
||||
private static final BigInteger[] TEST_COEFFICIENTS = new BigInteger[3];
|
||||
private static final BigInteger TEST_PRIME = BigInteger.valueOf(1613);
|
||||
private static final BigInteger TEST_SECRET = BigInteger.valueOf(1234);
|
||||
private static final List<SecretShare> SHARES;
|
||||
|
||||
static {
|
||||
TEST_COEFFICIENTS[0] = TEST_SECRET;
|
||||
TEST_COEFFICIENTS[1] = BigInteger.valueOf(166);
|
||||
TEST_COEFFICIENTS[2] = BigInteger.valueOf(94);
|
||||
SHARES = ShamirSecretSharing.split(TEST_SECRET, TEST_COEFFICIENTS, 6, 3, TEST_PRIME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplit() {
|
||||
System.out.println("split");
|
||||
assertTrue(SHARES.get(0).getShare().compareTo(BigInteger.valueOf(1494)) == 0);
|
||||
assertTrue(SHARES.get(1).getShare().compareTo(BigInteger.valueOf(329)) == 0);
|
||||
assertTrue(SHARES.get(2).getShare().compareTo(BigInteger.valueOf(965)) == 0);
|
||||
assertTrue(SHARES.get(3).getShare().compareTo(BigInteger.valueOf(176)) == 0);
|
||||
assertTrue(SHARES.get(4).getShare().compareTo(BigInteger.valueOf(1188)) == 0);
|
||||
assertTrue(SHARES.get(5).getShare().compareTo(BigInteger.valueOf(775)) == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinInsufficientShares() {
|
||||
System.out.println("joinInsufficientShares");
|
||||
List<SecretShare> shares = new ArrayList<>();
|
||||
shares.add(SHARES.get(0));
|
||||
shares.add(SHARES.get(5));
|
||||
BigInteger joined = ShamirSecretSharing.join(shares);
|
||||
assertTrue(joined.compareTo(TEST_SECRET) != 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinSufficientShares() {
|
||||
System.out.println("joinSufficientShares");
|
||||
List<SecretShare> shares = new ArrayList<>();
|
||||
shares.add(SHARES.get(3));
|
||||
shares.add(SHARES.get(5));
|
||||
shares.add(SHARES.get(1));
|
||||
BigInteger joined = ShamirSecretSharing.join(shares);
|
||||
assertTrue(joined.compareTo(TEST_SECRET) == 0);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user