Preparing for austrian meters

This commit is contained in:
Gunnar Skjold 2021-12-05 20:09:34 +01:00
parent 6054e900e6
commit 21687368c6
4 changed files with 142 additions and 30 deletions

View File

@ -1,6 +1,16 @@
# After decode:
7E A1 E9 41 03 13 C6 37
E6 E7 00 DB 08 4B 41 4D 45 01 AC 4D 6E 82 01 D0 30 00 00 A3 2F 0F 00 00 00 00
7E
A1 E9 // Frame type and size
41 03 13 C6 37 E6 E7 00
DB // Encrypted
08 4B 41 4D 45 01 AC 4D 6E // System title
82 // Prefix for 2-byte length
01 D0 // Length
30 // Security tag 0011 0000, 0=Compression off, 0=Unicast, 1=Encryption, 0=Authentication, 0000= Security Suite ID
00 00 A3 2F // Frame counter
// Decrypted frame below
0F 00 00 00 00
0C 07 E4 05 0C 02 0A 19 00 FF 80 00 80 // Package timestamp
02 41

39
frames/austria.raw Normal file
View File

@ -0,0 +1,39 @@
// HDLC header
68
01 01 // Format (0x00) and total length (257)
68 // Start
53 // Control field
FF // Address (Broadcast address)
// LLC
00 // Control information field
01 // Source SAP
67 // Destination SAP
DB // Encrypted
08 53 41 47 59 05 E6 D9 FD // System title
81 // Prefix for 1-byte length
F8 // Length (248)
20 // Security tag 0010 0000, 0=Compression off, 0=Unicast, 1=Encryption, 0=No auth, 0000= Security Suite ID
00 72 00 76 // Frame counter
1F 16 66 EA 3C 81 67 0D 0C FE AC 1F 98 20 36 92
DA 43 68 8F 58 58 23 7D D1 2F 02 C7 70 81 94 C6
4D 24 EF DC 04 4A F4 F6 C6 61 92 5F 63 E4 17 78
8A E3 A0 3B 8E 26 C8 9A FE 28 73 37 BA EF 13 BD
// 64
50 78 AB EE E0 ED F5 4B 8C B7 2D 72 88 1A 30 54
48 88 BE EE 7A 70 3A EA FA EC 90 88 39 08 8A 53
14 37 16 CD 3F CA 7E 7B 68 46 5D 86 46 F6 CE 1F
28 32 A6 CF CB 4B 3A CB E7 05 7F 1A 52 06 DE E8
// 128
5B 68 02 A6 67 0E 0B EF 1E 6D 40 08 51 C7 C2 6E
7F AA C0 8A BD DC B6 1F F8 FA BA 54 CF C0 CC A4
33 67 93 FA 0E DF 5E 64 89 72 CC 0E 85 19 F9 D7
5E FF 0C DE B7 1E 20 A9 BB 90 22 2C D9 00 E2 DE
// 192
17 E2 D9 25 45 BD B1 7B 62 C6 C4 0E A9 00 55 43
12 75 85 C4 18 6B 22 D9 26 FB B3 25 28 39 0D 97
// 224
45 03 90 03 FF 05 AD E6 69

View File

