public static byte[] Encrypt(byte[] fileData, CMFileType fileType) { using (MemoryStream stream = new MemoryStream()) using (BinaryWriter writer = new BinaryWriter(stream)) { byte[] fileTypeStrBuffer = new byte[8]; Buffer.BlockCopy(fileTypes[fileType], 0, fileTypeStrBuffer, 0, fileTypes[fileType].Length); string fileTypeStr = Encoding.ASCII.GetString(fileTypeStrBuffer).TrimEnd('\0'); ushort encryptionKey = Crc16.GetCrc(Encoding.ASCII.GetBytes(fileTypeStr)); encryptionKey = Crc16.GetCrc(fileTypeStrBuffer, 0, encryptionKey); byte[] encryptedData = new byte[fileData.Length]; Buffer.BlockCopy(fileData, 0, encryptedData, 0, fileData.Length); ushort dataCrc = Encrypt(encryptedData, encryptionKey); // Offset 0 (file signature) writer.Write(fileSignature); // Offset 4 (header crc - to be built after the header is fully formed) writer.Write((int)0); // Offset 8 (timestamp) writer.Write(GetTimeStamp(DateTime.Now)); // Offset 12 (data length) writer.Write(fileData.Length); // Offset 16 (data crc) writer.Write((int)dataCrc); // Offset 20 (file type) writer.Write((int)fileType); // Offset 24-32 (file type string) for (int i = 0; i < 8; i++) { writer.Write((byte)(i < fileTypeStrBuffer.Length ? fileTypeStrBuffer[i] : 0)); } writer.Write(encryptedData); // Get the header bytes, calculate the crc, and insert the crc into the header long tempPos = stream.Position; stream.Position = 0; byte[] header = new byte[32]; stream.Read(header, 0, header.Length); ushort headerCrc = Crc16.GetCrc(header); stream.Position = 4; writer.Write(headerCrc); stream.Position = tempPos; return(stream.ToArray()); } }
private byte[] GetResponse(byte op1, byte op2) { // This is a bit of a hack to consume any packets sent from the keyboard which we didn't request // such as changing the keyboard layer manually. TODO: Add a proper packet handler and remove this hack. const int reportHeaderLen = 1; while (true) { byte[] resultBufferWithReportId = new byte[65]; try { stream.Read(resultBufferWithReportId); } catch { return(null); } if (resultBufferWithReportId[0] != 0) { return(null); } if (resultBufferWithReportId[1] == op1) { byte[] resultBuffer = new byte[64]; Buffer.BlockCopy(resultBufferWithReportId, 1, resultBuffer, 0, resultBuffer.Length); return(resultBuffer); } else if (resultBufferWithReportId[1] == (byte)OpCodes.DriverKeyCallback) { byte callbackId = resultBufferWithReportId[3]; bool callbackKeyDown = resultBufferWithReportId[4] != 0; if (callbackKeyDown) { KeyboardState.Key key; State.KeysByLogicCode.TryGetValue(callbackId, out key); Program.Log("Index:" + callbackId + " " + (key == null ? "(null)" : "Name:" + key.KeyName + " " + // Name of the key "Indx:" + key.LogicCode + " " + // 'Indx' for logic code / key index "Loc:" + key.LocationCode + " " + // 'Loc' for location code "D:0x" + key.DriverValue.ToString("X8") + " " + // 'D' for DriverValue //"E:" + (DriverValue)key.DriverValue + " " +// 'E' for the Enum variant of the DriverValue "S:" + key.DriverValueName)); // <--- 'S' for string representation (use this for mapping keys in your UserData file) } } else if (!Crc16.ValidateCrc(resultBufferWithReportId, reportHeaderLen, reportHeaderLen + 6)) { return(null); } } }
private static byte[] Decrypt(byte[] buffer, string file) { using (MemoryStream stream = new MemoryStream(buffer)) using (BinaryReader reader = new BinaryReader(stream)) { if (reader.ReadUInt32() != fileSignature) { Log("Bad file signature", buffer, file); return(null); } // Header crc is at offset 4, written as 4 bytes (but still a crc16) // (this is a crc of the first 32 bytes (where the crc bytes are 0) stream.Position = 4; ushort headerCrc = reader.ReadUInt16(); // Timestamp is never used // Timestamp is at offset 8, written as 4 bytes // stream.Position = 8; // int timestamp = reader.ReadInt32(); // Length is at offset 12, written as 4 bytes stream.Position = 12; int dataLength = reader.ReadInt32(); // Data crc is at offset 16, written as 4 bytes (but still a crc16) stream.Position = 16; ushort dataCrc = reader.ReadUInt16(); // File type is never used // File type is at offset 20, written as 4 bytes // stream.Position = 20; // int fileType = reader.ReadInt32(); // File type (string) is at offset 24, written as 8 bytes, padded with 00 stream.Position = 24; byte[] fileTypeStrBuffer = reader.ReadBytes(8); uint intFileType = BitConverter.ToUInt32(fileTypeStrBuffer, 0); if (unknownMaps.ContainsKey(intFileType)) { byte[] newType = BitConverter.GetBytes(unknownMaps[intFileType]); fileTypeStrBuffer = new byte[8]; Buffer.BlockCopy(newType, 0, fileTypeStrBuffer, 0, newType.Length); } List <byte> blob = new List <byte>(); for (int i = 0; i < fileTypeStrBuffer.Length; i++) { if (fileTypeStrBuffer[i] == 0) { break; } blob.Add(fileTypeStrBuffer[i]); } // First crc the file type name, then get crc the file type name (including zeroed bytes) ushort encryptionKey = Crc16.GetCrc(blob.ToArray()); encryptionKey = Crc16.GetCrc(fileTypeStrBuffer, 0, encryptionKey); // Data is at offset 32 stream.Position = 32; byte[] data = reader.ReadBytes(dataLength); ushort calculatedDataCrc = Decrypt(data, encryptionKey); if (dataCrc != calculatedDataCrc) { Log("File has an invalid data crc", buffer, file); } if (stream.Position != stream.Length) { Log("File has trailing bytes", buffer, file); } stream.Position = 0; byte[] header = reader.ReadBytes(32); header[4] = 0; header[5] = 0; header[6] = 0; header[7] = 0; ushort calculatedHeaderCrc = Crc16.GetCrc(header); if (headerCrc != calculatedHeaderCrc) { Log("File has an invalid header crc", buffer, file); } return(data); } }
private static KeyboardState Handshake(HidStream stream) { try { byte bufferSizeA; byte bufferSizeB; uint firmwareId; byte firmwareMinorVersion; byte firmwareMajorVersion; uint modelId; using (Packet packet = WriteSimplePacket(stream, 0x0901)) { if (packet == null) { LogHandshakeFailed(stream.Device, "opcode 01 09"); return(null); } bufferSizeA = packet.ReadByte(); bufferSizeB = packet.ReadByte(); if (bufferSizeA == 0 || bufferSizeB == 0) { LogHandshakeFailed(stream.Device, "Bad buffer size"); return(null); } } using (Packet packet = WriteSimplePacket(stream, 0x0101)) { if (packet == null) { LogHandshakeFailed(stream.Device, "opcode 01 01"); return(null); } firmwareId = packet.ReadUInt32(); firmwareMinorVersion = packet.ReadByte(); firmwareMajorVersion = packet.ReadByte(); if (firmwareId == 0 || (firmwareMinorVersion == 0 && firmwareMajorVersion == 0)) { LogHandshakeFailed(stream.Device, "Bad firmware id"); return(null); } } using (Packet packet = WriteSimplePacket(stream, 0x0801)) { if (packet == null) { LogHandshakeFailed(stream.Device, "opcode 01 08"); return(null); } byte[] crcBytes = packet.ReadBytes(6); ushort calculatedModelIdCrc = Crc16.GetCrc(crcBytes); packet.Index -= 6; modelId = packet.ReadUInt32(); if (modelId == 0) { LogHandshakeFailed(stream.Device, "Bad keyboard model id"); return(null); } ushort crcValidation1 = packet.ReadUInt16(); ushort modelIdCrc = packet.ReadUInt16(); if (calculatedModelIdCrc != modelIdCrc) { LogHandshakeFailed(stream.Device, "Bad keyboard model crc"); return(null); } } // OpCodes_Info.Unk_02 should probably also be sent? Not sure what it's used for though... KeyboardState result = KeyboardState.GetKeyboardState(modelId); if (result == null || result.FirmwareId != firmwareId) { LogHandshakeFailed(stream.Device, "Couldn't find data for keyboard"); return(null); } result.FirmwareMinorVersion = firmwareMinorVersion; result.FirmwareMajorVersion = firmwareMajorVersion; result.InitializeBuffers(bufferSizeA, bufferSizeB); return(result); } catch (Exception e) { LogHandshakeFailed(stream.Device, "Exception occured. " + e); return(null); } }
public Packet WritePacket(OpCodes op1, byte op2, Packet packet, byte op3 = 0) { lock (locker) { int numPackets = 1; int offset = 0; byte[] completeBuffer = null; if (packet != null) { completeBuffer = packet.GetWrittenBuffer(); numPackets = (completeBuffer.Length / 0x38) + 1; } bool allowsLongOffset = false; int offsetOffset = 2; int lengthOffset = 4; switch (op1) { case OpCodes.DriverLayerSetKeyValues: case OpCodes.DriverLayerUpdateRealtimeLighting: case OpCodes.LayerSetLightValues: offsetOffset = 2; lengthOffset = 5; allowsLongOffset = true; break; case OpCodes.LayerSetKeyPressLightingEffect: case OpCodes.LayerSetKeyValues: case OpCodes.LayerFnSetKeyValues: case OpCodes.LayerSetMacros: offsetOffset = 2; lengthOffset = 4; break; } const int reportHeaderLen = 1; try { for (int i = 0; i < numPackets; i++) { byte[] report = new byte[65]; report[1] = (byte)op1; report[2] = op2; if (op3 > 0) { // Not really an op, used for setting keyboard data buffers report[3] = op3; } if (completeBuffer != null) { int numBytesToWrite = Math.Min(0x38, completeBuffer.Length - offset); Buffer.BlockCopy(completeBuffer, offset, report, reportHeaderLen + 8, numBytesToWrite); if (numPackets > 1) { report[reportHeaderLen + offsetOffset + 0] = (byte)offset; report[reportHeaderLen + offsetOffset + 1] = (byte)(offset >> 8); if (allowsLongOffset) { report[reportHeaderLen + offsetOffset + 3] = (byte)(offset >> 16); } report[reportHeaderLen + lengthOffset] = (byte)numBytesToWrite; } } Crc16.InsertCrc(report, reportHeaderLen, 6 + reportHeaderLen); stream.Write(report); byte[] resultBuffer = GetResponse((byte)op1, op2); if (resultBuffer[0] != (byte)op1 /* || resultBuffer[1] != op2*/) { return(null); } if (!Crc16.ValidateCrc(resultBuffer)) { return(null); } if (i == numPackets - 1) { Packet result = new Packet(true, resultBuffer); result.Index = 8; lastSentMessage = Environment.TickCount; return(result); } offset += 0x38; } } catch { return(null); } return(null); } }
public void SetMacros(KeyboardLayer layer, UserDataFile userData) { WritePacketNoResponse(OpCodes.LayerResetDataType, (byte)layer, null, (byte)KeyboardLayerDataType.Macros); // The following check has been disabled so that the WebGUI can assign macros without having to setup the keys /*if (userData.GetNumMacros(layer) == 0) * { * return; * }*/ using (Packet packet = new Packet()) { // TODO: This should be improved to only send the macros to the layers that require the macro foreach (UserDataFile.Macro macro in userData.Macros.Values) { if (macro.Id < 0) { continue; } if (macro.Actions.Count * 2 > byte.MaxValue) { Program.Log("Macro '" + macro.Name + "' has too many actions (" + macro.Actions.Count + ", limit is " + (byte.MaxValue / 2) + ")"); continue; } if (macro.Actions.Count == 0 && macro.Id >= 0) { Program.Log("Macro '" + macro.Name + "' doesn't have any actions!"); continue; } packet.WriteUInt16(0x55AA);// Macro magic (21930 / 0x55AA / AA 55) // Crc to be filled out once all data is written int crcIndex = packet.Index; packet.WriteUInt16(0); byte numActionInts = (byte)((macro.Actions.Count * 2) - (macro.UseTrailingDelay ? 0 : 1)); packet.WriteByte(numActionInts); packet.WriteByte((byte)macro.Id); packet.WriteByte((byte)macro.RepeatType); packet.WriteByte(macro.RepeatCount); int crcDataStartIndex = packet.Index; int crcDataEndIndex = packet.Index + (numActionInts * 4); for (int i = 0; i < 63; i++) { if (i < macro.Actions.Count) { UserDataFile.Macro.Action action = macro.Actions[i]; packet.WriteByte(action.KeyCode); packet.WriteByte((byte)action.Modifier); packet.WriteByte((byte)action.State); packet.WriteByte((byte)action.Type); if (i < macro.Actions.Count - 1 || (macro.UseTrailingDelay && i == macro.Actions.Count - 1)) { packet.WriteUInt16(action.Delay); //TODO: Investigate! (ushort)(action.Delay == 0 ? 0 : action.Delay / 2));// Delay seems to be doubled? packet.WriteByte(0); // Always 0? packet.WriteByte(3); // Always 3? (1/2 use no delay, 0/4 (and maybe others) seem to never stop pressing the key) } else { packet.WriteInt32(0); } } else { packet.WriteInt64(0); } } byte[] buffer = packet.GetBuffer(); byte[] bytesToCrc = new byte[crcDataEndIndex - crcDataStartIndex]; Buffer.BlockCopy(buffer, crcDataStartIndex, bytesToCrc, 0, bytesToCrc.Length); ushort crc = Crc16.GetCrc(bytesToCrc); // Always 0? packet.WriteByte(0); int tempIndex = packet.Index; packet.Index = crcIndex; packet.WriteUInt16(crc); packet.Index = tempIndex; } WritePacketNoResponse(OpCodes.LayerSetMacros, (byte)layer, packet); } }
private static byte[] Decrypt(byte[] buffer, string file) { using (MemoryStream stream = new MemoryStream(buffer)) using (BinaryReader reader = new BinaryReader(stream)) { if (reader.ReadUInt32() != fileSignature) { Log("Bad file signature", buffer, file); return null; } // Header crc is at offset 4, written as 4 bytes (but still a crc16) // (this is a crc of the first 32 bytes (where the crc bytes are 0) stream.Position = 4; ushort headerCrc = reader.ReadUInt16(); // Timestamp is at offset 8, written as 4 bytes stream.Position = 8; int timestamp = reader.ReadInt32(); // Length is at offset 12, written as 4 bytes stream.Position = 12; int dataLength = reader.ReadInt32(); // Data crc is at offset 16, written as 4 bytes (but still a crc16) stream.Position = 16; ushort dataCrc = reader.ReadUInt16(); // File type is at offset 20, written as 4 bytes stream.Position = 20; int fileType = reader.ReadInt32(); // File type (string) is at offset 24, written as 8 bytes, padded with 00 stream.Position = 24; byte[] fileTypeStrBuffer = reader.ReadBytes(8); // First crc the file type name, then get crc the file type name (including zeroed bytes) string fileTypeStr = Encoding.ASCII.GetString(fileTypeStrBuffer).TrimEnd('\0'); ushort encryptionKey = Crc16.GetCrc(Encoding.ASCII.GetBytes(fileTypeStr)); encryptionKey = Crc16.GetCrc(fileTypeStrBuffer, 0, encryptionKey); // Data is at offset 32 stream.Position = 32; byte[] data = reader.ReadBytes(dataLength); ushort calculatedDataCrc = Decrypt(data, encryptionKey); if (dataCrc != calculatedDataCrc) { Log("File has an invalid data crc", buffer, file); } if (stream.Position != stream.Length) { Log("File has trailing bytes", buffer, file); } stream.Position = 0; byte[] header = reader.ReadBytes(32); header[4] = 0; header[5] = 0; header[6] = 0; header[7] = 0; ushort calculatedHeaderCrc = Crc16.GetCrc(header); if (headerCrc != calculatedHeaderCrc) { Log("File has an invalid header crc", buffer, file); } return data; } }