forked from htumanyan/navien
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathchecksum.cpp
More file actions
188 lines (131 loc) · 6.39 KB
/
Copy pathchecksum.cpp
File metadata and controls
188 lines (131 loc) · 6.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/**
* checksum.cpp
* Hovhannes Tumanyan (htumanyan@gmail.com)
* Aug, 2024
*
* Implementation of the algorithm and test suite for CRC-like, but quite unusual checksum algorithm,
* that is used by Navien water heaters for communication with external devices (Navien WiFi lite and alike).
* I was unable to find an industry accepted compatible CRC implementation.
* The algorithm was reverse engineered and validated with traces captured over RS485 communication lines.
*/
#include <stdio.h>
typedef unsigned int uint;
typedef unsigned short ushort;
typedef unsigned long ulong;
typedef unsigned char byte;
// This is the seed value that was observed
// in all collected traces
const byte CHECKSUM_SEED_4B = 0x4b;
// This value was found in the binary
// but not in traces.
const byte CHECKSUM_SEED_62 = 0x62;
/**
* This structure defines test vectors (input) and expected results.
* Notice that on the wire the checksum is transmitted as the very last byte
* after the data vector, while here for simplicity we take off the last byte
* and store in the "result".
*/
typedef struct{
const unsigned char * vector; // test input - header + data
const int len; // count of bytes in vector
const unsigned char result; // expected checksum result
const byte seed; // seed value for checksum
} TEST_VEC;
// This is an example of short format packet that is typically 41 bytes long (7 bytes header + 34 bytes data)
// the actual length of this test vector is 40 bytes as the last byte is cut and placed in the "result" field.
const unsigned char TEST_VEC_1[] = {0xF7, 0x05, 0x50, 0x50, 0x90, 0x22, 0x42, 0x00, 0x00, 0x25, 0x14, 0x56, 0x49, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xC2, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x21, 0x03, 0x99, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// Same as above but for the longer packet that is usually 49 bytes long.
const unsigned char TEST_VEC_2[] = {
0xF7, 0x05, 0x50, 0x0F, 0x90, 0x2A, 0x45, 0x00, 0x01, 0x01, 0x14, 0x03, 0x1F, 0x00, 0x56, 0x56, 0x48, 0x00, 0x00, 0x00, 0x14, 0x01, 0x74, 0x13, 0x0B, 0x44, 0x00, 0x00, 0x9D, 0x07, 0x60, 0x20, 0x4B, 0x3B, 0x20, 0x00, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x49, 0x00, 0x00, 0x01, 0x00
};
// Same as above
const unsigned char TEST_VEC_3[] = {
0xF7, 0x05, 0x50, 0x0F, 0x90, 0x2A, 0x45, 0x00, 0x01, 0x01, 0x14, 0x03, 0x1F, 0x00, 0x56, 0x49, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0B, 0x44, 0x00, 0x00, 0x9D, 0x07, 0x60, 0x20, 0x4B, 0x3B, 0x20, 0x00, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x49, 0x00, 0x00, 0x01, 0x00
};
// TEST_VEC_240A2_SRC_51_1: 240 packet, checksum 0xA2, seed 0x62
const unsigned char TEST_VEC_240A2_SRC_51_1[] = {
0xF7, 0x02, 0x51, 0x50, 0x90, 0x22, 0x42, 0x20, 0x00, 0x25, 0x14, 0x5C, 0x57, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xBE, 0x00, 0x20, 0x02, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// TEST_VEC_240BE_SRC_51_2: 240 packet, checksum 0x2E, seed 0x62
const unsigned char TEST_VEC_240BE_SRC_51_2[] = {
0xF7, 0x02, 0x51, 0x50, 0x90, 0x22, 0x42, 0x20, 0x00, 0x25, 0x49, 0x5C, 0x5B, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xBE, 0x00, 0x20, 0x02, 0x0C, 0x01, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// TEST_VEC_240BE_SRC_51: 240 packet, checksum 0xBE, seed 0x62
const unsigned char TEST_VEC_240BE_SRC_51[] = {
0xF7, 0x02, 0x50, 0x51, 0x10, 0x22, 0x41, 0x00, 0x00, 0x01, 0x00, 0x5C, 0x56, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x20, 0x02, 0x0C, 0x43, 0x20, 0x6B, 0x0A, 0x0E, 0x00, 0x06, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00, 0xBE
};
// TEST_VEC_240A2_SRC_51_3: 240 packet, checksum 0x98, seed 0x62
const unsigned char TEST_VEC_240A2_SRC_51_3[] = {
0xF7, 0x02, 0x51, 0x0F, 0x90, 0x2A, 0x45, 0x00, 0x0C, 0x02, 0x0C, 0x07, 0x1B, 0x00, 0x5C, 0x5B, 0x44, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x16, 0x00, 0x7D, 0x2F, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0xAA, 0x48, 0x00, 0x00, 0x00, 0x00
};
const TEST_VEC TEST_VECTORS[] = {
{TEST_VEC_1, sizeof(TEST_VEC_1), 0x65, CHECKSUM_SEED_4B},
{TEST_VEC_2, sizeof(TEST_VEC_2), 0x36, CHECKSUM_SEED_4B},
{TEST_VEC_3, sizeof(TEST_VEC_3), 0xe5, CHECKSUM_SEED_4B},
/**
* These are test vectors collected from the slave unit in cascade setup.
* The source address is 0x51 indicating they are sent by the slave unit.
*/
{TEST_VEC_240A2_SRC_51_1, sizeof(TEST_VEC_240A2_SRC_51_1), 0xA2, CHECKSUM_SEED_62},
{TEST_VEC_240BE_SRC_51_2, sizeof(TEST_VEC_240BE_SRC_51_2), 0x2E, CHECKSUM_SEED_62},
{TEST_VEC_240A2_SRC_51_3, sizeof(TEST_VEC_240A2_SRC_51_3), 0x98, CHECKSUM_SEED_62},
{TEST_VEC_240BE_SRC_51, sizeof(TEST_VEC_240BE_SRC_51), 0xBE, CHECKSUM_SEED_62} // TODO: verify expected checksum and seed
};
byte likely_crc_calc(const byte * buffer,uint len,ushort seed){
ushort result;
if (len < 2) {
result = 0x00;
}
else {
result = 0xff;
for (uint i = 0; i < len; i++){
result = result << 1;
if (result > 0xff){
result = (result & 0xff) ^ seed;
}
// this is important!!
// the checksum is calculated
// based on the lower byte, i.e.
// only the lower byte is XOR-ed
result = ((byte)result) ^ (ushort)buffer[i];
}
}
return result;
}
int main(int argc, char * argv[]){
printf ("Starting\n");
for (int i = 0; i < sizeof(TEST_VECTORS) / sizeof(TEST_VEC); i++){
TEST_VEC v = TEST_VECTORS[i];
printf ("Checking test vector seed=0x%02x (%d bytes): expected result 0x%02hhx, actual: 0x%02hhx\n",
v.seed,
v.len,
v.result,
likely_crc_calc(v.vector, v.len, v.seed)
);
}
}
/*
int __fastcall likely_crc_calc(unsigned __int8 *a1, unsigned int len, unsigned __int16 seed_val){
if ( (int)len < 2 )
{
*(_WORD *)&byte_200117C4[576] = 0;
}
else
{
*(_WORD *)&byte_200117C4[576] = 0xFF;
for ( *(_DWORD *)&byte_200117C4[580] = a1;
*(_DWORD *)&byte_200117C4[580] < (unsigned int)&a1[(unsigned __int16)len - 1];
++*(_DWORD *)&byte_200117C4[580] )
{
*(_WORD *)&byte_200117C4[576] *= 2;
if ( *(unsigned __int16 *)&byte_200117C4[576] > 0xFFu )
*(_WORD *)&byte_200117C4[576] = byte_200117C4[576] ^ seed_val;
*(_DWORD *)&byte_200117C4[584] = *(_DWORD *)&byte_200117C4[580];
*(_WORD *)&byte_200117C4[576] ^= (unsigned __int8)**(_BYTE **)&byte_200117C4[580];
}
}
return byte_200117C4[576];
*/