/// <summary> /// Agree on global unicast encryption key. /// </summary> /// <param name="client"> DLMS client that is used to generate action.</param> /// <param name="key"> List of keys.</param> /// <returns>Generated action.</returns> public byte[][] KeyAgreement(GXDLMSSecureClient client) { if (Version == 0) { throw new ArgumentException("Public and private key isn't implemented for version 0."); } if (client.Ciphering.EphemeralKeyPair.Value == null) { throw new ArgumentException("Invalid Ephemeral key."); } if (client.Ciphering.SigningKeyPair.Value == null) { throw new ArgumentException("Invalid Signiture key."); } GXByteBuffer bb = new GXByteBuffer(); //Add Ephemeral public key. bb.Set(client.Ciphering.EphemeralKeyPair.Value.RawValue, 1, client.Ciphering.EphemeralKeyPair.Value.RawValue.Length - 1); //Add signature. byte[] sign = GXSecure.GetEphemeralPublicKeySignature(0, client.Ciphering.EphemeralKeyPair.Value, client.Ciphering.SigningKeyPair.Key); bb.Set(sign); List <KeyValuePair <GlobalKeyType, byte[]> > list = new List <KeyValuePair <GlobalKeyType, byte[]> >(); list.Add(new KeyValuePair <GlobalKeyType, byte[]>(GlobalKeyType.UnicastEncryption, bb.Array())); return(KeyAgreement(client, list)); }
///<summary> ///Parse AARQ request that client send and returns AARE request. /// </summary> ///<returns> ///Reply to the client. ///</returns> private void HandleAarqRequest(GXByteBuffer data, GXDLMSConnectionEventArgs connectionInfo) { AssociationResult result = AssociationResult.Accepted; Settings.CtoSChallenge = null; if (!Settings.UseCustomChallenge) { Settings.StoCChallenge = null; } // Reset settings for wrapper. if (Settings.InterfaceType == InterfaceType.WRAPPER) { Reset(true); } SourceDiagnostic diagnostic = GXAPDU.ParsePDU(Settings, Settings.Cipher, data, null); if (diagnostic != SourceDiagnostic.None) { result = AssociationResult.PermanentRejected; diagnostic = SourceDiagnostic.ApplicationContextNameNotSupported; InvalidConnection(connectionInfo); } else { diagnostic = ValidateAuthentication(Settings.Authentication, Settings.Password); if (diagnostic != SourceDiagnostic.None) { result = AssociationResult.PermanentRejected; InvalidConnection(connectionInfo); } else if (Settings.Authentication > Authentication.Low) { // If High authentication is used. if (!Settings.UseCustomChallenge) { Settings.StoCChallenge = GXSecure.GenerateChallenge(Settings.Authentication); } result = AssociationResult.Accepted; diagnostic = SourceDiagnostic.AuthenticationRequired; } else { Connected(connectionInfo); Settings.Connected = true; } } Settings.IsAuthenticationRequired = diagnostic == SourceDiagnostic.AuthenticationRequired; if (Settings.InterfaceType == Enums.InterfaceType.HDLC) { replyData.Set(GXCommon.LLCReplyBytes); } // Generate AARE packet. GXAPDU.GenerateAARE(Settings, replyData, result, diagnostic, Settings.Cipher, null); }
byte[] IGXDLMSBase.Invoke(GXDLMSSettings settings, int index, Object parameters) { //Check reply_to_HLS_authentication if (index == 1) { UInt32 ic = 0; byte[] secret; if (settings.Authentication == Authentication.HighGMAC) { secret = settings.SourceSystemTitle; GXByteBuffer bb = new GXByteBuffer(parameters as byte[]); bb.GetUInt8(); ic = bb.GetUInt32(); } else { secret = Secret; } byte[] serverChallenge = GXSecure.Secure(settings, settings.Cipher, ic, settings.StoCChallenge, secret); byte[] clientChallenge = (byte[])parameters; if (GXCommon.Compare(serverChallenge, clientChallenge)) { if (settings.Authentication == Authentication.HighGMAC) { secret = settings.Cipher.SystemTitle; } else { secret = Secret; } ic = settings.Cipher.FrameCounter; byte[] tmp = GXSecure.Secure(settings, settings.Cipher, ic, settings.CtoSChallenge, secret); GXByteBuffer challenge = new GXByteBuffer(); // ReturnParameters. challenge.SetUInt8(1); challenge.SetUInt8(0); challenge.SetUInt8((byte)DataType.OctetString); GXCommon.SetObjectCount(tmp.Length, challenge); challenge.Set(tmp); return(challenge.Array()); } else { throw new ArgumentException("Invoke failed. Invalid attribute index."); } } else { throw new ArgumentException("Invoke failed. Invalid attribute index."); } }
byte[] IGXDLMSBase.Invoke(GXDLMSSettings settings, ValueEventArgs e) { //Check reply_to_HLS_authentication if (e.Index == 8) { UInt32 ic = 0; byte[] secret; if (settings.Authentication == Authentication.HighGMAC) { secret = settings.SourceSystemTitle; GXByteBuffer bb = new GXByteBuffer(e.Parameters as byte[]); bb.GetUInt8(); ic = bb.GetUInt32(); } else { secret = Secret; } byte[] serverChallenge = GXSecure.Secure(settings, settings.Cipher, ic, settings.StoCChallenge, secret); byte[] clientChallenge = (byte[])e.Parameters; if (GXCommon.Compare(serverChallenge, clientChallenge)) { if (settings.Authentication == Authentication.HighGMAC) { secret = settings.Cipher.SystemTitle; ic = settings.Cipher.InvocationCounter; } else { secret = Secret; } settings.Connected = true; return(GXSecure.Secure(settings, settings.Cipher, ic, settings.CtoSChallenge, secret)); } else { // If the password does not match. settings.Connected = false; return(null); } } else { e.Error = ErrorCode.ReadWriteDenied; return(null); } }
///<summary> ///Parse AARQ request that client send and returns AARE request. /// </summary> ///<returns> ///Reply to the client. ///</returns> private byte[][] HandleAarqRequest() { AssociationResult result = AssociationResult.Accepted; Settings.CtoSChallenge = null; if (!Settings.UseCustomChallenge) { Settings.StoCChallenge = null; } SourceDiagnostic diagnostic = GXAPDU.ParsePDU(Settings, Settings.Cipher, Reply.Data); if (diagnostic != SourceDiagnostic.None) { result = AssociationResult.PermanentRejected; diagnostic = SourceDiagnostic.ApplicationContextNameNotSupported; } else { // Check that user can access server. diagnostic = ValidateAuthentication(Settings.Authentication, Settings.Password); if (diagnostic != SourceDiagnostic.None) { result = AssociationResult.PermanentRejected; } else if (Settings.Authentication > Authentication.Low) { // If High authentication is used. Settings.StoCChallenge = GXSecure.GenerateChallenge(Settings.Authentication); result = AssociationResult.Accepted; diagnostic = SourceDiagnostic.AuthenticationRequired; } } // Generate AARE packet. GXByteBuffer buff = new GXByteBuffer(150); GXAPDU.GenerateAARE(Settings, buff, result, diagnostic, Settings.Cipher); return(GXDLMS.SplitPdu(Settings, Command.Aare, 0, buff, ErrorCode.Ok, DateTime.MinValue)[0]); }
byte[] IGXDLMSBase.Invoke(GXDLMSSettings settings, ValueEventArgs e) { //Check reply_to_HLS_authentication if (e.Index == 1) { UInt32 ic = 0; byte[] secret; if (settings.Authentication == Authentication.HighGMAC) { secret = settings.SourceSystemTitle; GXByteBuffer bb = new GXByteBuffer(e.Parameters as byte[]); bb.GetUInt8(); ic = bb.GetUInt32(); } else { secret = Secret; } byte[] serverChallenge = GXSecure.Secure(settings, settings.Cipher, ic, settings.StoCChallenge, secret); byte[] clientChallenge = (byte[])e.Parameters; if (serverChallenge != null && clientChallenge != null && GXCommon.Compare(serverChallenge, clientChallenge)) { if (settings.Authentication == Authentication.HighGMAC) { secret = settings.Cipher.SystemTitle; ic = settings.Cipher.InvocationCounter; } else { secret = Secret; } AssociationStatus = AssociationStatus.Associated; return(GXSecure.Secure(settings, settings.Cipher, ic, settings.CtoSChallenge, secret)); } else //If the password does not match. { AssociationStatus = AssociationStatus.NonAssociated; return(null); } } else if (e.Index == 2) { byte[] tmp = e.Parameters as byte[]; if (tmp == null || tmp.Length == 0) { e.Error = ErrorCode.ReadWriteDenied; } else { Secret = tmp; } } else if (e.Index == 3) { //Add COSEM object. GXDLMSObject obj = GetObject(settings, e.Parameters as object[]); //Unknown objects are not add. if (obj is IGXDLMSBase) { if (ObjectList.FindByLN(obj.ObjectType, obj.LogicalName) == null) { ObjectList.Add(obj); } if (settings.Objects.FindByLN(obj.ObjectType, obj.LogicalName) == null) { settings.Objects.Add(obj); } } } else if (e.Index == 4) { //Remove COSEM object. GXDLMSObject obj = GetObject(settings, e.Parameters as object[]); //Unknown objects are not removed. if (obj is IGXDLMSBase) { GXDLMSObject t = ObjectList.FindByLN(obj.ObjectType, obj.LogicalName); if (t != null) { ObjectList.Remove(t); } //Item is not removed from all objects. It might be that use wants remove object only from association view. } } else if (e.Index == 5) { object[] tmp = e.Parameters as object[]; if (tmp == null || tmp.Length != 2) { e.Error = ErrorCode.ReadWriteDenied; } else { UserList.Add(new KeyValuePair <byte, string>(Convert.ToByte(tmp[0]), Convert.ToString(tmp[1]))); } } else if (e.Index == 6) { object[] tmp = e.Parameters as object[]; if (tmp == null || tmp.Length != 2) { e.Error = ErrorCode.ReadWriteDenied; } else { UserList.Remove(new KeyValuePair <byte, string>(Convert.ToByte(tmp[0]), Convert.ToString(tmp[1]))); } } else { e.Error = ErrorCode.ReadWriteDenied; } return(null); }
/// <summary> /// Update ephemeral keys. /// </summary> /// <param name="client">DLMS Client.</param> /// <param name="value">Received reply from the server.</param> /// <returns>List of Parsed key id and GUAK. This is for debugging purpose.</returns> public List <KeyValuePair <GlobalKeyType, byte[]> > UpdateEphemeralKeys(GXDLMSSecureClient client, GXByteBuffer value) { if (client == null) { throw new ArgumentNullException(nameof(client)); } if (value.GetUInt8() != (byte)DataType.Array) { throw new ArgumentOutOfRangeException("Invalid tag."); } GXEcdsa c = new GXEcdsa(client.Ciphering.EphemeralKeyPair.Key); int count = GXCommon.GetObjectCount(value); List <KeyValuePair <GlobalKeyType, byte[]> > list = new List <KeyValuePair <GlobalKeyType, byte[]> >(); for (int pos = 0; pos != count; ++pos) { if (value.GetUInt8() != (byte)DataType.Structure) { throw new ArgumentOutOfRangeException("Invalid tag."); } if (value.GetUInt8() != 2) { throw new ArgumentOutOfRangeException("Invalid length."); } if (value.GetUInt8() != (byte)DataType.Enum) { throw new ArgumentOutOfRangeException("Invalid key id data type."); } int keyId = value.GetUInt8(); if (keyId > 4) { throw new ArgumentOutOfRangeException("Invalid key type."); } if (value.GetUInt8() != (byte)DataType.OctetString) { throw new ArgumentOutOfRangeException("Invalid tag."); } if (GXCommon.GetObjectCount(value) != 128) { throw new ArgumentOutOfRangeException("Invalid length."); } //Get ephemeral public key server. GXByteBuffer key = new GXByteBuffer(); key.SetUInt8(4); key.Set(value, 64); GXPublicKey targetEphemeralKey = GXPublicKey.FromRawBytes(key.Array()); //Get ephemeral public key signature server. byte[] signature = new byte[64]; value.Get(signature); key.SetUInt8(0, (byte)keyId); //Verify signature. if (!GXSecure.ValidateEphemeralPublicKeySignature(key.Array(), signature, client.Ciphering.SigningKeyPair.Value)) { throw new GXDLMSCipherException("Invalid signature."); } byte[] z = c.GenerateSecret(targetEphemeralKey); System.Diagnostics.Debug.WriteLine("Shared secret:" + GXCommon.ToHex(z, true)); GXByteBuffer kdf = new GXByteBuffer(); kdf.Set(GXSecure.GenerateKDF(client.SecuritySuite, z, AlgorithmId.AesGcm128, client.Ciphering.SystemTitle, client.Settings.SourceSystemTitle, null, null)); System.Diagnostics.Debug.WriteLine("KDF:" + kdf.ToString()); list.Add(new KeyValuePair <GlobalKeyType, byte[]>((GlobalKeyType)keyId, kdf.SubArray(0, 16))); } //Update ephemeral keys. foreach (KeyValuePair <GlobalKeyType, byte[]> it in list) { switch (it.Key) { case GlobalKeyType.UnicastEncryption: client.Settings.EphemeralBlockCipherKey = it.Value; break; case GlobalKeyType.BroadcastEncryption: client.Settings.EphemeralBroadcastBlockCipherKey = it.Value; break; case GlobalKeyType.Authentication: client.Settings.EphemeralAuthenticationKey = it.Value; break; case GlobalKeyType.Kek: client.Settings.EphemeralKek = it.Value; break; } } return(list); }
byte[] IGXDLMSBase.Invoke(GXDLMSSettings settings, ValueEventArgs e) { //Check reply_to_HLS_authentication if (e.Index == 1) { UInt32 ic = 0; byte[] secret; if (settings.Authentication == Authentication.HighGMAC) { secret = settings.SourceSystemTitle; GXByteBuffer bb = new GXByteBuffer(e.Parameters as byte[]); bb.GetUInt8(); ic = bb.GetUInt32(); } else { secret = Secret; } byte[] serverChallenge = GXSecure.Secure(settings, settings.Cipher, ic, settings.StoCChallenge, secret); byte[] clientChallenge = (byte[])e.Parameters; if (serverChallenge != null && clientChallenge != null && GXCommon.Compare(serverChallenge, clientChallenge)) { if (settings.Authentication == Authentication.HighGMAC) { secret = settings.Cipher.SystemTitle; ic = settings.Cipher.InvocationCounter; } else { secret = Secret; } settings.Connected = true; return(GXSecure.Secure(settings, settings.Cipher, ic, settings.CtoSChallenge, secret)); } else //If the password does not match. { settings.Connected = false; return(null); } } else if (e.Index == 2) { byte[] tmp = e.Parameters as byte[]; if (tmp == null || tmp.Length == 0) { e.Error = ErrorCode.ReadWriteDenied; } else { Secret = tmp; } } else if (e.Index == 5) { object[] tmp = e.Parameters as object[]; if (tmp == null || tmp.Length != 2) { e.Error = ErrorCode.ReadWriteDenied; } else { UserList.Add(new KeyValuePair <byte, string>(Convert.ToByte(tmp[0]), Convert.ToString(tmp[1]))); } } else if (e.Index == 6) { object[] tmp = e.Parameters as object[]; if (tmp == null || tmp.Length != 2) { e.Error = ErrorCode.ReadWriteDenied; } else { UserList.Remove(new KeyValuePair <byte, string>(Convert.ToByte(tmp[0]), Convert.ToString(tmp[1]))); } } else { e.Error = ErrorCode.ReadWriteDenied; } return(null); }