HarpoS7 is a C# library designed to authenticate sessions in the S7 Comm Plus protocol. It supports both legacy challenge authentication (found in projects made using TIA Portal V16 and older) and the more recent TLS authentication introduced in project made using TIA Portal V17 and newer.
HarpoS7 is intended for integration into other libraries and frameworks rather than as a standalone tool for end users.
For a comprehensive example of how to use HarpoS7, explore the HarpoS7.PoC project included in the repository.
This project provides a hands-on demonstration of how to integrate HarpoS7 into a sample application. Follow the example code and comments to fully understand how to use the library.
Since 1.1.0 the default public keys are integrated into the library.
If you want to use a custom public key, you can implement your own IPublicKeyStore or pass in a raw public key to the Authenticate function directly.
In order to authenticate a legacy session (challenge-based):
using HarpoS7.Auth;
using HarpoS7.PublicKeys.Impl;
// The "input" buffers - you have to load/fill them yourselves
// The "output" buffers - the library fills them
// Input - challenge received from the PLC (20 bytes long)
var challenge = new byte[20];
// Input - public key used by the PLC (loaded from local storage,
// can be identified by the fingerprint sent by the PLC)
var exampleFingerprint = "00:181B7B0847D1169";
var store = new DefaultPublicKeyStore();
var publicKey = new byte[store.GetPublicKeyLength(exampleFingerprint)];
store.ReadPublicKey(publicKey.AsSpan(), exampleFingerprint);
// Input - public key family (must be read from the fingerprint)
// Example: 00:181B7B0847D11694, the 00 before the ':' is the public key family
// Currently the 00, 01 and 03 families are supported.
var family = EPublicKeyFamily.S71500;
// Output - "Encrypted key" which you send back to the PLC (216 bytes long)
var keyBlob = new byte[Constants.FinalBlobDataLength];
// Output - Session key used later on to calculate packet integrity hashes (24 bytes long)
var sessionKey = new byte[Constants.SessionKeyLength];
LegacyAuthenticationScheme.Authenticate(
keyBlob.AsSpan(),
sessionKey.AsSpan(),
challenge.AsSpan(),
publicKey.AsSpan(),
family);
In order to calculate a packet digest (these are used to prevent tampering):
// Input - your packet data (without the S7-Header and S7-Trailer)
var data = new byte[dataLength];
// Input - session key (output from LegacyAuthenticationScheme.Authenticate)
var sessionKey = new byte[Constants.SessionKeyLength];
// Output - the packet data digest, usually placed in the S7-header
var digestBuffer = new byte[HarpoPacketDigest.DigestLength];
HarpoPacketDigest.CalculateDigest(digestBuffer.AsSpan(), data, sessionKey);
It is important to note that although TLS authentication is present in HarpoS7, it should be treated as a proof of concept rather than a ready-to-use solution.
The reason is that the implementation is simply the built-in SslStream wrapped around a primitive CotpStream.
It is possible to authorize with a password against a password-protected PLC.
In order to authorize with a password you need to firstly obtain a valid session (using TLS or legacy auth scheme).
Then you need to get a legitimation challenge using a GetVarSubStreamed request (use Wireshark to know how).
Finally, you can pass everything down to the SolveLegitimateChallenge
function.
using HarpoS7.Auth;
var blobData = new byte[LegitimateScheme.OutputBlobDataLength];
LegitimateScheme.SolveLegitimateChallenge(
blobData, // OUT - send this with SetVarSubStreamed
challenge, // IN - get this with GetVarSubStreamed
publicKey, // IN - get this from a local storage by matching the fingerprint sent by the PLC
family, // IN - public key family (must be read from the fingerprint)
sessionKey, // IN - generated by LegacyAuthenticationScheme.Authenticate (TODO: check TLS)
"password"); // IN - password required by the PLC