# KeyStoreManager.kt

    /*
     * The MIT License
     *
     * Original work sponsored and donated by The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     * Copyright (C) 2025 The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy of
     * this software and associated documentation files (the "Software"), to deal in
     * the Software without restriction, including without limitation the rights to
     * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
     * of the Software, and to permit persons to whom the Software is furnished to do
     * so, subject to the following conditions:
     *
     *
     * The above copyright notice and this permission notice shall be included in all
     * copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     * SOFTWARE.
     */
    package dk.nsp.mingraviditet.core.datastore.crypto

    import android.security.keystore.KeyGenParameterSpec
    import android.security.keystore.KeyProperties
    import java.security.KeyStore
    import javax.crypto.KeyGenerator
    import javax.crypto.SecretKey

    interface KeyStoreManager {
        fun getOrGenerateKey(alias: String): SecretKey
        fun getExistingSecretKey(alias: String): SecretKey?
    }

    class KeyStoreManagerImpl : KeyStoreManager {

        private val keyGenerator by lazy {
            KeyGenerator.getInstance(
                /* algorithm = */
                Crypto.ALGORITHM,
                /* provider = */
                Crypto.KEY_STORE_PROVIDER,
            )
        }

        private val keyStore: KeyStore by lazy {
            KeyStore.getInstance(
                Crypto.KEY_STORE_PROVIDER,
            ).apply {
                load(null)
            }
        }

        override fun getOrGenerateKey(alias: String): SecretKey {
            keyGenerator.initKeyGenerator(alias)
            return getExistingSecretKey(alias) ?: generateSecretKey()
        }

        override fun getExistingSecretKey(alias: String): SecretKey? {
            return (keyStore.getEntry(alias, null) as? KeyStore.SecretKeyEntry)?.secretKey
        }

        private fun generateSecretKey(): SecretKey {
            return keyGenerator.generateKey()
        }

        private fun keyGeneratorParamSpec(alias: String) =
            KeyGenParameterSpec.Builder(
                /* keystoreAlias = */
                alias,
                /* purposes = */
                KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT,
            )

        private fun KeyGenerator.initKeyGenerator(alias: String) {
            this.init(
                keyGeneratorParamSpec(alias)
                    .setBlockModes(Crypto.BLOCK_MODE)
                    .setEncryptionPaddings(Crypto.PADDING)
                    .setRandomizedEncryptionRequired(true)
                    .setUserAuthenticationRequired(true)
                    .setUserAuthenticationParameters(
                        KEY_TIMEOUT,
                        KeyProperties.AUTH_DEVICE_CREDENTIAL or KeyProperties.AUTH_BIOMETRIC_STRONG,
                    )
                    .build(),
            )
        }

        companion object {
            private const val KEY_TIMEOUT = 30 * 60 // 30 minutes
        }
    }