Skip to content

Commit aacde0c

Browse files
committed
BER lenght fix
1 parent 1b7c1e4 commit aacde0c

File tree

3 files changed

+38
-34
lines changed

3 files changed

+38
-34
lines changed

src/dlms_parser/apdu_handler.cpp

Lines changed: 21 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "apdu_handler.h"
22
#include "log.h"
3+
#include "utils.h"
34
#include <cstring>
45

56
namespace dlms_parser {
@@ -89,18 +90,9 @@ bool ApduHandler::parse_ciphered_apdu_(const uint8_t* buf, size_t len, uint8_t /
8990
std::memcpy(iv, buf + pos, DLMS_SYSTITLE_LENGTH);
9091
pos += DLMS_SYSTITLE_LENGTH;
9192

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;
10496

10597
// Security control byte (1 byte, informational — skip)
10698
if (pos >= len) return false;
@@ -142,26 +134,27 @@ bool ApduHandler::parse_general_block_transfer_(const uint8_t* buf, size_t len,
142134
size_t pos = 0;
143135

144136
while (pos < len && buf[pos] == DLMS_APDU_GENERAL_BLOCK_TRANSFER) {
145-
if (pos + 7 > len) {
137+
if (pos + 6 > len) {
146138
Logger::log(LogLevel::WARNING, "GBT: truncated block header at offset %zu", pos);
147139
return false;
148140
}
149141
const uint8_t ctrl = buf[pos + 1];
150142
const bool is_last = (ctrl & 0x80U) != 0;
151143
const uint16_t block_num = static_cast<uint16_t>(buf[pos + 2] << 8 | buf[pos + 3]);
152144
// 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);
154147

155-
if (pos + 7 + block_len > len) {
148+
if (ber_pos + block_len > len) {
156149
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);
158151
return false;
159152
}
160153

161154
Logger::log(LogLevel::DEBUG, "GBT block %u: %u bytes%s", block_num, block_len,
162155
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;
165158

166159
if (is_last) break;
167160
}
@@ -239,28 +232,31 @@ ApduHandler::UnwrapResult ApduHandler::unwrap_in_place(uint8_t* buf, size_t len)
239232
}
240233

241234
// --- General-Block-Transfer (0xE0): reassemble blocks in-place
235+
// Block format: E0 [ctrl:1] [block_num:2] [block_num_ack:2] [BER_len] [data...]
242236
if (tag == DLMS_APDU_GENERAL_BLOCK_TRANSFER) {
243237
Logger::log(LogLevel::DEBUG, "Found General-Block-Transfer (0xE0)");
244238
size_t read_pos = 0;
245239
size_t write_pos = 0;
246240

247241
while (read_pos < len && buf[read_pos] == DLMS_APDU_GENERAL_BLOCK_TRANSFER) {
248-
if (read_pos + 7 > len) {
242+
if (read_pos + 6 > len) {
249243
Logger::log(LogLevel::WARNING, "GBT: truncated block header");
250244
return {0, 0};
251245
}
252246
const uint8_t ctrl = buf[read_pos + 1];
253247
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);
255251

256-
if (read_pos + 7 + block_len > len) {
252+
if (ber_pos + block_len > len) {
257253
Logger::log(LogLevel::WARNING, "GBT: block truncated");
258254
return {0, 0};
259255
}
260256

261-
std::memmove(buf + write_pos, buf + read_pos + 7, block_len);
257+
std::memmove(buf + write_pos, buf + ber_pos, block_len);
262258
write_pos += block_len;
263-
read_pos += 7 + block_len;
259+
read_pos = ber_pos + block_len;
264260

265261
if (is_last) break;
266262
}
@@ -294,17 +290,8 @@ ApduHandler::UnwrapResult ApduHandler::unwrap_in_place(uint8_t* buf, size_t len)
294290
pos += st_len;
295291

296292
// 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};
308295

309296
// Security control byte (skip)
310297
if (pos >= len) return {0, 0};

src/dlms_parser/utils.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,19 @@ void data_to_string(const DlmsDataType value_type, const uint8_t* ptr, const uin
197197
}
198198
}
199199

200+
uint32_t read_ber_length(const uint8_t* buf, size_t& pos, size_t buf_len) {
201+
if (pos >= buf_len) return 0;
202+
const uint8_t first = buf[pos++];
203+
if (first <= 0x7F) return first;
204+
const uint8_t num_bytes = first & 0x7F;
205+
uint32_t length = 0;
206+
for (uint8_t i = 0; i < num_bytes; i++) {
207+
if (pos >= buf_len) return 0;
208+
length = (length << 8) | buf[pos++];
209+
}
210+
return length;
211+
}
212+
200213
void obis_to_string(const uint8_t* obis, char* buffer, const size_t max_len) {
201214
if (max_len > 0) buffer[0] = '\0';
202215
if (!obis || max_len == 0) return;

src/dlms_parser/utils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ void data_to_string(DlmsDataType value_type, const uint8_t* ptr, uint8_t len, ch
5252
void obis_to_string(const uint8_t* obis, char* buffer, size_t max_len);
5353
const char* dlms_data_type_to_string(DlmsDataType vt);
5454

55+
// Read a BER-encoded length from buf[pos]. Advances pos past the length bytes.
56+
// Returns the decoded length, or 0 if the buffer is too short.
57+
uint32_t read_ber_length(const uint8_t* buf, size_t& pos, size_t buf_len);
58+
5559
int get_data_type_size(DlmsDataType type);
5660
bool is_value_data_type(DlmsDataType type);
5761

0 commit comments

Comments
 (0)