Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a8acd375 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Avoid using XPath in recoverablekeystore.certificate.CertUtils"

parents af7deaba f3f9fcdc
Loading
Loading
Loading
Loading
+53 −46
Original line number Diff line number Diff line
@@ -16,13 +16,17 @@

package com.android.server.locksettings.recoverablekeystore.certificate;

import static javax.xml.xpath.XPathConstants.NODESET;

import android.annotation.IntDef;
import android.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -40,7 +44,6 @@ import java.security.cert.CertPathBuilderException;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
@@ -58,15 +61,6 @@ import java.util.Set;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/** Utility functions related to parsing and validating public-key certificates. */
public final class CertUtils {
@@ -167,50 +161,63 @@ public final class CertUtils {
    static List<String> getXmlNodeContents(@MustExist int mustExist, Element rootNode,
            String... nodeTags)
            throws CertParsingException {
        String expression = String.join("/", nodeTags);

        XPath xPath = XPathFactory.newInstance().newXPath();
        NodeList nodeList;
        try {
            nodeList = (NodeList) xPath.compile(expression).evaluate(rootNode, NODESET);
        } catch (XPathExpressionException e) {
            throw new CertParsingException(e);
        if (nodeTags.length == 0) {
            throw new CertParsingException("The tag list must not be empty");
        }

        // Go down through all the intermediate node tags (except the last tag for the leaf nodes).
        // Note that this implementation requires that at most one path exists for the given
        // intermediate node tags.
        Element parent = rootNode;
        for (int i = 0; i < nodeTags.length - 1; i++) {
            String tag = nodeTags[i];
            List<Element> children = getXmlDirectChildren(parent, tag);
            if ((children.size() == 0 && mustExist != MUST_EXIST_UNENFORCED)
                    || children.size() > 1) {
                throw new CertParsingException(
                        "The XML file must contain exactly one path with the tag " + tag);
            }
            if (children.size() == 0) {
                return new ArrayList<>();
            }
            parent = children.get(0);
        }

        switch (mustExist) {
            case MUST_EXIST_UNENFORCED:
                break;

            case MUST_EXIST_EXACTLY_ONE:
                if (nodeList.getLength() != 1) {
        // Then collect the contents of the leaf nodes.
        List<Element> leafs = getXmlDirectChildren(parent, nodeTags[nodeTags.length - 1]);
        if (mustExist == MUST_EXIST_EXACTLY_ONE && leafs.size() != 1) {
            throw new CertParsingException(
                    "The XML file must contain exactly one node with the path "
                                    + expression);
                            + String.join("/", nodeTags));
        }
                break;

            case MUST_EXIST_AT_LEAST_ONE:
                if (nodeList.getLength() == 0) {
        if (mustExist == MUST_EXIST_AT_LEAST_ONE && leafs.size() == 0) {
            throw new CertParsingException(
                    "The XML file must contain at least one node with the path "
                                    + expression);
                            + String.join("/", nodeTags));
        }
                break;

            default:
                throw new UnsupportedOperationException(
                        "This value of MustExist is not supported: " + mustExist);
        }

        List<String> result = new ArrayList<>();
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
        for (Element leaf : leafs) {
            // Remove whitespaces and newlines.
            result.add(node.getTextContent().replaceAll("\\s", ""));
            result.add(leaf.getTextContent().replaceAll("\\s", ""));
        }
        return result;
    }

    /** Get the direct child nodes with a given tag. */
    private static List<Element> getXmlDirectChildren(Element parent, String tag) {
        // Cannot use Element.getElementsByTagName because it will return all descendant elements
        // with the tag name, i.e. not only the direct child nodes.
        List<Element> children = new ArrayList<>();
        NodeList childNodes = parent.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node node = childNodes.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals(tag)) {
                children.add((Element) node);
            }
        }
        return children;
    }

    /**
     * Decodes a base64-encoded string.
     *