diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index ecf8859..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,46 +0,0 @@ -# -# Copyright © 2017 Coda Hale (coda.hale@gmail.com) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -version: 2 -jobs: - build: - docker: - - image: maven:3-jdk-12-alpine - working_directory: ~/repo - environment: - MAVEN_OPTS: -Xmx3200m - steps: - - checkout - - restore_cache: - keys: - - v1-dependencies-{{ checksum ".circleci/config.yml" }}-{{ checksum "pom.xml" }} - # fallback to using the latest cache if no exact match is found - - v1-dependencies- - - run: mvn clean verify - - save_cache: - paths: - - ~/.m2 - key: v1-dependencies-{{ checksum ".circleci/config.yml" }}-{{ checksum "pom.xml" }} - - run: - name: Save test results - command: | - mkdir -p ~/junit/ - find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} ~/junit/ \; - when: always - - store_test_results: - path: ~/junit - - store_artifacts: - path: ~/junit diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +node_modules diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..397702a --- /dev/null +++ b/.eslintrc @@ -0,0 +1,16 @@ +{ + "env": { + "browser": true, + "commonjs": true, + "es6": true, + "node": true + }, + "extends": ["eslint:recommended"], + "parserOptions": { + "sourceType": "module", + "ecmaVersion": 2018 + }, + "rules": { + "linebreak-style": ["error", "unix"] + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1cdc9f7..9701a9c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,9 @@ release.properties dependency-reduced-pom.xml buildNumber.properties .mvn/timing.properties +node_modules/ +.classpath +.idea/ +.project +.settings +.vscode/ diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..fed401d --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +package-lock.json +node_modules/ \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..92cde39 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "singleQuote": true +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..cd87f5c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +# Run the stand java build on openjdk +FROM maven AS build +COPY . src +WORKDIR src +RUN mvn package + +# Copy the result to graaljs and test node.js+java in the same vm +FROM ghcr.io/graalvm/nodejs-community:23.0.2-jvm17-ol9-20231024 AS graal +COPY --from=build /src /app/src +WORKDIR src +RUN npm test && npm run testwithjava && npm run lint diff --git a/README.md b/README.md index 64ce109..248caa1 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,52 @@ # Shamir's Secret Sharing -[![CircleCI](https://circleci.com/gh/codahale/shamir.svg?style=svg)](https://circleci.com/gh/codahale/shamir) +A implementation of [Shamir's Secret Sharing +algorithm](http://en.wikipedia.org/wiki/Shamir's_Secret_Sharing) over GF(256) in both Java and JavaScript. The Java code is from +the archive Codahale's [shamir](https://github.com/codahale/shamir) implementation. The +Javascript version is original and is crossed checked against the Java version. -A Java implementation of [Shamir's Secret Sharing -algorithm](http://en.wikipedia.org/wiki/Shamir's_Secret_Sharing) over GF(256). +You can use docker build both codebases and run cross-checks between them using: -## Add to your project +`docker build . ` + +*Note: module name for Java 9+ is `com.codahale.shamir`.* + +## Add to your JavaScript project + +```sh +npm i shamir +``` + +## Use the thing in JavaScript + +```javascript +const { split, join } = require('shamir'); +const { randomBytes } = require('crypto'); + +const PARTS = 5; +const QUORUM = 3; + +function doIt() { + const secret = 'hello there'; + // you can use any polyfill to covert between strings and Uint8Array + const utf8Encoder = new TextEncoder(); + const utf8Decoder = new TextDecoder(); + const secretBytes = utf8Encoder.encode(secret); + // parts is a map of part numbers to Uint8Array + const parts = split(randomBytes, PARTS, QUORUM, secretBytes); + // we only need QUORUM of the parts to recover the secret + delete parts['2']; + delete parts['3']; + // recovered is an Unit8Array + const recovered = join(parts); + // prints 'hello there' + console.log(utf8Decoder.decode(recovered)); +} +``` + +## [Optional] Add to your Java project + +The Java version is available as the orginal Codahale distribution: ```xml @@ -15,9 +56,7 @@ algorithm](http://en.wikipedia.org/wiki/Shamir's_Secret_Sharing) over GF(256). ``` -*Note: module name for Java 9+ is `com.codahale.shamir`.* - -## Use the thing +## Use the thing in Java ```java import com.codahale.shamir.Scheme; @@ -94,7 +133,7 @@ performing the same operation over `GF(Q)` takes several seconds, even using per Treating the secret as a single `y` coordinate over `GF(Q)` is even slower, and requires a modulus larger than the secret. -## Performance +## Java Performance It's fast. Plenty fast. @@ -108,7 +147,25 @@ Benchmarks.split 4 1024 avgt 200 396.708 ± 1.520 us/op **N.B.:** `split` is quadratic with respect to the number of shares being combined. -## Tiered sharing +## JavaScript Performance + +For a 1KiB secret split with a `n=4,k=3` scheme running on NodeJS v10.16.0: + +``` +Benchmark (n) (secretSize) Cnt Score Units +Benchmarks.join 4 1024 200 2.08 ms/op +Benchmarks.split 4 1024 200 2.78 ms/op +``` + +Split is dominated by the calls to `Crypto.randomBytes` to get random polynomials to encode each byte of the secet. Using a more realistic 128 bit secret with `n=4,k=3` scheme running on NodeJS v10.16.0: + +``` +Benchmark (n) (secretSize) Cnt Score Units +Benchmarks.join 5 16 200 0.083 ms/op +Benchmarks.split 5 16 200 0.081 ms/op +``` + +## Tiered Sharing Java Some usages of secret sharing involve levels of access: e.g. recovering a secret requires two admin shares and three user shares. As @ba1ciu discovered, these can be implemented by building a tree of @@ -120,7 +177,7 @@ class BuildTree { final byte[] secret = "this is a secret".getBytes(StandardCharsets.UTF_8); // tier 1 of the tree - final Scheme adminScheme = new Scheme(new SecureRandom(), 5, 2); + final Scheme adminScheme = new Scheme(new SecureRandom(), 3, 2); final Map admins = adminScheme.split(secret); // tier 2 of the tree @@ -147,8 +204,50 @@ By discarding the third admin share and the first two sets of user shares, we ha which can be used to recover the original secret as long as either two admins or one admin and three users agree. +## Tiered Sharing JavaScript + +Sharing a secret requiring either two admins or one admin and three users to recover: + +```javascript + const secret = new Unit8Array([1, 2, 3]); + + const adminParts = 3; + const adminQuorum = 2; + const adminSplits = split(randomBytes, adminParts, adminQuorum, secret); + + const userParts = 4; + const userQuorum = 3; + const usersSplits = split(randomBytes, userParts, userQuorum, adminSplits['3'] ); + + // throw away third share that is split into 4 user parts + delete adminSplits['3']; + + console.log('Admin Shares:'); + console.log(`1 = ${adminSplits['1']}`); + console.log(`2 = ${adminSplits['2']}`); + + console.log('User Shares:'); + console.log(`1 = ${usersSplits['1']}`); + console.log(`2 = ${usersSplits['2']}`); + console.log(`3 = ${usersSplits['3']}`); + console.log(`4 = ${usersSplits['4']}`); + + // throw away an admin share and one user share + delete adminSplits['2']; + delete usersSplits['1']; + + // reconstruct the deleted third admin share from the three user shares + const joinedUserShares = join(usersSplits); + // use the first admin share and the recovered third share + const recoverdSecret = join({ '1': adminSplits['1'], '3': joinedUserShares } ); +``` + +There is a unit test for this in `src/test/js/TieredSharing.js`. + ## License Copyright © 2017 Coda Hale +Copyright © 2019 Simon Massey + Distributed under the Apache License 2.0. diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ee93bb6 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1270 @@ +{ + "name": "shamir", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "dev": true + }, + "acorn-jsx": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", + "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", + "dev": true + }, + "ajv": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-walk": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", + "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.0.1.tgz", + "integrity": "sha512-DyQRaMmORQ+JsWShYsSg4OPTjY56u1nCjAmICrE8vLWqyLKxhFXOthwMj1SA8xwfrv0CofLNVnqbfyhwCkaO0w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^4.0.3", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^6.0.0", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^3.1.0", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.2.2", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^5.5.1", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0" + } + }, + "eslint-config-prettier": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.0.0.tgz", + "integrity": "sha512-vDrcCFE3+2ixNT5H83g28bO/uYAwibJxerXPj+E7op4qzBCsAV36QfvdAyVOoNxKAH2Os/e01T/2x++V0LPukA==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + } + }, + "eslint-plugin-prettier": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.0.tgz", + "integrity": "sha512-XWX2yVuwVNLOUhQijAkXz+rMPPoCr7WFiAl8ig6I7Xn+pPVhDhzg4DxHpmbeb0iqjO9UronEA3Tb09ChnFVHHA==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", + "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", + "dev": true + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "dev": true + }, + "espree": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.0.0.tgz", + "integrity": "sha512-lJvCS6YbCn3ImT3yKkPe0+tJ+mH6ljhGNjHQH9mRtiO6gjhVAOhVXW1yjnwqGwTkK3bGbye+hb00nFNmu0l/1Q==", + "dev": true, + "requires": { + "acorn": "^6.0.7", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "external-editor": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "dev": true + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "global": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", + "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "dev": true, + "requires": { + "min-document": "^2.19.0", + "process": "~0.5.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", + "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "inquirer": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.4.1.tgz", + "integrity": "sha512-/Jw+qPZx4EDYsaT6uz7F4GJRNFMRdKNeUZw3ZnKV8lyuUgz/YWRCSUAJMZSVhSq4Ec0R2oYnyi6b3d4JXcL5Nw==", + "dev": true, + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.11", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "dev": true, + "requires": { + "dom-walk": "^0.1.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "object-inspect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prettier": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz", + "integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "resolve": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", + "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "resumer": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", + "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", + "dev": true, + "requires": { + "through": "~2.3.4" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "rxjs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", + "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string.prototype.trim": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", + "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.0", + "function-bind": "^1.0.2" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.1.tgz", + "integrity": "sha512-E6CK1/pZe2N75rGZQotFOdmzWQ1AILtgYbMAbAjvms0S1l5IDB47zG3nCnFGB/w+7nB3vKofbLXCH7HPBo864w==", + "dev": true, + "requires": { + "ajv": "^6.9.1", + "lodash": "^4.17.11", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "tape": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/tape/-/tape-4.11.0.tgz", + "integrity": "sha512-yixvDMX7q7JIs/omJSzSZrqulOV51EC9dK8dM0TzImTIkHWfe2/kFyL5v+d9C+SrCMaICk59ujsqFAVidDqDaA==", + "dev": true, + "requires": { + "deep-equal": "~1.0.1", + "defined": "~1.0.0", + "for-each": "~0.3.3", + "function-bind": "~1.1.1", + "glob": "~7.1.4", + "has": "~1.0.3", + "inherits": "~2.0.4", + "minimist": "~1.2.0", + "object-inspect": "~1.6.0", + "resolve": "~1.11.1", + "resumer": "~0.0.0", + "string.prototype.trim": "~1.1.2", + "through": "~2.3.8" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + } + } + }, + "tape-catch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tape-catch/-/tape-catch-1.0.6.tgz", + "integrity": "sha1-EpMdXqYKA6l9m9GdDX2M/D9s7PE=", + "dev": true, + "requires": { + "global": "~4.3.0" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..29172f4 --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "name": "shamir", + "version": "0.7.1", + "description": "A JavaScript implementation of Shamir's Secret Sharing algorithm over GF(256).", + "main": "src/main/js/Scheme.js", + "scripts": { + "test": "tape src/test/js/GF256Tests.js src/test/js/SchemeTests.js src/test/js/TieredSharing.js", + "testwithjava": "node --jvm --vm.cp=./target/classes:./target/test-classes src/test/js/PolygotTests.js", + "lint": "eslint ./src/main/js/GF256.js ./src/main/js/Scheme.js ./src/test/js/GF256Tests.js ./src/test/js/SchemeTests.js ./src/test/js/PolygotTests.js && echo 'Lint complete.'" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/simbo1905/shamir.git" + }, + "keywords": [ + "sss", + "shamir", + "secrets", + "sharing", + "crypto", + "nuclear-codes", + "shamir-secret-sharing" + ], + "author": "Simon Massey", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/simbo1905/shamir/issues" + }, + "homepage": "https://github.com/simbo1905/shamir#readme", + "devDependencies": { + "eslint": "^6.0.1", + "eslint-config-prettier": "^6.0.0", + "eslint-plugin-prettier": "^3.1.0", + "prettier": "^1.18.2", + "tape": "^4.11.0", + "tape-catch": "^1.0.6" + } +} diff --git a/pom.xml b/pom.xml index d5eab5c..db941d9 100644 --- a/pom.xml +++ b/pom.xml @@ -16,54 +16,118 @@ limitations under the License. --> - - 4.0.0 - - com.codahale - common-pom - 0.0.19 - + + 4.0.0 - shamir - 0.7.1-SNAPSHOT - Shamir's Secret Sharing - https://github.com/codahale/shamir - - An implementation of Shamir's Secret Sharing algorithm over GF(256). - - 2017 - - scm:git:https://github.com/codahale/shamir.git - scm:git:https://github.com/codahale/shamir.git + shamir + com.codahale + 0.7.1-SNAPSHOT + Shamir's Secret Sharing https://github.com/codahale/shamir - HEAD - + + An implementation of Shamir's Secret Sharing algorithm over GF(256). + + + 2017 + + scm:git:https://github.com/codahale/shamir.git + scm:git:https://github.com/codahale/shamir.git + https://github.com/codahale/shamir + HEAD + + + 17 + 17 + 1.21 + + + + com.google.guava + guava + 27.0.1-jre + test + + + + org.quicktheories + quicktheories + 0.26 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.9.2 + test + + + org.assertj + assertj-core + 3.24.2 + test + + + org.openjdk.jmh + jmh-core + ${jmh.version} - - - com.google.guava - guava - test - - + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + test + + - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - com.codahale.shamir - - - - - - - + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + com.codahale.shamir + + + + + + + + + + com.mycila + license-maven-plugin + 3.0 + +
com/mycila/maven/plugin/license/templates/APACHE-2.txt
+ + Coda Hale + coda.hale@gmail.com + + + SLASHSTAR_STYLE + + + + + LICENSE + + **/*.txt + **/*.js + + src/main/resources/** + src/test/resources/** + node_modules/** + +
+
+
+
diff --git a/shamir.iml b/shamir.iml new file mode 100644 index 0000000..ab5590c --- /dev/null +++ b/shamir.iml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/js/GF256.js b/src/main/js/GF256.js new file mode 100644 index 0000000..af14e9e --- /dev/null +++ b/src/main/js/GF256.js @@ -0,0 +1,242 @@ +/* + * Copyright © 2019 Simon Massey (massey1905@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* eslint-disable no-bitwise */ +function add(a, b) { + return a ^ b; +} + +exports.add = add; + +/* The Laws of Cryptograhy with Java Code by Neal R. Wagner +http://www.cs.utsa.edu/~wagner/lawsbookcolor/laws.pdf +Page 120 (134) section "20.3 Addition in GP(2^n)" is equal \ +to subtraction. +*/ +const sub = add; + +exports.sub = exports.add; + +const LOG = new Uint8Array([ + 0xff, 0x00, 0x19, 0x01, 0x32, 0x02, 0x1a, + 0xc6, 0x4b, 0xc7, 0x1b, 0x68, 0x33, 0xee, + 0xdf, 0x03, 0x64, 0x04, 0xe0, 0x0e, 0x34, + 0x8d, 0x81, 0xef, 0x4c, 0x71, 0x08, 0xc8, + 0xf8, 0x69, 0x1c, 0xc1, 0x7d, 0xc2, 0x1d, + 0xb5, 0xf9, 0xb9, 0x27, 0x6a, 0x4d, 0xe4, + 0xa6, 0x72, 0x9a, 0xc9, 0x09, 0x78, 0x65, + 0x2f, 0x8a, 0x05, 0x21, 0x0f, 0xe1, 0x24, + 0x12, 0xf0, 0x82, 0x45, 0x35, 0x93, 0xda, + 0x8e, 0x96, 0x8f, 0xdb, 0xbd, 0x36, 0xd0, + 0xce, 0x94, 0x13, 0x5c, 0xd2, 0xf1, 0x40, + 0x46, 0x83, 0x38, 0x66, 0xdd, 0xfd, 0x30, + 0xbf, 0x06, 0x8b, 0x62, 0xb3, 0x25, 0xe2, + 0x98, 0x22, 0x88, 0x91, 0x10, 0x7e, 0x6e, + 0x48, 0xc3, 0xa3, 0xb6, 0x1e, 0x42, 0x3a, + 0x6b, 0x28, 0x54, 0xfa, 0x85, 0x3d, 0xba, + 0x2b, 0x79, 0x0a, 0x15, 0x9b, 0x9f, 0x5e, + 0xca, 0x4e, 0xd4, 0xac, 0xe5, 0xf3, 0x73, + 0xa7, 0x57, 0xaf, 0x58, 0xa8, 0x50, 0xf4, + 0xea, 0xd6, 0x74, 0x4f, 0xae, 0xe9, 0xd5, + 0xe7, 0xe6, 0xad, 0xe8, 0x2c, 0xd7, 0x75, + 0x7a, 0xeb, 0x16, 0x0b, 0xf5, 0x59, 0xcb, + 0x5f, 0xb0, 0x9c, 0xa9, 0x51, 0xa0, 0x7f, + 0x0c, 0xf6, 0x6f, 0x17, 0xc4, 0x49, 0xec, + 0xd8, 0x43, 0x1f, 0x2d, 0xa4, 0x76, 0x7b, + 0xb7, 0xcc, 0xbb, 0x3e, 0x5a, 0xfb, 0x60, + 0xb1, 0x86, 0x3b, 0x52, 0xa1, 0x6c, 0xaa, + 0x55, 0x29, 0x9d, 0x97, 0xb2, 0x87, 0x90, + 0x61, 0xbe, 0xdc, 0xfc, 0xbc, 0x95, 0xcf, + 0xcd, 0x37, 0x3f, 0x5b, 0xd1, 0x53, 0x39, + 0x84, 0x3c, 0x41, 0xa2, 0x6d, 0x47, 0x14, + 0x2a, 0x9e, 0x5d, 0x56, 0xf2, 0xd3, 0xab, + 0x44, 0x11, 0x92, 0xd9, 0x23, 0x20, 0x2e, + 0x89, 0xb4, 0x7c, 0xb8, 0x26, 0x77, 0x99, + 0xe3, 0xa5, 0x67, 0x4a, 0xed, 0xde, 0xc5, + 0x31, 0xfe, 0x18, 0x0d, 0x63, 0x8c, 0x80, + 0xc0, 0xf7, 0x70, 0x07, +]); + +/* https://crypto.stackexchange.com/a/21174/13860 +*/ +const EXP = new Uint8Array([ + 0x01, 0x03, 0x05, 0x0f, 0x11, 0x33, 0x55, + 0xff, 0x1a, 0x2e, 0x72, 0x96, 0xa1, 0xf8, + 0x13, 0x35, 0x5f, 0xe1, 0x38, 0x48, 0xd8, + 0x73, 0x95, 0xa4, 0xf7, 0x02, 0x06, 0x0a, + 0x1e, 0x22, 0x66, 0xaa, 0xe5, 0x34, 0x5c, + 0xe4, 0x37, 0x59, 0xeb, 0x26, 0x6a, 0xbe, + 0xd9, 0x70, 0x90, 0xab, 0xe6, 0x31, 0x53, + 0xf5, 0x04, 0x0c, 0x14, 0x3c, 0x44, 0xcc, + 0x4f, 0xd1, 0x68, 0xb8, 0xd3, 0x6e, 0xb2, + 0xcd, 0x4c, 0xd4, 0x67, 0xa9, 0xe0, 0x3b, + 0x4d, 0xd7, 0x62, 0xa6, 0xf1, 0x08, 0x18, + 0x28, 0x78, 0x88, 0x83, 0x9e, 0xb9, 0xd0, + 0x6b, 0xbd, 0xdc, 0x7f, 0x81, 0x98, 0xb3, + 0xce, 0x49, 0xdb, 0x76, 0x9a, 0xb5, 0xc4, + 0x57, 0xf9, 0x10, 0x30, 0x50, 0xf0, 0x0b, + 0x1d, 0x27, 0x69, 0xbb, 0xd6, 0x61, 0xa3, + 0xfe, 0x19, 0x2b, 0x7d, 0x87, 0x92, 0xad, + 0xec, 0x2f, 0x71, 0x93, 0xae, 0xe9, 0x20, + 0x60, 0xa0, 0xfb, 0x16, 0x3a, 0x4e, 0xd2, + 0x6d, 0xb7, 0xc2, 0x5d, 0xe7, 0x32, 0x56, + 0xfa, 0x15, 0x3f, 0x41, 0xc3, 0x5e, 0xe2, + 0x3d, 0x47, 0xc9, 0x40, 0xc0, 0x5b, 0xed, + 0x2c, 0x74, 0x9c, 0xbf, 0xda, 0x75, 0x9f, + 0xba, 0xd5, 0x64, 0xac, 0xef, 0x2a, 0x7e, + 0x82, 0x9d, 0xbc, 0xdf, 0x7a, 0x8e, 0x89, + 0x80, 0x9b, 0xb6, 0xc1, 0x58, 0xe8, 0x23, + 0x65, 0xaf, 0xea, 0x25, 0x6f, 0xb1, 0xc8, + 0x43, 0xc5, 0x54, 0xfc, 0x1f, 0x21, 0x63, + 0xa5, 0xf4, 0x07, 0x09, 0x1b, 0x2d, 0x77, + 0x99, 0xb0, 0xcb, 0x46, 0xca, 0x45, 0xcf, + 0x4a, 0xde, 0x79, 0x8b, 0x86, 0x91, 0xa8, + 0xe3, 0x3e, 0x42, 0xc6, 0x51, 0xf3, 0x0e, + 0x12, 0x36, 0x5a, 0xee, 0x29, 0x7b, 0x8d, + 0x8c, 0x8f, 0x8a, 0x85, 0x94, 0xa7, 0xf2, + 0x0d, 0x17, 0x39, 0x4b, 0xdd, 0x7c, 0x84, + 0x97, 0xa2, 0xfd, 0x1c, 0x24, 0x6c, 0xb4, + 0xc7, 0x52, 0xf6, 0x01, 0x03, 0x05, 0x0f, + 0x11, 0x33, 0x55, 0xff, 0x1a, 0x2e, 0x72, + 0x96, 0xa1, 0xf8, 0x13, 0x35, 0x5f, 0xe1, + 0x38, 0x48, 0xd8, 0x73, 0x95, 0xa4, 0xf7, + 0x02, 0x06, 0x0a, 0x1e, 0x22, 0x66, 0xaa, + 0xe5, 0x34, 0x5c, 0xe4, 0x37, 0x59, 0xeb, + 0x26, 0x6a, 0xbe, 0xd9, 0x70, 0x90, 0xab, + 0xe6, 0x31, 0x53, 0xf5, 0x04, 0x0c, 0x14, + 0x3c, 0x44, 0xcc, 0x4f, 0xd1, 0x68, 0xb8, + 0xd3, 0x6e, 0xb2, 0xcd, 0x4c, 0xd4, 0x67, + 0xa9, 0xe0, 0x3b, 0x4d, 0xd7, 0x62, 0xa6, + 0xf1, 0x08, 0x18, 0x28, 0x78, 0x88, 0x83, + 0x9e, 0xb9, 0xd0, 0x6b, 0xbd, 0xdc, 0x7f, + 0x81, 0x98, 0xb3, 0xce, 0x49, 0xdb, 0x76, + 0x9a, 0xb5, 0xc4, 0x57, 0xf9, 0x10, 0x30, + 0x50, 0xf0, 0x0b, 0x1d, 0x27, 0x69, 0xbb, + 0xd6, 0x61, 0xa3, 0xfe, 0x19, 0x2b, 0x7d, + 0x87, 0x92, 0xad, 0xec, 0x2f, 0x71, 0x93, + 0xae, 0xe9, 0x20, 0x60, 0xa0, 0xfb, 0x16, + 0x3a, 0x4e, 0xd2, 0x6d, 0xb7, 0xc2, 0x5d, + 0xe7, 0x32, 0x56, 0xfa, 0x15, 0x3f, 0x41, + 0xc3, 0x5e, 0xe2, 0x3d, 0x47, 0xc9, 0x40, + 0xc0, 0x5b, 0xed, 0x2c, 0x74, 0x9c, 0xbf, + 0xda, 0x75, 0x9f, 0xba, 0xd5, 0x64, 0xac, + 0xef, 0x2a, 0x7e, 0x82, 0x9d, 0xbc, 0xdf, + 0x7a, 0x8e, 0x89, 0x80, 0x9b, 0xb6, 0xc1, + 0x58, 0xe8, 0x23, 0x65, 0xaf, 0xea, 0x25, + 0x6f, 0xb1, 0xc8, 0x43, 0xc5, 0x54, 0xfc, + 0x1f, 0x21, 0x63, 0xa5, 0xf4, 0x07, 0x09, + 0x1b, 0x2d, 0x77, 0x99, 0xb0, 0xcb, 0x46, + 0xca, 0x45, 0xcf, 0x4a, 0xde, 0x79, 0x8b, + 0x86, 0x91, 0xa8, 0xe3, 0x3e, 0x42, 0xc6, + 0x51, 0xf3, 0x0e, 0x12, 0x36, 0x5a, 0xee, + 0x29, 0x7b, 0x8d, 0x8c, 0x8f, 0x8a, 0x85, + 0x94, 0xa7, 0xf2, 0x0d, 0x17, 0x39, 0x4b, + 0xdd, 0x7c, 0x84, 0x97, 0xa2, 0xfd, 0x1c, + 0x24, 0x6c, 0xb4, 0xc7, 0x52, 0xf6, +]); + +function mul(a, b) { + if (a === 0 || b === 0) { + return 0; + } + return EXP[LOG[a] + LOG[b]]; +} + +exports.mul = mul; + +function div(a, b) { + // multiply by the inverse of b + return mul(a, EXP[255 - LOG[b]]); +} + +exports.div = div; + +function degree(p) { + // eslint-disable-next-line no-plusplus + for (let i = p.length - 1; i >= 1; i--) { + if (p[i] !== 0) { + return i; + } + } + return 0; +} + +exports.degree = degree; + +/** + * Calculates f(0) of the given points using Lagrangian interpolation. + * @param {array[Uint8Array]} points The supplied point. + */ +function interpolate(points) { + const x = 0; + let y = 0; + // eslint-disable-next-line no-plusplus + for (let i = 0; i < points.length; i++) { + const aX = points[i][0]; + const aY = points[i][1]; + let li = 1; + // eslint-disable-next-line no-plusplus + for (let j = 0; j < points.length; j++) { + const bX = points[j][0]; + if (i !== j) { + li = mul(li, div(sub(x, bX), sub(aX, bX))); + } + } + y = add(y, mul(li, aY)); + } + return y; +} + +exports.interpolate = interpolate; + +/** + * Generates a random polynomal of the correct degree and sets x as the first coefficient. + * @param {function int -> array[Uint8Array]} randomBytes Takes a length and returns a + * Uint8Array of that length. + * @param {Number} d The degree of the polynomial driven by the number shares and join threshold. + * @param {Number} x The point to hide. + * @return {Uint8Array} The random polynomial with x as the fist coefficient. + */ +function generate(randomBytes, d, x) { + let p = null; + // generate random polynomials until we find one of the given degree + do { + p = randomBytes(d + 1); + } while (degree(p) !== d); + + // set y intercept + p[0] = x; + + return p; +} + +exports.generate = generate; + +/** + * Evaluates a polynomal at point x using Horner's method. + * @param {Uint8Array} p The polynomial + * @return {Number} x The point to evaluate. + */ +function evaluate(p, x) { + let result = 0; + // eslint-disable-next-line no-plusplus + for (let i = p.length - 1; i >= 0; i--) { + result = add(mul(result, x), p[i]); + } + return result; +} + +exports.eval = evaluate; diff --git a/src/main/js/Scheme.js b/src/main/js/Scheme.js new file mode 100644 index 0000000..c57bd41 --- /dev/null +++ b/src/main/js/Scheme.js @@ -0,0 +1,100 @@ +/* + * Copyright © 2019 Simon Massey (massey1905@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const GF256 = require('./GF256.js'); + +/** + * Splits the given secret into {@code n} parts, of which any {@code k} or more can be combined to + * recover the original secret. + * @param {function int -> Uint8Array} randomBytes Takes a length and returns a random + * Uint8Array of that length + * @param {Number} n the number of parts to produce (must be {@code >1}) + * @param {Number} k the threshold of joinable parts (must be {@code <= n}) + * @param {array[Uint8Array]} secret The secret to split as an array of bytes + * @return {Object.} an map of {@code n} parts that are arrays of bytes of the + * secret length + */ +function split(randomBytes, n, k, secret) { + if (k <= 1) throw new Error('K must be > 1'); + if (n < k) throw new Error('N must be >= K'); + if (n > 255) throw new Error('N must be <= 255'); + + const values = new Array(n) + .fill(0) + .map(() => new Uint8Array(secret.length).fill(0)); + // eslint-disable-next-line no-plusplus + for (let i = 0; i < secret.length; i++) { + const p = GF256.generate(randomBytes, k - 1, secret[i]); + // eslint-disable-next-line no-plusplus + for (let x = 1; x <= n; x++) { + values[x - 1][i] = GF256.eval(p, x); + } + } + + const parts = {}; + + // eslint-disable-next-line no-plusplus + for (let i = 0; i < values.length; i++) { + const part = `${i + 1}`; + parts[part] = values[i]; + } + + return parts; +} + +exports.split = split; + +/** + * Joins the given parts to recover the original secret. + * + *

N.B.: There is no way to determine whether or not the returned value is actually the + * original secret. If the parts are incorrect, or are under the threshold value used to split the + * secret, a random value will be returned. + * + * @param {Object.} parts an map of {@code n} parts that are arrays of bytes + * of the secret length + * @return {Uint8Array} the original secret + * + */ +function join(parts) { + if (Object.keys(parts).length === 0) throw new Error('No parts provided'); + const lengths = Object.values(parts).map(x => x.length); + const max = Math.max.apply(null, lengths); + const min = Math.min.apply(null, lengths); + if (max !== min) { + throw new Error(`Varying lengths of part values. Min ${min}, Max ${max}`); + } + const secret = new Uint8Array(max); + // eslint-disable-next-line no-plusplus + for (let i = 0; i < secret.length; i++) { + const keys = Object.keys(parts); + const points = new Array(keys.length) + .fill(0) + .map(() => new Uint8Array(2).fill(0)); + // eslint-disable-next-line no-plusplus + for (let j = 0; j < keys.length; j++) { + const key = keys[j]; + const k = Number(key); + points[j][0] = k; + points[j][1] = parts[key][i]; + } + secret[i] = GF256.interpolate(points); + } + + return secret; +} + +exports.join = join; diff --git a/src/test/java/com/codahale/shamir/polygot/JavaScriptUtils.java b/src/test/java/com/codahale/shamir/polygot/JavaScriptUtils.java new file mode 100644 index 0000000..dbff7a1 --- /dev/null +++ b/src/test/java/com/codahale/shamir/polygot/JavaScriptUtils.java @@ -0,0 +1,53 @@ +/* + * Copyright © 2017 Coda Hale (coda.hale@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.codahale.shamir.polygot; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +/** + * The GraalJS internals cannot be fooled into casting between unsigned JavaScript bytes and signed + * Java bytes. We only ever want to do this within unit tests to compare arrays in-memory. This + * class uses an inefficent workaround of building bidirectional maps. The keys are Integer to fit a + * signed byte. + */ +public class JavaScriptUtils { + // https://stackoverflow.com/a/12310078/329496 + public static String byteToBinaryString(final byte b) { + return String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0'); + } + + static Map signedToUnsignedByteMap() { + final Map result = new HashMap<>(); + for (byte b = Byte.MIN_VALUE; ; b++) { + final String bits = byteToBinaryString(b); + final Integer i = Integer.parseInt(bits, 2) & 0xff; + result.put((int) b, i); + // here we avoid overflow on b++ causing an infinit loop + if (b == Byte.MAX_VALUE) break; + } + return Collections.unmodifiableMap(result); + } + + public static Map signedToUnsignedByteMap = signedToUnsignedByteMap(); + + public static Map unsignedToSignedByteMap = + signedToUnsignedByteMap.entrySet().stream() + .collect(Collectors.toMap(Entry::getValue, Entry::getKey));; +} diff --git a/src/test/java/com/codahale/shamir/polygot/NotRandomSource.java b/src/test/java/com/codahale/shamir/polygot/NotRandomSource.java new file mode 100644 index 0000000..51798aa --- /dev/null +++ b/src/test/java/com/codahale/shamir/polygot/NotRandomSource.java @@ -0,0 +1,35 @@ +/* + * Copyright © 2017 Coda Hale (coda.hale@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.codahale.shamir.polygot; + +import java.security.SecureRandom; + +/** This class ensures that unit tests can repeatedly initalise with the same polynomial. */ +public class NotRandomSource extends SecureRandom { + + @Override + public void nextBytes(byte[] bytes) { + for (int b = 0; b < bytes.length; b++) { + bytes[b] = (byte) (b + 1); + } + } + + public byte[] notRandomBytes(int len) { + byte[] bytes = new byte[len]; + this.nextBytes(bytes); + return bytes; + } +} diff --git a/src/test/java/com/codahale/shamir/polygot/package-info.java b/src/test/java/com/codahale/shamir/polygot/package-info.java new file mode 100644 index 0000000..064fdde --- /dev/null +++ b/src/test/java/com/codahale/shamir/polygot/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright © 2017 Coda Hale (coda.hale@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * This package provides classes to aid testing JavaScript against Java within + * the same JVM using GraalJS. + */ +package com.codahale.shamir.polygot; diff --git a/src/test/js/Benchmark.js b/src/test/js/Benchmark.js new file mode 100644 index 0000000..f90493b --- /dev/null +++ b/src/test/js/Benchmark.js @@ -0,0 +1,53 @@ +const { split, join } = require('../../main/js/Scheme.js'); + +const { randomBytes } = require('crypto'); + +const secret = new Uint8Array(16); + +for (let i = 0; i < secret.length; i++) { + secret[i] = i % 255; +} + +const mainrun = 200; + +const parts = 4; +const quorum = 3; + +var splits = split(randomBytes, parts, quorum, secret); + +function benchmarkSplit() { + for( let i = 0; i < mainrun; i++ ) { + splits = split(randomBytes, parts, quorum, secret); + } +} + +// https://nodejs.org/docs/latest-v10.x/api/perf_hooks.html#perf_hooks_performance_now +const { + performance, + PerformanceObserver +} = require('perf_hooks'); +const wrappedSplit = performance.timerify(benchmarkSplit); +const obsSplit = new PerformanceObserver((list) => { + const timed = list.getEntries()[0].duration / mainrun; + console.log(`split ${timed} ms`); + obsSplit.disconnect(); +}); +obsSplit.observe({ entryTypes: ['function'] }); +wrappedSplit(); + +var recovered = join(splits); + +function benchmarkJoin() { + for( let i = 0; i < mainrun; i++ ) { + recovered = join(splits); + } +} + +const wrappedJoin = performance.timerify(benchmarkJoin); +const obsJoin = new PerformanceObserver((list) => { + const timed = list.getEntries()[0].duration / mainrun + console.log(`join ${timed} ms`); + obsJoin.disconnect(); +}); +obsJoin.observe({ entryTypes: ['function'] }); +wrappedJoin(); diff --git a/src/test/js/GF256Tests.js b/src/test/js/GF256Tests.js new file mode 100644 index 0000000..45784f5 --- /dev/null +++ b/src/test/js/GF256Tests.js @@ -0,0 +1,159 @@ +/* + * Copyright © 2019 Simon Massey (massey1905@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const test = require('tape'); +const { randomBytes } = require('crypto'); + +const GF256 = require('../../main/js/GF256.js'); + +/* eslint-disable prefer-arrow-callback */ +/* eslint-disable func-names */ +/* eslint-disable no-plusplus */ +test('GF256Tests add', function (t) { + t.plan(1); + t.equal(GF256.add(100, 30), 122); +}); + +test('GF256Tests sub', function (t) { + t.plan(1); + t.equal(GF256.sub(100, 30), 122); +}); + +test('GF256Tests mul', function (t) { + t.plan(4); + t.equal(GF256.mul(90, 21), 254); + t.equal(GF256.mul(133, 5), 167); + t.equal(GF256.mul(0, 21), 0); + t.equal(GF256.mul(0xb6, 0x53), 0x36); +}); + +test('GF256Tests div', function (t) { + t.plan(4); + t.equal(GF256.div(90, 21), 189); + t.equal(GF256.div(6, 55), 151); + t.equal(GF256.div(22, 192), 138); + t.equal(GF256.div(0, 192), 0); +}); + +test('GF256Tests degree', function (t) { + t.plan(4); + t.equal(GF256.degree([1, 2]), 1); + t.equal(GF256.degree([1, 2, 0]), 1); + t.equal(GF256.degree([1, 2, 3]), 2); + t.equal(GF256.degree([0, 0, 0]), 0); +}); + + +const bytes = new Uint8Array(function () { + const returned = []; + for (let i = 1; i <= 255; i++) { + returned.push(i); + } + return returned; +}()); + +function makePairs(arr) { + const res = []; + const l = arr.length; + for (let i = 0; i < l; ++i) { + for (let j = i + 1; j < l; ++j) res.push([arr[i], arr[j]]); + } + return res; +} + +const pairs = makePairs(bytes); + +test('GF256Tests mul is commutative', function (t) { + pairs.forEach(function (pair) { + if (GF256.mul(pair[0], pair[1]) !== GF256.mul(pair[1], pair[0])) { + throw new Error(`mul not commutative for pair ${pair}`); + } + }); + t.end(); +}); + +test('GF256Tests add is commutative', function (t) { + pairs.forEach(function (pair) { + if (GF256.add(pair[0], pair[1]) !== GF256.add(pair[1], pair[0])) { + throw new Error(`add not commutitive for pair ${pair}`); + } + }); + t.end(); +}); + +test('GF256Tests sub is the inverse of add', function (t) { + pairs.forEach(function (pair) { + if (GF256.sub(GF256.add(pair[0], pair[1]), pair[1]) !== pair[0]) { + throw new Error(`sub is not the inverse of add for pair ${pair}`); + } + }); + t.end(); +}); + +test('GF256Tests div is the inverse of mul', function (t) { + pairs.forEach(function (pair) { + if (GF256.div(GF256.mul(pair[0], pair[1]), pair[1]) !== pair[0]) { + throw new Error(`div is not the inverse of mul for pair ${pair}`); + } + }); + t.end(); +}); + +test('GF256Tests mul is the inverse of div', function (t) { + pairs.forEach(function (pair) { + if (GF256.mul(GF256.div(pair[0], pair[1]), pair[1]) !== pair[0]) { + throw new Error(`mul is not the inverse of div for pair ${pair}`); + } + }); + t.end(); +}); + +test('GF256Tests eval', function (t) { + t.equal(GF256.eval([1, 0, 2, 3], 2), 17); + t.end(); +}); + +test('GF256Tests interpolate', function (t) { + t.equal(GF256.interpolate([[1, 1], [2, 2], [3, 3]]), 0); + t.equal(GF256.interpolate([[1, 80], [2, 90], [3, 20]]), 30); + t.equal(GF256.interpolate([[1, 43], [2, 22], [3, 86]]), 107); + t.end(); +}); + +let countDownFrom2 = 2; + +const zeroLastByteFirstTWoAttemptsRandomBytes = function (length) { + const p = randomBytes(length); + if (countDownFrom2 >= 0) { + p[p.length - 1] = 0; + countDownFrom2--; + } + return p; +}; + +test('GF256Tests generate', function (t) { + const p = GF256.generate(zeroLastByteFirstTWoAttemptsRandomBytes, 5, 20); + t.equal(p[0], 20); + t.equal(p.length, 6); + t.notOk(p[p.length - 1] === 0); + t.end(); +}); + +test('GF256Tests eval', function (t) { + t.plan(1); + const v = GF256.eval([1, 0, 2, 3], 2); + t.equal(v, 17); +}); diff --git a/src/test/js/PolygotTests.js b/src/test/js/PolygotTests.js new file mode 100644 index 0000000..1e1a8d4 --- /dev/null +++ b/src/test/js/PolygotTests.js @@ -0,0 +1,226 @@ +/* + * Copyright © 2019 Simon Massey (massey1905@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const test = require('tape-catch'); +const { randomBytes } = require('crypto'); + +/* eslint-disable prefer-arrow-callback */ +/* eslint-disable func-names */ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ + +// https://codereview.stackexchange.com/a/3589/75693 +function bytesToSring(bytes) { + const chars = []; + for (let i = 0, n = bytes.length; i < n;) { + chars.push(((bytes[i++] & 0xff) << 8) | (bytes[i++] & 0xff)); + } + return String.fromCharCode.apply(null, chars); +} + +// https://codereview.stackexchange.com/a/3589/75693 +function stringToBytes(str) { + const bytes = []; + for (let i = 0, n = str.length; i < n; i++) { + const char = str.charCodeAt(i); + bytes.push(char >>> 8, char & 0xff); + } + return bytes; +} + +// eslint-disable-next-line no-undef +const ByteArray = Java.type('byte[]'); +// eslint-disable-next-line no-undef +const JavaScriptUtils = Java.type('com.codahale.shamir.polygot.JavaScriptUtils'); + +function jsByteArrayToJavaByteArray(jsarray) { + const jbarray = new ByteArray(jsarray.length); + for (let i = 0; i < jsarray.length; i++) { + jbarray[i] = JavaScriptUtils.unsignedToSignedByteMap.get(jsarray[i]); + } + return jbarray; +} + +function jBytesArrayToJavaScriptByteArray(barray) { + const jsarray = []; + for (let i = 0; i < barray.length; i++) { + jsarray.push(JavaScriptUtils.signedToUnsignedByteMap.get(barray[i])); + } + return jsarray; +} + +test('PolygotTests Java and JavaScript byte array roundtrip', function (t) { + t.plan(1); + const secretUtf8 = 'ᚠᛇᚻ'; + const jsBytes = stringToBytes(secretUtf8); + const jbytes = jsByteArrayToJavaByteArray(jsBytes); + const jsRoundTripBytes = jBytesArrayToJavaScriptByteArray(jbytes); + t.equal(secretUtf8, bytesToSring(jsRoundTripBytes)); +}); + +// eslint-disable-next-line no-undef +const Scheme = Java.type('com.codahale.shamir.Scheme'); +// eslint-disable-next-line no-undef +const SecureRandom = Java.type('java.security.SecureRandom'); +const secureRandom = new SecureRandom(); + +test('PolygotTests JavaScript strings with all Java logic', function (t) { + t.plan(1); + const scheme = new Scheme(secureRandom, 5, 3); + const secretUtf8 = 'ᚠᛇᚻ'; + + const parts = scheme.split(jsByteArrayToJavaByteArray(stringToBytes(secretUtf8))); + const joined = scheme.join(parts); + + t.equal(secretUtf8, bytesToSring(jBytesArrayToJavaScriptByteArray(joined))); +}); + +const { split } = require('../../main/js/Scheme.js'); + +// eslint-disable-next-line no-undef +const HashMap = Java.type('java.util.HashMap'); + +function javaScriptToJavaParts(parts) { + const map = new HashMap(); + // eslint-disable-next-line no-restricted-syntax, guard-for-in + for (const key in parts) { + const bytes = parts[key]; + const jbarr = jsByteArrayToJavaByteArray(bytes); + map.put(Number(key), jbarr); + } + return map; +} + +function javaToJavaScriptParts(javaMap) { + const result = {}; + const entrySetIterator = javaMap.entrySet().iterator(); + while (entrySetIterator.hasNext()) { + const pair = entrySetIterator.next(); + const key = pair.getKey(); + const value = pair.getValue(); + result[key] = jBytesArrayToJavaScriptByteArray(value); + } + return result; +} + +// eslint-disable-next-line no-undef +const Collectors = Java.type('java.util.stream.Collectors'); + +function equalParts(jParts, jsParts) { + // js keys are strings + const jsKeysNumbers = Object.keys(jsParts); + // j keys are java integers that we map to strings + const jKeysSet = jParts + .keySet() + .stream() + .map(n => n.toString()) + .collect(Collectors.toList()); + + // check that all js keys are in the j keys + // eslint-disable-next-line no-restricted-syntax + for (const jsk of jsKeysNumbers) { + if (!jKeysSet.contains(jsk)) { + throw new Error(`jKeysSet ${jKeysSet} does not contain jsk ${jsk}`); + } + } + + // check that all j keys are in the js keys + // eslint-disable-next-line no-restricted-syntax + for (const jk of jKeysSet) { + if (!jsKeysNumbers.includes(jk)) { + throw new Error(`jsKeysNumbers ${jsKeysNumbers} does not contain jk ${jk}`); + } + } + + // eslint-disable-next-line no-restricted-syntax + for (const k of Object.keys(jsParts)) { + const jArray = jBytesArrayToJavaScriptByteArray(jParts.get(Number(k))); + const jsArray = jsParts[k]; + if (jArray.length !== jsArray.length) { + throw new Error(`unequal lengths ${jArray.length} != ${jsArray.length}`); + } + for (let l = 0; l < jArray.length; l++) { + if (jArray[l] !== jsArray[l]) { + throw new Error(`at index ${l}: ${jArray[l]} != ${jsArray[l]}`); + } + } + } + + return true; +} + +test('PolygotTests roundrip parts between JavaScript and Java', function (t) { + t.plan(1); + const secretUtf8 = 'ᚠᛇᚻ'; + const secret = stringToBytes(secretUtf8); + const parts = split(randomBytes, 3, 2, secret); + const jParts = javaScriptToJavaParts(parts); + const jsParts = javaToJavaScriptParts(jParts); + t.ok(equalParts(jParts, jsParts), 'roundtrip parts'); +}); + +// eslint-disable-next-line no-undef +const NotRandomSource = Java.type('com.codahale.shamir.polygot.NotRandomSource'); +const notRandomSource = new NotRandomSource(); +function notRandomSourceJavaScript(len) { + const bytes = []; + for (let i = 0; i < len; i++) { + bytes[i] = i + 1; + } + return bytes; +} + +test('PolygotTests compare Java and JavaScript split', function (t) { + t.plan(1); + const secretUtf8 = 'ᚠᛇᚻ'; + const secret = stringToBytes(secretUtf8); + + const jsParts = split(notRandomSourceJavaScript, 3, 2, secret); + + const jscheme = new Scheme(notRandomSource, 3, 2); + const jParts = jscheme.split(jsByteArrayToJavaByteArray(secret)); + t.ok(equalParts(jParts, jsParts), 'splits match'); +}); + +test('PolygotTests JavaScript split with Java join', function (t) { + t.plan(1); + const secretUtf8 = 'ᚠᛇᚻ'; + + const secret = stringToBytes(secretUtf8); + const jsParts = split(randomBytes, 3, 2, secret); + const jscheme = new Scheme(secureRandom, 3, 2); + const joined = jscheme.join(javaScriptToJavaParts(jsParts)); + + t.equal( + bytesToSring(jBytesArrayToJavaScriptByteArray(joined)), + secretUtf8, + 'java joined js parts', + ); +}); + +const { join } = require('../../main/js/Scheme.js'); + +test('PolygotTests Java split with JavaScript join', function (t) { + t.plan(1); + const secretUtf8 = 'ᚠᛇᚻ'; + + const secret = stringToBytes(secretUtf8); + const jscheme = new Scheme(secureRandom, 3, 2); + const jParts = jscheme.split(jsByteArrayToJavaByteArray(secret)); + const joined = join(javaToJavaScriptParts(jParts)); + + t.equal(bytesToSring(joined), secretUtf8, 'java joined js parts'); +}); diff --git a/src/test/js/SchemeTests.js b/src/test/js/SchemeTests.js new file mode 100644 index 0000000..373c4ae --- /dev/null +++ b/src/test/js/SchemeTests.js @@ -0,0 +1,140 @@ +/* + * Copyright © 2019 Simon Massey (massey1905@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const test = require('tape'); + +const { split, join } = require('../../main/js/Scheme.js'); + +const { randomBytes } = require('crypto'); + +/* eslint-disable prefer-arrow-callback */ +/* eslint-disable func-names */ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ + +// https://codereview.stackexchange.com/a/3589/75693 +function bytesToSring(bytes) { + const chars = []; + for (let i = 0, n = bytes.length; i < n;) { + chars.push(((bytes[i++] & 0xff) << 8) | (bytes[i++] & 0xff)); + } + return String.fromCharCode.apply(null, chars); +} + + +// https://codereview.stackexchange.com/a/3589/75693 +function stringToBytes(str) { + const bytes = []; + for (let i = 0, n = str.length; i < n; i++) { + const char = str.charCodeAt(i); + bytes.push(char >>> 8, char & 0xff); + } + return bytes; +} + +test('SchemeTests roundtrip', function (t) { + const parts = 5; + const quorum = 3; + + // http://kermitproject.org/utf8.html + // From the Anglo-Saxon Rune Poem (Rune version) + const secretUtf8 = `ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ +ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ +ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ᛬`; + + // string length is only 117 characters but the byte length is 234. + const secret = stringToBytes(secretUtf8); + + const splits = split(randomBytes, parts, quorum, secret); + // only quorum parts are necessary + + delete splits['2']; + delete splits['3']; + + const joined = join(splits); + t.equal(joined[200], secret[200]); + t.equal(joined[201], secret[201]); + const joinedUtf8 = bytesToSring(joined); + t.equal(secretUtf8, joinedUtf8); + t.end(); +}); + +test('SchemeTests roundtrip two parts', function (t) { + const parts = 3; + const quorum = 2; + + const secretUtf8 = 'ᚠᛇᚻ'; + const secret = stringToBytes(secretUtf8); + + for (let i = 1; i <= 3; i++) { + const splits = split(randomBytes, parts, quorum, secret); + delete splits[`${i}`]; + const joinedUtf8 = bytesToSring(join(splits)); + t.equal(secretUtf8, joinedUtf8); + } + + t.end(); +}); + +test('SchemeTests split input validation', function (t) { + const secretUtf8 = 'ᚠᛇᚻ'; + const secret = stringToBytes(secretUtf8); + + t.plan(3); + + try { + split(randomBytes, 256, 2, secret); + t.notOk(true); + } catch (e) { + t.ok(e.toString().includes('N must be <= 255'), e); + } + + try { + split(randomBytes, 3, 1, secret); + t.notOk(true); + } catch (e) { + t.ok(e.toString().includes('K must be > 1'), e); + } + + try { + split(randomBytes, 2, 3, secret); + t.notOk(true); + } catch (e) { + t.ok(e.toString().includes('N must be >= K'), e); + } + + t.end(); +}); + +test('SchemeTests join input validation', function (t) { + try { + join({}); + t.notOk(true); + } catch (e) { + t.ok(e.toString().includes('No parts provided'), e); + } + + try { + const splits = split(randomBytes, 3, 2, stringToBytes('ᚠᛇᚻ')); + splits['2'] = Uint8Array.of(216, 30, 190, 102); + join(splits); + t.notOk(true); + } catch (e) { + t.ok(e.toString().includes('Varying lengths of part values'), e); + } + + t.end(); +}); diff --git a/src/test/js/TieredSharing.js b/src/test/js/TieredSharing.js new file mode 100644 index 0000000..a59665d --- /dev/null +++ b/src/test/js/TieredSharing.js @@ -0,0 +1,81 @@ +/* + * Copyright © 2019 Simon Massey (massey1905@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const test = require('tape'); + +const { split, join } = require('../../main/js/Scheme.js'); + +const { randomBytes } = require('crypto'); + +/* eslint-disable prefer-arrow-callback */ +/* eslint-disable func-names */ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ + +// https://codereview.stackexchange.com/a/3589/75693 +function bytesToSring(bytes) { + const chars = []; + for (let i = 0, n = bytes.length; i < n;) { + chars.push(((bytes[i++] & 0xff) << 8) | (bytes[i++] & 0xff)); + } + return String.fromCharCode.apply(null, chars); +} + + +// https://codereview.stackexchange.com/a/3589/75693 +function stringToBytes(str) { + const bytes = []; + for (let i = 0, n = str.length; i < n; i++) { + const char = str.charCodeAt(i); + bytes.push(char >>> 8, char & 0xff); + } + return bytes; +} + +test('TieredSharing roundtrip', function (t) { + + const secretUtf8 = 'ᚠᛇᚻ'; + const secret = stringToBytes(secretUtf8); + + const adminParts = 3; + const adminQuorum = 2; + const adminSplits = split(randomBytes, adminParts, adminQuorum, secret); + + const userParts = 4; + const userQuorum = 3; + const usersSplits = split(randomBytes, userParts, userQuorum, adminSplits['3'] ); + + // throw away third share that is split into 4 user parts + delete adminSplits['3']; + + // reconstruct the secret with two admin shares + const joinedAdminShares = join(adminSplits); + t.equal(secretUtf8, bytesToSring(joinedAdminShares)); + + // throw away second admin share we only have one remaining + delete adminSplits['2']; + // throw away one user share as we only need three + delete usersSplits['1']; + + // reconstruct the third admin share from the three user shares + const joinedUserShares = join(usersSplits); + + // use the first admin share and the recovered third share + const mixedShares = { '1': adminSplits['1'], '3': joinedUserShares }; + const joinedMixedShares = join(mixedShares); + t.equal(secretUtf8, bytesToSring(joinedMixedShares)); + t.end(); +}); diff --git a/test_js_against_java.sh b/test_js_against_java.sh new file mode 100755 index 0000000..08e25bf --- /dev/null +++ b/test_js_against_java.sh @@ -0,0 +1,19 @@ +#!/bin/sh +# +# Copyright © 2017 Coda Hale (coda.hale@gmail.com) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This tests the +docker build -f Dockerfile.graaljs . \ No newline at end of file