| /* |
| * 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_BACKEND_PUBLIC_KEY; |
| 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()); |
| if (keyChainSnapshot.getTrustedHardwarePublicKey() != null) { |
| writePropertyTag(xmlSerializer, TAG_BACKEND_PUBLIC_KEY, |
| keyChainSnapshot.getTrustedHardwarePublicKey()); |
| } |
| } |
| |
| 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() {} |
| } |