mirror of
https://github.com/github/codeql.git
synced 2026-04-29 18:55:14 +02:00
Query for cleartext storage using Android SharedPreferences
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
public void testSetSharedPrefs(Context context, String name, String password)
|
||||
{
|
||||
{
|
||||
// BAD - save sensitive information in cleartext
|
||||
SharedPreferences sharedPrefs = context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE);
|
||||
Editor editor = sharedPrefs.edit();
|
||||
editor.putString("name", name);
|
||||
editor.putString("password", password);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
{
|
||||
// GOOD - save sensitive information in encrypted format
|
||||
SharedPreferences sharedPrefs = context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE);
|
||||
Editor editor = sharedPrefs.edit();
|
||||
editor.putString("name", encrypt(name));
|
||||
editor.putString("password", encrypt(password));
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
{
|
||||
// GOOD - save sensitive information using the built-in `EncryptedSharedPreferences` class in androidx.
|
||||
MasterKey masterKey = new MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
|
||||
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
||||
.build();
|
||||
|
||||
SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
|
||||
context,
|
||||
"secret_shared_prefs",
|
||||
masterKey,
|
||||
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);
|
||||
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString("name", name);
|
||||
editor.putString("password", password);
|
||||
editor.commit();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
<code>SharedPreferences</code> is an Android API that stores application preferences using simple sets of data values. Almost every Android application uses this API. It allows to easily save, alter, and retrieve the values stored in <code>SharedPreferences</code>. However, sensitive information shall not be saved in cleartext. Otherwise it can be accessed by any process or user on rooted devices, or can be disclosed through chained vulnerabilities e.g. unexpected access to its private storage through exposed components.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Use the <code>EncryptedSharedPreferences</code> API or other encryption algorithms for storing sensitive information.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
In the first example, sensitive user information is stored in cleartext.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In the second and third examples, the code encrypts sensitive information before saving to the device.
|
||||
</p>
|
||||
<sample src="ClearTextStorageSharedPrefs.java" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
CWE:
|
||||
<a href="https://cwe.mitre.org/data/definitions/312.html">CWE-312: Cleartext Storage of Sensitive Information</a>
|
||||
</li>
|
||||
<li>
|
||||
Android Developers:
|
||||
<a href="https://developer.android.com/topic/security/data">Work with data more securely</a>
|
||||
</li>
|
||||
<li>
|
||||
PRO ANDROID DEV:
|
||||
<a href="https://proandroiddev.com/encrypted-preferences-in-android-af57a89af7c8">Encrypted Preferences in Android</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @name Cleartext storage of sensitive information using `SharedPreferences` on Android
|
||||
* @description Cleartext Storage of Sensitive Information using SharedPreferences on Android allows user with root privileges to access or unexpected exposure from chained vulnerabilities.
|
||||
* @kind problem
|
||||
* @id java/android/cleartext-storage-shared-prefs
|
||||
* @tags security
|
||||
* external/cwe/cwe-312
|
||||
*/
|
||||
|
||||
import java
|
||||
import SensitiveStorage
|
||||
|
||||
from SensitiveSource data, SharedPreferencesEditor s, Expr input, Expr store
|
||||
where
|
||||
input = s.getAnInput() and
|
||||
store = s.getAStore() and
|
||||
data.flowsToCached(input) and
|
||||
// Exclude results in test code.
|
||||
not testMethod(store.getEnclosingCallable()) and
|
||||
not testMethod(data.getEnclosingCallable())
|
||||
select store, "'SharedPreferences' class $@ containing $@ is stored here. Data was added $@.", s,
|
||||
s.toString(), data, "sensitive data", input, "here"
|
||||
@@ -1,6 +1,7 @@
|
||||
import java
|
||||
import semmle.code.java.frameworks.Properties
|
||||
import semmle.code.java.frameworks.JAXB
|
||||
import semmle.code.java.frameworks.android.SharedPreferences
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.dataflow.DataFlow3
|
||||
import semmle.code.java.dataflow.DataFlow4
|
||||
@@ -28,6 +29,10 @@ private class SensitiveSourceFlowConfig extends TaintTracking::Configuration {
|
||||
m.getMethod() instanceof PropertiesSetPropertyMethod and sink.asExpr() = m.getArgument(1)
|
||||
)
|
||||
or
|
||||
exists(MethodAccess m |
|
||||
m.getMethod() instanceof SharedPreferencesSetMethod and sink.asExpr() = m.getArgument(1)
|
||||
)
|
||||
or
|
||||
sink.asExpr() = getInstanceInput(_, _)
|
||||
}
|
||||
|
||||
@@ -243,3 +248,72 @@ class Marshallable extends ClassStore {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/* Holds if the method call is a setter method of `SharedPreferences`. */
|
||||
private predicate sharedPreferencesInput(DataFlow::Node sharedPrefs, Expr input) {
|
||||
exists(MethodAccess m |
|
||||
m.getMethod() instanceof SharedPreferencesSetMethod and
|
||||
input = m.getArgument(1) and
|
||||
sharedPrefs.asExpr() = m.getQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
/* Holds if the method call is the save method of `SharedPreferences`. */
|
||||
private predicate sharedPreferencesStore(DataFlow::Node sharedPrefs, Expr store) {
|
||||
exists(MethodAccess m |
|
||||
m.getMethod() instanceof SharedPreferencesStoreMethod and
|
||||
store = m and
|
||||
sharedPrefs.asExpr() = m.getQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
/* Flow from `SharedPreferences` to the method call changing its value. */
|
||||
class SharedPreferencesFlowConfig extends TaintTracking::Configuration {
|
||||
SharedPreferencesFlowConfig() { this = "SensitiveStorage::SharedPreferencesFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) {
|
||||
src.asExpr() instanceof SharedPreferencesEditor
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sharedPreferencesInput(sink, _) or
|
||||
sharedPreferencesStore(sink, _)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node n) {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod().getName().toLowerCase().matches("%encrypt%") and
|
||||
n.asExpr() = ma.getAnArgument()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** The call to get a `SharedPreferences.Editor` object, which can set shared preferences or be stored to device. */
|
||||
class SharedPreferencesEditor extends MethodAccess {
|
||||
SharedPreferencesEditor() {
|
||||
this.getMethod() instanceof SharedPreferencesGetEditorMethod and
|
||||
not exists(
|
||||
MethodAccess cma // not exists `SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(...)`
|
||||
|
|
||||
cma.getQualifier().getType() instanceof TypeEncryptedSharedPreferences and
|
||||
cma.getMethod().hasName("create") and
|
||||
cma.getParent().(VariableAssign).getDestVar().getAnAccess() = this.getQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets an input, for example `input` in `editor.putString("password", password);`. */
|
||||
Expr getAnInput() {
|
||||
exists(SharedPreferencesFlowConfig conf, DataFlow::Node n |
|
||||
sharedPreferencesInput(n, result) and
|
||||
conf.hasFlow(DataFlow::exprNode(this), n)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a store, for example `editor.commit();`. */
|
||||
Expr getAStore() {
|
||||
exists(SharedPreferencesFlowConfig conf, DataFlow::Node n |
|
||||
sharedPreferencesStore(n, result) and
|
||||
conf.hasFlow(DataFlow::exprNode(this), n)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/* Definitions related to `android.content.SharedPreferences`. */
|
||||
import semmle.code.java.Type
|
||||
|
||||
/* The interface `android.content.SharedPreferences` */
|
||||
library class TypeSharedPreferences extends Interface {
|
||||
TypeSharedPreferences() { hasQualifiedName("android.content", "SharedPreferences") }
|
||||
}
|
||||
|
||||
/* The class `androidx.security.crypto.EncryptedSharedPreferences`, which implements `SharedPreferences` with encryption support. */
|
||||
library class TypeEncryptedSharedPreferences extends Class {
|
||||
TypeEncryptedSharedPreferences() {
|
||||
hasQualifiedName("androidx.security.crypto", "EncryptedSharedPreferences")
|
||||
}
|
||||
}
|
||||
|
||||
/* A getter method of `android.content.SharedPreferences`. */
|
||||
library class SharedPreferencesGetMethod extends Method {
|
||||
SharedPreferencesGetMethod() {
|
||||
getDeclaringType() instanceof TypeSharedPreferences and
|
||||
getName().matches("get%")
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns `android.content.SharedPreferences.Editor` from the `edit` call of `android.content.SharedPreferences`. */
|
||||
library class SharedPreferencesGetEditorMethod extends Method {
|
||||
SharedPreferencesGetEditorMethod() {
|
||||
getDeclaringType() instanceof TypeSharedPreferences and
|
||||
hasName("edit") and
|
||||
getReturnType() instanceof TypeSharedPreferencesEditor
|
||||
}
|
||||
}
|
||||
|
||||
/* Definitions related to `android.content.SharedPreferences.Editor`. */
|
||||
library class TypeSharedPreferencesEditor extends Interface {
|
||||
TypeSharedPreferencesEditor() { hasQualifiedName("android.content", "SharedPreferences$Editor") }
|
||||
}
|
||||
|
||||
/* A setter method for `android.content.SharedPreferences`. */
|
||||
library class SharedPreferencesSetMethod extends Method {
|
||||
SharedPreferencesSetMethod() {
|
||||
getDeclaringType() instanceof TypeSharedPreferencesEditor and
|
||||
getName().matches("put%")
|
||||
}
|
||||
}
|
||||
|
||||
/* A setter method for `android.content.SharedPreferences`. */
|
||||
library class SharedPreferencesStoreMethod extends Method {
|
||||
SharedPreferencesStoreMethod() {
|
||||
getDeclaringType() instanceof TypeSharedPreferencesEditor and
|
||||
hasName(["commit", "apply"])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
| ClearTextStorageSharedPrefs.java:16:3:16:17 | commit(...) | 'SharedPreferences' class $@ containing $@ is stored here. Data was added $@. | ClearTextStorageSharedPrefs.java:13:19:13:36 | edit(...) | edit(...) | ClearTextStorageSharedPrefs.java:15:32:15:39 | password | sensitive data | ClearTextStorageSharedPrefs.java:15:32:15:39 | password | here |
|
||||
@@ -0,0 +1,54 @@
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import androidx.security.crypto.MasterKey;
|
||||
import androidx.security.crypto.EncryptedSharedPreferences;
|
||||
|
||||
/** Android activity that tests saving sensitive information in `SharedPreferences` */
|
||||
public class ClearTextStorageSharedPrefs extends Activity {
|
||||
// BAD - save sensitive information in cleartext
|
||||
public void testSetSharedPrefs1(Context context, String name, String password) {
|
||||
SharedPreferences sharedPrefs = context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE);
|
||||
Editor editor = sharedPrefs.edit();
|
||||
editor.putString("name", name);
|
||||
editor.putString("password", password);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
// GOOD - save sensitive information in encrypted format
|
||||
public void testSetSharedPrefs2(Context context, String name, String password) {
|
||||
SharedPreferences sharedPrefs = context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE);
|
||||
Editor editor = sharedPrefs.edit();
|
||||
editor.putString("name", encrypt(name));
|
||||
editor.putString("password", encrypt(password));
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
private static String encrypt(String cleartext) {
|
||||
//Use an encryption or hashing algorithm in real world. The demo below just returns an arbitrary value.
|
||||
String cipher = "whatever_encrypted";
|
||||
return cipher;
|
||||
}
|
||||
|
||||
// GOOD - save sensitive information using the built-in `EncryptedSharedPreferences` class in androidx.
|
||||
public void testSetSharedPrefs3(Context context, String name, String password) {
|
||||
MasterKey masterKey = new MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
|
||||
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
||||
.build();
|
||||
|
||||
SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
|
||||
context,
|
||||
"secret_shared_prefs",
|
||||
masterKey,
|
||||
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);
|
||||
|
||||
// Use the shared preferences and editor as you normally would
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString("name", name);
|
||||
editor.putString("password", password);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-312/ClearTextStorageSharedPrefs.ql
|
||||
@@ -0,0 +1 @@
|
||||
// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/google-android-9.0.0
|
||||
@@ -26,6 +26,84 @@ import android.os.Bundle;
|
||||
* broadcasting and receiving intents, etc.
|
||||
*/
|
||||
public abstract class Context {
|
||||
/**
|
||||
* File creation mode: the default mode, where the created file can only
|
||||
* be accessed by the calling application (or all applications sharing the
|
||||
* same user ID).
|
||||
* @see #MODE_WORLD_READABLE
|
||||
* @see #MODE_WORLD_WRITEABLE
|
||||
*/
|
||||
public static final int MODE_PRIVATE = 0x0000;
|
||||
|
||||
/**
|
||||
* @deprecated Creating world-readable files is very dangerous, and likely
|
||||
* to cause security holes in applications. It is strongly discouraged;
|
||||
* instead, applications should use more formal mechanism for interactions
|
||||
* such as {@link ContentProvider}, {@link BroadcastReceiver}, and
|
||||
* {@link android.app.Service}. There are no guarantees that this
|
||||
* access mode will remain on a file, such as when it goes through a
|
||||
* backup and restore.
|
||||
* File creation mode: allow all other applications to have read access
|
||||
* to the created file.
|
||||
* @see #MODE_PRIVATE
|
||||
* @see #MODE_WORLD_WRITEABLE
|
||||
*/
|
||||
@Deprecated
|
||||
public static final int MODE_WORLD_READABLE = 0x0001;
|
||||
|
||||
/**
|
||||
* @deprecated Creating world-writable files is very dangerous, and likely
|
||||
* to cause security holes in applications. It is strongly discouraged;
|
||||
* instead, applications should use more formal mechanism for interactions
|
||||
* such as {@link ContentProvider}, {@link BroadcastReceiver}, and
|
||||
* {@link android.app.Service}. There are no guarantees that this
|
||||
* access mode will remain on a file, such as when it goes through a
|
||||
* backup and restore.
|
||||
* File creation mode: allow all other applications to have write access
|
||||
* to the created file.
|
||||
* @see #MODE_PRIVATE
|
||||
* @see #MODE_WORLD_READABLE
|
||||
*/
|
||||
@Deprecated
|
||||
public static final int MODE_WORLD_WRITEABLE = 0x0002;
|
||||
|
||||
/**
|
||||
* File creation mode: for use with {@link #openFileOutput}, if the file
|
||||
* already exists then write data to the end of the existing file
|
||||
* instead of erasing it.
|
||||
* @see #openFileOutput
|
||||
*/
|
||||
public static final int MODE_APPEND = 0x8000;
|
||||
|
||||
/**
|
||||
* SharedPreference loading flag: when set, the file on disk will
|
||||
* be checked for modification even if the shared preferences
|
||||
* instance is already loaded in this process. This behavior is
|
||||
* sometimes desired in cases where the application has multiple
|
||||
* processes, all writing to the same SharedPreferences file.
|
||||
* Generally there are better forms of communication between
|
||||
* processes, though.
|
||||
*
|
||||
* <p>This was the legacy (but undocumented) behavior in and
|
||||
* before Gingerbread (Android 2.3) and this flag is implied when
|
||||
* targetting such releases. For applications targetting SDK
|
||||
* versions <em>greater than</em> Android 2.3, this flag must be
|
||||
* explicitly set if desired.
|
||||
*
|
||||
* @see #getSharedPreferences
|
||||
*/
|
||||
public static final int MODE_MULTI_PROCESS = 0x0004;
|
||||
|
||||
/**
|
||||
* Database open flag: when set, the database is opened with write-ahead
|
||||
* logging enabled by default.
|
||||
*
|
||||
* @see #openOrCreateDatabase(String, int, CursorFactory)
|
||||
* @see #openOrCreateDatabase(String, int, CursorFactory, DatabaseErrorHandler)
|
||||
* @see SQLiteDatabase#enableWriteAheadLogging
|
||||
*/
|
||||
public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 0x0008;
|
||||
|
||||
/**
|
||||
* Return the context of the single, global Application object of the current
|
||||
* process. This generally should only be used if you need a Context whose
|
||||
@@ -76,19 +154,42 @@ public abstract class Context {
|
||||
public abstract File getFileStreamPath(String name);
|
||||
|
||||
/**
|
||||
* Returns the absolute path on the filesystem where a file created with
|
||||
* {@link #getSharedPreferences(String, int)} is stored.
|
||||
* <p>
|
||||
* The returned path may change over time if the calling app is moved to an
|
||||
* adopted storage device, so only relative paths should be persisted.
|
||||
* {@hide}
|
||||
* Return the full path to the shared prefs file for the given prefs group name.
|
||||
*
|
||||
* @param name The name of the shared preferences for which you would like to
|
||||
* get a path.
|
||||
* @return An absolute path to the given file.
|
||||
* @see #getSharedPreferences(String, int)
|
||||
* @removed
|
||||
* <p>Note: this is not generally useful for applications, since they should
|
||||
* not be directly accessing the file system.
|
||||
*/
|
||||
public abstract File getSharedPreferencesPath(String name);
|
||||
public abstract File getSharedPrefsFile(String name);
|
||||
|
||||
/**
|
||||
* Retrieve and hold the contents of the preferences file 'name', returning
|
||||
* a SharedPreferences through which you can retrieve and modify its
|
||||
* values. Only one instance of the SharedPreferences object is returned
|
||||
* to any callers for the same name, meaning they will see each other's
|
||||
* edits as soon as they are made.
|
||||
*
|
||||
* @param name Desired preferences file. If a preferences file by this name
|
||||
* does not exist, it will be created when you retrieve an
|
||||
* editor (SharedPreferences.edit()) and then commit changes (Editor.commit()).
|
||||
* @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the
|
||||
* default operation, {@link #MODE_WORLD_READABLE}
|
||||
* and {@link #MODE_WORLD_WRITEABLE} to control permissions. The bit
|
||||
* {@link #MODE_MULTI_PROCESS} can also be used if multiple processes
|
||||
* are mutating the same SharedPreferences file. {@link #MODE_MULTI_PROCESS}
|
||||
* is always on in apps targetting Gingerbread (Android 2.3) and below, and
|
||||
* off by default in later versions.
|
||||
*
|
||||
* @return Returns the single SharedPreferences instance that can be used
|
||||
* to retrieve and modify the preference values.
|
||||
*
|
||||
* @see #MODE_PRIVATE
|
||||
* @see #MODE_WORLD_READABLE
|
||||
* @see #MODE_WORLD_WRITEABLE
|
||||
* @see #MODE_MULTI_PROCESS
|
||||
*/
|
||||
public abstract SharedPreferences getSharedPreferences(String name,
|
||||
int mode);
|
||||
|
||||
/**
|
||||
* Returns the absolute path to the directory on the filesystem where all
|
||||
|
||||
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.content;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
/**
|
||||
* Interface for accessing and modifying preference data returned by {@link
|
||||
* Context#getSharedPreferences}. For any particular set of preferences,
|
||||
* there is a single instance of this class that all clients share.
|
||||
* Modifications to the preferences must go through an {@link Editor} object
|
||||
* to ensure the preference values remain in a consistent state and control
|
||||
* when they are committed to storage. Objects that are returned from the
|
||||
* various <code>get</code> methods must be treated as immutable by the application.
|
||||
*
|
||||
* <p><em>Note: currently this class does not support use across multiple
|
||||
* processes. This will be added later.</em>
|
||||
*
|
||||
* <div class="special reference">
|
||||
* <h3>Developer Guides</h3>
|
||||
* <p>For more information about using SharedPreferences, read the
|
||||
* <a href="{@docRoot}guide/topics/data/data-storage.html#pref">Data Storage</a>
|
||||
* developer guide.</p></div>
|
||||
*
|
||||
* @see Context#getSharedPreferences
|
||||
*/
|
||||
public interface SharedPreferences {
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when a shared
|
||||
* preference is changed.
|
||||
*/
|
||||
public interface OnSharedPreferenceChangeListener {
|
||||
/**
|
||||
* Called when a shared preference is changed, added, or removed. This
|
||||
* may be called even if a preference is set to its existing value.
|
||||
*
|
||||
* <p>This callback will be run on your main thread.
|
||||
*
|
||||
* @param sharedPreferences The {@link SharedPreferences} that received
|
||||
* the change.
|
||||
* @param key The key of the preference that was changed, added, or
|
||||
* removed.
|
||||
*/
|
||||
void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface used for modifying values in a {@link SharedPreferences}
|
||||
* object. All changes you make in an editor are batched, and not copied
|
||||
* back to the original {@link SharedPreferences} until you call {@link #commit}
|
||||
* or {@link #apply}
|
||||
*/
|
||||
public interface Editor {
|
||||
/**
|
||||
* Set a String value in the preferences editor, to be written back once
|
||||
* {@link #commit} or {@link #apply} are called.
|
||||
*
|
||||
* @param key The name of the preference to modify.
|
||||
* @param value The new value for the preference. Supplying {@code null}
|
||||
* as the value is equivalent to calling {@link #remove(String)} with
|
||||
* this key.
|
||||
*
|
||||
* @return Returns a reference to the same Editor object, so you can
|
||||
* chain put calls together.
|
||||
*/
|
||||
Editor putString(String key, String value);
|
||||
|
||||
/**
|
||||
* Set a set of String values in the preferences editor, to be written
|
||||
* back once {@link #commit} is called.
|
||||
*
|
||||
* @param key The name of the preference to modify.
|
||||
* @param values The set of new values for the preference. Passing {@code null}
|
||||
* for this argument is equivalent to calling {@link #remove(String)} with
|
||||
* this key.
|
||||
* @return Returns a reference to the same Editor object, so you can
|
||||
* chain put calls together.
|
||||
*/
|
||||
Editor putStringSet(String key, Set<String> values);
|
||||
|
||||
/**
|
||||
* Set an int value in the preferences editor, to be written back once
|
||||
* {@link #commit} or {@link #apply} are called.
|
||||
*
|
||||
* @param key The name of the preference to modify.
|
||||
* @param value The new value for the preference.
|
||||
*
|
||||
* @return Returns a reference to the same Editor object, so you can
|
||||
* chain put calls together.
|
||||
*/
|
||||
Editor putInt(String key, int value);
|
||||
|
||||
/**
|
||||
* Set a long value in the preferences editor, to be written back once
|
||||
* {@link #commit} or {@link #apply} are called.
|
||||
*
|
||||
* @param key The name of the preference to modify.
|
||||
* @param value The new value for the preference.
|
||||
*
|
||||
* @return Returns a reference to the same Editor object, so you can
|
||||
* chain put calls together.
|
||||
*/
|
||||
Editor putLong(String key, long value);
|
||||
|
||||
/**
|
||||
* Set a float value in the preferences editor, to be written back once
|
||||
* {@link #commit} or {@link #apply} are called.
|
||||
*
|
||||
* @param key The name of the preference to modify.
|
||||
* @param value The new value for the preference.
|
||||
*
|
||||
* @return Returns a reference to the same Editor object, so you can
|
||||
* chain put calls together.
|
||||
*/
|
||||
Editor putFloat(String key, float value);
|
||||
|
||||
/**
|
||||
* Set a boolean value in the preferences editor, to be written back
|
||||
* once {@link #commit} or {@link #apply} are called.
|
||||
*
|
||||
* @param key The name of the preference to modify.
|
||||
* @param value The new value for the preference.
|
||||
*
|
||||
* @return Returns a reference to the same Editor object, so you can
|
||||
* chain put calls together.
|
||||
*/
|
||||
Editor putBoolean(String key, boolean value);
|
||||
/**
|
||||
* Mark in the editor that a preference value should be removed, which
|
||||
* will be done in the actual preferences once {@link #commit} is
|
||||
* called.
|
||||
*
|
||||
* <p>Note that when committing back to the preferences, all removals
|
||||
* are done first, regardless of whether you called remove before
|
||||
* or after put methods on this editor.
|
||||
*
|
||||
* @param key The name of the preference to remove.
|
||||
*
|
||||
* @return Returns a reference to the same Editor object, so you can
|
||||
* chain put calls together.
|
||||
*/
|
||||
Editor remove(String key);
|
||||
/**
|
||||
* Mark in the editor to remove <em>all</em> values from the
|
||||
* preferences. Once commit is called, the only remaining preferences
|
||||
* will be any that you have defined in this editor.
|
||||
*
|
||||
* <p>Note that when committing back to the preferences, the clear
|
||||
* is done first, regardless of whether you called clear before
|
||||
* or after put methods on this editor.
|
||||
*
|
||||
* @return Returns a reference to the same Editor object, so you can
|
||||
* chain put calls together.
|
||||
*/
|
||||
Editor clear();
|
||||
/**
|
||||
* Commit your preferences changes back from this Editor to the
|
||||
* {@link SharedPreferences} object it is editing. This atomically
|
||||
* performs the requested modifications, replacing whatever is currently
|
||||
* in the SharedPreferences.
|
||||
*
|
||||
* <p>Note that when two editors are modifying preferences at the same
|
||||
* time, the last one to call commit wins.
|
||||
*
|
||||
* <p>If you don't care about the return value and you're
|
||||
* using this from your application's main thread, consider
|
||||
* using {@link #apply} instead.
|
||||
*
|
||||
* @return Returns true if the new values were successfully written
|
||||
* to persistent storage.
|
||||
*/
|
||||
boolean commit();
|
||||
/**
|
||||
* Commit your preferences changes back from this Editor to the
|
||||
* {@link SharedPreferences} object it is editing. This atomically
|
||||
* performs the requested modifications, replacing whatever is currently
|
||||
* in the SharedPreferences.
|
||||
*
|
||||
* <p>Note that when two editors are modifying preferences at the same
|
||||
* time, the last one to call apply wins.
|
||||
*
|
||||
* <p>Unlike {@link #commit}, which writes its preferences out
|
||||
* to persistent storage synchronously, {@link #apply}
|
||||
* commits its changes to the in-memory
|
||||
* {@link SharedPreferences} immediately but starts an
|
||||
* asynchronous commit to disk and you won't be notified of
|
||||
* any failures. If another editor on this
|
||||
* {@link SharedPreferences} does a regular {@link #commit}
|
||||
* while a {@link #apply} is still outstanding, the
|
||||
* {@link #commit} will block until all async commits are
|
||||
* completed as well as the commit itself.
|
||||
*
|
||||
* <p>As {@link SharedPreferences} instances are singletons within
|
||||
* a process, it's safe to replace any instance of {@link #commit} with
|
||||
* {@link #apply} if you were already ignoring the return value.
|
||||
*
|
||||
* <p>You don't need to worry about Android component
|
||||
* lifecycles and their interaction with <code>apply()</code>
|
||||
* writing to disk. The framework makes sure in-flight disk
|
||||
* writes from <code>apply()</code> complete before switching
|
||||
* states.
|
||||
*
|
||||
* <p class='note'>The SharedPreferences.Editor interface
|
||||
* isn't expected to be implemented directly. However, if you
|
||||
* previously did implement it and are now getting errors
|
||||
* about missing <code>apply()</code>, you can simply call
|
||||
* {@link #commit} from <code>apply()</code>.
|
||||
*/
|
||||
void apply();
|
||||
}
|
||||
/**
|
||||
* Retrieve all values from the preferences.
|
||||
*
|
||||
* <p>Note that you <em>must not</em> modify the collection returned
|
||||
* by this method, or alter any of its contents. The consistency of your
|
||||
* stored data is not guaranteed if you do.
|
||||
*
|
||||
* @return Returns a map containing a list of pairs key/value representing
|
||||
* the preferences.
|
||||
*
|
||||
* @throws NullPointerException
|
||||
*/
|
||||
Map<String, ?> getAll();
|
||||
/**
|
||||
* Retrieve a String value from the preferences.
|
||||
*
|
||||
* @param key The name of the preference to retrieve.
|
||||
* @param defValue Value to return if this preference does not exist.
|
||||
*
|
||||
* @return Returns the preference value if it exists, or defValue. Throws
|
||||
* ClassCastException if there is a preference with this name that is not
|
||||
* a String.
|
||||
*
|
||||
* @throws ClassCastException
|
||||
*/
|
||||
String getString(String key, String defValue);
|
||||
|
||||
/**
|
||||
* Retrieve a set of String values from the preferences.
|
||||
*
|
||||
* <p>Note that you <em>must not</em> modify the set instance returned
|
||||
* by this call. The consistency of the stored data is not guaranteed
|
||||
* if you do, nor is your ability to modify the instance at all.
|
||||
*
|
||||
* @param key The name of the preference to retrieve.
|
||||
* @param defValues Values to return if this preference does not exist.
|
||||
*
|
||||
* @return Returns the preference values if they exist, or defValues.
|
||||
* Throws ClassCastException if there is a preference with this name
|
||||
* that is not a Set.
|
||||
*
|
||||
* @throws ClassCastException
|
||||
*/
|
||||
Set<String> getStringSet(String key, Set<String> defValues);
|
||||
|
||||
/**
|
||||
* Retrieve an int value from the preferences.
|
||||
*
|
||||
* @param key The name of the preference to retrieve.
|
||||
* @param defValue Value to return if this preference does not exist.
|
||||
*
|
||||
* @return Returns the preference value if it exists, or defValue. Throws
|
||||
* ClassCastException if there is a preference with this name that is not
|
||||
* an int.
|
||||
*
|
||||
* @throws ClassCastException
|
||||
*/
|
||||
int getInt(String key, int defValue);
|
||||
|
||||
/**
|
||||
* Retrieve a long value from the preferences.
|
||||
*
|
||||
* @param key The name of the preference to retrieve.
|
||||
* @param defValue Value to return if this preference does not exist.
|
||||
*
|
||||
* @return Returns the preference value if it exists, or defValue. Throws
|
||||
* ClassCastException if there is a preference with this name that is not
|
||||
* a long.
|
||||
*
|
||||
* @throws ClassCastException
|
||||
*/
|
||||
long getLong(String key, long defValue);
|
||||
|
||||
/**
|
||||
* Retrieve a float value from the preferences.
|
||||
*
|
||||
* @param key The name of the preference to retrieve.
|
||||
* @param defValue Value to return if this preference does not exist.
|
||||
*
|
||||
* @return Returns the preference value if it exists, or defValue. Throws
|
||||
* ClassCastException if there is a preference with this name that is not
|
||||
* a float.
|
||||
*
|
||||
* @throws ClassCastException
|
||||
*/
|
||||
float getFloat(String key, float defValue);
|
||||
|
||||
/**
|
||||
* Retrieve a boolean value from the preferences.
|
||||
*
|
||||
* @param key The name of the preference to retrieve.
|
||||
* @param defValue Value to return if this preference does not exist.
|
||||
*
|
||||
* @return Returns the preference value if it exists, or defValue. Throws
|
||||
* ClassCastException if there is a preference with this name that is not
|
||||
* a boolean.
|
||||
*
|
||||
* @throws ClassCastException
|
||||
*/
|
||||
boolean getBoolean(String key, boolean defValue);
|
||||
/**
|
||||
* Checks whether the preferences contains a preference.
|
||||
*
|
||||
* @param key The name of the preference to check.
|
||||
* @return Returns true if the preference exists in the preferences,
|
||||
* otherwise false.
|
||||
*/
|
||||
boolean contains(String key);
|
||||
|
||||
/**
|
||||
* Create a new Editor for these preferences, through which you can make
|
||||
* modifications to the data in the preferences and atomically commit those
|
||||
* changes back to the SharedPreferences object.
|
||||
*
|
||||
* <p>Note that you <em>must</em> call {@link Editor#commit} to have any
|
||||
* changes you perform in the Editor actually show up in the
|
||||
* SharedPreferences.
|
||||
*
|
||||
* @return Returns a new instance of the {@link Editor} interface, allowing
|
||||
* you to modify the values in this SharedPreferences object.
|
||||
*/
|
||||
Editor edit();
|
||||
|
||||
/**
|
||||
* Registers a callback to be invoked when a change happens to a preference.
|
||||
*
|
||||
* @param listener The callback that will run.
|
||||
* @see #unregisterOnSharedPreferenceChangeListener
|
||||
*/
|
||||
void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener);
|
||||
|
||||
/**
|
||||
* Unregisters a previous callback.
|
||||
*
|
||||
* @param listener The callback that should be unregistered.
|
||||
* @see #registerOnSharedPreferenceChangeListener
|
||||
*/
|
||||
void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener);
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.security.crypto;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An implementation of {@link SharedPreferences} that encrypts keys and values.
|
||||
*
|
||||
* <pre>
|
||||
* String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
|
||||
*
|
||||
* SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
|
||||
* "secret_shared_prefs",
|
||||
* masterKeyAlias,
|
||||
* context,
|
||||
* EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||
* EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||
* );
|
||||
*
|
||||
* // use the shared preferences and editor as you normally would
|
||||
* SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
* </pre>
|
||||
*/
|
||||
public final class EncryptedSharedPreferences implements SharedPreferences {
|
||||
/**
|
||||
* Opens an instance of encrypted SharedPreferences
|
||||
*
|
||||
* @param fileName The name of the file to open; can not contain path
|
||||
* separators.
|
||||
* @param masterKey The master key to use.
|
||||
* @param prefKeyEncryptionScheme The scheme to use for encrypting keys.
|
||||
* @param prefValueEncryptionScheme The scheme to use for encrypting values.
|
||||
* @return The SharedPreferences instance that encrypts all data.
|
||||
* @throws GeneralSecurityException when a bad master key or keyset has been attempted
|
||||
* @throws IOException when fileName can not be used
|
||||
*/
|
||||
public static SharedPreferences create(Context context,
|
||||
String fileName,
|
||||
MasterKey masterKey,
|
||||
PrefKeyEncryptionScheme prefKeyEncryptionScheme,
|
||||
PrefValueEncryptionScheme prefValueEncryptionScheme)
|
||||
throws GeneralSecurityException, IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens an instance of encrypted SharedPreferences
|
||||
*
|
||||
* @param fileName The name of the file to open; can not contain path
|
||||
* separators.
|
||||
* @param masterKeyAlias The alias of the master key to use.
|
||||
* @param context The context to use to open the preferences file.
|
||||
* @param prefKeyEncryptionScheme The scheme to use for encrypting keys.
|
||||
* @param prefValueEncryptionScheme The scheme to use for encrypting values.
|
||||
* @return The SharedPreferences instance that encrypts all data.
|
||||
* @throws GeneralSecurityException when a bad master key or keyset has been attempted
|
||||
* @throws IOException when fileName can not be used
|
||||
* @deprecated Use {@link #create(Context, String, MasterKey,
|
||||
* PrefKeyEncryptionScheme, PrefValueEncryptionScheme)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static SharedPreferences create(String fileName,
|
||||
String masterKeyAlias,
|
||||
Context context,
|
||||
PrefKeyEncryptionScheme prefKeyEncryptionScheme,
|
||||
PrefValueEncryptionScheme prefValueEncryptionScheme)
|
||||
throws GeneralSecurityException, IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* The encryption scheme to encrypt keys.
|
||||
*/
|
||||
public enum PrefKeyEncryptionScheme {
|
||||
/**
|
||||
* Pref keys are encrypted deterministically with AES256-SIV-CMAC (RFC 5297).
|
||||
*
|
||||
* For more information please see the Tink documentation:
|
||||
*
|
||||
* <a href="https://google.github.io/tink/javadoc/tink/1.4.0/com/google/crypto/tink/daead/AesSivKeyManager.html">AesSivKeyManager</a>.aes256SivTemplate()
|
||||
*/
|
||||
AES256_SIV;
|
||||
}
|
||||
/**
|
||||
* The encryption scheme to encrypt values.
|
||||
*/
|
||||
public enum PrefValueEncryptionScheme {
|
||||
/**
|
||||
* Pref values are encrypted with AES256-GCM. The associated data is the encrypted pref key.
|
||||
*
|
||||
* For more information please see the Tink documentation:
|
||||
*
|
||||
* <a href="https://google.github.io/tink/javadoc/tink/1.4.0/com/google/crypto/tink/aead/AesGcmKeyManager.html">AesGcmKeyManager</a>.aes256GcmTemplate()
|
||||
*/
|
||||
AES256_GCM;
|
||||
}
|
||||
|
||||
// SharedPreferences methods
|
||||
@Override
|
||||
public Map<String, ?> getAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String key, String defValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getStringSet(String key, Set<String> defValues) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(String key, int defValue) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(String key, long defValue) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(String key, float defValue) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String key, boolean defValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedPreferences.Editor edit() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerOnSharedPreferenceChangeListener(
|
||||
OnSharedPreferenceChangeListener listener) {
|
||||
}
|
||||
@Override
|
||||
public void unregisterOnSharedPreferenceChangeListener(
|
||||
OnSharedPreferenceChangeListener listener) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.security.crypto;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* Wrapper for a master key used in the library.
|
||||
*
|
||||
* On Android M (API 23) and above, this is class references a key that's stored in the
|
||||
* Android Keystore. On Android L (API 21, 22), there isn't a master key.
|
||||
*/
|
||||
public final class MasterKey {
|
||||
static final String KEYSTORE_PATH_URI = "android-keystore://";
|
||||
/**
|
||||
* The default master key alias.
|
||||
*/
|
||||
public static final String DEFAULT_MASTER_KEY_ALIAS = "_androidx_security_master_key_";
|
||||
|
||||
/**
|
||||
* The default and recommended size for the master key.
|
||||
*/
|
||||
public static final int DEFAULT_AES_GCM_MASTER_KEY_SIZE = 256;
|
||||
|
||||
/**
|
||||
* Algorithm/Cipher choices used for the master key.
|
||||
*/
|
||||
public enum KeyScheme {
|
||||
AES256_GCM
|
||||
}
|
||||
|
||||
/**
|
||||
* The default validity period for authentication in seconds.
|
||||
*/
|
||||
public static int getDefaultAuthenticationValidityDurationSeconds() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* package */ MasterKey(String keyAlias, Object keyGenParameterSpec) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this key is backed by the Android Keystore.
|
||||
*
|
||||
* @return {@code true} if the key is in Android Keystore, {@code false} otherwise. This
|
||||
* method always returns false when called on Android Lollipop (API 21 and 22).
|
||||
*/
|
||||
public boolean isKeyStoreBacked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether user authentication is required to use this key.
|
||||
*
|
||||
* This method always returns {@code false} on Android L (API 21 + 22).
|
||||
*/
|
||||
public boolean isUserAuthenticationRequired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the duration in seconds that the key is unlocked for following user authentication.
|
||||
*
|
||||
* The value returned for this method is only meaningful on Android M+ (API 23) when
|
||||
* {@link #isUserAuthenticationRequired()} returns {@code true}.
|
||||
*
|
||||
* @return The duration the key is unlocked for in seconds.
|
||||
*/
|
||||
public int getUserAuthenticationValidityDurationSeconds() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the key is backed by strong box.
|
||||
*/
|
||||
public boolean isStrongBoxBacked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* package */ String getKeyAlias() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for generating a {@link MasterKey}.
|
||||
*/
|
||||
public static final class Builder {
|
||||
/**
|
||||
* Creates a builder for a {@link MasterKey} using the default alias of
|
||||
* {@link #DEFAULT_MASTER_KEY_ALIAS}.
|
||||
*
|
||||
* @param context The context to use with this master key.
|
||||
*/
|
||||
public Builder(Context context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a builder for a {@link MasterKey}.
|
||||
*
|
||||
* @param context The context to use with this master key.
|
||||
*/
|
||||
public Builder(Context context, String keyAlias) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@link KeyScheme} to be used for the master key.
|
||||
* This uses a default {@link KeyGenParameterSpec} associated with the provided
|
||||
* {@code KeyScheme}.
|
||||
* NOTE: Either this method OR {@link #setKeyGenParameterSpec} should be used to set
|
||||
* the parameters to use for building the master key. Calling either function after
|
||||
* the other will throw an {@link IllegalArgumentException}.
|
||||
*
|
||||
* @param keyScheme The KeyScheme to use.
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder setKeyScheme(KeyScheme keyScheme) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* When used with {@link #setKeyScheme(KeyScheme)}, sets that the built master key should
|
||||
* require the user to authenticate before it's unlocked, probably using the
|
||||
* androidx.biometric library.
|
||||
*
|
||||
* This method sets the validity duration of the key to
|
||||
* {@link #getDefaultAuthenticationValidityDurationSeconds()}.
|
||||
*
|
||||
* @param authenticationRequired Whether user authentication should be required to use
|
||||
* the key.
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder setUserAuthenticationRequired(boolean authenticationRequired) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* When used with {@link #setKeyScheme(KeyScheme)}, sets that the built master key should
|
||||
* require the user to authenticate before it's unlocked, probably using the
|
||||
* androidx.biometric library, and that the key should remain unlocked for the provided
|
||||
* duration.
|
||||
*
|
||||
* @param authenticationRequired Whether user authentication should be
|
||||
* required to use the key.
|
||||
* @param userAuthenticationValidityDurationSeconds Duration in seconds that the key
|
||||
* should remain unlocked following user
|
||||
* authentication.
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder setUserAuthenticationRequired(boolean authenticationRequired,
|
||||
int userAuthenticationValidityDurationSeconds) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not to request this key is strong box backed. This setting is only
|
||||
* applicable on {@link Build.VERSION_CODES#P} and above, and only on devices that
|
||||
* support Strongbox.
|
||||
*
|
||||
* @param requestStrongBoxBacked Whether to request to use strongbox
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder setRequestStrongBoxBacked(boolean requestStrongBoxBacked) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link MasterKey} from this builder.
|
||||
*
|
||||
* @return The master key.
|
||||
*/
|
||||
public MasterKey build() throws GeneralSecurityException, IOException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user