Home | History | Annotate | Download | only in ct
      1 /*
      2  * Copyright (C) 2015 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 org.conscrypt.ct;
     18 
     19 import java.io.File;
     20 import java.io.FileNotFoundException;
     21 import java.io.IOException;
     22 import java.io.PrintWriter;
     23 import java.io.StringBufferInputStream;
     24 import java.security.PublicKey;
     25 import junit.framework.TestCase;
     26 import org.conscrypt.OpenSSLKey;
     27 
     28 public class CTLogStoreImplTest extends TestCase {
     29     private static final String[] LOG_KEYS = new String[] {
     30         "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmXg8sUUzwBYaWrRb+V0IopzQ6o3U" +
     31         "yEJ04r5ZrRXGdpYM8K+hB0pXrGRLI0eeWz+3skXrS0IO83AhA3GpRL6s6w==",
     32 
     33         "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAErEULmlBnX9L/+AK20hLYzPMFozYx" +
     34         "pP0Wm1ylqGkPEwuDKn9DSpNSOym49SN77BLGuAXu9twOW/qT+ddIYVBEIw==",
     35 
     36         "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEP6PGcXmjlyCBz2ZFUuUjrgbZLaEF" +
     37         "gfLUkt2cEqlSbb4vTuB6WWmgC9h0L6PN6JF0CPcajpBKGlTI15242a8d4g==",
     38 
     39         "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAER3qB0NADsP1szXxe4EagrD/ryPVh" +
     40         "Y/azWbKyXcK12zhXnO8WH2U4QROVUMctFXLflIzw0EivdRN9t7UH1Od30w==",
     41 
     42         "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEY0ww9JqeJvzVtKNTPVb3JZa7s0ZV" +
     43         "duH3PpshpMS5XVoPRSjSQCph6f3HjUcM3c4N2hpa8OFbrFFy37ttUrgD+A=="
     44     };
     45     private static final String[] LOG_FILENAMES = new String[] {
     46         "df1c2ec11500945247a96168325ddc5c7959e8f7c6d388fc002e0bbd3f74d764",
     47         "84f8ae3f613b13407a75fa2893b93ab03b18d86c455fe7c241ae020033216446",
     48         "89baa01a445100009d8f9a238947115b30702275aafee675a7d94b6b09287619",
     49         "57456bffe268e49a190dce4318456034c2b4958f3c0201bed5a366737d1e74ca",
     50         "896c898ced4b8e6547fa351266caae4ca304f1c1ec2b623c2ee259c5452147b0"
     51     };
     52 
     53     private static final CTLogInfo[] LOGS;
     54     private static final String[] LOGS_SERIALIZED;
     55 
     56     static {
     57         try {
     58             int logCount = LOG_KEYS.length;
     59             LOGS = new CTLogInfo[logCount];
     60             LOGS_SERIALIZED = new String[logCount];
     61             for (int i = 0; i < logCount; i++) {
     62                 PublicKey key = OpenSSLKey.fromPublicKeyPemInputStream(new StringBufferInputStream(
     63                     "-----BEGIN PUBLIC KEY-----\n" +
     64                     LOG_KEYS[i] + "\n" +
     65                     "-----END PUBLIC KEY-----\n")).getPublicKey();
     66                 String description = String.format("Test Log %d", i);
     67                 String url = String.format("log%d.example.com", i);
     68                 LOGS[i] = new CTLogInfo(key, description, url);
     69                 LOGS_SERIALIZED[i] = String.format("description:%s\nurl:%s\nkey:%s",
     70                     description, url, LOG_KEYS[i]);
     71             }
     72         } catch (Exception e) {
     73             throw new RuntimeException(e);
     74         }
     75     }
     76 
     77     /* CTLogStoreImpl loads the list of logs lazily when they are first needed
     78      * to avoid any overhead when CT is disabled.
     79      * This test simply forces the logs to be loaded to make sure it doesn't
     80      * fail, as all of the other tests use a different log store.
     81      */
     82     public void test_getDefaultFallbackLogs() {
     83         CTLogInfo[] knownLogs = CTLogStoreImpl.getDefaultFallbackLogs();
     84         assertEquals(KnownLogs.LOG_COUNT, knownLogs.length);
     85     }
     86 
     87     public void test_loadLog() throws Exception {
     88         CTLogInfo log = CTLogStoreImpl.loadLog(new StringBufferInputStream(LOGS_SERIALIZED[0]));
     89         assertEquals(LOGS[0], log);
     90 
     91         File testFile = writeFile(LOGS_SERIALIZED[0]);
     92         log = CTLogStoreImpl.loadLog(testFile);
     93         assertEquals(LOGS[0], log);
     94 
     95         // Empty log file, used to mask fallback logs
     96         assertEquals(null, CTLogStoreImpl.loadLog(new StringBufferInputStream("")));
     97         try {
     98             CTLogStoreImpl.loadLog(new StringBufferInputStream("randomgarbage"));
     99             fail("InvalidLogFileException not thrown");
    100         } catch (CTLogStoreImpl.InvalidLogFileException e) {}
    101 
    102         try {
    103             CTLogStoreImpl.loadLog(new File("/nonexistent"));
    104             fail("FileNotFoundException not thrown");
    105         } catch (FileNotFoundException e) {}
    106     }
    107 
    108     public void test_getKnownLog() throws Exception {
    109         File userDir = createTempDirectory();
    110         userDir.deleteOnExit();
    111 
    112         File systemDir = createTempDirectory();
    113         systemDir.deleteOnExit();
    114 
    115         CTLogInfo[] fallback = new CTLogInfo[] { LOGS[2], LOGS[3] };
    116 
    117         CTLogStore store = new CTLogStoreImpl(userDir, systemDir, fallback);
    118 
    119         /* Add logs 0 and 1 to the user and system directories respectively
    120          * Log 2 & 3 are part of the fallbacks
    121          * But mask log 3 with an empty file in the user directory.
    122          * Log 4 is not in the store
    123          */
    124         File log0File = new File(userDir, LOG_FILENAMES[0]);
    125         File log1File = new File(systemDir, LOG_FILENAMES[1]);
    126         File log3File = new File(userDir, LOG_FILENAMES[3]);
    127         File log4File = new File(userDir, LOG_FILENAMES[4]);
    128 
    129         writeFile(log0File, LOGS_SERIALIZED[0]);
    130         writeFile(log1File, LOGS_SERIALIZED[1]);
    131         writeFile(log3File, "");
    132 
    133         // Logs 01 are present, log 2 is in the fallback and unused, log 3 is present but masked,
    134         // log 4 is missing
    135         assertEquals(LOGS[0], store.getKnownLog(LOGS[0].getID()));
    136         assertEquals(LOGS[1], store.getKnownLog(LOGS[1].getID()));
    137         // Fallback logs are not used if the userDir is present.
    138         assertEquals(null, store.getKnownLog(LOGS[2].getID()));
    139         assertEquals(null, store.getKnownLog(LOGS[3].getID()));
    140         assertEquals(null, store.getKnownLog(LOGS[4].getID()));
    141 
    142         /* Test whether CTLogStoreImpl caches properly
    143          * Modify the files on the disk, the result of the store should not change
    144          * Delete log 0, mask log 1, add log 4
    145          */
    146         log0File.delete();
    147         writeFile(log1File, "");
    148         writeFile(log4File, LOGS_SERIALIZED[4]);
    149 
    150         assertEquals(LOGS[0], store.getKnownLog(LOGS[0].getID()));
    151         assertEquals(LOGS[1], store.getKnownLog(LOGS[1].getID()));
    152         assertEquals(null, store.getKnownLog(LOGS[4].getID()));
    153 
    154         // Test that fallback logs are used when the userDir doesn't exist.
    155         File doesntExist = new File("/doesnt/exist/");
    156         store = new CTLogStoreImpl(doesntExist, doesntExist, fallback);
    157         assertEquals(LOGS[2], store.getKnownLog(LOGS[2].getID()));
    158         assertEquals(LOGS[3], store.getKnownLog(LOGS[3].getID()));
    159     }
    160 
    161     /**
    162      * Create a temporary file and write to it.
    163      * The file will be deleted on exit.
    164      * @param contents The data to be written to the file
    165      * @return A reference to the temporary file
    166      */
    167     private File writeFile(String contents) throws IOException {
    168         File file = File.createTempFile("test", null);
    169         file.deleteOnExit();
    170         writeFile(file, contents);
    171         return file;
    172     }
    173 
    174     private static void writeFile(File file, String contents) throws FileNotFoundException {
    175         PrintWriter writer = new PrintWriter(file);
    176         try {
    177             writer.write(contents);
    178         } finally {
    179             writer.close();
    180         }
    181     }
    182 
    183     /*
    184      * This is NOT safe, as another process could create a file between delete() and mkdir()
    185      * It should be fine for tests though
    186      */
    187     private static File createTempDirectory() throws IOException {
    188         File folder = File.createTempFile("test", "");
    189         folder.delete();
    190         folder.mkdir();
    191         return folder;
    192     }
    193 }
    194 
    195