CertificateUtilsProviderFuzzer.java

// Copyright 2023 the cncf-fuzzing authors
//
// 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.
//
///////////////////////////////////////////////////////////////////////////
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import java.io.ByteArrayInputStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;
import org.keycloak.common.crypto.CertificateUtilsProvider;
import org.keycloak.crypto.def.BCCertificateUtilsProvider;
import org.keycloak.crypto.elytron.ElytronCertificateUtils;
import org.keycloak.crypto.fips.BCFIPSCertificateUtilsProvider;

/**
 * This fuzzer targets the methods in different
 * Certificate Utils Provider implementation classes
 * in the crypto package.
 */
public class CertificateUtilsProviderFuzzer {
  private static Boolean initSuccess;
  private static CertificateFactory cf;
  private static KeyPair keyPair;

  public static void fuzzerInitialize() {
    try {
      // Initialize certificate factory
      cf = CertificateFactory.getInstance("X.509");

      // Initialize key pair
      KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
      generator.initialize(2048);
      keyPair = generator.generateKeyPair();
    } catch (CertificateException | NoSuchAlgorithmException e) {
      // Directly exit if initialisation fails
      throw new RuntimeException(e);
    }
  }

  public static void fuzzerTestOneInput(FuzzedDataProvider data) {
    // Initialise base certificate related object
    CertificateUtilsProvider provider = null;
    X509Certificate cert = null;

    try {
      // Randomly create a certificate utils provider instance
      switch (data.consumeInt(1, 3)) {
        case 1:
          provider = new BCCertificateUtilsProvider();
          break;
        case 2:
          provider = new ElytronCertificateUtils();
          break;
        case 3:
          provider = new BCFIPSCertificateUtilsProvider();
          break;
      }

      // Randomly choose which method to invoke
      Integer choice = data.consumeInt(1, 5);
      switch (choice) {
        case 1:
          cert = (X509Certificate) cf.generateCertificate(
              new ByteArrayInputStream(data.consumeBytes(data.remainingBytes() / 2)));
          provider.generateV3Certificate(
              keyPair, keyPair.getPrivate(), cert, data.consumeRemainingAsString());
          break;
        case 2:
          provider.generateV1SelfSignedCertificate(keyPair, data.consumeRemainingAsString());
          break;
        case 3:
          cert = (X509Certificate) cf.generateCertificate(
              new ByteArrayInputStream(data.consumeBytes(data.remainingBytes() / 2)));
          provider.getCertificatePolicyList(cert);
          break;
        case 4:
          cert = (X509Certificate) cf.generateCertificate(
              new ByteArrayInputStream(data.consumeBytes(data.remainingBytes() / 2)));
          provider.getCRLDistributionPoints(cert);
          break;
        case 5:
          Date startDate = new Date(data.consumeLong());
          Date expiryDate = new Date(data.consumeLong());
          provider.createServicesTestCertificate(data.consumeString(data.remainingBytes() / 2),
              startDate, expiryDate, keyPair, data.consumeRemainingAsString());
          break;
      }
    } catch (Exception | NoSuchMethodError | ExceptionInInitializerError | NoClassDefFoundError e) {
      // Known exception and errors directly thrown from the above methods
      // Some methods above capture all exception and throw the general
      // Exception explicitly, thus it need to be catch.
      // ExceptionInInitializerError and NoClassDefFoundError is caught to
      // ensure BouncyIntegration fail because of missing providers won't stop
      // the fuzzer.
    }
  }
}