diff --git a/.gitignore b/.gitignore index bc327875..7f783eea 100644 --- a/.gitignore +++ b/.gitignore @@ -17,17 +17,9 @@ DerivedData/ /platform-darwin/Podfile.lock /platform-darwin/Pods/ -# Media -public/Press/Background.png -public/Press/Front-Page.png -public/Press/MasterPassword_PressKit/MasterPassword_pressrelease_*.pdf - # Gradle build -!/build .gradle local.properties - -# Maven -target -dependency-reduced-pom.xml +/gradle/builds +/platform-android/.externalNativeBuild diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dc9164a8..835c0390 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,7 +5,7 @@ variables: build_project: stage: build script: - - "( brew install libsodium json-c )" + - "( brew bundle )" - "( ./lib/bin/build_libsodium-macos )" - "( cd ./platform-independent/c/cli && ./clean && targets=all ./build && ./mpw-tests && ./mpw-cli-tests )" - "( cd ./gradle && ./gradlew --info clean test )" diff --git a/.gitmodules b/.gitmodules index bba8bfbb..99749761 100644 --- a/.gitmodules +++ b/.gitmodules @@ -24,7 +24,7 @@ url = https://github.com/jedisct1/libsodium.git [submodule "lib/libjson-c"] path = lib/libjson-c - url = https://github.com/lhunath/json-c.git + url = https://github.com/json-c/json-c.git [submodule "public/site"] path = public/site url = https://github.com/Lyndir/MasterPassword.git diff --git a/Brewfile b/Brewfile new file mode 100644 index 00000000..cb16d9ba --- /dev/null +++ b/Brewfile @@ -0,0 +1,5 @@ +brew "libsodium" +brew "json-c" + +brew "automake" +brew "autoconf" diff --git a/gradle/README.md b/gradle/README.md index 8fdb2a38..d2d89327 100644 --- a/gradle/README.md +++ b/gradle/README.md @@ -2,11 +2,11 @@ To build a release distribution: Desktop: - STORE_PW=$(mpw masterpassword.keystore) KEY_PW=$(mpw masterpassword-desktop) gradle clean masterpassword-gui:shadowJar + STORE_PW=$(mpw masterpassword.keystore) KEY_PW_DESKTOP=$(mpw masterpassword-desktop) gradle clean masterpassword-gui:shadowJar Android: - STORE_PW=$(mpw masterpassword.keystore) KEY_PW=$(mpw masterpassword-android) gradle clean masterpassword-android:assembleRelease + STORE_PW=$(mpw masterpassword.keystore) KEY_PW_ANDROID=$(mpw masterpassword-android) gradle clean masterpassword-android:assembleRelease Note: diff --git a/gradle/build.gradle b/gradle/build.gradle index aa8fce5e..d2e843e0 100644 --- a/gradle/build.gradle +++ b/gradle/build.gradle @@ -11,8 +11,8 @@ allprojects { } tasks.withType( FindBugs ) { reports { - xml.enabled false - html.enabled true + xml.enabled = false + html.enabled = true } } } diff --git a/lib/bin/build_lib b/lib/bin/build_lib index efb89206..ad77139d 100755 --- a/lib/bin/build_lib +++ b/lib/bin/build_lib @@ -50,7 +50,7 @@ _initialize() { # # Check if all tools needed for the default implementations are available. # -# By default, this will check for `automake` and `autoreconf`. +# By default, this will check for `automake` (for aclocal) and `autoconf` (for autoreconf). initialize_needs() { _initialize_needs "$@"; } _initialize_needs() { if [[ $platform = windows ]]; then @@ -58,7 +58,7 @@ _initialize_needs() { export VSINSTALLDIR="${VSINSTALLDIR:-$(cd "$(cygpath -F 0x002a)/Microsoft Visual Studio"/*/*/Common7/.. && pwd)}" [[ -e "$VSINSTALLDIR/Common7/Tools/VsMSBuildCmd.bat" ]] || { echo >&2 "Missing: msbuild. Please install 'Build Tools for Visual Studio'."; return 1; } else - needs automake autoreconf + needs automake autoconf fi } diff --git a/lib/bin/build_libjson-c-android b/lib/bin/build_libjson-c-android new file mode 100755 index 00000000..faab07ee --- /dev/null +++ b/lib/bin/build_libjson-c-android @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +source "${BASH_SOURCE%/*}/build_lib" + +autoreconf() { + command autoreconf -Iautoconf-archive/m4 "$@" +} + +build libjson-c android diff --git a/lib/libjson-c b/lib/libjson-c index 3e5ad38a..e3752b58 160000 --- a/lib/libjson-c +++ b/lib/libjson-c @@ -1 +1 @@ -Subproject commit 3e5ad38a83a05b641cd84414a67a64992bf0e8c6 +Subproject commit e3752b58945d23895535857ed136a5185bad02ce diff --git a/platform-android/CMakeLists.txt b/platform-android/CMakeLists.txt new file mode 100644 index 00000000..87dcea9b --- /dev/null +++ b/platform-android/CMakeLists.txt @@ -0,0 +1,24 @@ +project( mpw-core C ) +cmake_minimum_required( VERSION 3.0.0 ) + +add_library( mpw SHARED + "${PROJECT_SOURCE_DIR}/../platform-independent/c/core/src/base64.c" + "${PROJECT_SOURCE_DIR}/../platform-independent/c/core/src/aes.c" + "${PROJECT_SOURCE_DIR}/../platform-independent/c/core/src/mpw-algorithm.c" + "${PROJECT_SOURCE_DIR}/../platform-independent/c/core/src/mpw-types.c" + "${PROJECT_SOURCE_DIR}/../platform-independent/c/core/src/mpw-util.c" + "${PROJECT_SOURCE_DIR}/../platform-independent/c/core/src/mpw-marshal-util.c" + "${PROJECT_SOURCE_DIR}/../platform-independent/c/core/src/mpw-marshal.c" + "${PROJECT_SOURCE_DIR}/../platform-independent/c/core/src/mpw-jni.c" ) + +add_library( sodium SHARED IMPORTED ) +set_target_properties( sodium PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../lib/libsodium/build-android~/out/lib/${ANDROID_ABI}/libsodium.so" ) +target_include_directories( mpw PRIVATE "${PROJECT_SOURCE_DIR}/../lib/libsodium/build-android~/out" ) +target_compile_definitions( mpw PRIVATE -DMPW_SODIUM=1 ) +target_link_libraries( mpw PRIVATE sodium ) + +add_library( json-c SHARED IMPORTED ) +set_target_properties( json-c PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../lib/libjson-c/build-android~/out/lib/${ANDROID_ABI}/libjson-c.so" ) +target_include_directories( mpw PRIVATE "${PROJECT_SOURCE_DIR}/../lib/libjson-c/build-android~/out" ) +target_compile_definitions( mpw PRIVATE -DMPW_JSON=1 ) +target_link_libraries( mpw PRIVATE json-c ) diff --git a/platform-android/build.gradle b/platform-android/build.gradle index b2b84e08..ed491ee3 100644 --- a/platform-android/build.gradle +++ b/platform-android/build.gradle @@ -4,17 +4,27 @@ android { compileSdkVersion 25 buildToolsVersion '27.0.3' - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - defaultConfig { applicationId 'com.lyndir.masterpassword' minSdkVersion 19 targetSdkVersion 25 - versionCode 20501 - versionName '2.5.1' + versionCode 20701 + versionName '2.7.1' + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + externalNativeBuild { + cmake { + path 'CMakeLists.txt' + } + } + sourceSets { + main { + jniLibs.srcDirs "$projectDir/../lib/libsodium/build-android~/out/lib", + "$projectDir/../lib/libjson-c/build-android~/out/lib" + } } // release with: STORE_PW=$(mpw masterpassword.keystore) KEY_PW_ANDROID=$(mpw masterpassword-android) gradle masterpassword-android:assembleRelease @@ -43,3 +53,13 @@ dependencies { implementation group: 'com.jakewharton', name: 'butterknife', version: '8.5.1' annotationProcessor group: 'com.jakewharton', name: 'butterknife-compiler', version: '8.5.1' } + +preBuild { + dependsOn task( type: Exec, 'buildLibSodium', { + commandLine "$projectDir/../lib/bin/build_libsodium-android" + } ) + dependsOn task( type: Exec, 'buildLibJson-c', { + commandLine "$projectDir/../lib/bin/build_libjson-c-android" + } ) +} + diff --git a/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java b/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java index a66a19c7..95386054 100644 --- a/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java +++ b/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java @@ -130,7 +130,7 @@ public class EmergencyActivity extends Activity { siteNameField.addTextChangedListener( new ValueChangedListener() { @Override void update() { - siteCounterButton.setText( MessageFormat.format( "{0}", 1 ) ); + siteCounterButton.setText( MessageFormat.format( "{0}", UnsignedInteger.ONE ) ); updateSitePassword(); } } ); @@ -155,6 +155,17 @@ public class EmergencyActivity extends Activity { updateSitePassword(); } } ); + siteCounterButton.setOnLongClickListener( new View.OnLongClickListener() { + @Override + public boolean onLongClick(final View v) { + if (UnsignedInteger.valueOf( siteCounterButton.getText().toString() ).equals( UnsignedInteger.ONE )) + return false; + + siteCounterButton.setText( MessageFormat.format( "{0}", UnsignedInteger.ONE ) ); + updateSitePassword(); + return true; + } + } ); siteVersionButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(final View v) { @@ -229,7 +240,7 @@ public class EmergencyActivity extends Activity { MPAlgorithm.Version defaultVersion = preferences.getDefaultVersion(); siteVersionButton.setTag( defaultVersion ); siteVersionButton.setText( defaultVersion.name() ); - siteCounterButton.setText( MessageFormat.format( "{0}", 1 ) ); + siteCounterButton.setText( MessageFormat.format( "{0}", UnsignedInteger.ONE ) ); if (TextUtils.isEmpty( fullNameField.getText() )) fullNameField.requestFocus(); diff --git a/platform-independent/c/cli/.gitignore b/platform-independent/c/cli/.gitignore index db7b6106..c883e165 100644 --- a/platform-independent/c/cli/.gitignore +++ b/platform-independent/c/cli/.gitignore @@ -14,3 +14,4 @@ CMakeCache.txt CMakeFiles Makefile cmake_install.cmake +install_manifest.txt diff --git a/platform-independent/c/cli/CMakeLists.txt b/platform-independent/c/cli/CMakeLists.txt index 6eb0e5bb..26df69dd 100644 --- a/platform-independent/c/cli/CMakeLists.txt +++ b/platform-independent/c/cli/CMakeLists.txt @@ -1,5 +1,5 @@ ### CMAKE -project( mpw C ) +project( masterpassword-cli C ) cmake_minimum_required( VERSION 3.0.2 ) @@ -39,8 +39,8 @@ endif() ### DEPENDENCIES function( use_mpw_sodium t r ) if( USE_SODIUM ) - target_link_libraries( "${t}" sodium ) - target_compile_definitions( "${t}" PUBLIC -DMPW_SODIUM=1 ) + target_link_libraries( "${t}" PRIVATE sodium ) + target_compile_definitions( "${t}" PRIVATE -DMPW_SODIUM=1 ) message( STATUS "${t}: USE_SODIUM is enabled." ) elseif( r STREQUAL "required" ) @@ -56,9 +56,9 @@ function( use_mpw_color t ) find_package( Curses ) if( USE_COLOR ) if ( CURSES_FOUND ) - target_include_directories( "${t}" PUBLIC ${CURSES_INCLUDE_DIR} ) - target_link_libraries( "${t}" ${CURSES_LIBRARIES} ) - target_compile_definitions( "${t}" PUBLIC -DMPW_COLOR=1 ${CURSES_DEFINITIONS} ) + target_include_directories( "${t}" PRIVATE ${CURSES_INCLUDE_DIR} ) + target_link_libraries( "${t}" PRIVATE ${CURSES_LIBRARIES} ) + target_compile_definitions( "${t}" PRIVATE -DMPW_COLOR=1 ${CURSES_DEFINITIONS} ) message( STATUS "${t}: USE_COLOR is enabled." ) elseif( r STREQUAL "required" ) @@ -80,8 +80,8 @@ endfunction() function( use_mpw_json t ) if( USE_JSON ) - target_link_libraries( "${t}" json-c ) - target_compile_definitions( "${t}" PUBLIC -DMPW_JSON=1 ) + target_link_libraries( "${t}" PRIVATE json-c ) + target_compile_definitions( "${t}" PRIVATE -DMPW_JSON=1 ) message( STATUS "${t}: USE_JSON is enabled." ) elseif( r STREQUAL "required" ) @@ -97,9 +97,9 @@ function( use_mpw_xml t r ) find_package( LibXml2 ) if( USE_XML ) if ( LIBXML2_FOUND ) - target_include_directories( "${t}" PUBLIC ${LIBXML2_INCLUDE_DIR} ) - target_link_libraries( "${t}" ${LIBXML2_LIBRARIES} ) - target_compile_definitions( "${t}" PUBLIC -DMPW_XML=1 ${LIBXML2_DEFINITIONS} ) + target_include_directories( "${t}" PRIVATE ${LIBXML2_INCLUDE_DIR} ) + target_link_libraries( "${t}" PRIVATE ${LIBXML2_LIBRARIES} ) + target_compile_definitions( "${t}" PRIVATE -DMPW_XML=1 ${LIBXML2_DEFINITIONS} ) message( STATUS "${t}: USE_XML is enabled." ) elseif( r STREQUAL "required" ) diff --git a/platform-independent/c/core/build.gradle b/platform-independent/c/core/build.gradle index c67a8b45..66d01a3f 100644 --- a/platform-independent/c/core/build.gradle +++ b/platform-independent/c/core/build.gradle @@ -21,6 +21,7 @@ artifacts { } library { + baseName.set( "mpw" ) linkage.set( [ Linkage.SHARED ] ) // Reconfigure the toolchain from C++ to C. diff --git a/platform-independent/c/core/src/mpw-jni.c b/platform-independent/c/core/src/mpw-jni.c index 5c936e16..a6ae656b 100644 --- a/platform-independent/c/core/src/mpw-jni.c +++ b/platform-independent/c/core/src/mpw-jni.c @@ -6,10 +6,21 @@ // TODO: We may need to zero the jbytes safely. +JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { + JNIEnv* env; + if ((*vm)->GetEnv( vm, (void **)&env, JNI_VERSION_1_6 ) != JNI_OK) + return -1; + + return JNI_VERSION_1_6; +} + /* native int _masterKey(final String fullName, final byte[] masterPassword, final Version version) */ JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1masterKey(JNIEnv *env, jobject obj, jstring fullName, jbyteArray masterPassword, jint algorithmVersion) { + if (!fullName || !masterPassword) + return NULL; + const char *fullNameString = (*env)->GetStringUTFChars( env, fullName, NULL ); jbyte *masterPasswordString = (*env)->GetByteArrayElements( env, masterPassword, NULL ); @@ -32,6 +43,9 @@ JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__ JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteKey(JNIEnv *env, jobject obj, jbyteArray masterKey, jstring siteName, jlong siteCounter, jint keyPurpose, jstring keyContext, jint algorithmVersion) { + if (!masterKey || !siteName) + return NULL; + jbyte *masterKeyBytes = (*env)->GetByteArrayElements( env, masterKey, NULL ); const char *siteNameString = (*env)->GetStringUTFChars( env, siteName, NULL ); const char *keyContextString = keyContext? (*env)->GetStringUTFChars( env, keyContext, NULL ): NULL; @@ -40,7 +54,8 @@ JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__ (MPKeyPurpose)keyPurpose, keyContextString, (MPAlgorithmVersion)algorithmVersion ); (*env)->ReleaseByteArrayElements( env, masterKey, masterKeyBytes, JNI_ABORT ); (*env)->ReleaseStringUTFChars( env, siteName, siteNameString ); - (*env)->ReleaseStringUTFChars( env, keyContext, keyContextString ); + if (keyContext) + (*env)->ReleaseStringUTFChars( env, keyContext, keyContextString ); if (!siteKeyBytes) return NULL; @@ -59,6 +74,9 @@ JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1si jbyteArray masterKey, jbyteArray siteKey, jstring siteName, jlong siteCounter, jint keyPurpose, jstring keyContext, jint resultType, jstring resultParam, jint algorithmVersion) { + if (!masterKey || !siteKey || !siteName) + return NULL; + jbyte *masterKeyBytes = (*env)->GetByteArrayElements( env, masterKey, NULL ); jbyte *siteKeyBytes = (*env)->GetByteArrayElements( env, siteKey, NULL ); const char *siteNameString = (*env)->GetStringUTFChars( env, siteName, NULL ); @@ -70,8 +88,10 @@ JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1si (*env)->ReleaseByteArrayElements( env, masterKey, masterKeyBytes, JNI_ABORT ); (*env)->ReleaseByteArrayElements( env, siteKey, siteKeyBytes, JNI_ABORT ); (*env)->ReleaseStringUTFChars( env, siteName, siteNameString ); - (*env)->ReleaseStringUTFChars( env, keyContext, keyContextString ); - (*env)->ReleaseStringUTFChars( env, resultParam, resultParamString ); + if (keyContext) + (*env)->ReleaseStringUTFChars( env, keyContext, keyContextString ); + if (resultParam) + (*env)->ReleaseStringUTFChars( env, resultParam, resultParamString ); if (!siteResultString) return NULL; @@ -89,6 +109,9 @@ JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1si jbyteArray masterKey, jbyteArray siteKey, jstring siteName, jlong siteCounter, jint keyPurpose, jstring keyContext, jint resultType, jstring resultParam, jint algorithmVersion) { + if (!masterKey || !siteKey || !siteName || !resultParam) + return NULL; + jbyte *masterKeyBytes = (*env)->GetByteArrayElements( env, masterKey, NULL ); jbyte *siteKeyBytes = (*env)->GetByteArrayElements( env, siteKey, NULL ); const char *siteNameString = (*env)->GetStringUTFChars( env, siteName, NULL ); @@ -100,8 +123,10 @@ JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1si (*env)->ReleaseByteArrayElements( env, masterKey, masterKeyBytes, JNI_ABORT ); (*env)->ReleaseByteArrayElements( env, siteKey, siteKeyBytes, JNI_ABORT ); (*env)->ReleaseStringUTFChars( env, siteName, siteNameString ); - (*env)->ReleaseStringUTFChars( env, keyContext, keyContextString ); - (*env)->ReleaseStringUTFChars( env, resultParam, resultParamString ); + if (keyContextString) + (*env)->ReleaseStringUTFChars( env, keyContext, keyContextString ); + if (resultParam) + (*env)->ReleaseStringUTFChars( env, resultParam, resultParamString ); if (!siteStateString) return NULL; diff --git a/platform-independent/c/core/src/mpw-marshal-util.c b/platform-independent/c/core/src/mpw-marshal-util.c index 17cba9f1..a8c6c12d 100644 --- a/platform-independent/c/core/src/mpw-marshal-util.c +++ b/platform-independent/c/core/src/mpw-marshal-util.c @@ -95,7 +95,7 @@ bool mpw_get_json_boolean( if (!json_value) return defaultValue; - return json_object_get_boolean( json_value ) == TRUE; + return json_object_get_boolean( json_value ) == true; } #endif diff --git a/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV0.java b/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV0.java index eed0cdb9..8cc665c5 100644 --- a/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV0.java +++ b/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/MPAlgorithmV0.java @@ -42,7 +42,7 @@ public class MPAlgorithmV0 extends MPAlgorithm { protected static final int AES_BLOCKSIZE = 128 /* bit */; static { - Native.load( MPAlgorithmV0.class, "masterpassword-core" ); + Native.load( MPAlgorithmV0.class, "mpw" ); } public final Version version = MPAlgorithm.Version.V0; diff --git a/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/Native.java b/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/Native.java index e12ac6d8..1d69f4c8 100644 --- a/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/Native.java +++ b/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/impl/Native.java @@ -40,8 +40,16 @@ public final class Native { @SuppressWarnings({ "HardcodedFileSeparator", "LoadLibraryWithNonConstantString" }) public static void load(final Class context, final String name) { + + // Try to load the library using the native system. + try { + System.loadLibrary( name ); + return; + } catch (@SuppressWarnings("ErrorNotRethrown") final UnsatisfiedLinkError ignored) { + } + + // Try to find and open a stream to the packaged library resource. try { - // Find and open a stream to the packaged library resource. String library = System.mapLibraryName( name ); int libraryDot = library.lastIndexOf( EXTENSION_SEPARATOR ); String libraryName = (libraryDot > 0)? library.substring( 0, libraryDot ): library; diff --git a/public/site b/public/site index 6cde8d9d..979398a3 160000 --- a/public/site +++ b/public/site @@ -1 +1 @@ -Subproject commit 6cde8d9db84310abba0ec5f278862466a51e6a96 +Subproject commit 979398a3259773344412d5d3cf8ef56afc0023ef