android_native_keyring_store/lib.rs
1/*!
2# Keyring-compatible Android-native credential store
3
4This crate uses two Android-native features---SharedPreferences and the
5Keystore---to provide secure storage of passwords and other sensitive data
6for the
7[keyring ecosystem](https://github.com/open-source-cooperative/keyring-rs/wiki/Keyring).
8
9# Named Credential Stores
10
11This crate supports multiple, named credential stores, each
12backed by a dedicated SharedPreferences file and a dedicated Android
13Keystore entry. The implementation, found in
14the [by_store] module, supports search and doesn't allow for ambiguity
15or provide any attributes on credentials.
16
17# Legacy Credential Store
18
19Earlier versions of this crate provided a single store that used one SharedPreferences
20file and Keystore entry _per service name_, rather than _per store name_. This
21legacy implementation, found in the [by_service] module, does not support search and leaves
22keys behind even when all of their associated credentials are deleted. It is still
23available under the `legacy` feature flag via the [LegacyStore::from_ndk_context]
24constructor, but it is deprecated and may be removed in future versions of the crate. All
25client applications are advised to migrate any existing credentials from legacy storage to
26a named store. See the [Migration Guide](by_service#migration-guide) for details.
27
28## Application Requirements
29
30This crate compiles to produce a native library that can be loaded into an Android
31application. Because this crate gets its Android application context from the
32[ndk-context crate](https://crates.io/crates/ndk-context), applications that use
33this crate must initialize the `application-context` object provided by the `ndk-context`
34crate before they can create credential stores. The
35[README](https://github.com/open-source-cooperative/android-native-keyring-store) for this
36crate provides detailed instructions for how to do this.
37
38 */
39
40use std::ffi::c_void;
41use std::sync::OnceLock;
42
43use jni::{
44 JNIEnv,
45 objects::{GlobalRef, JObject},
46};
47
48pub mod by_store;
49pub use by_store::Cred;
50pub use by_store::Store;
51
52#[cfg(feature = "legacy")]
53pub mod by_service;
54#[cfg(feature = "legacy")]
55pub use by_service::Cred as LegacyCred;
56#[cfg(feature = "legacy")]
57pub use by_service::Store as LegacyStore;
58
59#[cfg(feature = "android-log")]
60mod android_log;
61mod cipher;
62mod crypto;
63mod error;
64mod keystore;
65mod methods;
66mod shared_preferences;
67
68#[cfg(feature = "compile-tests")]
69pub mod tests;
70
71/// Initialize the NDK context.
72///
73/// This JNI function can be called from your application's Java
74/// code to prepare the NDK context for use by this crate.
75/// (Some Android application frameworks do this for you.)
76///
77/// You can invoke this function automatically by defining it as
78/// a companion object's `init` function, as shown in the example
79/// below.
80/// ```java
81/// package io.crates.keyring
82/// import android.content.Context
83/// class Keyring {
84/// companion object {
85/// init {
86/// System.loadLibrary("android_native_keyring_store")
87/// }
88/// external fun initializeNdkContext(context: Context);
89/// }
90/// }
91/// ```
92#[allow(non_snake_case)]
93#[unsafe(no_mangle)]
94pub extern "system" fn Java_io_crates_keyring_Keyring_00024Companion_initializeNdkContext(
95 env: JNIEnv,
96 _class: JObject,
97 context: JObject,
98) {
99 static REF: OnceLock<Option<GlobalRef>> = OnceLock::new();
100 REF.get_or_init(|| match env.new_global_ref(&context) {
101 Ok(ref_) => {
102 let vm = env.get_java_vm().unwrap();
103 let vm = vm.get_java_vm_pointer() as *mut c_void;
104 unsafe {
105 ndk_context::initialize_android_context(vm, ref_.as_obj().as_raw() as _);
106 }
107 Some(ref_)
108 }
109 Err(e) => {
110 tracing::error!(%e, "error creating global reference for context");
111 tracing::debug!(?e);
112 None
113 }
114 });
115}