├── core
├── .gitignore
├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── cpp
│ │ │ ├── crypto
│ │ │ │ ├── base64.h
│ │ │ │ ├── crypto_wrapper.h
│ │ │ │ ├── crypto_wrapper.cpp
│ │ │ │ ├── base64.cpp
│ │ │ │ ├── aes.h
│ │ │ │ └── aes.cpp
│ │ │ ├── CMakeLists.txt
│ │ │ └── secure-keys.cpp
│ │ └── java
│ │ │ └── com
│ │ │ └── u
│ │ │ └── securekeys
│ │ │ └── SecureEnvironment.java
│ └── test
│ │ └── java
│ │ └── com
│ │ └── u
│ │ └── securekeys
│ │ └── SecureEnvironmentTest.java
├── proguard-rules.pro
└── build.gradle
├── annotation
├── .gitignore
├── build.gradle
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── u
│ │ └── securekeys
│ │ └── annotation
│ │ ├── SecureKeys.java
│ │ └── SecureKey.java
│ └── test
│ └── java
│ └── com
│ └── u
│ └── securekeys
│ └── annotation
│ ├── SecureKeyTest.java
│ └── SecureKeysTest.java
├── processor
├── .gitignore
├── src
│ ├── main
│ │ ├── resources
│ │ │ └── META-INF
│ │ │ │ └── services
│ │ │ │ └── javax.annotation.processing.Processor
│ │ └── java
│ │ │ └── com
│ │ │ └── u
│ │ │ └── securekeys
│ │ │ ├── internal
│ │ │ ├── Restrictions.java
│ │ │ └── Encoder.java
│ │ │ └── SecureKeysProcessor.java
│ └── test
│ │ └── java
│ │ └── com
│ │ └── u
│ │ └── securekeys
│ │ ├── internal
│ │ ├── EncoderTest.java
│ │ └── RestictionsTest.java
│ │ ├── mocks
│ │ └── Mocks.java
│ │ └── SecureKeysProcessorTest.java
└── build.gradle
├── testapp
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ └── styles.xml
│ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ │ └── dimens.xml
│ │ └── layout
│ │ │ └── activity_main.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── u
│ │ └── testapp
│ │ └── MainActivity.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .github
├── PULL_REQUEST_TEMPLATE.md
└── ISSUE_TEMPLATE.md
├── ci
├── install_ndk.sh
├── install_android.sh
└── install_cmake.sh
├── .gitignore
├── CMakeLists.txt
├── dependencies.gradle
├── gradle.properties
├── circle.yml
├── gradlew.bat
├── README.md
└── gradlew
/core/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/annotation/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/processor/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/testapp/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':core', ':testapp', ':processor', ':annotation'
2 |
--------------------------------------------------------------------------------
/core/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor:
--------------------------------------------------------------------------------
1 | com.u.securekeys.SecureKeysProcessor
--------------------------------------------------------------------------------
/testapp/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | testapp
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/k0shk0sh/android-api-SecureKeys/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/core/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | -keep class com.u.securekeys.ProcessedMap { *; }
2 |
3 | -dontnote org.apache.http.**
4 | -dontnote android.net.http.**
--------------------------------------------------------------------------------
/testapp/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/k0shk0sh/android-api-SecureKeys/HEAD/testapp/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/testapp/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/k0shk0sh/android-api-SecureKeys/HEAD/testapp/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/testapp/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/k0shk0sh/android-api-SecureKeys/HEAD/testapp/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/testapp/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/k0shk0sh/android-api-SecureKeys/HEAD/testapp/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/testapp/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/k0shk0sh/android-api-SecureKeys/HEAD/testapp/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Description
2 |
3 | [Complete]
4 |
5 | ## Issues fixed
6 |
7 | - fixes #issue
8 |
9 | ## Dependencies added
10 |
11 | - \
--------------------------------------------------------------------------------
/annotation/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin:'java'
2 |
3 | sourceCompatibility = JavaVersion.VERSION_1_7
4 | targetCompatibility = JavaVersion.VERSION_1_7
5 |
6 | dependencies {
7 | testCompile testing.junit
8 | }
--------------------------------------------------------------------------------
/ci/install_ndk.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [[ ! -e ~/android-ndk-$1 ]]; then
4 | wget http://dl.google.com/android/repository/android-ndk-$1-linux-x86_64.zip
5 | unzip -qq -d ~ android-ndk-$1-linux-x86_64.zip
6 | fi
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | Makefile
3 | CMakeFiles/
4 | **/CMakeFiles/
5 | cmake_install.cmake
6 | CMakeCache.txt
7 | .gradle
8 | /local.properties
9 | .idea/
10 | *.so
11 | *.dylib
12 | .DS_Store
13 | /build
14 | /captures
15 | .externalNativeBuild
16 |
--------------------------------------------------------------------------------
/testapp/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/testapp/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 16 22:50:37 ART 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-all.zip
7 |
--------------------------------------------------------------------------------
/testapp/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/core/src/main/cpp/crypto/base64.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Santiago Aguilera on 3/5/17.
3 | //
4 |
5 | #ifndef SECUREKEYS_BASE64_H
6 | #define SECUREKEYS_BASE64_H
7 |
8 | #include
9 |
10 | static const std::string base64_available_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
11 | "abcdefghijklmnopqrstuvwxyz"
12 | "0123456789+/";
13 |
14 | std::string base64_decode(std::string &encoded_string);
15 |
16 | #endif //SECUREKEYS_BASE64_H
17 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 |
3 | project(secure-keys-root)
4 |
5 | if(ANDROID)
6 | message (STATUS "Android OS Detected")
7 | else()
8 | message (STATUS "Not android.")
9 | find_package(JNI REQUIRED)
10 | endif(ANDROID)
11 |
12 | include_directories(${JNI_INCLUDE_DIRS})
13 |
14 | include(CheckCXXCompilerFlag)
15 | CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
16 |
17 | add_subdirectory(core/src/main/cpp)
--------------------------------------------------------------------------------
/testapp/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/processor/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java'
2 |
3 | sourceCompatibility = JavaVersion.VERSION_1_7
4 | targetCompatibility = JavaVersion.VERSION_1_7
5 |
6 | repositories {
7 | mavenCentral()
8 | }
9 |
10 | dependencies {
11 | compile project(path:':annotation')
12 | compile misc.javapoet
13 |
14 | testCompile testing.junit
15 | testCompile testing.googleTruth
16 | testCompile testing.googleTesting
17 | testCompile files(org.gradle.internal.jvm.Jvm.current().getToolsJar())
18 | }
19 |
--------------------------------------------------------------------------------
/core/src/main/cpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 |
3 | project(secure-keys)
4 |
5 | link_directories(${JNI_LIBRARIES})
6 |
7 | add_library(secure-keys
8 |
9 | SHARED
10 |
11 | secure-keys.cpp
12 | crypto/aes.cpp
13 | crypto/base64.cpp
14 | crypto/crypto_wrapper.cpp
15 |
16 | crypto/aes.h
17 | crypto/base64.h
18 | crypto/crypto_wrapper.h)
19 |
20 | set(CMAKE_CXX_FLAGS "-fPIC")
21 | set_target_properties(secure-keys PROPERTIES CMAKE_SHARED_LINKER_FLAGS "-fPIC")
--------------------------------------------------------------------------------
/ci/install_android.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ ! -d "/usr/local/android-sdk-linux/platforms/android-25" ]; then
4 | echo y | android update sdk --no-ui --all --filter "android-25"
5 | fi
6 |
7 | if [ ! -d "/usr/local/android-sdk-linux/build-tools/25.0.3" ]; then
8 | echo y | android update sdk --no-ui --all --filter "build-tools-25.0.3"
9 | fi
10 |
11 | if [ ! -d "/usr/local/android_sdk/extras/android/m2repository/com/android/support/support-core-utils/25.4.0" ]; then
12 | echo y | android update sdk --no-ui --all --filter "extra-android-m2repository"
13 | fi
14 |
--------------------------------------------------------------------------------
/testapp/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Description
2 |
3 | [FILL THIS OUT: Explain what you did, what you expected to happen, and what actually happens.]
4 |
5 | ### Reproduction
6 |
7 | [FILL THIS OUT: How can we reproduce the bug? Provide URLs to relevant images if possible, or a sample project.]
8 |
9 | ### Solution
10 |
11 | [OPTIONAL: Do you know what needs to be done to address this issue? Ideally, provide a pull request which fixes this issue.]
12 |
13 | ### Additional Information
14 |
15 | * Dependency version: [FILL THIS OUT]
16 | * Dependencies used: [FILL THIS OUT]
17 | * Platform version: [FILL THIS OUT: specific to a particular Android version? Device?]
--------------------------------------------------------------------------------
/dependencies.gradle:
--------------------------------------------------------------------------------
1 | ext {
2 | supportVersion = '25.4.0'
3 | support = [
4 | annotations : "com.android.support:support-annotations:$supportVersion",
5 | appcompat : "com.android.support:appcompat-v7:$supportVersion"
6 | ]
7 |
8 | javapoetVersion = '1.8.0'
9 | misc = [
10 | javapoet : "com.squareup:javapoet:$javapoetVersion"
11 | ]
12 |
13 | junitVersion = '4.4'
14 | googleTesting = '0.11'
15 | googleTruth = '0.33'
16 | testing = [
17 | junit : "junit:junit:$junitVersion",
18 | googleTesting : "com.google.testing.compile:compile-testing:$googleTesting",
19 | googleTruth : "com.google.truth:truth:$googleTruth"
20 | ]
21 | }
--------------------------------------------------------------------------------
/annotation/src/main/java/com/u/securekeys/annotation/SecureKeys.java:
--------------------------------------------------------------------------------
1 | package com.u.securekeys.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Annotation for saving as secure a set of key-value. Add it were you like most.
10 | * Created by saguilera on 3/3/17.
11 | */
12 | @Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
13 | @Retention(RetentionPolicy.SOURCE)
14 | public @interface SecureKeys {
15 |
16 | String CLASSPATH = "com.u.securekeys.annotation.SecureKeys";
17 |
18 | SecureKey[] value();
19 |
20 | }
--------------------------------------------------------------------------------
/annotation/src/main/java/com/u/securekeys/annotation/SecureKey.java:
--------------------------------------------------------------------------------
1 | package com.u.securekeys.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Annotation for saving as secure a key-value. Add it were you like most.
10 | * Created by saguilera on 3/3/17.
11 | */
12 | @Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
13 | @Retention(RetentionPolicy.SOURCE)
14 | public @interface SecureKey {
15 |
16 | String CLASSPATH = "com.u.securekeys.annotation.SecureKey";
17 |
18 | // Key and value are mandatory.
19 | String key();
20 | String value();
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/testapp/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/testapp/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/saguilera/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # So we can test the libs and not the testapp
20 | -keep class com.u.testapp.** { *; }
--------------------------------------------------------------------------------
/core/src/main/cpp/crypto/crypto_wrapper.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Santiago Aguilera on 3/5/17.
3 | //
4 | #ifndef SECUREKEYS_CRYPTO_WRAPPER_H
5 | #define SECUREKEYS_CRYPTO_WRAPPER_H
6 |
7 | #include "jni.h"
8 | #include
9 |
10 | #define CRYPTO_WRAPPER_AES_KEY_SIZE 256
11 |
12 | static unsigned char CRYPTO_WRAPPER_AES_IV[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
13 | 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
14 |
15 | static unsigned char CRYPTO_WRAPPER_AES_KEY[32] = { 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71,
16 | 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35, 0x2c,
17 | 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf,
18 | 0xf4 };
19 |
20 | std::string crypto_wrapper_decode(JNIEnv *env, std::string encoded_string);
21 |
22 | #endif //SECUREKEYS_CRYPTO_WRAPPER_H
23 |
--------------------------------------------------------------------------------
/circle.yml:
--------------------------------------------------------------------------------
1 | machine:
2 | environment:
3 | ANDROID_NDK: $HOME/android-ndk-r14
4 | ANDROID_NDK_HOME: $ANDROID_NDK
5 | PATH: $PATH:$ANDROID_NDK
6 | NDK_VERSION: "r14"
7 | GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"'
8 | java:
9 | version: oraclejdk8
10 |
11 | dependencies:
12 | cache_directories:
13 | - ~/android-ndk-$NDK_VERSION
14 | pre:
15 | - bash "ci/install_ndk.sh" "$NDK_VERSION"
16 | - bash "ci/install_cmake.sh"
17 | - bash "ci/install_android.sh"
18 |
19 | test:
20 | override:
21 | - ./gradlew annotation:build --console=plain
22 | - ./gradlew core:assemble core:lint --console=plain # Cant run tests in CI yet, need to find out why on IDE they pass and from terminal cant find the dylib/so files
23 | - ./gradlew processor:build --console=plain
24 | - ./gradlew testapp:build --console=plain
25 | post:
26 | - if [ -d testapp/build/outputs ]; then cp -r testapp/build/outputs $CIRCLE_ARTIFACTS; fi
27 | - if [ -d annotation/build/reports ]; then cp -r annotation/build/reports $CIRCLE_TEST_REPORTS; fi
28 | - if [ -d core/build/reports ]; then cp -r core/build/reports $CIRCLE_TEST_REPORTS; fi
29 | - if [ -d processor/build/reports ]; then cp -r processor/build/reports $CIRCLE_TEST_REPORTS; fi
--------------------------------------------------------------------------------
/annotation/src/test/java/com/u/securekeys/annotation/SecureKeyTest.java:
--------------------------------------------------------------------------------
1 | package com.u.securekeys.annotation;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Test;
5 |
6 | import static org.junit.Assert.fail;
7 |
8 | /**
9 | * Created by saguilera on 6/16/17.
10 | */
11 | @SecureKey(key = "test_key", value = "test_value")
12 | public class SecureKeyTest {
13 |
14 | /**
15 | * Test we can generate the clss from the fully-qualified name correctly.
16 | * This is crucial since the annotation processor is hooked via the fully-qualified name,
17 | * if its not a correct one, it wont work
18 | */
19 | @Test
20 | public void test_ClasspathCorrectlyGeneratesClass() {
21 | try {
22 | Class> clazz = Class.forName(SecureKey.CLASSPATH);
23 | Assert.assertTrue(clazz.isAnnotation());
24 | Assert.assertEquals(clazz, SecureKey.class);
25 | } catch (Exception ex) {
26 | fail(ex.getMessage());
27 | }
28 | }
29 |
30 | /**
31 | * Test the annotation doesnt get to runtime, if it does then its a major security flaw.
32 | */
33 | @Test
34 | public void test_AnnotationDoesntGetRetainedForRuntime() {
35 | Assert.assertFalse(SecureKeyTest.class.isAnnotationPresent(SecureKey.class));
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/processor/src/test/java/com/u/securekeys/internal/EncoderTest.java:
--------------------------------------------------------------------------------
1 | package com.u.securekeys.internal;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Test;
5 |
6 | /**
7 | * Created by saguilera on 6/18/17.
8 | */
9 |
10 | public class EncoderTest {
11 |
12 | private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
13 |
14 | private static String bytesToHex(byte[] bytes) {
15 | char[] hexChars = new char[bytes.length * 2];
16 | for ( int j = 0; j < bytes.length; j++ ) {
17 | int v = bytes[j] & 0xFF;
18 | hexChars[j * 2] = hexArray[v >>> 4];
19 | hexChars[j * 2 + 1] = hexArray[v & 0x0F];
20 | }
21 | return new String(hexChars);
22 | }
23 |
24 | @Test
25 | public void test_EncodeAES() {
26 | // Test array of bytes to crypt
27 | byte[] arr = {0x01, 0x02, 0x03};
28 |
29 | Encoder encoder = new Encoder();
30 | byte[] aesarr = encoder.aes(arr);
31 |
32 | Assert.assertEquals("F81B0C071652901E54BA6781CED9589D", bytesToHex(aesarr));
33 | }
34 |
35 | @Test
36 | public void test_FullCrypt() {
37 | // Test array of bytes to crypt
38 | String value = "test_message";
39 |
40 | Encoder encoder = new Encoder();
41 | String encodedValue = encoder.encode(value);
42 |
43 | Assert.assertEquals("MPRSyn8EtW48RrsKM8WFwg==", encodedValue);
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/annotation/src/test/java/com/u/securekeys/annotation/SecureKeysTest.java:
--------------------------------------------------------------------------------
1 | package com.u.securekeys.annotation;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Test;
5 |
6 | import static org.junit.Assert.fail;
7 |
8 | /**
9 | * Created by saguilera on 6/16/17.
10 | */
11 | @SecureKeys({
12 | @SecureKey(key = "test_key_1", value = "test_key_1"),
13 | @SecureKey(key = "test_key_2", value = "test_key_2"),
14 | @SecureKey(key = "test_key_3", value = "test_key_3")
15 | })
16 | public class SecureKeysTest {
17 |
18 | /**
19 | * Test we can generate the clss from the fully-qualified name correctly.
20 | * This is crucial since the annotation processor is hooked via the fully-qualified name,
21 | * if its not a correct one, it wont work
22 | */
23 | @Test
24 | public void test_ClasspathCorrectlyGeneratesClass() {
25 | try {
26 | Class> clazz = Class.forName(SecureKeys.CLASSPATH);
27 | Assert.assertTrue(clazz.isAnnotation());
28 | Assert.assertEquals(clazz, SecureKeys.class);
29 | } catch (Exception ex) {
30 | fail(ex.getMessage());
31 | }
32 | }
33 |
34 | /**
35 | * Test the annotation doesnt get to runtime, if it does then its a major security flaw.
36 | */
37 | @Test
38 | public void test_AnnotationDoesntGetRetainedForRuntime() {
39 | Assert.assertFalse(SecureKeysTest.class.isAnnotationPresent(SecureKeys.class));
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/testapp/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin:'com.android.application'
2 | apply plugin: 'com.neenbedankt.android-apt'
3 |
4 | android {
5 | compileSdkVersion 25
6 | buildToolsVersion "25.0.3"
7 |
8 | defaultConfig {
9 | applicationId "com.u.testapp"
10 | minSdkVersion 14
11 | targetSdkVersion 25
12 | versionCode 1
13 | versionName "1.0"
14 | }
15 | buildTypes {
16 | debug {
17 | minifyEnabled false
18 | shrinkResources false
19 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
20 |
21 | // Dont put here sensible data, the attacker can see it unencrypted in the BuildConfig file later!!
22 | // If your app has Proguard (and you do remove the BuildConfig fields), then its no problem at all.
23 | buildConfigField "String", "TESTING_VALUE_1", "\"5000\""
24 | buildConfigField "String", "TESTING_VALUE_2", "\"3.1415\""
25 | }
26 | release {
27 | minifyEnabled true
28 | shrinkResources true
29 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
30 |
31 | // Dont put here sensible data, the attacker can see it unencrypted in the BuildConfig file later!!
32 | // If your app has Proguard (and you do remove the BuildConfig fields), then its no problem at all.
33 | buildConfigField "String", "TESTING_VALUE_1", "\"5000\""
34 | buildConfigField "String", "TESTING_VALUE_2", "\"3.1415\""
35 | }
36 | }
37 | }
38 |
39 | dependencies {
40 | compile support.appcompat
41 |
42 | compile project(path:':core')
43 | apt project(path:':processor')
44 | }
45 |
--------------------------------------------------------------------------------
/core/src/main/cpp/crypto/crypto_wrapper.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Santiago Aguilera on 3/5/17.
3 | //
4 | #include "base64.h"
5 | #include "aes.h"
6 | #include
7 | #include "crypto_wrapper.h"
8 |
9 | std::string crypto_wrapper_decode(JNIEnv *env, std::string encoded_string) {
10 | std::string base64_decode_string = base64_decode(encoded_string);
11 |
12 | // Prepare data for aes
13 | unsigned int len = base64_decode_string.length();
14 | unsigned int src_len = len;
15 |
16 | // Copy data input to ashmem buffer
17 | char *data = &base64_decode_string[0];
18 | unsigned char *input = (unsigned char *) malloc(src_len);
19 | memset(input, 0, src_len);
20 | memcpy(input, data, len);
21 |
22 | unsigned char * buff = (unsigned char*) malloc(src_len);
23 | if (!buff) {
24 | free(input);
25 | throw "Couldnt assign memmory for buffer inside decode";
26 | }
27 | memset(buff, src_len, 0);
28 |
29 | // Set key and iv
30 | unsigned int key_schedule[AES_BLOCK_SIZE * 4] = { 0 };
31 | aes_key_setup(CRYPTO_WRAPPER_AES_KEY, key_schedule, CRYPTO_WRAPPER_AES_KEY_SIZE);
32 |
33 | // Decrypt
34 | aes_decrypt_cbc(input, src_len, buff, key_schedule, CRYPTO_WRAPPER_AES_KEY_SIZE, CRYPTO_WRAPPER_AES_IV);
35 |
36 | // Read padding assigned from last byte, remove it if exist.
37 | unsigned char *ptr = buff;
38 | ptr += (src_len - 1);
39 | unsigned int padding_len = (unsigned int) *ptr;
40 | if (padding_len > 0 && padding_len <= AES_BLOCK_SIZE) {
41 | src_len -= padding_len;
42 | }
43 |
44 | // Interpret it as a string
45 | std::string result(reinterpret_cast(buff), src_len);
46 |
47 | // Release ashmem
48 | free(input);
49 | free(buff);
50 |
51 | return result;
52 | }
--------------------------------------------------------------------------------
/core/src/main/cpp/crypto/base64.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Santiago Aguilera on 3/5/17.
3 | //
4 | #include "base64.h"
5 |
6 | static inline bool is_decodable(unsigned char c) {
7 | return (isalnum(c) || (c == '+') || (c == '/'));
8 | }
9 |
10 | std::string base64_decode(std::string &encoded_string) {
11 | int in_len = encoded_string.size();
12 | int i = 0;
13 | int j = 0;
14 | int in_ = 0;
15 | unsigned char char_array_4[4], char_array_3[3];
16 | std::string ret;
17 |
18 | while (in_len-- && (encoded_string[in_] != '=') && is_decodable(encoded_string[in_])) {
19 | char_array_4[i++] = encoded_string[in_]; in_++;
20 | if (i ==4) {
21 | for (i = 0; i <4; i++)
22 | char_array_4[i] = base64_available_chars.find(char_array_4[i]);
23 |
24 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
25 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
26 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
27 |
28 | for (i = 0; (i < 3); i++)
29 | ret += char_array_3[i];
30 | i = 0;
31 | }
32 | }
33 |
34 | if (i) {
35 | for (j = i; j <4; j++)
36 | char_array_4[j] = 0;
37 |
38 | for (j = 0; j <4; j++)
39 | char_array_4[j] = base64_available_chars.find(char_array_4[j]);
40 |
41 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
42 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
43 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
44 |
45 | for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
46 | }
47 |
48 | return ret;
49 | }
--------------------------------------------------------------------------------
/testapp/src/main/java/com/u/testapp/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.u.testapp;
2 |
3 | import android.support.v7.app.AppCompatActivity;
4 | import android.os.Bundle;
5 | import android.widget.TextView;
6 | import com.u.securekeys.SecureEnvironment;
7 | import com.u.securekeys.annotation.SecureKey;
8 | import com.u.securekeys.annotation.SecureKeys;
9 | import junit.framework.Assert;
10 |
11 | @SecureKeys({
12 | @SecureKey(key = "a", value = "e"),
13 | @SecureKey(key = "b", value = "f"),
14 | @SecureKey(key = "c", value = "g"),
15 | @SecureKey(key = "d", value = "h"),
16 | @SecureKey(key = "long_from_BuildConfig", value = BuildConfig.TESTING_VALUE_1),
17 | @SecureKey(key = "double_from_BuildConfig", value = BuildConfig.TESTING_VALUE_2)
18 | })
19 | public class MainActivity extends AppCompatActivity {
20 |
21 | @Override
22 | @SecureKey(key = "client-secret", value = "aD98E2GEk23TReYds9Zs9zdSdDBi23EAsdq29fXkpsDwp0W+h")
23 | protected void onCreate(Bundle savedInstanceState) {
24 | super.onCreate(savedInstanceState);
25 | setContentView(R.layout.activity_main);
26 |
27 | Assert.assertEquals("aD98E2GEk23TReYds9Zs9zdSdDBi23EAsdq29fXkpsDwp0W+h", SecureEnvironment.getString("client-secret"));
28 | Assert.assertEquals("e", SecureEnvironment.getString("a"));
29 | Assert.assertEquals("f", SecureEnvironment.getString("b"));
30 | Assert.assertEquals("g", SecureEnvironment.getString("c"));
31 | Assert.assertEquals("h", SecureEnvironment.getString("d"));
32 | Assert.assertEquals(Long.valueOf(BuildConfig.TESTING_VALUE_1), Long.valueOf(SecureEnvironment.getLong("long_from_BuildConfig")));
33 | Assert.assertEquals(Double.valueOf(BuildConfig.TESTING_VALUE_2), SecureEnvironment.getDouble("double_from_BuildConfig"));
34 |
35 | ((TextView) findViewById(R.id.activity_main_key)).setText(SecureEnvironment.getString("client-secret"));
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/processor/src/test/java/com/u/securekeys/internal/RestictionsTest.java:
--------------------------------------------------------------------------------
1 | package com.u.securekeys.internal;
2 |
3 | import java.security.InvalidKeyException;
4 | import javax.crypto.Cipher;
5 | import javax.crypto.spec.IvParameterSpec;
6 | import javax.crypto.spec.SecretKeySpec;
7 | import org.junit.Assert;
8 | import org.junit.Test;
9 |
10 | /**
11 | * Created by saguilera on 6/18/17.
12 | */
13 |
14 | public class RestictionsTest {
15 |
16 | // Initial vector for AES cipher
17 | private static final byte initialVectorBytes[] = new byte[] { 0x04, 0x06, 0x02, 0x01, 0x01,
18 | 0x05, 0x06, 0x0f, 0x0a, 0x09, 0x03, 0x03, 0x03, 0x04, 0x02, 0x0a };
19 |
20 | // Key used for AES cypher
21 | private static final byte keyBytes[] = new byte[] { 0x60, 0x3d, (byte) 0xe5,
22 | 0x1a, 0x5c, (byte) 0x1a, 0x11, (byte) 0xde, 0x2b, 0x74,
23 | (byte) 0xae, (byte) 0xf0, (byte) 0x8a, 0x7d, 0x77, (byte) 0x83,
24 | 0x13, 0x3d, 0x2c, 0x07, 0x3b, 0x61, 0x08, (byte) 0xda, 0x22,
25 | (byte) 0x9a, 0x11, (byte) 0xa3, 0x09, 0x14, (byte) 0xdf,
26 | (byte) 0xf1 };
27 |
28 | @Test
29 | public void test_RestrictionsAreRemoved() {
30 | boolean removed = Restrictions.remove();
31 |
32 | Assert.assertTrue(removed);
33 |
34 | try {
35 | SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
36 |
37 | Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
38 | final IvParameterSpec iv = new IvParameterSpec(initialVectorBytes);
39 |
40 | cipher.init(Cipher.ENCRYPT_MODE, key, iv);
41 | byte[] data = cipher.doFinal("test_message".getBytes());
42 |
43 | Assert.assertNotNull(data);
44 | Assert.assertTrue(data.length > 0);
45 | } catch (InvalidKeyException ex) {
46 | Assert.fail("Shouldnt reach here.");
47 | } catch (Exception ex) {
48 | Assert.fail("Shouldnt reach here.");
49 | }
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/processor/src/main/java/com/u/securekeys/internal/Restrictions.java:
--------------------------------------------------------------------------------
1 | package com.u.securekeys.internal;
2 |
3 | import java.lang.reflect.Field;
4 | import java.lang.reflect.Modifier;
5 | import java.security.Permission;
6 | import java.security.PermissionCollection;
7 | import java.util.Map;
8 |
9 | /**
10 | * Removes cryptographics restrictions imposed by java jce. You should have it installed in your
11 | * jre/lib/security. But if not (for travis and other CI's running) we disable them.
12 | *
13 | * Created by saguilera on 3/4/17.
14 | */
15 | class Restrictions {
16 |
17 | private static boolean removed = false;
18 |
19 | public static boolean remove() {
20 | if (removed) return true;
21 |
22 | try {
23 | final Class> jceSecurity = Class.forName("javax.crypto.JceSecurity");
24 | final Class> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
25 | final Class> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");
26 |
27 | final Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
28 | isRestrictedField.setAccessible(true);
29 | final Field modifiersField = Field.class.getDeclaredField("modifiers");
30 | modifiersField.setAccessible(true);
31 | modifiersField.setInt(isRestrictedField, isRestrictedField.getModifiers() & ~Modifier.FINAL);
32 | isRestrictedField.set(null, false);
33 |
34 | final Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
35 | defaultPolicyField.setAccessible(true);
36 | final PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);
37 |
38 | final Field perms = cryptoPermissions.getDeclaredField("perms");
39 | perms.setAccessible(true);
40 | ((Map, ?>) perms.get(defaultPolicy)).clear();
41 |
42 | final Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
43 | instance.setAccessible(true);
44 | defaultPolicy.add((Permission) instance.get(null));
45 |
46 | return removed = true;
47 | } catch (final Exception e) {
48 | return false;
49 | }
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/processor/src/test/java/com/u/securekeys/mocks/Mocks.java:
--------------------------------------------------------------------------------
1 | package com.u.securekeys.mocks;
2 |
3 | import com.u.securekeys.annotation.SecureKey;
4 | import com.u.securekeys.annotation.SecureKeys;
5 |
6 | /**
7 | * Created by saguilera on 6/18/17.
8 | */
9 | public class Mocks {
10 |
11 | /**
12 | * MOCK AN EMPTY CLASS
13 | */
14 |
15 | public static final String MOCK_EMPTY = "final class EmptyClass {}";
16 |
17 | /**
18 | * MOCK A CLASS WITH A SINGLE SECURE_KEY
19 | */
20 |
21 | public static final String MOCK_SECURE_KEY = "" +
22 | "@com.u.securekeys.annotation.SecureKey(key = \"key\", value = \"value\")\n" +
23 | "final class SingleKeyClass {}";
24 | public static final String MOCK_SECURE_KEY_GEN_FILE = "// Method that retrieves the mapping of the values\n" +
25 | "package com.u.securekeys;\n" +
26 | "\n" +
27 | "import java.lang.String;\n" +
28 | "\n" +
29 | "final class ProcessedMap {\n" +
30 | " public static final String[] retrieve() {\n" +
31 | " String array[] = new String[1];\n" +
32 | " array[0] = \"O0TenKit32Pp/dEAQ9RS6g==;;;;2/lhAK3rkMJXwau5KsBvEA==\";\n" +
33 | " return array;\n" +
34 | " }\n" +
35 | "}";
36 |
37 | /**
38 | * MOCK A CLASS WITH MORE THAN ONE SECURE_KEY
39 | */
40 |
41 | public static final String MOCK_SECURE_KEY_MULTIPLE = "" +
42 | " @com.u.securekeys.annotation.SecureKey(key = \"key\", value = \"value\")\n" +
43 | " final class MultipleKeyClass {\n" +
44 | " @com.u.securekeys.annotation.SecureKey(key = \"another\", value = \"anothervalue\")\n" +
45 | " private int field;\n" +
46 | " }";
47 | public static final String MOCK_SECURE_KEY_MULTIPLE_GEN_FILE = "// Method that retrieves the mapping of the values\n" +
48 | "package com.u.securekeys;\n" +
49 | "\n" +
50 | "import java.lang.String;\n" +
51 | "\n" +
52 | "final class ProcessedMap {\n" +
53 | " public static final String[] retrieve() {\n" +
54 | " String array[] = new String[2];\n" +
55 | " array[0] = \"O0TenKit32Pp/dEAQ9RS6g==;;;;2/lhAK3rkMJXwau5KsBvEA==\";\n" +
56 | " array[1] = \"xGNS15pgulUZCvUQRntc5w==;;;;J2jHPSzh8VORCgND0L9A5g==\";\n" +
57 | " return array;\n" +
58 | " }\n" +
59 | "}";
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/processor/src/main/java/com/u/securekeys/internal/Encoder.java:
--------------------------------------------------------------------------------
1 | package com.u.securekeys.internal;
2 |
3 | import java.security.InvalidKeyException;
4 | import javax.crypto.Cipher;
5 | import javax.crypto.spec.IvParameterSpec;
6 | import javax.crypto.spec.SecretKeySpec;
7 | import javax.xml.bind.DatatypeConverter;
8 |
9 | /**
10 | * Class to encode strings with a given key
11 | * Created by saguilera on 3/3/17.
12 | */
13 | public class Encoder {
14 |
15 | // Initial vector for AES cipher
16 | private static final byte initialVectorBytes[] = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04,
17 | 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
18 |
19 | // Key used for AES cypher
20 | private static final byte keyBytes[] = new byte[] { 0x60, 0x3d, (byte) 0xeb,
21 | 0x10, 0x15, (byte) 0xca, 0x71, (byte) 0xbe, 0x2b, 0x73,
22 | (byte) 0xae, (byte) 0xf0, (byte) 0x85, 0x7d, 0x77, (byte) 0x81,
23 | 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, (byte) 0xd7, 0x2d,
24 | (byte) 0x98, 0x10, (byte) 0xa3, 0x09, 0x14, (byte) 0xdf,
25 | (byte) 0xf4 };
26 |
27 | public Encoder() {}
28 |
29 | public String encode(String value) {
30 | try {
31 | return DatatypeConverter.printBase64Binary(aes(value.getBytes()));
32 | } catch (Exception e) {
33 | e.printStackTrace();
34 | throw new RuntimeException("Couldnt encode value: " + value, e);
35 | }
36 | }
37 |
38 | byte[] aes(byte[] content) {
39 | try {
40 | SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
41 |
42 | Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
43 | final IvParameterSpec iv = new IvParameterSpec(initialVectorBytes);
44 |
45 | cipher.init(Cipher.ENCRYPT_MODE, key, iv);
46 | return cipher.doFinal(content);
47 | } catch (InvalidKeyException e) {
48 | System.out.println("Please install JCE's Unlimited Strength Policies for next compilation");
49 | if (Restrictions.remove())
50 | return aes(content);
51 | else throw new RuntimeException("No JCE's policies installed + couldnt bypass them", e);
52 | } catch (Exception e) {
53 | e.printStackTrace();
54 | throw new RuntimeException("Unknown exception while trying to encript with aes", e);
55 | }
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/core/src/main/cpp/crypto/aes.h:
--------------------------------------------------------------------------------
1 | /*********************************************************************
2 | * Filename: aes.h
3 | * Author: Brad Conte (brad AT bradconte.com)
4 | * Copyright:
5 | * Disclaimer: This code is presented "as is" without any guarantees.
6 | * Details: Defines the API for the corresponding AES implementation.
7 | *
8 | * Edited by: Santiago Aguilera
9 | *********************************************************************/
10 |
11 | #ifndef SECUREKEYS_AES_H
12 | #define SECUREKEYS_AES_H
13 |
14 | /*************************** HEADER FILES ***************************/
15 | #include
16 | #include
17 |
18 | /****************************** MACROS ******************************/
19 | #define AES_BLOCK_SIZE 16 // AES operates on 16 bytes at a time
20 |
21 | /**************************** DATA TYPES ****************************/
22 | typedef unsigned char BYTE; // 8-bit byte
23 | typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
24 |
25 | /*********************** FUNCTION DECLARATIONS **********************/
26 | ///////////////////
27 | // AES
28 | ///////////////////
29 | // Key setup must be done before any AES decryption function can be used.
30 | void aes_key_setup(const BYTE key[], // The key, must be 128, 192, or 256 bits
31 | WORD w[], // Output key schedule to be used later
32 | int keysize); // Bit length of the key, 128, 192, or 256
33 |
34 | void aes_decrypt(const BYTE in[], // 16 bytes of ciphertext
35 | BYTE out[], // 16 bytes of plaintext
36 | const WORD key[], // From the key setup
37 | int keysize); // Bit length of the key, 128, 192, or 256
38 |
39 | ///////////////////
40 | // AES - CBC
41 | ///////////////////
42 | int aes_decrypt_cbc(const BYTE in[], // Plaintext
43 | size_t in_len, // Must be a multiple of AES_BLOCK_SIZE
44 | BYTE out[], // Ciphertext, same length as plaintext!
45 | const WORD key[], // From the key setup
46 | int keysize, // Bit length of the key, 128, 192 or 256
47 | const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long
48 |
49 | #endif //SECUREKEYS_AES_H
50 |
--------------------------------------------------------------------------------
/core/src/main/java/com/u/securekeys/SecureEnvironment.java:
--------------------------------------------------------------------------------
1 | package com.u.securekeys;
2 |
3 | import android.support.annotation.Keep;
4 | import android.support.annotation.NonNull;
5 | import java.lang.reflect.Method;
6 |
7 | /**
8 | * Bridge between native and java for accessing secure keys.
9 | *
10 | * Created by saguilera on 3/3/17.
11 | */
12 | public final class SecureEnvironment {
13 |
14 | private static final String ENV_LIBRARY_NAME = "secure-keys";
15 | static final String ENV_PROCESSED_MAP_NAME = "com.u.securekeys.ProcessedMap";
16 | static final String ENV_PROCESSED_MAP_METHOD = "retrieve";
17 |
18 | private static final long NAN_LONG = -1;
19 | private static final String NAN_STRING = "";
20 |
21 | private static boolean initialized;
22 |
23 | static {
24 | System.loadLibrary(ENV_LIBRARY_NAME);
25 |
26 | try {
27 | tryNativeInit();
28 | initialized = true;
29 | } catch (Exception e) {
30 | initialized = false;
31 | }
32 | }
33 |
34 | private SecureEnvironment() throws IllegalAccessException {
35 | throw new IllegalAccessException("This object cant be instantiated");
36 | }
37 |
38 | @Keep
39 | private static void tryNativeInit() throws Exception {
40 | Class> clazz = Class.forName(ENV_PROCESSED_MAP_NAME);
41 | Method method = clazz.getDeclaredMethod(ENV_PROCESSED_MAP_METHOD);
42 | method.setAccessible(true);
43 | nativeInit((String[]) method.invoke(null));
44 | }
45 |
46 | public static @NonNull String getString(@NonNull String key) {
47 | if (!initialized || key.isEmpty()) {
48 | return NAN_STRING;
49 | }
50 |
51 | return nativeGetString(key);
52 | }
53 |
54 | public static long getLong(@NonNull String key) {
55 | String value = getString(key);
56 |
57 | if (!initialized || value.isEmpty()) {
58 | return NAN_LONG;
59 | }
60 |
61 | return Long.valueOf(value);
62 | }
63 |
64 | public static double getDouble(@NonNull String key) {
65 | String value = getString(key);
66 |
67 | if (!initialized || value.isEmpty()) {
68 | return NAN_LONG;
69 | }
70 |
71 | return Double.valueOf(value);
72 | }
73 |
74 | @Keep
75 | private static native String nativeGetString(String key);
76 | @Keep
77 | private static native void nativeInit(String[] array);
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/core/src/main/cpp/secure-keys.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 | #include