Merge "Persist KeyChainSnapshot to XML" into pi-dev
This commit is contained in:
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 com.android.server.locksettings.recoverablekeystore.serialization;
|
||||
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.CERTIFICATE_FACTORY_TYPE;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.NAMESPACE;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.OUTPUT_ENCODING;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALGORITHM;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_SNAPSHOT;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_DERIVATION_PARAMS;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_MATERIAL;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_LOCK_SCREEN_UI_TYPE;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MAX_ATTEMPTS;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MEMORY_DIFFICULTY;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SALT;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SERVER_PARAMS;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SNAPSHOT_VERSION;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_TRUSTED_HARDWARE_CERT_PATH;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_USER_SECRET_TYPE;
|
||||
|
||||
import android.security.keystore.recovery.KeyChainProtectionParams;
|
||||
import android.security.keystore.recovery.KeyChainSnapshot;
|
||||
import android.security.keystore.recovery.KeyDerivationParams;
|
||||
import android.security.keystore.recovery.WrappedApplicationKey;
|
||||
import android.util.Base64;
|
||||
import android.util.Xml;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.cert.CertPath;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
/**
|
||||
* Deserializes a {@link android.security.keystore.recovery.KeyChainSnapshot} instance from XML.
|
||||
*/
|
||||
public class KeyChainSnapshotDeserializer {
|
||||
|
||||
/**
|
||||
* Deserializes a {@link KeyChainSnapshot} instance from the XML in the {@code inputStream}.
|
||||
*
|
||||
* @throws IOException if there is an IO error reading from the stream.
|
||||
* @throws KeyChainSnapshotParserException if the XML does not conform to the expected XML for
|
||||
* a snapshot.
|
||||
*/
|
||||
public static KeyChainSnapshot deserialize(InputStream inputStream)
|
||||
throws KeyChainSnapshotParserException, IOException {
|
||||
try {
|
||||
return deserializeInternal(inputStream);
|
||||
} catch (XmlPullParserException e) {
|
||||
throw new KeyChainSnapshotParserException("Malformed KeyChainSnapshot XML", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static KeyChainSnapshot deserializeInternal(InputStream inputStream) throws IOException,
|
||||
XmlPullParserException, KeyChainSnapshotParserException {
|
||||
XmlPullParser parser = Xml.newPullParser();
|
||||
parser.setInput(inputStream, OUTPUT_ENCODING);
|
||||
|
||||
parser.nextTag();
|
||||
parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_SNAPSHOT);
|
||||
|
||||
KeyChainSnapshot.Builder builder = new KeyChainSnapshot.Builder();
|
||||
while (parser.next() != XmlPullParser.END_TAG) {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = parser.getName();
|
||||
|
||||
switch (name) {
|
||||
case TAG_SNAPSHOT_VERSION:
|
||||
builder.setSnapshotVersion(readIntTag(parser, TAG_SNAPSHOT_VERSION));
|
||||
break;
|
||||
|
||||
case TAG_RECOVERY_KEY_MATERIAL:
|
||||
builder.setEncryptedRecoveryKeyBlob(
|
||||
readBlobTag(parser, TAG_RECOVERY_KEY_MATERIAL));
|
||||
break;
|
||||
|
||||
case TAG_COUNTER_ID:
|
||||
builder.setCounterId(readLongTag(parser, TAG_COUNTER_ID));
|
||||
break;
|
||||
|
||||
case TAG_SERVER_PARAMS:
|
||||
builder.setServerParams(readBlobTag(parser, TAG_SERVER_PARAMS));
|
||||
break;
|
||||
|
||||
case TAG_MAX_ATTEMPTS:
|
||||
builder.setMaxAttempts(readIntTag(parser, TAG_MAX_ATTEMPTS));
|
||||
break;
|
||||
|
||||
case TAG_TRUSTED_HARDWARE_CERT_PATH:
|
||||
try {
|
||||
builder.setTrustedHardwareCertPath(
|
||||
readCertPathTag(parser, TAG_TRUSTED_HARDWARE_CERT_PATH));
|
||||
} catch (CertificateException e) {
|
||||
throw new KeyChainSnapshotParserException(
|
||||
"Could not set trustedHardwareCertPath", e);
|
||||
}
|
||||
break;
|
||||
|
||||
case TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST:
|
||||
builder.setKeyChainProtectionParams(readKeyChainProtectionParamsList(parser));
|
||||
break;
|
||||
|
||||
case TAG_APPLICATION_KEYS:
|
||||
builder.setWrappedApplicationKeys(readWrappedApplicationKeys(parser));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new KeyChainSnapshotParserException(String.format(
|
||||
Locale.US, "Unexpected tag %s in keyChainSnapshot", name));
|
||||
}
|
||||
}
|
||||
|
||||
parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_KEY_CHAIN_SNAPSHOT);
|
||||
try {
|
||||
return builder.build();
|
||||
} catch (NullPointerException e) {
|
||||
throw new KeyChainSnapshotParserException("Failed to build KeyChainSnapshot", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<WrappedApplicationKey> readWrappedApplicationKeys(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
|
||||
parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_APPLICATION_KEYS);
|
||||
ArrayList<WrappedApplicationKey> keys = new ArrayList<>();
|
||||
while (parser.next() != XmlPullParser.END_TAG) {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
keys.add(readWrappedApplicationKey(parser));
|
||||
}
|
||||
parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_APPLICATION_KEYS);
|
||||
return keys;
|
||||
}
|
||||
|
||||
private static WrappedApplicationKey readWrappedApplicationKey(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
|
||||
parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_APPLICATION_KEY);
|
||||
WrappedApplicationKey.Builder builder = new WrappedApplicationKey.Builder();
|
||||
while (parser.next() != XmlPullParser.END_TAG) {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = parser.getName();
|
||||
|
||||
switch (name) {
|
||||
case TAG_ALIAS:
|
||||
builder.setAlias(readStringTag(parser, TAG_ALIAS));
|
||||
break;
|
||||
|
||||
case TAG_KEY_MATERIAL:
|
||||
builder.setEncryptedKeyMaterial(readBlobTag(parser, TAG_KEY_MATERIAL));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new KeyChainSnapshotParserException(String.format(
|
||||
Locale.US, "Unexpected tag %s in wrappedApplicationKey", name));
|
||||
}
|
||||
}
|
||||
parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_APPLICATION_KEY);
|
||||
|
||||
try {
|
||||
return builder.build();
|
||||
} catch (NullPointerException e) {
|
||||
throw new KeyChainSnapshotParserException("Failed to build WrappedApplicationKey", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<KeyChainProtectionParams> readKeyChainProtectionParamsList(
|
||||
XmlPullParser parser) throws IOException, XmlPullParserException,
|
||||
KeyChainSnapshotParserException {
|
||||
parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST);
|
||||
|
||||
ArrayList<KeyChainProtectionParams> keyChainProtectionParamsList = new ArrayList<>();
|
||||
while (parser.next() != XmlPullParser.END_TAG) {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
keyChainProtectionParamsList.add(readKeyChainProtectionParams(parser));
|
||||
}
|
||||
|
||||
parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST);
|
||||
return keyChainProtectionParamsList;
|
||||
}
|
||||
|
||||
private static KeyChainProtectionParams readKeyChainProtectionParams(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
|
||||
parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS);
|
||||
|
||||
KeyChainProtectionParams.Builder builder = new KeyChainProtectionParams.Builder();
|
||||
while (parser.next() != XmlPullParser.END_TAG) {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = parser.getName();
|
||||
|
||||
switch (name) {
|
||||
case TAG_LOCK_SCREEN_UI_TYPE:
|
||||
builder.setLockScreenUiFormat(readIntTag(parser, TAG_LOCK_SCREEN_UI_TYPE));
|
||||
break;
|
||||
|
||||
case TAG_USER_SECRET_TYPE:
|
||||
builder.setUserSecretType(readIntTag(parser, TAG_USER_SECRET_TYPE));
|
||||
break;
|
||||
|
||||
case TAG_KEY_DERIVATION_PARAMS:
|
||||
builder.setKeyDerivationParams(readKeyDerivationParams(parser));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new KeyChainSnapshotParserException(String.format(
|
||||
Locale.US,
|
||||
"Unexpected tag %s in keyChainProtectionParams",
|
||||
name));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS);
|
||||
|
||||
try {
|
||||
return builder.build();
|
||||
} catch (NullPointerException e) {
|
||||
throw new KeyChainSnapshotParserException(
|
||||
"Failed to build KeyChainProtectionParams", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static KeyDerivationParams readKeyDerivationParams(XmlPullParser parser)
|
||||
throws XmlPullParserException, IOException, KeyChainSnapshotParserException {
|
||||
parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_DERIVATION_PARAMS);
|
||||
|
||||
int memoryDifficulty = -1;
|
||||
int algorithm = -1;
|
||||
byte[] salt = null;
|
||||
|
||||
while (parser.next() != XmlPullParser.END_TAG) {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = parser.getName();
|
||||
|
||||
switch (name) {
|
||||
case TAG_MEMORY_DIFFICULTY:
|
||||
memoryDifficulty = readIntTag(parser, TAG_MEMORY_DIFFICULTY);
|
||||
break;
|
||||
|
||||
case TAG_ALGORITHM:
|
||||
algorithm = readIntTag(parser, TAG_ALGORITHM);
|
||||
break;
|
||||
|
||||
case TAG_SALT:
|
||||
salt = readBlobTag(parser, TAG_SALT);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new KeyChainSnapshotParserException(
|
||||
String.format(
|
||||
Locale.US,
|
||||
"Unexpected tag %s in keyDerivationParams",
|
||||
name));
|
||||
}
|
||||
}
|
||||
|
||||
if (salt == null) {
|
||||
throw new KeyChainSnapshotParserException("salt was not set in keyDerivationParams");
|
||||
}
|
||||
|
||||
KeyDerivationParams keyDerivationParams = null;
|
||||
|
||||
switch (algorithm) {
|
||||
case KeyDerivationParams.ALGORITHM_SHA256:
|
||||
keyDerivationParams = KeyDerivationParams.createSha256Params(salt);
|
||||
break;
|
||||
|
||||
case KeyDerivationParams.ALGORITHM_SCRYPT:
|
||||
keyDerivationParams = KeyDerivationParams.createScryptParams(
|
||||
salt, memoryDifficulty);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new KeyChainSnapshotParserException(
|
||||
"Unknown algorithm in keyDerivationParams");
|
||||
}
|
||||
|
||||
parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_KEY_DERIVATION_PARAMS);
|
||||
return keyDerivationParams;
|
||||
}
|
||||
|
||||
private static int readIntTag(XmlPullParser parser, String tagName)
|
||||
throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
|
||||
parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
|
||||
String text = readText(parser);
|
||||
parser.require(XmlPullParser.END_TAG, NAMESPACE, tagName);
|
||||
try {
|
||||
return Integer.valueOf(text);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new KeyChainSnapshotParserException(
|
||||
String.format(
|
||||
Locale.US, "%s expected int but got '%s'", tagName, text), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static long readLongTag(XmlPullParser parser, String tagName)
|
||||
throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
|
||||
parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
|
||||
String text = readText(parser);
|
||||
parser.require(XmlPullParser.END_TAG, NAMESPACE, tagName);
|
||||
try {
|
||||
return Long.valueOf(text);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new KeyChainSnapshotParserException(
|
||||
String.format(
|
||||
Locale.US, "%s expected long but got '%s'", tagName, text), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String readStringTag(XmlPullParser parser, String tagName)
|
||||
throws IOException, XmlPullParserException {
|
||||
parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
|
||||
String text = readText(parser);
|
||||
parser.require(XmlPullParser.END_TAG, NAMESPACE, tagName);
|
||||
return text;
|
||||
}
|
||||
|
||||
private static byte[] readBlobTag(XmlPullParser parser, String tagName)
|
||||
throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
|
||||
parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
|
||||
String text = readText(parser);
|
||||
parser.require(XmlPullParser.END_TAG, NAMESPACE, tagName);
|
||||
|
||||
try {
|
||||
return Base64.decode(text, /*flags=*/ Base64.DEFAULT);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new KeyChainSnapshotParserException(
|
||||
String.format(
|
||||
Locale.US,
|
||||
"%s expected base64 encoded bytes but got '%s'",
|
||||
tagName, text), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static CertPath readCertPathTag(XmlPullParser parser, String tagName)
|
||||
throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
|
||||
byte[] bytes = readBlobTag(parser, tagName);
|
||||
try {
|
||||
return CertificateFactory.getInstance(CERTIFICATE_FACTORY_TYPE)
|
||||
.generateCertPath(new ByteArrayInputStream(bytes));
|
||||
} catch (CertificateException e) {
|
||||
throw new KeyChainSnapshotParserException("Could not parse CertPath in tag " + tagName,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String readText(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
String result = "";
|
||||
if (parser.next() == XmlPullParser.TEXT) {
|
||||
result = parser.getText();
|
||||
parser.nextTag();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Statics only
|
||||
private KeyChainSnapshotDeserializer() {}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 com.android.server.locksettings.recoverablekeystore.serialization;
|
||||
|
||||
/**
|
||||
* Error thrown when parsing invalid XML, while trying to read a
|
||||
* {@link android.security.keystore.recovery.KeyChainSnapshot}.
|
||||
*/
|
||||
public class KeyChainSnapshotParserException extends Exception {
|
||||
|
||||
public KeyChainSnapshotParserException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public KeyChainSnapshotParserException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 com.android.server.locksettings.recoverablekeystore.serialization;
|
||||
|
||||
/**
|
||||
* Describes the XML schema of the {@link android.security.keystore.recovery.KeyChainSnapshot} file.
|
||||
*/
|
||||
class KeyChainSnapshotSchema {
|
||||
static final String NAMESPACE = null;
|
||||
|
||||
static final String OUTPUT_ENCODING = "UTF-8";
|
||||
|
||||
static final String CERTIFICATE_FACTORY_TYPE = "X.509";
|
||||
static final String CERT_PATH_ENCODING = "PkiPath";
|
||||
|
||||
static final String TAG_KEY_CHAIN_SNAPSHOT = "keyChainSnapshot";
|
||||
|
||||
static final String TAG_SNAPSHOT_VERSION = "snapshotVersion";
|
||||
static final String TAG_COUNTER_ID = "counterId";
|
||||
static final String TAG_MAX_ATTEMPTS = "maxAttempts";
|
||||
static final String TAG_RECOVERY_KEY_MATERIAL = "recoveryKeyMaterial";
|
||||
static final String TAG_SERVER_PARAMS = "serverParams";
|
||||
static final String TAG_TRUSTED_HARDWARE_CERT_PATH = "thmCertPath";
|
||||
|
||||
static final String TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST =
|
||||
"keyChainProtectionParamsList";
|
||||
static final String TAG_KEY_CHAIN_PROTECTION_PARAMS = "keyChainProtectionParams";
|
||||
static final String TAG_USER_SECRET_TYPE = "userSecretType";
|
||||
static final String TAG_LOCK_SCREEN_UI_TYPE = "lockScreenUiType";
|
||||
|
||||
static final String TAG_KEY_DERIVATION_PARAMS = "keyDerivationParams";
|
||||
static final String TAG_ALGORITHM = "algorithm";
|
||||
static final String TAG_MEMORY_DIFFICULTY = "memoryDifficulty";
|
||||
static final String TAG_SALT = "salt";
|
||||
|
||||
static final String TAG_APPLICATION_KEYS = "applicationKeysList";
|
||||
static final String TAG_APPLICATION_KEY = "applicationKey";
|
||||
static final String TAG_ALIAS = "alias";
|
||||
static final String TAG_KEY_MATERIAL = "keyMaterial";
|
||||
|
||||
// Statics only
|
||||
private KeyChainSnapshotSchema() {}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 com.android.server.locksettings.recoverablekeystore.serialization;
|
||||
|
||||
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.CERT_PATH_ENCODING;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.NAMESPACE;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.OUTPUT_ENCODING;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALGORITHM;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_SNAPSHOT;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_DERIVATION_PARAMS;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_MATERIAL;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_LOCK_SCREEN_UI_TYPE;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MAX_ATTEMPTS;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MEMORY_DIFFICULTY;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SALT;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SERVER_PARAMS;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SNAPSHOT_VERSION;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_TRUSTED_HARDWARE_CERT_PATH;
|
||||
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_USER_SECRET_TYPE;
|
||||
|
||||
import android.security.keystore.recovery.KeyChainProtectionParams;
|
||||
import android.security.keystore.recovery.KeyChainSnapshot;
|
||||
import android.security.keystore.recovery.KeyDerivationParams;
|
||||
import android.security.keystore.recovery.WrappedApplicationKey;
|
||||
import android.util.Base64;
|
||||
import android.util.Xml;
|
||||
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.security.cert.CertPath;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Serializes a {@link KeyChainSnapshot} instance to XML.
|
||||
*/
|
||||
public class KeyChainSnapshotSerializer {
|
||||
|
||||
/**
|
||||
* Serializes {@code keyChainSnapshot} to XML, writing to {@code outputStream}.
|
||||
*
|
||||
* @throws IOException if there was an IO error writing to the stream.
|
||||
* @throws CertificateEncodingException if the {@link CertPath} from
|
||||
* {@link KeyChainSnapshot#getTrustedHardwareCertPath()} is not encoded correctly.
|
||||
*/
|
||||
public static void serialize(KeyChainSnapshot keyChainSnapshot, OutputStream outputStream)
|
||||
throws IOException, CertificateEncodingException {
|
||||
XmlSerializer xmlSerializer = Xml.newSerializer();
|
||||
xmlSerializer.setOutput(outputStream, OUTPUT_ENCODING);
|
||||
xmlSerializer.startDocument(
|
||||
/*encoding=*/ null,
|
||||
/*standalone=*/ null);
|
||||
xmlSerializer.startTag(NAMESPACE, TAG_KEY_CHAIN_SNAPSHOT);
|
||||
writeKeyChainSnapshotProperties(xmlSerializer, keyChainSnapshot);
|
||||
writeKeyChainProtectionParams(xmlSerializer,
|
||||
keyChainSnapshot.getKeyChainProtectionParams());
|
||||
writeApplicationKeys(xmlSerializer,
|
||||
keyChainSnapshot.getWrappedApplicationKeys());
|
||||
xmlSerializer.endTag(NAMESPACE, TAG_KEY_CHAIN_SNAPSHOT);
|
||||
xmlSerializer.endDocument();
|
||||
}
|
||||
|
||||
private static void writeApplicationKeys(
|
||||
XmlSerializer xmlSerializer, List<WrappedApplicationKey> wrappedApplicationKeys)
|
||||
throws IOException {
|
||||
xmlSerializer.startTag(NAMESPACE, TAG_APPLICATION_KEYS);
|
||||
for (WrappedApplicationKey key : wrappedApplicationKeys) {
|
||||
xmlSerializer.startTag(NAMESPACE, TAG_APPLICATION_KEY);
|
||||
writeApplicationKeyProperties(xmlSerializer, key);
|
||||
xmlSerializer.endTag(NAMESPACE, TAG_APPLICATION_KEY);
|
||||
}
|
||||
xmlSerializer.endTag(NAMESPACE, TAG_APPLICATION_KEYS);
|
||||
}
|
||||
|
||||
private static void writeApplicationKeyProperties(
|
||||
XmlSerializer xmlSerializer, WrappedApplicationKey applicationKey) throws IOException {
|
||||
writePropertyTag(xmlSerializer, TAG_ALIAS, applicationKey.getAlias());
|
||||
writePropertyTag(xmlSerializer, TAG_KEY_MATERIAL, applicationKey.getEncryptedKeyMaterial());
|
||||
}
|
||||
|
||||
private static void writeKeyChainProtectionParams(
|
||||
XmlSerializer xmlSerializer,
|
||||
List<KeyChainProtectionParams> keyChainProtectionParamsList) throws IOException {
|
||||
xmlSerializer.startTag(NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST);
|
||||
for (KeyChainProtectionParams keyChainProtectionParams : keyChainProtectionParamsList) {
|
||||
xmlSerializer.startTag(NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS);
|
||||
writeKeyChainProtectionParamsProperties(xmlSerializer, keyChainProtectionParams);
|
||||
xmlSerializer.endTag(NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS);
|
||||
}
|
||||
xmlSerializer.endTag(NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST);
|
||||
}
|
||||
|
||||
private static void writeKeyChainProtectionParamsProperties(
|
||||
XmlSerializer xmlSerializer, KeyChainProtectionParams keyChainProtectionParams)
|
||||
throws IOException {
|
||||
writePropertyTag(xmlSerializer, TAG_USER_SECRET_TYPE,
|
||||
keyChainProtectionParams.getUserSecretType());
|
||||
writePropertyTag(xmlSerializer, TAG_LOCK_SCREEN_UI_TYPE,
|
||||
keyChainProtectionParams.getLockScreenUiFormat());
|
||||
|
||||
// NOTE: Do not serialize the 'secret' field. It should never be set anyway for snapshots
|
||||
// we generate.
|
||||
|
||||
writeKeyDerivationParams(xmlSerializer, keyChainProtectionParams.getKeyDerivationParams());
|
||||
}
|
||||
|
||||
private static void writeKeyDerivationParams(
|
||||
XmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams)
|
||||
throws IOException {
|
||||
xmlSerializer.startTag(NAMESPACE, TAG_KEY_DERIVATION_PARAMS);
|
||||
writeKeyDerivationParamsProperties(
|
||||
xmlSerializer, keyDerivationParams);
|
||||
xmlSerializer.endTag(NAMESPACE, TAG_KEY_DERIVATION_PARAMS);
|
||||
}
|
||||
|
||||
private static void writeKeyDerivationParamsProperties(
|
||||
XmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams)
|
||||
throws IOException {
|
||||
writePropertyTag(xmlSerializer, TAG_ALGORITHM, keyDerivationParams.getAlgorithm());
|
||||
writePropertyTag(xmlSerializer, TAG_SALT, keyDerivationParams.getSalt());
|
||||
writePropertyTag(xmlSerializer, TAG_MEMORY_DIFFICULTY,
|
||||
keyDerivationParams.getMemoryDifficulty());
|
||||
}
|
||||
|
||||
private static void writeKeyChainSnapshotProperties(
|
||||
XmlSerializer xmlSerializer, KeyChainSnapshot keyChainSnapshot)
|
||||
throws IOException, CertificateEncodingException {
|
||||
|
||||
writePropertyTag(xmlSerializer, TAG_SNAPSHOT_VERSION,
|
||||
keyChainSnapshot.getSnapshotVersion());
|
||||
writePropertyTag(xmlSerializer, TAG_MAX_ATTEMPTS, keyChainSnapshot.getMaxAttempts());
|
||||
writePropertyTag(xmlSerializer, TAG_COUNTER_ID, keyChainSnapshot.getCounterId());
|
||||
writePropertyTag(xmlSerializer, TAG_RECOVERY_KEY_MATERIAL,
|
||||
keyChainSnapshot.getEncryptedRecoveryKeyBlob());
|
||||
writePropertyTag(xmlSerializer, TAG_SERVER_PARAMS, keyChainSnapshot.getServerParams());
|
||||
writePropertyTag(xmlSerializer, TAG_TRUSTED_HARDWARE_CERT_PATH,
|
||||
keyChainSnapshot.getTrustedHardwareCertPath());
|
||||
}
|
||||
|
||||
private static void writePropertyTag(
|
||||
XmlSerializer xmlSerializer, String propertyName, long propertyValue)
|
||||
throws IOException {
|
||||
xmlSerializer.startTag(NAMESPACE, propertyName);
|
||||
xmlSerializer.text(Long.toString(propertyValue));
|
||||
xmlSerializer.endTag(NAMESPACE, propertyName);
|
||||
}
|
||||
|
||||
private static void writePropertyTag(
|
||||
XmlSerializer xmlSerializer, String propertyName, String propertyValue)
|
||||
throws IOException {
|
||||
xmlSerializer.startTag(NAMESPACE, propertyName);
|
||||
xmlSerializer.text(propertyValue);
|
||||
xmlSerializer.endTag(NAMESPACE, propertyName);
|
||||
}
|
||||
|
||||
private static void writePropertyTag(
|
||||
XmlSerializer xmlSerializer, String propertyName, byte[] propertyValue)
|
||||
throws IOException {
|
||||
xmlSerializer.startTag(NAMESPACE, propertyName);
|
||||
xmlSerializer.text(Base64.encodeToString(propertyValue, /*flags=*/ Base64.DEFAULT));
|
||||
xmlSerializer.endTag(NAMESPACE, propertyName);
|
||||
}
|
||||
|
||||
private static void writePropertyTag(
|
||||
XmlSerializer xmlSerializer, String propertyName, CertPath certPath)
|
||||
throws IOException, CertificateEncodingException {
|
||||
writePropertyTag(xmlSerializer, propertyName, certPath.getEncoded(CERT_PATH_ENCODING));
|
||||
}
|
||||
|
||||
// Statics only
|
||||
private KeyChainSnapshotSerializer() {}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 com.android.server.locksettings.recoverablekeystore.serialization;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.security.keystore.recovery.KeyChainProtectionParams;
|
||||
import android.security.keystore.recovery.KeyChainSnapshot;
|
||||
import android.security.keystore.recovery.KeyDerivationParams;
|
||||
import android.security.keystore.recovery.WrappedApplicationKey;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.server.locksettings.recoverablekeystore.TestData;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.security.cert.CertPath;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class KeyChainSnapshotSerializerTest {
|
||||
private static final int COUNTER_ID = 2134;
|
||||
private static final int SNAPSHOT_VERSION = 125;
|
||||
private static final int MAX_ATTEMPTS = 21;
|
||||
private static final byte[] SERVER_PARAMS = new byte[] { 8, 2, 4 };
|
||||
private static final byte[] KEY_BLOB = new byte[] { 124, 53, 53, 53 };
|
||||
private static final CertPath CERT_PATH = TestData.CERT_PATH_1;
|
||||
private static final int SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN;
|
||||
private static final int LOCK_SCREEN_UI = KeyChainProtectionParams.UI_FORMAT_PASSWORD;
|
||||
private static final byte[] SALT = new byte[] { 5, 4, 3, 2, 1 };
|
||||
private static final int MEMORY_DIFFICULTY = 45;
|
||||
private static final int ALGORITHM = KeyDerivationParams.ALGORITHM_SCRYPT;
|
||||
private static final byte[] SECRET = new byte[] { 1, 2, 3, 4 };
|
||||
|
||||
private static final String TEST_KEY_1_ALIAS = "key1";
|
||||
private static final byte[] TEST_KEY_1_BYTES = new byte[] { 66, 77, 88 };
|
||||
|
||||
private static final String TEST_KEY_2_ALIAS = "key2";
|
||||
private static final byte[] TEST_KEY_2_BYTES = new byte[] { 99, 33, 11 };
|
||||
|
||||
private static final String TEST_KEY_3_ALIAS = "key3";
|
||||
private static final byte[] TEST_KEY_3_BYTES = new byte[] { 2, 8, 100 };
|
||||
|
||||
@Test
|
||||
public void roundTrip_persistsCounterId() throws Exception {
|
||||
assertThat(roundTrip().getCounterId()).isEqualTo(COUNTER_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTrip_persistsSnapshotVersion() throws Exception {
|
||||
assertThat(roundTrip().getSnapshotVersion()).isEqualTo(SNAPSHOT_VERSION);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTrip_persistsMaxAttempts() throws Exception {
|
||||
assertThat(roundTrip().getMaxAttempts()).isEqualTo(MAX_ATTEMPTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTrip_persistsRecoveryKey() throws Exception {
|
||||
assertThat(roundTrip().getEncryptedRecoveryKeyBlob()).isEqualTo(KEY_BLOB);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTrip_persistsServerParams() throws Exception {
|
||||
assertThat(roundTrip().getServerParams()).isEqualTo(SERVER_PARAMS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTrip_persistsCertPath() throws Exception {
|
||||
assertThat(roundTrip().getTrustedHardwareCertPath()).isEqualTo(CERT_PATH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTrip_persistsParamsList() throws Exception {
|
||||
assertThat(roundTrip().getKeyChainProtectionParams()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripParams_persistsUserSecretType() throws Exception {
|
||||
assertThat(roundTripParams().getUserSecretType()).isEqualTo(SECRET_TYPE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripParams_persistsLockScreenUi() throws Exception {
|
||||
assertThat(roundTripParams().getLockScreenUiFormat()).isEqualTo(LOCK_SCREEN_UI);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripParams_persistsSalt() throws Exception {
|
||||
assertThat(roundTripParams().getKeyDerivationParams().getSalt()).isEqualTo(SALT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripParams_persistsAlgorithm() throws Exception {
|
||||
assertThat(roundTripParams().getKeyDerivationParams().getAlgorithm()).isEqualTo(ALGORITHM);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripParams_persistsMemoryDifficulty() throws Exception {
|
||||
assertThat(roundTripParams().getKeyDerivationParams().getMemoryDifficulty())
|
||||
.isEqualTo(MEMORY_DIFFICULTY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripParams_doesNotPersistSecret() throws Exception {
|
||||
assertThat(roundTripParams().getSecret()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripKeys_hasCorrectLength() throws Exception {
|
||||
assertThat(roundTripKeys()).hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripKeys_0_persistsAlias() throws Exception {
|
||||
assertThat(roundTripKeys().get(0).getAlias()).isEqualTo(TEST_KEY_1_ALIAS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripKeys_0_persistsKeyBytes() throws Exception {
|
||||
assertThat(roundTripKeys().get(0).getEncryptedKeyMaterial()).isEqualTo(TEST_KEY_1_BYTES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripKeys_1_persistsAlias() throws Exception {
|
||||
assertThat(roundTripKeys().get(1).getAlias()).isEqualTo(TEST_KEY_2_ALIAS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripKeys_1_persistsKeyBytes() throws Exception {
|
||||
assertThat(roundTripKeys().get(1).getEncryptedKeyMaterial()).isEqualTo(TEST_KEY_2_BYTES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripKeys_2_persistsAlias() throws Exception {
|
||||
assertThat(roundTripKeys().get(2).getAlias()).isEqualTo(TEST_KEY_3_ALIAS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripKeys_2_persistsKeyBytes() throws Exception {
|
||||
assertThat(roundTripKeys().get(2).getEncryptedKeyMaterial()).isEqualTo(TEST_KEY_3_BYTES);
|
||||
}
|
||||
|
||||
private static List<WrappedApplicationKey> roundTripKeys() throws Exception {
|
||||
return roundTrip().getWrappedApplicationKeys();
|
||||
}
|
||||
|
||||
private static KeyChainProtectionParams roundTripParams() throws Exception {
|
||||
return roundTrip().getKeyChainProtectionParams().get(0);
|
||||
}
|
||||
|
||||
public static KeyChainSnapshot roundTrip() throws Exception {
|
||||
KeyChainSnapshot snapshot = createTestKeyChainSnapshot();
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
KeyChainSnapshotSerializer.serialize(snapshot, byteArrayOutputStream);
|
||||
return KeyChainSnapshotDeserializer.deserialize(
|
||||
new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
|
||||
}
|
||||
|
||||
private static KeyChainSnapshot createTestKeyChainSnapshot() throws Exception {
|
||||
KeyDerivationParams keyDerivationParams =
|
||||
KeyDerivationParams.createScryptParams(SALT, MEMORY_DIFFICULTY);
|
||||
KeyChainProtectionParams keyChainProtectionParams = new KeyChainProtectionParams.Builder()
|
||||
.setKeyDerivationParams(keyDerivationParams)
|
||||
.setUserSecretType(SECRET_TYPE)
|
||||
.setLockScreenUiFormat(LOCK_SCREEN_UI)
|
||||
.setSecret(SECRET)
|
||||
.build();
|
||||
ArrayList<KeyChainProtectionParams> keyChainProtectionParamsList =
|
||||
new ArrayList<>(1);
|
||||
keyChainProtectionParamsList.add(keyChainProtectionParams);
|
||||
|
||||
ArrayList<WrappedApplicationKey> keyList = new ArrayList<>();
|
||||
keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES));
|
||||
keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES));
|
||||
keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES));
|
||||
|
||||
return new KeyChainSnapshot.Builder()
|
||||
.setCounterId(COUNTER_ID)
|
||||
.setSnapshotVersion(SNAPSHOT_VERSION)
|
||||
.setServerParams(SERVER_PARAMS)
|
||||
.setMaxAttempts(MAX_ATTEMPTS)
|
||||
.setEncryptedRecoveryKeyBlob(KEY_BLOB)
|
||||
.setKeyChainProtectionParams(keyChainProtectionParamsList)
|
||||
.setWrappedApplicationKeys(keyList)
|
||||
.setTrustedHardwareCertPath(CERT_PATH)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static WrappedApplicationKey createKey(String alias, byte[] bytes) {
|
||||
return new WrappedApplicationKey.Builder()
|
||||
.setAlias(alias)
|
||||
.setEncryptedKeyMaterial(bytes)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user