GordianCAStatus.java

/*
 * GordianKnot: Security Suite
 * Copyright 2026. Tony Washer
 *
 * 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 io.github.tonywasher.joceanus.gordianknot.impl.core.cert;

import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
import io.github.tonywasher.joceanus.gordianknot.api.cert.GordianKeyPairUsage;
import io.github.tonywasher.joceanus.gordianknot.api.cert.GordianKeyPairUse;
import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianIOException;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.ExtensionsGenerator;

import java.io.IOException;
import java.math.BigInteger;

/**
 * CA Status.
 */
public class GordianCAStatus {
    /**
     * Is this a CA.
     */
    private final boolean isCA;

    /**
     * The Path Length.
     */
    private final BigInteger thePathLen;

    /**
     * Constructor.
     *
     * @param pCA is this a CA?
     */
    GordianCAStatus(final boolean pCA) {
        isCA = pCA;
        thePathLen = null;
    }

    /**
     * Constructor.
     *
     * @param pPathLen the path length.
     */
    GordianCAStatus(final BigInteger pPathLen) {
        isCA = true;
        thePathLen = pPathLen;
    }

    /**
     * Constructor.
     *
     * @param pUsage        the keyPair usage
     * @param pSignerStatus the signerStatus.
     */
    GordianCAStatus(final GordianKeyPairUsage pUsage,
                    final GordianCAStatus pSignerStatus) {
        isCA = pUsage.getUsageSet().contains(GordianKeyPairUse.CERTIFICATE);
        if (isCA) {
            final BigInteger mySignerPath = pSignerStatus.getPathLen();
            thePathLen = mySignerPath == null
                    ? BigInteger.ZERO
                    : mySignerPath.add(BigInteger.ONE);
        } else {
            thePathLen = null;
        }
    }

    /**
     * is this a CA?.
     *
     * @return true/false
     */
    boolean isCA() {
        return isCA;
    }

    /**
     * Obtain the pathLen.
     *
     * @return the pathLen
     */
    BigInteger getPathLen() {
        return thePathLen;
    }

    /**
     * Create extensions.
     *
     * @param pGenerator the extensions generator
     * @throws GordianException on error
     */
    void createExtensions(final ExtensionsGenerator pGenerator) throws GordianException {
        /* Protect against exceptions */
        try {
            pGenerator.addExtension(Extension.basicConstraints, isCA, thePathLen == null
                    ? new BasicConstraints(isCA)
                    : new BasicConstraints(thePathLen.intValue()));
        } catch (IOException e) {
            throw new GordianIOException("Failed to create extensions", e);
        }
    }

    /**
     * Determine CAStatus.
     *
     * @param pExtensions the extensions.
     * @return the CAStatus
     */
    static GordianCAStatus determineStatus(final Extensions pExtensions) {
        /* Access details */
        final BasicConstraints myConstraint = BasicConstraints.fromExtensions(pExtensions);

        /* Check for CA */
        if (myConstraint != null && myConstraint.isCA()) {
            return new GordianCAStatus(myConstraint.getPathLenConstraint());
        }

        /* Not CA */
        return new GordianCAStatus(false);
    }
}