/*
 * Copyright (C) 2025 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.keystore2;

import android.annotation.NonNull;
import android.security.KeyStoreSecurityLevel;
import android.security.keystore.KeyProperties;
import android.system.keystore2.Authorization;
import android.system.keystore2.KeyDescriptor;

import java.security.ProviderException;

/**
 * {@link java.security.PrivateKey} implementation for ML-DSA private keys backed by Android
 * Keystore. Only the ML-DSA-65 and ML-DSA-87 parameter sets are supported.
 *
 * <p>This class is not case-sensitive since Java Security Standard Algorithm Names are not
 * case-sensitive.
 *
 * <p>This class is partially compliant with <a href="https://openjdk.org/jeps/497">JEP 497</a>. The
 * only deviation is that it does <b>not</b> implement {@link
 * java.security.AsymmetricKey#getParams()}. This is because the {@link java.security.AsymmetricKey}
 * interface was introduced in JDK 22 and has not been imported into Android. The interface would
 * not only need to be imported, but wrapper classes would need to be created to enable Conscrypt to
 * continue to be backwards-compatible to JDK 8. However, callers can leverage other methods to
 * retrieve the same information:
 *
 * <ul>
 *   <li>The algorithm family ("ML-DSA") can be obtained via {@link
 *       java.security.Key#getAlgorithm()}.
 *   <li>The parameter set name (e.g. "ML-DSA-65") can be determined by calling {@link
 *       java.security.Key#getEncoded()} on an instance of this class and inspecting the PKCS#8
 *       preamble since it is fixed for each parameter set.
 * </ul>
 *
 * @hide
 */
public class AndroidKeyStoreMlDsaPrivateKey extends AndroidKeyStorePrivateKey
        implements AndroidKeyStoreMlDsaKey {
    // Java Security Standard Algorithm Name for the key's ML-DSA parameter set (e.g. "ML-DSA-65").
    private final String mMlDsaAlgorithm;

    /**
     * Constructs an {@link AndroidKeyStoreMlDsaPrivateKey}.
     *
     * @param descriptor Key descriptor.
     * @param keyId Key ID.
     * @param authorizations Key authorizations.
     * @param securityLevel Security level of the key.
     * @param algorithm Java Security Standard Algorithm Name for the key's ML-DSA parameter set
     *     (not the family name "ML-DSA").
     */
    public AndroidKeyStoreMlDsaPrivateKey(
            @NonNull KeyDescriptor descriptor,
            long keyId,
            @NonNull Authorization[] authorizations,
            @NonNull KeyStoreSecurityLevel securityLevel,
            @NonNull String algorithm) {
        super(descriptor, keyId, authorizations, KeyProperties.KEY_ALGORITHM_ML_DSA, securityLevel);

        // Check that a (supported) parameter set name, not the family name "ML-DSA", is provided.
        // This is technically not necessary since this constructor should only be called from
        // {@link AndroidKeyStoreMlDsaPublicKey#getPrivateKey()} with the parameter set name, but
        // there's no way to prevent additional, incorrect calls.
        if (!algorithm.equalsIgnoreCase(KeyProperties.KEY_ALGORITHM_ML_DSA_65)
                && !algorithm.equalsIgnoreCase(KeyProperties.KEY_ALGORITHM_ML_DSA_87)) {
            throw new ProviderException("Unsupported algorithm: " + algorithm);
        }

        mMlDsaAlgorithm = algorithm;
    }

    @Override
    public String getMlDsaAlgorithm() {
        return mMlDsaAlgorithm;
    }
}
