blob: 5853d2def5b2ac3fde155fdebc4606e10f59f60b [file] [log] [blame]
Tianjie Xua0a12cf2019-12-05 21:50:22 -08001/*
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 <HadamardUtils.h>
18
19#include <android-base/logging.h>
20
21namespace aidl {
22namespace android {
23namespace hardware {
24namespace rebootescrow {
25namespace hadamard {
26
27constexpr auto BYTE_LENGTH = 8u;
28
29std::vector<uint8_t> BitsetToBytes(const std::bitset<ENCODE_LENGTH>& encoded_bits) {
30 CHECK_EQ(0, (encoded_bits.size() % BYTE_LENGTH));
31 std::vector<uint8_t> result;
32 for (size_t i = 0; i < encoded_bits.size(); i += 8) {
33 uint8_t current = 0;
34 // Set each byte starting from the LSB.
35 for (size_t j = 0; j < BYTE_LENGTH; j++) {
36 CHECK_LE(i + j, encoded_bits.size());
37 if (encoded_bits[i + j]) {
38 current |= (1u << j);
39 }
40 }
41 result.push_back(current);
42 }
43 return result;
44}
45
46std::bitset<ENCODE_LENGTH> BytesToBitset(const std::vector<uint8_t>& encoded) {
47 CHECK_EQ(ENCODE_LENGTH, encoded.size() * BYTE_LENGTH);
48
49 std::bitset<ENCODE_LENGTH> result;
50 size_t offset = 0;
51 for (const auto& byte : encoded) {
52 // Set each byte starting from the LSB.
53 for (size_t j = 0; j < BYTE_LENGTH; j++) {
54 result[offset + j] = byte & (1u << j);
55 }
56 offset += BYTE_LENGTH;
57 }
58 return result;
59}
60
61// The encoding is equivalent to multiply the word with the generator matrix (and take the module
62// of 2). Here is an example of encoding a number with 3 bits. The encoded length is thus
63// 2^(3-1) = 4 bits.
64// |1 1 1 1| |0|
65// |0 1 1| * |0 0 1 1| = |1|
66// |0 1 0 1| |1|
67// |0|
68std::bitset<ENCODE_LENGTH> EncodeWord(uint16_t word) {
69 std::bitset<ENCODE_LENGTH> result;
70 for (uint64_t i = ENCODE_LENGTH; i < 2 * ENCODE_LENGTH; i++) {
71 uint32_t wi = word & i;
72 // Sum all the bits in the word and check its parity.
73 wi ^= wi >> 8u;
74 wi ^= wi >> 4u;
75 wi ^= wi >> 2u;
76 wi ^= wi >> 1u;
77 result[i - ENCODE_LENGTH] = wi & 1u;
78 }
79 return result;
80}
81
82std::vector<uint8_t> EncodeKey(const std::vector<uint8_t>& key) {
83 CHECK_EQ(KEY_SIZE_IN_BYTES, key.size());
84
85 std::vector<uint8_t> result;
86 for (size_t i = 0; i < key.size(); i += 2) {
87 uint16_t word = static_cast<uint16_t>(key[i + 1]) << BYTE_LENGTH | key[i];
88 auto encoded_bits = EncodeWord(word);
89 auto byte_array = BitsetToBytes(encoded_bits);
90 std::move(byte_array.begin(), byte_array.end(), std::back_inserter(result));
91 }
92 return result;
93}
94
95std::vector<uint8_t> DecodeKey(const std::vector<uint8_t>& encoded) {
96 CHECK_EQ(0, (encoded.size() * 8) % ENCODE_LENGTH);
97 std::vector<uint8_t> result;
98 for (size_t i = 0; i < encoded.size(); i += ENCODE_LENGTH / 8) {
99 auto current =
100 std::vector<uint8_t>{encoded.begin() + i, encoded.begin() + i + ENCODE_LENGTH / 8};
101 auto bits = BytesToBitset(current);
102 auto candidates = DecodeWord(bits);
103 CHECK(!candidates.empty());
104 // TODO(xunchang) Do we want to try other candidates?
105 uint16_t val = candidates.top().second;
106 result.push_back(val & 0xffu);
107 result.push_back(val >> BYTE_LENGTH);
108 }
109
110 return result;
111}
112
113std::priority_queue<std::pair<int32_t, uint16_t>> DecodeWord(
114 const std::bitset<ENCODE_LENGTH>& encoded) {
115 std::vector<int32_t> scores;
116 scores.reserve(ENCODE_LENGTH);
117 // Convert 0 -> -1 in the encoded bits. e.g [0, 1, 1, 0] -> [-1, 1, 1, -1]
118 for (uint32_t i = 0; i < ENCODE_LENGTH; i++) {
119 scores.push_back(2 * encoded[i] - 1);
120 }
121
122 // Multiply the hadamard matrix by the transformed input.
123 // |1 1 1 1| |-1| | 0|
124 // |1 -1 1 -1| * | 1| = | 0|
125 // |1 1 -1 -1| | 1| | 0|
126 // |1 -1 -1 1| |-1| |-4|
127 for (uint32_t i = 0; i < CODE_K; i++) {
128 uint16_t step = 1u << i;
129 for (uint32_t j = 0; j < ENCODE_LENGTH; j += 2 * step) {
130 for (uint32_t k = j; k < j + step; k++) {
131 auto a0 = scores[k];
132 auto a1 = scores[k + step];
133 scores[k] = a0 + a1;
134 scores[k + step] = a0 - a1;
135 }
136 }
137 }
138
139 // Assign the corresponding score to each index; larger score indicates higher probability. e.g.
140 // value 3, encoding [0, 1, 1, 0] -> score: 4
141 // value 7, encoding [1, 0, 0, 1] (3's complement) -> score: -4
142 std::priority_queue<std::pair<int32_t, uint16_t>> candidates;
143 // TODO(xunchang) limit the candidate size since we don't need all of them?
144 for (uint32_t i = 0; i < scores.size(); i++) {
145 candidates.emplace(-scores[i], i);
146 candidates.emplace(scores[i], (1u << CODE_K) | i);
147 }
148
149 CHECK_EQ(2 * ENCODE_LENGTH, candidates.size());
150 return candidates;
151}
152
153} // namespace hadamard
154} // namespace rebootescrow
155} // namespace hardware
156} // namespace android
157} // namespace aidl