mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-01-12 00:02:53 +00:00
Preparing for austrian meters
This commit is contained in:
parent
6054e900e6
commit
21687368c6
@ -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
39
frames/austria.raw
Normal 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
|
||||
109
src/ams/hdlc.cpp
109
src/ams/hdlc.cpp
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user