Import native code changes from Mike Mohr.

This commit is contained in:
Brian Pellin
2010-11-21 15:38:33 -06:00
parent 7150359dad
commit 7569c7548b
16 changed files with 535 additions and 540 deletions

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@ build.properties
local.properties
bin
gen
obj

View File

@@ -3,6 +3,7 @@ Brian Pellin
Achim Weimert
Johan Berts - search patches
Mike Mohr - Better native code for aes and sha
Tobias Selig - icon support
Tolga Onbay - password generator
@@ -13,4 +14,4 @@ Laurent, Norman Obry - French
Maciej Bieniek - Polish
Максим Сёмочкин - Russian
MaWi - German
yslandro - Norwegian Nynorsk
yslandro - Norwegian Nynorsk

25
LICENSE
View File

@@ -261,3 +261,28 @@ no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is
included in the section entitled "GNU Free Documentation License".
---
Many files under jni/final_key/ are coverage by the following license:
Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
LICENSE TERMS
The redistribution and use of this software (with or without changes)
is allowed without the payment of fees or royalties provided that:
1. source code distributions include the above copyright notice, this
list of conditions and the following disclaimer;
2. binary distributions include the above copyright notice, this list
of conditions and the following disclaimer in their documentation;
3. the name of the copyright holder is not used to endorse products
built using this software without specific written permission.
DISCLAIMER
This software is provided 'as is' with no explicit or implied warranties
in respect of its properties, including, but not limited to, correctness
and/or fitness for purpose.

4
README
View File

@@ -5,6 +5,4 @@ Build instructions:
c. Move the KeePassDroid git root to ${ndk-root}/apps/KeePassDroid/project
d. cp -a ${ndk-root}/apps/KeePassDroid/project/jni_root ${ndk-root}/apps/KeePassDroid
2. Run prep-build.sh from ${ndk-root}/apps/KeePassDroid to download, unpack, and patch the openssl sources.
3. At ${ndk-root} run 'make APP=KeePassDroid' to build the native sources.
2. At ${ndk-root} run 'make APP=KeePassDroid' to build the native sources.

21
jni/aes/.gitignore vendored Normal file
View File

@@ -0,0 +1,21 @@
aes_amd64.asm
aescpp.h
aescrypt.c
aes.h
aeskey.c
aes_modes.c
aesopt.h
aestab.c
aestab.h
aes.txt
aes_via_ace.h
aes_x86_v1.asm
aes_x86_v2.asm
aesxam.c
brg_endian.h
brg_types.h
rfc3686.c
tablegen.c
vbaxam.doc
vb.txt
via_ace.txt

13
jni/aes/Android.mk Normal file
View File

@@ -0,0 +1,13 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := aes
LOCAL_SRC_FILES := \
aescrypt.c \
aeskey.c \
aes_modes.c \
aestab.c
include $(BUILD_STATIC_LIBRARY)

View File

@@ -4,11 +4,12 @@ include $(CLEAR_VARS)
LOCAL_MODULE := final-key
LOCAL_SRC_FILES := final_key.c aes.c
LOCAL_SRC_FILES := \
kpd_jni.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../openssl-0.9.8l/include
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../sha $(LOCAL_PATH)/../aes
LOCAL_STATIC_LIBRARIES := openssl-crypto
LOCAL_STATIC_LIBRARIES := aes sha
LOCAL_LDLIBS := -llog

View File

