demux: libmp4: add and parse 3DDS uuid
[vlc.git] / modules / keystore / file_crypt_android.c
blob14638459e05f9597e2c6d07f54faa9a1ea6c300f
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_memory.h>
30 #include <vlc_keystore.h>
32 #include "file_crypt.h"
34 #include <jni.h>
36 JNIEnv * android_getEnv(vlc_object_t *, const char *);
37 #define GET_ENV() android_getEnv(VLC_OBJECT(p_keystore), "android keystore")
39 static struct
41 struct
43 jmethodID toString;
44 } Object;
45 struct
47 jclass clazz;
48 jmethodID getInstance;
49 jmethodID load;
50 jmethodID getEntry;
51 struct
53 jmethodID getSecretKey;
54 } SecretKeyEntry;
55 } KeyStore;
56 struct
58 jint PURPOSE_ENCRYPT;
59 jint PURPOSE_DECRYPT;
60 jstring BLOCK_MODE_CBC;
61 jstring ENCRYPTION_PADDING_PKCS7;
62 jstring KEY_ALGORITHM_AES;
63 } KeyProperties;
64 struct
66 jclass clazz;
67 jmethodID getInstance;
68 jmethodID init;
69 jmethodID generateKey;
70 } KeyGenerator;
71 struct
73 struct
75 jclass clazz;
76 jmethodID ctor;
77 jmethodID setKeySize;
78 jmethodID setBlockModes;
79 jmethodID setEncryptionPaddings;
80 jmethodID build;
81 } Builder;
82 } KeyGenParameterSpec;
83 struct
85 jclass clazz;
86 jmethodID ctor;
87 } IvParameterSpec;
88 struct
90 jclass clazz;
91 jmethodID getInstance;
92 jmethodID init;
93 jmethodID doFinal;
94 jmethodID getIV;
95 jint ENCRYPT_MODE;
96 jint DECRYPT_MODE;
97 } Cipher;
98 jstring VLC_CIPHER;
99 } fields;
100 static jobject s_jkey = NULL;
102 #define CALL(caller, obj, method, ...) \
103 (*p_env)->caller(p_env, obj, fields.method, ##__VA_ARGS__)
105 #define CALL_VOID(obj, method, ...) \
106 CALL(CallVoidMethod, obj, method, ##__VA_ARGS__)
108 #define CALL_OBJ(obj, method, ...) \
109 CALL(CallObjectMethod, obj, method, ##__VA_ARGS__)
111 #define CALL_STATICOBJ(obj, method, ...) \
112 CALL(CallStaticObjectMethod, fields.obj.clazz, method, ##__VA_ARGS__)
114 #define CALL_INT(obj, method, ...) \
115 CALL(CallIntMethod, obj, method, ##__VA_ARGS__)
117 #define CALL_BOOL(obj, method, ...) \
118 CALL(CallBooleanMethod, obj, method, ##__VA_ARGS__)
120 #define NEW_OBJECT(arg, ...) \
121 (*p_env)->NewObject(p_env, fields.arg.clazz, fields.arg.ctor, ##__VA_ARGS__)
123 #define NEW_GREF(obj) \
124 (*p_env)->NewGlobalRef(p_env, obj)
126 #define DEL_GREF(obj) \
127 (*p_env)->DeleteGlobalRef(p_env, obj)
129 #define DEL_LREF(obj) \
130 (*p_env)->DeleteLocalRef(p_env, obj)
132 #define NEW_STR(str) \
133 (*p_env)->NewStringUTF(p_env, str)
135 static bool
136 check_expection(vlc_keystore *p_keystore, JNIEnv *p_env)
138 jthrowable jex = (*p_env)->ExceptionOccurred(p_env);
139 if (jex != NULL)
141 (*p_env)->ExceptionClear(p_env);
142 if (fields.Object.toString != NULL)
144 const char *psz_str = NULL;
145 jstring jstr = NULL;
147 jstr = (jstring) CALL_OBJ(jex, Object.toString);
148 if (jstr != NULL)
149 psz_str = (*p_env)->GetStringUTFChars(p_env, jstr, NULL);
150 if (psz_str != NULL)
152 msg_Err(p_keystore, "%s", psz_str);
153 (*p_env)->ReleaseStringUTFChars(p_env, jstr, psz_str);
155 DEL_LREF(jstr);
157 else
158 msg_Err(p_keystore, "unknown exception");
159 DEL_LREF(jex);
160 return true;
162 return false;
164 #define CHECK_EXCEPTION() check_expection(p_keystore, p_env)
166 #define GET_CLASS(str) do { \
167 if (clazz != NULL) \
168 DEL_LREF(clazz); \
169 clazz = (*p_env)->FindClass(p_env, (str)); \
170 if (CHECK_EXCEPTION()) \
171 return VLC_EGENERIC; \
172 } while (0)
173 #define GET_GLOBAL_CLASS(id) do { \
174 fields.id.clazz = (jclass) NEW_GREF(clazz); \
175 } while (0)
176 #define GET_ID(get, id, str, args) do { \
177 fields.id = (*p_env)->get(p_env, clazz, (str), (args)); \
178 if (CHECK_EXCEPTION()) \
179 return VLC_EGENERIC; \
180 } while (0)
181 #define GET_CONST_INT(id, str) do { \
182 jfieldID field = (*p_env)->GetStaticFieldID(p_env, clazz, (str), "I"); \
183 if (!CHECK_EXCEPTION()) \
185 fields.id = (*p_env)->GetStaticIntField(p_env, clazz, field); \
186 if (CHECK_EXCEPTION()) \
187 return VLC_EGENERIC; \
188 } else { \
189 return VLC_EGENERIC; \
191 } while(0)
192 #define GET_CONST_OBJ(id, str, type) do { \
193 jfieldID field = (*p_env)->GetStaticFieldID(p_env, clazz, (str), type); \
194 if (!CHECK_EXCEPTION()) \
196 jobject jobj = (*p_env)->GetStaticObjectField(p_env, clazz, field); \
197 if (CHECK_EXCEPTION()) \
198 return VLC_EGENERIC; \
199 fields.id = NEW_GREF(jobj); \
200 DEL_LREF(jobj); \
201 } else { \
202 return VLC_EGENERIC; \
204 } while(0)
207 * Init JNI fields that will be used to fetch the key and crypt/encrypt
209 static int
210 InitJni(vlc_keystore *p_keystore, JNIEnv *p_env)
212 jclass clazz = NULL;
214 GET_CLASS("java/lang/Object");
215 GET_ID(GetMethodID, Object.toString, "toString", "()Ljava/lang/String;");
217 GET_CLASS("java/security/KeyStore");
218 GET_GLOBAL_CLASS(KeyStore);
219 GET_ID(GetStaticMethodID, KeyStore.getInstance, "getInstance",
220 "(Ljava/lang/String;)Ljava/security/KeyStore;");
221 GET_ID(GetMethodID, KeyStore.load, "load",
222 "(Ljava/security/KeyStore$LoadStoreParameter;)V");
223 GET_ID(GetMethodID, KeyStore.getEntry, "getEntry",
224 "(Ljava/lang/String;Ljava/security/KeyStore$ProtectionParameter;)"
225 "Ljava/security/KeyStore$Entry;");
227 GET_CLASS("java/security/KeyStore$SecretKeyEntry");
228 GET_ID(GetMethodID, KeyStore.SecretKeyEntry.getSecretKey, "getSecretKey",
229 "()Ljavax/crypto/SecretKey;");
232 GET_CLASS("javax/crypto/spec/IvParameterSpec");
233 GET_GLOBAL_CLASS(IvParameterSpec);
234 GET_ID(GetMethodID, IvParameterSpec.ctor, "<init>", "([B)V");
236 GET_CLASS("javax/crypto/Cipher");
237 GET_GLOBAL_CLASS(Cipher);
238 GET_ID(GetStaticMethodID, Cipher.getInstance, "getInstance",
239 "(Ljava/lang/String;)Ljavax/crypto/Cipher;");
240 GET_ID(GetMethodID, Cipher.init, "init",
241 "(ILjava/security/Key;Ljava/security/spec/AlgorithmParameterSpec;)V");
242 GET_ID(GetMethodID, Cipher.doFinal, "doFinal", "([B)[B");
243 GET_ID(GetMethodID, Cipher.getIV, "getIV", "()[B");
244 GET_CONST_INT(Cipher.ENCRYPT_MODE, "ENCRYPT_MODE");
245 GET_CONST_INT(Cipher.DECRYPT_MODE, "DECRYPT_MODE");
247 DEL_LREF(clazz);
249 jstring VLC_CIPHER = NEW_STR("AES/CBC/PKCS7Padding");
250 if (CHECK_EXCEPTION())
251 return VLC_EGENERIC;
252 fields.VLC_CIPHER = NEW_GREF(VLC_CIPHER);
253 DEL_LREF(VLC_CIPHER);
255 return VLC_SUCCESS;
259 * Init JNI fields that will be used by generateKey()
261 static int
262 InitJniGenKey(vlc_keystore *p_keystore, JNIEnv *p_env)
264 jclass clazz = NULL;
266 GET_CLASS("android/security/keystore/KeyProperties");
267 GET_CONST_INT(KeyProperties.PURPOSE_ENCRYPT, "PURPOSE_ENCRYPT");
268 GET_CONST_INT(KeyProperties.PURPOSE_DECRYPT, "PURPOSE_DECRYPT");
269 GET_CONST_OBJ(KeyProperties.BLOCK_MODE_CBC,
270 "BLOCK_MODE_CBC", "Ljava/lang/String;");
271 GET_CONST_OBJ(KeyProperties.ENCRYPTION_PADDING_PKCS7,
272 "ENCRYPTION_PADDING_PKCS7", "Ljava/lang/String;");
273 GET_CONST_OBJ(KeyProperties.KEY_ALGORITHM_AES,
274 "KEY_ALGORITHM_AES", "Ljava/lang/String;");
276 GET_CLASS("android/security/keystore/KeyGenParameterSpec$Builder");
277 GET_GLOBAL_CLASS(KeyGenParameterSpec.Builder);
278 GET_ID(GetMethodID, KeyGenParameterSpec.Builder.ctor, "<init>",
279 "(Ljava/lang/String;I)V");
280 GET_ID(GetMethodID, KeyGenParameterSpec.Builder.setKeySize, "setKeySize",
281 "(I)Landroid/security/keystore/KeyGenParameterSpec$Builder;");
282 GET_ID(GetMethodID, KeyGenParameterSpec.Builder.setBlockModes, "setBlockModes",
283 "([Ljava/lang/String;)"
284 "Landroid/security/keystore/KeyGenParameterSpec$Builder;");
285 GET_ID(GetMethodID, KeyGenParameterSpec.Builder.setEncryptionPaddings,
286 "setEncryptionPaddings", "([Ljava/lang/String;)"
287 "Landroid/security/keystore/KeyGenParameterSpec$Builder;");
288 GET_ID(GetMethodID, KeyGenParameterSpec.Builder.build, "build",
289 "()Landroid/security/keystore/KeyGenParameterSpec;");
291 GET_CLASS("javax/crypto/KeyGenerator");
292 GET_GLOBAL_CLASS(KeyGenerator);
293 GET_ID(GetStaticMethodID, KeyGenerator.getInstance, "getInstance",
294 "(Ljava/lang/String;Ljava/lang/String;)Ljavax/crypto/KeyGenerator;");
295 GET_ID(GetMethodID, KeyGenerator.init, "init",
296 "(Ljava/security/spec/AlgorithmParameterSpec;)V");
297 GET_ID(GetMethodID, KeyGenerator.generateKey, "generateKey",
298 "()Ljavax/crypto/SecretKey;");
300 DEL_LREF(clazz);
301 return VLC_SUCCESS;
305 * Encrypt or Decrypt
307 static size_t
308 Process(vlc_keystore *p_keystore, JNIEnv *p_env, jobject jcipher,
309 const uint8_t *p_src, size_t i_src_len,
310 const uint8_t *p_iv, uint32_t i_iv_len, uint8_t **pp_dst)
312 size_t i_dst_size = 0;
313 uint8_t *p_dst;
314 jbyteArray jsrcArray = NULL, jdstArray = NULL;
316 jsrcArray = (*p_env)->NewByteArray(p_env, i_src_len);
317 if (CHECK_EXCEPTION())
318 goto end;
319 (*p_env)->SetByteArrayRegion(p_env, jsrcArray, 0, i_src_len, (jbyte *)p_src);
321 jdstArray = (jbyteArray) CALL_OBJ(jcipher, Cipher.doFinal, jsrcArray);
322 if (CHECK_EXCEPTION())
323 goto end;
325 if (jdstArray == NULL)
326 goto end;
328 jsize dstSize = (*p_env)->GetArrayLength(p_env, jdstArray);
330 if (dstSize == 0)
331 goto end;
333 jbyte *p_bytes = (*p_env)->GetByteArrayElements(p_env, jdstArray, 0);
335 p_dst = i_iv_len > 0 ? malloc(dstSize + i_iv_len + sizeof(uint32_t))
336 : malloc(dstSize);
337 if (p_dst == NULL)
339 (*p_env)->ReleaseByteArrayElements(p_env, jdstArray, p_bytes, 0);
340 free(p_dst);
341 goto end;
344 if (i_iv_len > 0)
346 /* Store the IV just before the encrypted password */
347 memcpy(p_dst, &i_iv_len, sizeof(uint32_t));
348 memcpy(p_dst + sizeof(uint32_t), p_iv, i_iv_len);
349 memcpy(p_dst + sizeof(uint32_t) + i_iv_len, p_bytes, dstSize);
350 i_dst_size = dstSize + i_iv_len + sizeof(uint32_t);
352 else
354 memcpy(p_dst, p_bytes, dstSize);
355 i_dst_size = dstSize;
357 (*p_env)->ReleaseByteArrayElements(p_env, jdstArray, p_bytes, 0);
359 *pp_dst = p_dst;
361 end:
362 if (jsrcArray != NULL)
363 DEL_LREF(jsrcArray);
364 if (jdstArray != NULL)
365 DEL_LREF(jdstArray);
366 return i_dst_size;
369 static size_t
370 AndroidEncrypt(vlc_keystore *p_keystore, void *p_ctx, const uint8_t *p_src,
371 size_t i_src_len, uint8_t **pp_dst)
373 (void) p_ctx;
374 JNIEnv *p_env = GET_ENV();
375 if (p_env == NULL)
376 return 0;
378 jobject jcipher = NULL;
379 jcipher = CALL_STATICOBJ(Cipher, Cipher.getInstance, fields.VLC_CIPHER);
380 if (CHECK_EXCEPTION())
381 return 0;
383 size_t i_dst_len = 0;
384 CALL_VOID(jcipher, Cipher.init, fields.Cipher.ENCRYPT_MODE, s_jkey, NULL);
385 if (CHECK_EXCEPTION())
386 goto end;
388 /* Get the IV (Initialization Vector) initialized by Android that will be
389 * used to decrypt this secret. This IV will be stored with the encrypted
390 * secret */
391 jarray jivArray = (jarray) CALL_OBJ(jcipher, Cipher.getIV);
392 if (jivArray == NULL)
393 goto end;
395 jsize i_iv_len = (*p_env)->GetArrayLength(p_env, jivArray);
396 if (i_iv_len == 0)
397 goto end;
398 jbyte *p_iv_bytes = (*p_env)->GetByteArrayElements(p_env, jivArray, 0);
400 i_dst_len = Process(p_keystore, p_env, jcipher, p_src, i_src_len,
401 (const uint8_t *)p_iv_bytes, i_iv_len, pp_dst);
403 (*p_env)->ReleaseByteArrayElements(p_env, jivArray, p_iv_bytes, 0);
404 DEL_LREF(jivArray);
406 end:
408 DEL_LREF(jcipher);
409 return i_dst_len;
412 static size_t
413 AndroidDecrypt(vlc_keystore *p_keystore, void *p_ctx, const uint8_t *p_src,
414 size_t i_src_len, uint8_t **pp_dst)
416 (void) p_ctx;
417 JNIEnv *p_env = GET_ENV();
418 if (p_env == NULL)
419 return 0;
421 jobject jivArray = NULL, jiv = NULL, jcipher = NULL;
423 jcipher = CALL_STATICOBJ(Cipher, Cipher.getInstance, fields.VLC_CIPHER);
424 if (CHECK_EXCEPTION())
425 return 0;
427 /* Get the IV located at the beginning of the secret */
428 size_t i_dst_len = 0;
429 uint32_t i_iv_len;
430 if (i_src_len < sizeof(uint32_t))
431 goto end;
433 memcpy(&i_iv_len, p_src, sizeof(uint32_t));
434 if (i_iv_len == 0 || i_src_len < (sizeof(uint32_t) + i_iv_len))
435 goto end;
437 jivArray = (*p_env)->NewByteArray(p_env, i_iv_len);
438 if (CHECK_EXCEPTION())
439 goto end;
440 (*p_env)->SetByteArrayRegion(p_env, jivArray, 0, i_iv_len,
441 (jbyte *)(p_src + sizeof(uint32_t)) );
443 jiv = NEW_OBJECT(IvParameterSpec, jivArray);
444 if (CHECK_EXCEPTION())
445 goto end;
447 /* Use the IV to initialize the decrypt Cipher */
448 CALL_VOID(jcipher, Cipher.init, fields.Cipher.DECRYPT_MODE, s_jkey, jiv);
449 if (CHECK_EXCEPTION())
450 goto end;
452 i_dst_len = Process(p_keystore, p_env, jcipher,
453 p_src + sizeof(uint32_t) + i_iv_len,
454 i_src_len - sizeof(uint32_t) - i_iv_len,
455 NULL, 0, pp_dst);
457 end:
458 DEL_LREF(jcipher);
459 if (jivArray != NULL)
460 DEL_LREF(jivArray);
461 if (jiv != NULL)
462 DEL_LREF(jiv);
463 return i_dst_len;
467 * Generate a AES/CBC/PKCS7Padding key that will be stored by the Android
468 * Keystore
470 static jobject
471 GenerateKey(vlc_keystore *p_keystore, JNIEnv *p_env, jstring jstringAlias,
472 jstring jstringProvider)
474 if (InitJniGenKey(p_keystore, p_env) != VLC_SUCCESS)
475 return NULL;
477 jobject jkey = NULL, jbuilder = NULL, jspec = NULL,
478 jkeyGen = NULL;
479 jclass jstringClass = NULL;
480 jobjectArray jarray = NULL;
482 jbuilder = NEW_OBJECT(KeyGenParameterSpec.Builder, jstringAlias,
483 fields.KeyProperties.PURPOSE_ENCRYPT |
484 fields.KeyProperties.PURPOSE_DECRYPT);
485 CALL_OBJ(jbuilder, KeyGenParameterSpec.Builder.setKeySize, 256);
487 jstringClass = (*p_env)->FindClass(p_env, "java/lang/String");
488 if (CHECK_EXCEPTION())
489 goto end;
491 jarray = (*p_env)->NewObjectArray(p_env, 1, jstringClass, NULL);
492 if (CHECK_EXCEPTION())
493 goto end;
495 (*p_env)->SetObjectArrayElement(p_env, jarray, 0,
496 fields.KeyProperties.BLOCK_MODE_CBC);
497 CALL_OBJ(jbuilder, KeyGenParameterSpec.Builder.setBlockModes, jarray);
499 (*p_env)->SetObjectArrayElement(p_env, jarray, 0,
500 fields.KeyProperties.ENCRYPTION_PADDING_PKCS7);
501 CALL_OBJ(jbuilder, KeyGenParameterSpec.Builder.setEncryptionPaddings, jarray);
502 jspec = CALL_OBJ(jbuilder, KeyGenParameterSpec.Builder.build);
503 if (CHECK_EXCEPTION())
504 goto end;
506 jkeyGen = CALL_STATICOBJ(KeyGenerator, KeyGenerator.getInstance,
507 fields.KeyProperties.KEY_ALGORITHM_AES,
508 jstringProvider);
509 if (CHECK_EXCEPTION())
510 goto end;
512 CALL_VOID(jkeyGen, KeyGenerator.init, jspec);
513 if (CHECK_EXCEPTION())
514 goto end;
516 jkey = CALL_OBJ(jkeyGen, KeyGenerator.generateKey);
517 CHECK_EXCEPTION();
519 end:
520 if (jbuilder != NULL)
521 DEL_LREF(jbuilder);
522 if (jstringClass != NULL)
523 DEL_LREF(jstringClass);
524 if (jarray != NULL)
525 DEL_LREF(jarray);
526 if (jspec != NULL)
527 DEL_LREF(jspec);
528 if (jkeyGen != NULL)
529 DEL_LREF(jkeyGen);
531 return jkey;
535 * Init JNI fields, fetch the key stored by Android or generate a new one
537 static void
538 AndroidInit(vlc_keystore *p_keystore)
540 JNIEnv *p_env = GET_ENV();
541 if (p_env == NULL)
542 return;
544 if (InitJni(p_keystore, p_env) != VLC_SUCCESS)
545 return;
547 jobject jkeystore = NULL, jentry = NULL, jkey = NULL;
548 jstring jstringAlias = NULL, jstringProvider = NULL;
550 jstringAlias = NEW_STR("LibVLCAndroid");
551 if (CHECK_EXCEPTION())
552 goto end;
554 jstringProvider = NEW_STR("AndroidKeyStore");
555 if (CHECK_EXCEPTION())
556 goto end;
558 jkeystore = CALL_STATICOBJ(KeyStore, KeyStore.getInstance, jstringProvider);
559 if (CHECK_EXCEPTION())
560 goto end;
562 CALL_VOID(jkeystore, KeyStore.load, NULL);
563 if (CHECK_EXCEPTION())
564 goto end;
566 jentry = CALL_OBJ(jkeystore, KeyStore.getEntry, jstringAlias, NULL);
567 if (CHECK_EXCEPTION())
568 goto end;
569 if (jentry != NULL)
571 jkey = CALL_OBJ(jentry, KeyStore.SecretKeyEntry.getSecretKey);
572 if (CHECK_EXCEPTION())
573 goto end;
575 else
577 jkey = GenerateKey(p_keystore, p_env, jstringAlias, jstringProvider);
578 if (jkey == NULL)
579 goto end;
582 s_jkey = NEW_GREF(jkey);
584 end:
585 if (jstringAlias != NULL)
586 DEL_LREF(jstringAlias);
587 if (jstringProvider != NULL)
588 DEL_LREF(jstringProvider);
589 if (jkeystore != NULL)
590 DEL_LREF(jkeystore);
591 if (jentry != NULL)
592 DEL_LREF(jentry);
593 if (jkey != NULL)
594 DEL_LREF(jkey);
598 CryptInit(vlc_keystore *p_keystore, struct crypt *p_crypt)
600 static vlc_mutex_t s_lock = VLC_STATIC_MUTEX;
601 static bool s_init = false;
603 vlc_mutex_lock(&s_lock);
604 if (!s_init)
606 AndroidInit(p_keystore);
607 s_init = true;
609 if (s_jkey == NULL)
611 vlc_mutex_unlock(&s_lock);
612 return VLC_EGENERIC;
614 vlc_mutex_unlock(&s_lock);
616 p_crypt->pf_encrypt = AndroidEncrypt;
617 p_crypt->pf_decrypt = AndroidDecrypt;
619 return VLC_SUCCESS;