├── Android-PRNG.png ├── AndroidManifest.xml ├── README.md ├── ant.properties ├── build.xml ├── clean.sh ├── jni ├── Android.mk ├── Application.mk ├── cleanup.h ├── libprng.cpp └── libprng.h ├── lint.xml ├── proguard.txt ├── project.properties ├── res ├── drawable-hdpi │ └── ic_launcher.png ├── drawable-ldpi │ └── ic_launcher.png ├── drawable-mdpi │ └── ic_launcher.png ├── layout │ └── main.xml └── values │ └── strings.xml └── src └── com └── cryptopp └── prng ├── MainActivity.java └── PRNG.java /Android-PRNG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noloader/Android-PRNG/4dd19eb7486e985d71f1a6ad3a2e33505e66dfbe/Android-PRNG.png -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Android-PRNG is a sample Android NDK project to demonstrate two topics. First, it shows you how to compile a shared object using the Crypto++ library on Android. Second, it shows you how to sample sensors to accumulate seed data to use with a software based random number generator. 2 | 3 | The project requires the Crypto++ library built for Android with architectures armeabi-v7a, arm64-v8a, x64 and x86_64. The project looks for them in the following locations based on architecture: 4 | 5 | * /usr/local/cryptopp/android-armeabi-v7a 6 | * /usr/local/cryptopp/android-arm64-v8a 7 | * /usr/local/cryptopp/android-x86 8 | * /usr/local/cryptopp/android-x86_64 9 | 10 | You can create the prerequisites by repeatedly building and installing the Crypto++ library. The steps for the task are: 11 | 12 | ```bash 13 | git clone https://github.com/weidai11/cryptopp.git 14 | cd cryptopp 15 | cp -p TestScripts/setenv-android.sh . 16 | source ./setenv-android.sh armeabi-v7a 17 | 18 | make -f GNUmakefile-cross distclean 19 | make -f GNUmakefile-cross static dynamic 20 | sudo make install PREFIX=/usr/local/cryptopp/android-armeabi-v7a 21 | ``` 22 | 23 | Lather, rinse, and repeat for each architecture. 24 | 25 | Once you have the libraries installed, use `ndk-build` to build the library: 26 | 27 | ```bash 28 | cd Android-PRNG 29 | ndk-build 30 | ``` 31 | 32 | After the native libraries are built, use `ant` to build the APK and install it on a device: 33 | 34 | ```bash 35 | ant debug install 36 | ``` 37 | 38 | Once installed, you should find it in the App Launcher. 39 | 40 | ### References 41 | 42 | The following references from the Crypto++ wiki should be helpful. 43 | 44 | * http://www.cryptopp.com/wiki/Android_(Command_Line) 45 | * http://www.cryptopp.com/wiki/Android.mk_(Command_Line) 46 | * http://www.cryptopp.com/wiki/Android_Activity 47 | * http://www.cryptopp.com/wiki/Wrapper_DLL 48 | -------------------------------------------------------------------------------- /ant.properties: -------------------------------------------------------------------------------- 1 | # This file is used to override default values used by the Ant build system. 2 | # 3 | # This file must be checked into Version Control Systems, as it is 4 | # integral to the build system of your project. 5 | 6 | # This file is only used by the Ant script. 7 | 8 | # You can use this to override default values such as 9 | # 'source.dir' for the location of your java source folder and 10 | # 'out.dir' for the location of your output folder. 11 | 12 | # You can also use it define how the release builds are signed by declaring 13 | # the following properties: 14 | # 'key.store' for the location of your keystore and 15 | # 'key.alias' for the name of the key to use. 16 | # The password will be asked during the build when you use the 'release' target. 17 | 18 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 13 | 14 | 34 | 35 | 36 | 37 | 43 | 44 | 45 | 46 | 49 | 50 | 51 | 52 | 53 | 64 | 65 | 66 | 67 | 68 | 69 | 72 | 73 | 85 | 86 | 89 | 90 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | find . -name '.DS_Store*' -exec rm -rf {} \; 4 | rm -rf ./bin/ 5 | rm -rf ./gen/ 6 | rm -rf ./libs/ 7 | rm -rf ./obj/ 8 | -------------------------------------------------------------------------------- /jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | # NDK_DEBUG_IMPORTS := 1 4 | 5 | ######################################################### 6 | # Crypto++ library 7 | include $(CLEAR_VARS) 8 | 9 | CRYPTOPP_INCL := /usr/local/cryptopp/android-$(TARGET_ARCH_ABI)/include 10 | CRYPTOPP_LIB := /usr/local/cryptopp/android-$(TARGET_ARCH_ABI)/lib 11 | 12 | LOCAL_MODULE := cryptopp 13 | LOCAL_SRC_FILES := $(CRYPTOPP_LIB)/libcryptopp_static.a 14 | LOCAL_CPP_FEATURES := rtti exceptions 15 | 16 | LOCAL_EXPORT_C_INCLUDES := $(CRYPTOPP_INCL) $(CRYPTOPP_INCL)/cryptopp 17 | 18 | include $(PREBUILT_SHARED_LIBRARY) 19 | 20 | LOCAL_SHARED_LIBRARIES := cryptopp 21 | 22 | ######################################################### 23 | # PRNG library 24 | include $(CLEAR_VARS) 25 | 26 | LOCAL_MODULE := prng 27 | LOCAL_SRC_FILES := prng.cpp 28 | LOCAL_CPPFLAGS := -Wall -fvisibility=hidden 29 | LOCAL_CPP_FEATURES := rtti exceptions 30 | LOCAL_LDFLAGS := -Wl,--exclude-libs,ALL -Wl,--as-needed 31 | 32 | # Configure for release unless NDK_DEBUG=1 33 | ifeq ($(NDK_DEBUG),1) 34 | LOCAL_CPPFLAGS := $(LOCAL_CPPFLAGS) -DDEBUG 35 | else 36 | LOCAL_CPPFLAGS := $(LOCAL_CPPFLAGS) -DNDEBUG 37 | endif 38 | 39 | LOCAL_EXPORT_CPPFLAGS := $(LOCAL_CPPFLAGS) 40 | LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/.. 41 | LOCAL_EXPORT_LDFLAGS := -Wl,--gc-sections 42 | 43 | LOCAL_STATIC_LIBRARIES := cryptopp_static 44 | 45 | include $(BUILD_SHARED_LIBRARY) 46 | -------------------------------------------------------------------------------- /jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi armeabi-v7a armeabi-v7a-hard arm64-v8a mips mips64 x86 x86_64 2 | -------------------------------------------------------------------------------- /jni/cleanup.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* Header for class com_deltoid_prng_cleanup */ 4 | 5 | #ifndef _Included_com_deltoid_prng_cleanup 6 | #define _Included_com_deltoid_prng_cleanup 7 | 8 | class ReadByteBuffer 9 | { 10 | public: 11 | explicit ReadByteBuffer(JNIEnv*& env, jbyteArray& barr) 12 | : m_env(env), m_arr(barr), m_ptr(NULL), m_len(0) 13 | { 14 | if(m_env && m_arr) 15 | { 16 | m_ptr = m_env->GetByteArrayElements(m_arr, NULL); 17 | m_len = m_env->GetArrayLength(m_arr); 18 | } 19 | } 20 | 21 | ~ReadByteBuffer() 22 | { 23 | if(m_env && m_arr) 24 | { 25 | m_env->ReleaseByteArrayElements(m_arr, m_ptr, JNI_ABORT); 26 | } 27 | } 28 | 29 | const byte* GetByteArray() const { 30 | return (const byte*) m_ptr; 31 | } 32 | 33 | size_t GetArrayLen() const { 34 | if(m_len < 0) 35 | return 0; 36 | return (size_t) m_len; 37 | } 38 | 39 | private: 40 | JNIEnv*& m_env; 41 | jbyteArray& m_arr; 42 | 43 | jbyte* m_ptr; 44 | jint m_len; 45 | }; 46 | 47 | class WriteByteBuffer 48 | { 49 | public: 50 | explicit WriteByteBuffer(JNIEnv*& env, jbyteArray& barr) 51 | : m_env(env), m_arr(barr), m_ptr(NULL), m_len(0) 52 | { 53 | if(m_env && m_arr) 54 | { 55 | m_ptr = m_env->GetByteArrayElements(m_arr, NULL); 56 | m_len = m_env->GetArrayLength(m_arr); 57 | } 58 | } 59 | 60 | ~WriteByteBuffer() 61 | { 62 | if(m_env && m_arr) 63 | { 64 | m_env->ReleaseByteArrayElements(m_arr, m_ptr, 0); 65 | } 66 | } 67 | 68 | byte* GetByteArray() const { 69 | return (byte*) m_ptr; 70 | } 71 | 72 | size_t GetArrayLen() const { 73 | if(m_len < 0) 74 | return 0; 75 | return (size_t) m_len; 76 | } 77 | 78 | private: 79 | JNIEnv*& m_env; 80 | jbyteArray& m_arr; 81 | 82 | jbyte* m_ptr; 83 | jint m_len; 84 | }; 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /jni/libprng.cpp: -------------------------------------------------------------------------------- 1 | // Debug settings in this block 2 | #if 1 3 | # undef NDEBUG 4 | # undef DEBUG 5 | # undef NDK_DEBUG 6 | 7 | # define DEBUG 1 8 | # define NDK_DEBUG 1 9 | #endif 10 | 11 | // Release settings in this block 12 | #if 0 13 | # undef NDEBUG 14 | # undef DEBUG 15 | # undef NDK_DEBUG 16 | 17 | # define NDEBUG 1 18 | #endif 19 | 20 | //#if defined(DEBUG) || defined(NDK_DEBUG) 21 | //# error Debug is defined 22 | //#endif 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #define LOG_TAG "PRNG" 33 | #define LOG_DEBUG(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) 34 | #define LOG_INFO(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) 35 | #define LOG_WARN(...) ((void)__android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) 36 | #define LOG_ERROR(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) 37 | 38 | #if defined(NDEBUG) 39 | # undef LOG_VERBOSE 40 | # define LOG_VERBOSE(...) 41 | # undef LOG_DEBUG 42 | # define LOG_DEBUG(...) 43 | #endif 44 | 45 | #define COUNTOF(x) (sizeof(x) / sizeof(x[0])) 46 | 47 | #include 48 | using std::runtime_error; 49 | 50 | #include 51 | using std::string; 52 | 53 | #include 54 | using std::vector; 55 | 56 | #include 57 | using std::ifstream; 58 | 59 | #include 60 | using CryptoPP::AutoSeededRandomPool; 61 | 62 | #include 63 | using CryptoPP::Exception; 64 | 65 | #include "libprng.h" 66 | #include "cleanup.h" 67 | 68 | static double TimeInMilliSeconds(double offset /*milliseconds*/); 69 | static int SamplesPerSecondToMicroSecond(int samples); 70 | 71 | #ifndef NDEBUG 72 | static const char* SensorTypeToName(int sensorType); 73 | #endif 74 | 75 | /* https://groups.google.com/forum/#!topic/android-ndk/ukQBmKJH2eM */ 76 | static const int EXPECTED_JNI_VERSION = JNI_VERSION_1_6; 77 | 78 | /* Used for a delay in sampling sensors. That is, we read a */ 79 | /* sample, and then delay N microseconds before reading from */ 80 | /* the sensor again. This gives the sensor time to latch a */ 81 | /* a new reading. */ 82 | static const int PREFERRED_INTERVAL_MICROSECONDS = 83 | SamplesPerSecondToMicroSecond(30 /*samples per second*/); 84 | 85 | /* App_glue.h defines this. If its not defined, then define it. */ 86 | #ifndef LOOPER_ID_USER 87 | # define LOOPER_ID_USER 3 88 | #endif 89 | 90 | static const int LOOPER_ID_PRNG = LOOPER_ID_USER + 1; 91 | 92 | /* Assuming each float (32-bits) in the {x,y,z} tuple has */ 93 | /* 6-bits of entropy, and each time stamp (64-bits) has 4-bits */ 94 | /* of entropy, then each event has 22-bits. We want 32-bytes or */ 95 | /* 256-bits for to mix into the PRNG for each call to GetBytes, */ 96 | /* so we need 11.6 or 12 events. Note that sizeof(ASensorEvent) */ 97 | /* is over 100 bytes, and it holds other info. Also, the */ 98 | /* accelerometer (if present) appears to produce almost 28-bits */ 99 | /* per member in the tuple. */ 100 | 101 | /* How many sensor events we would like to sample. If we */ 102 | /* reach TIME_LIMIT_IN_MILLISECONDS, then we stop sampling. */ 103 | /* If we sample 0 events, then we fall back to the random */ 104 | /* device and try to grab RANDOM_DEVICE_BYTES bytes. */ 105 | static const int SENSOR_SAMPLE_COUNT = 12; 106 | 107 | /* Sampling time limit, in milliseconds. 200 to 400 ms is */ 108 | /* the sweet spot. Its the same time for the blink of an */ 109 | /* eye. Keep in mind that Java may add from 30 to 80 ms. A */ 110 | /* sensor rich device will be done in 125 ms because there */ 111 | /* are so many readings. */ 112 | static const double TIME_LIMIT_IN_MILLISECONDS = 0.250f * 1000; 113 | 114 | /* How many bytes to read from /dev/urandom. We read from the */ 115 | /* random device as a fallback to ensure something is read */ 116 | /* before providing bytes in GetBytes(). */ 117 | static const int RANDOM_DEVICE_BYTES = 16; 118 | 119 | /* Prototypes */ 120 | static int AddSensorData(); 121 | static int AddRandomDevice(); 122 | static int AddProcessInfo(); 123 | 124 | struct SensorContext { 125 | 126 | SensorContext() : 127 | m_looper(NULL), m_manager(NULL), m_queue(NULL), m_signaled(0), m_stop( 128 | 0.0f) { 129 | } 130 | 131 | ~SensorContext() { 132 | m_looper = NULL; 133 | m_manager = NULL; 134 | m_queue = NULL; 135 | m_signaled = 1; 136 | m_stop = 0.0f; 137 | } 138 | 139 | // Looper 140 | ALooper* m_looper; 141 | 142 | // Sensor Manager 143 | ASensorManager* m_manager; 144 | 145 | // And the queue 146 | ASensorEventQueue* m_queue; 147 | 148 | // If not signaled, then processing should continue. 149 | // If signaled, then processing should stop. 150 | int m_signaled; 151 | 152 | /* Time when the callback should stop */ 153 | double m_stop; 154 | }; 155 | 156 | struct Sensor { 157 | Sensor() : 158 | m_type(0), m_sensor(NULL) { 159 | } 160 | 161 | explicit Sensor(int type, string name, const ASensor* sensor) : 162 | m_type(type), m_name(name), m_sensor(sensor) { 163 | } 164 | 165 | int m_type; 166 | string m_name; 167 | const ASensor* m_sensor; 168 | }; 169 | 170 | typedef vector SensorArray; 171 | 172 | #ifndef NDEBUG 173 | struct RawFloat { 174 | union { 175 | byte b[sizeof(float)]; 176 | unsigned int n[sizeof(float) / sizeof(unsigned int)]; 177 | float f; 178 | }; 179 | }; 180 | #endif 181 | 182 | /* Return the time in milliseconds. If offset = 0.0f, then it returns */ 183 | /* now. Positive offsets are milliseconds into the future. Negative */ 184 | /* offsets are milliseconds in the past. */ 185 | static double TimeInMilliSeconds(double offset /*seconds*/= 0.0f) { 186 | struct timespec res; 187 | clock_gettime(CLOCK_REALTIME, &res); 188 | 189 | double t = 1000.0 * (double) res.tv_sec + (double) res.tv_nsec / 1e6; 190 | return t + offset; 191 | } 192 | 193 | /* Given a sample rate, returns the microseconds in an interval */ 194 | static int SamplesPerSecondToMicroSecond(int samples) { 195 | return (int) ((1 / (double) samples) * 1000 * 1000); 196 | } 197 | 198 | static AutoSeededRandomPool& GetPRNG() { 199 | static AutoSeededRandomPool prng; 200 | return prng; 201 | } 202 | 203 | static SensorArray& GetSensorArray() { 204 | static SensorArray s_list; 205 | static volatile bool s_init = false; 206 | 207 | if (!s_init) { 208 | LOG_DEBUG("SensorArray: initializing list"); 209 | 210 | ASensorList sensorArray; 211 | ASensorManager* sensorManager = ASensorManager_getInstance(); 212 | int n = ASensorManager_getSensorList(sensorManager, &sensorArray); 213 | 214 | if (n < 0) { 215 | LOG_ERROR("SensorArray: failed to retrieve list"); 216 | } else if (n == 0) { 217 | LOG_WARN("SensorArray: no sensors available"); 218 | } else { 219 | s_list.reserve(static_cast(n)); 220 | 221 | for (int i = 0; i < n; i++) { 222 | const ASensor* sensor = sensorArray[i]; 223 | if (sensor == NULL) 224 | continue; 225 | 226 | const char* name = ASensor_getName(sensor); 227 | int type = ASensor_getType(sensor); 228 | 229 | #ifndef NDEBUG 230 | const char* vendor = ASensor_getVendor(sensor); 231 | int min_delay = ASensor_getMinDelay(sensor); 232 | float resolution = ASensor_getResolution(sensor); 233 | 234 | LOG_DEBUG("SensorArray: %s (%s) %d %d %f", name, vendor, type, 235 | min_delay, resolution); 236 | #endif 237 | 238 | s_list.push_back(Sensor(type, name, sensor)); 239 | } 240 | 241 | LOG_DEBUG("SensorArray: added %d sensors", (int )s_list.size()); 242 | } 243 | 244 | s_init = true; 245 | } 246 | 247 | return s_list; 248 | } 249 | 250 | static int SensorEvent(int fd, int events, void* data) { 251 | 252 | LOG_DEBUG("Entered SensorEvent"); 253 | 254 | SensorContext* context = reinterpret_cast(data); 255 | if (!context) { 256 | LOG_ERROR("SensorEvent: context is not valid"); 257 | return 0; 258 | } 259 | 260 | /**************** Return Values ****************/ 261 | /* 1: continue processing; 0: stop processing. */ 262 | /***********************************************/ 263 | 264 | /* Return 0 while the context is signaled. */ 265 | if (context->m_signaled) { 266 | LOG_DEBUG("SensorEvent: signaled to stop"); 267 | return 0; 268 | } 269 | 270 | /* Return 1 while the context is not signaled. */ 271 | return 1; 272 | } 273 | 274 | jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { 275 | LOG_DEBUG("Entered JNI_OnLoad"); 276 | 277 | if (vm == NULL) { 278 | LOG_ERROR("JNI_OnLoad: virtual machine is not valid"); 279 | return -1; 280 | } 281 | 282 | JNIEnv* env = NULL; 283 | if (vm->GetEnv(reinterpret_cast(&env), EXPECTED_JNI_VERSION)) { 284 | LOG_ERROR("JNI_OnLoad: GetEnv failed"); 285 | return -1; 286 | } 287 | 288 | if (env == NULL) { 289 | LOG_ERROR("JNI_OnLoad: JNI environment is not valid"); 290 | return -1; 291 | } 292 | 293 | JNINativeMethod methods[2]; 294 | 295 | methods[0].name = "CryptoPP_Reseed"; 296 | methods[0].signature = "([B)I"; 297 | methods[0].fnPtr = 298 | reinterpret_cast(Java_com_cryptopp_prng_PRNG_CryptoPP_1Reseed); 299 | 300 | methods[1].name = "CryptoPP_GetBytes"; 301 | methods[1].signature = "([B)I"; 302 | methods[1].fnPtr = 303 | reinterpret_cast(Java_com_cryptopp_prng_PRNG_CryptoPP_1GetBytes); 304 | 305 | jclass cls = env->FindClass("com/cryptopp/prng/PRNG"); 306 | if (cls == NULL) { 307 | LOG_ERROR("JNI_OnLoad: FindClass com/cryptopp/prng/PRNG failed"); 308 | } else { 309 | env->RegisterNatives(cls, methods, COUNTOF(methods)); 310 | } 311 | 312 | return EXPECTED_JNI_VERSION; 313 | } 314 | 315 | /* 316 | * Class: com_cryptopp_prng_PRNG 317 | * Method: CryptoPP_Reseed 318 | * Signature: ([B)I 319 | */ 320 | jint JNICALL Java_com_cryptopp_prng_PRNG_CryptoPP_1Reseed(JNIEnv* env, jclass, 321 | jbyteArray seed) { 322 | 323 | LOG_DEBUG("Entered Reseed"); 324 | 325 | int consumed = 0; 326 | 327 | try { 328 | if (!env) { 329 | LOG_ERROR("Reseed: environment is NULL"); 330 | return 0; 331 | } 332 | 333 | if (!seed) { 334 | // OK if the caller passed NULL for the array 335 | LOG_WARN("GetBytes: byte array is NULL"); 336 | return 0; 337 | } 338 | 339 | ReadByteBuffer buffer(env, seed); 340 | 341 | const byte* seed_arr = buffer.GetByteArray(); 342 | size_t seed_len = buffer.GetArrayLen(); 343 | 344 | if ((seed_arr == NULL)) { 345 | LOG_ERROR("Reseed: array pointer is not valid"); 346 | } else if ((seed_len == 0)) { 347 | LOG_ERROR("Reseed: array size is not valid"); 348 | } else { 349 | AutoSeededRandomPool& prng = GetPRNG(); 350 | prng.IncorporateEntropy(seed_arr, seed_len); 351 | 352 | LOG_INFO("Reseed: seeded with %d bytes", (int )seed_len); 353 | 354 | consumed += (int) seed_len; 355 | } 356 | } catch (const Exception& ex) { 357 | LOG_ERROR("Reseed: Crypto++ exception: \"%s\"", ex.what()); 358 | return 0; 359 | } 360 | 361 | return consumed; 362 | } 363 | 364 | /* 365 | * Class: com_cryptopp_prng_PRNG 366 | * Method: CryptoPP_GetBytes 367 | * Signature: ([B)I 368 | */ 369 | JNIEXPORT jint JNICALL Java_com_cryptopp_prng_PRNG_CryptoPP_1GetBytes( 370 | JNIEnv* env, jclass, jbyteArray bytes) { 371 | 372 | LOG_DEBUG("Entered GetBytes"); 373 | 374 | int retrieved = 0; 375 | 376 | // Always mix in data during a call to GetBytes, even if the array is null. 377 | // Any entropy gathered will help future calls with a non-null array. 378 | int rc1, rc2, rc3; 379 | 380 | rc1 = AddProcessInfo(); 381 | assert(rc1 > 0); 382 | 383 | rc2 = AddSensorData(); 384 | assert(rc2 > 0); 385 | 386 | /* Fallback to a random device on failure. This is not */ 387 | /* catastrophic since the Crypto++ generator is OK */ 388 | if (rc1 <= 0 || rc2 <= 0) { 389 | rc3 = AddRandomDevice(); 390 | assert(rc3 > 0); 391 | } 392 | 393 | try { 394 | 395 | if (!env) { 396 | LOG_ERROR("GetBytes: environment is NULL"); 397 | return 0; 398 | } 399 | 400 | if (!bytes) { 401 | // OK if the caller passed NULL for the array 402 | LOG_WARN("GetBytes: byte array is NULL"); 403 | return 0; 404 | } 405 | 406 | WriteByteBuffer buffer(env, bytes); 407 | 408 | byte* prng_arr = buffer.GetByteArray(); 409 | size_t prng_len = buffer.GetArrayLen(); 410 | 411 | if ((prng_arr == NULL)) { 412 | LOG_ERROR("GetBytes: array pointer is not valid"); 413 | } else if ((prng_len == 0)) { 414 | LOG_ERROR("GetBytes: array size is not valid"); 415 | } else { 416 | AutoSeededRandomPool& prng = GetPRNG(); 417 | prng.GenerateBlock(prng_arr, prng_len); 418 | 419 | LOG_INFO("GetBytes: generated %d bytes", (int )prng_len); 420 | 421 | retrieved += (int) prng_len; 422 | } 423 | } catch (const Exception& ex) { 424 | LOG_ERROR("GetBytes: Crypto++ exception: \"%s\"", ex.what()); 425 | return 0; 426 | } 427 | 428 | return retrieved; 429 | } 430 | 431 | static int AddSensorData() { 432 | LOG_DEBUG("Entered AddSensorData"); 433 | 434 | const SensorArray& sensorArray = GetSensorArray(); 435 | if (sensorArray.size() == 0) { 436 | LOG_WARN("SensorData: no sensors available"); 437 | return 0; 438 | } 439 | 440 | ALooper* looper = ALooper_forThread(); 441 | if (looper == NULL) 442 | looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); 443 | 444 | if (looper == NULL) { 445 | LOG_ERROR("SensorData: looper is not valid"); 446 | return 0; 447 | } 448 | 449 | LOG_DEBUG("SensorData: created looper"); 450 | 451 | ASensorManager* sensorManager = ASensorManager_getInstance(); 452 | 453 | if (sensorManager == NULL) { 454 | LOG_ERROR("SensorData: sensor manager is not valid"); 455 | return 0; 456 | } 457 | 458 | LOG_DEBUG("SensorData: created sensor manager"); 459 | 460 | SensorContext context; 461 | 462 | ASensorEventQueue* queue = ASensorManager_createEventQueue(sensorManager, 463 | looper, LOOPER_ID_PRNG, SensorEvent, 464 | reinterpret_cast(&context)); 465 | 466 | if (queue == NULL) { 467 | LOG_ERROR("SensorData: queue is not valid"); 468 | return 0; 469 | } 470 | 471 | LOG_DEBUG("SensorData: created event queue"); 472 | 473 | context.m_manager = sensorManager; 474 | context.m_looper = looper; 475 | context.m_queue = queue; 476 | context.m_stop = TimeInMilliSeconds(TIME_LIMIT_IN_MILLISECONDS); 477 | 478 | /* Accumulate the various delays. */ 479 | int sensor_delay = 0; 480 | 481 | for (size_t i = 0; i < sensorArray.size(); i++) { 482 | 483 | const ASensor* sensor = sensorArray[i].m_sensor; 484 | if (sensor == NULL) { 485 | LOG_WARN("SensorData: sensor number %i is not valid", (int)i); 486 | continue; 487 | } 488 | 489 | // Take the larger of our preferred versus the sensor's rate 490 | // Even if a sensor advertises 'min_delay = 0', we still 491 | // try and extract data from it. Often we can get a reading. 492 | int rate = ASensor_getMinDelay(sensor); 493 | int pref = PREFERRED_INTERVAL_MICROSECONDS; 494 | int adj = std::max(rate, pref); 495 | 496 | /* sensor_delay is the max delay of all sensors. */ 497 | /* Its used below to usleep(3) on error. */ 498 | sensor_delay = std::max(adj, sensor_delay); 499 | 500 | ASensorEventQueue_enableSensor(queue, sensor); 501 | ASensorEventQueue_setEventRate(queue, sensor, adj); 502 | } 503 | 504 | LOG_DEBUG("SensorData: enabled sensors"); 505 | 506 | /////////////////////////////////////////////////////////// 507 | 508 | ASensorEvent sensor_events[SENSOR_SAMPLE_COUNT * 2]; 509 | int totalSensors = 0, n = 0; 510 | const double time_start = TimeInMilliSeconds(); 511 | double time_now = time_start; 512 | 513 | const int SENSOR_DELAY = sensor_delay; 514 | LOG_DEBUG("SensorData: sensor delay is %d microseconds", SENSOR_DELAY); 515 | 516 | while (context.m_signaled == 0) { 517 | 518 | n = ASensorEventQueue_hasEvents(queue); 519 | 520 | #ifdef NDEBUG 521 | if (n <= 0) { 522 | LOG_DEBUG("SensorData: no events, waiting for measurement"); 523 | usleep(SENSOR_DELAY); 524 | continue; 525 | } 526 | #else 527 | if (n == 0) { 528 | LOG_DEBUG("SensorData: no events, waiting for measurement (1)"); 529 | usleep(SENSOR_DELAY); 530 | continue; 531 | } else if (n < 0) { 532 | LOG_DEBUG("SensorData: no events, waiting for measurement (2)"); 533 | usleep(SENSOR_DELAY); 534 | continue; 535 | } 536 | #endif 537 | 538 | /* If we get this far, we should not encounter an error */ 539 | n = ASensorEventQueue_getEvents(queue, sensor_events, 540 | COUNTOF(sensor_events)); 541 | if (n == 0) { 542 | LOG_WARN("SensorData: no events (warn)"); 543 | usleep(SENSOR_DELAY); 544 | continue; 545 | } else if (n < 0) { 546 | LOG_ERROR("SensorData: no events (error)"); 547 | usleep(SENSOR_DELAY); 548 | continue; 549 | } 550 | 551 | #ifndef NDEBUG 552 | for (int i = 0; i < n; i++) { 553 | const ASensorEvent ee = sensor_events[i]; 554 | const ASensorVector vv = ee.vector; 555 | LOG_DEBUG("SensorData: %s, v[0]: %.9f, v[1]: %.9f, v[2]: %.9f ", 556 | SensorTypeToName(ee.type), vv.v[0], vv.v[1], vv.v[2]); 557 | 558 | RawFloat x, y, z; 559 | x.f = vv.x, y.f = vv.y, z.f = vv.z; 560 | LOG_DEBUG(" x: %08x%08x, y: %08x%08x, z: %08x%08x", 561 | x.n[0], x.n[1], y.n[0], y.n[1], z.n[0], z.n[1]); 562 | } 563 | #endif 564 | 565 | try { 566 | AutoSeededRandomPool& prng = GetPRNG(); 567 | prng.IncorporateEntropy((const byte*) sensor_events, 568 | n * sizeof(ASensorEvent)); 569 | } catch (Exception& ex) { 570 | LOG_ERROR("SensorData: Crypto++ exception: \"%s\"", ex.what()); 571 | } 572 | 573 | LOG_DEBUG("SensorData: added %d events, %d bytes", n, 574 | static_cast(n * sizeof(ASensorEvent))); 575 | 576 | // Book keeping 577 | totalSensors += n; 578 | time_now = TimeInMilliSeconds(); 579 | 580 | if (totalSensors >= SENSOR_SAMPLE_COUNT) { 581 | LOG_DEBUG("SensorData: reached event count of %d", 582 | SENSOR_SAMPLE_COUNT); 583 | context.m_signaled = 1; 584 | } else if (context.m_stop < time_now) { 585 | LOG_DEBUG("SensorData: reached time limit of %.2f ms", 586 | TIME_LIMIT_IN_MILLISECONDS); 587 | context.m_signaled = 1; 588 | } 589 | 590 | /* Give the sensors some time to latch another measurement */ 591 | if (context.m_signaled == 0) 592 | usleep(SENSOR_DELAY); 593 | } 594 | 595 | /////////////////////////////////////////////////////////// 596 | 597 | for (size_t i = 0; i < sensorArray.size(); i++) { 598 | 599 | const ASensor* sensor = sensorArray[i].m_sensor; 600 | if (sensor == NULL) 601 | continue; 602 | 603 | ASensorEventQueue_disableSensor(queue, sensor); 604 | } 605 | 606 | LOG_DEBUG("SensorData: disabled sensors"); 607 | 608 | const double elapsed = time_now - time_start; 609 | LOG_INFO("SensorData: added %d total events, %d total bytes, in %.2f ms", 610 | totalSensors, int(totalSensors * sizeof(ASensorEvent)), elapsed); 611 | 612 | return totalSensors * sizeof(ASensorEvent); 613 | } 614 | 615 | static int AddRandomDevice() { 616 | LOG_DEBUG("Entered AddRandomDevice"); 617 | 618 | ifstream ifs; 619 | byte buff[RANDOM_DEVICE_BYTES]; 620 | 621 | try { 622 | ifs.open("/dev/urandom"); 623 | ifs.read(reinterpret_cast(buff), sizeof(buff)); 624 | } catch (const std::exception&) { 625 | LOG_ERROR("RandomDevice: failed to read random device"); 626 | return 0; 627 | } 628 | 629 | try { 630 | AutoSeededRandomPool& prng = GetPRNG(); 631 | prng.IncorporateEntropy(buff, sizeof(buff)); 632 | 633 | LOG_INFO("RandomDevice: added %d total bytes", (int)sizeof(buff)); 634 | } catch (const Exception& ex) { 635 | LOG_ERROR("RandomDevice: Crypto++ exception: \"%s\"", ex.what()); 636 | return 0; 637 | } 638 | 639 | return sizeof(buff); 640 | } 641 | 642 | static int AddProcessInfo() { 643 | LOG_DEBUG("Entered AddProcessInfo"); 644 | 645 | /* Bytes added across all calls */ 646 | static unsigned long accum = 0; 647 | 648 | pid_t pid1, pid2; 649 | timespec tspec[4]; 650 | 651 | pid1 = getpid(); 652 | pid2 = getppid(); 653 | 654 | /* We don't care about return values here */ 655 | (void) clock_gettime(CLOCK_REALTIME, &tspec[0]); 656 | (void) clock_gettime(CLOCK_MONOTONIC, &tspec[1]); 657 | (void) clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tspec[2]); 658 | (void) clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tspec[3]); 659 | 660 | /* Send data into the prng in one shot. Its faster than 6 different calls. */ 661 | byte buff[128]; 662 | size_t idx = 0; 663 | 664 | memcpy(&buff[idx], &pid1, sizeof(pid1)); 665 | idx += sizeof(pid1); 666 | 667 | memcpy(&buff[idx], &pid2, sizeof(pid2)); 668 | idx += sizeof(pid2); 669 | 670 | memcpy(&buff[idx], tspec, sizeof(tspec)); 671 | idx += sizeof(tspec); 672 | 673 | memcpy(&buff[idx], &accum, sizeof(accum)); 674 | idx += sizeof(accum); 675 | 676 | accum += idx; 677 | 678 | try { 679 | AutoSeededRandomPool& prng = GetPRNG(); 680 | prng.IncorporateEntropy(buff, sizeof(buff)); 681 | 682 | LOG_INFO("ProcessInfo: added %d total bytes", (int)idx); 683 | } catch (const Exception& ex) { 684 | LOG_ERROR("ProcessInfo: Crypto++ exception: \"%s\"", ex.what()); 685 | return 0; 686 | } 687 | 688 | return (int) idx; 689 | } 690 | 691 | #ifndef NDEBUG 692 | static const char* SensorTypeToName(int sensorType) { 693 | switch (sensorType) { 694 | 695 | /* /.../sensor.h */ 696 | case ASENSOR_TYPE_ACCELEROMETER: /* 1 */ 697 | return "Accelerometer"; 698 | case ASENSOR_TYPE_MAGNETIC_FIELD: /* 2 */ 699 | return "Magnetic field"; 700 | case ASENSOR_TYPE_GYROSCOPE: /* 4 */ 701 | return "Gyroscope"; 702 | case ASENSOR_TYPE_LIGHT: /* 5 */ 703 | return "Light"; 704 | case ASENSOR_TYPE_PROXIMITY: /* 8 */ 705 | return "Proximity"; 706 | 707 | /* http://developer.android.com/reference/android/hardware/Sensor.html */ 708 | case 0: 709 | return "type 0"; 710 | case 3: 711 | return "Orientation"; 712 | case 6: 713 | return "Pressure"; 714 | case 7: 715 | return "Temperature"; 716 | case 9: 717 | return "Gravity"; 718 | case 10: 719 | return "Linear acceleration"; 720 | case 11: 721 | return "Rotation vector"; 722 | case 12: 723 | return "Relative humidity"; 724 | case 13: 725 | return "Ambient temperature"; 726 | case 14: 727 | return "Uncalibrated magnetic field"; 728 | case 15: 729 | return "Rotation vector"; 730 | case 16: 731 | return "Uncalibrated gyroscope"; 732 | case 17: 733 | return "Significant motion"; 734 | case 18: 735 | return "type 18"; 736 | case 19: 737 | return "Step counter"; 738 | case 20: 739 | return "Geo-magnetic rotation vector."; 740 | case 21: 741 | return "Heart rate"; 742 | default: 743 | ; 744 | } 745 | return "Unknown"; 746 | } 747 | #endif 748 | -------------------------------------------------------------------------------- /jni/libprng.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class com_cryptopp_prng_PRNG */ 4 | 5 | #ifndef _Included_com_cryptopp_prng_PRNG 6 | #define _Included_com_cryptopp_prng_PRNG 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: com_cryptopp_prng_PRNG 12 | * Method: CryptoPP_Reseed 13 | * Signature: ([B)I 14 | */ 15 | JNIEXPORT jint JNICALL Java_com_cryptopp_prng_PRNG_CryptoPP_1Reseed 16 | (JNIEnv *, jclass, jbyteArray); 17 | 18 | /* 19 | * Class: com_cryptopp_prng_PRNG 20 | * Method: CryptoPP_GetBytes 21 | * Signature: ([B)I 22 | */ 23 | JNIEXPORT jint JNICALL Java_com_cryptopp_prng_PRNG_CryptoPP_1GetBytes 24 | (JNIEnv *, jclass, jbyteArray); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | #endif 30 | -------------------------------------------------------------------------------- /lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /proguard.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | -------------------------------------------------------------------------------- /project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-14 15 | -------------------------------------------------------------------------------- /res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noloader/Android-PRNG/4dd19eb7486e985d71f1a6ad3a2e33505e66dfbe/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noloader/Android-PRNG/4dd19eb7486e985d71f1a6ad3a2e33505e66dfbe/res/drawable-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noloader/Android-PRNG/4dd19eb7486e985d71f1a6ad3a2e33505e66dfbe/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 16 | 22 | 23 | 24 | 28 | 29 | 35 | 36 | 37 | 53 | 54 | 58 | 59 | 69 | 70 |