1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.locksettings.recoverablekeystore.certificate;
18 
19 import android.annotation.Nullable;
20 
21 import org.w3c.dom.Element;
22 
23 import java.security.cert.X509Certificate;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.Date;
27 import java.util.List;
28 
29 /**
30  * Parses and holds the XML file containing the signature of the XML file containing the list of THM
31  * public-key certificates.
32  */
33 public final class SigXml {
34 
35     private static final String INTERMEDIATE_CERT_LIST_TAG = "intermediates";
36     private static final String INTERMEDIATE_CERT_ITEM_TAG = "cert";
37     private static final String SIGNER_CERT_NODE_TAG = "certificate";
38     private static final String SIGNATURE_NODE_TAG = "value";
39 
40     private final List<X509Certificate> intermediateCerts;
41     private final X509Certificate signerCert;
42     private final byte[] signature;
43 
SigXml( List<X509Certificate> intermediateCerts, X509Certificate signerCert, byte[] signature)44     private SigXml(
45             List<X509Certificate> intermediateCerts, X509Certificate signerCert, byte[] signature) {
46         this.intermediateCerts = intermediateCerts;
47         this.signerCert = signerCert;
48         this.signature = signature;
49     }
50 
51     /**
52      * Verifies the signature contained in this XML file against a trusted root certificate and the
53      * binary content of another file. The signer's public-key certificate and possible intermediate
54      * CA certificates are included in this XML file, and will be validated against the trusted root
55      * certificate.
56      *
57      * @param trustedRoot     the trusted root certificate
58      * @param signedFileBytes the original file content that has been signed
59      * @param validationDate use null for current time
60      *
61      * @throws CertValidationException if the signature verification fails, or the signer's
62      *                                 certificate contained in this XML file cannot be validated
63      *                                 based on the trusted root certificate
64      */
verifyFileSignature( X509Certificate trustedRoot, byte[] signedFileBytes, @Nullable Date validationDate)65     public void verifyFileSignature(
66             X509Certificate trustedRoot, byte[] signedFileBytes, @Nullable Date validationDate)
67             throws CertValidationException {
68         CertUtils.validateCert(validationDate, trustedRoot, intermediateCerts, signerCert);
69         CertUtils.verifyRsaSha256Signature(signerCert.getPublicKey(), signature, signedFileBytes);
70     }
71 
72     /**
73      * Parses a byte array as the content of the XML file containing the signature and related
74      * certificates.
75      *
76      * @param bytes the bytes of the XML file
77      * @return a {@code SigXml} instance that contains the parsing result
78      * @throws CertParsingException if any parsing error occurs
79      */
parse(byte[] bytes)80     public static SigXml parse(byte[] bytes) throws CertParsingException {
81         Element rootNode = CertUtils.getXmlRootNode(bytes);
82         return new SigXml(
83                 parseIntermediateCerts(rootNode), parseSignerCert(rootNode),
84                 parseFileSignature(rootNode));
85     }
86 
parseIntermediateCerts(Element rootNode)87     private static List<X509Certificate> parseIntermediateCerts(Element rootNode)
88             throws CertParsingException {
89         List<String> contents =
90                 CertUtils.getXmlNodeContents(
91                         CertUtils.MUST_EXIST_UNENFORCED,
92                         rootNode,
93                         INTERMEDIATE_CERT_LIST_TAG,
94                         INTERMEDIATE_CERT_ITEM_TAG);
95         List<X509Certificate> res = new ArrayList<>();
96         for (String content : contents) {
97             res.add(CertUtils.decodeCert(CertUtils.decodeBase64(content)));
98         }
99         return Collections.unmodifiableList(res);
100     }
101 
parseSignerCert(Element rootNode)102     private static X509Certificate parseSignerCert(Element rootNode) throws CertParsingException {
103         List<String> contents =
104                 CertUtils.getXmlNodeContents(
105                         CertUtils.MUST_EXIST_EXACTLY_ONE, rootNode, SIGNER_CERT_NODE_TAG);
106         return CertUtils.decodeCert(CertUtils.decodeBase64(contents.get(0)));
107     }
108 
parseFileSignature(Element rootNode)109     private static byte[] parseFileSignature(Element rootNode) throws CertParsingException {
110         List<String> contents =
111                 CertUtils.getXmlNodeContents(
112                         CertUtils.MUST_EXIST_EXACTLY_ONE, rootNode, SIGNATURE_NODE_TAG);
113         return CertUtils.decodeBase64(contents.get(0));
114     }
115 }
116