/*
 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

import java.net.SocketTimeoutException;
import java.security.PrivateKey;
import java.security.cert.CertPathValidatorException;
import java.security.cert.X509Certificate;
import java.io.File;

/**
 * Test for OCSP timeout.
 *
 * OCSP connection timeout can be set via com.sun.security.ocsp.timeout system
 * property.
 *
 * Test scenario: - a certificate is good - an application tries to check
 * revocation status of certificate via OCSP - SOCKS proxy is used - Proxy
 * server delays responses - OCSP connection timeout value is less than proxy
 * delay
 *
 * Expected result: validation failure (CertPathValidatorException that is
 * caused by SocketTimeoutException)
 */

/*
 * @test
 * @author Shivangi Gupta
 * @library ../../common ../../common/bcpkix-jdk15on.jar ../../common/bcprov-jdk15on.jar
 * @run main/othervm/policy=socksProxyTimeoutpolicy -Djava.security.manager
 * SocksProxyOCSPTimeoutReachedTest
 */
public class SocksProxyOCSPTimeoutReachedTest {

    private static final String WORK_DIR = System.getProperty("test.src", ".");
    private X509Certificate cert;
    private X509Certificate trustedCert;
    private PrivateKey ocspResponderKey;

    private final int proxyDelay = 30;

    private final int ocspConnectionTimeout = 5;

    public static void main(String[] args) throws Exception {
        SocksProxyOCSPTimeoutReachedTest test = new SocksProxyOCSPTimeoutReachedTest();
        test.runTest(args);
    }

    private void parseArgs(String[] args) throws Exception {
        String BASE = WORK_DIR.substring(0, WORK_DIR.indexOf("RevocationChecking") + "RevocationChecking".length());
        String certFile = BASE + File.separator + "common" + File.separator + "data" + File.separator + "ca"
                + File.separator + "valid.pem";
        String trustedCertFile = BASE + File.separator + "common" + File.separator + "data" + File.separator + "ca"
                + File.separator + "root.pem";
        String ocspResponderKeyFile = BASE + File.separator + "common" + File.separator + "data" + File.separator + "ca"
                + File.separator + "ocsp_responder_key";
        cert = Utils.getCertFromFile(certFile);
        trustedCert = Utils.getCertFromFile(trustedCertFile);
        ocspResponderKey = Utils.loadRsaPrivateKey(ocspResponderKeyFile);

        if (cert == null) {
            throw new IllegalArgumentException("Certificate is not set");
        }

        if (trustedCert == null) {
            throw new IllegalArgumentException("Trusted certificate is not set");
        }

        if (ocspResponderKey == null) {
            throw new IllegalArgumentException("OCSP responder key is not set");
        }
    }

    private void runTest(String[] args) throws Exception {
        parseArgs(args);

        // Start SOCKS4 proxy server that will be used by the main application for
        // network operations
        int socksProxyPort = Utils.getFreePort();
        Socks4Server proxyServer = new Socks4Server(socksProxyPort);
        proxyServer.setDaemon(true);
        proxyServer.start();
        System.out.println("SOCKS4 Server was started on  port" + socksProxyPort);

        proxyServer.setDelay(proxyDelay);
        Utils.sleep(5);

        System.setProperty("socksProxyHost", "localhost");
        System.setProperty("socksProxyPort", String.valueOf(socksProxyPort));
        System.setProperty("socksProxyVersion", "4");
        System.setProperty("socksNonProxyHosts", "");

        SimpleOCSPResponderDataBase ocspResponderDatabase = new SimpleOCSPResponderDataBase();
        ocspResponderDatabase.addGood(cert.getSerialNumber());
        int simpleOcspResponderPort = Utils.getFreePort();
        SimpleOCSPResponder simpleOcspResponder = new SimpleOCSPResponder(simpleOcspResponderPort,
                ocspResponderDatabase, "SHA1withRSA", ocspResponderKey);
        simpleOcspResponder.setDaemon(true);
        simpleOcspResponder.start();

        Utils.sleep(5);

        try {
            // Set maximum allowable OCSP connection timeout
            System.setProperty("com.sun.security.ocsp.timeout", String.valueOf(ocspConnectionTimeout));
            System.out.println("com.sun.security.ocsp.timeout" + System.getProperty("com.sun.security.ocsp.timeout"));

            System.out.println("Testing with OCSP connection timeout that is less than SOCKS proxy delay");
            System.out.println("Expected result: validation failure (CertPathValidatorException)");

            Utils.doOcspValidation(cert, trustedCert, simpleOcspResponderPort);

            throw new Exception("Test failed: CertPathValidatorException was not thrown");
        } catch (CertPathValidatorException e) {
            System.out.println("Got CertPathValidatorException" + e.getMessage());
            System.out.println("Expected CertPathValidatorException was thrown");
            Throwable cause = e.getCause();
            if (cause == null || !(cause instanceof SocketTimeoutException)) {
                e.printStackTrace();
                throw new Exception("CertPathValidatorException was not caused by SocketTimeoutException");
            }
            System.out.println(
                    "Test passed: expected CertPathValidatorException that was caused by SocketTimeoutException");
        }
    }
}
