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

Commit e287fd2c authored by Alan Stokes's avatar Alan Stokes
Browse files

Store CRCs as Integer rather than as hex strings.

This allows us to do lookup with fewer memory allocations.

Bug: 142333043
Test: atest services/tests/servicestests/src/com/android/server/net/watchlist/*.java
Change-Id: I8a8ecd488fabfaed6f7627d934c0ae5adbd4ac4c
parent 8401f0e4
Loading
Loading
Loading
Loading
+63 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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 com.android.server.net.watchlist;

import com.android.internal.util.HexDump;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Helper class to store a set of harmful CRC32s in memory.
 * TODO: Optimize memory usage using int array with binary search.
 */
class HarmfulCrcs {

    private final Set<Integer> mCrcSet;

    HarmfulCrcs(List<byte[]> digests) {
        final HashSet<Integer> crcSet = new HashSet<>();
        final int size = digests.size();
        for (int i = 0; i < size; i++) {
            byte[] bytes = digests.get(i);
            if (bytes.length <= 4) {
                int crc = 0;
                for (byte b : bytes) {
                    // Remember byte is signed
                    crc = (crc << 8) | (b & 0xff);
                }
                crcSet.add(crc);
            }
        }
        mCrcSet = Collections.unmodifiableSet(crcSet);
    }

    public boolean contains(int crc) {
        return mCrcSet.contains(crc);
    }

    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        for (int crc : mCrcSet) {
            pw.println(HexDump.toHexString(crc));
        }
        pw.println("");
    }
}
+14 −19
Original line number Original line Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.server.net.watchlist;


import android.annotation.Nullable;
import android.annotation.Nullable;
import android.os.FileUtils;
import android.os.FileUtils;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Log;
import android.util.Slog;
import android.util.Slog;
import android.util.Xml;
import android.util.Xml;
@@ -66,11 +65,11 @@ class WatchlistConfig {
    }
    }


    private static class CrcShaDigests {
    private static class CrcShaDigests {
        final HarmfulDigests crc32Digests;
        public final HarmfulCrcs crc32s;
        final HarmfulDigests sha256Digests;
        public final HarmfulDigests sha256Digests;


        public CrcShaDigests(HarmfulDigests crc32Digests, HarmfulDigests sha256Digests) {
        CrcShaDigests(HarmfulCrcs crc32s, HarmfulDigests sha256Digests) {
            this.crc32Digests = crc32Digests;
            this.crc32s = crc32s;
            this.sha256Digests = sha256Digests;
            this.sha256Digests = sha256Digests;
        }
        }
    }
    }
@@ -140,9 +139,9 @@ class WatchlistConfig {
                }
                }
            }
            }
            parser.require(XmlPullParser.END_TAG, null, XmlTags.WATCHLIST_CONFIG);
            parser.require(XmlPullParser.END_TAG, null, XmlTags.WATCHLIST_CONFIG);
            mDomainDigests = new CrcShaDigests(new HarmfulDigests(crc32DomainList),
            mDomainDigests = new CrcShaDigests(new HarmfulCrcs(crc32DomainList),
                    new HarmfulDigests(sha256DomainList));
                    new HarmfulDigests(sha256DomainList));
            mIpDigests = new CrcShaDigests(new HarmfulDigests(crc32IpList),
            mIpDigests = new CrcShaDigests(new HarmfulCrcs(crc32IpList),
                    new HarmfulDigests(sha256IpList));
                    new HarmfulDigests(sha256IpList));
            Log.i(TAG, "Reload watchlist done");
            Log.i(TAG, "Reload watchlist done");
        } catch (IllegalStateException | NullPointerException | NumberFormatException |
        } catch (IllegalStateException | NullPointerException | NumberFormatException |
@@ -171,8 +170,8 @@ class WatchlistConfig {
            return false;
            return false;
        }
        }
        // First it does a quick CRC32 check.
        // First it does a quick CRC32 check.
        final byte[] crc32 = getCrc32(domain);
        final int crc32 = getCrc32(domain);
        if (!domainDigests.crc32Digests.contains(crc32)) {
        if (!domainDigests.crc32s.contains(crc32)) {
            return false;
            return false;
        }
        }
        // Now we do a slow SHA256 check.
        // Now we do a slow SHA256 check.
@@ -187,8 +186,8 @@ class WatchlistConfig {
            return false;
            return false;
        }
        }
        // First it does a quick CRC32 check.
        // First it does a quick CRC32 check.
        final byte[] crc32 = getCrc32(ip);
        final int crc32 = getCrc32(ip);
        if (!ipDigests.crc32Digests.contains(crc32)) {
        if (!ipDigests.crc32s.contains(crc32)) {
            return false;
            return false;
        }
        }
        // Now we do a slow SHA256 check.
        // Now we do a slow SHA256 check.
@@ -198,15 +197,11 @@ class WatchlistConfig {




    /** Get CRC32 of a string
    /** Get CRC32 of a string
     *
     * TODO: Review if we should use CRC32 or other algorithms
     */
     */
    private byte[] getCrc32(String str) {
    private int getCrc32(String str) {
        final CRC32 crc = new CRC32();
        final CRC32 crc = new CRC32();
        crc.update(str.getBytes());
        crc.update(str.getBytes());
        final long tmp = crc.getValue();
        return (int) crc.getValue();
        return new byte[]{(byte) (tmp >> 24 & 255), (byte) (tmp >> 16 & 255),
                (byte) (tmp >> 8 & 255), (byte) (tmp & 255)};
    }
    }


    /** Get SHA256 of a string */
    /** Get SHA256 of a string */
@@ -279,7 +274,7 @@ class WatchlistConfig {
        pw.println("Domain CRC32 digest list:");
        pw.println("Domain CRC32 digest list:");
        // mDomainDigests won't go from non-null to null so it's safe
        // mDomainDigests won't go from non-null to null so it's safe
        if (mDomainDigests != null) {
        if (mDomainDigests != null) {
            mDomainDigests.crc32Digests.dump(fd, pw, args);
            mDomainDigests.crc32s.dump(fd, pw, args);
        }
        }
        pw.println("Domain SHA256 digest list:");
        pw.println("Domain SHA256 digest list:");
        if (mDomainDigests != null) {
        if (mDomainDigests != null) {
@@ -288,7 +283,7 @@ class WatchlistConfig {
        pw.println("Ip CRC32 digest list:");
        pw.println("Ip CRC32 digest list:");
        // mIpDigests won't go from non-null to null so it's safe
        // mIpDigests won't go from non-null to null so it's safe
        if (mIpDigests != null) {
        if (mIpDigests != null) {
            mIpDigests.crc32Digests.dump(fd, pw, args);
            mIpDigests.crc32s.dump(fd, pw, args);
        }
        }
        pw.println("Ip SHA256 digest list:");
        pw.println("Ip SHA256 digest list:");
        if (mIpDigests != null) {
        if (mIpDigests != null) {
+60 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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 com.android.server.net.watchlist;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.internal.util.HexDump;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.Arrays;

/**
 * runtest frameworks-services -c com.android.server.net.watchlist.HarmfulCrcTests
 */
@RunWith(AndroidJUnit4.class)
@SmallTest
public class HarmfulCrcsTests {

    private static final byte[] TEST_DIGEST = HexDump.hexStringToByteArray("AABBCCDD");

    @Before
    public void setUp() throws Exception {
    }

    @After
    public void tearDown() throws Exception {
    }

    @Test
    public void testHarmfulCrcs_setAndContains() throws Exception {
        HarmfulCrcs harmfulCrcs = new HarmfulCrcs(
                Arrays.asList(new byte[][] {TEST_DIGEST}));
        assertTrue(harmfulCrcs.contains(0xaabbccdd));
        assertFalse(harmfulCrcs.contains(0xbbbbbbbb));
        assertFalse(harmfulCrcs.contains(0x01020304));
        assertFalse(harmfulCrcs.contains(0xddccbbaa));
    }
}