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_memory.h>
30 #include <vlc_keystore.h>
32 #include "file_crypt.h"
36 JNIEnv
* android_getEnv(vlc_object_t
*, const char *);
37 #define GET_ENV() android_getEnv(VLC_OBJECT(p_keystore), "android keystore")
48 jmethodID getInstance
;
53 jmethodID getSecretKey
;
60 jstring BLOCK_MODE_CBC
;
61 jstring ENCRYPTION_PADDING_PKCS7
;
62 jstring KEY_ALGORITHM_AES
;
67 jmethodID getInstance
;
69 jmethodID generateKey
;
78 jmethodID setBlockModes
;
79 jmethodID setEncryptionPaddings
;
82 } KeyGenParameterSpec
;
91 jmethodID getInstance
;
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)
136 check_expection(vlc_keystore
*p_keystore
, JNIEnv
*p_env
)
138 jthrowable jex
= (*p_env
)->ExceptionOccurred(p_env
);
141 (*p_env
)->ExceptionClear(p_env
);
142 if (fields
.Object
.toString
!= NULL
)
144 const char *psz_str
= NULL
;
147 jstr
= (jstring
) CALL_OBJ(jex
, Object
.toString
);
149 psz_str
= (*p_env
)->GetStringUTFChars(p_env
, jstr
, NULL
);
152 msg_Err(p_keystore
, "%s", psz_str
);
153 (*p_env
)->ReleaseStringUTFChars(p_env
, jstr
, psz_str
);
158 msg_Err(p_keystore
, "unknown exception");
164 #define CHECK_EXCEPTION() check_expection(p_keystore, p_env)
166 #define GET_CLASS(str) do { \
169 clazz = (*p_env)->FindClass(p_env, (str)); \
170 if (CHECK_EXCEPTION()) \
171 return VLC_EGENERIC; \
173 #define GET_GLOBAL_CLASS(id) do { \
174 fields.id.clazz = (jclass) NEW_GREF(clazz); \
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; \
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; \
189 return VLC_EGENERIC; \
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); \
202 return VLC_EGENERIC; \
207 * Init JNI fields that will be used to fetch the key and crypt/encrypt
210 InitJni(vlc_keystore
*p_keystore
, JNIEnv
*p_env
)
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");
249 jstring VLC_CIPHER
= NEW_STR("AES/CBC/PKCS7Padding");
250 if (CHECK_EXCEPTION())
252 fields
.VLC_CIPHER
= NEW_GREF(VLC_CIPHER
);
253 DEL_LREF(VLC_CIPHER
);
259 * Init JNI fields that will be used by generateKey()
262 InitJniGenKey(vlc_keystore
*p_keystore
, JNIEnv
*p_env
)
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;");
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;
314 jbyteArray jsrcArray
= NULL
, jdstArray
= NULL
;
316 jsrcArray
= (*p_env
)->NewByteArray(p_env
, i_src_len
);
317 if (CHECK_EXCEPTION())
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())
325 if (jdstArray
== NULL
)
328 jsize dstSize
= (*p_env
)->GetArrayLength(p_env
, jdstArray
);
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))
339 (*p_env
)->ReleaseByteArrayElements(p_env
, jdstArray
, p_bytes
, 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);
354 memcpy(p_dst
, p_bytes
, dstSize
);
355 i_dst_size
= dstSize
;
357 (*p_env
)->ReleaseByteArrayElements(p_env
, jdstArray
, p_bytes
, 0);
362 if (jsrcArray
!= NULL
)
364 if (jdstArray
!= NULL
)
370 AndroidEncrypt(vlc_keystore
*p_keystore
, void *p_ctx
, const uint8_t *p_src
,
371 size_t i_src_len
, uint8_t **pp_dst
)
374 JNIEnv
*p_env
= GET_ENV();
378 jobject jcipher
= NULL
;
379 jcipher
= CALL_STATICOBJ(Cipher
, Cipher
.getInstance
, fields
.VLC_CIPHER
);
380 if (CHECK_EXCEPTION())
383 size_t i_dst_len
= 0;
384 CALL_VOID(jcipher
, Cipher
.init
, fields
.Cipher
.ENCRYPT_MODE
, s_jkey
, NULL
);
385 if (CHECK_EXCEPTION())
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
391 jarray jivArray
= (jarray
) CALL_OBJ(jcipher
, Cipher
.getIV
);
392 if (jivArray
== NULL
)
395 jsize i_iv_len
= (*p_env
)->GetArrayLength(p_env
, jivArray
);
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);
413 AndroidDecrypt(vlc_keystore
*p_keystore
, void *p_ctx
, const uint8_t *p_src
,
414 size_t i_src_len
, uint8_t **pp_dst
)
417 JNIEnv
*p_env
= GET_ENV();
421 jobject jivArray
= NULL
, jiv
= NULL
, jcipher
= NULL
;
423 jcipher
= CALL_STATICOBJ(Cipher
, Cipher
.getInstance
, fields
.VLC_CIPHER
);
424 if (CHECK_EXCEPTION())
427 /* Get the IV located at the beginning of the secret */
428 size_t i_dst_len
= 0;
430 if (i_src_len
< sizeof(uint32_t))
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
))
437 jivArray
= (*p_env
)->NewByteArray(p_env
, i_iv_len
);
438 if (CHECK_EXCEPTION())
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())
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())
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
,
459 if (jivArray
!= NULL
)
467 * Generate a AES/CBC/PKCS7Padding key that will be stored by the Android
471 GenerateKey(vlc_keystore
*p_keystore
, JNIEnv
*p_env
, jstring jstringAlias
,
472 jstring jstringProvider
)
474 if (InitJniGenKey(p_keystore
, p_env
) != VLC_SUCCESS
)
477 jobject jkey
= NULL
, jbuilder
= NULL
, jspec
= 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())
491 jarray
= (*p_env
)->NewObjectArray(p_env
, 1, jstringClass
, NULL
);
492 if (CHECK_EXCEPTION())
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())
506 jkeyGen
= CALL_STATICOBJ(KeyGenerator
, KeyGenerator
.getInstance
,
507 fields
.KeyProperties
.KEY_ALGORITHM_AES
,
509 if (CHECK_EXCEPTION())
512 CALL_VOID(jkeyGen
, KeyGenerator
.init
, jspec
);
513 if (CHECK_EXCEPTION())
516 jkey
= CALL_OBJ(jkeyGen
, KeyGenerator
.generateKey
);
520 if (jbuilder
!= NULL
)
522 if (jstringClass
!= NULL
)
523 DEL_LREF(jstringClass
);
535 * Init JNI fields, fetch the key stored by Android or generate a new one
538 AndroidInit(vlc_keystore
*p_keystore
)
540 JNIEnv
*p_env
= GET_ENV();
544 if (InitJni(p_keystore
, p_env
) != VLC_SUCCESS
)
547 jobject jkeystore
= NULL
, jentry
= NULL
, jkey
= NULL
;
548 jstring jstringAlias
= NULL
, jstringProvider
= NULL
;
550 jstringAlias
= NEW_STR("LibVLCAndroid");
551 if (CHECK_EXCEPTION())
554 jstringProvider
= NEW_STR("AndroidKeyStore");
555 if (CHECK_EXCEPTION())
558 jkeystore
= CALL_STATICOBJ(KeyStore
, KeyStore
.getInstance
, jstringProvider
);
559 if (CHECK_EXCEPTION())
562 CALL_VOID(jkeystore
, KeyStore
.load
, NULL
);
563 if (CHECK_EXCEPTION())
566 jentry
= CALL_OBJ(jkeystore
, KeyStore
.getEntry
, jstringAlias
, NULL
);
567 if (CHECK_EXCEPTION())
571 jkey
= CALL_OBJ(jentry
, KeyStore
.SecretKeyEntry
.getSecretKey
);
572 if (CHECK_EXCEPTION())
577 jkey
= GenerateKey(p_keystore
, p_env
, jstringAlias
, jstringProvider
);
582 s_jkey
= NEW_GREF(jkey
);
585 if (jstringAlias
!= NULL
)
586 DEL_LREF(jstringAlias
);
587 if (jstringProvider
!= NULL
)
588 DEL_LREF(jstringProvider
);
589 if (jkeystore
!= NULL
)
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
);
606 AndroidInit(p_keystore
);
611 vlc_mutex_unlock(&s_lock
);
614 vlc_mutex_unlock(&s_lock
);
616 p_crypt
->pf_encrypt
= AndroidEncrypt
;
617 p_crypt
->pf_decrypt
= AndroidDecrypt
;