I have finished a AES encryption wrapper written in C using OpenSSL library. I used this wrapper in my Android project. When I called the encrypt function hundred of times to encrypt a lot of small files (images) it caused the crash error:
02-06 14:39:44.110: A/libc(5114): ### ABORTING: INVALID HEAP ADDRESS IN dlfree
02-06 14:39:44.110: A/libc(5114): Fatal signal 11 (SIGSEGV) at 0xdeadbaad (code=1)
This is memory leak error but I can't figure out myself. I guess something went wrong in my native code. Below is my encrypt function written in C using OpenSSL library. (Compiled by NDK)
Function init encrypt key
int aes_init_encrypt(unsigned char *key_data, int key_data_len, unsigned char *salt, EVP_CIPHER_CTX *e_ctx)
{
int i, nrounds = 4;
unsigned char key[16], iv[16];
/*
* Gen key & IV for AES 128 CBC mode. A SHA1 digest is used to hash the supplied key material.
* nrounds is the number of times the we hash the material. More rounds are more secure but
* slower.
*/
i = EVP_BytesToKey(EVP_aes_128_cbc(), EVP_sha1(), salt, key_data, key_data_len, nrounds, key, iv);
if (i != 16)
{
//__android_log_write(ANDROID_LOG_ERROR, "TamNV-Encryption", "Key size is %d bits - should be 128 bits\n");
return -1;
}
EVP_CIPHER_CTX_init(e_ctx);
EVP_EncryptInit_ex(e_ctx, EVP_aes_128_cbc(), NULL, key, iv);
return 0;
}
Function encrypt
jint Java_com_openssl_aes_WrapperAES_encryptAES(JNIEnv *env, jobject obj, jstring password, jstring source, jstring destination)
{
FILE* source_file;
FILE* destination_file;
const char *source_path = (*env)->GetStringUTFChars(env, source, NULL);
const char *destination_path = (*env)->GetStringUTFChars(env, destination, NULL);
source_file = fopen(source_path, "rb");
destination_file = fopen(destination_path, "wb");
if (source_file == NULL || destination_file == NULL) {
return -1;
}
// Prepare to encrypt
// 1. Get password
const char *pass = (*env)->GetStringUTFChars(env, password, NULL);
unsigned char *key_data = malloc(strlen(pass));
if (key_data)
strcpy(key_data, pass);
else
return -1;
// 2. Init EVP_CIPHER
EVP_CIPHER_CTX e_ctx;
if (aes_init_encrypt(key_data, strlen(key_data), key_data, &e_ctx) == -1)
return -1;
/* Buffers */
unsigned char inbuf[BUFFER_SIZE];
int inlen;
/* Allow enough space in output buffer for additional cipher block */
unsigned char outbuf[BUFFER_SIZE + AES_BLOCK_SIZE];
int outlen;
int writelen;
while ((inlen = fread(inbuf, 1, BUFFER_SIZE, source_file)) > 0) {
// fwrite(inbuf, 1, inlen, destination_file);
if (!EVP_CipherUpdate(&e_ctx, outbuf, &outlen, inbuf, inlen)) {
/* Error */
EVP_CIPHER_CTX_cleanup(&e_ctx);
return -1;
}
writelen = fwrite(outbuf, sizeof(*outbuf), outlen, destination_file);
if (writelen != outlen) {
/* Error */
EVP_CIPHER_CTX_cleanup(&e_ctx);
return -1;
}
}
/* Handle remaining cipher block + padding */
if (!EVP_CipherFinal_ex(&e_ctx, outbuf, &outlen)) {
/* Error */
EVP_CIPHER_CTX_cleanup(&e_ctx);
return -1;
}
/* Write remainign cipher block + padding*/
fwrite(outbuf, sizeof(*inbuf), outlen, destination_file);
EVP_CIPHER_CTX_cleanup(&e_ctx);
fclose(source_file);
fclose(destination_file);
free(key_data);
// __android_log_print(ANDROID_LOG_DEBUG, "TamNV","Encrypt successfully --- %s", source_path);
return 0;
}
What's wrong with my native code? Any help will be much appreciated!
Related
Description of the intended goal
I'm trying to implement OpenSSL-generated public/private key pairs in Android/Kotlin using JNI, in order to implement user encryption on the information stored to the cloud server. I've compiled all OpenSSL source code and generated all .so files correcly.
The code (C++)
The C++ code to use OpenSSL is shown below. CmakeLists.txt and NDK configuration is working fine.
extern "C"
JNIEXPORT jobjectArray JNICALL
Java_eu_joober_ui_entry_SplashFragment_generateRSAKeyPair(JNIEnv *env, jobject thiz) {
int ret = 0;
RSA *r = nullptr;
BIGNUM *bne = nullptr;
BIO *bp_public = nullptr, *bp_private = nullptr;
int bits = 2048;
unsigned long e = RSA_F4;
jstring public_key_text;
jstring private_key_text;
jobjectArray returnPair = env->NewObjectArray(2, env->FindClass("java/lang/String"),nullptr);
// 1. generate rsa key
bne = BN_new();
ret = BN_set_word(bne,e);
if(ret != 1){
goto free_all;
}
r = RSA_new();
ret = RSA_generate_key_ex(r, bits, bne, nullptr);
if(ret != 1){
goto free_all;
}
// 2. get public key
bp_public = BIO_new(BIO_s_mem());
ret = PEM_write_bio_RSAPublicKey(bp_public, r);
BIO_get_mem_data(bp_public, &public_key_text);
if(ret != 1){
goto free_all;
}
// 3. get private key
bp_private = BIO_new(BIO_s_mem());
ret = PEM_write_bio_RSAPrivateKey(bp_private, r, nullptr, nullptr, 0, nullptr, nullptr);
BIO_get_mem_data(bp_private, &private_key_text);
// Check public and private keys were generated correctly
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "Public key is: \n%s",public_key_text);
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "Private key is: \n%s",private_key_text);
// 4. free
free_all:
BIO_free_all(bp_public);
BIO_free_all(bp_private);
RSA_free(r);
BN_free(bne);
// 5. Return strings using jobjectArray
if (ret == 1) {
env->SetObjectArrayElement(returnPair, 0, public_key_text);
env->SetObjectArrayElement(returnPair, 1, private_key_text);
return returnPair;
}
else {
return nullptr;
}
}
The error
If I check the Android Logcat, both public and private key seem to be generated correctly (as per __android_log_print output) but the app crashes with the following error when env->SetObjectArrayElement(returnPair, 0, public_key_text); is called:
JNI DETECTED ERROR IN APPLICATION: use of invalid jobject
The IDE (Android Studio) does not complain on any error, and the log suggests that key pair is being generated correctly, but I don't know why the keys are not being stored in the jobjectArray correctly. In fact, if I just simply put:
env->SetObjectArrayElement(returnPair, 0, env->NewStringUTF("Hello"));
env->SetObjectArrayElement(returnPair, 1, env->NewStringUTF("World"));
the code works completely fine, my Kotlin code gets the Strings correctly ("Hello" and "World"), and the app does not crash, which makes me think that problem is only on the C++ side.
The question
What I am doing wrong? I have checked other SO questions like JNI converting jstring to char * or jstring return in JNI program with slight modifications and combinations with no luck.
SIDE NOTE: I'm using OpenSSL implementation with C++ because Android/Kotlin code does not provide the private key generated using KeyPairGenerator.getInstance() and generatePair() (only public key can be retrieved from Keystore), which I need to be stored in a different place so that user information can be retrieved even if the app is uninstalled, as every subsequent call to generatePair() will lead to a different key pair. If you know a different approach to this problem I am more than welcome to any suggestions you may provide.
You never created a java string out of public_key_text
Try
char * public_key_text;
...
BIO_get_mem_data(bp_public, &public_key_text);
...
env->SetObjectArrayElement(returnPair, 0, env->NewStringUTF(public_key_text));
When our app is getting a lot of traffic through JNI (hundreds of elements) seems like we are getting a lot of heap corruption errors (seems like it happens more for bigger elements).
abort 0x0000007e32cdf360
art::Runtime::Abort(char const*) 0x0000007daf4c22ac
android::base::LogMessage::~LogMessage() 0x0000007e33a6a654
art::gc::Verification::LogHeapCorruption(art::ObjPtr<art::mirror::Object>, art::MemberOffset, art::mirror::Object*, bool) const 0x0000007daf298318
art::gc::collector::ConcurrentCopying::MarkNonMoving(art::Thread*, art::mirror::Object*, art::mirror::Object*, art::MemberOffset) 0x0000007daf226b98
art::gc::collector::ConcurrentCopying::ThreadFlipVisitor::VisitRoots(art::mirror::CompressedReference<art::mirror::Object>**, unsigned long, art::RootInfo const&) 0x0000007daf22909c
art::Thread::HandleScopeVisitRoots(art::RootVisitor*, int) 0x0000007daf50af7c
void art::Thread::VisitRoots<false>(art::RootVisitor*) 0x0000007daf50e840
art::gc::collector::ConcurrentCopying::ThreadFlipVisitor::Run(art::Thread*) 0x0000007daf22870c
art::(anonymous namespace)::CheckJNI::ReleasePrimitiveArrayElements(char const*, art::Primitive::Type, _JNIEnv*, _jarray*, void*, int) 0x0000007daf37c680
Java_org_libsodium_jni_SodiumJNI_crypto_1aead_1xchacha20poly1305_1ietf_1decrypt sodium-jni.c:156
art_quick_generic_jni_trampoline 0x0000007daf148354
<unknown> 0x000000009d05bbe8
Seems like the line causing the is located here (our code is open source) https://github.com/standardnotes/react-native-sodium/blob/367b61a90180fe75ddef5b599e01c47cb4761b1f/android/src/main/cpp/sodium-jni.c#L156. I've tried to debug this more but my JNI + CPP knowledge is limited. Do you have any tips for exchanging data from Java to C++ in a better way?
Code snippet:
JNIEXPORT jint JNICALL
Java_org_libsodium_jni_SodiumJNI_crypto_1aead_1xchacha20poly1305_1ietf_1decrypt(JNIEnv *jenv,
jclass clazz,
jbyteArray j_m,
jintArray j_mlen_p,
jbyteArray j_nsec,
jbyteArray j_c,
jint j_clen,
jbyteArray j_ad,
jint j_adlen,
jbyteArray j_npub,
jbyteArray j_k) {
unsigned char *c = as_unsigned_char_array(jenv, j_c);
unsigned char *m = (unsigned char *) (*jenv)->GetByteArrayElements(jenv, j_m, 0);
unsigned char *npub = as_unsigned_char_array(jenv, j_npub);
unsigned char *ad = as_unsigned_char_array(jenv, j_ad);
unsigned char *nsec = as_unsigned_char_array(jenv, j_nsec);
unsigned char *k = as_unsigned_char_array(jenv, j_k);
int result = crypto_aead_xchacha20poly1305_ietf_decrypt(m, j_mlen_p, nsec, c, j_clen, ad, j_adlen, npub, k);
(*jenv)->ReleaseByteArrayElements(jenv, j_m, (jbyte *) m, 0);
return (jint)result;
}
Calling from java:
#ReactMethod
public void crypto_aead_xchacha20poly1305_ietf_decrypt(final String cipherText, final String public_nonce, final String key, final String additionalData, final Promise p) {
try {
byte[] c = this.base64ToBin(cipherText, Sodium.base64_variant_ORIGINAL());
byte[] npub = this.hexToBin(public_nonce);
byte[] k = this.hexToBin(key);
if (c == null || c.length <= 0)
p.reject(ESODIUM,ERR_FAILURE);
else if (npub.length != Sodium.crypto_aead_xchacha20poly1305_IETF_NPUBBYTES())
p.reject(ESODIUM,ERR_BAD_NONCE);
else if (k.length != Sodium.crypto_aead_xchacha20poly1305_IETF_KEYBYTES())
p.reject(ESODIUM,ERR_BAD_KEY);
else {
byte[] ad = additionalData != null ? additionalData.getBytes(StandardCharsets.UTF_8) : null;
int adlen = additionalData != null ? ad.length : 0;
int[] decrypted_len = new int[1];
byte[] decrypted = new byte[c.length - Sodium.crypto_aead_chacha20poly1305_IETF_ABYTES()];
int result = Sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(decrypted, decrypted_len, null, c, c.length, ad, adlen, npub, k);
if (result != 0)
p.reject(ESODIUM,ERR_FAILURE);
else
p.resolve(new String(decrypted, StandardCharsets.UTF_8));
}
}
catch (Throwable t) {
p.reject(ESODIUM,ERR_FAILURE,t);
}
}
Seems like it happens for bigger elements most of the time, but not always. Also happens for crypto_1aead_1xchacha20poly1305_1ietf_1encrypt.
ReleasePrimitiveArrayElements means ->ReleaseByteArrayElements().
The issue is likely that you're referring to JNIEnv* and at some point it detaches from the thread (the processing time would be rather interesting). You'd need to obtain JNIEnv* differently, eg. alike AttachCurrentThreadIfNeeded(). Also see JNI threads.
I use superpowered, I need send midi note to a controller midi.
The problem is that I saw a function send(int deviceID, unsigned char *data, int bytes);
Where in their source code say:
deviceID: Device identifier.
data: Raw MIDI data.
bytes: Number of
bytes.
I don't know the values that I need put exactly on data and bytes to work.
The raw midi could be 0x80 - 0x48 - 0x00(start of C4 note, pitch= 72, See values)
And the bytes 1001nnnn0kkkkkkk0kkkkkkk(note on event See values) for example?
Something like that:
SuperpoweredUSBMIDI::send(deviceID, reinterpret_cast(0x80 - 0x48 - 0x00), 1001nnnn0kkkkkkk0kkkkkkk);
The problem that always crash, and I can't debug or get the error for the reason that I use the mobile with otg to replicate the error.
When I find a solution, I will put it as soon as I can.
I'm newbie with markdown, sorry for any mistakes and my English grammar.
Edit: I'm using the example project that they have on GitHub for testing purposes, specifically the simpleusb project. (source)
I make a small modifications and work, but with this specifically I try with many ways and nothing. I think this simple macrochange at least could work if I insert well the values
class simpleusb.cpp:
#include <jni.h>
#include <math.h>
#include <SuperpoweredCPU.h>
#include <AndroidIO/SuperpoweredUSBAudio.h>
#include <malloc.h>
#include <pthread.h>
// Called when the application is initialized. You can initialize SuperpoweredUSBSystem
// at any time btw. Although this function is marked __unused, it's due Android Studio's
// annoying warning only. It's definitely used.
__unused jint JNI_OnLoad (
JavaVM * __unused vm,
void * __unused reserved
) {
SuperpoweredUSBSystem::initialize(NULL, NULL, NULL, NULL, NULL);
return JNI_VERSION_1_6;
}
// Called when the application is closed. You can destroy SuperpoweredUSBSystem at any time btw.
// Although this function is marked __unused, it's due Android Studio's annoying warning only.
// It's definitely used.
__unused void JNI_OnUnload (
JavaVM * __unused vm,
void * __unused reserved
) {
SuperpoweredUSBSystem::destroy();
}
// A helper structure for sine wave output.
typedef struct sineWaveOutput {
float mul;
unsigned int step;
} sineWaveOutput;
// This is called periodically for audio I/O. Audio is always 32-bit floating point,
// regardless of the bit depth preference. (SuperpoweredUSBAudioProcessingCallback)
static bool audioProcessing (
void *clientdata,
int __unused deviceID,
float *audioIO,
int numberOfSamples,
int samplerate,
int __unused numInputChannels,
int numOutputChannels
) {
// If audioIO is NULL, then it's the very last call, IO is closing.
if (!audioIO) {
// Free memory for sine wave struct.
free(clientdata);
return true;
}
sineWaveOutput *swo = (sineWaveOutput *)clientdata;
if (swo->mul == 0.0f) swo->mul = (2.0f * float(M_PI) * 300.0f) / float(samplerate);
// Output sine wave on all output channels.
for (int n = 0; n < numberOfSamples; n++) {
float v = sinf(swo->step++ * swo->mul) * 0.5f;
for (int c = 0; c < numOutputChannels; c++) *audioIO++ = v;
}
return true; // Return false for silence, true if we put audio output into audioIO.
}
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static int latestMidiCommand = -1;
static int latestMidiChannel = 0;
static int latestMidiNumber = 0;
static int latestMidiValue = 0;
// This is called when some MIDI data is coming in.
// We are doing some primitive MIDI data processing here.
static void onMidiReceived (
void * __unused clientdata,
int __unused deviceID,
unsigned char *data,
int bytes
) {
while (bytes > 0) {
if (*data > 127) {
int command = *data >> 4;
switch (command) {
case 8: // note off
case 9: // note on
case 11: // control change
pthread_mutex_lock(&mutex);
// store incoming MIDI data
latestMidiCommand = command;
latestMidiChannel = *data++ & 15;
latestMidiNumber = *data++;
latestMidiValue = *data++;
pthread_mutex_unlock(&mutex);
bytes -= 3;
break;
default:
data++;
bytes--;
}
} else {
data++;
bytes--;
}
}
}
// Beautifying the ugly Java-C++ bridge (JNI) with these macros.
#define PID com_superpowered_simpleusb_SuperpoweredUSBAudio // Java package name and class name. Don't forget to update when you copy this code.
#define MAKE_JNI_FUNCTION(r, n, p) extern "C" JNIEXPORT r JNICALL Java_ ## p ## _ ## n
#define JNI(r, n, p) MAKE_JNI_FUNCTION(r, n, p)
// This is called by the SuperpoweredUSBAudio Java object when a USB device is connected.
JNI(jint, onConnect, PID) (
JNIEnv *env,
jobject __unused obj,
jint deviceID,
jint fd,
jbyteArray rawDescriptor
) {
jbyte *rd = env->GetByteArrayElements(rawDescriptor, NULL);
int dataBytes = env->GetArrayLength(rawDescriptor);
int r = SuperpoweredUSBSystem::onConnect(deviceID, fd, (unsigned char *)rd, dataBytes);
env->ReleaseByteArrayElements(rawDescriptor, rd, JNI_ABORT);
// r is 0 if SuperpoweredUSBSystem can't do anything with the connected device.
// r & 2 is true if the device has MIDI. Start receiving events.
if (r & 2) {
SuperpoweredUSBMIDI::startIO(deviceID, NULL, onMidiReceived);
//TODO HERE IT'S THE PROBLEM: error: integer literal is too large to be represented in any integer type
SuperpoweredUSBMIDI::send(deviceID, reinterpret_cast<unsigned char *>(0x80 - 0x48 - 0x00), 100100010011100000000011);
//FINISH PROBLEM
}
// r & 1 is true if the device has audio. Start output.
if (r & 1) {
// allocate struct for sine wave oscillator
sineWaveOutput *swo = (sineWaveOutput *)malloc(sizeof(sineWaveOutput));
if (swo) {
swo->mul = 0.0f;
swo->step = 0;
SuperpoweredCPU::setSustainedPerformanceMode(true);
// Our preferred settings: 44100 Hz, 16 bits, 0 input channels, 256 output channels,
// low latency. Superpowered will set up the audio device as close as it can to these.
SuperpoweredUSBAudio::easyIO (
deviceID, // deviceID
44100, // sampling rate
16, // bits per sample
0, // numInputChannels
256, // numOutputChannels
SuperpoweredUSBLatency_Low, // latency
swo, // clientData
audioProcessing // SuperpoweredUSBAudioProcessingCallback
);
}
}
return r;
}
// This is called by the SuperpoweredUSBAudio Java object when a USB device is disconnected.
JNI(void, onDisconnect, PID) (
JNIEnv * __unused env,
jobject __unused obj,
jint deviceID
) {
SuperpoweredUSBSystem::onDisconnect(deviceID);
SuperpoweredCPU::setSustainedPerformanceMode(false);
}
#undef PID
#define PID com_superpowered_simpleusb_MainActivity
// This is called by the MainActivity Java object periodically.
JNI(jintArray, getLatestMidiMessage, PID) (
JNIEnv *env,
jobject __unused obj
) {
jintArray ints = env->NewIntArray(4);
jint *i = env->GetIntArrayElements(ints, 0);
pthread_mutex_lock(&mutex);
i[0] = latestMidiCommand;
i[1] = latestMidiChannel;
i[2] = latestMidiNumber;
i[3] = latestMidiValue;
pthread_mutex_unlock(&mutex);
env->ReleaseIntArrayElements(ints, i, 0);
return ints;
}
The other important class but I don't change on this problem, MainActivity:
#RequiresApi(api = Build.VERSION_CODES.M)
public class MainActivity extends AppCompatActivity implements SuperpoweredUSBAudioHandler {
private Handler handler;
private TextView textView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text);
SuperpoweredUSBAudio usbAudio = new SuperpoweredUSBAudio(getApplicationContext(), this);
usbAudio.check();
// Update UI every 40 ms.
Runnable runnable = new Runnable() {
#Override
public void run() {
int[] midi = getLatestMidiMessage();
switch (midi[0]) {
case 8: textView.setText(String.format(Locale.ENGLISH, "Note Off, CH %d, %d, %d",
midi[1] + 1, midi[2], midi[3]));
break;
case 9: textView.setText(String.format(Locale.ENGLISH, "Note On, CH %d, %d, %d",
midi[1] + 1, midi[2], midi[3]));
break;
case 11: textView.setText(String.format(Locale.ENGLISH, "Control Change, CH %d, %d, %d",
midi[1] + 1, midi[2], midi[3]));
break;
}
handler.postDelayed(this, 40);
}
};
handler = new Handler();
handler.postDelayed(runnable, 40);
/*Not look, only for testing purposes and for remember what use.
byte[] buffer = new byte[32];
int numBytes = 0;
int channel = 6; // MIDI channels 1-16 are encoded as 0-15.
buffer[numBytes++] = (byte)(0x90 + (channel - 1)); // note on
buffer[numBytes++] = (byte)60; // pitch is middle C
buffer[numBytes++] = (byte)127; // max velocity
int offset = 0;*/
}
public void onUSBAudioDeviceAttached(int deviceIdentifier) {
}
public void onUSBMIDIDeviceAttached(int deviceIdentifier) {
}
public void onUSBDeviceDetached(int deviceIdentifier) {
}
// Function implemented in the native library.
private native int[] getLatestMidiMessage();
static {
System.loadLibrary("SuperpoweredExample");
}
}
Error that I can't build app finally:
Build command failed.
Error while executing process D:\Users\ramoc\AppData\Local\Android\sdk\cmake\3.6.4111459\bin\cmake.exe with arguments {--build F:\PROYECTOFIN\SuperpoweredUSBExample\simpleusb\.externalNativeBuild\cmake\debug\arm64-v8a --target SuperpoweredExample}
[1/2] Building CXX object CMakeFiles/SuperpoweredExample.dir/simpleusb.cpp.o
FAILED: D:\Users\ramoc\AppData\Local\Android\sdk\ndk-bundle\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++.exe --target=aarch64-none-linux-android --gcc-toolchain=D:/Users/ramoc/AppData/Local/Android/sdk/ndk-bundle/toolchains/aarch64-linux-android-4.9/prebuilt/windows-x86_64 --sysroot=D:/Users/ramoc/AppData/Local/Android/sdk/ndk-bundle/sysroot -DSuperpoweredExample_EXPORTS -IF:/PROYECTOFIN/SuperpoweredUSBExample/simpleusb/src/main/jni/src/main/jni -IF:/PROYECTOFIN/SuperpoweredUSBExample/simpleusb/../../../Superpowered -isystem D:/Users/ramoc/AppData/Local/Android/sdk/ndk-bundle/sources/cxx-stl/gnu-libstdc++/4.9/include -isystem D:/Users/ramoc/AppData/Local/Android/sdk/ndk-bundle/sources/cxx-stl/gnu-libstdc++/4.9/libs/arm64-v8a/include -isystem D:/Users/ramoc/AppData/Local/Android/sdk/ndk-bundle/sources/cxx-stl/gnu-libstdc++/4.9/include/backward -isystem D:/Users/ramoc/AppData/Local/Android/sdk/ndk-bundle/sysroot/usr/include/aarch64-linux-android -D__ANDROID_API__=21 -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -Wa,--noexecstack -Wformat -Werror=format-security -fsigned-char -IF:\PROYECTOFIN\SuperpoweredUSBExample\simpleusb\..\..\..\Superpowered -O0 -fno-limit-debug-info -fPIC -MD -MT CMakeFiles/SuperpoweredExample.dir/simpleusb.cpp.o -MF CMakeFiles\SuperpoweredExample.dir\simpleusb.cpp.o.d -o CMakeFiles/SuperpoweredExample.dir/simpleusb.cpp.o -c F:\PROYECTOFIN\SuperpoweredUSBExample\simpleusb\src\main\jni\simpleusb.cpp
F:\PROYECTOFIN\SuperpoweredUSBExample\simpleusb\src\main\jni\simpleusb.cpp:129:100: error: integer literal is too large to be represented in any integer type
SuperpoweredUSBMIDI::send(deviceID, reinterpret_cast<unsigned char *>(0x80 - 0x48 - 0x00), 100100010011100000000011);
^
F:\PROYECTOFIN\SuperpoweredUSBExample\simpleusb\src\main\jni\simpleusb.cpp:129:100: warning: implicit conversion from 'unsigned long long' to 'int' changes value from 7976667151972931595 to 887068683 [-Wconstant-conversion]
SuperpoweredUSBMIDI::send(deviceID, reinterpret_cast<unsigned char *>(0x80 - 0x48 - 0x00), 100100010011100000000011);
~~~~~~~~~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~~~
1 warning and 1 error generated.
ninja: build stopped: subcommand failed.
Maybe it's for the documentation, very newbie with jni or too complex to me for now to understand 100%.
Ok, so here's what send is saying:
send(int deviceID, unsigned char *data, int bytes);
Send to deviceId a pointer to a buffer called data that has a certain number of bytes.
So when you say:
SuperpoweredUSBMIDI::send(deviceID, reinterpret_cast(0x80 - 0x48 - 0x00), 100100010011100000000011);
What you are essentially saying is "subtract these 3 numbers: 0x80 - 0x48 - 0x00", then re-interpret that number as a pointer to a buffer somewhere in memory. That buffer in memory contains 100100010011100000000011 bytes of data that I want you to read.
To fix this, we would send the data like this:
unsigned char* send_buffer[32] = {0}; // zero out buffer to use as scratch
send_buffer[0] = 0x90;
send_buffer[1] = 0x48;
send_buffer[2] = 0x00;
SuperpoweredUSBMIDI::send(deviceID, send_buffer, 3);
i thought midi had a check sum value (byte) appended to the sequence - is that done in your code or in the library code?
the message should be an array of unsigned char and pass the address of the array (name)
well that's what id have done in C when I was programming midi.
I've implemented a C native function with for android NDK, to send UDP packets.
I have a working receiver but i seem to get nothing when i use the sender.
How can i get more info from the return value of sendto? I'm having a hard time debugging native functions - no "debug step mode"
can anyone see anything wrong about the sender code? is there something im not doing right?
Thanks!
jstring
Java_com_example_adhocktest_SenderUDP_SendUdpJNI( JNIEnv* env,
jobject thiz, jstring ip, jstring message)
{
int PORT = 8888;
int i;
int sock_fd;
char *_ip = (*env)->GetStringUTFChars(env, ip, 0);
char *send_buf = (*env)->GetStringUTFChars(env, message, 0);
////////////////
//////// create socket
////////////////
if (( sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
return (*env)->NewStringUTF(env,"Cannot create socket");
}
struct sockaddr_in myaddr;
////////////////
//////// send
////////////////
struct sockaddr_in servaddr;
memset((char*)&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = 8888;
if ((inet_aton(_ip,&servaddr.sin_addr)) == 0) {
return (*env)->NewStringUTF(env,"Cannot decode IP address");
}
int retval = sendto(sock_fd, send_buf, strlen(send_buf), 0, (struct sockaddr*)&servaddr, sizeof(servaddr));
close(sock_fd);
char str[100];
if ( retval < 0) {
sprintf(str, "sendto failed with %d", retval);
} else {
sprintf(str, "sendto success with %d", retval);
}
return (*env)->NewStringUTF(env,str);
}
The problem in the code was this line
servaddr.sin_port = 8888;
the correct line would be
servaddr.sin_port = htons(8888);
the reason for this was explained here by
bornruffians:
" htons() stands for "host-to-network short". On a given platform
(called the host), it converts the endianness of a short (16-bit
generally) integer to the endianness required for sending on the
network (generally big endian).
sendto() returns the number of bytes sent. You should check that
retval is the string length of your 'send_buf' variable, not just a
positive value."
Thanks all for your help,
Ben.
If it's still not working, did you give your app the necessary permissions for manipulating sockets?
<uses-permission android:name="android.permission.INTERNET" />
htons() stands for "host-to-network short". On a given platform (called the host), it converts the endianness of a short (16-bit generally) integer to the endianness required for sending on the network (generally big endian).
sendto() returns the number of bytes sent. You should check that retval is the string length of your 'send_buf' variable, not just a positive value.
I am trying to do aes encryption/decryption in native code C. Encryption does work but when I try to decrypt the string. It doesn't end up as original string. Here is the JNI method which does encrypt/decrpt based on mode param:
jbyteArray Java_com_example_hellojni_HelloJni_encrypt( JNIEnv* env,
jobject this,
jbyteArray srcData,
jint mode)
{
// get length of bytes
int srcLen=(*env)->GetArrayLength(env,srcData);
//convert jbyteArray to byte []
jbyte data[srcLen];
(*env)->GetByteArrayRegion(env, srcData, 0, srcLen, data);
(*env)->ReleaseByteArrayElements(env, srcData,data , 0);
unsigned char* indata=(unsigned char*)data;
const unsigned char ukey[] = { 'H','A','R','D','C','O','D','E','D',' ','K','E','Y','1','2','3'};
unsigned char *outdata = NULL;
outdata = malloc(srcLen);
AES_KEY key;
memset(&key, 0, sizeof(AES_KEY));
if(mode == AES_ENCRYPT)
AES_set_encrypt_key(ukey, 128, &key);
else
AES_set_decrypt_key(ukey, 128, &key);
AES_ecb_encrypt(indata, outdata, &key, mode);
jbyteArray bArray = (*env)->NewByteArray(env, srcLen);
jboolean isCopy;
void *decrypteddata = (*env)->GetPrimitiveArrayCritical(env, (jarray)bArray, &isCopy);
memcpy(decrypteddata, outdata, srcLen);
(*env)->ReleasePrimitiveArrayCritical(env, bArray, decrypteddata, 0);
return bArray;
}
Any ideas why decrypting the encrypted bytes are not the same as the original?
As suggested by Codo and owlstead I tried higher level implementation which still has the same issue.
Here is the code from saju.net.in/code/misc/openssl_aes.c.txt
/**
* Create an 256 bit key and IV using the supplied key_data. salt can be added for taste.
* Fills in the encryption and decryption ctx objects and returns 0 on success
**/
int aes_init(unsigned char *key_data, int key_data_len, unsigned char *salt, EVP_CIPHER_CTX *e_ctx,
EVP_CIPHER_CTX *d_ctx)
{
int i, nrounds = 5;
unsigned char key[32], iv[32];
/*
* Gen key & IV for AES 256 CBC mode. A SHA1 digest is used to hash the supplied key material.
* nrounds is the number of times the we hash the material. More rounds are more secure but
* slower.
*/
i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, key_data, key_data_len, nrounds, key, iv);
if (i != 32) {
printf("Key size is %d bits - should be 256 bits\n", i);
return -1;
}
EVP_CIPHER_CTX_init(e_ctx);
EVP_EncryptInit_ex(e_ctx, EVP_aes_256_cbc(), NULL, key, iv);
EVP_CIPHER_CTX_init(d_ctx);
EVP_DecryptInit_ex(d_ctx, EVP_aes_256_cbc(), NULL, key, iv);
return 0;
}
/*
* Encrypt *len bytes of data
* All data going in & out is considered binary (unsigned char[])
*/
unsigned char *aes_encrypt(EVP_CIPHER_CTX *e, unsigned char *plaintext, int *len)
{
/* max ciphertext len for a n bytes of plaintext is n + AES_BLOCK_SIZE -1 bytes */
int c_len = *len + AES_BLOCK_SIZE, f_len = 0;
unsigned char *ciphertext = malloc(c_len);
/* allows reusing of 'e' for multiple encryption cycles */
EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL);
/* update ciphertext, c_len is filled with the length of ciphertext generated,
*len is the size of plaintext in bytes */
EVP_EncryptUpdate(e, ciphertext, &c_len, plaintext, *len);
/* update ciphertext with the final remaining bytes */
EVP_EncryptFinal_ex(e, ciphertext+c_len, &f_len);
*len = c_len + f_len;
return ciphertext;
}
/*
* Decrypt *len bytes of ciphertext
*/
unsigned char *aes_decrypt(EVP_CIPHER_CTX *e, const unsigned char *ciphertext, int *len)
{
/* because we have padding ON, we must allocate an extra cipher block size of memory */
int p_len = *len, f_len = 0;
unsigned char *plaintext = malloc(p_len + AES_BLOCK_SIZE);
EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
EVP_DecryptUpdate(e, plaintext, &p_len, ciphertext, *len);
EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len);
*len = p_len + f_len;
return plaintext;
}
here are my methods that are called form java:
/* "opaque" encryption, decryption ctx structures that libcrypto uses to record
status of enc/dec operations */
EVP_CIPHER_CTX en, de;
jint
Java_com_example_hellojni_HelloJni_aesinit( JNIEnv* env,
jobject obj)
{
unsigned int salt[] = {12345, 54321};
unsigned char key_data[]={ 'G','X','8','j','E','r','0','4','o','6','P','C','+','I','E','+'};
int key_data_len;
key_data_len = strlen(key_data);
/* gen key and iv. init the cipher ctx object */
if (aes_init(key_data, key_data_len, (unsigned char *)&salt, &en, &de)) {
printf("Couldn't initialize AES cipher\n");
__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "initializing aes failed");
return 0;
}
__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "initializing aes success");
return 1;
}
jint
Java_com_example_hellojni_HelloJni_aesCleanup( JNIEnv* env,
jobject obj)
{
EVP_CIPHER_CTX_cleanup(&en);
EVP_CIPHER_CTX_cleanup(&de);
return 1;
}
jbyteArray
Java_com_example_hellojni_HelloJni_encrypt( JNIEnv* env,
jobject obj, jstring textToEncrypt)
{
const char *plainText = (*env)->GetStringUTFChars(env, textToEncrypt, 0);
int len = strlen(plainText)+1;
unsigned char *ciphertext = aes_encrypt(&en, (unsigned char *)plainText, &len);
jbyteArray byteArray=(*env)->NewByteArray(env, strlen(ciphertext));
(*env)->SetByteArrayRegion(env, byteArray, 0, strlen(ciphertext), (const jbyte*)ciphertext);
(*env)->ReleaseStringUTFChars(env, textToEncrypt, plainText);
return byteArray;
}
jbyteArray
Java_com_example_hellojni_HelloJni_decrypt( JNIEnv* env,
jobject obj, jstring textToDecrypt)
{
const unsigned char *cipherText = (*env)->GetStringUTFChars(env, textToDecrypt, NULL);
int len = strlen(cipherText)+1;
char *plainText = (char *)aes_decrypt(&de, cipherText, &len);
jbyteArray byteArray=(*env)->NewByteArray(env, strlen(plainText));
(*env)->SetByteArrayRegion(env, byteArray, 0, strlen(plainText), (const jbyte*)plainText);
(*env)->ReleaseStringUTFChars(env, textToDecrypt, cipherText);
return byteArray;
}
You provide a key that is 72 bits long (9 characters x 8 bits). But it needs to be 128 bit longs (as you specify in the call to AES_set_encrypt_key). Thus the missing 56 bits will be more or less random (depending on what's next to the ukey array).
To fix it, specified a longer key or fill the remaining bytes with 0s.
You are using the low level encryption modes of OpenSSL. Your troubles are likely to vanish if you use the higher level EVP_* methods, e.g. for AES/CBC mode encryption. See also this related question.