content: /*
   * Copyright (C) 2018 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 com.android.ike.ikev2;
  
  import android.annotation.IntDef;
  import android.util.ArraySet;
  
  import com.android.ike.ikev2.message.IkePayload;
  import com.android.ike.ikev2.message.IkeSaPayload.DhGroupTransform;
  import com.android.ike.ikev2.message.IkeSaPayload.EncryptionTransform;
  import com.android.ike.ikev2.message.IkeSaPayload.EsnTransform;
  import com.android.ike.ikev2.message.IkeSaPayload.IntegrityTransform;
  import com.android.ike.ikev2.message.IkeSaPayload.PrfTransform;
  import com.android.ike.ikev2.message.IkeSaPayload.Transform;
  
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.List;
  import java.util.Set;
  
  /**
   * SaProposal represents a user configured set contains cryptograhic algorithms and key generating
   * materials for negotiating an IKE or Child SA.
   *
   * <p>User must provide at least a valid SaProposal when they are creating a new IKE SA or Child SA.
   *
   * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key Exchange
   *     Protocol Version 2 (IKEv2)</a>
   */
  public final class SaProposal {
  
      @Retention(RetentionPolicy.SOURCE)
      @IntDef({
          ENCRYPTION_ALGORITHM_3DES,
          ENCRYPTION_ALGORITHM_AES_CBC,
          ENCRYPTION_ALGORITHM_AES_GCM_8,
          ENCRYPTION_ALGORITHM_AES_GCM_12,
          ENCRYPTION_ALGORITHM_AES_GCM_16
      })
      public @interface EncryptionAlgorithm {}
  
      public static final int ENCRYPTION_ALGORITHM_3DES = 3;
      public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12;
      public static final int ENCRYPTION_ALGORITHM_AES_GCM_8 = 18;
      public static final int ENCRYPTION_ALGORITHM_AES_GCM_12 = 19;
      public static final int ENCRYPTION_ALGORITHM_AES_GCM_16 = 20;
  
      private static final Set<Integer> SUPPORTED_ENCRYPTION_ALGORITHM;
  
      static {
          SUPPORTED_ENCRYPTION_ALGORITHM = new ArraySet<>();
          SUPPORTED_ENCRYPTION_ALGORITHM.add(ENCRYPTION_ALGORITHM_3DES);
          SUPPORTED_ENCRYPTION_ALGORITHM.add(ENCRYPTION_ALGORITHM_AES_CBC);
          SUPPORTED_ENCRYPTION_ALGORITHM.add(ENCRYPTION_ALGORITHM_AES_GCM_8);
          SUPPORTED_ENCRYPTION_ALGORITHM.add(ENCRYPTION_ALGORITHM_AES_GCM_12);
          SUPPORTED_ENCRYPTION_ALGORITHM.add(ENCRYPTION_ALGORITHM_AES_GCM_16);
      }
  
      public static final int KEY_LEN_AES_128 = 128;
      public static final int KEY_LEN_AES_192 = 192;
      public static final int KEY_LEN_AES_256 = 256;
  
      @Retention(RetentionPolicy.SOURCE)
      @IntDef({PSEUDORANDOM_FUNCTION_HMAC_SHA1, PSEUDORANDOM_FUNCTION_AES128_XCBC})
      public @interface PseudorandomFunction {}
  
      public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2;
      public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4;
  
      private static final Set<Integer> SUPPORTED_PSEUDORANDOM_FUNCTION;
  
      static {
          SUPPORTED_PSEUDORANDOM_FUNCTION = new ArraySet<>();
          SUPPORTED_PSEUDORANDOM_FUNCTION.add(PSEUDORANDOM_FUNCTION_HMAC_SHA1);
          SUPPORTED_PSEUDORANDOM_FUNCTION.add(PSEUDORANDOM_FUNCTION_AES128_XCBC);
      }
  
      @Retention(RetentionPolicy.SOURCE)
      @IntDef({
          INTEGRITY_ALGORITHM_NONE,
          INTEGRITY_ALGORITHM_HMAC_SHA1_96,
          INTEGRITY_ALGORITHM_AES_XCBC_96,
          INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
          INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
          INTEGRITY_ALGORITHM_HMAC_SHA2_512_256
      })
      public @interface IntegrityAlgorithm {}
  
      public static final int INTEGRITY_ALGORITHM_NONE = 0;
      public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2;
      public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5;
      public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12;
      public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13;
      public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14;
  
      private static final Set<Integer> SUPPORTED_INTEGRITY_ALGORITHM;
  
      static {
          SUPPORTED_INTEGRITY_ALGORITHM = new ArraySet<>();
          SUPPORTED_INTEGRITY_ALGORITHM.add(INTEGRITY_ALGORITHM_NONE);
          SUPPORTED_INTEGRITY_ALGORITHM.add(INTEGRITY_ALGORITHM_HMAC_SHA1_96);
          SUPPORTED_INTEGRITY_ALGORITHM.add(INTEGRITY_ALGORITHM_AES_XCBC_96);
          SUPPORTED_INTEGRITY_ALGORITHM.add(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
          SUPPORTED_INTEGRITY_ALGORITHM.add(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
          SUPPORTED_INTEGRITY_ALGORITHM.add(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
      }
  
      @Retention(RetentionPolicy.SOURCE)
      @IntDef({DH_GROUP_NONE, DH_GROUP_1024_BIT_MODP, DH_GROUP_2048_BIT_MODP})
      public @interface DhGroup {}
  
      public static final int DH_GROUP_NONE = 0;
      public static final int DH_GROUP_1024_BIT_MODP = 2;
      public static final int DH_GROUP_2048_BIT_MODP = 14;
  
      private static final Set<Integer> SUPPORTED_DH_GROUP;
  
      static {
          SUPPORTED_DH_GROUP = new ArraySet<>();
          SUPPORTED_DH_GROUP.add(DH_GROUP_NONE);
          SUPPORTED_DH_GROUP.add(DH_GROUP_1024_BIT_MODP);
          SUPPORTED_DH_GROUP.add(DH_GROUP_2048_BIT_MODP);
      }
  
      /** Package private */
      @IkePayload.ProtocolId private final int mProtocolId;
      /** Package private */
      private final EncryptionTransform[] mEncryptionAlgorithms;
      /** Package private */
      private final PrfTransform[] mPseudorandomFunctions;
      /** Package private */
      private final IntegrityTransform[] mIntegrityAlgorithms;
      /** Package private */
      private final DhGroupTransform[] mDhGroups;
      /** Package private */
      private final EsnTransform[] mEsns;
  
      private SaProposal(
              @IkePayload.ProtocolId int protocol,
              EncryptionTransform[] encryptionAlgos,
              PrfTransform[] prfs,
              IntegrityTransform[] integrityAlgos,
              DhGroupTransform[] dhGroups) {
          mProtocolId = protocol;
          mEncryptionAlgorithms = encryptionAlgos;
          mPseudorandomFunctions = prfs;
          mIntegrityAlgorithms = integrityAlgos;
          mDhGroups = dhGroups;
  
          if (protocol == IkePayload.PROTOCOL_ID_IKE) {
              // Do not negotiate ESN for IKE SA proposal
              mEsns = new EsnTransform[0];
          } else {
              // Do not support negotiating Child SAs using extended sequence numbers.
              mEsns = new EsnTransform[] {new EsnTransform()};
          }
      }
  
      /**
       * Construct SaProposal from a decoded inbound IKE packet, only called by IkeSaPayload.
       *
       * @param protocol IP protocol ID
       * @param encryptionAlgos encryption algorithms decoded from inbound IKE packet.
       * @param prfs pseudorandom functions decoded from inbound IKE packet.
       * @param integrityAlgos integrity algorithms decoded from inbound IKE packet.
       * @param dhGroups Dh groups decoded from inbound IKE packet.
       * @param esns ESN policies decoded from IKE packet.
       */
      public SaProposal(
              @IkePayload.ProtocolId int protocol,
              EncryptionTransform[] encryptionAlgos,
              PrfTransform[] prfs,
              IntegrityTransform[] integrityAlgos,
              DhGroupTransform[] dhGroups,
              EsnTransform[] esns) {
          mProtocolId = protocol;
          mEncryptionAlgorithms = encryptionAlgos;
          mPseudorandomFunctions = prfs;
          mIntegrityAlgorithms = integrityAlgos;
          mDhGroups = dhGroups;
          mEsns = esns;
      }
  
      /**
       * Check if the current SaProposal from the SA responder is consistent with the selected
       * reqProposal from the SA initiator.
       *
       * @param reqProposal selected SaProposal from SA initiator
       * @return if current SaProposal from SA responder is consistent with the selected reqProposal
       *     from SA initiator.
       */
      public boolean isNegotiatedFrom(SaProposal reqProposal) {
          return isTransformSelectedFrom(mEncryptionAlgorithms, reqProposal.mEncryptionAlgorithms)
                  && isTransformSelectedFrom(
                          mPseudorandomFunctions, reqProposal.mPseudorandomFunctions)
                  && isTransformSelectedFrom(mIntegrityAlgorithms, reqProposal.mIntegrityAlgorithms)
                  && isTransformSelectedFrom(mDhGroups, reqProposal.mDhGroups)
                  && isTransformSelectedFrom(mEsns, reqProposal.mEsns);
      }
  
      /** Package private */
      static boolean isTransformSelectedFrom(Transform[] selected, Transform[] selectFrom) {
          // If the selected proposal has multiple transforms with the same type, the responder MUST
          // choose a single one.
          if ((selected.length > 1) || (selected.length == 0) != (selectFrom.length == 0)) {
              return false;
          }
  
          if (selected.length == 0) return true;
  
          return Arrays.asList(selectFrom).contains(selected[0]);
      }
  
      /*Package private*/
      @IkePayload.ProtocolId
      int getProtocolId() {
          return mProtocolId;
      }
  
      /*Package private*/
      EncryptionTransform[] getEncryptionTransforms() {
          return mEncryptionAlgorithms;
      }
  
      /*Package private*/
      PrfTransform[] getPrfTransforms() {
          return mPseudorandomFunctions;
      }
  
      /*Package private*/
      IntegrityTransform[] getIntegrityTransforms() {
          return mIntegrityAlgorithms;
      }
  
      /*Package private*/
      DhGroupTransform[] getDhGroupTransforms() {
          return mDhGroups;
      }
  
      /*Package private*/
      EsnTransform[] getEsnTransforms() {
          return mEsns;
      }
  
      /**
       * Return all SA Transforms in this SaProposal to be encoded for building an outbound IKE
       * message.
       *
       * <p>This method can be called by only IKE library.
       *
       * @return Array of Transforms to be encoded.
       */
      public Transform[] getAllTransforms() {
          int encodedNumTransforms =
                  mEncryptionAlgorithms.length
                          + mPseudorandomFunctions.length
                          + mIntegrityAlgorithms.length
                          + mDhGroups.length
                          + mEsns.length;
  
          List<Transform> transformList = new ArrayList<Transform>(encodedNumTransforms);
          transformList.addAll(Arrays.asList(mEncryptionAlgorithms));
          transformList.addAll(Arrays.asList(mPseudorandomFunctions));
          transformList.addAll(Arrays.asList(mIntegrityAlgorithms));
          transformList.addAll(Arrays.asList(mDhGroups));
          transformList.addAll(Arrays.asList(mEsns));
  
          return transformList.toArray(new Transform[encodedNumTransforms]);
      }
  
      /**
       * This class can be used to incrementally construct a SaProposal. SaProposal instances are
       * immutable once built.
       *
       * <p>TODO: Support users to add algorithms from most preferred to least preferred.
       */
      public static final class Builder {
          private static final String ERROR_TAG = "Invalid SA Proposal: ";
  
          /** Indicate if Builder is for building IKE SA proposal or Child SA proposal. */
          private final boolean mIsIkeProposal;
          /**
           * Indicate if Builder is for building first Child SA proposal or addtional Child SA
           * proposal. Only valid if mIsIkeProposal is false.
           */
          private final boolean mIsFirstChild;
  
          // Use set to avoid adding repeated algorithms.
          private final Set<EncryptionTransform> mProposedEncryptAlgos = new ArraySet<>();
          private final Set<PrfTransform> mProposedPrfs = new ArraySet<>();
          private final Set<IntegrityTransform> mProposedIntegrityAlgos = new ArraySet<>();
          private final Set<DhGroupTransform> mProposedDhGroups = new ArraySet<>();
  
          private boolean mHasAead = false;
  
          private Builder(boolean isIke, boolean isFirstChild) {
              mIsIkeProposal = isIke;
              mIsFirstChild = isFirstChild;
          }
  
          private static boolean isAead(@EncryptionAlgorithm int algorithm) {
              switch (algorithm) {
                  case ENCRYPTION_ALGORITHM_3DES:
                      // Fall through
                  case ENCRYPTION_ALGORITHM_AES_CBC:
                      return false;
                  case ENCRYPTION_ALGORITHM_AES_GCM_8:
                      // Fall through
                  case ENCRYPTION_ALGORITHM_AES_GCM_12:
                      // Fall through
                  case ENCRYPTION_ALGORITHM_AES_GCM_16:
                      return true;
                  default:
                      // Won't hit here.
                      throw new IllegalArgumentException("Unsupported Encryption Algorithm.");
              }
          }
  
          private EncryptionTransform[] buildEncryptAlgosOrThrow() {
              if (mProposedEncryptAlgos.isEmpty()) {
                  throw new IllegalArgumentException(
                          ERROR_TAG + "Encryption algorithm must be proposed.");
              }
  
              return mProposedEncryptAlgos.toArray(
                      new EncryptionTransform[mProposedEncryptAlgos.size()]);
          }
  
          private PrfTransform[] buildPrfsOrThrow() {
              if (mIsIkeProposal == mProposedPrfs.isEmpty()) {
                  throw new IllegalArgumentException(
                          ERROR_TAG + "Invalid PRF configuration for this SA Proposal.");
              }
  
              return mProposedPrfs.toArray(new PrfTransform[mProposedPrfs.size()]);
          }
  
          private IntegrityTransform[] buildIntegAlgosForIkeOrThrow() {
              // When building IKE SA Proposal with normal-mode ciphers, mProposedIntegrityAlgos must
              // not be empty and must not have INTEGRITY_ALGORITHM_NONE. When building IKE SA
              // Proposal with combined-mode ciphers, mProposedIntegrityAlgos must be either empty or
              // only have INTEGRITY_ALGORITHM_NONE.
              if (mProposedIntegrityAlgos.isEmpty() && !mHasAead) {
                  throw new IllegalArgumentException(
                          ERROR_TAG
                                  + "Integrity algorithm "
                                  + "must be proposed with normal ciphers in IKE proposal.");
              }
  
              for (IntegrityTransform transform : mProposedIntegrityAlgos) {
                  if ((transform.id == INTEGRITY_ALGORITHM_NONE) != mHasAead) {
                      throw new IllegalArgumentException(
                              ERROR_TAG
                                      + "Invalid integrity algorithm configuration"
                                      + " for this SA Proposal");
                  }
              }
  
              return mProposedIntegrityAlgos.toArray(
                      new IntegrityTransform[mProposedIntegrityAlgos.size()]);
          }
  
          private IntegrityTransform[] buildIntegAlgosForChildOrThrow() {
              // When building Child SA Proposal with normal-mode ciphers, there is no contraint on
              // integrity algorithm. When building Child SA Proposal with combined-mode ciphers,
              // mProposedIntegrityAlgos must be either empty or only have INTEGRITY_ALGORITHM_NONE.
              for (IntegrityTransform transform : mProposedIntegrityAlgos) {
                  if (transform.id != INTEGRITY_ALGORITHM_NONE && mHasAead) {
                      throw new IllegalArgumentException(
                              ERROR_TAG
                                      + "Only INTEGRITY_ALGORITHM_NONE can be"
                                      + " proposed with combined-mode ciphers in any proposal.");
                  }
              }
  
              return mProposedIntegrityAlgos.toArray(
                      new IntegrityTransform[mProposedIntegrityAlgos.size()]);
          }
  
          private DhGroupTransform[] buildDhGroupsForIkeOrThrow() {
              if (mProposedDhGroups.isEmpty()) {
                  throw new IllegalArgumentException(
                          ERROR_TAG + "DH group must be proposed in IKE SA proposal.");
              }
  
              for (DhGroupTransform transform : mProposedDhGroups) {
                  if (transform.id == DH_GROUP_NONE) {
                      throw new IllegalArgumentException(
                              ERROR_TAG
                                      + "None-value DH group must not"
                                      + " be proposed in IKE SA proposal");
                  }
              }
  
              return mProposedDhGroups.toArray(new DhGroupTransform[mProposedDhGroups.size()]);
          }
  
          private DhGroupTransform[] buildDhGroupsForChildOrThrow() {
              for (DhGroupTransform transform : mProposedDhGroups) {
                  if (transform.id != DH_GROUP_NONE && mIsFirstChild) {
                      throw new IllegalArgumentException(
                              ERROR_TAG
                                      + "Only DH_GROUP_NONE can be"
                                      + " proposed in first Child SA proposal.");
                  }
              }
              return mProposedDhGroups.toArray(new DhGroupTransform[mProposedDhGroups.size()]);
          }
  
          /** Returns a new Builder for a IKE SA Proposal. */
          public static Builder newIkeSaProposalBuilder() {
              return new Builder(true, false);
          }
  
          /**
           * Returns a new Builder for a Child SA Proposal.
           *
           * @param isFirstChildSaProposal indicates if this SA proposal for first Child SA.
           * @return Builder for a Child SA Proposal.
           */
          public static Builder newChildSaProposalBuilder(boolean isFirstChildSaProposal) {
              return new Builder(false, isFirstChildSaProposal);
          }
  
          /**
           * Adds an encryption algorithm to SA proposal being built.
           *
           * @param algorithm encryption algorithm to add to SaProposal.
           * @return Builder of SaProposal.
           */
          public Builder addEncryptionAlgorithm(@EncryptionAlgorithm int algorithm) {
              // Construct EncryptionTransform and validate proposed algorithm during
              // construction.
              EncryptionTransform encryptionTransform = new EncryptionTransform(algorithm);
  
              validateOnlyOneModeEncryptAlgoProposedOrThrow(algorithm);
  
              mProposedEncryptAlgos.add(encryptionTransform);
              return this;
          }
  
          /**
           * Adds an encryption algorithm with specific key length to SA proposal being built.
           *
           * @param algorithm encryption algorithm to add to SaProposal.
           * @param keyLength key length of algorithm.
           * @return Builder of SaProposal.
           * @throws IllegalArgumentException if AEAD and non-combined mode algorithms are mixed.
           */
          public Builder addEncryptionAlgorithm(@EncryptionAlgorithm int algorithm, int keyLength) {
              // Construct EncryptionTransform and validate proposed algorithm during
              // construction.
              EncryptionTransform encryptionTransform = new EncryptionTransform(algorithm, keyLength);
  
              validateOnlyOneModeEncryptAlgoProposedOrThrow(algorithm);
  
              mProposedEncryptAlgos.add(encryptionTransform);
              return this;
          }
  
          private void validateOnlyOneModeEncryptAlgoProposedOrThrow(
                  @EncryptionAlgorithm int algorithm) {
              boolean isCurrentAead = isAead(algorithm);
  
              if (!mProposedEncryptAlgos.isEmpty() && (mHasAead ^ isCurrentAead)) {
                  throw new IllegalArgumentException(
                          ERROR_TAG
                                  + "Proposal cannot has both normal ciphers "
                                  + "and combined-mode ciphers.");
              }
  
              if (isCurrentAead) mHasAead = true;
          }
  
          /**
           * Adds a pseudorandom function to SA proposal being built.
           *
           * @param algorithm pseudorandom function to add to SaProposal.
           * @return Builder of SaProposal.
           */
          public Builder addPseudorandomFunction(@PseudorandomFunction int algorithm) {
              // Construct PrfTransform and validate proposed algorithm during
              // construction.
              mProposedPrfs.add(new PrfTransform(algorithm));
              return this;
          }
  
          /**
           * Adds an integrity algorithm to SA proposal being built.
           *
           * @param algorithm integrity algorithm to add to SaProposal.
           * @return Builder of SaProposal.
           */
          public Builder addIntegrityAlgorithm(@IntegrityAlgorithm int algorithm) {
              // Construct IntegrityTransform and validate proposed algorithm during
              // construction.
              mProposedIntegrityAlgos.add(new IntegrityTransform(algorithm));
              return this;
          }
  
          /**
           * Adds a Diffie-Hellman Group to SA proposal being built.
           *
           * @param dhGroup to add to SaProposal.
           * @return Builder of SaProposal.
           */
          public Builder addDhGroup(@DhGroup int dhGroup) {
              // Construct DhGroupTransform and validate proposed dhGroup during
              // construction.
              mProposedDhGroups.add(new DhGroupTransform(dhGroup));
              return this;
          }
  
          /**
           * Validates, builds and returns the SaProposal
           *
           * @return SaProposal the validated SaProposal.
           * @throws IllegalArgumentException if SaProposal is invalid.
           */
          public SaProposal build() {
              EncryptionTransform[] encryptionTransforms = buildEncryptAlgosOrThrow();
              PrfTransform[] prfTransforms = buildPrfsOrThrow();
              IntegrityTransform[] integrityTransforms =
                      mIsIkeProposal
                              ? buildIntegAlgosForIkeOrThrow()
                              : buildIntegAlgosForChildOrThrow();
  
              DhGroupTransform[] dhGroupTransforms =
                      mIsIkeProposal ? buildDhGroupsForIkeOrThrow() : buildDhGroupsForChildOrThrow();
              // IKE library only supports negotiating ESP Child SA.
              int protocol = mIsIkeProposal ? IkePayload.PROTOCOL_ID_IKE : IkePayload.PROTOCOL_ID_ESP;
  
              return new SaProposal(
                      protocol,
                      encryptionTransforms,
                      prfTransforms,
                      integrityTransforms,
                      dhGroupTransforms);
          }
      }
  
      /**
       * Check if the provided algorithm is a supported encryption algorithm.
       *
       * @param algorithm IKE standard encryption algorithm id.
       * @return true if the provided algorithm is a supported encryption algorithm.
       */
      public static boolean isSupportedEncryptionAlgorithm(@EncryptionAlgorithm int algorithm) {
          return SUPPORTED_ENCRYPTION_ALGORITHM.contains(algorithm);
      }
  
      /**
       * Check if the provided algorithm is a supported pseudorandom function.
       *
       * @param algorithm IKE standard pseudorandom function id.
       * @return true if the provided algorithm is a supported pseudorandom function.
       */
      public static boolean isSupportedPseudorandomFunction(@PseudorandomFunction int algorithm) {
          return SUPPORTED_PSEUDORANDOM_FUNCTION.contains(algorithm);
      }
  
      /**
       * Check if the provided algorithm is a supported integrity algorithm.
       *
       * @param algorithm IKE standard integrity algorithm id.
       * @return true if the provided algorithm is a supported integrity algorithm.
       */
      public static boolean isSupportedIntegrityAlgorithm(@IntegrityAlgorithm int algorithm) {
          return SUPPORTED_INTEGRITY_ALGORITHM.contains(algorithm);
      }
  
      /**
       * Check if the provided group number is for a supported Diffie-Hellman Group.
       *
       * @param dhGroup IKE standard DH Group id.
       * @return true if the provided number is for a supported Diffie-Hellman Group.
       */
      public static boolean isSupportedDhGroup(@DhGroup int dhGroup) {
          return SUPPORTED_DH_GROUP.contains(dhGroup);
      }
  }
  