Initial commit

This commit is contained in:
Marko Zivanovic
2014-11-25 19:07:35 +01:00
commit d396dfba7f
10 changed files with 586 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
target
nb-configuration.xml
+2
View File
@@ -0,0 +1,2 @@
web: java -Dserver.port=$PORT $JAVA_OPTS -jar target/share-a-secret-api-1.0.0-SNAPSHOT.jar
+62
View File
@@ -0,0 +1,62 @@
<?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-api</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>share-a-secret-api</name>
<description>Share-a-Secret REST API service.</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.1.9.RELEASE</version>
</parent>
<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>
<organization>
<name>Marko Zivanovic</name>
<url>http://marko.zivanovic.in.rs</url>
</organization>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
</project>
@@ -0,0 +1,43 @@
/*
* 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.share.a.secret.api;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
*
* @author Marko Zivanovic <marko@zivanovic.in.rs>
*/
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2014, Marko Živanović
* 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.api;
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 Zivanovic
* 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.api;
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 Zivanovic
* 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.api;
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 prime) {
BigInteger res = BigInteger.ZERO;
while (res.compareTo(prime) <= 0) {
res = BigInteger.probablePrime(prime.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,51 @@
/*
* 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.share.a.secret.api.controllers;
import java.util.List;
import rs.in.zivanovic.share.a.secret.api.dto.Shares;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import rs.in.zivanovic.share.a.secret.api.SecretShare;
import rs.in.zivanovic.share.a.secret.api.ShamirSecretSharing;
/**
*
* @author Marko Zivanovic <marko@zivanovic.in.rs>
*/
@RestController
@RequestMapping("/sas")
public class SasController {
@RequestMapping(method = RequestMethod.POST, value = "/split")
public Shares split(
@RequestParam(value = "secret", required = true) String secret,
@RequestParam(value = "total", required = true) int total,
@RequestParam(value = "threshold", required = true) int threshold) {
List<SecretShare> shares = ShamirSecretSharing.split(secret, total, threshold);
return new Shares(shares);
}
}
@@ -0,0 +1,57 @@
/*
* 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.share.a.secret.api.dto;
import java.util.ArrayList;
import java.util.List;
import org.apache.tomcat.util.codec.binary.Base64;
import rs.in.zivanovic.share.a.secret.api.SecretShare;
import rs.in.zivanovic.share.a.secret.api.Utils;
/**
*
* @author Marko Zivanovic <marko@zivanovic.in.rs>
*/
public class Shares {
private List<String> shares = new ArrayList<>();
public Shares(List<SecretShare> shares) {
this.shares.clear();
shares.stream().forEachOrdered(share -> {
this.shares.add(Base64.encodeBase64String(Utils.encodeToBinary(share)));
});
}
public Shares() {
}
public List<String> getShares() {
return shares;
}
public void setShares(List<String> shares) {
this.shares = shares;
}
}
+1
View File
@@ -0,0 +1 @@
java.runtime.version=1.8