public static byte[] reverseAuthenticatedEncryption(CosemParameters parameters, CosemConnection connection, byte[] data) { switch (data[0] & 0xff) { case SC_AUTHENTICATION: byte[] aux = helper.extensions.copyOfRange(data, 1, 5); connection.serverInvocationCounter = BitConverter.ToInt32(new byte[] { (aux[3]), (aux[2]), (aux[1]), (aux[0]) }, 0); return(aesGcmReverse(new byte[0], helper.extensions.copyOfRange(data, 5, data.Length), parameters, connection)); case SC_AUTHENTICATION_ENCRYPTION: byte[] authData = new byte[parameters.ak.Length + 1]; authData[0] = SC_AUTHENTICATION_ENCRYPTION; Array.Copy(parameters.ak, 0, authData, 1, parameters.ak.Length); aux = helper.extensions.copyOfRange(data, 1, 5); connection.serverInvocationCounter = BitConverter.ToInt32(new byte[] { (aux[3]), (aux[2]), (aux[1]), (aux[0]) }, 0); return(aesGcmReverse(helper.extensions.copyOfRange(data, 5, data.Length), authData, parameters, connection)); case SC_ENCRYPTION: aux = helper.extensions.copyOfRange(data, 1, 5); connection.serverInvocationCounter = BitConverter.ToInt32(new byte[] { (aux[3]), (aux[2]), (aux[1]), (aux[0]) }, 0); return(aesGcmReverse(helper.extensions.copyOfRange(data, 5, data.Length), new byte[0], parameters, connection)); default: return(data); } }
private static byte[] generateInitiateRequest(CosemParameters parameters) { System.IO.MemoryStream stream = new System.IO.MemoryStream(); stream.WriteByte(Constants.xDlmsApdu.NoCiphering.INITIATE_REQUEST); stream.WriteByte(0); //Dedicated key stream.WriteByte(0); //Response-allowed stream.WriteByte(0); //Proposed quality of service stream.WriteByte(Constants.DLMS_VERSION); //Dlms version stream.WriteByte(Constants.ConformanceBlock.TAG); //Conformance block tag byte[] conformance = generateConformanceBlock(parameters); stream.WriteByte((byte)(conformance.Length)); stream.Write(conformance, 0, conformance.Length); //Conformance block //stream.WriteByte(ByteBuffer.allocate(2).putShort(parameters.maxPduSize).array()); stream.WriteByte((byte)(parameters.maxPduSize >> 8)); //Max pdu size stream.WriteByte((byte)(parameters.maxPduSize)); System.IO.MemoryStream stream2 = new System.IO.MemoryStream(); if (parameters.securityType != SecurityType.NONE) { stream2.WriteByte(Constants.xDlmsApdu.GlobalCiphering.INITIATE_REQUEST); byte[] data = Security.authenticatedEncryption(parameters, stream.ToArray()); stream2.WriteByte((byte)(data.Length)); stream2.Write(data, 0, data.Length); } else { //stream2.WriteByte(stream.ToArray()); byte[] aux = stream.ToArray(); stream2.Write(aux, 0, aux.Length); } return(stream2.ToArray()); }
public static byte[] authenticatedEncryption(CosemParameters parameters, byte[] data) { if (parameters.securityType == SecurityType.NONE) { return(data); } int ivCounter = parameters.getInvocationCounter(); int sc = 0; switch (parameters.securityType) { case SecurityType.AUTHENTICATION: sc = SC_AUTHENTICATION; byte[] authData = new byte[parameters.ak.Length + data.Length + 1]; authData[0] = SC_AUTHENTICATION; Array.Copy(parameters.ak, 0, authData, 1, parameters.ak.Length); Array.Copy(data, 0, authData, parameters.ak.Length + 1, data.Length); byte[] mac = aesGcm(new byte[0], authData, parameters, ivCounter); byte[] data_ = new byte[data.Length + mac.Length]; Array.Copy(data, 0, data_, 0, data.Length); Array.Copy(mac, 0, data_, data.Length, mac.Length); data = data_; break; case SecurityType.AUTHENTICATION_ENCRYPTION: sc = SC_AUTHENTICATION_ENCRYPTION; authData = new byte[parameters.ak.Length + 1]; authData[0] = SC_AUTHENTICATION_ENCRYPTION; Array.Copy(parameters.ak, 0, authData, 1, parameters.ak.Length); data = aesGcm(data, authData, parameters, ivCounter); break; case SecurityType.ENCRYPTION: sc = SC_ENCRYPTION; data = aesGcm(data, new byte[0], parameters, ivCounter); break; default: throw new System.InvalidOperationException(); } try { System.IO.MemoryStream stream = new System.IO.MemoryStream(); stream.WriteByte((byte)sc); stream.WriteByte((byte)(ivCounter >> 24)); stream.WriteByte((byte)(ivCounter >> 16)); stream.WriteByte((byte)(ivCounter >> 8)); stream.WriteByte((byte)(ivCounter)); stream.Write(data, 0, data.Length); return(stream.ToArray()); } catch (IOException) { throw new DlmsException(DlmsException.DlmsExceptionReason.INTERNAL_ERROR); } }
internal static byte[] generateChallanger(CosemParameters parameters) { byte[] random = new byte[parameters.challengerSize]; lock (randomLocker) { sr.NextBytes(random); } return(random); }
internal static bool verifyChallenger(CosemParameters parameters, CosemConnection connection, byte[] data) { if (data == null || data.Length == 0) { return(false); } try { byte[] calculated = new byte[0]; switch (parameters.authenticationType.innerEnumValue) { case AuthenticationType.InnerEnum.PUBLIC: case AuthenticationType.InnerEnum.LLS: throw new System.InvalidOperationException(); case AuthenticationType.InnerEnum.HLS: calculated = aes128(connection.challengeServerToClient, parameters.llsHlsSecret); break; case AuthenticationType.InnerEnum.HLS_MD5: calculated = md5(connection.challengeClientToServer, parameters.llsHlsSecret); break; case AuthenticationType.InnerEnum.HLS_SHA1: calculated = sha1(connection.challengeClientToServer, parameters.llsHlsSecret); break; case AuthenticationType.InnerEnum.HLS_GMAC: if (data[0] != SC_AUTHENTICATION) { return(false); } System.IO.MemoryStream stream = new System.IO.MemoryStream(); stream.WriteByte(SC_AUTHENTICATION); stream.Write(parameters.ak, 0, parameters.ak.Length); stream.Write(connection.challengeClientToServer, 0, connection.challengeClientToServer.Length); //connection.serverInvocationCounter = BitConverter.ToInt32(helper.extensions.copyOfRange(data, 1, 5), 0); var aux = helper.extensions.copyOfRange(data, 1, 5); connection.serverInvocationCounter = BitConverter.ToInt32(new byte[] { (aux[3]), (aux[2]), (aux[1]), (aux[0]) }, 0); data = helper.extensions.copyOfRange(data, 5, data.Length); CosemParameters cosemParams = new CosemParameters(); cosemParams.setSystemTitle(connection.serverSysTitle); cosemParams.setEk(parameters.ek); calculated = Security.aesGcm(new byte[0], stream.ToArray(), cosemParams, connection.serverInvocationCounter); break; default: throw new System.ArgumentException(); } return(Enumerable.SequenceEqual(data, calculated)); } catch (IOException) { throw new DlmsException(DlmsException.DlmsExceptionReason.INTERNAL_ERROR); } }
public static byte[] aesGcmReverse(byte[] encrypted, byte[] authData, CosemParameters parameters, CosemConnection connection) { try { byte[] iv = getIv(connection.serverSysTitle, connection.serverInvocationCounter); lock (cipherLocker) { using (var cipherStream = new MemoryStream(encrypted)) using (var cipherReader = new BinaryReader(cipherStream)) { var cipher = new GcmBlockCipher(new AesEngine()); cipher.Init(false, new AeadParameters(new KeyParameter(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }), MacBitSize, iv)); var cipherText = cipherReader.ReadBytes(encrypted.Length); var plainText = new byte[cipher.GetOutputSize(cipherText.Length)]; var len = cipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0); cipher.ProcessAadBytes(authData, 0, authData.Length); cipher.DoFinal(plainText, len); return(plainText); } } } catch (InvalidKeyException e) { Console.WriteLine(e.ToString()); Console.Write(e.StackTrace); } //catch (InvalidAlgorithmParameterException e) catch (InvalidParameterException e) { Console.WriteLine(e.ToString()); Console.Write(e.StackTrace); } //catch (IllegalBlockSizeException e) catch (CryptographicException e) { Console.WriteLine(e.ToString()); Console.Write(e.StackTrace); } //catch (AEADBadTagException) //{ // throw new DlmsException(DlmsException.DlmsExceptionReason.SECURITY_FAIL); //} //catch (BadPaddingException e) //{ // Console.WriteLine(e.ToString()); // Console.Write(e.StackTrace); //} throw new DlmsException(DlmsException.DlmsExceptionReason.INTERNAL_ERROR); }
internal static byte[] processChallanger(CosemParameters parameters, CosemConnection connection) { try { switch (parameters.authenticationType.innerEnumValue) { case AuthenticationType.InnerEnum.PUBLIC: case AuthenticationType.InnerEnum.LLS: throw new System.InvalidOperationException(); case AuthenticationType.InnerEnum.HLS: return(aes128(connection.challengeServerToClient, parameters.llsHlsSecret)); case AuthenticationType.InnerEnum.HLS_MD5: return(md5(connection.challengeServerToClient, parameters.llsHlsSecret)); case AuthenticationType.InnerEnum.HLS_SHA1: return(sha1(connection.challengeServerToClient, parameters.llsHlsSecret)); case AuthenticationType.InnerEnum.HLS_GMAC: int ivCounter = parameters.getInvocationCounter(); System.IO.MemoryStream data = new System.IO.MemoryStream(); data.WriteByte(SC_AUTHENTICATION); data.Write(parameters.ak, 0, parameters.ak.Length); data.Write(connection.challengeServerToClient, 0, connection.challengeServerToClient.Length); System.IO.MemoryStream stream = new System.IO.MemoryStream(); stream.WriteByte(SC_AUTHENTICATION); stream.WriteByte((byte)(ivCounter >> 24)); stream.WriteByte((byte)(ivCounter >> 16)); stream.WriteByte((byte)(ivCounter >> 8)); stream.WriteByte((byte)(ivCounter)); byte[] aux = Security.aesGcm(new byte[0], data.ToArray(), parameters, ivCounter); stream.Write(aux, 0, aux.Length); return(stream.ToArray()); default: throw new System.ArgumentException(); } } catch (IOException) { throw new DlmsException(DlmsException.DlmsExceptionReason.INTERNAL_ERROR); } }
public static byte[] aesGcm(byte[] data, byte[] authData, CosemParameters parameters, int ivCounter) { try { byte[] iv = getIv(parameters.systemTitle, ivCounter); lock (cipherLocker) { var cipher = new GcmBlockCipher(new AesEngine()); cipher.Init(true, new AeadParameters(new KeyParameter(parameters.ek), MacBitSize, iv)); var cipherText = new byte[cipher.GetOutputSize(data.Length)]; var len = cipher.ProcessBytes(data, 0, data.Length, cipherText, 0); cipher.ProcessAadBytes(authData, 0, authData.Length); cipher.DoFinal(cipherText, len); return(cipherText); } } catch (InvalidKeyException e) { Console.WriteLine(e.ToString()); Console.Write(e.StackTrace); } catch (InvalidParameterException e) { Console.WriteLine(e.ToString()); Console.Write(e.StackTrace); } catch (CryptographicException e) { Console.WriteLine(e.ToString()); Console.Write(e.StackTrace); } //catch (AEADBadTagException) //{ // throw new DlmsException(DlmsException.DlmsExceptionReason.SECURITY_FAIL); //} //catch (BadPaddingException e) //{ // Console.WriteLine(e.ToString()); // Console.Write(e.StackTrace); //} throw new DlmsException(DlmsException.DlmsExceptionReason.INTERNAL_ERROR); }
private static byte[] generateConformanceBlock(CosemParameters parameters) { int conformanceBlock = 0; if (parameters.referenceType == ReferenceType.SHORT_NAME) { conformanceBlock |= Constants.ConformanceBlock.READ; conformanceBlock |= Constants.ConformanceBlock.WRITE; } if (parameters.referenceType == ReferenceType.LOGICAL_NAME) { conformanceBlock |= Constants.ConformanceBlock.GET; conformanceBlock |= Constants.ConformanceBlock.SET; } conformanceBlock |= Constants.ConformanceBlock.ACTION; conformanceBlock |= Constants.ConformanceBlock.BLOCK_TRANSFER_WITH_ACTION; conformanceBlock |= Constants.ConformanceBlock.BLOCK_TRANSFER_WITH_GET_OR_READ; conformanceBlock |= Constants.ConformanceBlock.BLOCK_TRANSFER_WITH_SET_OR_WRITE; conformanceBlock |= Constants.ConformanceBlock.SELECTIVE_ACCESS; //return ByteBuffer.allocate(4).putInt(conformanceBlock).array(); return(new byte[] { (byte)(conformanceBlock >> 24), (byte)(conformanceBlock >> 16), (byte)(conformanceBlock >> 8), (byte)(conformanceBlock) }); }
internal static byte[] request(CosemParameters parameters, CosemConnection connection) { const int BASE = Constants.Ber.CLASS_CONTEXT | Constants.Ber.CONSTRUCTED; byte[] applicationContextName = generateApplicationContextName(parameters.referenceType, parameters.securityType); connection.proposedContextName = applicationContextName; System.IO.MemoryStream data = new System.IO.MemoryStream(); //TODO if (parameters.authenticationType == AuthenticationType.PUBLIC) { //data.WriteByte(new byte[] {(byte)0x80, 0x02, 0x07, (byte)0x80}); byte[] aux = new byte[] { (byte)0x80, 0x02, 0x07, (byte)0x80 }; data.Write(aux, 0, aux.Length); } data.WriteByte(BASE | Constants.AarqApdu.APPLICATION_CONTEXT_NAME); data.WriteByte((byte)(applicationContextName.Length + 2)); data.WriteByte(Constants.Ber.OBJECT_IDENTIFIER); data.WriteByte((byte)(applicationContextName.Length)); data.Write(applicationContextName, 0, applicationContextName.Length); //big or little endian? if (parameters.securityType != SecurityType.NONE || parameters.authenticationType == AuthenticationType.HLS_GMAC) { data.WriteByte(BASE | Constants.Ber.OBJECT_IDENTIFIER); data.WriteByte((byte)(parameters.systemTitle.Length + 2)); data.WriteByte(Constants.Ber.OCTET_STRING); data.WriteByte((byte)(parameters.systemTitle.Length)); //data.WriteByte((byte)(parameters.systemTitle)); byte[] aux = parameters.systemTitle; data.Write(aux, 0, aux.Length); } if (parameters.authenticationType != AuthenticationType.PUBLIC) { data.WriteByte(Constants.Ber.CLASS_CONTEXT | Constants.AarqApdu.SENDER_ACSE_REQUIREMENTS); data.WriteByte(2); data.WriteByte(Constants.Ber.BIT_STRING | Constants.Ber.OCTET_STRING); data.WriteByte(0x80); data.WriteByte(Constants.Ber.CLASS_CONTEXT | Constants.AarqApdu.MECHANISM_NAME); data.WriteByte(7); //data.WriteByte(new byte[]{0x60, (byte)0x85, 0x74, 0x05, 0x08, 0x02, (byte)parameters.authenticationType.value}); byte[] aux = new byte[] { 0x60, (byte)0x85, 0x74, 0x05, 0x08, 0x02, (byte)parameters.authenticationType.value }; data.Write(aux, 0, aux.Length); data.WriteByte(BASE | Constants.AarqApdu.CALLING_AUTHENTICATION_VALUE); if (parameters.authenticationType == AuthenticationType.LLS) { data.WriteByte((byte)(parameters.llsHlsSecret.Length + 2)); data.WriteByte(Constants.Ber.CLASS_CONTEXT); data.WriteByte((byte)(parameters.llsHlsSecret.Length)); //data.WriteByte(parameters.llsHlsSecret); byte[] aux2 = parameters.llsHlsSecret; data.Write(aux2, 0, aux2.Length); } else if (parameters.authenticationType != AuthenticationType.PUBLIC) { connection.challengeClientToServer = Security.generateChallanger(parameters); data.WriteByte((byte)(connection.challengeClientToServer.Length + 2)); data.WriteByte(Constants.Ber.CLASS_CONTEXT); data.WriteByte((byte)(connection.challengeClientToServer.Length)); //data.WriteByte(connection.challengeClientToServer); byte[] aux2 = connection.challengeClientToServer; data.Write(aux2, 0, aux2.Length); } } data.WriteByte(Constants.Ber.CONTEXT_CONSTRUCTED | Constants.AarqApdu.USER_INFORMATION); byte[] initiateRequest = generateInitiateRequest(parameters); data.WriteByte((byte)(initiateRequest.Length + 2)); data.WriteByte(Constants.Ber.OCTET_STRING); data.WriteByte((byte)(initiateRequest.Length)); data.Write(initiateRequest, 0, initiateRequest.Length); System.IO.MemoryStream stream = new System.IO.MemoryStream(); stream.WriteByte(Constants.Ber.CLASS_APPLICATION | Constants.Ber.CONSTRUCTED); stream.WriteByte((byte)(data.Length)); stream.Write(data.ToArray(), 0, data.ToArray().Length); return(stream.ToArray()); }
/// <summary> /// Creates a Cosem object </summary> /// <param name="params"> CosemParameters for this Cosem object </param> public Cosem(CosemParameters parameters) { this.parameters = parameters; this.connection = new CosemConnection(); }
internal static void parseResponse(CosemParameters parameters, CosemConnection connection, byte[] data) { if (data == null || data.Length < 4) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } if ((data[0] & 0xFF) != Constants.AareApdu.APPLICATION_1) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } if ((data[1] & 0xFF) != data.Length - 2) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } if ((data[2] & 0xFF) != (Constants.Ber.CONTEXT_CONSTRUCTED | Constants.AareApdu.APPLICATION_CONTEXT_NAME)) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } int offset = (data[3] & 0xFF) + 4; parseContextName(helper.extensions.copyOfRange(data, 4, offset), connection.proposedContextName); if ((data[offset++] & 0xFF) != (Constants.Ber.CONTEXT_CONSTRUCTED | Constants.AareApdu.RESULT)) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } int resultLen = data[offset++] & 0xFF; byte[] result = helper.extensions.copyOfRange(data, offset, offset + resultLen); offset += resultLen; if ((data[offset++] & 0xFF) != (Constants.Ber.CONTEXT_CONSTRUCTED | Constants.AareApdu.RESULT_SOURCE_DIAGNOSTIC)) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } resultLen = data[offset++] & 0xFF; byte[] diagnostic = helper.extensions.copyOfRange(data, offset, offset + resultLen); offset += resultLen; parseResultAndDiagnostic(result, diagnostic); //Parse optional tags while (offset < data.Length) { int tag = (data[offset++] & 0xFF); int len = (data[offset++] & 0xFF); if (data.Length < (offset + len)) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } byte[] value = helper.extensions.copyOfRange(data, offset, offset + len); offset += len; if (tag == (Constants.Ber.CONTEXT_CONSTRUCTED | Constants.AareApdu.RESPONDING_AP_TITLE)) { if (value[0] != Constants.Ber.OCTET_STRING || value[1] != value.Length - 2) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } connection.serverSysTitle = helper.extensions.copyOfRange(value, 2, value.Length); StringBuilder sb = new StringBuilder(); foreach (byte b in connection.serverSysTitle) { sb.Append(b); } Console.WriteLine(sb); } else if (tag == (Constants.Ber.CLASS_CONTEXT | Constants.AareApdu.RESPONDER_ACSE_REQUIREMENTS)) { //System.out.println("Resp ACSE Req: "+printBytes(value)); } else if (tag == (Constants.Ber.CLASS_CONTEXT | Constants.AareApdu.MECHANISM_NAME)) { //System.out.println("Mechanism Name: "+printBytes(value)); } else if (tag == (Constants.Ber.CONTEXT_CONSTRUCTED | Constants.AareApdu.RESPONDING_AUTHENTICATION_VALUE)) { if ((value[0] & 0xFF) != 0x80 || value[1] != value.Length - 2) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } connection.challengeServerToClient = helper.extensions.copyOfRange(value, 2, value.Length); } else if (tag == (Constants.Ber.CONTEXT_CONSTRUCTED | Constants.AareApdu.USER_INFORMATION)) { parseUserInfo(value, parameters, connection); } } }
private static void parseUserInfo(byte[] data, CosemParameters parameters, CosemConnection connection) { if (data == null || data.Length < 16) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } if (data[0] != Constants.Ber.OCTET_STRING) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } if (data[1] < 14 || data[1] != data.Length - 2) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } if (parameters.securityType != SecurityType.NONE) { if (data[2] != Constants.xDlmsApdu.GlobalCiphering.INITIATE_RESPONSE) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } if (data[3] != data.Length - 4) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } byte[] encrypted = new byte[data[3] & 0xFF]; Array.Copy(data, 4, encrypted, 0, encrypted.Length); data = Security.reverseAuthenticatedEncryption(parameters, connection, encrypted); } else { data = helper.extensions.copyOfRange(data, 2, data.Length); } if (data[0] != Constants.xDlmsApdu.NoCiphering.INITIATE_RESPONSE) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } if (data[1] != 0 || data[2] != Constants.DLMS_VERSION) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } if (data[3] != Constants.ConformanceBlock.TAG) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } if (data[4] != 0x1F || data[5] != 0x04 || data[6] != 0x00) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } connection.conformanceBlock = helper.extensions.copyOfRange(data, 7, 10); //int vaa = ByteBuffer.allocate(2).put(Arrays.copyOfRange(data, 12, data.Length)).getShort(0); byte[] aux = helper.extensions.copyOfRange(data, 12, data.Length); int vaa = BitConverter.ToInt16(new byte[] { (aux[1]), (aux[0]) }, 0); if (parameters.referenceType == ReferenceType.LOGICAL_NAME && vaa != 0x0007) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } if (parameters.referenceType == ReferenceType.SHORT_NAME && vaa != 0xFA00) { throw new DlmsException(DlmsException.DlmsExceptionReason.MALFORMED_AARE_FRAME); } connection.maxPduSize = ((data[10] & 0xFF) << 8) | (data[11] & 0xFF); }