@@ -1,140 +0,0 @@
#include <openssl/evp.h>
#include <openssl/aes.h>
#include <jni.h>
#include <stdlib.h>
#include <android/log.h>
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "KeePassDroidNative", __VA_ARGS__)
#define LOGD(...)
//#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , "KeePassDroidNative", __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO , "KeePassDroidNative", __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN , "KeePassDroidNative", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , "KeePassDroidNative", __VA_ARGS__)
jlong Java_com_keepassdroid_crypto_NativeAESCipherSpi_nativeInit(JNIEnv *env,
jobject this, jboolean encrypt, jbyteArray key, jbyteArray iv,
jboolean padding) {
LOGD("1");
// Convert keys to c
jsize key_len = (*env)->GetArrayLength(env, key);
char *c_key = (char *) malloc(key_len);
(*env)->GetByteArrayRegion(env, key, 0, key_len, c_key);
LOGD("2: Keylen: %d", key_len);
// Covert iv to c
jsize iv_len = (*env)->GetArrayLength(env, iv);
char *c_iv = (char *) malloc(iv_len);
(*env)->GetByteArrayRegion(env, iv, 0, iv_len, c_iv);
LOGD("3: IvLen: %d", iv_len);
EVP_CIPHER_CTX *ctx = (EVP_CIPHER_CTX *) malloc(sizeof(EVP_CIPHER_CTX));
LOGD("3.5: %d", sizeof(EVP_CIPHER_CTX));
EVP_CIPHER_CTX_init(ctx);
EVP_CipherInit_ex(ctx, EVP_aes_256_cbc(), NULL, c_key, c_iv, encrypt);
LOGD("4");
if ( padding ) {
EVP_CIPHER_CTX_set_padding(ctx, 1);
} else {
EVP_CIPHER_CTX_set_padding(ctx, 0);
}
LOGD("5");
// Free allocated memory
free(c_iv);
free(c_key);
LOGD("6: ctxPtr=%d",ctx);
return (jlong) ctx;
}
void Java_com_keepassdroid_crypto_NativeAESCipherSpi_nativeCleanup(JNIEnv *env,
jclass this, jlong ctxPtr) {
LOGD("cleanup");
EVP_CIPHER_CTX *ctx = (EVP_CIPHER_CTX *) ctxPtr;
EVP_CIPHER_CTX_cleanup(ctx);
free(ctx);
}
jint Java_com_keepassdroid_crypto_NativeAESCipherSpi_nativeUpdate(JNIEnv *env,
jobject this, jlong ctxPtr, jbyteArray input, jint inputOffset,
jint inputLen, jbyteArray output, jint outputOffset, jint outputSize) {
LOGD("InputSize: %d; InputOffset: %d, OutputOffset: %d, OutputSize: %d", inputLen, inputOffset,
outputOffset, outputSize);
if ( inputLen == 0 ) {
return 0;
}
char *c_input = (char *) malloc(inputLen);
(*env)->GetByteArrayRegion(env, input, inputOffset, inputLen, c_input);
int outLen;
char *c_output;
// Worst case is all full blocks with 1 byte on the left and right
//int max_update_size = (((inputLen - 2) / AES_BLOCK_SIZE) + 2) * AES_BLOCK_SIZE;
c_output = (char *) malloc(outputSize);
EVP_CIPHER_CTX *ctx = (EVP_CIPHER_CTX *) ctxPtr;
LOGD("Pre: ctxPtr=%d", ctx);
EVP_CipherUpdate(ctx, c_output, &outLen, c_input, inputLen);
LOGD("Post");
/* output can differ on final
if ( outLen != ((int) outputSize) ) {
LOGD("Outsize differs: %d", outLen);
free(c_output);
free(c_input);
return -1;
}
*/
LOGD("PreOut: outputSize=%d, outputOffset=%d, OutLen=%d", outputSize, outputOffset, outLen);
(*env)->SetByteArrayRegion(env, output, outputOffset, outLen, c_output);
free(c_output);
free(c_input);
LOGD("PostOut");
return (jint) outLen; // I think jint should always be bigger than int
}
jint Java_com_keepassdroid_crypto_NativeAESCipherSpi_nativeDoFinal(JNIEnv *env,
jobject this, jlong ctxPtr, jbyteArray output, jint outputOffset,
jint outputSize) {
LOGD("outputOffset=%d", outputOffset);
char *c_output = (char *) malloc(outputSize);
int outLen;
EVP_CIPHER_CTX *ctx = (EVP_CIPHER_CTX *) ctxPtr;
EVP_CipherFinal_ex(ctx, c_output, &outLen);
/*
if ( outLen != ((int) outputSize) ) {
free(c_output);
return -1;
}
*/
LOGD("Final: OutputLen=%d, outputOffset=%d", outLen, (int)outputOffset);
(*env)->SetByteArrayRegion(env, output, outputOffset, outLen, c_output);
free(c_output);
return (jint) outLen;
}

View File

@@ -1,84 +0,0 @@
#include <openssl/evp.h>
#include <openssl/aes.h>
#include <jni.h>
#include <string.h>
#define BLOCK_SIZE AES_BLOCK_SIZE * 2
/* Prepare the final key */
jbyteArray Java_com_keepassdroid_crypto_finalkey_NativeFinalKey_nativeTransformMasterKey(JNIEnv *env, jobject this,
jbyteArray seed, jbyteArray key, jint rounds) {
char cSeed[BLOCK_SIZE];
char key1[BLOCK_SIZE];
char key2[BLOCK_SIZE];
// Verify length of key and seed
if ( (*env)->GetArrayLength(env, seed) != BLOCK_SIZE ) {
return NULL;
}
if ( (*env)->GetArrayLength(env, key) != BLOCK_SIZE ) {
return NULL;
}
// TODO: Test to see if GetByteArrayElements is cheaper
(*env)->GetByteArrayRegion(env, seed, 0, BLOCK_SIZE, cSeed);
(*env)->GetByteArrayRegion(env, key, 0, BLOCK_SIZE, key1);
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_aes_256_ecb(), NULL, cSeed, NULL);
int c_len; // Not really needed, we always work at even block sizes here
int i;
char flip = 0;
for (i = 0; i < rounds; i++) {
// Toggle between arrays as source and destination
if ( flip ) {
EVP_EncryptUpdate(&ctx, key1, &c_len, key2, BLOCK_SIZE);
flip = 0;
} else {
EVP_EncryptUpdate(&ctx, key2, &c_len, key1, BLOCK_SIZE);
flip = 1;
}
}
EVP_CIPHER_CTX_cleanup(&ctx);
jbyteArray result = (*env)->NewByteArray(env, BLOCK_SIZE);
// Need to take the SHA1 digest
EVP_MD_CTX digestCtx;
EVP_MD_CTX_init(&digestCtx);
EVP_DigestInit_ex(&digestCtx, EVP_sha256(), NULL);
if ( flip ) {
EVP_DigestUpdate(&digestCtx, key2, BLOCK_SIZE);
EVP_DigestFinal_ex(&digestCtx, key1, NULL);
flip = 0;
} else {
EVP_DigestUpdate(&digestCtx, key1, BLOCK_SIZE);
EVP_DigestFinal_ex(&digestCtx, key2, NULL);
flip = 1;
}
EVP_MD_CTX_cleanup(&digestCtx);
if ( flip ) {
(*env)->SetByteArrayRegion(env, result, 0, BLOCK_SIZE, key2);
} else {
(*env)->SetByteArrayRegion(env, result, 0, BLOCK_SIZE, key1);
}
return result;
}
/* For testing purposes only */
jbyteArray Java_com_keepassdroid_crypto_finalkey_NativeFinalKey_nativeReflect(JNIEnv *env, jclass this,
jbyteArray key) {
return key;
}

