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

Commit f3f9fcdc authored by Bo Zhu's avatar Bo Zhu
Browse files

Avoid using XPath in recoverablekeystore.certificate.CertUtils

Change-Id: I6b1a0b460334d69766daa13eed98a5a067bbedca
Fix: 74730734
Test: atest
FrameworksServicesTests:com.android.server.locksettings.recoverablekeystore
atest -m RecoveryControllerHostTest RecoverableKeyStoreEndtoEndHostTest
RecoverySessionHostTest
parent ed11f757
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.
     *