package com.airlock.iam.factor.application.configuration.fido;

import com.airlock.iam.core.misc.util.crypto.keystore.KeystoreConfig;
import com.airlock.iam.plugin.api.annotation.config.Groups;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.license.PluginLicenseRestriction;
import com.airlock.iam.plugin.api.annotation.lifecycle.AfterConfigActivation;
import com.airlock.iam.plugin.api.annotation.property.GroupProperty;
import com.airlock.iam.plugin.api.annotation.property.IntegerProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.security.KeyStore;

@Plugin(
   description = "Verifies that the attestation certificate and the associated chain of certificates provided in an attestation statement produced by a FIDO authenticator during registration can be trusted. Currently, only the following attestation statement formats are supported:<ul><li>\"packed\" attestation statement (see <a href=\"https://www.w3.org/TR/webauthn/#packed-attestation\">Webauthn - Packed Attestation Statement Format</a>)</li><li>\"fido-u2f\" attestation statement (see <a href=\"https://www.w3.org/TR/webauthn/#fido-u2f-attestation\">Webauthn - FIDO U2F Attestation Statement Format</a>)</li></ul><p>An attestation certificate in such an attestation statement is valid if trust can be derived back to a trusted certificate (a.k.a. trust anchor) configured in the truststore, requiring that<ul><li>the attestation certificate and every certificate in the chain is valid at the time of registration;</li><li>the signature of the attestation certificate and every certificate in the chain (excepted the last one) can be verified using the public key attested in the next certificate;</li><li>the signature of the attestation certificate or of at least one certificate in the chain can be verified by a public key attested in one of the trusted certificates configured in the trust store.</li></ul></p>",
   displayName = "FIDO Attestation Certificate Trust Verifier"
)
@PluginLicenseRestriction(moduleAffiliation = "FIDO")
@Groups(groupSequence = {"Basic Settings", "Advanced Settings"}, openGroups = "Basic Settings")
public final class FidoAttestationCertificateTrustVerifierConfig implements FidoAttestationVerifierConfig {
   @PluginProperty(
      description = "The truststore containing the trusted certificates to verify the attestation certificate produced by a FIDO authenticator during registration. The certificates must be in <strong>X509</strong> format and <strong>all</strong> X509 certificates in the truststore will be used for verification and assumed to be trusted.<p><b>Security warning:</b> Putting a certificate in the trust store has security implications. In particular, trusting a certificate implies trusting every chain of certificates that can be derived back to this trusted certificate. Depending on your requirements as to which type of FIDO authenticators should or should not be accepted, it might be advisable to not directly trust root certificates of a particular FIDO authenticator manufacturer, in order to ensure that future FIDO authenticators from this manufacturer will not be automatically accepted. If the system needs to be as closed as possible, then trusting only the deepest intermediate certificate in the chain would be advisable.</p>",
      optional = false
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private KeystoreConfig trustStore;
   @IntegerProperty(
      description = "Maximum length of certificate chain to verify. Longer chains of certificates will fail this verification. <p>It is recommended to keep this value as low as possible to ensure that Airlock IAM is not overly burdened by such verifications (denial of service).</p>",
      defaultValue = 3,
      exampleValues = {1, 3, 10, 100},
      minValue = 1,
      maxValue = 200,
      optional = true
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private int maximumCertificateChainLength;
   private KeyStore loadedKeyStore;

   @AfterConfigActivation
   void loadKeyStore() {
      this.loadedKeyStore = this.trustStore.loadKeystore();
   }

   public KeyStore getKeyStore() {
      return this.loadedKeyStore;
   }

   public int getMaximumCertificateChainLength() {
      return this.maximumCertificateChainLength;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static FidoAttestationCertificateTrustVerifierConfig.FidoAttestationCertificateTrustVerifierConfigBuilder builder() {
      return new FidoAttestationCertificateTrustVerifierConfig.FidoAttestationCertificateTrustVerifierConfigBuilder();
   }

   @SuppressFBWarnings(justification = "generated code")
   public FidoAttestationCertificateTrustVerifierConfig() {
   }

   @SuppressFBWarnings(justification = "generated code")
   public FidoAttestationCertificateTrustVerifierConfig(KeystoreConfig trustStore, int maximumCertificateChainLength, KeyStore loadedKeyStore) {
      this.trustStore = trustStore;
      this.maximumCertificateChainLength = maximumCertificateChainLength;
      this.loadedKeyStore = loadedKeyStore;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class FidoAttestationCertificateTrustVerifierConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private KeystoreConfig trustStore;
      @SuppressFBWarnings(justification = "generated code")
      private int maximumCertificateChainLength;
      @SuppressFBWarnings(justification = "generated code")
      private KeyStore loadedKeyStore;

      @SuppressFBWarnings(justification = "generated code")
      FidoAttestationCertificateTrustVerifierConfigBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoAttestationCertificateTrustVerifierConfig.FidoAttestationCertificateTrustVerifierConfigBuilder trustStore(KeystoreConfig trustStore) {
         this.trustStore = trustStore;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoAttestationCertificateTrustVerifierConfig.FidoAttestationCertificateTrustVerifierConfigBuilder maximumCertificateChainLength(
         int maximumCertificateChainLength
      ) {
         this.maximumCertificateChainLength = maximumCertificateChainLength;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoAttestationCertificateTrustVerifierConfig.FidoAttestationCertificateTrustVerifierConfigBuilder loadedKeyStore(KeyStore loadedKeyStore) {
         this.loadedKeyStore = loadedKeyStore;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoAttestationCertificateTrustVerifierConfig build() {
         return new FidoAttestationCertificateTrustVerifierConfig(this.trustStore, this.maximumCertificateChainLength, this.loadedKeyStore);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "FidoAttestationCertificateTrustVerifierConfig.FidoAttestationCertificateTrustVerifierConfigBuilder(trustStore="
            + this.trustStore
            + ", maximumCertificateChainLength="
            + this.maximumCertificateChainLength
            + ", loadedKeyStore="
            + this.loadedKeyStore
            + ")";
      }
   }
}
