glwin32: don't activate on XP
[vlc.git] / modules / keystore / file_crypt_android.c
blob2cbe98154c352af76094be6447a5b653d3fa23cd
1 /*****************************************************************************
2 * file_crypt_android.c: Crypt using AndroidKeyStore
3 *****************************************************************************
4 * Copyright © 2016 VLC authors, VideoLAN and VideoLabs
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include <stdarg.h>
26 #include <stdio.h>
28 #include <vlc_common.h>
29 #include <vlc_keystore.h>
31 #include "file_crypt.h"
33 #include <jni.h>
35 JNIEnv * android_getEnv(vlc_object_t *, const char *);
36 #define GET_ENV() android_getEnv(VLC_OBJECT(p_keystore), "android keystore")
38 static struct
40 struct
42 jmethodID toString;
43 } Object;
44 struct
46 jclass clazz;
47 jmethodID getInstance;
48 jmethodID load;
49 jmethodID getEntry;
50 struct
52 jmethodID getSecretKey;
53 } SecretKeyEntry;
54 } KeyStore;
55 struct
57 jint PURPOSE_ENCRYPT;
58 jint PURPOSE_DECRYPT;
59 jstring BLOCK_MODE_CBC;
60 jstring ENCRYPTION_PADDING_PKCS7;
61 jstring KEY_ALGORITHM_AES;
62 } KeyProperties;
63 struct
65 jclass clazz;
66 jmethodID getInstance;
67 jmethodID init;
68 jmethodID generateKey;
69 } KeyGenerator;
70 struct
72 struct
74 jclass clazz;
75 jmethodID ctor;
76 jmethodID setKeySize;
77 jmethodID setBlockModes;
78 jmethodID setEncryptionPaddings;
79 jmethodID build;
80 } Builder;
81 } KeyGenParameterSpec;
82 struct
84 jclass clazz;
85 jmethodID ctor;
86 } IvParameterSpec;
87 struct
89 jclass clazz;
90 jmethodID getInstance;
91 jmethodID init;
92 jmethodID doFinal;
93 jmethodID getIV;
94 jint ENCRYPT_MODE;
95 jint DECRYPT_MODE;
96 } Cipher;
97 jstring VLC_CIPHER;
98 } fields;
99 static jobject s_jkey = NULL;
101 #define CALL(caller, obj, method, ...) \
102 (*p_env)->caller(p_env, obj, fields.method, ##__VA_ARGS__)
104 #define CALL_VOID(obj, method, ...) \
105 CALL(CallVoidMethod, obj, method, ##__VA_ARGS__)
107 #define CALL_OBJ(obj, method, ...) \
108 CALL(CallObjectMethod, obj, method, ##__VA_ARGS__)
110 #define CALL_STATICOBJ(obj, method, ...) \
111 CALL(CallStaticObjectMethod, fields.obj.clazz, method, ##__VA_ARGS__)
113 #define CALL_INT(obj, method, ...) \
114 CALL(CallIntMethod, obj, method, ##__VA_ARGS__)
116 #define CALL_BOOL(obj, method, ...) \
117 CALL(CallBooleanMethod, obj, method, ##__VA_ARGS__)
119 #define NEW_OBJECT(arg, ...) \
120 (*p_env)->NewObject(p_env, fields.arg.clazz, fields.arg.ctor, ##__VA_ARGS__)
122 #define NEW_GREF(obj) \
123 (*p_env)->NewGlobalRef(p_env, obj)
125 #define DEL_GREF(obj) \
126 (*p_env)->DeleteGlobalRef(p_env, obj)
128 #define DEL_LREF(obj) \
129 (*p_env)->DeleteLocalRef(p_env, obj)
131 #define NEW_STR(str) \
132 (*p_env)->NewStringUTF(p_env, str)
134 static bool
135 check_expection(vlc_keystore *p_keystore, JNIEnv *p_env)
137 jthrowable jex = (*p_env)->ExceptionOccurred(p_env);
138 if (jex != NULL)
140 (*p_env)->ExceptionClear(p_env);
141 if (fields.Object.toString != NULL)
143 const char *psz_str = NULL;
144 jstring jstr = NULL;
146 jstr = (jstring) CALL_OBJ(jex, Object.toString);
147 if (jstr != NULL)
148 psz_str = (*p_env)->GetStringUTFChars(p_env, jstr, NULL);
149 if (psz_str != NULL)
151 msg_Err(p_keystore, "%s", psz_str);
152 (*p_env)->ReleaseStringUTFChars(p_env, jstr, psz_str);
154 DEL_LREF(jstr);
156 else
157 msg_Err(p_keystore, "unknown exception");
158 DEL_LREF(jex);
159 return true;
161 return false;
163 #define CHECK_EXCEPTION() check_expection(p_keystore, p_env)
165 #define GET_CLASS(str) do { \
166 if (clazz != NULL) \
167 DEL_LREF(clazz); \
168 clazz = (*p_env)->FindClass(p_env, (str)); \
169 if (CHECK_EXCEPTION()) \
170 return VLC_EGENERIC; \
171 } while (0)
172 #define GET_GLOBAL_CLASS(id) do { \
173 fields.id.clazz = (jclass) NEW_GREF(clazz); \
174 } while (0)
175 #define GET_ID(get, id, str, args) do { \
176 fields.id = (*p_env)->get(p_env, clazz, (str), (args)); \
177 if (CHECK_EXCEPTION()) \
178 return VLC_EGENERIC; \
179 } while (0)
180 #define GET_CONST_INT(id, str) do { \
181 jfieldID field = (*p_env)->GetStaticFieldID(p_env, clazz, (str), "I"); \
182 if (!CHECK_EXCEPTION()) \
184 fields.id = (*p_env)->GetStaticIntField(p_env, clazz, field); \
185 if (CHECK_EXCEPTION()) \
186 return VLC_EGENERIC; \
187 } else { \
188 return VLC_EGENERIC; \
190 } while(0)
191 #define GET_CONST_OBJ(id, str, type) do { \
192 jfieldID field = (*p_env)->GetStaticFieldID(p_env, clazz, (str), type); \
193 if (!CHECK_EXCEPTION()) \
195 jobject jobj = (*p_env)->GetStaticObjectField(p_env, clazz, field); \
196 if (CHECK_EXCEPTION()) \
197 return VLC_EGENERIC; \
198 fields.id = NEW_GREF(jobj); \
199 DEL_LREF(jobj); \
200 } else { \
201 return VLC_EGENERIC; \
203 } while(0)
206 * Init JNI fields that will be used to fetch the key and crypt/encrypt
208 static int
209 InitJni(vlc_keystore *p_keystore, JNIEnv *p_env)
211 jclass clazz = NULL;
213 GET_CLASS("java/lang/Object");
214 GET_ID(GetMethodID, Object.toString, "toString", "()Ljava/lang/String;");
216 GET_CLASS("java/security/KeyStore");
217 GET_GLOBAL_CLASS(KeyStore);
218 GET_ID(GetStaticMethodID, KeyStore.getInstance, "getInstance",
219 "(Ljava/lang/String;)Ljava/security/KeyStore;");
220 GET_ID(GetMethodID, KeyStore.load, "load",
221 "(Ljava/security/KeyStore$LoadStoreParameter;)V");
222 GET_ID(GetMethodID, KeyStore.getEntry, "getEntry",
223 "(Ljava/lang/String;Ljava/security/KeyStore$ProtectionParameter;)"
224 "Ljava/security/KeyStore$Entry;");
226 GET_CLASS("java/security/KeyStore$SecretKeyEntry");
227 GET_ID(GetMethodID, KeyStore.SecretKeyEntry.getSecretKey, "getSecretKey",
228 "()Ljavax/crypto/SecretKey;");
231 GET_CLASS("javax/crypto/spec/IvParameterSpec");
232 GET_GLOBAL_CLASS(IvParameterSpec);
233 GET_ID(GetMethodID, IvParameterSpec.ctor, "<init>", "([B)V");
235 GET_CLASS("javax/crypto/Cipher");
236 GET_GLOBAL_CLASS(Cipher);
237 GET_ID(GetStaticMethodID, Cipher.getInstance, "getInstance",
238 "(Ljava/lang/String;)Ljavax/crypto/Cipher;");
239 GET_ID(GetMethodID, Cipher.init, "init",
240 "(ILjava/security/Key;Ljava/security/spec/AlgorithmParameterSpec;)V");
241 GET_ID(GetMethodID, Cipher.doFinal, "doFinal", "([B)[B");
242 GET_ID(GetMethodID, Cipher.getIV, "getIV", "()[B");
243 GET_CONST_INT(Cipher.ENCRYPT_MODE, "ENCRYPT_MODE");
244 GET_CONST_INT(Cipher.DECRYPT_MODE, "DECRYPT_MODE");
246 DEL_LREF(clazz);
248 jstring VLC_CIPHER = NEW_STR("AES/CBC/PKCS7Padding");
249 if (CHECK_EXCEPTION())
250 return VLC_EGENERIC;
251 fields.VLC_CIPHER = NEW_GREF(VLC_CIPHER);
252 DEL_LREF(VLC_CIPHER);
254 return VLC_SUCCESS;
258 * Init JNI fields that will be used by generateKey()
260 static int
261 InitJniGenKey(vlc_keystore *p_keystore, JNIEnv *p_env)
263 jclass clazz = NULL;
265 GET_CLASS("android/security/keystore/KeyProperties");
266 GET_CONST_INT(KeyProperties.PURPOSE_ENCRYPT, "PURPOSE_ENCRYPT");
267 GET_CONST_INT(KeyProperties.PURPOSE_DECRYPT, "PURPOSE_DECRYPT");
268 GET_CONST_OBJ(KeyProperties.BLOCK_MODE_CBC,
269 "BLOCK_MODE_CBC", "Ljava/lang/String;");
270 GET_CONST_OBJ(KeyProperties.ENCRYPTION_PADDING_PKCS7,
271 "ENCRYPTION_PADDING_PKCS7", "Ljava/lang/String;");
272 GET_CONST_OBJ(KeyProperties.KEY_ALGORITHM_AES,
273 "KEY_ALGORITHM_AES", "Ljava/lang/String;");
275 GET_CLASS("android/security/keystore/KeyGenParameterSpec$Builder");
276 GET_GLOBAL_CLASS(KeyGenParameterSpec.Builder);
277 GET_ID(GetMethodID, KeyGenParameterSpec.Builder.ctor, "<init>",
278 "(Ljava/lang/String;I)V");
279 GET_ID(GetMethodID, KeyGenParameterSpec.Builder.setKeySize, "setKeySize",
280 "(I)Landroid/security/keystore/KeyGenParameterSpec$Builder;");
281 GET_ID(GetMethodID, KeyGenParameterSpec.Builder.setBlockModes, "setBlockModes",
282 "([Ljava/lang/String;)"
283 "Landroid/security/keystore/KeyGenParameterSpec$Builder;");
284 GET_ID(GetMethodID, KeyGenParameterSpec.Builder.setEncryptionPaddings,
285 "setEncryptionPaddings", "([Ljava/lang/String;)"
286 "Landroid/security/keystore/KeyGenParameterSpec$Builder;");
287 GET_ID(GetMethodID, KeyGenParameterSpec.Builder.build, "build",
288 "()Landroid/security/keystore/KeyGenParameterSpec;");
290 GET_CLASS("javax/crypto/KeyGenerator");
291 GET_GLOBAL_CLASS(KeyGenerator);
292 GET_ID(GetStaticMethodID, KeyGenerator.getInstance, "getInstance",
293 "(Ljava/lang/String;Ljava/lang/String;)Ljavax/crypto/KeyGenerator;");
294 GET_ID(GetMethodID, KeyGenerator.init, "init",
295 "(Ljava/security/spec/AlgorithmParameterSpec;)V");
296 GET_ID(GetMethodID, KeyGenerator.generateKey, "generateKey",
297 "()Ljavax/crypto/SecretKey;");
299 DEL_LREF(clazz);
300 return VLC_SUCCESS;
304 * Encrypt or Decrypt
306 static size_t
307 Process(vlc_keystore *p_keystore, JNIEnv *p_env, jobject jcipher,
308 const uint8_t *p_src, size_t i_src_len,
309 const uint8_t *p_iv, uint32_t i_iv_len, uint8_t **pp_dst)
311 size_t i_dst_size = 0;
312 uint8_t *p_dst;
313 jbyteArray jsrcArray = NULL, jdstArray = NULL;
315 jsrcArray = (*p_env)->NewByteArray(p_env, i_src_len);
316 if (CHECK_EXCEPTION())
317 goto end;
318 (*p_env)->SetByteArrayRegion(p_env, jsrcArray, 0, i_src_len, (jbyte *)p_src);
320 jdstArray = (jbyteArray) CALL_OBJ(jcipher, Cipher.doFinal, jsrcArray);
321 if (CHECK_EXCEPTION())
322 goto end;
324 if (jdstArray == NULL)
325 goto end;
327 jsize dstSize = (*p_env)->GetArrayLength(p_env, jdstArray);
329 if (dstSize == 0)
330 goto end;
332 jbyte *p_bytes = (*p_env)->GetByteArrayElements(p_env, jdstArray, 0);
334 p_dst = i_iv_len > 0 ? malloc(dstSize + i_iv_len + sizeof(uint32_t))
335 : malloc(dstSize);
336 if (p_dst == NULL)
338 (*p_env)->ReleaseByteArrayElements(p_env, jdstArray, p_bytes, 0);
339 free(p_dst);
340 goto end;
343 if (i_iv_len > 0)
345 /* Store the IV just before the encrypted password */
346 memcpy(p_dst, &i_iv_len, sizeof(uint32_t));
347 memcpy(p_dst + sizeof(uint32_t), p_iv, i_iv_len);
348 memcpy(p_dst + sizeof(uint32_t) + i_iv_len, p_bytes, dstSize);
349 i_dst_size = dstSize + i_iv_len + sizeof(uint32_t);
351 else
353 memcpy(p_dst, p_bytes, dstSize);
354 i_dst_size = dstSize;
356 (*p_env)->ReleaseByteArrayElements(p_env, jdstArray, p_bytes, 0);
358 *pp_dst = p_dst;
360 end:
361 if (jsrcArray != NULL)
362 DEL_LREF(jsrcArray);
363 if (jdstArray != NULL)
364 DEL_LREF(jdstArray);
365 return i_dst_size;
368 static size_t
369 AndroidEncrypt(vlc_keystore *p_keystore, void *p_ctx, const uint8_t *p_src,
370 size_t i_src_len, uint8_t **pp_dst)
372 (void) p_ctx;
373 JNIEnv *p_env = GET_ENV();
374 if (p_env == NULL)
375 return 0;
377 jobject jcipher = NULL;
378 jcipher = CALL_STATICOBJ(Cipher, Cipher.getInstance, fields.VLC_CIPHER);
379 if (CHECK_EXCEPTION())
380 return 0;
382 size_t i_dst_len = 0;
383 CALL_VOID(jcipher, Cipher.init, fields.Cipher.ENCRYPT_MODE, s_jkey, NULL);
384 if (CHECK_EXCEPTION())
385 goto end;
387 /* Get the IV (Initialization Vector) initialized by Android that will be
388 * used to decrypt this secret. This IV will be stored with the encrypted
389 * secret */
390 jarray jivArray = (jarray) CALL_OBJ(jcipher, Cipher.getIV);
391 if (jivArray == NULL)
392 goto end;
394 jsize i_iv_len = (*p_env)->GetArrayLength(p_env, jivArray);
395 if (i_iv_len == 0)
396 goto end;
397 jbyte *p_iv_bytes = (*p_env)->GetByteArrayElements(p_env, jivArray, 0);
399 i_dst_len = Process(p_keystore, p_env, jcipher, p_src, i_src_len,
400 (const uint8_t *)p_iv_bytes, i_iv_len, pp_dst);
402 (*p_env)->ReleaseByteArrayElements(p_env, jivArray, p_iv_bytes, 0);
403 DEL_LREF(jivArray);
405 end:
407 DEL_LREF(jcipher);
408 return i_dst_len;
411 static size_t
412 AndroidDecrypt(vlc_keystore *p_keystore, void *p_ctx, const uint8_t *p_src,
413 size_t i_src_len, uint8_t **pp_dst)
415 (void) p_ctx;
416 JNIEnv *p_env = GET_ENV();
417 if (p_env == NULL)
418 return 0;
420 jobject jivArray = NULL, jiv = NULL, jcipher = NULL;
422 jcipher = CALL_STATICOBJ(Cipher, Cipher.getInstance, fields.VLC_CIPHER);
423 if (CHECK_EXCEPTION())
424 return 0;
426 /* Get the IV located at the beginning of the secret */
427 size_t i_dst_len = 0;
428 uint32_t i_iv_len;
429 if (i_src_len < sizeof(uint32_t))
430 goto end;
432 memcpy(&i_iv_len, p_src, sizeof(uint32_t));
433 if (i_iv_len == 0 || i_src_len < (sizeof(uint32_t) + i_iv_len))
434 goto end;
436 jivArray = (*p_env)->NewByteArray(p_env, i_iv_len);
437 if (CHECK_EXCEPTION())
438 goto end;
439 (*p_env)->SetByteArrayRegion(p_env, jivArray, 0, i_iv_len,
440 (jbyte *)(p_src + sizeof(uint32_t)) );
442 jiv = NEW_OBJECT(IvParameterSpec, jivArray);
443 if (CHECK_EXCEPTION())
444 goto end;
446 /* Use the IV to initialize the decrypt Cipher */
447 CALL_VOID(jcipher, Cipher.init, fields.Cipher.DECRYPT_MODE, s_jkey, jiv);
448 if (CHECK_EXCEPTION())
449 goto end;
451 i_dst_len = Process(p_keystore, p_env, jcipher,
452 p_src + sizeof(uint32_t) + i_iv_len,
453 i_src_len - sizeof(uint32_t) - i_iv_len,
454 NULL, 0, pp_dst);
456 end:
457 DEL_LREF(jcipher);
458 if (jivArray != NULL)
459 DEL_LREF(jivArray);
460 if (jiv != NULL)
461 DEL_LREF(jiv);
462 return i_dst_len;
466 * Generate a AES/CBC/PKCS7Padding key that will be stored by the Android
467 * Keystore
469 static jobject
470 GenerateKey(vlc_keystore *p_keystore, JNIEnv *p_env, jstring jstringAlias,
471 jstring jstringProvider)
473 if (InitJniGenKey(p_keystore, p_env) != VLC_SUCCESS)
474 return NULL;
476 jobject jkey = NULL, jbuilder = NULL, jspec = NULL,
477 jkeyGen = NULL;
478 jclass jstringClass = NULL;
479 jobjectArray jarray = NULL;
481 jbuilder = NEW_OBJECT(KeyGenParameterSpec.Builder, jstringAlias,
482 fields.KeyProperties.PURPOSE_ENCRYPT |
483 fields.KeyProperties.PURPOSE_DECRYPT);
484 CALL_OBJ(jbuilder, KeyGenParameterSpec.Builder.setKeySize, 256);
486 jstringClass = (*p_env)->FindClass(p_env, "java/lang/String");
487 if (CHECK_EXCEPTION())
488 goto end;
490 jarray = (*p_env)->NewObjectArray(p_env, 1, jstringClass, NULL);
491 if (CHECK_EXCEPTION())
492 goto end;
494 (*p_env)->SetObjectArrayElement(p_env, jarray, 0,
495 fields.KeyProperties.BLOCK_MODE_CBC);
496 CALL_OBJ(jbuilder, KeyGenParameterSpec.Builder.setBlockModes, jarray);
498 (*p_env)->SetObjectArrayElement(p_env, jarray, 0,
499 fields.KeyProperties.ENCRYPTION_PADDING_PKCS7);
500 CALL_OBJ(jbuilder, KeyGenParameterSpec.Builder.setEncryptionPaddings, jarray);
501 jspec = CALL_OBJ(jbuilder, KeyGenParameterSpec.Builder.build);
502 if (CHECK_EXCEPTION())
503 goto end;
505 jkeyGen = CALL_STATICOBJ(KeyGenerator, KeyGenerator.getInstance,
506 fields.KeyProperties.KEY_ALGORITHM_AES,
507 jstringProvider);
508 if (CHECK_EXCEPTION())
509 goto end;
511 CALL_VOID(jkeyGen, KeyGenerator.init, jspec);
512 if (CHECK_EXCEPTION())
513 goto end;
515 jkey = CALL_OBJ(jkeyGen, KeyGenerator.generateKey);
516 CHECK_EXCEPTION();
518 end:
519 if (jbuilder != NULL)
520 DEL_LREF(jbuilder);
521 if (jstringClass != NULL)
522 DEL_LREF(jstringClass);
523 if (jarray != NULL)
524 DEL_LREF(jarray);
525 if (jspec != NULL)
526 DEL_LREF(jspec);
527 if (jkeyGen != NULL)
528 DEL_LREF(jkeyGen);
530 return jkey;
534 * Init JNI fields, fetch the key stored by Android or generate a new one
536 static void
537 AndroidInit(vlc_keystore *p_keystore)
539 JNIEnv *p_env = GET_ENV();
540 if (p_env == NULL)
541 return;
543 if (InitJni(p_keystore, p_env) != VLC_SUCCESS)
544 return;
546 jobject jkeystore = NULL, jentry = NULL, jkey = NULL;
547 jstring jstringAlias = NULL, jstringProvider = NULL;
549 jstringAlias = NEW_STR("LibVLCAndroid");
550 if (CHECK_EXCEPTION())
551 goto end;
553 jstringProvider = NEW_STR("AndroidKeyStore");
554 if (CHECK_EXCEPTION())
555 goto end;
557 jkeystore = CALL_STATICOBJ(KeyStore, KeyStore.getInstance, jstringProvider);
558 if (CHECK_EXCEPTION())
559 goto end;
561 CALL_VOID(jkeystore, KeyStore.load, NULL);
562 if (CHECK_EXCEPTION())
563 goto end;
565 jentry = CALL_OBJ(jkeystore, KeyStore.getEntry, jstringAlias, NULL);
566 if (CHECK_EXCEPTION())
567 goto end;
568 if (jentry != NULL)
570 jkey = CALL_OBJ(jentry, KeyStore.SecretKeyEntry.getSecretKey);
571 if (CHECK_EXCEPTION())
572 goto end;
574 else
576 jkey = GenerateKey(p_keystore, p_env, jstringAlias, jstringProvider);
577 if (jkey == NULL)
578 goto end;
581 s_jkey = NEW_GREF(jkey);
583 end:
584 if (jstringAlias != NULL)
585 DEL_LREF(jstringAlias);
586 if (jstringProvider != NULL)
587 DEL_LREF(jstringProvider);
588 if (jkeystore != NULL)
589 DEL_LREF(jkeystore);
590 if (jentry != NULL)
591 DEL_LREF(jentry);
592 if (jkey != NULL)
593 DEL_LREF(jkey);
597 CryptInit(vlc_keystore *p_keystore, struct crypt *p_crypt)
599 static vlc_mutex_t s_lock = VLC_STATIC_MUTEX;
600 static bool s_init = false;
602 vlc_mutex_lock(&s_lock);
603 if (!s_init)
605 AndroidInit(p_keystore);
606 s_init = true;
608 if (s_jkey == NULL)
610 vlc_mutex_unlock(&s_lock);
611 return VLC_EGENERIC;
613 vlc_mutex_unlock(&s_lock);
615 p_crypt->pf_encrypt = AndroidEncrypt;
616 p_crypt->pf_decrypt = AndroidDecrypt;
618 return VLC_SUCCESS;