Initial version.
This commit is contained in:
@@ -0,0 +1,75 @@
|
|||||||
|
<?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>share-a-secret</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.10</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<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>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.github.wvengen</groupId>
|
||||||
|
<artifactId>proguard-maven-plugin</artifactId>
|
||||||
|
<version>2.0.6</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>proguard</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<options>
|
||||||
|
<option>-allowaccessmodification</option>
|
||||||
|
<option>-keep public class rs.in.zivanovic.share.a.secret.Main { *; }</option>
|
||||||
|
</options>
|
||||||
|
<libs>
|
||||||
|
<lib>${java.home}/lib/rt.jar</lib>
|
||||||
|
</libs>
|
||||||
|
<assembly>
|
||||||
|
<inclusions>
|
||||||
|
<!-- <inclusion>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
</inclusion>-->
|
||||||
|
</inclusions>
|
||||||
|
</assembly>
|
||||||
|
</configuration>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sf.proguard</groupId>
|
||||||
|
<artifactId>proguard-base</artifactId>
|
||||||
|
<version>4.9</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>2.5</version>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<addClasspath>true</addClasspath>
|
||||||
|
<mainClass>rs.in.zivanovic.share.a.secret.Main</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014, marko
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package rs.in.zivanovic.share.a.secret;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author marko
|
||||||
|
*/
|
||||||
|
public class Main {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
if (args.length == 1 && args[0].toLowerCase().equals("-d")) {
|
||||||
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
|
||||||
|
List<SecretShare> shares = new ArrayList<>();
|
||||||
|
System.out.println("Enter a number of shares, one per line, that you'd like to try to join.");
|
||||||
|
System.out.println("Finish with empty line.");
|
||||||
|
while (true) {
|
||||||
|
String line = br.readLine();
|
||||||
|
if (line != null && !line.isEmpty()) {
|
||||||
|
try {
|
||||||
|
int s = line.indexOf("U1");
|
||||||
|
if (s >= 0) {
|
||||||
|
SecretShare share = Utils.decodeFromBinary(Base64.decode(line.substring(s)));
|
||||||
|
shares.add(share);
|
||||||
|
} else {
|
||||||
|
System.err.println("Invalid data");
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
System.err.println(ex.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String secret = ShamirSecretSharing.joinToUtf8String(shares);
|
||||||
|
System.out.println("Secret = " + secret);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
System.err.println(ex.getMessage());
|
||||||
|
}
|
||||||
|
} else if (args.length == 3 && args[0].toLowerCase().equals("-e")) {
|
||||||
|
Integer total = Integer.valueOf(args[1]);
|
||||||
|
Integer threshold = Integer.valueOf(args[2]);
|
||||||
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
|
||||||
|
String line = br.readLine();
|
||||||
|
List<SecretShare> shares = ShamirSecretSharing.split(line, total, threshold);
|
||||||
|
for (SecretShare share : shares) {
|
||||||
|
System.out.println(Base64.encodeBytes(Utils.encodeToBinary(share)));
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
System.err.println(ex.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.err.println("Share-A-Secret 1.0.0-SNAPSHOT");
|
||||||
|
System.err.println("This application uses Shamir's secret sharing algorithm to split the secret data into");
|
||||||
|
System.err.println("a desired number of shares. In order to retrieve the secret data, at least a required");
|
||||||
|
System.err.println("minimum number of shares must be present.");
|
||||||
|
System.err.println();
|
||||||
|
System.err.println("All data is read from standard input and sent to standard output.");
|
||||||
|
System.err.println();
|
||||||
|
System.err.println("To encode: java -jar sas.jar -e <n> <t>");
|
||||||
|
System.err.println("To decode: java -jar sas.jar -d");
|
||||||
|
System.err.println();
|
||||||
|
System.err.println(" n - total number of shares to generate");
|
||||||
|
System.err.println(" t - minimum number of shares required to retrieve the secret.");
|
||||||
|
System.err.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014, marko
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package rs.in.zivanovic.share.a.secret;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author marko
|
||||||
|
*/
|
||||||
|
public final class SecretShare {
|
||||||
|
|
||||||
|
private final int n;
|
||||||
|
private final BigInteger share;
|
||||||
|
private final BigInteger prime;
|
||||||
|
|
||||||
|
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,126 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014, marko
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package rs.in.zivanovic.share.a.secret;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author marko
|
||||||
|
*/
|
||||||
|
public final class ShamirSecretSharing {
|
||||||
|
|
||||||
|
public static List<SecretShare> split(String secretString, int total, int threshold) {
|
||||||
|
BigInteger secret = Utils.encodeSecret(secretString);
|
||||||
|
return split(secret, total, threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SecretShare> split(BigInteger secretNumber, int total, int threshold) {
|
||||||
|
BigInteger prime = Utils.getFirstPrimeGreaterThan(secretNumber);
|
||||||
|
BigInteger[] coeffs = Utils.generateRandomCoefficients(total, secretNumber, prime);
|
||||||
|
return split(secretNumber, coeffs, total, threshold, prime);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 + ", has " +
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String joinToUtf8String(List<SecretShare> shares) {
|
||||||
|
return Utils.decodeSecret(join(shares));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,148 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014, marko
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package rs.in.zivanovic.share.a.secret;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author marko
|
||||||
|
*/
|
||||||
|
public final class Utils {
|
||||||
|
|
||||||
|
private static final Random RANDOM = new SecureRandom();
|
||||||
|
private static final byte[] SIGNATURE = "SS".getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
public static BigInteger encodeSecret(String secret) {
|
||||||
|
byte[] bytes = secret.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("empty-statement")
|
||||||
|
public static String decodeSecret(BigInteger secret) {
|
||||||
|
byte[] bytes = secret.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BigInteger getFirstPrimeGreaterThan(BigInteger secret) {
|
||||||
|
return secret.nextProbablePrime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BigInteger getRandomPrimeGreaterThan(BigInteger secret) {
|
||||||
|
BigInteger res = BigInteger.ZERO;
|
||||||
|
while (res.compareTo(secret) <= 0) {
|
||||||
|
res = BigInteger.probablePrime(secret.bitLength(), RANDOM);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BigInteger getRandomLessThan(BigInteger prime) {
|
||||||
|
BigInteger r = null;
|
||||||
|
while (r == null || r.compareTo(prime) >= 0) {
|
||||||
|
r = new BigInteger(prime.bitLength(), RANDOM);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
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] = Utils.getRandomLessThan(prime);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] encodeToBinary(SecretShare share) {
|
||||||
|
assert share.getN() >= 0;
|
||||||
|
assert share.getN() <= 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SecretShare decodeFromBinary(byte[] data) {
|
||||||
|
ByteBuffer bb = ByteBuffer.wrap(data);
|
||||||
|
byte[] signature = new byte[SIGNATURE.length];
|
||||||
|
bb.get(signature);
|
||||||
|
if (!Arrays.equals(SIGNATURE, signature)) {
|
||||||
|
throw new IllegalArgumentException("Invalid data");
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|
||||||
|
assert bb.position() == bb.capacity();
|
||||||
|
|
||||||
|
BigInteger share = new BigInteger(shareData);
|
||||||
|
BigInteger prime = new BigInteger(primeData);
|
||||||
|
assert share.compareTo(BigInteger.ZERO) > 0;
|
||||||
|
assert prime.compareTo(BigInteger.ZERO) > 0;
|
||||||
|
return new SecretShare(n, share, prime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Utils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014, marko
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package rs.in.zivanovic.share.a.secret;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author marko
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLongStrings() {
|
||||||
|
System.out.println("longStrings");
|
||||||
|
String s = "Quick brown fox jumps over the lazy dog.";
|
||||||
|
test(s, 6, 5);
|
||||||
|
|
||||||
|
s = "Текст на ћирилици, чучко, џеки, Ђорђе, ...";
|
||||||
|
test(s, 6, 5);
|
||||||
|
|
||||||
|
s = "A sada da vidimo istu duzinu, ali, obican ";
|
||||||
|
test(s, 6, 5);
|
||||||
|
|
||||||
|
s = "ATTACK AT DAWN";
|
||||||
|
test(s, 6, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void test(String s, int total, int required) {
|
||||||
|
List<SecretShare> shares = ShamirSecretSharing.split(s, total, required);
|
||||||
|
List<SecretShare> someShares = new ArrayList<>();
|
||||||
|
for (int i = required - 1; i >= 0; i--) {
|
||||||
|
someShares.add(shares.get(i));
|
||||||
|
}
|
||||||
|
String joined = ShamirSecretSharing.joinToUtf8String(someShares);
|
||||||
|
assertTrue(joined.equals(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014, marko
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package rs.in.zivanovic.share.a.secret;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author marko
|
||||||
|
*/
|
||||||
|
public class UtilsTest {
|
||||||
|
|
||||||
|
private static final String s1 = "Hello World!";
|
||||||
|
private static final String s2 = "The quick brown fox jumps over the lazy dog.";
|
||||||
|
private static final String s3 = "Текст на ћирилици.";
|
||||||
|
|
||||||
|
public UtilsTest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetFirstPrimeGreaterThan() {
|
||||||
|
System.out.println("getFirstPrimeGreaterThan");
|
||||||
|
BigInteger si1 = Utils.encodeSecret(s1);
|
||||||
|
BigInteger si2 = Utils.encodeSecret(s2);
|
||||||
|
BigInteger p1 = Utils.getFirstPrimeGreaterThan(si1);
|
||||||
|
BigInteger p2 = Utils.getFirstPrimeGreaterThan(si2);
|
||||||
|
assertTrue(p1.compareTo(si1) > 0);
|
||||||
|
assertTrue(p2.compareTo(si2) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetRandomPrimeGreaterThan() {
|
||||||
|
System.out.println("getRandomPrimeGreaterThan");
|
||||||
|
BigInteger si1 = Utils.encodeSecret(s1);
|
||||||
|
BigInteger si2 = Utils.encodeSecret(s2);
|
||||||
|
BigInteger p1 = Utils.getRandomPrimeGreaterThan(si1);
|
||||||
|
BigInteger p2 = Utils.getRandomPrimeGreaterThan(si2);
|
||||||
|
assertTrue(p1.compareTo(si1) > 0);
|
||||||
|
assertTrue(p2.compareTo(si2) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetRandomLessThan() {
|
||||||
|
System.out.println("getRandomLessThan");
|
||||||
|
BigInteger i = BigInteger.valueOf(100);
|
||||||
|
BigInteger r = Utils.getRandomLessThan(i);
|
||||||
|
assertTrue(r.compareTo(i) < 0);
|
||||||
|
i = BigInteger.valueOf(100_000_000);
|
||||||
|
r = Utils.getRandomLessThan(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 = Utils.encodeToBinary(src);
|
||||||
|
SecretShare dst = Utils.decodeFromBinary(data);
|
||||||
|
assertTrue(src.equals(dst));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSecretCodec() {
|
||||||
|
System.out.println("secretCodec");
|
||||||
|
String s = "Текст на ћирилици, чучко, џеки, Ђорђе, ...";
|
||||||
|
BigInteger bi = Utils.encodeSecret(s);
|
||||||
|
String d = Utils.decodeSecret(bi);
|
||||||
|
assertTrue(s.equals(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user