/*
 * Copyright (C) 2017 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.security.keystore;

import android.os.Parcelable;
import android.os.Parcel;

import java.math.BigInteger;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
import java.util.Date;

import javax.security.auth.x500.X500Principal;

A parcelable version of KeyGenParameterSpec
@hideonly used for communicating with the DPMS.
/** * A parcelable version of KeyGenParameterSpec * @hide only used for communicating with the DPMS. */
public final class ParcelableKeyGenParameterSpec implements Parcelable { private static final int ALGORITHM_PARAMETER_SPEC_NONE = 1; private static final int ALGORITHM_PARAMETER_SPEC_RSA = 2; private static final int ALGORITHM_PARAMETER_SPEC_EC = 3; private final KeyGenParameterSpec mSpec; public ParcelableKeyGenParameterSpec( KeyGenParameterSpec spec) { mSpec = spec; } public int describeContents() { return 0; } private static void writeOptionalDate(Parcel out, Date date) { if (date != null) { out.writeBoolean(true); out.writeLong(date.getTime()); } else { out.writeBoolean(false); } } public void writeToParcel(Parcel out, int flags) { out.writeString(mSpec.getKeystoreAlias()); out.writeInt(mSpec.getPurposes()); out.writeInt(mSpec.getUid()); out.writeInt(mSpec.getKeySize()); // Only needs to support RSAKeyGenParameterSpec and ECGenParameterSpec. AlgorithmParameterSpec algoSpec = mSpec.getAlgorithmParameterSpec(); if (algoSpec == null) { out.writeInt(ALGORITHM_PARAMETER_SPEC_NONE); } else if (algoSpec instanceof RSAKeyGenParameterSpec) { RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) algoSpec; out.writeInt(ALGORITHM_PARAMETER_SPEC_RSA); out.writeInt(rsaSpec.getKeysize()); out.writeByteArray(rsaSpec.getPublicExponent().toByteArray()); } else if (algoSpec instanceof ECGenParameterSpec) { ECGenParameterSpec ecSpec = (ECGenParameterSpec) algoSpec; out.writeInt(ALGORITHM_PARAMETER_SPEC_EC); out.writeString(ecSpec.getName()); } else { throw new IllegalArgumentException( String.format("Unknown algorithm parameter spec: %s", algoSpec.getClass())); } out.writeByteArray(mSpec.getCertificateSubject().getEncoded()); out.writeByteArray(mSpec.getCertificateSerialNumber().toByteArray()); out.writeLong(mSpec.getCertificateNotBefore().getTime()); out.writeLong(mSpec.getCertificateNotAfter().getTime()); writeOptionalDate(out, mSpec.getKeyValidityStart()); writeOptionalDate(out, mSpec.getKeyValidityForOriginationEnd()); writeOptionalDate(out, mSpec.getKeyValidityForConsumptionEnd()); if (mSpec.isDigestsSpecified()) { out.writeStringArray(mSpec.getDigests()); } else { out.writeStringArray(null); } out.writeStringArray(mSpec.getEncryptionPaddings()); out.writeStringArray(mSpec.getSignaturePaddings()); out.writeStringArray(mSpec.getBlockModes()); out.writeBoolean(mSpec.isRandomizedEncryptionRequired()); out.writeBoolean(mSpec.isUserAuthenticationRequired()); out.writeInt(mSpec.getUserAuthenticationValidityDurationSeconds()); out.writeBoolean(mSpec.isUserPresenceRequired()); out.writeByteArray(mSpec.getAttestationChallenge()); out.writeBoolean(mSpec.isUniqueIdIncluded()); out.writeBoolean(mSpec.isUserAuthenticationValidWhileOnBody()); out.writeBoolean(mSpec.isInvalidatedByBiometricEnrollment()); out.writeBoolean(mSpec.isStrongBoxBacked()); out.writeBoolean(mSpec.isUserConfirmationRequired()); out.writeBoolean(mSpec.isUnlockedDeviceRequired()); } private static Date readDateOrNull(Parcel in) { boolean hasDate = in.readBoolean(); if (hasDate) { return new Date(in.readLong()); } else { return null; } } private ParcelableKeyGenParameterSpec(Parcel in) { final String keystoreAlias = in.readString(); final int purposes = in.readInt(); final int uid = in.readInt(); final int keySize = in.readInt(); final int keySpecType = in.readInt(); AlgorithmParameterSpec algorithmSpec = null; if (keySpecType == ALGORITHM_PARAMETER_SPEC_NONE) { algorithmSpec = null; } else if (keySpecType == ALGORITHM_PARAMETER_SPEC_RSA) { int rsaKeySize = in.readInt(); BigInteger publicExponent = new BigInteger(in.createByteArray()); algorithmSpec = new RSAKeyGenParameterSpec(rsaKeySize, publicExponent); } else if (keySpecType == ALGORITHM_PARAMETER_SPEC_EC) { String stdName = in.readString(); algorithmSpec = new ECGenParameterSpec(stdName); } else { throw new IllegalArgumentException( String.format("Unknown algorithm parameter spec: %d", keySpecType)); } final X500Principal certificateSubject = new X500Principal(in.createByteArray()); final BigInteger certificateSerialNumber = new BigInteger(in.createByteArray()); final Date certificateNotBefore = new Date(in.readLong()); final Date certificateNotAfter = new Date(in.readLong()); final Date keyValidityStartDate = readDateOrNull(in); final Date keyValidityForOriginationEnd = readDateOrNull(in); final Date keyValidityForConsumptionEnd = readDateOrNull(in); final String[] digests = in.createStringArray(); final String[] encryptionPaddings = in.createStringArray(); final String[] signaturePaddings = in.createStringArray(); final String[] blockModes = in.createStringArray(); final boolean randomizedEncryptionRequired = in.readBoolean(); final boolean userAuthenticationRequired = in.readBoolean(); final int userAuthenticationValidityDurationSeconds = in.readInt(); final boolean userPresenceRequired = in.readBoolean(); final byte[] attestationChallenge = in.createByteArray(); final boolean uniqueIdIncluded = in.readBoolean(); final boolean userAuthenticationValidWhileOnBody = in.readBoolean(); final boolean invalidatedByBiometricEnrollment = in.readBoolean(); final boolean isStrongBoxBacked = in.readBoolean(); final boolean userConfirmationRequired = in.readBoolean(); final boolean unlockedDeviceRequired = in.readBoolean(); // The KeyGenParameterSpec is intentionally not constructed using a Builder here: // The intention is for this class to break if new parameters are added to the // KeyGenParameterSpec constructor (whereas using a builder would silently drop them). mSpec = new KeyGenParameterSpec( keystoreAlias, uid, keySize, algorithmSpec, certificateSubject, certificateSerialNumber, certificateNotBefore, certificateNotAfter, keyValidityStartDate, keyValidityForOriginationEnd, keyValidityForConsumptionEnd, purposes, digests, encryptionPaddings, signaturePaddings, blockModes, randomizedEncryptionRequired, userAuthenticationRequired, userAuthenticationValidityDurationSeconds, userPresenceRequired, attestationChallenge, uniqueIdIncluded, userAuthenticationValidWhileOnBody, invalidatedByBiometricEnrollment, isStrongBoxBacked, userConfirmationRequired, unlockedDeviceRequired); } public static final Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() { @Override public ParcelableKeyGenParameterSpec createFromParcel(Parcel in) { return new ParcelableKeyGenParameterSpec(in); } @Override public ParcelableKeyGenParameterSpec[] newArray(int size) { return new ParcelableKeyGenParameterSpec[size]; } }; public KeyGenParameterSpec getSpec() { return mSpec; } }