Persist KeyChainSnapshot to XML
Adds parser and serializer, and round trip test.
Bug: 73921897
Test: runtest frameworks-services -p \
com.android.server.locksettings.recoverablekeystore
Change-Id: I8259ec398ee076823ac8bbf847534738514de8dc
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
new file mode 100644
index 0000000..f817a8f
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
@@ -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() {}
+}