├── 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 |
78 |
79 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Android PRNG
4 | Tap Reseed to add bytes to the Crypto++ generator. Tap Generate to fetch random numbers from the Crypto++ generator.
5 | 0x00 0x01 ....
6 | Generate
7 | Reseed
8 |
9 |
--------------------------------------------------------------------------------
/src/com/cryptopp/prng/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.cryptopp.prng;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Typeface;
5 | import android.os.AsyncTask;
6 | import android.os.Build;
7 | import android.os.Build.VERSION;
8 | import android.os.Bundle;
9 | import android.os.StrictMode;
10 | import android.util.DisplayMetrics;
11 | import android.view.View;
12 | import android.widget.TextView;
13 |
14 | import com.cryptopp.prng.R;
15 | import com.cryptopp.prng.PRNG;
16 |
17 | public class MainActivity extends Activity {
18 | /** Called when the activity is first created. */
19 | @Override
20 | public void onCreate(Bundle savedInstanceState) {
21 | super.onCreate(savedInstanceState);
22 | setContentView(R.layout.main);
23 |
24 | // ///////////////////////////////////////////////////////////
25 |
26 | final TextView lblNumbers = (TextView) findViewById(R.id.lblNumbers);
27 | if (lblNumbers != null) {
28 | lblNumbers.setTypeface(Typeface.MONOSPACE);
29 | }
30 |
31 | // ///////////////////////////////////////////////////////////
32 |
33 | StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
34 | builder.detectLeakedSqlLiteObjects();
35 |
36 | if (VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
37 | builder.detectAll();
38 | }
39 |
40 | builder.penaltyLog();
41 |
42 | StrictMode.VmPolicy vmp = builder.build();
43 | StrictMode.setVmPolicy(vmp);
44 |
45 | PRNG prng = new PRNG();
46 | byte[] bytes = new byte[32];
47 | prng.getBytes(bytes);
48 | }
49 |
50 | public void btnReseed_onClick(View view) {
51 |
52 | new AsyncTask() {
53 | @Override
54 | protected Void doInBackground(Void... params) {
55 |
56 | final TextView lblNumbers = (TextView) findViewById(R.id.lblNumbers);
57 | if (lblNumbers != null) {
58 | byte[] seed = lblNumbers.getText().toString().getBytes();
59 |
60 | if (seed != null)
61 | PRNG.Reseed(seed);
62 |
63 | seed = null;
64 | }
65 | return null;
66 | }
67 | }.execute();
68 | }
69 |
70 | public void btnGenerate_onClick(View view) {
71 |
72 | new AsyncTask() {
73 | @Override
74 | protected String doInBackground(Void... params) {
75 |
76 | Float pixelWidth = 0.0f;
77 | DisplayMetrics dm = getBaseContext().getResources()
78 | .getDisplayMetrics();
79 | if (dm != null) {
80 | pixelWidth = (float) dm.widthPixels;
81 | }
82 |
83 | Float charWidth = 0.0f;
84 | final TextView lblNumbers = (TextView) findViewById(R.id.lblNumbers);
85 | if (lblNumbers != null) {
86 | charWidth = lblNumbers.getPaint().measureText(" ");
87 | }
88 |
89 | /* The extra gyrations negate Math.round's rounding up */
90 | int charPerLine = Math.round(pixelWidth - 0.5f)
91 | / Math.round(charWidth - 0.5f);
92 | if (charPerLine == 0)
93 | charPerLine = 21;
94 |
95 | /* This prints about 4 lines of random numbers */
96 | byte[] bytes = new byte[(charPerLine / 3) * 4];
97 | PRNG.GetBytes(bytes);
98 |
99 | StringBuilder sb = new StringBuilder();
100 | for (byte b : bytes)
101 | sb.append(String.format("%02X ", (0xff & b)));
102 |
103 | bytes = null;
104 | dm = null;
105 |
106 | return sb.toString();
107 | }
108 |
109 | @Override
110 | protected void onPostExecute(String result) {
111 |
112 | final TextView lblNumbers = (TextView) findViewById(R.id.lblNumbers);
113 | if (lblNumbers != null) {
114 | lblNumbers.setText(result);
115 | }
116 | }
117 | }.execute();
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/com/cryptopp/prng/PRNG.java:
--------------------------------------------------------------------------------
1 | package com.cryptopp.prng;
2 |
3 | public class PRNG {
4 |
5 | static {
6 | System.loadLibrary("c++_shared");
7 | System.loadLibrary("cryptopp");
8 | System.loadLibrary("prng");
9 | }
10 |
11 | private static native int CryptoPP_Reseed(byte[] bytes);
12 |
13 | private static native int CryptoPP_GetBytes(byte[] bytes);
14 |
15 | private static Object lock = new Object();
16 |
17 | // Class method. Returns the number of bytes consumed from the seed.
18 | public static int Reseed(byte[] seed) {
19 |
20 | synchronized (lock) {
21 | return CryptoPP_Reseed(seed);
22 | }
23 | }
24 |
25 | // Class method. Returns the number of bytes generated.
26 | public static int GetBytes(byte[] bytes) {
27 | synchronized (lock) {
28 | return CryptoPP_GetBytes(bytes);
29 | }
30 | }
31 |
32 | // Instance method. Returns the number of bytes consumed from the seed.
33 | public int reseed(byte[] seed) {
34 | synchronized (lock) {
35 | return CryptoPP_Reseed(seed);
36 | }
37 | }
38 |
39 | // Instance method. Returns the number of bytes generated.
40 | public int getBytes(byte[] bytes) {
41 | synchronized (lock) {
42 | return CryptoPP_GetBytes(bytes);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------