1 /*
2 * Copyright (C) 2016 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 "ChecksumCalculator.h"
18 
19 #include <string>
20 #include <vector>
21 #include <string.h>
22 
23 // Checklist when implementing new protocol:
24 // 1. update CHECKSUMHELPER_MAX_VERSION
25 // 2. update maxChecksumSize()
26 // 3. update checksumByteSize()
27 // 4. update addBuffer, writeChecksum, resetChecksum, validate
28 
29 // change CHECKSUMHELPER_MAX_VERSION when you want to update the protocol version
30 #define CHECKSUMHELPER_MAX_VERSION 1
31 
32 // checksum buffer size
33 // Please add a new checksum buffer size when implementing a new protocol,
34 // as well as modifying the maxChecksumSize function.
35 static const size_t kV1ChecksumSize = 8;
36 
maxChecksumSize()37 static constexpr size_t maxChecksumSize() {
38     return 0 > kV1ChecksumSize ? 0 : kV1ChecksumSize;
39 }
40 
41 static const size_t kMaxChecksumSize = maxChecksumSize();
42 
43 // utility macros to create checksum string at compilation time
44 #define CHECKSUMHELPER_VERSION_STR_PREFIX "ANDROID_EMU_CHECKSUM_HELPER_v"
45 #define CHECKSUMHELPER_MACRO_TO_STR(x) #x
46 #define CHECKSUMHELPER_MACRO_VAL_TO_STR(x) CHECKSUMHELPER_MACRO_TO_STR(x)
47 
48 static const uint32_t kMaxVersion = CHECKSUMHELPER_MAX_VERSION;
49 static const char* kMaxVersionStrPrefix = CHECKSUMHELPER_VERSION_STR_PREFIX;
50 static const char* kMaxVersionStr = CHECKSUMHELPER_VERSION_STR_PREFIX CHECKSUMHELPER_MACRO_VAL_TO_STR(CHECKSUMHELPER_MAX_VERSION);
51 
52 #undef CHECKSUMHELPER_MAX_VERSION
53 #undef CHECKSUMHELPER_VERSION_STR_PREFIX
54 #undef CHECKSUMHELPER_MACRO_TO_STR
55 #undef CHECKSUMHELPER_MACRO_VAL_TO_STR
56 
getMaxVersion()57 uint32_t ChecksumCalculator::getMaxVersion() {return kMaxVersion;}
getMaxVersionStr()58 const char* ChecksumCalculator::getMaxVersionStr() {return kMaxVersionStr;}
getMaxVersionStrPrefix()59 const char* ChecksumCalculator::getMaxVersionStrPrefix() {return kMaxVersionStrPrefix;}
60 
setVersion(uint32_t version)61 bool ChecksumCalculator::setVersion(uint32_t version) {
62     if (version > kMaxVersion) {  // unsupported version
63         LOG_CHECKSUMHELPER("%s: ChecksumCalculator Set Unsupported version Version %d\n",
64                 __FUNCTION__, m_version);
65         return false;
66     }
67     if (m_isEncodingChecksum) { // setVersion is called in the middle of encoding checksums
68         LOG_CHECKSUMHELPER("%s: called between addBuffer and writeChecksum\n",
69                 __FUNCTION__);
70         return false;
71     }
72     m_version = version;
73     LOG_CHECKSUMHELPER("%s: ChecksumCalculator Set Version %d\n", __FUNCTION__,
74                 m_version);
75     return true;
76 }
77 
checksumByteSize() const78 size_t ChecksumCalculator::checksumByteSize() const {
79     switch (m_version) {
80         case 0:
81             return 0;
82         case 1:
83             return sizeof(uint32_t) + sizeof(m_numWrite);
84         default:
85             return 0;
86     }
87 }
88 
addBuffer(const void * buf,size_t packetLen)89 void ChecksumCalculator::addBuffer(const void* buf, size_t packetLen) {
90     m_isEncodingChecksum = true;
91     switch (m_version) {
92         case 1:
93             m_v1BufferTotalLength += packetLen;
94             break;
95     }
96 }
97 
writeChecksum(void * outputChecksum,size_t outputChecksumLen)98 bool ChecksumCalculator::writeChecksum(void* outputChecksum, size_t outputChecksumLen) {
99     if (outputChecksumLen < checksumByteSize()) return false;
100     char *checksumPtr = (char *)outputChecksum;
101     switch (m_version) {
102         case 1: { // protocol v1 is to reverse the packetLen and write it at the end
103             uint32_t val = computeV1Checksum();
104             memcpy(checksumPtr, &val, sizeof(val));
105             memcpy(checksumPtr+sizeof(val), &m_numWrite, sizeof(m_numWrite));
106             break;
107         }
108     }
109     resetChecksum();
110     m_numWrite++;
111     return true;
112 }
113 
resetChecksum()114 void ChecksumCalculator::resetChecksum() {
115     switch (m_version) {
116         case 1:
117             m_v1BufferTotalLength = 0;
118             break;
119     }
120     m_isEncodingChecksum = false;
121 }
122 
validate(const void * expectedChecksum,size_t expectedChecksumLen)123 bool ChecksumCalculator::validate(const void* expectedChecksum, size_t expectedChecksumLen) {
124     size_t checksumSize = checksumByteSize();
125     if (expectedChecksumLen != checksumSize) {
126         m_numRead++;
127         resetChecksum();
128         return false;
129     }
130     // buffers for computing the checksum
131     unsigned char sChecksumBuffer[kMaxChecksumSize];
132     switch (m_version) {
133         case 1: {
134             uint32_t val = computeV1Checksum();
135             memcpy(sChecksumBuffer, &val, sizeof(val));
136             memcpy(sChecksumBuffer+sizeof(val), &m_numRead, sizeof(m_numRead));
137             break;
138         }
139     }
140     bool isValid = !memcmp(sChecksumBuffer, expectedChecksum, checksumSize);
141     m_numRead++;
142     resetChecksum();
143     return isValid;
144 }
145 
computeV1Checksum()146 uint32_t ChecksumCalculator::computeV1Checksum() {
147     uint32_t revLen = m_v1BufferTotalLength;
148     revLen = (revLen & 0xffff0000) >> 16 | (revLen & 0x0000ffff) << 16;
149     revLen = (revLen & 0xff00ff00) >> 8 | (revLen & 0x00ff00ff) << 8;
150     revLen = (revLen & 0xf0f0f0f0) >> 4 | (revLen & 0x0f0f0f0f) << 4;
151     revLen = (revLen & 0xcccccccc) >> 2 | (revLen & 0x33333333) << 2;
152     revLen = (revLen & 0xaaaaaaaa) >> 1 | (revLen & 0x55555555) << 1;
153     return revLen;
154 }
155