From 6e603c1f555db4d81dc1e869be48ce60a16bcfc8 Mon Sep 17 00:00:00 2001 From: Marko Zivanovic Date: Sun, 30 Nov 2014 20:21:32 +0100 Subject: [PATCH] Initial commit --- .gitignore | 2 + .travis.yml | 1 + LICENSE | 22 ++ README.md | 0 pom.xml | 111 +++++++++ .../java/rs/in/zivanovic/sss/SasUtils.java | 215 ++++++++++++++++++ .../java/rs/in/zivanovic/sss/SecretShare.java | 98 ++++++++ .../in/zivanovic/sss/ShamirSecretSharing.java | 176 ++++++++++++++ .../rs/in/zivanovic/sss/SasUtilsTest.java | 107 +++++++++ .../sss/ShamirSecretSharingTest.java | 77 +++++++ 10 files changed, 809 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 pom.xml create mode 100644 src/main/java/rs/in/zivanovic/sss/SasUtils.java create mode 100644 src/main/java/rs/in/zivanovic/sss/SecretShare.java create mode 100644 src/main/java/rs/in/zivanovic/sss/ShamirSecretSharing.java create mode 100644 src/test/java/rs/in/zivanovic/sss/SasUtilsTest.java create mode 100644 src/test/java/rs/in/zivanovic/sss/ShamirSecretSharingTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cf9144e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +nb-configuration.xml +target diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..dff5f3a --- /dev/null +++ b/.travis.yml @@ -0,0 +1 @@ +language: java diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b3a6874 --- /dev/null +++ b/LICENSE @@ -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. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..69d22e3 --- /dev/null +++ b/pom.xml @@ -0,0 +1,111 @@ + + + 4.0.0 + rs.in.zivanovic + sss + 1.0.0-SNAPSHOT + jar + + ${project.groupId}:${project.artifactId} + Implementation of the Shamir's Secret Sharing algorithm. + https://github.com/zmarko/sss + + + UTF-8 + 1.7 + 1.7 + + + + + MIT license + http://opensource.org/licenses/MIT + repo + + + + + + marko + Marko Zivanovic + marko@zivanovic.in.rs + http://marko.zivanovic.in.rs + + developer + + +1 + + + + + scm:git:git@github.com:zmarko/sss.git + scm:git:git@github.com:zmarko/sss.git + git@github.com:zmarko/sss.git + + + + + junit + junit + 4.10 + test + + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.4 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.1 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.1 + + true + false + release + deploy + + + + + + \ No newline at end of file diff --git a/src/main/java/rs/in/zivanovic/sss/SasUtils.java b/src/main/java/rs/in/zivanovic/sss/SasUtils.java new file mode 100644 index 0000000..451f76a --- /dev/null +++ b/src/main/java/rs/in/zivanovic/sss/SasUtils.java @@ -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: + * + * + * @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() { + } + +} diff --git a/src/main/java/rs/in/zivanovic/sss/SecretShare.java b/src/main/java/rs/in/zivanovic/sss/SecretShare.java new file mode 100644 index 0000000..1955e71 --- /dev/null +++ b/src/main/java/rs/in/zivanovic/sss/SecretShare.java @@ -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 + '}'; + } + +} diff --git a/src/main/java/rs/in/zivanovic/sss/ShamirSecretSharing.java b/src/main/java/rs/in/zivanovic/sss/ShamirSecretSharing.java new file mode 100644 index 0000000..b50b005 --- /dev/null +++ b/src/main/java/rs/in/zivanovic/sss/ShamirSecretSharing.java @@ -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 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 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 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 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 shares) { + return SasUtils.decodeIntegerToString(join(shares)); + } + + /** + * Join shares back into the secret. + * + * @param shares to join + * @return secret number + */ + public static BigInteger join(List 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 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() { + } + +} diff --git a/src/test/java/rs/in/zivanovic/sss/SasUtilsTest.java b/src/test/java/rs/in/zivanovic/sss/SasUtilsTest.java new file mode 100644 index 0000000..0de0e96 --- /dev/null +++ b/src/test/java/rs/in/zivanovic/sss/SasUtilsTest.java @@ -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); + } + +} diff --git a/src/test/java/rs/in/zivanovic/sss/ShamirSecretSharingTest.java b/src/test/java/rs/in/zivanovic/sss/ShamirSecretSharingTest.java new file mode 100644 index 0000000..f867a64 --- /dev/null +++ b/src/test/java/rs/in/zivanovic/sss/ShamirSecretSharingTest.java @@ -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 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 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 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); + } + +}