Tianjie Xu | a0a12cf | 2019-12-05 21:50:22 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 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 | #include <stdint.h> |
| 18 | #include <random> |
| 19 | |
| 20 | #include <bitset> |
| 21 | #include <utility> |
| 22 | #include <vector> |
| 23 | |
| 24 | #include <gtest/gtest.h> |
| 25 | |
| 26 | #include <HadamardUtils.h> |
| 27 | |
| 28 | using namespace aidl::android::hardware::rebootescrow::hadamard; |
| 29 | |
| 30 | class HadamardTest : public testing::Test { |
| 31 | protected: |
| 32 | void SetUp() override { |
| 33 | auto ones = std::bitset<ENCODE_LENGTH>{}.set(); |
| 34 | // Expects 0x4000 to encode as top half as ones, and lower half as zeros. i.e. |
| 35 | // [1, 1 .. 1, 0, 0 .. 0] |
| 36 | expected_half_size_ = ones << half_size_; |
| 37 | |
| 38 | // Expects 0x1 to encode as interleaved 1 and 0s i.e. [1, 0, 1, 0 ..] |
| 39 | expected_one_ = ones; |
| 40 | for (uint32_t i = ENCODE_LENGTH / 2; i >= 1; i /= 2) { |
| 41 | expected_one_ ^= (expected_one_ >> i); |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | uint16_t half_size_ = ENCODE_LENGTH / 2; |
| 46 | std::bitset<ENCODE_LENGTH> expected_one_; |
| 47 | std::bitset<ENCODE_LENGTH> expected_half_size_; |
| 48 | }; |
| 49 | |
| 50 | static void AddError(std::bitset<ENCODE_LENGTH>* corrupted_bits) { |
| 51 | // The hadamard code has a hamming distance of ENCODE_LENGTH/2. So we should always be able to |
| 52 | // correct the data if less than a quarter of the encoded bits are corrupted. |
| 53 | auto corrupted_max = 0.24f * corrupted_bits->size(); |
| 54 | auto corrupted_num = 0; |
| 55 | for (size_t i = 0; i < corrupted_bits->size() && corrupted_num < corrupted_max; i++) { |
| 56 | if (random() % 2 == 0) { |
| 57 | (*corrupted_bits)[i] = !(*corrupted_bits)[i]; |
| 58 | corrupted_num += 1; |
| 59 | } |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | static void EncodeAndDecodeKeys(const std::vector<uint8_t>& key) { |
| 64 | auto encoded = EncodeKey(key); |
| 65 | ASSERT_EQ(64 * 1024, encoded.size()); |
| 66 | auto decoded = DecodeKey(encoded); |
| 67 | ASSERT_EQ(key, std::vector<uint8_t>(decoded.begin(), decoded.begin() + key.size())); |
| 68 | } |
| 69 | |
| 70 | TEST_F(HadamardTest, Encode_smoke) { |
| 71 | ASSERT_EQ(expected_half_size_, EncodeWord(half_size_)); |
| 72 | ASSERT_EQ(expected_one_, EncodeWord(1)); |
| 73 | // Check the complement of 1. |
| 74 | ASSERT_EQ(~expected_one_, EncodeWord(1u << CODE_K | 1u)); |
| 75 | } |
| 76 | |
| 77 | TEST_F(HadamardTest, Decode_smoke) { |
| 78 | auto candidate = DecodeWord(expected_half_size_); |
| 79 | auto expected = std::pair<int32_t, uint16_t>{ENCODE_LENGTH, half_size_}; |
| 80 | ASSERT_EQ(expected, candidate.top()); |
| 81 | |
| 82 | candidate = DecodeWord(expected_one_); |
| 83 | expected = std::pair<int32_t, uint16_t>{ENCODE_LENGTH, 1}; |
| 84 | ASSERT_EQ(expected, candidate.top()); |
| 85 | } |
| 86 | |
| 87 | TEST_F(HadamardTest, Decode_error_correction) { |
| 88 | constexpr auto iteration = 10; |
| 89 | for (int i = 0; i < iteration; i++) { |
| 90 | uint16_t word = random() % (ENCODE_LENGTH * 2); |
| 91 | auto corrupted_bits = EncodeWord(word); |
| 92 | AddError(&corrupted_bits); |
| 93 | |
| 94 | auto candidate = DecodeWord(corrupted_bits); |
| 95 | ASSERT_EQ(word, candidate.top().second); |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | TEST_F(HadamardTest, BytesToBitset_smoke) { |
| 100 | auto bytes = BitsetToBytes(expected_one_); |
| 101 | |
| 102 | auto read_back = BytesToBitset(bytes); |
| 103 | ASSERT_EQ(expected_one_, read_back); |
| 104 | } |
| 105 | |
| 106 | TEST_F(HadamardTest, EncodeAndDecodeKey) { |
| 107 | std::vector<uint8_t> KEY_1{ |
| 108 | 0xA5, 0x00, 0xFF, 0x01, 0xA5, 0x5a, 0xAA, 0x55, 0x00, 0xD3, 0x2A, |
| 109 | 0x8C, 0x2E, 0x83, 0x0E, 0x65, 0x9E, 0x8D, 0xC6, 0xAC, 0x1E, 0x83, |
| 110 | 0x21, 0xB3, 0x95, 0x02, 0x89, 0x64, 0x64, 0x92, 0x12, 0x1F, |
| 111 | }; |
| 112 | std::vector<uint8_t> KEY_2{ |
| 113 | 0xFF, 0x00, 0x00, 0xAA, 0x5A, 0x19, 0x20, 0x71, 0x9F, 0xFB, 0xDA, |
| 114 | 0xB6, 0x2D, 0x06, 0xD5, 0x49, 0x7E, 0xEF, 0x63, 0xAC, 0x18, 0xFF, |
| 115 | 0x5A, 0xA3, 0x40, 0xBB, 0x64, 0xFA, 0x67, 0xC1, 0x10, 0x18, |
| 116 | }; |
| 117 | |
| 118 | EncodeAndDecodeKeys(KEY_1); |
| 119 | EncodeAndDecodeKeys(KEY_2); |
| 120 | |
| 121 | std::vector<uint8_t> key; |
| 122 | for (uint8_t i = 0; i < KEY_SIZE_IN_BYTES; i++) { |
| 123 | key.push_back(i); |
| 124 | }; |
| 125 | EncodeAndDecodeKeys(key); |
| 126 | } |