Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/decoder/src/GcmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,10 @@ int8_t GCMParser::parse(uint8_t *d, DataParserContext &ctx, bool hastag) {
}
br_gcm_flip(&gcmCtx);
br_gcm_run(&gcmCtx, 0, (void*) (ptr), len - authkeylen - 5); // 5 == security tag and frame counter
if(authkeylen > 0 && br_gcm_check_tag_trunc(&gcmCtx, authentication_tag, authkeylen) != 1) {
// Only enforce the tag when an authentication key is configured, matching the
// ESP32/native paths. With a blank AK we decrypt without verifying integrity,
// which lets meters that don't use standard SC+AK authentication be read.
if(authenticate && br_gcm_check_tag_trunc(&gcmCtx, authentication_tag, authkeylen) != 1) {
return GCM_AUTH_FAILED;
}
#elif defined(ESP32)
Expand Down
37 changes: 37 additions & 0 deletions test/test_decoder/test_encrypted.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,40 @@ void test_encrypted_framing_no_key(void) {
TEST_ASSERT_EQUAL_MESSAGE(0, missing_title, "frame reached GCM but extracted no system title");
TEST_ASSERT_GREATER_THAN_MESSAGE(0, reached, "no encrypted frame reached the GCM layer");
}

// Decoding an authenticated frame (one issued with an AK) using only the encryption
// key — AK omitted — must still decode: a blank authentication key skips GCM tag
// verification rather than failing. This locks the cross-platform contract that the
// ESP8266 BearSSL path now matches (the ESP32/native mbedTLS paths already behaved
// this way). Exercises the native mbedTLS path; the ESP8266 br_gcm path is identical
// in intent but only compiles on-device.
void test_encrypted_decode_without_authkey(void) {
#if !defined(HAVE_MBEDTLS)
TEST_IGNORE_MESSAGE("native mbedTLS not available (install libmbedtls-dev) — skipping");
#else
int tested = 0, failures = 0;
for (size_t i = 0; i < COUNT(ENC_KEYED); i++) {
const KeyedFixture& k = ENC_KEYED[i];
if (k.ak_secret == nullptr) continue; // only authenticated fixtures are meaningful here
uint8_t ek[16];
if (!load_key(k.ek_secret, ek)) continue; // need the encryption key

static uint8_t buf[4096];
int len = harness_load_fixture(k.path, buf, sizeof(buf));
if (len <= 0) { printf(" load FAIL %s\n", k.path); failures++; continue; }
MeterConfig cfg; memset(&cfg, 0, sizeof(cfg));
// EK only, AK deliberately omitted -> auth must be skipped, frame still decodes
AmsData* d = harness_decode(buf, (uint16_t)len, &cfg, ek, NULL);
tested++;
if (!d || d->getListType() < 1) { printf(" DECODE FAIL (no-AK) %s\n", k.path); failures++; }
if (d) delete d;
}
if (tested == 0) {
TEST_IGNORE_MESSAGE("no authenticated fixtures with keys available");
} else {
char msg[96];
snprintf(msg, sizeof(msg), "%d/%d authenticated fixtures failed to decode with AK omitted", failures, tested);
TEST_ASSERT_EQUAL_MESSAGE(0, failures, msg);
}
#endif
}
2 changes: 2 additions & 0 deletions test/test_decoder/test_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ void test_encrypted_landisgyr_501(void);
void test_encrypted_kaifa_905(void);
void test_encrypted_kamstrup_73(void);
void test_encrypted_framing_no_key(void);
void test_encrypted_decode_without_authkey(void);
// defined in test_plaintext_with_key.cpp
void test_plaintext_dsmr_with_key_does_not_overflow(void);

Expand All @@ -96,6 +97,7 @@ int main(int argc, char** argv) {
RUN_TEST(test_encrypted_kaifa_905);
RUN_TEST(test_encrypted_kamstrup_73);
RUN_TEST(test_encrypted_framing_no_key);
RUN_TEST(test_encrypted_decode_without_authkey);
RUN_TEST(test_plaintext_dsmr_with_key_does_not_overflow);
return UNITY_END();
}
Loading