├── .gitignore ├── .gitmodules ├── .travis.yml ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── shamirsecretsharing ├── build.gradle └── src ├── androidTest └── java │ └── com │ └── dsprenkels │ └── sss │ └── android │ └── jni │ └── test │ └── ShamirSecretSharingTest.java └── main ├── AndroidManifest.xml ├── cpp ├── CMakeLists.txt └── sss-jni.c └── java └── com └── dsprenkels └── sss └── android └── jni └── ShamirSecretSharing.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.aar 4 | *.ap_ 5 | 6 | # Files for the ART/Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | out/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | # Android Studio Navigation editor temp files 31 | .navigation/ 32 | 33 | # Android Studio captures folder 34 | captures/ 35 | 36 | # Intellij 37 | *.iml 38 | .idea/ 39 | 40 | # Keystore files 41 | *.jks 42 | 43 | # External native build folder generated in Android Studio 2.2 and later 44 | .externalNativeBuild 45 | 46 | # Google Services (e.g. APIs or Firebase) 47 | google-services.json 48 | 49 | # Freeline 50 | freeline.py 51 | freeline/ 52 | freeline_project_description.json 53 | 54 | *.cxx 55 | shamirsecretsharing/.cxx/* 56 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "shamirsecretsharing/src/main/cpp/sss"] 2 | path = shamirsecretsharing/src/main/cpp/sss 3 | url = https://github.com/dsprenkels/sss.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | android: 3 | components: 4 | - tools 5 | - platform-tools 6 | - build-tools-26.0.1 7 | - android-26 8 | - extra-google-m2repository 9 | - extra-android-m2repository 10 | before_install: 11 | - # https://github.com/travis-ci/travis-ci/issues/6617#issuecomment-274684121 12 | - mkdir -p "$ANDROID_HOME/licenses" 13 | - echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license" 14 | - echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license" 15 | install: 16 | - # https://github.com/travis-ci/travis-ci/issues/5395#issue-124658713 17 | - curl -L https://dl.google.com/android/repository/android-ndk-r15c-linux-x86_64.zip -O 18 | - unzip -q android-ndk-r15c-linux-x86_64.zip 19 | - export ANDROID_NDK_HOME=`pwd`/android-ndk-r15c 20 | - export LOCAL_ANDROID_NDK_HOME="$ANDROID_NDK_HOME" 21 | - export LOCAL_ANDROID_NDK_HOST_PLATFORM="linux-x86_64" 22 | - export PATH=$PATH:${ANDROID_NDK_HOME} 23 | script: 24 | - ./gradlew build 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sss-android 2 | Android bindings for Shamir secret sharing library 3 | 4 | This is a fork of https://github.com/dsprenkels/sss-android. 5 | Adapted to the version of Android Studio Flamingo | 2022.2.1 Patch 1 6 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | gradlePluginPortal() 5 | } 6 | dependencies { 7 | classpath 'com.android.tools.build:gradle:7.4.2' 8 | } 9 | } 10 | 11 | allprojects { 12 | repositories { 13 | google() 14 | gradlePluginPortal() 15 | } 16 | } 17 | 18 | task clean(type: Delete) { 19 | delete rootProject.buildDir 20 | } 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | android.useAndroidX=true 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ContractusTech/sss-android/a8771fadebd24395ade2b8354681b663d129e9f7/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Mar 15 19:57:53 GET 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':shamirsecretsharing' -------------------------------------------------------------------------------- /shamirsecretsharing/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 31 5 | buildToolsVersion '26.0.1' 6 | defaultConfig { 7 | minSdkVersion 20 8 | targetSdkVersion 31 9 | versionCode 1 10 | versionName "0.1.0" 11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 12 | } 13 | 14 | buildTypes { 15 | debug { 16 | debuggable true 17 | //minifyEnabled true 18 | //shrinkResources true 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | externalNativeBuild { 23 | cmake { 24 | path "src/main/cpp/CMakeLists.txt" 25 | } 26 | } 27 | } 28 | 29 | dependencies { 30 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 31 | androidTestImplementation 'junit:junit:4.13.2' 32 | androidTestImplementation "androidx.test:runner:1.5.2" 33 | } 34 | -------------------------------------------------------------------------------- /shamirsecretsharing/src/androidTest/java/com/dsprenkels/sss/android/jni/test/ShamirSecretSharingTest.java: -------------------------------------------------------------------------------- 1 | package com.dsprenkels.sss.android.jni.test; 2 | 3 | 4 | import com.dsprenkels.sss.android.jni.ShamirSecretSharing; 5 | 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | 11 | import static org.junit.Assert.assertArrayEquals; 12 | import static org.junit.Assert.assertFalse; 13 | import static org.junit.Assert.assertTrue; 14 | 15 | import androidx.test.ext.junit.runners.AndroidJUnit4; 16 | 17 | @RunWith(AndroidJUnit4.class) 18 | public class ShamirSecretSharingTest { 19 | private final byte[] data; 20 | private final byte[] key; 21 | 22 | public ShamirSecretSharingTest() { 23 | data = new byte[ShamirSecretSharing.DATA_LEN]; 24 | key = new byte[ShamirSecretSharing.KEY_LEN]; 25 | Arrays.fill(data, (byte) 42); 26 | Arrays.fill(key, (byte) 42); 27 | } 28 | 29 | @Test 30 | public void Create_combine_shares_functional(){ 31 | ArrayList shares; 32 | byte[] restored; 33 | 34 | shares = new ArrayList<>(Arrays.asList(ShamirSecretSharing.createShares(data, 5, 4))); 35 | restored = ShamirSecretSharing.combineShares(shares.toArray(new byte[][] {})); 36 | assertArrayEquals(restored, data); 37 | shares.remove(0); 38 | restored = ShamirSecretSharing.combineShares(shares.toArray(new byte[][] {})); 39 | assertArrayEquals(restored, data); 40 | shares.remove(0); 41 | restored = ShamirSecretSharing.combineShares(shares.toArray(new byte[][] {})); 42 | assertArrayEquals(restored, null); 43 | shares.remove(0); 44 | restored = ShamirSecretSharing.combineShares(shares.toArray(new byte[][] {})); 45 | assertArrayEquals(restored, null); 46 | shares.remove(0); 47 | restored = ShamirSecretSharing.combineShares(shares.toArray(new byte[][] {})); 48 | assertArrayEquals(restored, null); 49 | shares.remove(0); 50 | restored = ShamirSecretSharing.combineShares(shares.toArray(new byte[][] {})); 51 | assertArrayEquals(restored, null); 52 | } 53 | 54 | @Test 55 | public void Create_combine_keyshares_functional() { 56 | ArrayList keyshares; 57 | byte[] restored; 58 | 59 | keyshares = new ArrayList<>(Arrays.asList(ShamirSecretSharing.createKeyshares(key, 5, 4))); 60 | restored = ShamirSecretSharing.combineKeyshares(keyshares.toArray(new byte[][] {})); 61 | assertArrayEquals(restored, key); 62 | keyshares.remove(0); 63 | restored = ShamirSecretSharing.combineKeyshares(keyshares.toArray(new byte[][] {})); 64 | assertArrayEquals(restored, key); 65 | keyshares.remove(0); 66 | restored = ShamirSecretSharing.combineKeyshares(keyshares.toArray(new byte[][] {})); 67 | assertFalse(Arrays.equals(restored, key)); 68 | keyshares.remove(0); 69 | restored = ShamirSecretSharing.combineKeyshares(keyshares.toArray(new byte[][] {})); 70 | assertFalse(Arrays.equals(restored, key)); 71 | keyshares.remove(0); 72 | restored = ShamirSecretSharing.combineKeyshares(keyshares.toArray(new byte[][] {})); 73 | assertFalse(Arrays.equals(restored, key)); 74 | keyshares.remove(0); 75 | restored = ShamirSecretSharing.combineKeyshares(keyshares.toArray(new byte[][] {})); 76 | assertFalse(Arrays.equals(restored, key)); 77 | } 78 | 79 | @Test(expected = IllegalArgumentException.class) 80 | public void Create_shares_error_data_empty() { 81 | ShamirSecretSharing.createShares(new byte[] {}, 5, 4); 82 | } 83 | 84 | @Test(expected = IllegalArgumentException.class) 85 | public void Create_shares_error_data_len() { 86 | ShamirSecretSharing.createShares(key, 5, 4); 87 | } 88 | 89 | @Test(expected = IllegalArgumentException.class) 90 | public void Create_shares_error_count_lo() { 91 | ShamirSecretSharing.createShares(data, 0, 0); 92 | } 93 | 94 | @Test(expected = IllegalArgumentException.class) 95 | public void Create_shares_error_count_hi() { 96 | ShamirSecretSharing.createShares(data, 256, 4); 97 | } 98 | 99 | @Test(expected = IllegalArgumentException.class) 100 | public void Create_shares_error_threshold_lo() { 101 | ShamirSecretSharing.createShares(data, 5, 0); 102 | } 103 | 104 | @Test(expected = IllegalArgumentException.class) 105 | public void Create_shares_error_threshold_hi() { 106 | ShamirSecretSharing.createShares(data, 5, 6); 107 | } 108 | 109 | @Test(expected = IllegalArgumentException.class) 110 | public void Combine_shares_error_share_count() { 111 | ShamirSecretSharing.combineShares(new byte[256][ShamirSecretSharing.SHARE_LEN]); 112 | } 113 | 114 | @Test(expected = IllegalArgumentException.class) 115 | public void Create_keyshares_error_key_empty() { 116 | ShamirSecretSharing.createKeyshares(new byte[] {}, 5, 4); 117 | } 118 | 119 | @Test(expected = IllegalArgumentException.class) 120 | public void Create_keyshares_error_key_len() { 121 | ShamirSecretSharing.createKeyshares(data, 5, 4); 122 | } 123 | 124 | @Test(expected = IllegalArgumentException.class) 125 | public void Create_keyshares_error_count_lo() { 126 | ShamirSecretSharing.createKeyshares(key, 0, 0); 127 | } 128 | 129 | @Test(expected = IllegalArgumentException.class) 130 | public void Create_keyshares_error_count_hi() { 131 | ShamirSecretSharing.createKeyshares(key, 256, 4); 132 | } 133 | 134 | @Test(expected = IllegalArgumentException.class) 135 | public void Create_keyshares_error_threshold_lo() { 136 | ShamirSecretSharing.createKeyshares(key, 5, 0); 137 | } 138 | 139 | @Test(expected = IllegalArgumentException.class) 140 | public void Create_keyshares_error_threshold_hi() { 141 | ShamirSecretSharing.createKeyshares(key, 5, 6); 142 | } 143 | 144 | @Test(expected = IllegalArgumentException.class) 145 | public void Combine_keyshares_error_share_count() { 146 | ShamirSecretSharing.combineShares(new byte[256][ShamirSecretSharing.KEYSHARE_LEN]); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /shamirsecretsharing/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /shamirsecretsharing/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.1) 2 | project (sss-jni) 3 | 4 | add_library(sss-jni SHARED 5 | sss-jni.c 6 | sss/sss.c 7 | sss/hazmat.c 8 | sss/randombytes.c 9 | sss/tweetnacl.c) 10 | 11 | # Include libraries needed for sss-jni lib 12 | target_link_libraries(sss-jni 13 | android) 14 | -------------------------------------------------------------------------------- /shamirsecretsharing/src/main/cpp/sss-jni.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by daan on 04/08/17. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include "sss/sss.h" 9 | 10 | #define DATA_LEN 64 11 | #define SHARE_LEN 113 12 | #define KEY_LEN 32 13 | #define KEYSHARE_LEN 33 14 | 15 | static jint throw(JNIEnv *env, char *className, char *message) 16 | { 17 | jclass class; 18 | class = (*env)->FindClass(env, className); 19 | if (class == NULL) { 20 | class = (*env)->FindClass(env, "java/lang/NoClassDefFoundError"); 21 | // In the case this call to FindClass gives back NULL, we have an unrecoverable error. 22 | // We should just let this trigger an JNI exception to indicate that there is a bug in 23 | // the code. 24 | } 25 | return (*env)->ThrowNew(env, class, message); 26 | } 27 | 28 | static jboolean checkNK(JNIEnv *env, long n, long k) { 29 | if (n < 1 || n > 255) { 30 | throw(env, "java/lang/IllegalArgumentException", "`count` must be in 1..255"); 31 | return JNI_FALSE; 32 | } 33 | if (k < 1 || k > n) { 34 | throw(env, "java/lang/IllegalArgumentException", "`threshold` must be in 1..n"); 35 | return JNI_FALSE; 36 | } 37 | return JNI_TRUE; 38 | } 39 | 40 | JNIEXPORT jobjectArray JNICALL Java_com_dsprenkels_sss_android_jni_ShamirSecretSharing_createShares( 41 | JNIEnv *env, jobject this, 42 | jbyteArray data, jint count, jint threshold) 43 | { 44 | jsize data_len = (*env)->GetArrayLength(env, data); 45 | if (data_len != DATA_LEN) { 46 | throw(env, "java/lang/IllegalArgumentException", "invalid data length"); 47 | return NULL; 48 | } 49 | if (!checkNK(env, count, threshold)) { 50 | return NULL; 51 | } 52 | 53 | // Allocate i/o buffers 54 | jbyte in[DATA_LEN] = {0}; 55 | jbyte out[count * SHARE_LEN]; 56 | memset(out, 0, (size_t) count * SHARE_LEN); 57 | 58 | // Call sss_create_shares 59 | (*env)->GetByteArrayRegion(env, data, 0, DATA_LEN, in); 60 | sss_create_shares((sss_Share*) out, (const uint8_t*) in, count, threshold); 61 | 62 | // Group output shares into chunks of SHARE_LEN size 63 | jclass clsArray = (*env)->FindClass(env, "[B"); 64 | if (clsArray == NULL) { 65 | throw(env, "java/lang/NoClassDefFoundError", "no class '[B' found"); 66 | return NULL; 67 | } 68 | jobjectArray shares = (*env)->NewObjectArray(env, count, clsArray, NULL); 69 | for (int idx = 0; idx < count; ++idx) { 70 | jbyteArray tmp = (*env)->NewByteArray(env, SHARE_LEN); 71 | if (tmp == NULL) { 72 | throw(env, "java/lang/OutOfMemoryError", "could not allocate jbyteArray"); 73 | return NULL; 74 | } 75 | (*env)->SetByteArrayRegion(env, tmp, 0, SHARE_LEN, &out[idx * SHARE_LEN]); 76 | (*env)->SetObjectArrayElement(env, shares, idx, tmp); 77 | } 78 | return shares; 79 | } 80 | 81 | JNIEXPORT jbyteArray JNICALL Java_com_dsprenkels_sss_android_jni_ShamirSecretSharing_combineShares( 82 | JNIEnv *env, jobject this, jobjectArray shares) 83 | { 84 | // Fail if the amount of shares is not too big 85 | jsize count = (*env)->GetArrayLength(env, shares); 86 | if (count > 255) { 87 | throw(env, "java/lang/IllegalArgumentException", "too many shares given"); 88 | return NULL; 89 | } 90 | 91 | // Pack the shares into a consecutive buffer 92 | jbyte tmp[count * SHARE_LEN]; 93 | memset(tmp, 0, count * SHARE_LEN); 94 | for (int idx = 0; idx < count; ++idx) { 95 | jbyteArray share = (*env)->GetObjectArrayElement(env, shares, idx); 96 | if ((*env)->GetArrayLength(env, share) != SHARE_LEN) { 97 | throw(env, "java/lang/IllegalArgumentException", "invalid share length"); 98 | return NULL; 99 | } 100 | (*env)->GetByteArrayRegion(env, share, 0, SHARE_LEN, &tmp[idx * SHARE_LEN]); 101 | } 102 | 103 | // Call sss library 104 | jbyte out[DATA_LEN] = {0}; 105 | jint ret = sss_combine_shares((uint8_t*) out, (const sss_Share*) tmp, count); 106 | if (ret != 0) { 107 | // Failed to combine the shares 108 | return NULL; 109 | } 110 | 111 | // Convert result to byte array 112 | jbyteArray data = (*env)->NewByteArray(env, DATA_LEN); 113 | if (data == NULL) { 114 | throw(env, "java/lang/OutOfMemoryError", "could not allocate array for restored data"); 115 | return NULL; 116 | } 117 | (*env)->SetByteArrayRegion(env, data, 0, DATA_LEN, out); 118 | return data; 119 | } 120 | 121 | JNIEXPORT jobjectArray JNICALL Java_com_dsprenkels_sss_android_jni_ShamirSecretSharing_createKeyshares( 122 | JNIEnv *env, jobject this, 123 | jbyteArray key, jint count, jint threshold) 124 | { 125 | jsize key_len = (*env)->GetArrayLength(env, key); 126 | if (key_len != KEY_LEN) { 127 | throw(env, "java/lang/IllegalArgumentException", "invalid key length"); 128 | return NULL; 129 | } 130 | if (!checkNK(env, count, threshold)) { 131 | return NULL; 132 | } 133 | 134 | // Allocate i/o buffers 135 | jbyte in[KEY_LEN] = {0}; 136 | jbyte out[count * KEYSHARE_LEN]; 137 | memset(out, 0, (size_t) count * KEYSHARE_LEN); 138 | 139 | // Call sss_create_shares 140 | (*env)->GetByteArrayRegion(env, key, 0, KEY_LEN, in); 141 | sss_create_keyshares((sss_Keyshare*) out, (const uint8_t*) in, count, threshold); 142 | 143 | // Group output shares into chunks of SHARE_LEN size 144 | jclass clsArray = (*env)->FindClass(env, "[B"); 145 | if (clsArray == NULL) { 146 | throw(env, "java/lang/NoClassDefFoundError", "no class '[B' found"); 147 | return NULL; 148 | } 149 | jobjectArray keyshares = (*env)->NewObjectArray(env, count, clsArray, NULL); 150 | for (int idx = 0; idx < count; ++idx) { 151 | jbyteArray tmp = (*env)->NewByteArray(env, KEYSHARE_LEN); 152 | if (tmp == NULL) { 153 | throw(env, "java/lang/OutOfMemoryError", "could not allocate jbyteArray"); 154 | return NULL; 155 | } 156 | (*env)->SetByteArrayRegion(env, tmp, 0, KEYSHARE_LEN, &out[idx * KEYSHARE_LEN]); 157 | (*env)->SetObjectArrayElement(env, keyshares, idx, tmp); 158 | } 159 | return keyshares; 160 | } 161 | 162 | JNIEXPORT jbyteArray JNICALL Java_com_dsprenkels_sss_android_jni_ShamirSecretSharing_combineKeyshares( 163 | JNIEnv *env, jobject this, jobjectArray keyshares) 164 | { 165 | // Fail if the amount of shares is not too big 166 | jsize count = (*env)->GetArrayLength(env, keyshares); 167 | if (count > 255) { 168 | throw(env, "java/lang/IllegalArgumentException", "too many keyshares given"); 169 | return NULL; 170 | } 171 | 172 | // Pack the shares into a consecutive buffer 173 | jbyte tmp[count * KEYSHARE_LEN]; 174 | memset(tmp, 0, count * KEYSHARE_LEN); 175 | for (int idx = 0; idx < count; ++idx) { 176 | jbyteArray keyshare = (*env)->GetObjectArrayElement(env, keyshares, idx); 177 | if ((*env)->GetArrayLength(env, keyshare) != KEYSHARE_LEN) { 178 | throw(env, "java/lang/IllegalArgumentException", "invalid keyshare length"); 179 | return NULL; 180 | } 181 | (*env)->GetByteArrayRegion(env, keyshare, 0, KEYSHARE_LEN, &tmp[idx * KEYSHARE_LEN]); 182 | } 183 | 184 | // Call sss library 185 | jbyte out[KEY_LEN] = {0}; 186 | sss_combine_keyshares((uint8_t*) out, (const sss_Keyshare*) tmp, count); 187 | 188 | // Convert result to byte array 189 | jbyteArray key = (*env)->NewByteArray(env, KEY_LEN); 190 | if (key == NULL) { 191 | throw(env, "java/lang/OutOfMemoryError", "could not allocate array for restored key"); 192 | return NULL; 193 | } 194 | (*env)->SetByteArrayRegion(env, key, 0, KEY_LEN, out); 195 | return key; 196 | } -------------------------------------------------------------------------------- /shamirsecretsharing/src/main/java/com/dsprenkels/sss/android/jni/ShamirSecretSharing.java: -------------------------------------------------------------------------------- 1 | package com.dsprenkels.sss.android.jni; 2 | 3 | /** 4 | * Shamir secret sharing in Java 5 | * 6 | * @author Daan Sprenkels 7 | * @version 0.1 8 | * @since 2017-08-07 9 | * 10 | * This class exposes bindings to the Shamir secret sharing library at 11 | * github.com/dsprenkels/sss. 12 | * 13 | * The main methods that a normal user should need are {@link #createShares(byte[], int, int)} 14 | * and {@link #combineShares(byte[][])}. These allow you to secret-share buffers of 64 bytes long 15 | * easy and safely. 16 | * 17 | * The other functions {@link #createKeyshares(byte[], int, int)} and 18 | * {@link #combineKeyshares(byte[][])} are for sharing cryptographic keys. These function do not 19 | * guarantee the same security as the other ones. Use it only when you really know what you 20 | * are doing! If you don't, please just stick with the standard functions. An example use case (for 21 | * people familiar with cryptographic primitives) is to use the keyshare functions to use an AEAD 22 | * wrapper to share arbitrary lengths of data. Hold the key under escrow by secret-sharing it and 23 | * distributing the shares. This way you are not limited by the fact that 24 | * {@link #createShares(byte[], int, int)} can only share arrays of 64 bytes. 25 | */ 26 | public final class ShamirSecretSharing { 27 | static { 28 | System.loadLibrary("sss-jni"); 29 | } 30 | 31 | /** 32 | * The length of a to be secret-shared data buffer 33 | */ 34 | public static final int DATA_LEN = 64; 35 | 36 | /** 37 | * The length of a share, as created by {@link #createShares(byte[], int, int)} 38 | */ 39 | public static final int SHARE_LEN = 113; 40 | 41 | /** 42 | * The length of a key that will be shared using {@link #createKeyshares(byte[], int, int)} 43 | */ 44 | public static final int KEY_LEN = 32; 45 | 46 | /** 47 | * The length of a keyshare, as created by {@link #createKeyshares(byte[], int, int)} 48 | */ 49 | public static final int KEYSHARE_LEN = 33; 50 | 51 | /** 52 | * Create a set of shares from the data param 53 | * @param data the data buffer that needs to be split. The size of this array has to be equal 54 | * to {@link #DATA_LEN}. 55 | * @param count the amount of shares that will be created from the data. This has to be greater 56 | * than 0 and less than 256. 57 | * @param threshold the restoration threshold for restoring the secret-shared data. This has to be 58 | * greater than 0 and less than count. 59 | * @return a byte[][] array of {@code count} shares, each with a length of {@link #SHARE_LEN}. 60 | * @throws IllegalArgumentException if one of the arguments to this function is not given in the 61 | * correct format. 62 | */ 63 | public static native byte[][] createShares(byte[] data, int count, int threshold); 64 | 65 | /** 66 | * Try to restore the data using a set of shares. 67 | * @param shares the shares used to restore the data. 68 | * @return the original shared data, if it was possible to restore this from the shares that 69 | * were provided. If the shares could not reconstruct a valid secret (threshold was too 70 | * high, or some of the shares were corrupted) the return value will be {@code null} 71 | * instead. 72 | * @throws IllegalArgumentException if one of the arguments to this function is not given in the 73 | * correct format, e.g. when one of the shares has an invalid length. 74 | */ 75 | public static native byte[] combineShares(byte[][] shares); 76 | 77 | /** 78 | * Create a set of keyshares from the key param. 79 | * @param key key buffer that needs to be split. The size of this array has to be equal 80 | * to {@link #KEY_LEN}. 81 | * @param count amount of shares that will be created from the key. This has to be greater 82 | * than 0 and less than 256. 83 | * @param threshold restoration threshold for restoring the secret-shared key. This has to be 84 | * greater than 0 and less than count. 85 | * @return a byte[][] array of {@code count} keyshares, each with a length of 86 | * {@link #KEYSHARE_LEN}. 87 | * @throws IllegalArgumentException if one of the arguments to this function is not given in the 88 | * correct format. 89 | */ 90 | public static native byte[][] createKeyshares(byte[] key, int count, int threshold); 91 | 92 | /** 93 | * Try to restore the original key using a set of keyshares. 94 | * @param keyshares the keyshares we will use to try to reconstruct the key. 95 | * @return the original key. In contrast to {@link #combineShares(byte[][])}, there is no 96 | * checksum on the key, so the library cannot check if the key recovery was successful. 97 | * If restoring the key fails, this function gives back an array that looks like 98 | * garbage. However, treat this return value as a secret! Even when the keyshare 99 | * combination has failed, the return value may tell a lot about the keyshares that were 100 | * used in the process. 101 | * @throws IllegalArgumentException if one of the arguments to this function is not given in the 102 | * correct format, e.g. when one of the keyshares has an invalid length. 103 | */ 104 | public static native byte[] combineKeyshares(byte[][] keyshares); 105 | } 106 | --------------------------------------------------------------------------------