428
jni/final_key/kpd_jni.c Normal file
View File

@@ -0,0 +1,428 @@
/*
This is a JNI wrapper for AES & SHA source code on Android.
Copyright (C) 2010 Michael Mohr
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <jni.h>
#include "aes.h"
#include "sha2.h"
static JavaVM *cached_vm;
static jclass bad_arg, no_mem;
typedef enum {
ENCRYPTION,
DECRYPTION,
FINALIZED
} edir_t;
typedef struct _aes_encryption_state {
edir_t direction;
uint32_t cache_len;
uint8_t iv[16], cache[16];
aes_encrypt_ctx ctx[1];
} aes_encryption_state;
typedef struct _aes_decryption_state {
edir_t direction;
uint32_t cache_len;
uint8_t iv[16], cache[16];
aes_decrypt_ctx ctx[1];
} aes_decryption_state;
JNIEXPORT jint JNICALL JNI_OnLoad( JavaVM *vm, void *reserved ) {
JNIEnv *env;
jclass cls;
cached_vm = vm;
if((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6))
return JNI_ERR;
cls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
if( cls == NULL )
return JNI_ERR;
bad_arg = (*env)->NewWeakGlobalRef(env, cls);
if( bad_arg == NULL )
return JNI_ERR;
cls = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
if( cls == NULL )
return JNI_ERR;
no_mem = (*env)->NewWeakGlobalRef(env, cls);
if( no_mem == NULL )
return JNI_ERR;
aes_init();
return JNI_VERSION_1_6;
}
// called on garbage collection
JNIEXPORT void JNICALL JNI_OnUnload( JavaVM *vm, void *reserved ) {
JNIEnv *env;
if((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6)) {
return;
}
(*env)->DeleteWeakGlobalRef(env, bad_arg);
(*env)->DeleteWeakGlobalRef(env, no_mem);
return;
}
JNIEXPORT jlong JNICALL Java_com_keepassdroid_crypto_NativeAESCipherSpi_nEncryptInit(JNIEnv *env, jobject this, jbyteArray key, jbyteArray iv) {
unsigned char ckey[32];
aes_encryption_state *state;
jint key_len = (*env)->GetArrayLength(env, key);
jint iv_len = (*env)->GetArrayLength(env, iv);
if( ! ( key_len == 16 || key_len == 24 || key_len == 32 ) || iv_len != 16 ) {
(*env)->ThrowNew(env, bad_arg, "Invalid length of key or iv");
return -1;
}
state = (aes_encryption_state *)malloc(sizeof(aes_encryption_state));
if( state == NULL ) {
(*env)->ThrowNew(env, no_mem, "Cannot allocate memory for the encryption state");
return -1;
}
memset(state, 0, sizeof(aes_encryption_state));
(*env)->GetByteArrayRegion(env, key, (jint)0, key_len, (jbyte *)ckey);
state->direction = ENCRYPTION;
(*env)->GetByteArrayRegion(env, iv, (jint)0, iv_len, (jbyte *)state->iv);
aes_encrypt_key(ckey, key_len, state->ctx);
return (jlong)state;
}
JNIEXPORT jlong JNICALL Java_com_keepassdroid_crypto_NativeAESCipherSpi_nDecryptInit(JNIEnv *env, jobject this, jbyteArray key, jbyteArray iv) {
unsigned char ckey[32];
aes_decryption_state *state;
jint key_len = (*env)->GetArrayLength(env, key);
jint iv_len = (*env)->GetArrayLength(env, iv);
if( ! ( key_len == 16 || key_len == 24 || key_len == 32 ) || iv_len != 16 ) {
(*env)->ThrowNew(env, bad_arg, "Invalid length of key or iv");
return -1;
}
state = (aes_decryption_state *)malloc(sizeof(aes_decryption_state));
if( state == NULL ) {
(*env)->ThrowNew(env, no_mem, "Cannot allocate memory for the decryption state");
return -1;
}
memset(state, 0, sizeof(aes_decryption_state));
(*env)->GetByteArrayRegion(env, key, (jint)0, key_len, (jbyte *)ckey);
state->direction = DECRYPTION;
(*env)->GetByteArrayRegion(env, iv, (jint)0, iv_len, (jbyte *)state->iv);
aes_decrypt_key(ckey, key_len, state->ctx);
return (jlong)state;
}
JNIEXPORT void JNICALL Java_com_keepassdroid_crypto_NativeAESCipherSpi_nCleanup(JNIEnv *env, jclass this, jlong state) {
if( state == 0 || state == -1 ) return;
free((void *)state);
}
/*
TODO:
---Performance--- [low priority]
Align memory with posix_memalign() (does Android support this?)
*/
JNIEXPORT jint JNICALL Java_com_keepassdroid_crypto_NativeAESCipherSpi_nEncryptUpdate(JNIEnv *env, jobject this,
jlong state, jbyteArray input, jint inputOffset, jint inputLen, jbyteArray output, jint outputOffset, jint outputSize) {
int32_t outLen, trailing_bytes, input_plus_cache_len;
uint8_t *c_input, *c_output;
aes_encryption_state *c_state;
// step 1: first, some housecleaning
if( !inputLen || state <= 0 ) {
(*env)->ThrowNew(env, bad_arg, "Invalid input length or state");
return -1;
}
c_state = (aes_encryption_state *)state;
if( c_state->direction != ENCRYPTION ) {
(*env)->ThrowNew(env, bad_arg, "Decryption state passed to encryption function");
return -1;
}
input_plus_cache_len = inputLen + c_state->cache_len;
if( input_plus_cache_len < 16 ) {
(*env)->GetByteArrayRegion(env, input, inputOffset, inputLen, (jbyte *)(c_state->cache + c_state->cache_len));
c_state->cache_len = input_plus_cache_len;
return 0;
}
trailing_bytes = input_plus_cache_len & 15; // mask bottom 4 bits
outLen = (input_plus_cache_len - trailing_bytes); // output length is now aligned to a 16-byte boundary
if( outLen > outputSize ) {
(*env)->ThrowNew(env, bad_arg, "Output buffer does not have enough space");
return -1;
}
// step 2: allocate memory to hold input and output data
c_input = (uint8_t *)malloc(input_plus_cache_len);
if( c_input == NULL ) {
(*env)->ThrowNew(env, no_mem, "Unable to allocate heap space for encryption input");
return -1;
}
c_output = (uint8_t *)malloc(outLen);
if( c_output == NULL ) {
free(c_input);
(*env)->ThrowNew(env, no_mem, "Unable to allocate heap space for encryption output");
return -1;
}
// step 3: copy data from Java and encrypt it
if( c_state->cache_len ) {
memcpy(c_input, c_state->cache, c_state->cache_len);
(*env)->GetByteArrayRegion(env, input, inputOffset, inputLen, (jbyte *)(c_input + c_state->cache_len));
} else {
(*env)->GetByteArrayRegion(env, input, inputOffset, inputLen, (jbyte *)c_input);
}
if( aes_cbc_encrypt(c_input, c_output, outLen, c_state->iv, c_state->ctx) != EXIT_SUCCESS ) {
free(c_input);
free(c_output);
(*env)->ThrowNew(env, bad_arg, "Failed to encrypt input data"); // FIXME: get a better exception class for this...
return -1;
}
(*env)->SetByteArrayRegion(env, output, outputOffset, outLen, (jbyte *)c_output);
// step 4: cleanup and return
if( trailing_bytes ) {
c_state->cache_len = trailing_bytes; // set new cache length
memcpy(c_state->cache, (c_input + outLen), trailing_bytes); // cache overflow bytes for next call
} else {
c_state->cache_len = 0;
}
free(c_input);
free(c_output);
return outLen;
}
/*
nDecryptUpdate: decrypt a block of data. The input need not be a multiple of the AES block size.
Parameters:
state - a saved pointer obtained from a call to nDecryptInit()
input - an allocated Java byte[] object containing the input data
inputOffset - encrypted data will be decrypted starting at inputOffset bytes from the beginning of input
inputLen - a limit on how many bytes will be encrypted from input
output - analogous to input
outputOffset - analogous inputOffset
outputSize - a limit to how many bytes will be accepted for output
*/
JNIEXPORT jint JNICALL Java_com_keepassdroid_crypto_NativeAESCipherSpi_nDecryptUpdate(JNIEnv *env, jobject this,
jlong state, jbyteArray input, jint inputOffset, jint inputLen, jbyteArray output, jint outputOffset, jint outputSize) {
int32_t outLen, trailing_bytes, input_plus_cache_len;
uint8_t *c_input, *c_output;
aes_decryption_state *c_state;
// step 1: first, some housecleaning
if( !inputLen || state <= 0 ) {
(*env)->ThrowNew(env, bad_arg, "Invalid input length or state");
return -1;
}
c_state = (aes_decryption_state *)state;
if( c_state->direction != DECRYPTION ) {
(*env)->ThrowNew(env, bad_arg, "Encryption state passed to decryption function");
return -1;
}
input_plus_cache_len = inputLen + c_state->cache_len;
if( input_plus_cache_len < 16 ) {
(*env)->GetByteArrayRegion(env, input, inputOffset, inputLen, (jbyte *)(c_state->cache + c_state->cache_len));
c_state->cache_len = input_plus_cache_len;
return 0;
}
trailing_bytes = input_plus_cache_len & 15; // mask bottom 4 bits
outLen = (input_plus_cache_len - trailing_bytes); // output length is now aligned to a 16-byte boundary
if( outLen > outputSize ) {
(*env)->ThrowNew(env, bad_arg, "Output buffer does not have enough space");
return -1;
}
// step 2: allocate memory to hold input and output data
c_input = (uint8_t *)malloc(input_plus_cache_len);
if( c_input == NULL ) {
(*env)->ThrowNew(env, no_mem, "Unable to allocate heap space for decryption input");
return -1;
}
c_output = (uint8_t *)malloc(outLen);
if( c_output == NULL ) {
free(c_input);
(*env)->ThrowNew(env, no_mem, "Unable to allocate heap space for decryption output");
return -1;
}
// step 3: copy data from Java and decrypt it
if( c_state->cache_len ) {
memcpy(c_input, c_state->cache, c_state->cache_len);
(*env)->GetByteArrayRegion(env, input, inputOffset, inputLen, (jbyte *)(c_input + c_state->cache_len));
} else {
(*env)->GetByteArrayRegion(env, input, inputOffset, inputLen, (jbyte *)c_input);
}
if( aes_cbc_decrypt(c_input, c_output, outLen, c_state->iv, c_state->ctx) != EXIT_SUCCESS ) {
free(c_input);
free(c_output);
(*env)->ThrowNew(env, bad_arg, "Failed to decrypt input data"); // FIXME: get a better exception class for this...
return -1;
}
(*env)->SetByteArrayRegion(env, output, outputOffset, outLen, (jbyte *)c_output);
// step 4: cleanup and return
if( trailing_bytes ) {
c_state->cache_len = trailing_bytes; // set new cache length
memcpy(c_state->cache, (c_input + outLen), trailing_bytes); // cache overflow bytes for next call
} else {
c_state->cache_len = 0;
}
free(c_input);
free(c_output);
return outLen;
}
/*
nEncryptFinal: encrypt any data remaining in the state's block cache and perform PKCS#5 padding
*/
JNIEXPORT jint JNICALL Java_com_keepassdroid_crypto_NativeAESCipherSpi_nEncryptFinal(JNIEnv *env, jobject this,
jlong state, jboolean doPadding, jbyteArray output, jint outputOffset, jint outputSize) {
uint32_t pad;
uint8_t final_output[16] __attribute__ ((aligned (16)));
aes_encryption_state *c_state;
if( outputSize < 16 || state <= 0 ) {
(*env)->ThrowNew(env, bad_arg, "Invalid state or outputSize too small");
return -1;
}
c_state = (aes_encryption_state *)state;
if( c_state->direction != ENCRYPTION ) {
(*env)->ThrowNew(env, bad_arg, "Cannot finalize the passed state identifier");
return -1;
}
// allow fetching of remaining bytes from cache
if( !doPadding ) {
(*env)->SetByteArrayRegion(env, output, outputOffset, c_state->cache_len, (jbyte *)c_state->cache);
c_state->direction = FINALIZED;
return c_state->cache_len;
}
if( c_state->cache_len ) {
pad = 16 - c_state->cache_len;
memset(c_state->cache + c_state->cache_len, pad, pad);
} else {
memset(c_state->cache, 0x10, 16);
}
if( aes_cbc_encrypt(c_state->cache, final_output, 16, c_state->iv, c_state->ctx) != EXIT_SUCCESS ) {
(*env)->ThrowNew(env, bad_arg, "Failed to encrypt final data block"); // FIXME: get a better exception class for this...
return -1;
}
(*env)->SetByteArrayRegion(env, output, outputOffset, 16, (jbyte *)final_output);
c_state->direction = FINALIZED;
return 16;
}
JNIEXPORT jint JNICALL Java_com_keepassdroid_crypto_NativeAESCipherSpi_nDecryptFinal(JNIEnv *env, jobject this,
jlong state, jboolean doPadding, jbyteArray output, jint outputOffset, jint outputSize) {
aes_decryption_state *c_state;
if( outputSize < 16 || state <= 0 ) {
(*env)->ThrowNew(env, bad_arg, "Invalid state or outputSize too small");
return -1;
}
c_state = (aes_decryption_state *)state;
if( c_state->direction != DECRYPTION ) {
(*env)->ThrowNew(env, bad_arg, "Cannot finalize the passed state identifier");
return -1;
}
// allow fetching of remaining bytes from cache
if( !doPadding ) {
(*env)->SetByteArrayRegion(env, output, outputOffset, c_state->cache_len, (jbyte *)c_state->cache);
c_state->direction = FINALIZED;
return c_state->cache_len;
}
// FIXME: is there anything else to do here?
c_state->direction = FINALIZED;
return 0;
}
#define MASTER_KEY_SIZE 32
JNIEXPORT jbyteArray JNICALL Java_com_keepassdroid_crypto_finalkey_NativeFinalKey_nTransformMasterKey(JNIEnv *env, jobject this, jbyteArray seed, jbyteArray key, jint rounds) {
int i, flip = 0;
jbyteArray result;
aes_encrypt_ctx e_ctx[1] __attribute__ ((aligned (16)));
sha256_ctx h_ctx[1] __attribute__ ((aligned (16)));
unsigned char c_seed[MASTER_KEY_SIZE] __attribute__ ((aligned (16)));
unsigned char key1[MASTER_KEY_SIZE] __attribute__ ((aligned (16)));
unsigned char key2[MASTER_KEY_SIZE] __attribute__ ((aligned (16)));
// step 1: housekeeping - sanity checks and fetch data from the JVM
if( (*env)->GetArrayLength(env, seed) != MASTER_KEY_SIZE ) {
(*env)->ThrowNew(env, bad_arg, "TransformMasterKey: the seed is not the correct size");
return NULL;
}
if( (*env)->GetArrayLength(env, key) != MASTER_KEY_SIZE ) {
(*env)->ThrowNew(env, bad_arg, "TransformMasterKey: the key is not the correct size");
return NULL;
}
(*env)->GetByteArrayRegion(env, seed, 0, MASTER_KEY_SIZE, (jbyte *)c_seed);
(*env)->GetByteArrayRegion(env, key, 0, MASTER_KEY_SIZE, (jbyte *)key1);
// step 2: encrypt the hash "rounds" (default: 6000) times
aes_encrypt_key(c_seed, MASTER_KEY_SIZE, e_ctx);
for (i = 0; i < rounds; i++) {
if ( flip ) {
aes_ecb_encrypt(key2, key1, MASTER_KEY_SIZE, e_ctx);
flip = 0;
} else {
aes_ecb_encrypt(key1, key2, MASTER_KEY_SIZE, e_ctx);
flip = 1;
} // if
} // for
// step 3: final SHA256 hash
sha256_begin(h_ctx);
if( flip ) {
sha256_hash(key2, MASTER_KEY_SIZE, h_ctx);
sha256_end(key1, h_ctx);
flip = 0;
} else {
sha256_hash(key1, MASTER_KEY_SIZE, h_ctx);
sha256_end(key2, h_ctx);
flip = 1;
}
// step 4: send the hash into the JVM
result = (*env)->NewByteArray(env, MASTER_KEY_SIZE);
if( flip )
(*env)->SetByteArrayRegion(env, result, 0, MASTER_KEY_SIZE, (jbyte *)key2);
else
(*env)->SetByteArrayRegion(env, result, 0, MASTER_KEY_SIZE, (jbyte *)key1);
return result;
}
#undef MASTER_KEY_SIZE

