Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decrypt data #21

Open
svrooij opened this issue Feb 24, 2021 · 7 comments · Fixed by #23
Open

Decrypt data #21

svrooij opened this issue Feb 24, 2021 · 7 comments · Fixed by #23
Labels
enhancement New feature or request released on @beta

Comments

@svrooij
Copy link
Owner

svrooij commented Feb 24, 2021

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.

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 🎉 ... (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.

@svrooij svrooij added enhancement New feature or request help wanted Extra attention is needed labels Feb 24, 2021
@svrooij
Copy link
Owner Author

svrooij commented Feb 24, 2021

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:

const lines = this.dataBuffer.trim().split('\r\n');
lines.forEach((line) => {
this.emit(P1ReaderEvents.Line, line);
});

@svrooij
Copy link
Owner Author

svrooij commented Feb 24, 2021

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.

@zuidwijk
Copy link

I'm working on a stream for you :)

svrooij added a commit that referenced this issue Feb 25, 2021
@svrooij svrooij linked a pull request Feb 26, 2021 that will close this issue
svrooij added a commit that referenced this issue Feb 26, 2021
* wip: Luxemburg meter parsing

* fix: Timeout on socket connection

Fixed #22

* feat: Basic support for reading encrypted meters

Fixed #21

* fix: Final changes to support decrypting

* docs: Started documentation

* docs: Another docs update

* chore: Remove duplicate screenshot

* chore: Lint errors
@github-actions
Copy link

🎉 This issue has been resolved in version 1.7.0-beta.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@svrooij svrooij removed the help wanted Extra attention is needed label Feb 26, 2021
@alexboss
Copy link

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

@alexboss
Copy link

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.

@alexboss
Copy link

alexboss commented Jun 14, 2022

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request released on @beta
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants