Loading services/backup/java/com/android/server/backup/encryption/chunk/ChunkHash.java 0 → 100644 +89 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.backup.encryption.chunk; import com.android.internal.util.Preconditions; import java.util.Arrays; import java.util.Base64; /** * Represents the SHA-256 hash of the plaintext of a chunk, which is frequently used as a key. * * <p>This class is {@link Comparable} and implements {@link #equals(Object)} and {@link * #hashCode()}. */ public class ChunkHash implements Comparable<ChunkHash> { /** The length of the hash in bytes. The hash is a SHA-256, so this is 256 bits. */ public static final int HASH_LENGTH_BYTES = 256 / 8; private static final int UNSIGNED_MASK = 0xFF; private final byte[] mHash; /** Constructs a new instance which wraps the given SHA-256 hash bytes. */ public ChunkHash(byte[] hash) { Preconditions.checkArgument(hash.length == HASH_LENGTH_BYTES, "Hash must have 256 bits"); mHash = hash; } public byte[] getHash() { return mHash; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof ChunkHash)) { return false; } ChunkHash chunkHash = (ChunkHash) o; return Arrays.equals(mHash, chunkHash.mHash); } @Override public int hashCode() { return Arrays.hashCode(mHash); } @Override public int compareTo(ChunkHash other) { return lexicographicalCompareUnsignedBytes(getHash(), other.getHash()); } @Override public String toString() { return Base64.getEncoder().encodeToString(mHash); } private static int lexicographicalCompareUnsignedBytes(byte[] left, byte[] right) { int minLength = Math.min(left.length, right.length); for (int i = 0; i < minLength; i++) { int result = toInt(left[i]) - toInt(right[i]); if (result != 0) { return result; } } return left.length - right.length; } private static int toInt(byte value) { return value & UNSIGNED_MASK; } } services/robotests/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ LOCAL_AIDL_INCLUDES := \ LOCAL_STATIC_JAVA_LIBRARIES := \ platform-robolectric-android-all-stubs \ android-support-test \ guava \ mockito-robolectric-prebuilt \ platform-test-annotations \ truth-prebuilt \ Loading services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java 0 → 100644 +101 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.backup.encryption.chunk; import static com.google.common.truth.Truth.assertThat; import android.platform.test.annotations.Presubmit; import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.SystemLoaderPackages; import com.google.common.primitives.Bytes; import java.util.Arrays; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.annotation.Config; @RunWith(FrameworkRobolectricTestRunner.class) @Config(manifest = Config.NONE, sdk = 26) @SystemLoaderPackages({"com.android.server.backup"}) @Presubmit public class ChunkHashTest { private static final int HASH_LENGTH_BYTES = 256 / 8; private static final byte[] TEST_HASH_1 = Arrays.copyOf(new byte[] {1}, HASH_LENGTH_BYTES); private static final byte[] TEST_HASH_2 = Arrays.copyOf(new byte[] {2}, HASH_LENGTH_BYTES); @Test public void testGetHash_returnsHash() { ChunkHash chunkHash = new ChunkHash(TEST_HASH_1); byte[] hash = chunkHash.getHash(); assertThat(hash).asList().containsExactlyElementsIn(Bytes.asList(TEST_HASH_1)).inOrder(); } @Test public void testEquals() { ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1); ChunkHash equalChunkHash1 = new ChunkHash(TEST_HASH_1); ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2); assertThat(chunkHash1).isEqualTo(equalChunkHash1); assertThat(chunkHash1).isNotEqualTo(chunkHash2); } @Test public void testHashCode() { ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1); ChunkHash equalChunkHash1 = new ChunkHash(TEST_HASH_1); ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2); int hash1 = chunkHash1.hashCode(); int equalHash1 = equalChunkHash1.hashCode(); int hash2 = chunkHash2.hashCode(); assertThat(hash1).isEqualTo(equalHash1); assertThat(hash1).isNotEqualTo(hash2); } @Test public void testCompareTo_whenEqual_returnsZero() { ChunkHash chunkHash = new ChunkHash(TEST_HASH_1); ChunkHash equalChunkHash = new ChunkHash(TEST_HASH_1); int result = chunkHash.compareTo(equalChunkHash); assertThat(result).isEqualTo(0); } @Test public void testCompareTo_whenArgumentGreater_returnsNegative() { ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1); ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2); int result = chunkHash1.compareTo(chunkHash2); assertThat(result).isLessThan(0); } @Test public void testCompareTo_whenArgumentSmaller_returnsPositive() { ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1); ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2); int result = chunkHash2.compareTo(chunkHash1); assertThat(result).isGreaterThan(0); } } Loading
services/backup/java/com/android/server/backup/encryption/chunk/ChunkHash.java 0 → 100644 +89 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.backup.encryption.chunk; import com.android.internal.util.Preconditions; import java.util.Arrays; import java.util.Base64; /** * Represents the SHA-256 hash of the plaintext of a chunk, which is frequently used as a key. * * <p>This class is {@link Comparable} and implements {@link #equals(Object)} and {@link * #hashCode()}. */ public class ChunkHash implements Comparable<ChunkHash> { /** The length of the hash in bytes. The hash is a SHA-256, so this is 256 bits. */ public static final int HASH_LENGTH_BYTES = 256 / 8; private static final int UNSIGNED_MASK = 0xFF; private final byte[] mHash; /** Constructs a new instance which wraps the given SHA-256 hash bytes. */ public ChunkHash(byte[] hash) { Preconditions.checkArgument(hash.length == HASH_LENGTH_BYTES, "Hash must have 256 bits"); mHash = hash; } public byte[] getHash() { return mHash; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof ChunkHash)) { return false; } ChunkHash chunkHash = (ChunkHash) o; return Arrays.equals(mHash, chunkHash.mHash); } @Override public int hashCode() { return Arrays.hashCode(mHash); } @Override public int compareTo(ChunkHash other) { return lexicographicalCompareUnsignedBytes(getHash(), other.getHash()); } @Override public String toString() { return Base64.getEncoder().encodeToString(mHash); } private static int lexicographicalCompareUnsignedBytes(byte[] left, byte[] right) { int minLength = Math.min(left.length, right.length); for (int i = 0; i < minLength; i++) { int result = toInt(left[i]) - toInt(right[i]); if (result != 0) { return result; } } return left.length - right.length; } private static int toInt(byte value) { return value & UNSIGNED_MASK; } }
services/robotests/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ LOCAL_AIDL_INCLUDES := \ LOCAL_STATIC_JAVA_LIBRARIES := \ platform-robolectric-android-all-stubs \ android-support-test \ guava \ mockito-robolectric-prebuilt \ platform-test-annotations \ truth-prebuilt \ Loading
services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java 0 → 100644 +101 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.backup.encryption.chunk; import static com.google.common.truth.Truth.assertThat; import android.platform.test.annotations.Presubmit; import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.SystemLoaderPackages; import com.google.common.primitives.Bytes; import java.util.Arrays; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.annotation.Config; @RunWith(FrameworkRobolectricTestRunner.class) @Config(manifest = Config.NONE, sdk = 26) @SystemLoaderPackages({"com.android.server.backup"}) @Presubmit public class ChunkHashTest { private static final int HASH_LENGTH_BYTES = 256 / 8; private static final byte[] TEST_HASH_1 = Arrays.copyOf(new byte[] {1}, HASH_LENGTH_BYTES); private static final byte[] TEST_HASH_2 = Arrays.copyOf(new byte[] {2}, HASH_LENGTH_BYTES); @Test public void testGetHash_returnsHash() { ChunkHash chunkHash = new ChunkHash(TEST_HASH_1); byte[] hash = chunkHash.getHash(); assertThat(hash).asList().containsExactlyElementsIn(Bytes.asList(TEST_HASH_1)).inOrder(); } @Test public void testEquals() { ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1); ChunkHash equalChunkHash1 = new ChunkHash(TEST_HASH_1); ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2); assertThat(chunkHash1).isEqualTo(equalChunkHash1); assertThat(chunkHash1).isNotEqualTo(chunkHash2); } @Test public void testHashCode() { ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1); ChunkHash equalChunkHash1 = new ChunkHash(TEST_HASH_1); ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2); int hash1 = chunkHash1.hashCode(); int equalHash1 = equalChunkHash1.hashCode(); int hash2 = chunkHash2.hashCode(); assertThat(hash1).isEqualTo(equalHash1); assertThat(hash1).isNotEqualTo(hash2); } @Test public void testCompareTo_whenEqual_returnsZero() { ChunkHash chunkHash = new ChunkHash(TEST_HASH_1); ChunkHash equalChunkHash = new ChunkHash(TEST_HASH_1); int result = chunkHash.compareTo(equalChunkHash); assertThat(result).isEqualTo(0); } @Test public void testCompareTo_whenArgumentGreater_returnsNegative() { ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1); ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2); int result = chunkHash1.compareTo(chunkHash2); assertThat(result).isLessThan(0); } @Test public void testCompareTo_whenArgumentSmaller_returnsPositive() { ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1); ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2); int result = chunkHash2.compareTo(chunkHash1); assertThat(result).isGreaterThan(0); } }