@ -16,8 +16,6 @@ void mbus_hexdump(const uint8_t* buf, int len) {
}
int HDLC_validate(const uint8_t* d, int length, HDLCConfig* config, CosemDateTime* timestamp) {
//mbus_hexdump(d, len);
HDLCHeader* h = (HDLCHeader*) d;
// Length field (11 lsb of format)
@ -25,27 +23,28 @@ int HDLC_validate(const uint8_t* d, int length, HDLCConfig* config, CosemDateTim
if(len > length)
return HDLC_FRAME_INCOMPLETE;
HDLCFooter* f = (HDLCFooter*) (d + len - sizeof *f);
// First and last byte should be MBUS_HAN_TAG
if(h->flag != HDLC_FLAG || f->flag != HDLC_FLAG)
return HDLC_BOUNDRY_FLAG_MISSING;
// Verify FCS
if(ntohs(f->fcs) != crc16_x25(d + 1, len - sizeof *f - 1))
return HDLC_FCS_ERROR;
int headersize = 8;
int footersize = 3;
int headersize = 3;
int footersize = 1;
uint8_t* ptr = (uint8_t*) &h[1];
// Frame format type 3
if((h->format & 0xF0) == 0xA0) {
HDLCFooter* f = (HDLCFooter*) (d + len - sizeof *f);
footersize = sizeof *f;
// First and last byte should be MBUS_HAN_TAG
if(h->flag != HDLC_FLAG || f->flag != HDLC_FLAG)
return HDLC_BOUNDRY_FLAG_MISSING;
// Verify FCS
if(ntohs(f->fcs) != crc16_x25(d + 1, len - sizeof *f - 1))
return HDLC_FCS_ERROR;
// Skip destination address, LSB marks last byte
while(((*ptr) & 0x01) == 0x00) {
ptr++;
headersize++;
}
headersize++;
ptr++;
// Skip source address, LSB marks last byte
@ -53,6 +52,7 @@ int HDLC_validate(const uint8_t* d, int length, HDLCConfig* config, CosemDateTim
ptr++;
headersize++;
}
headersize++;
ptr++;
HDLC3CtrlHcs* t3 = (HDLC3CtrlHcs*) (ptr);
@ -63,11 +63,27 @@ int HDLC_validate(const uint8_t* d, int length, HDLCConfig* config, CosemDateTim
return HDLC_HCS_ERROR;
ptr += sizeof *t3;
} else if((h->format & 0xF0) == 0x00) {
MbusFooter* f = (MbusFooter*) (d + len - sizeof *f);
footersize = sizeof *f;
// First and last byte should be MBUS_HAN_TAG
if(h->flag != MBUS_START || f->flag != MBUS_END)
return HDLC_BOUNDRY_FLAG_MISSING;
// TODO: Verify FCS with crc8, not 16
//if(ntohs(f->fcs) != crc16_x25(d + 1, len - sizeof *f - 1))
// return HDLC_FCS_ERROR;
// Ignore: Flag + Control field + Address
ptr += 3;
headersize += 3;
}
// Extract LLC
HDLCLLC* llc = (HDLCLLC*) ptr;
ptr += sizeof *llc;
headersize += 3;
if(((*ptr) & 0xFF) == 0x0F) {
// Unencrypted APDU
@ -96,17 +112,52 @@ int HDLC_validate(const uint8_t* d, int length, HDLCConfig* config, CosemDateTim
return ptr-d;
} else if(((*ptr) & 0xFF) == 0xDB) {
ptr++;
// Encrypted APDU
// http://www.weigu.lu/tutorials/sensors2bus/04_encryption/index.html
if(config == NULL)
return HDLC_ENCRYPTION_CONFIG_MISSING;
memcpy(config->system_title, d + headersize + 2, 8);
memcpy(config->initialization_vector, config->system_title, 8);
memcpy(config->initialization_vector + 8, d + headersize + 14, 4);
memcpy(config->additional_authenticated_data, d + headersize + 13, 1);
memcpy(config->additional_authenticated_data + 1, config->authentication_key, 16);
memcpy(config->authentication_tag, d + headersize + len - headersize - footersize - 12, 12);
uint8_t systemTitleLength = *ptr;
ptr++;
memcpy(config->system_title, ptr, systemTitleLength);
memcpy(config->initialization_vector, config->system_title, systemTitleLength);
headersize += 2 + systemTitleLength;
ptr += systemTitleLength;
if(((*ptr) & 0xFF) == 0x81) {
ptr++
// 1-byte payload length
ptr++
} else if(((*ptr) & 0xFF) == 0x82) {
ptr++
headersize++;
// 2-byte payload length
ptr += 2;
headersize += 2;
}
memcpy(config->additional_authenticated_data, ptr, 1);
// Security tag
uint8_t sec = *ptr;
ptr++;
headersize++;
// Frame counter
memcpy(config->initialization_vector + 8, ptr, 4);
ptr += 4;
headersize += 4;
// Authentication enabled
uint8_t authkeylen = 0, aadlen = 0;
if((sec & 0x10) == 0x10) {
authkeylen = 12;
aadlen = 17;
footersize += authkeylen;
memcpy(config->additional_authenticated_data + 1, config->authentication_key, 16);
memcpy(config->authentication_tag, d + len - footersize, authkeylen);
}
#if defined(ESP8266)
br_gcm_context gcmCtx;
@ -114,15 +165,17 @@ int HDLC_validate(const uint8_t* d, int length, HDLCConfig* config, CosemDateTim
br_aes_ct_ctr_init(&bc, config->encryption_key, 16);
br_gcm_init(&gcmCtx, &bc.vtable, br_ghash_ctmul32);
br_gcm_reset(&gcmCtx, config->initialization_vector, sizeof(config->initialization_vector));
br_gcm_aad_inject(&gcmCtx, config->additional_authenticated_data, sizeof(config->additional_authenticated_data));
if(authkeylen > 0) {
br_gcm_aad_inject(&gcmCtx, config->additional_authenticated_data, aadlen);
}
br_gcm_flip(&gcmCtx);
br_gcm_run(&gcmCtx, 0, (void*) (d + headersize + 18), (len - headersize - footersize - 18 - 12));
if(br_gcm_check_tag_trunc(&gcmCtx, config->authentication_tag, 12) != 1) {
br_gcm_run(&gcmCtx, 0, (void*) ptr, (len - headersize - footersize));
if(authkeylen > 0 && br_gcm_check_tag_trunc(&gcmCtx, config->authentication_tag, authkeylen) != 1) {
return -91;
}
#elif defined(ESP32)
uint8_t cipher_text[len - headersize - footersize - 18 - 12];
memcpy(cipher_text, d + headersize + 18, len - headersize - footersize - 12 - 18);
uint8_t cipher_text[len - headersize - footersize];
memcpy(cipher_text, ptr, len - headersize - footersize);
mbedtls_gcm_context m_ctx;
mbedtls_gcm_init(&m_ctx);
@ -131,15 +184,15 @@ int HDLC_validate(const uint8_t* d, int length, HDLCConfig* config, CosemDateTim
return -92;
}
success = mbedtls_gcm_auth_decrypt(&m_ctx, sizeof(cipher_text), config->initialization_vector, sizeof(config->initialization_vector),
config->additional_authenticated_data, sizeof(config->additional_authenticated_data), config->authentication_tag, sizeof(config->authentication_tag),
cipher_text, (unsigned char*)(d + headersize + 18));
config->additional_authenticated_data, aadlen, config->authentication_tag, authkeylen,
cipher_text, (unsigned char*)(ptr));
if (0 != success) {
return -91;
}
mbedtls_gcm_free(&m_ctx);
#endif
ptr += 23; // TODO: Come to this number in a proper way...
ptr += 5; // TODO: Come to this number in a proper way...
// ADPU timestamp
CosemData* dateTime = (CosemData*) ptr;

View File

@ -11,6 +11,9 @@
#define HDLC_FRAME_INCOMPLETE -4
#define HDLC_ENCRYPTION_CONFIG_MISSING -90
#define MBUS_START 0x68
#define MBUS_END 0x16
struct HDLCConfig {
uint8_t encryption_key[32];
uint8_t authentication_key[32];
@ -46,6 +49,13 @@ typedef struct HDLCADPU {
uint32_t id;
} __attribute__((packed)) HDLCADPU;
typedef struct MbusFooter {
uint8_t fcs;
uint8_t flag;
} __attribute__((packed)) MbusFooter;
// Blue book, Table 2
enum CosemType {
CosemTypeNull = 0x00,