/*
 * Copyright (C) 2015 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.hardware.security.keymint.KeyParameter;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;

import java.security.InvalidKeyException;
import java.security.SignatureSpi;
import java.util.List;

/**
 * Base class for {@link SignatureSpi} providing Android KeyStore backed RSA signatures.
 *
 * @hide
 */
abstract class AndroidKeyStoreRSASignatureSpi extends AndroidKeyStoreSignatureSpiBase {

    abstract static class PKCS1Padding extends AndroidKeyStoreRSASignatureSpi {
        PKCS1Padding(int keymasterDigest) {
            super(keymasterDigest, KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN);
        }

        @Override
        protected final int getAdditionalEntropyAmountForSign() {
            // No entropy required for this deterministic signature scheme.
            return 0;
        }
    }

    public static final class NONEWithPKCS1Padding extends PKCS1Padding {
        public NONEWithPKCS1Padding() {
            super(KeymasterDefs.KM_DIGEST_NONE);
        }
        @Override
        protected String getAlgorithm() {
            return "NONEwithRSA";
        }
    }

    public static final class MD5WithPKCS1Padding extends PKCS1Padding {
        public MD5WithPKCS1Padding() {
            super(KeymasterDefs.KM_DIGEST_MD5);
        }
        @Override
        protected String getAlgorithm() {
            return "MD5withRSA";
        }
    }

    public static final class SHA1WithPKCS1Padding extends PKCS1Padding {
        public SHA1WithPKCS1Padding() {
            super(KeymasterDefs.KM_DIGEST_SHA1);
        }
        @Override
        protected String getAlgorithm() {
            return "SHA1withRSA";
        }
    }

    public static final class SHA224WithPKCS1Padding extends PKCS1Padding {
        public SHA224WithPKCS1Padding() {
            super(KeymasterDefs.KM_DIGEST_SHA_2_224);
        }
        @Override
        protected String getAlgorithm() {
            return "SHA224withRSA";
        }
    }

    public static final class SHA256WithPKCS1Padding extends PKCS1Padding {
        public SHA256WithPKCS1Padding() {
            super(KeymasterDefs.KM_DIGEST_SHA_2_256);
        }
        @Override
        protected String getAlgorithm() {
            return "SHA256withRSA";
        }
    }

    public static final class SHA384WithPKCS1Padding extends PKCS1Padding {
        public SHA384WithPKCS1Padding() {
            super(KeymasterDefs.KM_DIGEST_SHA_2_384);
        }
        @Override
        protected String getAlgorithm() {
            return "SHA384withRSA";
        }
    }

    public static final class SHA512WithPKCS1Padding extends PKCS1Padding {
        public SHA512WithPKCS1Padding() {
            super(KeymasterDefs.KM_DIGEST_SHA_2_512);
        }
        @Override
        protected String getAlgorithm() {
            return "SHA512withRSA";
        }
    }

    abstract static class PSSPadding extends AndroidKeyStoreRSASignatureSpi {
        private static final int SALT_LENGTH_BYTES = 20;

        PSSPadding(int keymasterDigest) {
            super(keymasterDigest, KeymasterDefs.KM_PAD_RSA_PSS);
        }

        @Override
        protected final int getAdditionalEntropyAmountForSign() {
            return SALT_LENGTH_BYTES;
        }
    }

    public static final class SHA1WithPSSPadding extends PSSPadding {
        public SHA1WithPSSPadding() {
            super(KeymasterDefs.KM_DIGEST_SHA1);
        }
        @Override
        protected String getAlgorithm() {
            return "SHA1withRSA/PSS";
        }
    }

    public static final class SHA224WithPSSPadding extends PSSPadding {
        public SHA224WithPSSPadding() {
            super(KeymasterDefs.KM_DIGEST_SHA_2_224);
        }
        @Override
        protected String getAlgorithm() {
            return "SHA224withRSA/PSS";
        }
    }

    public static final class SHA256WithPSSPadding extends PSSPadding {
        public SHA256WithPSSPadding() {
            super(KeymasterDefs.KM_DIGEST_SHA_2_256);
        }
        @Override
        protected String getAlgorithm() {
            return "SHA256withRSA/PSS";
        }
    }

    public static final class SHA384WithPSSPadding extends PSSPadding {
        public SHA384WithPSSPadding() {
            super(KeymasterDefs.KM_DIGEST_SHA_2_384);
        }
        @Override
        protected String getAlgorithm() {
            return "SHA384withRSA/PSS";
        }
    }

    public static final class SHA512WithPSSPadding extends PSSPadding {
        public SHA512WithPSSPadding() {
            super(KeymasterDefs.KM_DIGEST_SHA_2_512);
        }
        @Override
        protected String getAlgorithm() {
            return "SHA512withRSA/PSS";
        }
    }

    private final int mKeymasterDigest;
    private final int mKeymasterPadding;

    AndroidKeyStoreRSASignatureSpi(int keymasterDigest, int keymasterPadding) {
        mKeymasterDigest = keymasterDigest;
        mKeymasterPadding = keymasterPadding;
    }

    @Override
    protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
        if (!KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(key.getAlgorithm())) {
            throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
                    + ". Only " + KeyProperties.KEY_ALGORITHM_RSA + " supported");
        }
        super.initKey(key);
    }

    @Override
    protected final void resetAll() {
        super.resetAll();
    }

    @Override
    protected final void resetWhilePreservingInitState() {
        super.resetWhilePreservingInitState();
    }

    @Override
    protected final void addAlgorithmSpecificParametersToBegin(
            @NonNull List<KeyParameter> parameters) {
        parameters.add(KeyStore2ParameterUtils.makeEnum(
                KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA
        ));
        parameters.add(KeyStore2ParameterUtils.makeEnum(
                KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest
        ));
        parameters.add(KeyStore2ParameterUtils.makeEnum(
                KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding
        ));
    }
}
