smartmeter2mqtt
smartmeter2mqtt copied to clipboard
Decrypt data
Apparently the Luxemburg government thought that sending meter data in clear text isn't a good idea, so they are encrypting it with AES-128-GCM.
- Specifications page 9
- Python decryption
Every customer can request their own decryption key.
Needed data for decryption:
What | Docs | Default value |
---|---|---|
AAD | Additional data (17 bytes) | 3000112233445566778899AABBCCDDEEFF hex |
IV | 96 bits total system-title (64 bits) + frame counter (32 bits) |
|
KEY | (request from energy company) 16 bytes / 128 bits |
Sample frame: all these parts after each other
What | Value (sample) | bytes | specs |
---|---|---|---|
Start | db |
1 |
hex, static |
Length of system title | 08 |
1 |
hex, static |
System title (needed for IV) | ffffffffffffffff |
8 |
8 bytes / 64 bits (or other if length changes) |
Number of bytes that follow | ??? | ? (use delimeter) | 17 + length of data |
Delimiter | 30 |
1 |
hex, static |
Frame counter (needed for IV) | ffffffff (max) |
4 |
4 bytes / 32 bits |
Data :tada: | ... |
(see length above) |
computed number of bytes |
GCM tag | ffffffffffffffffffffffff (max) |
12 |
12 bytes / 96 bits |
Once the data is decrypted it should just be a DSMR 5 telegram
For this to be implemented, please contact me if you live in Luxemburg, requested your key and have access to either the Slimme lezer or a P1 cable.
Pseudo code to decrypt
const crypto = require("crypto");
const alg = 'aes-128-gcm';
const key = 'xxxx'; // Get from energy company
const aad = '3000112233445566778899AABBCCDDEEFF`;
const keyBuffer = Buffer.from(key, 'hex');
const systemTitle = ''; // read from message (as hex)
const frameCounter = ''; // read from message (as hex)
const encryptedData = ''; // read from message (as hex)
const gcmTag = ''; // read from message (last 12 bytes, as hex)
const ivBuffer = Buffer.from(`${systemTitle}${frameCounter}`, 'hex');
const decipher = crypto.createDecipheriv(alg, keyBuffer, ivBuffer);
decipher.setAuthTag(gcmTag, 'hex');
let result = decipher.update(encryptedData, 'hex', 'utf8');
result += decipher.final('utf8'); // Not sure if this would be needed
result
should now contain the regular dsmr data and can be fed to the parser, like:
https://github.com/svrooij/smartmeter2mqtt/blob/022134797a1ba273f7fd28821f8baa8ad1de1713/src/p1-reader.ts#L74-L77
I need some help from someone who has access to a luxemburg smartmeter!
Please execute the following script:
# clone repo and open correct brance
git clone --branch feature/lux-encryption https://github.com/svrooij/smartmeter2mqtt.git
# go to folder
cd smartmeter2mqtt/example
# Execute script (replace the ip and port) and add you key if this doesn't give an error.
node decode-from-socket.js 192.168.1.15 23
The script above should start reading some output from the socket, if that works and it shows all the fields filled in. You add your key (in hexadecimal format) and it should start decrypting the data to DSMR telegram messages.
Please share your output so I can actually start parsing this nice data.
I'm working on a stream for you :)
:tada: This issue has been resolved in version 1.7.0-beta.1 :tada:
The release is available on:
Your semantic-release bot :package::rocket:
Hi guys,
Thank you for this great project.
I purchased a P1 reader (ethernet) from Zuidwijk to follow my electric consumption in Home Assistant.
I'm based in Luxembourg and have a Sagem 210 meter.
Following the recommendation of Marcel Zuidwijk on https://www.zuidwijk.com/product/p1-reader-ethernet/#comment-31414 I gave a try to the project to read metrics, since the P1 with Ethernet does not cope with the decryption. I'm not using the docker version but standalone (or Bare metal as stated on your site).
My config:
P1 reader
- Sagem 210 with encryption key provided by the energy provider 7CC9xxxxxxxxxxxxxxxxxxxxxxxE6D5
- P1 reader configured with a TCP server on port 3000
- The P1 status reports frames being sent (half of them marked as failed)
Telegrams manager
- Smartmeter2mqtt bare metal
- Outputting in JSON or Raw Socket or port 3010
-
smartmeter2mqtt --socket 192.168.0.107:3000 --raw-tcp-server 3010 --enc-key "7CC9xxxxxxxxxxxxxxxxxxxxxxxE6D5" --enc-aad "3000112233445566778899AABBCCDDEEFF" --debug
The problem: when outputting in JSON, nothing happens.
And when outputting in Raw-output, I get messages that look encrypted, example:
~/Apps/smartmeter2mqtt$ telnet 127.0.0.1 3010
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
SAGgpl!P03KQE} z?2�*hCaz>-k&68q;m5�M=G|d! zg"gViGw
{Rt9R0I[]{\;\qF]ApvN>7EKd
Q\Th8j(@kL?�E!??,_!iX;
"VD�%GB|>cBEd:8aVe>JzYIe&w8qU%bQgw0h4|'3_1(
^YoGJn<K9
z;iHV.N
75gm~\z
Kz>iCdT=[h4Bh"B!s#%`"!^4zb+pQ*cQR*?nF|-
]fO�JmG6#yrG
]mM4sOyM+,;w}JeJ(?olBqsy5)
N<`i<:heDvC|;U+
;[7].;
Hjx5KmEiy)E<"Zbd)(@'-,
[vTss~_da<eP9;@Z%lCkq9_pU[�(aKjPJ:U#f
\gJ7`#6'qO aY[b{>g8;[JPf5Cp=aHd;4
r<j=2@KJJGw^gGI<'xQY#H \P%
pgG
EVOF<.|+aA{&M8Vjl[7~|vg7P>C/a~j]~yolD
b"S~:"Qiw}lK
o+ukbAKXgV'TtZe*n{i_m1 q%!]{4W,`"Wb^>RB0?Na4\=wP>W>PqHq
7S&J
(x([lTQ
rW3dfy %
n
kR^!(AQRKY:{r(o3y)d%Ue*K&?xW"Ad
<y;k5<k/M@ta67H#cABwfI
p]2bfPMua538qO`QE>t&2EFP ]e^?yHb
ETnK\ A V5s{f(:"+Kx_5)4Z>;J^
x;Zz6swWacl\IZ[ZdldX9D__3C^k@7yyZqAG/DCHYs6oo
iQ)l.R5\h
J"}Y|uL ^&kOpNRIR(^
tOeO\z6u{Vupp_e!`HjM
t
^CConnection closed by foreign host.
My bet is that the P1 output is not decrypted properly, even though I followed the instructions for LU P1 meter.
By any chance, did I miss something obvious in the configuration / settings? If so, could you please kindly point it to me?
Thank you and best regards.
Alexandre
Ouch, actually I have version 1.6.0 which does not deal with decryption I suppose. Gonna try with 1.7.x branch and let you know how it went. Sorry for the inconvenience.
Hi guys, excellent news, after installing 1.7.0-beta.9
, it works as it should!!!!
On the smartmeter2mqtt
console I get:
- usageChange Usage decreased -1 to 232
- new reading {
"crc": true,
"header": "Lux5\\253663629_D",
"p1Version": "42",
"powerTs": "2022-06-14T09:45:29",
"totalImportedEnergyP": 23949.19,
"totalExportedEnergyP": 0.002,
"totalImportedEnergyQ": 1138.874,
"totalExportedEnergyQ": 3723.324,
"currentUsage": 0.231,
"currentDelivery": 0,
"breakerControlState": 1,
"powerFailures": 16,
"voltageSagsL1": 4,
"voltageSagsL2": 4,
"voltageSagsL3": 4,
"voltageSwellsL1": 0,
"voltageSwellsL2": 0,
"voltageSwellsL3": 0,
"voltageL1": 233,
"voltageL2": 232,
"voltageL3": 233,
"currentL1": 0,
"currentL2": 0,
"currentL3": 0,
"currentUsageL1": 0.119,
"currentUsageL2": 0.091,
"currentUsageL3": 0.02,
"currentDeliveryL1": 0,
"currentDeliveryL2": 0,
"currentDeliveryL3": 0,
"calculatedUsage": 231
}
And on the telnet console I get:
1-3:0.2.8(42)
0-0:1.0.0(220614094609S)
0-0:42.0.0(53414731303330373030323839393533)
1-0:1.8.0(023949.193*kWh)
1-0:2.8.0(000000.002*kWh)
1-0:3.8.0(001138.874*kvarh)
1-0:4.8.0(003723.325*kvarh)
1-0:1.7.0(00.231*kW)
1-0:2.7.0(00.000*kW)
1-0:3.7.0(00.000*kvar)
1-0:4.7.0(00.075*kvar)
0-0:17.0.0(027.6*kVA)
1-0:9.7.0(00.251*kVA)
1-0:10.7.0(00.000*kVA)
1-1:31.4.0(040*A)(-040*A)
0-0:96.3.10(1)
0-1:96.3.10(0)
0-2:96.3.10(0)
0-0:96.7.21(00016)
1-0:32.32.0(00004)
1-0:52.32.0(00004)
1-0:72.32.0(00004)
1-0:32.36.0(00000)
1-0:52.36.0(00000)
1-0:72.36.0(00000)
0-0:96.13.0()
0-0:96.13.2()
0-0:96.13.3()
0-0:96.13.4()
0-0:96.13.5()
1-0:32.7.0(233.0*V)
1-0:52.7.0(232.0*V)
1-0:72.7.0(233.0*V)
1-0:31.7.0(000*A)
1-0:51.7.0(000*A)
1-0:71.7.0(000*A)
1-0:21.7.0(00.118*kW)
1-0:41.7.0(00.091*kW)
1-0:61.7.0(00.021*kW)
1-0:22.7.0(00.000*kW)
1-0:42.7.0(00.000*kW)
1-0:62.7.0(00.000*kW)
1-0:23.7.0(00.000*kvar)
1-0:43.7.0(00.000*kvar)
1-0:63.7.0(00.000*kvar)
1-0:24.7.0(00.030*kvar)
1-0:44.7.0(00.014*kvar)
1-0:64.7.0(00.030*kvar)
!C560
Even though sometimes I get error messages in the console:
(node:32617) UnhandledPromiseRejectionWarning: Error: write ECONNRESET
at afterWriteDispatched (internal/stream_base_commons.js:156:25)
at writeGeneric (internal/stream_base_commons.js:147:3)
at Socket._writeGeneric (net.js:788:11)
at Socket._write (net.js:800:8)
at doWrite (_stream_writable.js:403:12)
at writeOrBuffer (_stream_writable.js:387:5)
at Socket.Writable.write (_stream_writable.js:318:11)
at [/usr/local/lib/node_modules/smartmeter2mqtt/node_modules/@svrooij](https://github.com/svrooij/smartmeter2mqtt/issues/21#NOP)/tcp-server/dist/tcp-server.js:69:15
at new Promise (<anonymous>)
at [/usr/local/lib/node_modules/smartmeter2mqtt/node_modules/@svrooij](https://github.com/svrooij/smartmeter2mqtt/issues/21#NOP)/tcp-server/dist/tcp-server.js:68:54
(node:32617) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 196)
(node:32617) UnhandledPromiseRejectionWarning: Error [ERR_STREAM_DESTROYED]: Cannot call write after a stream was destroyed
at doWrite (_stream_writable.js:399:19)
at writeOrBuffer (_stream_writable.js:387:5)
at Socket.Writable.write (_stream_writable.js:318:11)
at [/usr/local/lib/node_modules/smartmeter2mqtt/node_modules/@svrooij](https://github.com/svrooij/smartmeter2mqtt/issues/21#NOP)/tcp-server/dist/tcp-server.js:69:15
at new Promise (<anonymous>)
at [/usr/local/lib/node_modules/smartmeter2mqtt/node_modules/@svrooij](https://github.com/svrooij/smartmeter2mqtt/issues/21#NOP)/tcp-server/dist/tcp-server.js:68:54
at Array.map (<anonymous>)
at TcpServer.publish ([/usr/local/lib/node_modules/smartmeter2mqtt/node_modules/@svrooij](https://github.com/svrooij/smartmeter2mqtt/issues/21#NOP)/tcp-server/dist/tcp-server.js:68:43)
at P1Reader.<anonymous> (/usr/local/lib/node_modules/smartmeter2mqtt/dist/output/tcp-output.js:21:77)
at P1Reader.emit (events.js:326:22)
(node:32617) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 197)
Thank you and best regards.
Alexandre