1use crate::{
2 error::{SecureEnvError, SecureEnvResult},
3 jni_tokens::*,
4 KeyOps, SecureEnvironmentOps,
5};
6use jni::{
7 objects::{JByteArray, JClass, JObject, JString, JValue},
8 sys::jobject,
9 JNIEnv,
10};
11use lazy_static::lazy_static;
12use libc::c_void;
13use p256::{ecdsa::Signature, elliptic_curve::sec1::ToEncodedPoint};
14use paste::paste;
15use std::sync::{Arc, Mutex};
16use x509_parser::{prelude::FromDer, x509::SubjectPublicKeyInfo};
17
18pub struct AndroidContext(*mut c_void);
19
20unsafe impl Send for AndroidContext {}
21unsafe impl Sync for AndroidContext {}
22
23lazy_static! {
24 static ref JAVA_VM: Arc<Mutex<Option<jni::JavaVM>>> = Arc::new(Mutex::new(None));
25}
26
27#[no_mangle]
30pub extern "system" fn Java_id_animo_SecureEnvironment_set_1env<'local>(
31 env: JNIEnv<'local>,
32 _class: JClass<'local>,
33) {
34 let vm = env.get_java_vm().unwrap();
35 *JAVA_VM.lock().unwrap() = Some(vm);
36}
37
38macro_rules! jni_handle_error {
39 ($env:expr, $err:ident, $e:expr) => {
40 match (|| -> $crate::error::SecureEnvResult<()> {
41 if $env
42 .exception_check()
43 .map_err(|e| $crate::error::SecureEnvError::$err(e.to_string()))?
44 {
45 let throwable = $env
46 .exception_occurred()
47 .map_err(|e| $crate::error::SecureEnvError::$err(e.to_string()))?;
48 $env.exception_clear()
49 .map_err(|e| $crate::error::SecureEnvError::$err(e.to_string()))?;
50
51 let message = $env
52 .call_method(
53 &throwable,
54 EXCEPTION_TO_STRING,
55 EXCEPTION_TO_STRING_SIG,
56 &[],
57 )
58 .and_then(|v| v.l())
59 .map_err(|e| {
60 $crate::error::SecureEnvError::UnableToCreateJavaValue(e.to_string())
61 })?;
62
63 let msg_rust: String = $env
64 .get_string(&message.into())
65 .map_err(|e| {
66 $crate::error::SecureEnvError::UnableToCreateJavaValue(e.to_string())
67 })?
68 .into();
69
70 return Err($crate::error::SecureEnvError::$err(msg_rust));
71 } else {
72 return Err($crate::error::SecureEnvError::$err($e.to_string()));
73 }
74 })() {
75 Ok(_) => $crate::error::SecureEnvError::$err($e.to_string()),
76 Err(e) => e,
77 }
78 };
79}
80
81macro_rules! jni_call_method {
82 ($env:expr, $cls:expr, $method:ident, $args:expr, $ret_typ:ident, $err:ident) => {
83 paste! {
84 $env.call_method($cls, $method, [<$method _SIG>], $args)
85 .and_then(|v| v.$ret_typ())
86 .map_err(|e| jni_handle_error!($env, $err, e))
87 }
88 };
89
90 ($env:expr, $cls:expr, $method:ident, $ret_typ:ident, $err:ident) => {
91 jni_call_method!($env, $cls, $method, &[], $ret_typ, $err)
92 };
93}
94
95macro_rules! jni_call_static_method {
96 ($env:expr, $cls:ident, $method:ident, $args:expr, $ret_typ:ident, $err:ident) => {
97 paste! {{
98 $env.call_static_method([<$cls _CLS>], $method, [<$method _SIG>], $args)
99 .and_then(|v| v.$ret_typ())
100 .map_err(|e| jni_handle_error!($env, $err, e))
101 }}
102 };
103
104 ($env:expr, $cls:ident, $method:ident, $ret_typ:ident, $err:ident) => {
105 jni_call_static_method!($env, $cls, $method, &[], $ret_typ, $err)
106 };
107}
108
109macro_rules! jni_get_static_field {
110 ($env:expr, $cls:expr, $method:ident, $ret_typ:ident, $err:ident) => {
111 paste! {{
112 $env.get_static_field($cls, $method, [<$method _SIG>])
113 .and_then(|v| v.$ret_typ())
114 .map_err(|e| jni_handle_error!($env, $err, e))
115 }}
116 };
117}
118
119macro_rules! jni_new_object {
120 ($env:expr, $cls:ident, $args:expr, $err:ident) => {
121 paste! {{
122 $env.new_object([<$cls _CLS>], [<$cls _CTOR_SIG>], $args)
123 .map_err(|e| jni_handle_error!($env, $err, e))
124 }}
125 };
126}
127
128macro_rules! jni_find_class {
129 ($env:expr, $cls:ident, $err:ident) => {
130 paste! {{
131 $env.find_class([<$cls _CLS>])
132 .map_err(|e| jni_handle_error!($env, $err, e))
133 }}
134 };
135}
136
137#[derive(Debug)]
138pub struct SecureEnvironment;
139
140impl SecureEnvironmentOps<Key> for SecureEnvironment {
141 fn generate_keypair(id: impl Into<String>, backed_by_biometrics: bool) -> SecureEnvResult<Key> {
142 let jvm = JAVA_VM.lock().map_err(|_| {
143 SecureEnvError::UnableToAttachJVMToThread("Could not acquire lock on JVM".to_owned())
144 })?;
145
146 let jvm = jvm
147 .as_ref()
148 .ok_or(SecureEnvError::UnableToAttachJVMToThread(
149 "JVM has not been set".to_owned(),
150 ))?;
151
152 let mut env = jvm
153 .attach_current_thread_as_daemon()
154 .map_err(|e| SecureEnvError::UnableToAttachJVMToThread(e.to_string()))?;
155
156 let id = id.into();
157
158 let id = env
159 .new_string(id)
160 .map_err(|e| SecureEnvError::UnableToCreateJavaValue(e.to_string()))?;
161
162 let purpose_sign = jni_get_static_field!(
163 env,
164 KEY_PROPERTIES_CLS,
165 KEY_PROPERTIES_PURPOSE_SIGN,
166 i,
167 UnableToGenerateKey
168 )?;
169
170 let builder = jni_new_object!(
171 env,
172 KEY_GEN_PARAMETER_SPEC_BUILDER,
173 &[(&id).into(), JValue::from(purpose_sign)],
174 UnableToGenerateKey
175 )?;
176
177 let kp_cls = jni_find_class!(env, KEY_PROPERTIES, UnableToGenerateKey)?;
178
179 let digest_sha256 = jni_get_static_field!(
180 env,
181 &kp_cls,
182 KEY_PROPERTIES_DIGEST_SHA256,
183 l,
184 UnableToGenerateKey
185 )?;
186
187 let string_cls = jni_find_class!(env, STRING, UnableToGenerateKey)?;
188
189 let args = env
190 .new_object_array(1, string_cls, &digest_sha256)
191 .map_err(|e| SecureEnvError::UnableToCreateJavaValue(e.to_string()))?;
192
193 let builder = jni_call_method!(
194 env,
195 builder,
196 KEY_GEN_PARAMETER_SPEC_BUILDER_SET_DIGESTS,
197 &[(&args).into()],
198 l,
199 UnableToGenerateKey
200 )?;
201
202 let builder = jni_call_method!(
203 env,
204 builder,
205 KEY_GEN_PARAMETER_SPEC_BUILDER_SET_KEY_SIZE,
206 &[JValue::from(256)],
207 l,
208 UnableToGenerateKey
209 )?;
210
211 let builder = if backed_by_biometrics {
212 let auth_biometric_strong = jni_get_static_field!(
213 env,
214 &kp_cls,
215 KEY_PROPERTIES_AUTH_BIOMETRIC_STRONG,
216 i,
217 UnableToGenerateKey
218 )?;
219
220 let builder = jni_call_method!(
221 env,
222 builder,
223 KEY_GEN_PARAMETER_SPEC_BUILDER_SET_USER_AUTHENTICATION_REQUIRED,
224 &[JValue::Bool(1)],
225 l,
226 UnableToGenerateKey
227 )?;
228
229 let builder = jni_call_method!(
230 env,
231 builder,
232 KEY_GEN_PARAMETER_SPEC_BUILDER_SET_INVALIDATED_BY_BIOMETRIC_ENROLLMENT,
233 &[JValue::Bool(1)],
234 l,
235 UnableToGenerateKey
236 )?;
237
238 jni_call_method!(
239 env,
240 builder,
241 KEY_GEN_PARAMETER_SPEC_BUILDER_SET_USER_AUTHENTICATION_PARAMETERS,
242 &[JValue::from(0), auth_biometric_strong.into()],
243 l,
244 UnableToGenerateKey
245 )?
246 } else {
247 builder
248 };
249
250 let current_activity_thread = jni_call_static_method!(
251 env,
252 ACTIVITY_THREAD,
253 ACTIVITY_THREAD_GET_CURRENT_ACTIVITY_THREAD,
254 l,
255 UnableToGenerateKey
256 )?;
257 let ctx = jni_call_method!(
258 env,
259 current_activity_thread,
260 ACTIVITY_THREAD_GET_APPLICATION,
261 l,
262 UnableToGenerateKey
263 )?;
264
265 let package_manager = jni_call_method!(
266 env,
267 ctx,
268 CONTEXT_GET_PACKAGE_MANAGER,
269 l,
270 UnableToGenerateKey
271 )?;
272
273 let hardware_keystore_token = env
274 .new_string("android.hardware.hardware_keystore")
275 .map_err(|e| SecureEnvError::UnableToCreateJavaValue(e.to_string()))?;
276
277 let required_hardware_keystore_version = 300;
282
283 let has_strongbox_support = jni_call_method!(
284 env,
285 &package_manager,
286 PACKAGE_MANAGER_HAS_SYSTEM_FEATURE,
287 &[
288 (&hardware_keystore_token).into(),
289 required_hardware_keystore_version.into()
290 ],
291 z,
292 UnableToGenerateKey
293 )?;
294
295 let builder = if has_strongbox_support {
296 jni_call_method!(
297 env,
298 &builder,
299 KEY_GEN_PARAMETER_SPEC_BUILDER_SET_IS_STRONG_BOX_BACKED,
300 &[JValue::Bool(1)],
301 l,
302 UnableToGenerateKey
303 )?
304 } else {
305 let required_device_unlocked_keystore_version = 41;
310
311 let has_device_unlocked_keystore_support = jni_call_method!(
312 env,
313 &package_manager,
314 PACKAGE_MANAGER_HAS_SYSTEM_FEATURE,
315 &[
316 (&hardware_keystore_token).into(),
317 required_device_unlocked_keystore_version.into()
318 ],
319 z,
320 UnableToGenerateKey
321 )?;
322
323 if !has_device_unlocked_keystore_support {
324 return Err(SecureEnvError::UnableToGenerateKey(
325 "Unable to generate keypair. Device has insufficient keystore support"
326 .to_owned(),
327 ));
328 }
329
330 builder
331 };
332
333 let algorithm = env
334 .new_string(EC_ALGORITHM)
335 .map_err(|e| SecureEnvError::UnableToCreateJavaValue(e.to_string()))?;
336
337 let provider = env
338 .new_string(ANDROID_KEY_STORE_PROVIDER)
339 .map_err(|e| SecureEnvError::UnableToCreateJavaValue(e.to_string()))?;
340
341 let key_pair_generator = jni_call_static_method!(
342 env,
343 KEY_PAIR_GENERATOR,
344 KEY_PAIR_GENERATOR_GET_INSTANCE,
345 &[(&algorithm).into(), (&provider).into()],
346 l,
347 UnableToGenerateKey
348 )?;
349
350 let params = jni_call_method!(
351 env,
352 &builder,
353 KEY_GEN_PARAMETER_SPEC_BUILDER_BUILD,
354 l,
355 UnableToGenerateKey
356 )?;
357
358 jni_call_method!(
359 env,
360 &key_pair_generator,
361 KEY_PAIR_GENERATOR_INITIALIZE,
362 &[(¶ms).into()],
363 v,
364 UnableToGenerateKey
365 )?;
366
367 let key = jni_call_method!(
368 env,
369 &key_pair_generator,
370 KEY_PAIR_GENERATOR_GENERATE_KEY_PAIR,
371 l,
372 UnableToGenerateKey
373 )?;
374
375 Ok(Key(Arc::new(Mutex::new(*key))))
376 }
377
378 fn get_keypair_by_id(id: impl Into<String>) -> SecureEnvResult<Key> {
379 let jvm = JAVA_VM.lock().map_err(|_| {
380 SecureEnvError::UnableToAttachJVMToThread("Could not acquire lock on JVM".to_owned())
381 })?;
382
383 let jvm = jvm
384 .as_ref()
385 .ok_or(SecureEnvError::UnableToAttachJVMToThread(
386 "JVM has not been set".to_owned(),
387 ))?;
388
389 let mut env = jvm
390 .attach_current_thread_as_daemon()
391 .map_err(|e| SecureEnvError::UnableToAttachJVMToThread(e.to_string()))?;
392
393 let provider = env
394 .new_string(ANDROID_KEY_STORE_PROVIDER)
395 .map_err(|e| SecureEnvError::UnableToCreateJavaValue(e.to_string()))?;
396
397 let id = id.into();
398 let id = env
399 .new_string(id)
400 .map_err(|e| SecureEnvError::UnableToCreateJavaValue(e.to_string()))?;
401
402 let key_store = jni_call_static_method!(
403 env,
404 KEY_STORE,
405 KEY_STORE_GET_INSTANCE,
406 &[(&provider).into()],
407 l,
408 UnableToGetKeyPairById
409 )?;
410
411 jni_call_method!(
412 env,
413 &key_store,
414 KEY_STORE_LOAD,
415 &[(&JObject::null()).into()],
416 v,
417 UnableToGetKeyPairById
418 )?;
419
420 let entry = jni_call_method!(
421 env,
422 &key_store,
423 KEY_STORE_GET_ENTRY,
424 &[(&id).into(), (&JObject::null()).into()],
425 l,
426 UnableToGetKeyPairById
427 )?;
428
429 let private_key = jni_call_method!(
430 env,
431 &entry,
432 KEY_STORE_ENTRY_GET_PRIVATE_KEY,
433 l,
434 UnableToGetKeyPairById
435 )?;
436
437 let certificate = jni_call_method!(
438 env,
439 &entry,
440 KEY_STORE_ENTRY_GET_CERTIFICATE,
441 l,
442 UnableToGetKeyPairById
443 )?;
444
445 let public_key = jni_call_method!(
446 env,
447 &certificate,
448 CERTIFICATE_GET_PUBLIC_KEY,
449 l,
450 UnableToGetKeyPairById
451 )?;
452
453 let key_pair = jni_new_object!(
454 env,
455 KEY_PAIR,
456 &[(&public_key).into(), (&private_key).into()],
457 UnableToGetKeyPairById
458 )?;
459
460 Ok(Key(Arc::new(Mutex::new(*key_pair))))
461 }
462}
463
464#[derive(Debug)]
465pub struct Key(Arc<Mutex<jobject>>);
466
467unsafe impl Send for Key {}
468unsafe impl Sync for Key {}
469
470impl Key {
471 unsafe fn get_object(&self) -> JObject {
472 let raw = self.0.lock().unwrap();
473 JObject::from_raw(*raw)
474 }
475}
476
477impl KeyOps for Key {
478 fn get_public_key(&self) -> SecureEnvResult<Vec<u8>> {
479 let jvm = JAVA_VM.lock().map_err(|_| {
480 SecureEnvError::UnableToAttachJVMToThread("Could not acquire lock on JVM".to_owned())
481 })?;
482
483 let jvm = jvm
484 .as_ref()
485 .ok_or(SecureEnvError::UnableToAttachJVMToThread(
486 "JVM has not been set".to_owned(),
487 ))?;
488
489 let mut env = jvm
490 .attach_current_thread_as_daemon()
491 .map_err(|e| SecureEnvError::UnableToAttachJVMToThread(e.to_string()))?;
492
493 let key = unsafe { self.get_object() };
494
495 let public_key = jni_call_method!(env, &key, KEY_PAIR_GET_PUBLIC, l, UnableToGetPublicKey)?;
496
497 let public_key_encoded = jni_call_method!(
498 env,
499 &public_key,
500 PUBLIC_KEY_GET_ENCODED,
501 l,
502 UnableToGetPublicKey
503 )?;
504
505 let format = jni_call_method!(
506 env,
507 &public_key,
508 PUBLIC_KEY_GET_FORMAT,
509 l,
510 UnableToGetPublicKey
511 )?;
512
513 let format = JString::from(format);
514 let format = env
515 .get_string(&format)
516 .map_err(|e| SecureEnvError::UnableToCreateJavaValue(e.to_string()))?;
517 let format = format
518 .to_str()
519 .map_err(|e| SecureEnvError::UnableToGetPublicKey(e.to_string()))?;
520
521 if format != "X.509" {
522 return Err(SecureEnvError::UnableToGetPublicKey(format!(
523 "Unexpected key format. Expected 'X.509', received: '{format}'"
524 )));
525 }
526
527 let public_key: JByteArray = public_key_encoded.into();
528
529 let public_key = env
530 .convert_byte_array(public_key)
531 .map_err(|e| SecureEnvError::UnableToCreateJavaValue(e.to_string()))?;
532
533 let spki = SubjectPublicKeyInfo::from_der(&public_key)
534 .map_err(|e| SecureEnvError::UnableToGetPublicKey(e.to_string()))?;
535
536 let spki_data = spki.1.subject_public_key.data;
537
538 let public_key = p256::PublicKey::from_sec1_bytes(&spki_data)
539 .map_err(|e| SecureEnvError::UnableToGetPublicKey(e.to_string()))?;
540
541 let encoded_point = public_key.to_encoded_point(true);
542
543 let public_key = encoded_point.to_bytes().to_vec();
544
545 Ok(public_key)
546 }
547
548 fn sign(&self, msg: &[u8]) -> SecureEnvResult<Vec<u8>> {
555 let jvm = JAVA_VM.lock().map_err(|_| {
556 SecureEnvError::UnableToAttachJVMToThread("Could not acquire lock on JVM".to_owned())
557 })?;
558
559 let jvm = jvm
560 .as_ref()
561 .ok_or(SecureEnvError::UnableToAttachJVMToThread(
562 "JVM has not been set".to_owned(),
563 ))?;
564
565 let mut env = jvm
566 .attach_current_thread_as_daemon()
567 .map_err(|e| SecureEnvError::UnableToAttachJVMToThread(e.to_string()))?;
568
569 let key = unsafe { self.get_object() };
570
571 let algorithm = env
572 .new_string(SHA256_WITH_ECDSA_ALGO)
573 .map_err(|e| SecureEnvError::UnableToCreateJavaValue(e.to_string()))?;
574
575 let private_key =
576 jni_call_method!(env, &key, KEY_PAIR_GET_PRIVATE, l, UnableToCreateSignature)?;
577
578 let signature_instance = jni_call_static_method!(
579 env,
580 SIGNATURE,
581 SIGNATURE_GET_INSTANCE,
582 &[(&algorithm).into()],
583 l,
584 UnableToCreateSignature
585 )?;
586
587 jni_call_method!(
588 env,
589 &signature_instance,
590 SIGNATURE_INIT_SIGN,
591 &[(&private_key).into()],
592 v,
593 UnableToCreateSignature
594 )?;
595
596 let b_arr = env
597 .byte_array_from_slice(msg)
598 .map_err(|e| SecureEnvError::UnableToCreateJavaValue(e.to_string()))?;
599
600 jni_call_method!(
601 env,
602 &signature_instance,
603 SIGNATURE_UPDATE,
604 &[(&b_arr).into()],
605 v,
606 UnableToCreateSignature
607 )?;
608
609 let signature = jni_call_method!(
610 env,
611 &signature_instance,
612 SIGNATURE_SIGN,
613 l,
614 UnableToCreateSignature
615 )?;
616
617 let signature: JByteArray = signature.into();
618
619 let signature = env
620 .convert_byte_array(signature)
621 .map_err(|e| SecureEnvError::UnableToCreateJavaValue(e.to_string()))?;
622
623 let signature = Signature::from_der(&signature)
624 .map_err(|e| SecureEnvError::UnableToCreateSignature(e.to_string()))?;
625
626 let r = signature.r();
627 let s = signature.s();
628 let compact_signature = [r.to_bytes(), s.to_bytes()].concat();
629
630 Ok(compact_signature)
631 }
632}