/// <summary> /// Decrypt data. /// </summary> /// <param name="p">Decryption parameters</param> /// <returns>Decrypted data.</returns> public static byte[] DecryptAesGcm(AesGcmParameter p, GXByteBuffer data) { if (data == null || data.Size < 2) { throw new ArgumentOutOfRangeException("cryptedData"); } byte[] tmp; int len; Command cmd = (Command)data.GetUInt8(); switch (cmd) { case Command.GeneralGloCiphering: case Command.GeneralDedCiphering: len = GXCommon.GetObjectCount(data); if (len != 0) { p.SystemTitle = new byte[len]; data.Get(p.SystemTitle); if (p.Xml != null && p.Xml.Comments) { p.Xml.AppendComment(GXCommon.SystemTitleToString(Standard.DLMS, p.SystemTitle)); } } if (p.SystemTitle == null || p.SystemTitle.Length != 8) { if (p.Xml == null) { throw new ArgumentNullException("Invalid sender system title."); } else { p.Xml.AppendComment("Invalid sender system title."); } } break; case Command.GeneralCiphering: case Command.GloInitiateRequest: case Command.GloInitiateResponse: case Command.GloReadRequest: case Command.GloReadResponse: case Command.GloWriteRequest: case Command.GloWriteResponse: case Command.GloGetRequest: case Command.GloGetResponse: case Command.GloSetRequest: case Command.GloSetResponse: case Command.GloMethodRequest: case Command.GloMethodResponse: case Command.GloEventNotification: case Command.DedInitiateRequest: case Command.DedInitiateResponse: case Command.DedGetRequest: case Command.DedGetResponse: case Command.DedSetRequest: case Command.DedSetResponse: case Command.DedMethodRequest: case Command.DedMethodResponse: case Command.DedEventNotification: case Command.DedReadRequest: case Command.DedReadResponse: case Command.DedWriteRequest: case Command.DedWriteResponse: case Command.GloConfirmedServiceError: case Command.DedConfirmedServiceError: break; default: throw new ArgumentOutOfRangeException("cryptedData"); } int value = 0; UInt64 transactionId = 0; if (cmd == Command.GeneralCiphering) { len = GXCommon.GetObjectCount(data); tmp = new byte[len]; data.Get(tmp); GXByteBuffer t = new GXByteBuffer(tmp); transactionId = t.GetUInt64(); len = GXCommon.GetObjectCount(data); if (len != 0) { tmp = new byte[len]; data.Get(tmp); p.SystemTitle = tmp; } if (p.SystemTitle == null || p.SystemTitle.Length != 8) { if (p.Xml == null) { throw new ArgumentNullException("Invalid sender system title."); } else { p.Xml.AppendComment("Invalid sender system title."); } } len = GXCommon.GetObjectCount(data); tmp = new byte[len]; data.Get(tmp); p.RecipientSystemTitle = tmp; // Get date time. len = GXCommon.GetObjectCount(data); if (len != 0) { tmp = new byte[len]; data.Get(tmp); p.DateTime = tmp; } // other-information len = data.GetUInt8(); if (len != 0) { tmp = new byte[len]; data.Get(tmp); p.OtherInformation = tmp; } // KeyInfo OPTIONAL len = data.GetUInt8(); // AgreedKey CHOICE tag. data.GetUInt8(); // key-parameters len = data.GetUInt8(); value = data.GetUInt8(); p.KeyParameters = value; if (value == 1) { // KeyAgreement.ONE_PASS_DIFFIE_HELLMAN // key-ciphered-data len = GXCommon.GetObjectCount(data); tmp = new byte[len]; data.Get(tmp); p.KeyCipheredData = tmp; } else if (value == 2) { // KeyAgreement.STATIC_UNIFIED_MODEL len = GXCommon.GetObjectCount(data); if (len != 0) { throw new ArgumentException("Invalid key parameters"); } } else { throw new ArgumentException("key-parameters"); } } len = GXCommon.GetObjectCount(data); p.CipheredContent = data.Remaining(); byte sc = (byte)data.GetUInt8(); Enums.Security security = (Enums.Security)(sc & 0x30); if ((sc & 0x80) != 0) { System.Diagnostics.Debug.WriteLine("Compression is used."); } if ((sc & 0x40) != 0) { System.Diagnostics.Debug.WriteLine("Error: Key_Set is used."); } if ((sc & 0x20) != 0) { System.Diagnostics.Debug.WriteLine("Encryption is applied."); } SecuritySuite ss = (SecuritySuite)(sc & 0x3); p.Security = (byte)security; UInt32 invocationCounter = data.GetUInt32(); p.InvocationCounter = invocationCounter; if (ss == SecuritySuite.Version2) { throw new NotImplementedException("Security Suite 2 is not implemented."); } System.Diagnostics.Debug.WriteLine("Decrypt settings: " + p.ToString()); System.Diagnostics.Debug.WriteLine("Encrypted: " + GXCommon.ToHex(data.Data, false, data.Position, data.Size - data.Position)); byte[] tag = new byte[12]; byte[] encryptedData; int length; if (security == Enums.Security.Authentication) { length = data.Size - data.Position - 12; encryptedData = new byte[length]; data.Get(encryptedData); data.Get(tag); // Check tag. EncryptAesGcm(p, encryptedData); if (!GXDLMSChipperingStream.TagsEquals(tag, p.CountTag)) { if (transactionId != 0) { p.InvocationCounter = transactionId; } if (p.Xml == null) { throw new GXDLMSException("Decrypt failed. Invalid tag."); } else { p.Xml.AppendComment("Decrypt failed. Invalid tag."); } } return(encryptedData); } byte[] ciphertext = null; if (security == Enums.Security.Encryption) { length = data.Size - data.Position; ciphertext = new byte[length]; data.Get(ciphertext); } else if (security == Enums.Security.AuthenticationEncryption) { length = data.Size - data.Position - 12; ciphertext = new byte[length]; data.Get(ciphertext); data.Get(tag); } byte[] aad = GetAuthenticatedData(p, ciphertext), iv = GetNonse(invocationCounter, p.SystemTitle); GXDLMSChipperingStream gcm = new GXDLMSChipperingStream((byte)security, true, p.BlockCipherKey, aad, iv, tag); gcm.Write(ciphertext); if (transactionId != 0) { p.InvocationCounter = transactionId; } return(gcm.FlushFinalBlock()); }
///<summary> ///Parse APDU. ///</summary> static internal SourceDiagnostic ParsePDU(GXDLMSSettings settings, GXICipher cipher, GXByteBuffer buff) { // Get AARE tag and length int tag = buff.GetUInt8(); if (settings.IsServer) { if (tag != ((byte)BerType.Application | (byte)BerType.Constructed | (byte)PduType.ProtocolVersion)) { throw new Exception("Invalid tag."); } } else { if (tag != ((byte)BerType.Application | (byte)BerType.Constructed | (byte)PduType.ApplicationContextName)) { throw new Exception("Invalid tag."); } } int len = buff.GetUInt8(); int size = buff.Size - buff.Position; if (len > size) { throw new Exception("Not enough data."); } AssociationResult resultComponent = AssociationResult.Accepted; SourceDiagnostic resultDiagnosticValue = SourceDiagnostic.None; while (buff.Position < buff.Size) { tag = buff.GetUInt8(); switch (tag) { case (byte)BerType.Context | (byte)BerType.Constructed | (byte)PduType.ApplicationContextName: //0xA1 if (!ParseApplicationContextName(settings, buff)) { throw new GXDLMSException(AssociationResult.PermanentRejected, SourceDiagnostic.ApplicationContextNameNotSupported); } break; case (byte)BerType.Context | (byte)BerType.Constructed | (byte)PduType.CalledApTitle: ////Result 0xA2 //Get len. if (buff.GetUInt8() != 3) { throw new Exception("Invalid tag."); } //Choice for result (INTEGER, universal) if (buff.GetUInt8() != (byte)BerType.Integer) { throw new Exception("Invalid tag."); } //Get len. if (buff.GetUInt8() != 1) { throw new Exception("Invalid tag."); } resultComponent = (AssociationResult)buff.GetUInt8(); break; case (byte)BerType.Context | (byte)BerType.Constructed | (byte)PduType.CalledAeQualifier: ////SourceDiagnostic 0xA3 len = buff.GetUInt8(); // ACSE service user tag. tag = buff.GetUInt8(); len = buff.GetUInt8(); // Result source diagnostic component. if (buff.GetUInt8() != (byte)BerType.Integer) { throw new Exception("Invalid tag."); } if (buff.GetUInt8() != 1) { throw new Exception("Invalid tag."); } resultDiagnosticValue = (SourceDiagnostic)buff.GetUInt8(); break; case (byte)BerType.Context | (byte)BerType.Constructed | (byte)PduType.CalledApInvocationId: ////Result 0xA4 //Get len. if (buff.GetUInt8() != 0xA) { throw new Exception("Invalid tag."); } //Choice for result (Universal, Octetstring type) if (buff.GetUInt8() != (byte)BerType.OctetString) { throw new Exception("Invalid tag."); } //responding-AP-title-field //Get len. len = buff.GetUInt8(); settings.SourceSystemTitle = new byte[len]; buff.Get(settings.SourceSystemTitle); break; //Client Challenge. case (byte)BerType.Context | (byte)BerType.Constructed | (byte)PduType.CallingApTitle: //0xA6 len = buff.GetUInt8(); tag = buff.GetUInt8(); len = buff.GetUInt8(); settings.SourceSystemTitle = new byte[len]; buff.Get(settings.SourceSystemTitle); break; //Server Challenge. case (byte)BerType.Context | (byte)BerType.Constructed | (byte)PduType.SenderAcseRequirements: //0xAA len = buff.GetUInt8(); tag = buff.GetUInt8(); len = buff.GetUInt8(); settings.StoCChallenge = new byte[len]; buff.Get(settings.StoCChallenge); break; case (byte)BerType.Context | (byte)PduType.SenderAcseRequirements: //0x8A case (byte)BerType.Context | (byte)PduType.CallingApInvocationId: //0x88 //Get sender ACSE-requirements field component. if (buff.GetUInt8() != 2) { throw new Exception("Invalid tag."); } if (buff.GetUInt8() != (byte)BerType.ObjectDescriptor) { throw new Exception("Invalid tag."); } if (buff.GetUInt8() != 0x80) { throw new Exception("Invalid tag."); } break; case (byte)BerType.Context | (byte)PduType.MechanismName: //0x8B case (byte)BerType.Context | (byte)PduType.CallingAeInvocationId: //0x89 len = buff.GetUInt8(); if (buff.GetUInt8() != 0x60) { throw new Exception("Invalid tag."); } if (buff.GetUInt8() != 0x85) { throw new Exception("Invalid tag."); } if (buff.GetUInt8() != 0x74) { throw new Exception("Invalid tag."); } if (buff.GetUInt8() != 0x05) { throw new Exception("Invalid tag."); } if (buff.GetUInt8() != 0x08) { throw new Exception("Invalid tag."); } if (buff.GetUInt8() != 0x02) { throw new Exception("Invalid tag."); } int tmp = buff.GetUInt8(); if (tmp < 0 || tmp > 5) { throw new Exception("Invalid tag."); } settings.Authentication = (Authentication)tmp; break; case (byte)BerType.Context | (byte)BerType.Constructed | (byte)PduType.CallingAuthenticationValue: //0xAC len = buff.GetUInt8(); // Get authentication information. if (buff.GetUInt8() != 0x80) { throw new Exception("Invalid tag."); } len = buff.GetUInt8(); if (settings.Authentication < Authentication.HighMD5) { settings.Password = new byte[len]; buff.Get(settings.Password); } else { settings.CtoSChallenge = new byte[len]; buff.Get(settings.CtoSChallenge); } break; case (byte)BerType.Context | (byte)BerType.Constructed | (byte)PduType.UserInformation: //0xBE if (resultComponent != AssociationResult.Accepted && resultDiagnosticValue != SourceDiagnostic.None) { throw new GXDLMSException(resultComponent, resultDiagnosticValue); } ParseUserInformation(settings, cipher, buff); break; default: //Unknown tags. System.Diagnostics.Debug.WriteLine("Unknown tag: " + tag + "."); len = buff.GetUInt8(); buff.Position += (UInt16)len; break; } } return(resultDiagnosticValue); }
/// <summary> /// Decrypt data. /// </summary> /// <param name="p">Decryption parameters</param> /// <returns>Decrypted data.</returns> public static byte[] DecryptAesGcm(AesGcmParameter p, GXByteBuffer data) { if (data == null || data.Size < 2) { throw new ArgumentOutOfRangeException("cryptedData"); } byte[] tmp; int len; Command cmd = (Command)data.GetUInt8(); switch (cmd) { case Command.GeneralGloCiphering: len = GXCommon.GetObjectCount(data); if (len != 0) { p.SystemTitle = new byte[len]; data.Get(p.SystemTitle); } break; case Command.GeneralCiphering: case Command.GloInitiateRequest: case Command.GloInitiateResponse: case Command.GloReadRequest: case Command.GloReadResponse: case Command.GloWriteRequest: case Command.GloWriteResponse: case Command.GloGetRequest: case Command.GloGetResponse: case Command.GloSetRequest: case Command.GloSetResponse: case Command.GloMethodRequest: case Command.GloMethodResponse: case Command.GloEventNotificationRequest: break; default: throw new ArgumentOutOfRangeException("cryptedData"); } int value = 0; UInt64 transactionId = 0; if (cmd == Command.GeneralCiphering) { len = GXCommon.GetObjectCount(data); tmp = new byte[len]; data.Get(tmp); GXByteBuffer t = new GXByteBuffer(tmp); transactionId = t.GetUInt64(); len = GXCommon.GetObjectCount(data); tmp = new byte[len]; data.Get(tmp); p.SystemTitle = tmp; len = GXCommon.GetObjectCount(data); tmp = new byte[len]; data.Get(tmp); p.RecipientSystemTitle = tmp; // Get date time. len = GXCommon.GetObjectCount(data); if (len != 0) { tmp = new byte[len]; data.Get(tmp); p.DateTime = tmp; } // other-information len = data.GetUInt8(); if (len != 0) { tmp = new byte[len]; data.Get(tmp); p.OtherInformation = tmp; } // KeyInfo OPTIONAL len = data.GetUInt8(); // AgreedKey CHOICE tag. data.GetUInt8(); // key-parameters len = data.GetUInt8(); value = data.GetUInt8(); p.KeyParameters = value; if (value == 1) { // KeyAgreement.ONE_PASS_DIFFIE_HELLMAN // key-ciphered-data len = GXCommon.GetObjectCount(data); tmp = new byte[len]; data.Get(tmp); p.KeyCipheredData = tmp; } else if (value == 2) { // KeyAgreement.STATIC_UNIFIED_MODEL len = GXCommon.GetObjectCount(data); if (len != 0) { throw new ArgumentException("Invalid key parameters"); } } else { throw new ArgumentException("key-parameters"); } } len = GXCommon.GetObjectCount(data); p.CipheredContent = data.Remaining(); byte sc = (byte)data.GetUInt8(); Enums.Security security = (Enums.Security)(sc & 0x30); SecuritySuite ss = (SecuritySuite)(sc & 0x3); p.Security = security; UInt32 invocationCounter = data.GetUInt32(); p.InvocationCounter = invocationCounter; if (ss != SecuritySuite.AesGcm128) { throw new NotImplementedException("Security Suite 1 is not implemented."); } System.Diagnostics.Debug.WriteLine("Decrypt settings: " + p.ToString()); System.Diagnostics.Debug.WriteLine("Encrypted: " + GXCommon.ToHex(data.Data, false, data.Position, data.Size - data.Position)); byte[] tag = new byte[12]; byte[] encryptedData; int length; if (security == Enums.Security.Authentication) { length = data.Size - data.Position - 12; encryptedData = new byte[length]; data.Get(encryptedData); data.Get(tag); // Check tag. EncryptAesGcm(p, encryptedData); if (!GXDLMSChipperingStream.TagsEquals(tag, p.CountTag)) { if (transactionId != 0) { p.InvocationCounter = transactionId; } throw new GXDLMSException("Decrypt failed. Invalid tag."); } return(encryptedData); } byte[] ciphertext = null; if (security == Enums.Security.Encryption) { length = data.Size - data.Position; ciphertext = new byte[length]; data.Get(ciphertext); } else if (security == Enums.Security.AuthenticationEncryption) { length = data.Size - data.Position - 12; ciphertext = new byte[length]; data.Get(ciphertext); data.Get(tag); } byte[] aad = GetAuthenticatedData(p, ciphertext), iv = GetNonse(invocationCounter, p.SystemTitle); GXDLMSChipperingStream gcm = new GXDLMSChipperingStream(security, true, p.BlockCipherKey, aad, iv, tag); gcm.Write(ciphertext); if (transactionId != 0) { p.InvocationCounter = transactionId; } return(gcm.FlushFinalBlock()); /* * len = Gurux.DLMS.Internal.GXCommon.GetObjectCount(data); * p.Security = (Gurux.DLMS.Enums.Security)data.GetUInt8(); * p.InvocationCounter = data.GetUInt32(); * System.Diagnostics.Debug.WriteLine("Decrypt settings: " + p.ToString()); * System.Diagnostics.Debug.WriteLine("Encrypted: " + GXCommon.ToHex(data.Array(), true)); * * byte[] tag = new byte[12]; * byte[] encryptedData; * int length; * if (p.Security == Gurux.DLMS.Enums.Security.Authentication) * { * length = data.Size - data.Position - 12; * encryptedData = new byte[length]; * data.Get(encryptedData); * data.Get(tag); * // Check tag. * EncryptAesGcm(p, encryptedData); * if (!GXDLMSChipperingStream.TagsEquals(tag, p.CountTag)) * { * throw new GXDLMSException("Decrypt failed. Invalid tag."); * } * return encryptedData; * } * byte[] ciphertext = null; * if (p.Security == Gurux.DLMS.Enums.Security.Encryption) * { * length = data.Size - data.Position; * ciphertext = new byte[length]; * data.Get(ciphertext); * } * else if (p.Security == Gurux.DLMS.Enums.Security.AuthenticationEncryption) * { * length = data.Size - data.Position - 12; * ciphertext = new byte[length]; * data.Get(ciphertext); * data.Get(tag); * } * byte[] aad = GetAuthenticatedData(p.Security, p.AuthenticationKey, ciphertext); * byte[] iv = GetNonse(p.InvocationCounter, p.SystemTitle); * GXDLMSChipperingStream gcm = new GXDLMSChipperingStream(p.Security, true, p.BlockCipherKey, aad, iv, tag); * gcm.Write(ciphertext); * ciphertext = gcm.FlushFinalBlock(); * if (p.Security == Gurux.DLMS.Enums.Security.AuthenticationEncryption) * { * // Check tag. * EncryptAesGcm(p, ciphertext); * if (!GXDLMSChipperingStream.TagsEquals(tag, p.CountTag)) * { * // throw new GXDLMSException("Decrypt failed. Invalid tag."); * } * } * return ciphertext; */ }
private static void GetValue(GXByteBuffer bb, IList <object> objects, GXAsn1Settings s) { int len; short type; IList <object> tmp; byte[] tmp2; type = bb.GetUInt8(); len = GXCommon.GetObjectCount(bb); if (len > bb.Available) { throw new OutOfMemoryException("GXAsn1Converter.GetValue"); } int connectPos = 0; if (s != null) { connectPos = s.XmlLength; } int start = bb.Position; string tagString = null; if (s != null) { s.AppendSpaces(); if (type == (byte)BerType.Integer) { if (len == 1 || len == 2 || len == 4 || len == 8) { tagString = s.GetTag((short)-len); } else { tagString = s.GetTag((byte)BerType.Integer); } } else { tagString = s.GetTag(type); } s.Append("<" + tagString + ">"); } switch (type) { case (byte)(BerType.Constructed | BerType.Context): case ((byte)(BerType.Constructed | BerType.Context) | 1): case ((byte)(BerType.Constructed | BerType.Context) | 2): case ((byte)(BerType.Constructed | BerType.Context) | 3): case ((byte)(BerType.Constructed | BerType.Context) | 4): if (s != null) { s.Increase(); } tmp = new GXAsn1Context() { Index = type & 0xF }; objects.Add(tmp); while (bb.Position < start + len) { GetValue(bb, tmp, s); } if (s != null) { s.Decrease(); } break; case (byte)(BerType.Constructed | BerType.Sequence): if (s != null) { s.Increase(); } tmp = new GXAsn1Sequence(); objects.Add(tmp); int cnt = 0; while (bb.Position < start + len) { ++cnt; GetValue(bb, tmp, s); } if (s != null) { // Append comment. s.AppendComment(connectPos, Convert.ToString(cnt) + " elements."); s.Decrease(); } break; case (byte)(BerType.Constructed | BerType.Set): if (s != null) { s.Increase(); } tmp = new List <object>(); GetValue(bb, tmp, s); if (tmp[0] is GXAsn1Sequence) { tmp = (GXAsn1Sequence)tmp[0]; objects.Add(new KeyValuePair <object, object>(tmp[0], tmp[1])); } else { KeyValuePair <object, object> e = new KeyValuePair <object, object>(tmp, null); objects.Add(e); } if (s != null) { s.Decrease(); } break; case (byte)BerType.ObjectIdentifier: GXAsn1ObjectIdentifier oi = new GXAsn1ObjectIdentifier(bb, len); objects.Add(oi); if (s != null) { string str = oi.Description; if (str != null) { s.AppendComment(connectPos, str); } s.Append(oi.ToString()); } break; case (byte)BerType.PrintableString: objects.Add(bb.GetString(len)); if (s != null) { s.Append(Convert.ToString(objects[objects.Count - 1])); } break; case (byte)BerType.Utf8StringTag: objects.Add(new GXAsn1Utf8String(bb.GetString(bb.Position, len))); bb.Position = bb.Position + len; if (s != null) { s.Append(Convert.ToString(objects[objects.Count - 1])); } break; case (byte)BerType.Ia5String: objects.Add(new GXAsn1Ia5String(bb.GetString(len))); if (s != null) { s.Append(Convert.ToString(objects[objects.Count - 1])); } break; case (byte)BerType.Integer: if (len == 1) { objects.Add(bb.GetInt8()); } else if (len == 2) { objects.Add(bb.GetInt16()); } else if (len == 4) { objects.Add(bb.GetInt32()); } else { tmp2 = new byte[len]; bb.Get(tmp2); objects.Add(new GXAsn1Integer(tmp2)); } if (s != null) { s.Append(Convert.ToString(objects[objects.Count - 1])); } break; case (byte)BerType.Null: objects.Add(null); break; case (byte)BerType.BitString: GXAsn1BitString tmp3 = new GXAsn1BitString(bb.SubArray(bb.Position, len)); objects.Add(tmp3); bb.Position = bb.Position + len; if (s != null) { // Append comment. s.AppendComment(connectPos, Convert.ToString(tmp3.Length) + " bit."); s.Append(tmp3.asString()); } break; case (byte)BerType.UtcTime: tmp2 = new byte[len]; bb.Get(tmp2); objects.Add(GetUtcTime(ASCIIEncoding.ASCII.GetString(tmp2))); if (s != null) { s.Append(((DateTimeOffset)objects[objects.Count - 1]).UtcDateTime.ToString("yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture)); } break; case (byte)BerType.GeneralizedTime: tmp2 = new byte[len]; bb.Get(tmp2); objects.Add(GXCommon.GetGeneralizedTime(ASCIIEncoding.ASCII.GetString(tmp2))); if (s != null) { s.Append(Convert.ToString(objects[objects.Count - 1])); } break; case (byte)BerType.Context: case (byte)BerType.Context | 1: case (byte)BerType.Context | 2: case (byte)BerType.Context | 3: tmp = new GXAsn1Context() { Constructed = false, Index = type & 0xF }; tmp2 = new byte[len]; bb.Get(tmp2); tmp.Add(tmp2); objects.Add(tmp); if (s != null) { s.Append(GXCommon.ToHex(tmp2, false)); } break; case (byte)BerType.OctetString: int t = bb.GetUInt8(bb.Position); switch (t) { case (byte)(BerType.Constructed | BerType.Sequence): case (byte)BerType.BitString: if (s != null) { s.Increase(); } GetValue(bb, objects, s); if (s != null) { s.Decrease(); } break; default: tmp2 = new byte[len]; bb.Get(tmp2); objects.Add(tmp2); if (s != null) { s.Append(GXCommon.ToHex(tmp2, false)); } break; } break; case (byte)BerType.Boolean: bool b = bb.GetUInt8() != 0; objects.Add(b); if (s != null) { s.Append(Convert.ToString(b)); } break; default: throw new System.ArgumentException("Invalid type: " + type); } if (s != null) { s.Append("</" + tagString + ">\r\n"); } }
/// <summary> /// Parse User Information from PDU. /// </summary> private static void ParseUserInformation(GXDLMSSettings settings, GXICipher cipher, GXByteBuffer data) { byte len = data.GetUInt8(); if (data.Size - data.Position < len) { throw new Exception("Not enough data."); } //Excoding the choice for user information int tag = data.GetUInt8(); if (tag != 0x4) { throw new Exception("Invalid tag."); } len = data.GetUInt8(); //Tag for xDLMS-Initate.response tag = data.GetUInt8(); if (tag == GXCommon.InitialResponceGlo) { --data.Position; cipher.Security = cipher.Decrypt(settings.SourceSystemTitle, data); tag = data.GetUInt8(); } else if (tag == GXCommon.InitialRequestGlo) { --data.Position; cipher.Security = cipher.Decrypt(settings.SourceSystemTitle, data); tag = data.GetUInt8(); } bool response = tag == GXCommon.InitialResponce; if (response) { //Optional usage field of the negotiated quality of service component tag = data.GetUInt8(); if (tag != 0)//Skip if used. { len = data.GetUInt8(); data.Position += len; } } else if (tag == GXCommon.InitialRequest) { //Optional usage field of the negotiated quality of service component tag = data.GetUInt8(); //CtoS. if (tag != 0) { len = data.GetUInt8(); settings.CtoSChallenge = new byte[len]; data.Get(settings.CtoSChallenge); } //Optional usage field of the negotiated quality of service component tag = data.GetUInt8(); if (tag != 0)//Skip if used. { len = data.GetUInt8(); data.Position += len; } //Optional usage field of the proposed quality of service component tag = data.GetUInt8(); if (tag != 0)//Skip if used. { len = data.GetUInt8(); data.Position += len; } } else { throw new Exception("Invalid tag."); } //Get DLMS version number. if (settings.IsServer) { settings.DlmsVersionNumber = data.GetUInt8(); } else { if (data.GetUInt8() != 6) { throw new Exception("Invalid DLMS version number."); } } //Tag for conformance block tag = data.GetUInt8(); if (tag != 0x5F) { throw new Exception("Invalid tag."); } //Old Way... if (data.GetUInt8(data.Position) == 0x1F) { data.GetUInt8(); } len = data.GetUInt8(); //The number of unused bits in the bit string. tag = data.GetUInt8(); if (settings.UseLogicalNameReferencing) { if (settings.IsServer) { //Skip settings what client asks. //All server settings are always returned. byte[] tmp = new byte[3]; data.Get(tmp); } else { data.Get(settings.LnSettings.ConformanceBlock); } } else { if (settings.IsServer) { //Skip settings what client asks. //All server settings are always returned. byte[] tmp = new byte[3]; data.Get(tmp); } else { data.Get(settings.SnSettings.ConformanceBlock); } } if (settings.IsServer) { data.GetUInt16(); } else { settings.MaxReceivePDUSize = data.GetUInt16(); } if (response) { //VAA Name tag = data.GetUInt16(); if (tag == 0x0007) { // If LN if (!settings.UseLogicalNameReferencing) { throw new ArgumentException("Invalid VAA."); } } else if (tag == 0xFA00) { // If SN if (settings.UseLogicalNameReferencing) { throw new ArgumentException("Invalid VAA."); } } else { // Unknown VAA. throw new ArgumentException("Invalid VAA."); } } }
/// <summary> /// Decrypt data. /// </summary> /// <param name="p">Decryption parameters</param> /// <returns>Decrypted data.</returns> public static byte[] DecryptAesGcm(AesGcmParameter p, GXByteBuffer data) { if (data == null || data.Size < 2) { throw new ArgumentOutOfRangeException("cryptedData"); } byte[] tmp; int len; Command cmd = (Command)data.GetUInt8(); switch (cmd) { case Command.GeneralGloCiphering: case Command.GeneralDedCiphering: len = GXCommon.GetObjectCount(data); if (len != 0) { p.SystemTitle = new byte[len]; data.Get(p.SystemTitle); if (p.Xml != null && p.Xml.Comments) { p.Xml.AppendComment(GXCommon.SystemTitleToString(Standard.DLMS, p.SystemTitle, true)); } } if (p.SystemTitle == null || p.SystemTitle.Length != 8) { if (p.Xml == null) { throw new ArgumentNullException("Invalid sender system title."); } else { p.Xml.AppendComment("Invalid sender system title."); } } break; case Command.GeneralCiphering: case Command.GloInitiateRequest: case Command.GloInitiateResponse: case Command.GloReadRequest: case Command.GloReadResponse: case Command.GloWriteRequest: case Command.GloWriteResponse: case Command.GloGetRequest: case Command.GloGetResponse: case Command.GloSetRequest: case Command.GloSetResponse: case Command.GloMethodRequest: case Command.GloMethodResponse: case Command.GloEventNotification: case Command.DedInitiateRequest: case Command.DedInitiateResponse: case Command.DedGetRequest: case Command.DedGetResponse: case Command.DedSetRequest: case Command.DedSetResponse: case Command.DedMethodRequest: case Command.DedMethodResponse: case Command.DedEventNotification: case Command.DedReadRequest: case Command.DedReadResponse: case Command.DedWriteRequest: case Command.DedWriteResponse: case Command.GloConfirmedServiceError: case Command.DedConfirmedServiceError: break; default: throw new ArgumentOutOfRangeException("cryptedData"); } int value = 0; GXPrivateKey key = null; GXPublicKey pub = null; GXByteBuffer transactionId = null; if (cmd == Command.GeneralCiphering) { transactionId = new GXByteBuffer(); len = GXCommon.GetObjectCount(data); GXCommon.SetObjectCount(len, transactionId); transactionId.Set(data, len); p.TransactionId = transactionId.GetUInt64(1); len = GXCommon.GetObjectCount(data); if (len != 0) { tmp = new byte[len]; data.Get(tmp); p.SystemTitle = tmp; } if (p.SystemTitle == null || p.SystemTitle.Length != 8) { if (p.Xml == null) { throw new ArgumentNullException("Invalid sender system title."); } else { p.Xml.AppendComment("Invalid sender system title."); } } len = GXCommon.GetObjectCount(data); tmp = new byte[len]; data.Get(tmp); p.RecipientSystemTitle = tmp; // Get date time. len = GXCommon.GetObjectCount(data); if (len != 0) { tmp = new byte[len]; data.Get(tmp); p.DateTime = tmp; } // other-information len = data.GetUInt8(); if (len != 0) { tmp = new byte[len]; data.Get(tmp); p.OtherInformation = tmp; } // KeyInfo OPTIONAL len = data.GetUInt8(); // AgreedKey CHOICE tag. data.GetUInt8(); // key-parameters len = data.GetUInt8(); value = data.GetUInt8(); p.KeyParameters = value; if (value == (int)KeyAgreementScheme.OnePassDiffieHellman) { // key-ciphered-data len = GXCommon.GetObjectCount(data); GXByteBuffer bb = new GXByteBuffer(); bb.Set(data, len); if (p.Xml != null) { p.KeyCipheredData = bb.Array(); //Find key agreement key using subject. string subject = GXAsn1Converter.SystemTitleToSubject(p.SystemTitle); foreach (KeyValuePair <GXPkcs8, GXx509Certificate> it in p.Settings.Keys) { if (it.Key != null && it.Value.KeyUsage == ASN.Enums.KeyUsage.KeyAgreement && it.Value.Subject.Contains(subject)) { key = it.Key.PrivateKey; //Get recipient Ephemeral public key. subject = GXAsn1Converter.SystemTitleToSubject(p.RecipientSystemTitle); foreach (KeyValuePair <GXPkcs8, GXx509Certificate> it2 in p.Settings.Keys) { if (it2.Value != null && it2.Value.KeyUsage == ASN.Enums.KeyUsage.KeyAgreement && it2.Value.Subject.Contains(subject)) { pub = it2.Value.PublicKey; break; } } break; } } if (key == null) { //Find key agreement key using subject. subject = GXAsn1Converter.SystemTitleToSubject(p.RecipientSystemTitle); foreach (KeyValuePair <GXPkcs8, GXx509Certificate> it in p.Settings.Keys) { if (it.Key != null && it.Value.KeyUsage == ASN.Enums.KeyUsage.KeyAgreement && it.Value.Subject.Contains(subject)) { key = it.Key.PrivateKey; break; } } } } else { key = p.Settings.Cipher.KeyAgreementKeyPair.Key; } if (key != null && pub == null) { //Get Ephemeral public key. int keySize = len / 2; pub = GXPublicKey.FromRawBytes(bb.SubArray(0, keySize)); } } else if (value == (int)KeyAgreementScheme.StaticUnifiedModel) { len = GXCommon.GetObjectCount(data); if (len != 0) { throw new ArgumentException("Invalid key parameters"); } if (p.Xml != null) { //Find key agreement key using subject. string subject = GXAsn1Converter.SystemTitleToSubject(p.RecipientSystemTitle); foreach (KeyValuePair <GXPkcs8, GXx509Certificate> it in p.Settings.Keys) { if (it.Value.KeyUsage == ASN.Enums.KeyUsage.KeyAgreement && it.Value.Subject.Contains(subject)) { key = it.Key.PrivateKey; break; } } if (key != null) { //Find key agreement key using subject. subject = GXAsn1Converter.SystemTitleToSubject(p.Settings.SourceSystemTitle); foreach (KeyValuePair <GXPkcs8, GXx509Certificate> it in p.Settings.Keys) { if (it.Value.KeyUsage == ASN.Enums.KeyUsage.KeyAgreement && it.Value.Subject.Contains(subject)) { pub = it.Value.PublicKey; break; } } } } else { key = p.Settings.Cipher.KeyAgreementKeyPair.Key; pub = p.Settings.Cipher.KeyAgreementKeyPair.Value; } } else { throw new ArgumentException("key-parameters"); } } len = GXCommon.GetObjectCount(data); if (len > data.Available) { throw new Exception("Not enought data."); } p.CipheredContent = data.Remaining(); byte sc = data.GetUInt8(); p.SecuritySuite = (SecuritySuite)(sc & 0x3); p.Security = (Security)(sc & 0x30); if ((sc & 0x80) != 0) { System.Diagnostics.Debug.WriteLine("Compression is used."); } if ((sc & 0x40) != 0) { System.Diagnostics.Debug.WriteLine("Error: Key_Set is used."); } if ((sc & 0x20) != 0) { System.Diagnostics.Debug.WriteLine("Encryption is applied."); } if (key != null) { if (value == (int)KeyAgreementScheme.OnePassDiffieHellman) { GXEcdsa c = new GXEcdsa(key); //Get Ephemeral signing key and verify it. byte[] z = c.GenerateSecret(pub); System.Diagnostics.Debug.WriteLine("Originator ephemeral public key: " + pub.ToHex()); System.Diagnostics.Debug.WriteLine("Recipient private agreement key: " + key.ToHex()); System.Diagnostics.Debug.WriteLine("Shared secret:" + GXCommon.ToHex(z, true)); GXByteBuffer kdf = new GXByteBuffer(); kdf.Set(GXSecure.GenerateKDF(p.SecuritySuite, z, p.SecuritySuite == SecuritySuite.Ecdsa256 ? AlgorithmId.AesGcm128 : AlgorithmId.AesGcm256, p.SystemTitle, p.RecipientSystemTitle, null, null)); System.Diagnostics.Debug.WriteLine("KDF:" + kdf.ToString()); p.BlockCipherKey = kdf.SubArray(0, 16); } else if (value == (int)KeyAgreementScheme.StaticUnifiedModel) { GXEcdsa c = new GXEcdsa(key); byte[] z = c.GenerateSecret(pub); System.Diagnostics.Debug.WriteLine("Shared secret:" + GXCommon.ToHex(z, true)); GXByteBuffer kdf = new GXByteBuffer(); kdf.Set(GXSecure.GenerateKDF(p.SecuritySuite, z, p.SecuritySuite == SecuritySuite.Ecdsa256 ? AlgorithmId.AesGcm128 : AlgorithmId.AesGcm256, p.SystemTitle, transactionId.Array(), p.RecipientSystemTitle, null)); System.Diagnostics.Debug.WriteLine("KDF:" + kdf.ToString()); p.BlockCipherKey = kdf.SubArray(0, 16); } else { throw new ArgumentOutOfRangeException("Invalid Key-id value."); } } UInt32 invocationCounter = data.GetUInt32(); p.InvocationCounter = invocationCounter; System.Diagnostics.Debug.WriteLine("Decrypt settings: " + p.ToString()); System.Diagnostics.Debug.WriteLine("Encrypted: " + GXCommon.ToHex(data.Data, false, data.Position, data.Size - data.Position)); byte[] tag = new byte[12]; byte[] encryptedData; int length; if (p.Security == Security.Authentication) { length = data.Size - data.Position - 12; encryptedData = new byte[length]; data.Get(encryptedData); data.Get(tag); // Check tag. EncryptAesGcm(p, encryptedData); if (!GXDLMSChipperingStream.TagsEquals(tag, p.CountTag)) { if (p.Xml == null) { throw new GXDLMSException("Decrypt failed. Invalid tag."); } else { p.Xml.AppendComment("Decrypt failed. Invalid tag."); } } return(encryptedData); } byte[] ciphertext = null; if (p.Security == Security.Encryption) { length = data.Size - data.Position; ciphertext = new byte[length]; data.Get(ciphertext); } else if (p.Security == Security.AuthenticationEncryption) { length = data.Size - data.Position - 12; ciphertext = new byte[length]; data.Get(ciphertext); data.Get(tag); } byte[] aad = GetAuthenticatedData(p, ciphertext), iv = GetNonse(invocationCounter, p.SystemTitle); GXDLMSChipperingStream gcm = new GXDLMSChipperingStream(p.Security, true, p.BlockCipherKey, aad, iv, tag); gcm.Write(ciphertext); byte[] decrypted = gcm.FlushFinalBlock(); System.Diagnostics.Debug.WriteLine("Decrypted: " + GXCommon.ToHex(decrypted, true)); if (p.Security != Security.Encryption) { if (!GXCommon.Compare(gcm.GetTag(), tag)) { if (p.Xml == null) { throw new Exception("Decrypt failed. Invalid authentication tag."); } p.Xml.AppendComment("Decrypt failed. Invalid authentication tag."); } } return(decrypted); }
/// <summary> /// Decrypt data. /// </summary> /// <param name="p">Decryption parameters</param> /// <returns>Decrypted data.</returns> public static byte[] DecryptAesGcm(AesGcmParameter p, GXByteBuffer data) { if (data == null || data.Size < 2) { throw new ArgumentOutOfRangeException("cryptedData"); } int len; Command cmd = (Command)data.GetUInt8(); switch (cmd) { case Command.GloGeneralCiphering: len = GXCommon.GetObjectCount(data); p.SystemTitle = new byte[len]; data.Get(p.SystemTitle); break; case Command.GloInitiateRequest: case Command.GloInitiateResponse: case Command.GloReadRequest: case Command.GloReadResponse: case Command.GloWriteRequest: case Command.GloWriteResponse: case Command.GloGetRequest: case Command.GloGetResponse: case Command.GloSetRequest: case Command.GloSetResponse: case Command.GloMethodRequest: case Command.GloMethodResponse: case Command.GloEventNotificationRequest: break; default: throw new ArgumentOutOfRangeException("cryptedData"); } len = Gurux.DLMS.Internal.GXCommon.GetObjectCount(data); p.Security = (Gurux.DLMS.Enums.Security)data.GetUInt8(); p.FrameCounter = data.GetUInt32(); System.Diagnostics.Debug.WriteLine("Decrypt settings: " + p.ToString()); System.Diagnostics.Debug.WriteLine("Encrypted: " + GXCommon.ToHex(data.Array(), true)); byte[] tag = new byte[12]; byte[] encryptedData; int length; if (p.Security == Gurux.DLMS.Enums.Security.Authentication) { length = data.Size - data.Position - 12; encryptedData = new byte[length]; data.Get(encryptedData); data.Get(tag); // Check tag. EncryptAesGcm(p, encryptedData); if (!GXDLMSChipperingStream.TagsEquals(tag, p.CountTag)) { throw new GXDLMSException("Decrypt failed. Invalid tag."); } return encryptedData; } byte[] ciphertext = null; if (p.Security == Gurux.DLMS.Enums.Security.Encryption) { length = data.Size - data.Position; ciphertext = new byte[length]; data.Get(ciphertext); } else if (p.Security == Gurux.DLMS.Enums.Security.AuthenticationEncryption) { length = data.Size - data.Position - 12; ciphertext = new byte[length]; data.Get(ciphertext); data.Get(tag); } byte[] aad = GetAuthenticatedData(p.Security, p.AuthenticationKey, data.Array()); byte[] iv = GetNonse(p.FrameCounter, p.SystemTitle); GXDLMSChipperingStream gcm = new GXDLMSChipperingStream(p.Security, true, p.BlockCipherKey, aad, iv, tag); gcm.Write(ciphertext); return gcm.FlushFinalBlock(); }
/// <summary> /// Decrypt data. /// </summary> /// <param name="p">Decryption parameters</param> /// <returns>Decrypted data.</returns> public static byte[] DecryptAesGcm(AesGcmParameter p, GXByteBuffer data) { if (data == null || data.Size < 2) { throw new ArgumentOutOfRangeException("cryptedData"); } int len; Command cmd = (Command)data.GetUInt8(); switch (cmd) { case Command.GeneralGloCiphering: len = GXCommon.GetObjectCount(data); if (len != 0) { p.SystemTitle = new byte[len]; data.Get(p.SystemTitle); } break; case Command.GloInitiateRequest: case Command.GloInitiateResponse: case Command.GloReadRequest: case Command.GloReadResponse: case Command.GloWriteRequest: case Command.GloWriteResponse: case Command.GloGetRequest: case Command.GloGetResponse: case Command.GloSetRequest: case Command.GloSetResponse: case Command.GloMethodRequest: case Command.GloMethodResponse: case Command.GloEventNotificationRequest: break; default: throw new ArgumentOutOfRangeException("cryptedData"); } len = Gurux.DLMS.Internal.GXCommon.GetObjectCount(data); p.Security = (Gurux.DLMS.Enums.Security)data.GetUInt8(); p.InvocationCounter = data.GetUInt32(); System.Diagnostics.Debug.WriteLine("Decrypt settings: " + p.ToString()); System.Diagnostics.Debug.WriteLine("Encrypted: " + GXCommon.ToHex(data.Array(), true)); byte[] tag = new byte[12]; byte[] encryptedData; int length; if (p.Security == Gurux.DLMS.Enums.Security.Authentication) { length = data.Size - data.Position - 12; encryptedData = new byte[length]; data.Get(encryptedData); data.Get(tag); // Check tag. EncryptAesGcm(p, encryptedData); if (!GXDLMSChipperingStream.TagsEquals(tag, p.CountTag)) { throw new GXDLMSException("Decrypt failed. Invalid tag."); } return(encryptedData); } byte[] ciphertext = null; if (p.Security == Gurux.DLMS.Enums.Security.Encryption) { length = data.Size - data.Position; ciphertext = new byte[length]; data.Get(ciphertext); } else if (p.Security == Gurux.DLMS.Enums.Security.AuthenticationEncryption) { length = data.Size - data.Position - 12; ciphertext = new byte[length]; data.Get(ciphertext); data.Get(tag); } byte[] aad = GetAuthenticatedData(p.Security, p.AuthenticationKey, ciphertext); byte[] iv = GetNonse(p.InvocationCounter, p.SystemTitle); GXDLMSChipperingStream gcm = new GXDLMSChipperingStream(p.Security, true, p.BlockCipherKey, aad, iv, tag); gcm.Write(ciphertext); ciphertext = gcm.FlushFinalBlock(); if (p.Security == Gurux.DLMS.Enums.Security.AuthenticationEncryption) { // Check tag. EncryptAesGcm(p, ciphertext); if (!GXDLMSChipperingStream.TagsEquals(tag, p.CountTag)) { // throw new GXDLMSException("Decrypt failed. Invalid tag."); } } return(ciphertext); }
///<summary> ///Get octect string value from DLMS data. ///</summary> ///<param name="buff"> ///Received DLMS data. ///</param> ///<param name="info"> ///Data info. ///</param> ///<returns> ///Parsed octet string value. ///</returns> private static object GetOctetString(GXByteBuffer buff, GXDataInfo info, bool knownType) { object value; int len; if (knownType) { len = buff.Size; } else { len = GXCommon.GetObjectCount(buff); // If there is not enough data available. if (buff.Size - buff.Position < len) { info.Complete = false; return null; } } byte[] tmp = new byte[len]; buff.Get(tmp); value = tmp; if (info.xml != null) { info.xml.AppendLine(info.xml.GetDataType(info.Type), "Value", GXCommon.ToHex(tmp, false)); } return value; }
private static void updatePassword(GXDLMSSettings settings, GXByteBuffer buff, GXDLMSTranslatorStructure xml) { int len = buff.GetUInt8(); // Get authentication information. if (buff.GetUInt8() != 0x80) { throw new Exception("Invalid tag."); } len = buff.GetUInt8(); if (settings.Authentication == Authentication.Low) { settings.Password = new byte[len]; buff.Get(settings.Password); } else { settings.CtoSChallenge = new byte[len]; buff.Get(settings.CtoSChallenge); } if (xml != null) { if (xml.OutputType == TranslatorOutputType.SimpleXml) { if (settings.Authentication == Authentication.Low) { xml.AppendLine(TranslatorGeneralTags.CallingAuthentication, "Value", GXCommon.ToHex(settings.Password, false)); } else { xml.AppendLine(TranslatorGeneralTags.CallingAuthentication, "Value", GXCommon.ToHex(settings.CtoSChallenge, false)); } } else { xml.AppendStartTag( TranslatorGeneralTags.CallingAuthentication); xml.AppendStartTag(TranslatorGeneralTags.CharString); if (settings.Authentication == Authentication.Low) { xml.Append(GXCommon.ToHex(settings.Password, false)); } else { xml.Append( GXCommon.ToHex(settings.CtoSChallenge, false)); } xml.AppendEndTag(TranslatorGeneralTags.CharString); xml.AppendEndTag(TranslatorGeneralTags.CallingAuthentication); } } }
///<summary> ///Parse APDU. ///</summary> static internal SourceDiagnostic ParsePDU(GXDLMSSettings settings, GXICipher cipher, GXByteBuffer buff, GXDLMSTranslatorStructure xml) { // Get AARE tag and length int tag = buff.GetUInt8(); if (settings.IsServer) { if (tag != ((byte)BerType.Application | (byte)BerType.Constructed | (byte)PduType.ProtocolVersion)) { throw new Exception("Invalid tag."); } } else { if (tag != ((byte)BerType.Application | (byte)BerType.Constructed | (byte)PduType.ApplicationContextName)) { throw new Exception("Invalid tag."); } } int len = buff.GetUInt8(); int size = buff.Size - buff.Position; if (len > size) { throw new Exception("Not enough data."); } //Opening tags if (xml != null) { if (settings.IsServer) { xml.AppendStartTag(Command.Aarq); } else { xml.AppendStartTag(Command.Aare); } } AssociationResult resultComponent = AssociationResult.Accepted; SourceDiagnostic resultDiagnosticValue = SourceDiagnostic.None; while (buff.Position < buff.Size) { tag = buff.GetUInt8(); switch (tag) { case (byte)BerType.Context | (byte)BerType.Constructed | (byte)PduType.ApplicationContextName://0xA1 if (!ParseApplicationContextName(settings, buff, xml)) { throw new GXDLMSException(AssociationResult.PermanentRejected, SourceDiagnostic.ApplicationContextNameNotSupported); } break; case (byte)BerType.Context | (byte)BerType.Constructed | (byte)PduType.CalledApTitle://0xA2 //Get len. if (buff.GetUInt8() != 3) { throw new Exception("Invalid tag."); } //Choice for result (INTEGER, universal) if (buff.GetUInt8() != (byte)BerType.Integer) { throw new Exception("Invalid tag."); } //Get len. if (buff.GetUInt8() != 1) { throw new Exception("Invalid tag."); } resultComponent = (AssociationResult)buff.GetUInt8(); if (xml != null) { xml.AppendLine(TranslatorGeneralTags.AssociationResult, "Value", xml.IntegerToHex((int)resultComponent, 2)); xml.AppendStartTag(TranslatorGeneralTags.ResultSourceDiagnostic); } break; case (byte)BerType.Context | (byte)BerType.Constructed | (byte)PduType.CalledAeQualifier:////SourceDiagnostic 0xA3 len = buff.GetUInt8(); // ACSE service user tag. tag = buff.GetUInt8(); len = buff.GetUInt8(); // Result source diagnostic component. if (buff.GetUInt8() != (byte)BerType.Integer) { throw new Exception("Invalid tag."); } if (buff.GetUInt8() != 1) { throw new Exception("Invalid tag."); } resultDiagnosticValue = (SourceDiagnostic)buff.GetUInt8(); if (xml != null) { xml.AppendLine(TranslatorGeneralTags.ACSEServiceUser, "Value", xml.IntegerToHex((int)resultDiagnosticValue, 2)); xml.AppendEndTag(TranslatorGeneralTags.ResultSourceDiagnostic); } break; case (byte)BerType.Context | (byte)BerType.Constructed | (byte)PduType.CalledApInvocationId: ////Result 0xA4 //Get len. if (buff.GetUInt8() != 0xA) { throw new Exception("Invalid tag."); } //Choice for result (Universal, Octetstring type) if (buff.GetUInt8() != (byte)BerType.OctetString) { throw new Exception("Invalid tag."); } //responding-AP-title-field //Get len. len = buff.GetUInt8(); settings.SourceSystemTitle = new byte[len]; buff.Get(settings.SourceSystemTitle); if (xml != null) { //RespondingAPTitle xml.AppendLine(TranslatorGeneralTags.RespondingAPTitle, "Value", GXCommon.ToHex(settings.SourceSystemTitle, false)); } break; //Client Challenge. case (byte)BerType.Context | (byte)BerType.Constructed | (byte)PduType.CallingApTitle://0xA6 len = buff.GetUInt8(); tag = buff.GetUInt8(); len = buff.GetUInt8(); settings.SourceSystemTitle = new byte[len]; buff.Get(settings.SourceSystemTitle); if (xml != null) { //CallingAPTitle xml.AppendLine(TranslatorGeneralTags.CallingAPTitle, "Value", GXCommon.ToHex(settings.SourceSystemTitle, false)); } break; //Server Challenge. case (byte)BerType.Context | (byte)BerType.Constructed | (byte)PduType.SenderAcseRequirements://0xAA len = buff.GetUInt8(); tag = buff.GetUInt8(); len = buff.GetUInt8(); settings.StoCChallenge = new byte[len]; buff.Get(settings.StoCChallenge); AppendServerSystemTitleToXml(settings, xml, tag); break; case (byte)BerType.Context | (byte)PduType.SenderAcseRequirements: //0x8A case (byte)BerType.Context | (byte)PduType.CallingApInvocationId: //0x88 //Get sender ACSE-requirements field component. if (buff.GetUInt8() != 2) { throw new Exception("Invalid tag."); } if (buff.GetUInt8() != (byte)BerType.ObjectDescriptor) { throw new Exception("Invalid tag."); } if (buff.GetUInt8() != 0x80) { throw new Exception("Invalid tag."); } //SenderACSERequirements if (xml != null) { xml.AppendLine(tag, "Value", "1"); } break; case (byte)BerType.Context | (byte)PduType.MechanismName://0x8B case (byte)BerType.Context | (byte)PduType.CallingAeInvocationId://0x89 UpdateAuthentication(settings, buff); if (xml != null) { if (xml.OutputType == TranslatorOutputType.SimpleXml) { xml.AppendLine(tag, "Value", settings.Authentication.ToString()); } else { xml.AppendLine(tag, "Value", ((int)settings.Authentication).ToString()); } } break; case (byte)BerType.Context | (byte)BerType.Constructed | (byte)PduType.CallingAuthenticationValue://0xAC updatePassword(settings, buff, xml); break; case (byte)BerType.Context | (byte)BerType.Constructed | (byte)PduType.UserInformation: //0xBE if (xml == null && resultComponent != AssociationResult.Accepted && resultDiagnosticValue != SourceDiagnostic.None) { throw new GXDLMSException(resultComponent, resultDiagnosticValue); } ParseUserInformation(settings, cipher, buff, xml); break; default: //Unknown tags. System.Diagnostics.Debug.WriteLine("Unknown tag: " + tag + "."); if (buff.Position < buff.Size) { len = buff.GetUInt8(); buff.Position += (UInt16)len; } break; } } //Closing tags if (xml != null) { if (settings.IsServer) { xml.AppendEndTag(Command.Aarq); } else { xml.AppendEndTag(Command.Aare); } } return resultDiagnosticValue; }
/// <summary> /// Parse User Information from PDU. /// </summary> public static void ParseUserInformation(GXDLMSSettings settings, GXICipher cipher, GXByteBuffer data, GXDLMSTranslatorStructure xml) { byte len = data.GetUInt8(); GXByteBuffer tmp2 = new GXByteBuffer(); tmp2.SetUInt8(0); if (data.Size - data.Position < len) { throw new Exception("Not enough data."); } if (xml != null && xml.OutputType == TranslatorOutputType.StandardXml) { len = (byte)(data.Size - data.Position); xml.AppendLine(Command.InitiateRequest, null, GXCommon .ToHex(data.Data, false, data.Position, len)); data.Position = data.Position + len; return; } //Excoding the choice for user information int tag = data.GetUInt8(); if (tag != 0x4) { throw new Exception("Invalid tag."); } len = data.GetUInt8(); //Tag for xDLMS-Initate.response tag = data.GetUInt8(); if (tag == (byte)Command.GloInitiateResponse) { if (xml != null) { int cnt = GXCommon.GetObjectCount(data); byte[] tmp = new byte[cnt]; data.Get(tmp); //<glo_InitiateResponse> xml.AppendLine(Command.GloInitiateResponse, "Value", GXCommon.ToHex(tmp, false)); return; } --data.Position; cipher.Security = cipher.Decrypt(settings.SourceSystemTitle, data); tag = data.GetUInt8(); } else if (tag == (byte)Command.GloInitiateRequest) { if (xml != null) { int cnt = GXCommon.GetObjectCount(data); byte[] tmp = new byte[cnt]; data.Get(tmp); //<glo_InitiateRequest> xml.AppendLine(Command.GloInitiateRequest, "Value", GXCommon.ToHex(tmp, false)); return; } --data.Position; cipher.Security = cipher.Decrypt(settings.SourceSystemTitle, data); tag = data.GetUInt8(); } bool response = tag == (byte)Command.InitiateResponse; if (response) { if (xml != null) { //<InitiateResponse> xml.AppendStartTag(Command.InitiateResponse); } //Optional usage field of the negotiated quality of service component tag = data.GetUInt8(); len = 0; if (tag != 0)//Skip if used. { len = data.GetUInt8(); data.Position += len; if (len == 0 && xml != null) { //NegotiatedQualityOfService xml.AppendLine(TranslatorGeneralTags.NegotiatedQualityOfService, "Value", "00"); } } } else if (tag == (byte)Command.InitiateRequest) { if (xml != null) { //<InitiateRequest> xml.AppendStartTag(Command.InitiateRequest); } //Optional usage field of the negotiated quality of service component tag = data.GetUInt8(); //CtoS. if (tag != 0) { len = data.GetUInt8(); settings.CtoSChallenge = new byte[len]; data.Get(settings.CtoSChallenge); } //Optional usage field of the negotiated quality of service component tag = data.GetUInt8(); if (tag != 0)//Skip if used. { len = data.GetUInt8(); data.Position += len; } //Optional usage field of the proposed quality of service component tag = data.GetUInt8(); if (tag != 0)//Skip if used. { len = data.GetUInt8(); data.Position += len; } } else { throw new Exception("Invalid tag."); } //Get DLMS version number. if (!response) { if (data.GetUInt8() != 6) { throw new Exception("Invalid DLMS version number."); } //ProposedDlmsVersionNumber if (xml != null) { xml.AppendLine(TranslatorGeneralTags.ProposedDlmsVersionNumber, "Value", xml.IntegerToHex(settings.DLMSVersion, 2)); } } else { if (data.GetUInt8() != 6) { throw new Exception("Invalid DLMS version number."); } if (xml != null) { xml.AppendLine(TranslatorGeneralTags.NegotiatedDlmsVersionNumber, "Value", xml.IntegerToHex(settings.DLMSVersion, 2)); } } //Tag for conformance block tag = data.GetUInt8(); if (tag != 0x5F) { throw new Exception("Invalid tag."); } //Old Way... if (data.GetUInt8(data.Position) == 0x1F) { data.GetUInt8(); } len = data.GetUInt8(); //The number of unused bits in the bit string. tag = data.GetUInt8(); if (!response) { //ProposedConformance if (xml != null) { xml.AppendStartTag(TranslatorGeneralTags.ProposedConformance); } data.Get(settings.ConformanceBlock); tmp2.Set(settings.ConformanceBlock); } else { //NegotiatedConformance if (xml != null) { xml.AppendStartTag(TranslatorGeneralTags.NegotiatedConformance); } if (settings.UseLogicalNameReferencing) { data.Get(settings.LnSettings.ConformanceBlock); tmp2.Set(settings.LnSettings.ConformanceBlock); } else { data.Get(settings.SnSettings.ConformanceBlock); tmp2.Set(settings.SnSettings.ConformanceBlock); } } if (xml != null) { GetConformance(tmp2.GetUInt32(), xml); } if (!response) { //Proposed max PDU size. settings.MaxPduSize = data.GetUInt16(); if (xml != null) { //ProposedConformance closing xml.AppendEndTag(TranslatorGeneralTags.ProposedConformance); //ProposedMaxPduSize xml.AppendLine(TranslatorGeneralTags.ProposedMaxPduSize, "Value", xml.IntegerToHex(settings.MaxPduSize, 4)); } //If client asks too high PDU. if (settings.MaxPduSize > settings.MaxServerPDUSize) { settings.MaxPduSize = settings.MaxServerPDUSize; } } else { //Max PDU size. settings.MaxPduSize = data.GetUInt16(); if (xml != null) { //NegotiatedConformance closing xml.AppendEndTag(TranslatorGeneralTags.NegotiatedConformance); //NegotiatedMaxPduSize xml.AppendLine(TranslatorGeneralTags.NegotiatedMaxPduSize, "Value", xml.IntegerToHex(settings.MaxPduSize, 4)); } } if (response) { //VAA Name tag = data.GetUInt16(); if (xml != null) { xml.AppendLine(TranslatorGeneralTags.VaaName, "Value", xml.IntegerToHex(tag, 4)); } if (tag == 0x0007) { // If LN if (!settings.UseLogicalNameReferencing) { throw new ArgumentException("Invalid VAA."); } } else if (tag == 0xFA00) { // If SN if (settings.UseLogicalNameReferencing) { throw new ArgumentException("Invalid VAA."); } } else { // Unknown VAA. throw new ArgumentException("Invalid VAA."); } if (xml != null) { //<InitiateResponse> xml.AppendEndTag(Command.InitiateResponse); } } else if (xml != null) { //</InitiateRequest> xml.AppendEndTag(Command.InitiateRequest); } }
/// <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); }
/// <summary> /// Decrypt data. /// </summary> /// <param name="p">Decryption parameters</param> /// <returns>Decrypted data.</returns> public static byte[] DecryptAesGcm(AesGcmParameter p, GXByteBuffer data) { if (data == null || data.Size < 2) { throw new ArgumentOutOfRangeException("cryptedData"); } Command cmd = (Command)data.GetUInt8(); if (!((byte)cmd == 0x21 || (byte)cmd == 0x28 || cmd == Command.GloGetRequest || cmd == Command.GloGetResponse || cmd == Command.GloSetRequest || cmd == Command.GloSetResponse || cmd == Command.GloMethodRequest || cmd == Command.GloMethodResponse)) { throw new ArgumentOutOfRangeException("cryptedData"); } int len = Gurux.DLMS.Internal.GXCommon.GetObjectCount(data); p.Security = (Security)data.GetUInt8(); p.FrameCounter = data.GetUInt32(); System.Diagnostics.Debug.WriteLine("Decrypt settings: " + p.ToString()); System.Diagnostics.Debug.WriteLine("Encrypted: " + GXCommon.ToHex(data.Array(), true)); byte[] tag = new byte[12]; byte[] encryptedData; int length; if (p.Security == Security.Authentication) { length = data.Size - data.Position - 12; encryptedData = new byte[length]; data.Get(encryptedData); data.Get(tag); // Check tag. EncryptAesGcm(p, encryptedData); if (!GXDLMSChipperingStream.TagsEquals(tag, p.CountTag)) { throw new GXDLMSException("Decrypt failed. Invalid tag."); } return(encryptedData); } byte[] ciphertext = null; if (p.Security == Security.Encryption) { length = data.Size - data.Position; ciphertext = new byte[length]; data.Get(ciphertext); } else if (p.Security == Security.AuthenticationEncryption) { length = data.Size - data.Position - 12; ciphertext = new byte[length]; data.Get(ciphertext); data.Get(tag); } byte[] aad = GetAuthenticatedData(p.Security, p.AuthenticationKey, data.Array()); GXDLMSChipperingStream gcm = new GXDLMSChipperingStream(p.Security, false, p.BlockCipherKey, aad, GetNonse(p.FrameCounter, p.SystemTitle), tag); gcm.Write(ciphertext); return(gcm.FlushFinalBlock()); }