|
1 | 1 | #include "apdu_handler.h" |
2 | 2 | #include "log.h" |
| 3 | +#include "utils.h" |
3 | 4 | #include <cstring> |
4 | 5 |
|
5 | 6 | namespace dlms_parser { |
@@ -89,18 +90,9 @@ bool ApduHandler::parse_ciphered_apdu_(const uint8_t* buf, size_t len, uint8_t / |
89 | 90 | std::memcpy(iv, buf + pos, DLMS_SYSTITLE_LENGTH); |
90 | 91 | pos += DLMS_SYSTITLE_LENGTH; |
91 | 92 |
|
92 | | - // BER length field: single byte if <= 127, otherwise 0x8N + N bytes |
93 | | - if (pos >= len) return false; |
94 | | - const uint8_t len_byte = buf[pos++]; |
95 | | - uint32_t cipher_len = len_byte; |
96 | | - if (len_byte > DLMS_LENGTH_SINGLE_BYTE_MAX) { |
97 | | - const uint8_t num_bytes = len_byte & 0x7F; |
98 | | - cipher_len = 0; |
99 | | - for (uint8_t i = 0; i < num_bytes; i++) { |
100 | | - if (pos >= len) return false; |
101 | | - cipher_len = (cipher_len << 8) | buf[pos++]; |
102 | | - } |
103 | | - } |
| 93 | + // BER length |
| 94 | + const uint32_t cipher_len = utils::read_ber_length(buf, pos, len); |
| 95 | + if (cipher_len == 0) return false; |
104 | 96 |
|
105 | 97 | // Security control byte (1 byte, informational — skip) |
106 | 98 | if (pos >= len) return false; |
@@ -142,26 +134,27 @@ bool ApduHandler::parse_general_block_transfer_(const uint8_t* buf, size_t len, |
142 | 134 | size_t pos = 0; |
143 | 135 |
|
144 | 136 | while (pos < len && buf[pos] == DLMS_APDU_GENERAL_BLOCK_TRANSFER) { |
145 | | - if (pos + 7 > len) { |
| 137 | + if (pos + 6 > len) { |
146 | 138 | Logger::log(LogLevel::WARNING, "GBT: truncated block header at offset %zu", pos); |
147 | 139 | return false; |
148 | 140 | } |
149 | 141 | const uint8_t ctrl = buf[pos + 1]; |
150 | 142 | const bool is_last = (ctrl & 0x80U) != 0; |
151 | 143 | const uint16_t block_num = static_cast<uint16_t>(buf[pos + 2] << 8 | buf[pos + 3]); |
152 | 144 | // pos+4..pos+5: block_num_ack (skip) |
153 | | - const uint8_t block_len = buf[pos + 6]; |
| 145 | + size_t ber_pos = pos + 6; |
| 146 | + const uint32_t block_len = utils::read_ber_length(buf, ber_pos, len); |
154 | 147 |
|
155 | | - if (pos + 7 + block_len > len) { |
| 148 | + if (ber_pos + block_len > len) { |
156 | 149 | Logger::log(LogLevel::WARNING, "GBT: block %u truncated (need %u, have %zu)", |
157 | | - block_num, block_len, len - pos - 7); |
| 150 | + block_num, block_len, len - ber_pos); |
158 | 151 | return false; |
159 | 152 | } |
160 | 153 |
|
161 | 154 | Logger::log(LogLevel::DEBUG, "GBT block %u: %u bytes%s", block_num, block_len, |
162 | 155 | is_last ? " (last)" : ""); |
163 | | - reassembled.insert(reassembled.end(), buf + pos + 7, buf + pos + 7 + block_len); |
164 | | - pos += 7 + block_len; |
| 156 | + reassembled.insert(reassembled.end(), buf + ber_pos, buf + ber_pos + block_len); |
| 157 | + pos = ber_pos + block_len; |
165 | 158 |
|
166 | 159 | if (is_last) break; |
167 | 160 | } |
@@ -239,28 +232,31 @@ ApduHandler::UnwrapResult ApduHandler::unwrap_in_place(uint8_t* buf, size_t len) |
239 | 232 | } |
240 | 233 |
|
241 | 234 | // --- General-Block-Transfer (0xE0): reassemble blocks in-place |
| 235 | + // Block format: E0 [ctrl:1] [block_num:2] [block_num_ack:2] [BER_len] [data...] |
242 | 236 | if (tag == DLMS_APDU_GENERAL_BLOCK_TRANSFER) { |
243 | 237 | Logger::log(LogLevel::DEBUG, "Found General-Block-Transfer (0xE0)"); |
244 | 238 | size_t read_pos = 0; |
245 | 239 | size_t write_pos = 0; |
246 | 240 |
|
247 | 241 | while (read_pos < len && buf[read_pos] == DLMS_APDU_GENERAL_BLOCK_TRANSFER) { |
248 | | - if (read_pos + 7 > len) { |
| 242 | + if (read_pos + 6 > len) { |
249 | 243 | Logger::log(LogLevel::WARNING, "GBT: truncated block header"); |
250 | 244 | return {0, 0}; |
251 | 245 | } |
252 | 246 | const uint8_t ctrl = buf[read_pos + 1]; |
253 | 247 | const bool is_last = (ctrl & 0x80U) != 0; |
254 | | - const uint8_t block_len = buf[read_pos + 6]; |
| 248 | + // BER length starts at offset 6 within the block |
| 249 | + size_t ber_pos = read_pos + 6; |
| 250 | + const uint32_t block_len = utils::read_ber_length(buf, ber_pos, len); |
255 | 251 |
|
256 | | - if (read_pos + 7 + block_len > len) { |
| 252 | + if (ber_pos + block_len > len) { |
257 | 253 | Logger::log(LogLevel::WARNING, "GBT: block truncated"); |
258 | 254 | return {0, 0}; |
259 | 255 | } |
260 | 256 |
|
261 | | - std::memmove(buf + write_pos, buf + read_pos + 7, block_len); |
| 257 | + std::memmove(buf + write_pos, buf + ber_pos, block_len); |
262 | 258 | write_pos += block_len; |
263 | | - read_pos += 7 + block_len; |
| 259 | + read_pos = ber_pos + block_len; |
264 | 260 |
|
265 | 261 | if (is_last) break; |
266 | 262 | } |
@@ -294,17 +290,8 @@ ApduHandler::UnwrapResult ApduHandler::unwrap_in_place(uint8_t* buf, size_t len) |
294 | 290 | pos += st_len; |
295 | 291 |
|
296 | 292 | // BER length |
297 | | - if (pos >= len) return {0, 0}; |
298 | | - const uint8_t len_byte = buf[pos++]; |
299 | | - uint32_t cipher_len = len_byte; |
300 | | - if (len_byte > DLMS_LENGTH_SINGLE_BYTE_MAX) { |
301 | | - const uint8_t num_bytes = len_byte & 0x7F; |
302 | | - cipher_len = 0; |
303 | | - for (uint8_t i = 0; i < num_bytes; i++) { |
304 | | - if (pos >= len) return {0, 0}; |
305 | | - cipher_len = (cipher_len << 8) | buf[pos++]; |
306 | | - } |
307 | | - } |
| 293 | + const uint32_t cipher_len = utils::read_ber_length(buf, pos, len); |
| 294 | + if (cipher_len == 0) return {0, 0}; |
308 | 295 |
|
309 | 296 | // Security control byte (skip) |
310 | 297 | if (pos >= len) return {0, 0}; |
|
0 commit comments