13
jni/sha/.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
brg_endian.h
brg_types.h
hmac.c
hmac.h
pwd2key.c
pwd2key.h
sha1b.c
sha1.c
sha1.h
sha2b.c
sha2.c
sha2.h
shasum.c

14
jni/sha/Android.mk Normal file
View File

@@ -0,0 +1,14 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := sha
LOCAL_SRC_FILES := \
sha1.c \
sha2.c \
hmac.c
LOCAL_CFLAGS := -DUSE_SHA256
include $(BUILD_STATIC_LIBRARY)

2
jni_root/.gitignore vendored
View File

@@ -1,2 +1,4 @@
openssl-0.9.8l.tar.gz
project
aes-src-29-04-09.zip
sha2-07-01-07.zip

View File

@@ -1,2 +1,4 @@
APP_PROJECT_PATH := $(call my-dir)/project
APP_MODULES := openssl-crypto final-key
APP_PROJECT_PATH := $(call my-dir)
APP_MODULES := aes sha final-key
APP_OPTIM := release
APP_ABI := armeabi armeabi-v7a

View File

@@ -1,302 +0,0 @@
diff -Nru openssl-0.9.8l.orig/Android.mk openssl-0.9.8l/Android.mk
--- openssl-0.9.8l.orig/Android.mk 1969-12-31 18:00:00.000000000 -0600
+++ openssl-0.9.8l/Android.mk 2009-12-02 19:19:38.458446809 -0600
@@ -0,0 +1,3 @@
+# Recursively sources all Android.mk files in subdirs:
+include $(call all-subdir-makefiles)
+
diff -Nru openssl-0.9.8l.orig/crypto/Android.mk openssl-0.9.8l/crypto/Android.mk
--- openssl-0.9.8l.orig/crypto/Android.mk 1969-12-31 18:00:00.000000000 -0600
+++ openssl-0.9.8l/crypto/Android.mk 2009-12-01 21:02:53.490604631 -0600
@@ -0,0 +1,291 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := openssl-crypto
+
+LOCAL_SRC_FILES := \
+ cryptlib.c \
+ dyn_lck.c \
+ mem_clr.c \
+ mem.c \
+ mem_dbg.c \
+ cversion.c \
+ ex_data.c \
+ tmdiff.c \
+ cpt_err.c \
+ ebcdic.c \
+ uid.c \
+ o_time.c \
+ o_str.c \
+ o_dir.c \
+ o_init.c \
+ fips_err.c \
+ aes/aes_misc.c \
+ aes/aes_ecb.c \
+ aes/aes_cfb.c \
+ aes/aes_ofb.c \
+ aes/aes_ctr.c \
+ aes/aes_ige.c \
+ aes/aes_wrap.c \
+ aes/aes_core.c \
+ aes/aes_cbc.c \
+ evp/encode.c \
+ evp/digest.c \
+ evp/dig_eng.c \
+ evp/evp_enc.c \
+ evp/evp_key.c \
+ evp/evp_acnf.c \
+ evp/evp_cnf.c \
+ evp/e_des.c \
+ evp/e_bf.c \
+ evp/e_idea.c \
+ evp/e_des3.c \
+ evp/e_camellia.c \
+ evp/e_rc4.c \
+ evp/e_aes.c \
+ evp/names.c \
+ evp/e_seed.c \
+ evp/e_xcbc_d.c \
+ evp/e_rc2.c \
+ evp/e_cast.c \
+ evp/e_rc5.c \
+ evp/enc_min.c \
+ evp/m_null.c \
+ evp/m_md2.c \
+ evp/m_md4.c \
+ evp/m_md5.c \
+ evp/m_sha.c \
+ evp/m_sha1.c \
+ evp/m_dss.c \
+ evp/m_dss1.c \
+ evp/m_mdc2.c \
+ evp/m_ripemd.c \
+ evp/m_ecdsa.c \
+ evp/p_open.c \
+ evp/p_seal.c \
+ evp/p_sign.c \
+ evp/p_verify.c \
+ evp/p_lib.c \
+ evp/p_enc.c \
+ evp/p_dec.c \
+ evp/bio_md.c \
+ evp/bio_b64.c \
+ evp/bio_enc.c \
+ evp/evp_err.c \
+ evp/e_null.c \
+ evp/c_all.c \
+ evp/c_allc.c \
+ evp/c_alld.c \
+ evp/evp_lib.c \
+ evp/bio_ok.c \
+ evp/evp_pkey.c \
+ evp/evp_pbe.c \
+ evp/p5_crpt.c \
+ evp/p5_crpt2.c \
+ evp/e_old.c \
+ engine/eng_err.c \
+ engine/eng_lib.c \
+ engine/eng_list.c \
+ engine/eng_init.c \
+ engine/eng_ctrl.c \
+ engine/eng_table.c \
+ engine/eng_pkey.c \
+ engine/eng_fat.c \
+ engine/eng_all.c \
+ engine/tb_rsa.c \
+ engine/tb_dsa.c \
+ engine/tb_ecdsa.c \
+ engine/tb_dh.c \
+ engine/tb_ecdh.c \
+ engine/tb_rand.c \
+ engine/tb_store.c \
+ engine/tb_cipher.c \
+ engine/tb_digest.c \
+ engine/eng_openssl.c \
+ engine/eng_cnf.c \
+ engine/eng_dyn.c \
+ engine/eng_cryptodev.c \
+ engine/eng_padlock.c \
+ err/err.c \
+ err/err_def.c \
+ err/err_all.c \
+ err/err_prn.c \
+ err/err_str.c \
+ err/err_bio.c \
+ stack/stack.c \
+ lhash/lhash.c \
+ lhash/lh_stats.c \
+ buffer/buffer.c \
+ buffer/buf_str.c \
+ buffer/buf_err.c \
+ rand/md_rand.c \
+ rand/randfile.c \
+ rand/rand_lib.c \
+ rand/rand_eng.c \
+ rand/rand_err.c \
+ rand/rand_egd.c \
+ rand/rand_win.c \
+ rand/rand_unix.c \
+ rand/rand_os2.c \
+ rand/rand_nw.c \
+ bio/bio_lib.c \
+ bio/bio_cb.c \
+ bio/bio_err.c \
+ bio/bss_mem.c \
+ bio/bss_null.c \
+ bio/bss_fd.c \
+ bio/bss_file.c \
+ bio/bss_sock.c \
+ bio/bss_conn.c \
+ bio/bf_null.c \
+ bio/bf_buff.c \
+ bio/b_print.c \
+ bio/b_dump.c \
+ bio/b_sock.c \
+ bio/bss_acpt.c \
+ bio/bf_nbio.c \
+ bio/bss_log.c \
+ bio/bss_bio.c \
+ bio/bss_dgram.c \
+ objects/o_names.c \
+ objects/obj_dat.c \
+ objects/obj_lib.c \
+ objects/obj_err.c \
+ sha/sha_dgst.c \
+ sha/sha1dgst.c \
+ sha/sha_one.c \
+ sha/sha1_one.c \
+ sha/sha256.c \
+ sha/sha512.c \
+ rsa/rsa_eay.c \
+ rsa/rsa_gen.c \
+ rsa/rsa_lib.c \
+ rsa/rsa_sign.c \
+ rsa/rsa_saos.c \
+ rsa/rsa_err.c \
+ rsa/rsa_pk1.c \
+ rsa/rsa_ssl.c \
+ rsa/rsa_none.c \
+ rsa/rsa_oaep.c \
+ rsa/rsa_chk.c \
+ rsa/rsa_null.c \
+ rsa/rsa_pss.c \
+ rsa/rsa_x931.c \
+ rsa/rsa_x931g.c \
+ rsa/rsa_asn1.c \
+ rsa/rsa_depr.c \
+ rsa/rsa_eng.c \
+ bn/bn_add.c \
+ bn/bn_div.c \
+ bn/bn_exp.c \
+ bn/bn_lib.c \
+ bn/bn_ctx.c \
+ bn/bn_mul.c \
+ bn/bn_mod.c \
+ bn/bn_print.c \
+ bn/bn_rand.c \
+ bn/bn_shift.c \
+ bn/bn_word.c \
+ bn/bn_blind.c \
+ bn/bn_kron.c \
+ bn/bn_sqrt.c \
+ bn/bn_gcd.c \
+ bn/bn_prime.c \
+ bn/bn_err.c \
+ bn/bn_sqr.c \
+ bn/bn_asm.c \
+ bn/bn_recp.c \
+ bn/bn_mont.c \
+ bn/bn_mpi.c \
+ bn/bn_exp2.c \
+ bn/bn_gf2m.c \
+ bn/bn_nist.c \
+ bn/bn_depr.c \
+ bn/bn_x931p.c \
+ bn/bn_const.c \
+ bn/bn_opt.c \
+ asn1/a_object.c \
+ asn1/a_bitstr.c \
+ asn1/a_utctm.c \
+ asn1/a_gentm.c \
+ asn1/a_time.c \
+ asn1/a_int.c \
+ asn1/a_octet.c \
+ asn1/a_print.c \
+ asn1/a_type.c \
+ asn1/a_set.c \
+ asn1/a_dup.c \
+ asn1/a_d2i_fp.c \
+ asn1/a_i2d_fp.c \
+ asn1/a_enum.c \
+ asn1/a_utf8.c \
+ asn1/a_sign.c \
+ asn1/a_digest.c \
+ asn1/a_verify.c \
+ asn1/a_mbstr.c \
+ asn1/a_strex.c \
+ asn1/x_algor.c \
+ asn1/x_val.c \
+ asn1/x_pubkey.c \
+ asn1/x_sig.c \
+ asn1/x_req.c \
+ asn1/x_attrib.c \
+ asn1/x_bignum.c \
+ asn1/x_long.c \
+ asn1/x_name.c \
+ asn1/x_x509.c \
+ asn1/x_x509a.c \
+ asn1/x_crl.c \
+ asn1/x_info.c \
+ asn1/x_spki.c \
+ asn1/nsseq.c \
+ asn1/d2i_pu.c \
+ asn1/d2i_pr.c \
+ asn1/i2d_pu.c \
+ asn1/i2d_pr.c \
+ asn1/t_req.c \
+ asn1/t_x509.c \
+ asn1/t_x509a.c \
+ asn1/t_crl.c \
+ asn1/t_pkey.c \
+ asn1/t_spki.c \
+ asn1/t_bitst.c \
+ asn1/tasn_new.c \
+ asn1/tasn_fre.c \
+ asn1/tasn_enc.c \
+ asn1/tasn_dec.c \
+ asn1/tasn_utl.c \
+ asn1/tasn_typ.c \
+ asn1/f_int.c \
+ asn1/f_string.c \
+ asn1/n_pkey.c \
+ asn1/f_enum.c \
+ asn1/a_hdr.c \
+ asn1/x_pkey.c \
+ asn1/a_bool.c \
+ asn1/x_exten.c \
+ asn1/asn_mime.c \
+ asn1/asn1_gen.c \
+ asn1/asn1_par.c \
+ asn1/asn1_lib.c \
+ asn1/asn1_err.c \
+ asn1/a_meth.c \
+ asn1/a_bytes.c \
+ asn1/a_strnid.c \
+ asn1/evp_asn1.c \
+ asn1/asn_pack.c \
+ asn1/p5_pbe.c \
+ asn1/p5_pbev2.c \
+ asn1/p8_pkey.c \
+ asn1/asn_moid.c \
+
+
+
+
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. $(LOCAL_PATH)/../include
+
+LOCAL_CFLAGS := -DNO_WINDOWS_BRAINDEATH
+
+include $(BUILD_STATIC_LIBRARY)

View File

@@ -1,10 +1,12 @@
#!/bin/sh
FILE="openssl-0.9.8l.tar.gz"
http://gladman.plushost.co.uk/oldsite/AES/
AES_FILE="aes-src-29-04-09.zip"
SHA_FILE="sha2-07-01-07.zip"
EXTRACT_PATH=project/jni/
PATCH=build.patch
ln -s ../ project
curl http://www.openssl.org/source/$FILE > $FILE
tar xzf $FILE -C $EXTRACT_PATH
patch -p1 -d $EXTRACT_PATH/openssl-0.9.8l < $PATCH
curl http://gladman.plushost.co.uk/oldsite/AES/$AES_FILE > $AES_FILE
unzip $AES_FILE -d $EXTRACT_PATH/aes
curl http://gladman.plushost.co.uk/oldsite/cryptography_technology/sha/$SHA_FILE > $SHA_FILE
unzip $SHA_FILE -d $EXTRACT_PATH/sha