1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "jni.h"
18#include <JNIHelp.h>
19#include <stdlib.h>
20#include <stdint.h>
21
22#include <openssl/md5.h>
23
24namespace android
25{
26
27struct fields_t {
28    jfieldID    context;
29};
30static fields_t fields;
31
32static void native_init(JNIEnv *env, jobject clazz)
33{
34    MD5_CTX* context = (MD5_CTX *)malloc(sizeof(MD5_CTX));
35    MD5_Init(context);
36
37    env->SetIntField(clazz, fields.context, (int)context);
38}
39
40static void native_reset(JNIEnv *env, jobject clazz)
41{
42    MD5_CTX *context = (MD5_CTX *)env->GetIntField(clazz, fields.context);
43    if (context != NULL) {
44        free(context);
45        env->SetIntField(clazz, fields.context, 0 );
46    }
47}
48
49static void native_update(JNIEnv *env, jobject clazz, jbyteArray dataArray)
50{
51    jbyte * data;
52    jsize dataSize;
53    MD5_CTX *context = (MD5_CTX *)env->GetIntField(clazz, fields.context);
54
55    if (context == NULL) {
56        native_init(env, clazz);
57        context = (MD5_CTX *)env->GetIntField(clazz, fields.context);
58    }
59
60    data = env->GetByteArrayElements(dataArray, NULL);
61    if (data == NULL) {
62        LOGE("Unable to get byte array elements");
63        jniThrowException(env, "java/lang/IllegalArgumentException",
64                          "Invalid data array when calling MessageDigest.update()");
65        return;
66    }
67    dataSize = env->GetArrayLength(dataArray);
68
69    MD5_Update(context, data, dataSize);
70
71    env->ReleaseByteArrayElements(dataArray, data, 0);
72}
73
74static jbyteArray native_digest(JNIEnv *env, jobject clazz)
75{
76    jbyteArray array;
77    jbyte md[MD5_DIGEST_LENGTH];
78    MD5_CTX *context = (MD5_CTX *)env->GetIntField(clazz, fields.context);
79
80    MD5_Final((uint8_t*)md, context);
81
82    array = env->NewByteArray(MD5_DIGEST_LENGTH);
83    LOG_ASSERT(array, "Native could not create new byte[]");
84
85    env->SetByteArrayRegion(array, 0, MD5_DIGEST_LENGTH, md);
86
87    native_reset(env, clazz);
88
89    return array;
90}
91
92
93/*
94 * JNI registration.
95 */
96
97static JNINativeMethod gMethods[] =
98{
99     /* name, signature, funcPtr */
100    {"init", "()V", (void *)native_init},
101    {"update", "([B)V", (void *)native_update},
102    {"digest", "()[B", (void *)native_digest},
103    {"reset", "()V", (void *)native_reset},
104};
105
106int register_android_security_Md5MessageDigest(JNIEnv *env)
107{
108    jclass clazz;
109
110    clazz = env->FindClass("android/security/Md5MessageDigest");
111    if (clazz == NULL) {
112        LOGE("Can't find android/security/Md5MessageDigest");
113        return -1;
114    }
115
116    fields.context = env->GetFieldID(clazz, "mNativeMd5Context", "I");
117    if (fields.context == NULL) {
118        LOGE("Can't find Md5MessageDigest.mNativeMd5Context");
119        return -1;
120    }
121
122    return jniRegisterNativeMethods(env, "android/security/Md5MessageDigest",
123        gMethods, NELEM(gMethods));
124}
125
126};
127