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 *****************************************************************************/
28 #include <vlc_common.h>
29 #include <vlc_keystore.h>
31 #include "file_crypt.h"
35 JNIEnv
* android_getEnv(vlc_object_t
*, const char *);
36 #define GET_ENV() android_getEnv(VLC_OBJECT(p_keystore), "android keystore")
47 jmethodID getInstance
;
52 jmethodID getSecretKey
;
59 jstring BLOCK_MODE_CBC
;
60 jstring ENCRYPTION_PADDING_PKCS7
;
61 jstring KEY_ALGORITHM_AES
;
66 jmethodID getInstance
;
68 jmethodID generateKey
;
77 jmethodID setBlockModes
;
78 jmethodID setEncryptionPaddings
;
81 } KeyGenParameterSpec
;
90 jmethodID getInstance
;
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)
135 check_expection(vlc_keystore
*p_keystore
, JNIEnv
*p_env
)
137 jthrowable jex
= (*p_env
)->ExceptionOccurred(p_env
);
140 (*p_env
)->ExceptionClear(p_env
);
141 if (fields
.Object
.toString
!= NULL
)
143 const char *psz_str
= NULL
;
146 jstr
= (jstring
) CALL_OBJ(jex
, Object
.toString
);
148 psz_str
= (*p_env
)->GetStringUTFChars(p_env
, jstr
, NULL
);
151 msg_Err(p_keystore
, "%s", psz_str
);
152 (*p_env
)->ReleaseStringUTFChars(p_env
, jstr
, psz_str
);
157 msg_Err(p_keystore
, "unknown exception");
163 #define CHECK_EXCEPTION() check_expection(p_keystore, p_env)
165 #define GET_CLASS(str) do { \
168 clazz = (*p_env)->FindClass(p_env, (str)); \
169 if (CHECK_EXCEPTION()) \
170 return VLC_EGENERIC; \
172 #define GET_GLOBAL_CLASS(id) do { \
173 fields.id.clazz = (jclass) NEW_GREF(clazz); \
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; \
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; \
188 return VLC_EGENERIC; \
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); \
201 return VLC_EGENERIC; \
206 * Init JNI fields that will be used to fetch the key and crypt/encrypt
209 InitJni(vlc_keystore
*p_keystore
, JNIEnv
*p_env
)
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");
248 jstring VLC_CIPHER
= NEW_STR("AES/CBC/PKCS7Padding");
249 if (CHECK_EXCEPTION())
251 fields
.VLC_CIPHER
= NEW_GREF(VLC_CIPHER
);
252 DEL_LREF(VLC_CIPHER
);
258 * Init JNI fields that will be used by generateKey()
261 InitJniGenKey(vlc_keystore
*p_keystore
, JNIEnv
*p_env
)
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;");
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;
313 jbyteArray jsrcArray
= NULL
, jdstArray
= NULL
;
315 jsrcArray
= (*p_env
)->NewByteArray(p_env
, i_src_len
);
316 if (CHECK_EXCEPTION())
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())
324 if (jdstArray
== NULL
)
327 jsize dstSize
= (*p_env
)->GetArrayLength(p_env
, jdstArray
);
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))
338 (*p_env
)->ReleaseByteArrayElements(p_env
, jdstArray
, p_bytes
, 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);
353 memcpy(p_dst
, p_bytes
, dstSize
);
354 i_dst_size
= dstSize
;
356 (*p_env
)->ReleaseByteArrayElements(p_env
, jdstArray
, p_bytes
, 0);
361 if (jsrcArray
!= NULL
)
363 if (jdstArray
!= NULL
)
369 AndroidEncrypt(vlc_keystore
*p_keystore
, void *p_ctx
, const uint8_t *p_src
,
370 size_t i_src_len
, uint8_t **pp_dst
)
373 JNIEnv
*p_env
= GET_ENV();
377 jobject jcipher
= NULL
;
378 jcipher
= CALL_STATICOBJ(Cipher
, Cipher
.getInstance
, fields
.VLC_CIPHER
);
379 if (CHECK_EXCEPTION())
382 size_t i_dst_len
= 0;
383 CALL_VOID(jcipher
, Cipher
.init
, fields
.Cipher
.ENCRYPT_MODE
, s_jkey
, NULL
);
384 if (CHECK_EXCEPTION())
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
390 jarray jivArray
= (jarray
) CALL_OBJ(jcipher
, Cipher
.getIV
);
391 if (jivArray
== NULL
)
394 jsize i_iv_len
= (*p_env
)->GetArrayLength(p_env
, jivArray
);
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);
412 AndroidDecrypt(vlc_keystore
*p_keystore
, void *p_ctx
, const uint8_t *p_src
,
413 size_t i_src_len
, uint8_t **pp_dst
)
416 JNIEnv
*p_env
= GET_ENV();
420 jobject jivArray
= NULL
, jiv
= NULL
, jcipher
= NULL
;
422 jcipher
= CALL_STATICOBJ(Cipher
, Cipher
.getInstance
, fields
.VLC_CIPHER
);
423 if (CHECK_EXCEPTION())
426 /* Get the IV located at the beginning of the secret */
427 size_t i_dst_len
= 0;
429 if (i_src_len
< sizeof(uint32_t))
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
))
436 jivArray
= (*p_env
)->NewByteArray(p_env
, i_iv_len
);
437 if (CHECK_EXCEPTION())
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())
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())
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
,
458 if (jivArray
!= NULL
)
466 * Generate a AES/CBC/PKCS7Padding key that will be stored by the Android
470 GenerateKey(vlc_keystore
*p_keystore
, JNIEnv
*p_env
, jstring jstringAlias
,
471 jstring jstringProvider
)
473 if (InitJniGenKey(p_keystore
, p_env
) != VLC_SUCCESS
)
476 jobject jkey
= NULL
, jbuilder
= NULL
, jspec
= 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())
490 jarray
= (*p_env
)->NewObjectArray(p_env
, 1, jstringClass
, NULL
);
491 if (CHECK_EXCEPTION())
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())
505 jkeyGen
= CALL_STATICOBJ(KeyGenerator
, KeyGenerator
.getInstance
,
506 fields
.KeyProperties
.KEY_ALGORITHM_AES
,
508 if (CHECK_EXCEPTION())
511 CALL_VOID(jkeyGen
, KeyGenerator
.init
, jspec
);
512 if (CHECK_EXCEPTION())
515 jkey
= CALL_OBJ(jkeyGen
, KeyGenerator
.generateKey
);
519 if (jbuilder
!= NULL
)
521 if (jstringClass
!= NULL
)
522 DEL_LREF(jstringClass
);
534 * Init JNI fields, fetch the key stored by Android or generate a new one
537 AndroidInit(vlc_keystore
*p_keystore
)
539 JNIEnv
*p_env
= GET_ENV();
543 if (InitJni(p_keystore
, p_env
) != VLC_SUCCESS
)
546 jobject jkeystore
= NULL
, jentry
= NULL
, jkey
= NULL
;
547 jstring jstringAlias
= NULL
, jstringProvider
= NULL
;
549 jstringAlias
= NEW_STR("LibVLCAndroid");
550 if (CHECK_EXCEPTION())
553 jstringProvider
= NEW_STR("AndroidKeyStore");
554 if (CHECK_EXCEPTION())
557 jkeystore
= CALL_STATICOBJ(KeyStore
, KeyStore
.getInstance
, jstringProvider
);
558 if (CHECK_EXCEPTION())
561 CALL_VOID(jkeystore
, KeyStore
.load
, NULL
);
562 if (CHECK_EXCEPTION())
565 jentry
= CALL_OBJ(jkeystore
, KeyStore
.getEntry
, jstringAlias
, NULL
);
566 if (CHECK_EXCEPTION())
570 jkey
= CALL_OBJ(jentry
, KeyStore
.SecretKeyEntry
.getSecretKey
);
571 if (CHECK_EXCEPTION())
576 jkey
= GenerateKey(p_keystore
, p_env
, jstringAlias
, jstringProvider
);
581 s_jkey
= NEW_GREF(jkey
);
584 if (jstringAlias
!= NULL
)
585 DEL_LREF(jstringAlias
);
586 if (jstringProvider
!= NULL
)
587 DEL_LREF(jstringProvider
);
588 if (jkeystore
!= NULL
)
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
);
605 AndroidInit(p_keystore
);
610 vlc_mutex_unlock(&s_lock
);
613 vlc_mutex_unlock(&s_lock
);
615 p_crypt
->pf_encrypt
= AndroidEncrypt
;
616 p_crypt
->pf_decrypt
= AndroidDecrypt
;