From 54eb4148d0c04376645b56de4f1cccdd6a68fb7a Mon Sep 17 00:00:00 2001 From: Marko Zivanovic Date: Sun, 30 Nov 2014 01:39:25 +0100 Subject: [PATCH] Implement joining functionality. Backend refactoring. --- .../a/secret/api/ShamirSecretSharing.java | 1 + .../zivanovic/share/a/secret/api/Utils.java | 1 + .../secret/api/controllers/SasController.java | 66 ++++++++--- .../a/secret/api/dto/JoinParameters.java | 48 ++++++++ .../share/a/secret/api/dto/JoinResponse.java | 46 ++++++++ .../a/secret/api/{ => dto}/SecretShare.java | 2 +- .../dto/{Shares.java => SplitResponse.java} | 7 +- .../{Version.java => VersionResponse.java} | 4 +- src/main/resources/static/about.html | 17 +++ src/main/resources/static/index.html | 106 +++++------------- src/main/resources/static/join.html | 36 ++++++ src/main/resources/static/js/app.js | 21 +++- src/main/resources/static/js/controllers.js | 30 ++++- src/main/resources/static/js/services.js | 4 + src/main/resources/static/split.html | 64 +++++++++++ 15 files changed, 352 insertions(+), 101 deletions(-) create mode 100644 src/main/java/rs/in/zivanovic/share/a/secret/api/dto/JoinParameters.java create mode 100644 src/main/java/rs/in/zivanovic/share/a/secret/api/dto/JoinResponse.java rename src/main/java/rs/in/zivanovic/share/a/secret/api/{ => dto}/SecretShare.java (98%) rename src/main/java/rs/in/zivanovic/share/a/secret/api/dto/{Shares.java => SplitResponse.java} (92%) rename src/main/java/rs/in/zivanovic/share/a/secret/api/dto/{Version.java => VersionResponse.java} (94%) create mode 100644 src/main/resources/static/about.html create mode 100644 src/main/resources/static/join.html create mode 100644 src/main/resources/static/split.html diff --git a/src/main/java/rs/in/zivanovic/share/a/secret/api/ShamirSecretSharing.java b/src/main/java/rs/in/zivanovic/share/a/secret/api/ShamirSecretSharing.java index 46576ee..3c4e551 100644 --- a/src/main/java/rs/in/zivanovic/share/a/secret/api/ShamirSecretSharing.java +++ b/src/main/java/rs/in/zivanovic/share/a/secret/api/ShamirSecretSharing.java @@ -25,6 +25,7 @@ */ package rs.in.zivanovic.share.a.secret.api; +import rs.in.zivanovic.share.a.secret.api.dto.SecretShare; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/rs/in/zivanovic/share/a/secret/api/Utils.java b/src/main/java/rs/in/zivanovic/share/a/secret/api/Utils.java index 94e0d3a..d2658be 100644 --- a/src/main/java/rs/in/zivanovic/share/a/secret/api/Utils.java +++ b/src/main/java/rs/in/zivanovic/share/a/secret/api/Utils.java @@ -25,6 +25,7 @@ */ package rs.in.zivanovic.share.a.secret.api; +import rs.in.zivanovic.share.a.secret.api.dto.SecretShare; import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/rs/in/zivanovic/share/a/secret/api/controllers/SasController.java b/src/main/java/rs/in/zivanovic/share/a/secret/api/controllers/SasController.java index 8f725c5..a9968d9 100644 --- a/src/main/java/rs/in/zivanovic/share/a/secret/api/controllers/SasController.java +++ b/src/main/java/rs/in/zivanovic/share/a/secret/api/controllers/SasController.java @@ -23,21 +23,26 @@ */ package rs.in.zivanovic.share.a.secret.api.controllers; +import rs.in.zivanovic.share.a.secret.api.dto.JoinParameters; +import rs.in.zivanovic.share.a.secret.api.dto.JoinResponse; +import java.util.ArrayList; +import java.util.Base64; import java.util.List; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.RequestBody; -import rs.in.zivanovic.share.a.secret.api.dto.Shares; +import rs.in.zivanovic.share.a.secret.api.dto.SplitResponse; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import rs.in.zivanovic.share.a.secret.api.SecretShare; +import rs.in.zivanovic.share.a.secret.api.dto.SecretShare; import rs.in.zivanovic.share.a.secret.api.ShamirSecretSharing; +import rs.in.zivanovic.share.a.secret.api.Utils; import rs.in.zivanovic.share.a.secret.api.dto.SasResponse; import rs.in.zivanovic.share.a.secret.api.dto.SplitParameters; -import rs.in.zivanovic.share.a.secret.api.dto.Version; +import rs.in.zivanovic.share.a.secret.api.dto.VersionResponse; /** * @@ -52,25 +57,56 @@ public class SasController { @RequestMapping(method = RequestMethod.POST, value = "/split") public ResponseEntity split(@Valid @RequestBody SplitParameters params, BindingResult br) { + SasResponse response; if (br.hasErrors()) { - SasResponse resp = SasResponse.badRequest(); - br.getFieldErrors().stream().forEach(err -> { - resp.withInvalidParameterValueError(err.getField(), err.getRejectedValue(), err.getDefaultMessage()); - }); - return resp.build(); + response = processValidationErrors(br); + } else if (params.getThreshold() > params.getTotal()) { + response = SasResponse.badRequest().withInvalidParameterValueError("threshold", params.getThreshold(), + "must not be grater than 'total' (" + String.valueOf(params.getTotal()) + ")"); + } else { + List shares = ShamirSecretSharing.split(params.getSecret(), params.getTotal(), + params.getThreshold()); + response = SasResponse.ok().withData(new SplitResponse(shares)); } - if (params.getThreshold() > params.getTotal()) { - return SasResponse.badRequest().withInvalidParameterValueError("threshold", params.getThreshold(), - "must not be grater than 'total' (" + String.valueOf(params.getTotal()) + ")").build(); + return response.build(); + } + + @RequestMapping(method = RequestMethod.POST, value = "/join") + public ResponseEntity join(@Valid @RequestBody JoinParameters params, BindingResult br) { + SasResponse response; + if (br.hasErrors()) { + response = processValidationErrors(br); + } else { + try { + String secret = ShamirSecretSharing.joinToUtf8String(makeSecretShares(params)); + response = SasResponse.ok().withData(new JoinResponse(secret)); + } catch (RuntimeException ex) { + response = SasResponse.badRequest().withInvalidParameterValueError("shares", params.getShares(), + "one or more shares are invalid"); + } } - List shares = ShamirSecretSharing.split(params.getSecret(), params.getTotal(), - params.getThreshold()); - return SasResponse.ok().withData(new Shares(shares)).build(); + return response.build(); } @RequestMapping(method = RequestMethod.GET, value = "/version") public ResponseEntity version() { String buildTime = ""; - return SasResponse.ok().withData(new Version(version, buildTime)).build(); + return SasResponse.ok().withData(new VersionResponse(version, buildTime)).build(); + } + + private SasResponse processValidationErrors(BindingResult br) { + SasResponse r = SasResponse.badRequest(); + br.getFieldErrors().stream().forEach(err -> { + r.withInvalidParameterValueError(err.getField(), err.getRejectedValue(), err.getDefaultMessage()); + }); + return r; + } + + private List makeSecretShares(JoinParameters params) { + List shares = new ArrayList<>(params.getShares().size()); + params.getShares().stream().forEach(share -> { + shares.add(Utils.decodeFromBinary(Base64.getDecoder().decode(share))); + }); + return shares; } } diff --git a/src/main/java/rs/in/zivanovic/share/a/secret/api/dto/JoinParameters.java b/src/main/java/rs/in/zivanovic/share/a/secret/api/dto/JoinParameters.java new file mode 100644 index 0000000..6d3a23a --- /dev/null +++ b/src/main/java/rs/in/zivanovic/share/a/secret/api/dto/JoinParameters.java @@ -0,0 +1,48 @@ +/* + * 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.List; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +/** + * + * @author Marko Zivanovic + */ +public class JoinParameters { + + @NotNull + @Size(min = 1) + private List shares; + + public List getShares() { + return shares; + } + + public void setShares(List shares) { + this.shares = shares; + } + +} diff --git a/src/main/java/rs/in/zivanovic/share/a/secret/api/dto/JoinResponse.java b/src/main/java/rs/in/zivanovic/share/a/secret/api/dto/JoinResponse.java new file mode 100644 index 0000000..c610471 --- /dev/null +++ b/src/main/java/rs/in/zivanovic/share/a/secret/api/dto/JoinResponse.java @@ -0,0 +1,46 @@ +/* + * 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; + +/** + * + * @author Marko Zivanovic + */ +public class JoinResponse { + + private String secret; + + public JoinResponse(String secret) { + this.secret = secret; + } + + public String getSecret() { + return secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } + +} diff --git a/src/main/java/rs/in/zivanovic/share/a/secret/api/SecretShare.java b/src/main/java/rs/in/zivanovic/share/a/secret/api/dto/SecretShare.java similarity index 98% rename from src/main/java/rs/in/zivanovic/share/a/secret/api/SecretShare.java rename to src/main/java/rs/in/zivanovic/share/a/secret/api/dto/SecretShare.java index 96c09b5..ea55917 100644 --- a/src/main/java/rs/in/zivanovic/share/a/secret/api/SecretShare.java +++ b/src/main/java/rs/in/zivanovic/share/a/secret/api/dto/SecretShare.java @@ -23,7 +23,7 @@ * 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; +package rs.in.zivanovic.share.a.secret.api.dto; import java.math.BigInteger; import java.util.Objects; diff --git a/src/main/java/rs/in/zivanovic/share/a/secret/api/dto/Shares.java b/src/main/java/rs/in/zivanovic/share/a/secret/api/dto/SplitResponse.java similarity index 92% rename from src/main/java/rs/in/zivanovic/share/a/secret/api/dto/Shares.java rename to src/main/java/rs/in/zivanovic/share/a/secret/api/dto/SplitResponse.java index 4908f93..88051f5 100644 --- a/src/main/java/rs/in/zivanovic/share/a/secret/api/dto/Shares.java +++ b/src/main/java/rs/in/zivanovic/share/a/secret/api/dto/SplitResponse.java @@ -26,25 +26,24 @@ 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 */ -public class Shares { +public class SplitResponse { private List shares = new ArrayList<>(); - public Shares(List shares) { + public SplitResponse(List shares) { this.shares.clear(); shares.stream().forEachOrdered(share -> { this.shares.add(Base64.encodeBase64String(Utils.encodeToBinary(share))); }); } - public Shares() { + public SplitResponse() { } public List getShares() { diff --git a/src/main/java/rs/in/zivanovic/share/a/secret/api/dto/Version.java b/src/main/java/rs/in/zivanovic/share/a/secret/api/dto/VersionResponse.java similarity index 94% rename from src/main/java/rs/in/zivanovic/share/a/secret/api/dto/Version.java rename to src/main/java/rs/in/zivanovic/share/a/secret/api/dto/VersionResponse.java index 80bda7a..966fa20 100644 --- a/src/main/java/rs/in/zivanovic/share/a/secret/api/dto/Version.java +++ b/src/main/java/rs/in/zivanovic/share/a/secret/api/dto/VersionResponse.java @@ -27,12 +27,12 @@ package rs.in.zivanovic.share.a.secret.api.dto; * * @author Marko Zivanovic */ -public class Version { +public class VersionResponse { private String frontendVersion; private String buildTime; - public Version(String frontendVersion, String buildTime) { + public VersionResponse(String frontendVersion, String buildTime) { this.frontendVersion = frontendVersion; this.buildTime = buildTime; } diff --git a/src/main/resources/static/about.html b/src/main/resources/static/about.html new file mode 100644 index 0000000..b76f9bd --- /dev/null +++ b/src/main/resources/static/about.html @@ -0,0 +1,17 @@ +
+
+

+ Secret sharing (also called secret splitting) refers to methods for distributing a secret amongst a group of participants, each of whom is allocated a share of the secret. The secret can be reconstructed only when a sufficient number, of possibly different types, of shares are combined together; individual shares are of no use on their own. +

+ +
+ +
+
+ +
+
+ +
+
+
\ No newline at end of file diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index c53f8b0..7fe67b1 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -1,15 +1,17 @@ - + Share-a-Secret - + - - + + + + @@ -17,91 +19,41 @@ - -
- + -
-
-
- - +
+ +
-
-
-

Whoops, something's wrong ...

-
-
-
{{err.message}}
-
-
+
-
-
-
-

Here are your secret shares ...

-
- - - - List -
-
-

Click on a share to select it for easy copying to clipboard. - After clicking it, the share will change color to indicate the ones you have already copied.

-
-
{{share}}
-
-
-
-
- - Text -
-
-

Use this panel to easily copy all shares at once.

- -
-
-
-
-
- -
- +
+ diff --git a/src/main/resources/static/join.html b/src/main/resources/static/join.html new file mode 100644 index 0000000..36ff6e5 --- /dev/null +++ b/src/main/resources/static/join.html @@ -0,0 +1,36 @@ +
+
+ +
+
+

Your secret, based on the shares you provided, is:

+

+ {{secret}} +

+
+
+
+ +
+
+ + +
+
+
+
+ +
+
+
+ +
+
+

Whoops, something's wrong ...

+
+
{{err.message}}
+
+
+
+ +
\ No newline at end of file diff --git a/src/main/resources/static/js/app.js b/src/main/resources/static/js/app.js index e663b5a..90cd167 100644 --- a/src/main/resources/static/js/app.js +++ b/src/main/resources/static/js/app.js @@ -24,6 +24,7 @@ angular.module('sasApp', [ 'ngResource', + 'ngRoute', 'ui.bootstrap', 'sasControllers', 'sasServices' @@ -43,4 +44,22 @@ angular.module('sasApp', [ SasService.version({}, function (response) { $rootScope.version = response.data; }); -}); +}).config(['$routeProvider', + function ($routeProvider) { + $routeProvider. + when('/split', { + templateUrl: 'split.html', + controller: 'SplitController' + }). + when('/join', { + templateUrl: 'join.html', + controller: 'JoinController' + }). + when('/', { + templateUrl: 'about.html', + controller: 'AboutController' + }). + otherwise({ + redirectTo: '/' + }); + }]); diff --git a/src/main/resources/static/js/controllers.js b/src/main/resources/static/js/controllers.js index 5ca65e2..915048d 100644 --- a/src/main/resources/static/js/controllers.js +++ b/src/main/resources/static/js/controllers.js @@ -23,7 +23,7 @@ */ angular.module('sasControllers', []). - controller('MainController', ['$scope', 'SasService', + controller('SplitController', ['$scope', 'SasService', function ($scope, SasService) { $scope.total = 10; @@ -63,6 +63,34 @@ angular.module('sasControllers', []). $scope.errors = response.data.errors; }); }; + }]). + controller('JoinController', ['$scope', 'SasService', + function ($scope, SasService) { + $scope.join = function () { + $scope.errors = []; + $scope.secret = ""; + var shares = []; + var text = $scope.sharesAsText.split("\n"); + for (var i in text) { + var share = text[i]; + shares.push(share); + } + SasService.join({shares: shares}, function (response) { + $scope.secret = response.data.secret; + }, function (response) { + $scope.errors = response.data.errors; + }); + }; + }]). + controller('AboutController', ['$scope', '$location', + function ($scope, $location) { + $scope.goJoin = function () { + $location.path("/join"); + }; + + $scope.goSplit = function () { + $location.path("/split"); + }; }]); function mergeSharesToString(shares) { diff --git a/src/main/resources/static/js/services.js b/src/main/resources/static/js/services.js index 61eb957..f468aa7 100644 --- a/src/main/resources/static/js/services.js +++ b/src/main/resources/static/js/services.js @@ -29,6 +29,10 @@ angular.module('sasServices', []). url: "/sas/split", method: "POST" }, + join: { + url: "/sas/join", + method: "POST" + }, version: { url: "/sas/version", method: "GET" diff --git a/src/main/resources/static/split.html b/src/main/resources/static/split.html new file mode 100644 index 0000000..1985e9a --- /dev/null +++ b/src/main/resources/static/split.html @@ -0,0 +1,64 @@ +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ +
+
+
+ +
+
+

Whoops, something's wrong ...

+
+
{{err.message}}
+
+
+
+ +
+
+
+

Here are your secret shares ...

+
+ + + + List +
+
+

Click on a share to select it for easy copying to clipboard. + After clicking it, the share will change color to indicate the ones you have already copied.

+
+
{{share}}
+
+
+
+
+ + Text +
+
+

Use this panel to easily copy all shares at once.

+ +
+
+
+
+
+
\ No newline at end of file