Skip to content

Commit

Permalink
implement bootloader v5 protocol (still disabled because didn't teste…
Browse files Browse the repository at this point in the history
…d yet)
  • Loading branch information
qrp73 committed Aug 23, 2024
1 parent 65daace commit f8fa965
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 106 deletions.
4 changes: 2 additions & 2 deletions AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.7.0.0")]
[assembly: AssemblyFileVersion("1.7.0.0")]
[assembly: AssemblyVersion("1.7.1.0")]
[assembly: AssemblyFileVersion("1.7.1.0")]
35 changes: 25 additions & 10 deletions Packets/ProtocolBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,20 @@ public virtual bool WriteFlash(string version, byte[] data)

var seqId = GenerateId();

WriteFlashInit();
EncryptFlashInit();
try
{
return ProcessAddressSpace(0x0000, data.Length, chunkSize, FirmwareConstraints.MinFlashAddr, FirmwareConstraints.MaxFlashAddr, MaxFlashBlock,
(absOffset, blockOffset, blockLength) =>
{
Console.Write(" Write {0:x4}...{1:x4}: ", absOffset, absOffset + blockLength);
var subData = new byte[blockLength];
Array.Copy(data, blockOffset, subData, 0, subData.Length);
subData = WriteFlashEncrypt(subData);
var subData = new byte[chunkSize];
for (var i = 0; i < subData.Length; i++)
subData[i] = 0xff;
Array.Copy(data, blockOffset, subData, 0, blockLength);
subData = EncryptFlashProcess(subData);
var chunkNumber = (ushort)(absOffset / chunkSize);
_device.Send(CreatePacketFlashWriteReq(chunkNumber, chunkCount, subData, seqId));
_device.Send(CreatePacketFlashWriteReq(chunkNumber, chunkCount, subData, blockLength, seqId));
for (var counter = 0; ; counter++)
{
packet = _device.Recv();
Expand Down Expand Up @@ -129,25 +131,38 @@ public virtual bool WriteFlash(string version, byte[] data)
}
finally
{
WriteFlashFinish();
EncryptFlashFinish();
}
}

public abstract PacketFlashWriteReq CreatePacketFlashWriteReq(ushort chunkNumber, ushort chunkCount, byte[] subData, uint seqId);
public abstract PacketFlashWriteReq CreatePacketFlashWriteReq(ushort chunkNumber, ushort chunkCount, byte[] data, int dataLength, uint seqId);

public abstract PacketFlashVersionReq CreatePacketFlashVersionReq(string version);

public abstract PacketFlashBeaconAck CreatePacketFlashBeaconAck();

public virtual void WriteFlashInit()
public virtual void EncryptFlashInit()
{
}

public virtual void WriteFlashFinish()
public virtual void EncryptFlashFinish()
{
}

public virtual byte[] WriteFlashEncrypt(byte[] data)
public virtual byte[] EncryptFlashProcess(byte[] data)
{
return data;
}

public virtual void DecryptFlashInit()
{
}

public virtual void DecryptFlashFinish()
{
}

public virtual byte[] DecryptFlashProcess(byte[] data)
{
return data;
}
Expand Down
18 changes: 10 additions & 8 deletions Packets/V2/Packet2FlashWriteReq.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ public Packet2FlashWriteReq(byte[] rawData)
throw new InvalidOperationException();
}

public Packet2FlashWriteReq(ushort chunkNumber, ushort chunkCount, byte[] data)
: this(chunkNumber, chunkCount, data, 0x1d9f8d8a)
public Packet2FlashWriteReq(ushort chunkNumber, ushort chunkCount, byte[] data, int dataLength)
: this(chunkNumber, chunkCount, data, dataLength, 0x1d9f8d8a)
{
}

public Packet2FlashWriteReq(ushort chunkNumber, ushort chunkCount, byte[] data, uint id/*=0x1d9f8d8a*/)
: base(MakePacketBuffer(id, chunkNumber, chunkCount, data, 0x0000))
public Packet2FlashWriteReq(ushort chunkNumber, ushort chunkCount, byte[] data, int dataLength, uint id/*=0x1d9f8d8a*/)
: base(MakePacketBuffer(id, chunkNumber, chunkCount, data, dataLength, 0x0000))
{
if (chunkNumber > chunkCount)
throw new ArgumentOutOfRangeException("chunkNumber");
Expand All @@ -52,13 +52,15 @@ public Packet2FlashWriteReq(ushort chunkNumber, ushort chunkCount, byte[] data,
}

// 0x19, 0x5, 0xc, 0x1, 0x8a, 0x8d, 0x9f, 0x1d, address_msb, address_lsb, address_final_msb, address_final_lsb, length_msb, length_lsb, 0x0, 0x0, ...data
private static byte[] MakePacketBuffer(uint id, ushort chunkNumber, ushort chunkCount, byte[] data, ushort padding)
private static byte[] MakePacketBuffer(uint id, ushort chunkNumber, ushort chunkCount, byte[] data, int dataLength, ushort padding)
{
if (data.Length > 0x100)
throw new ArgumentOutOfRangeException("data");
if (data.Length != 0x100)
throw new ArgumentOutOfRangeException("data.Length");
if (dataLength > 0x100)
throw new ArgumentOutOfRangeException("dataLength");
if ((chunkCount & 0xff00) != 0)
throw new ArgumentOutOfRangeException("chunkCount>0x100 is not tested yet");
var length = data.Length;
var length = dataLength;
var buf = new byte[16 + 0x100];
var hdrSize = buf.Length - 4;
if (hdrSize != 0x010c)
Expand Down
4 changes: 2 additions & 2 deletions Packets/V2/ProtocolV2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ public override PacketFlashVersionReq CreatePacketFlashVersionReq(string version
new Packet2FlashVersionReq();
}

public override PacketFlashWriteReq CreatePacketFlashWriteReq(ushort chunkNumber, ushort chunkCount, byte[] subData, uint seqId)
public override PacketFlashWriteReq CreatePacketFlashWriteReq(ushort chunkNumber, ushort chunkCount, byte[] data, int dataLength, uint seqId)
{
return new Packet2FlashWriteReq(chunkNumber, chunkCount, subData, seqId);
return new Packet2FlashWriteReq(chunkNumber, chunkCount, data, dataLength, seqId);
}

public override PacketFlashBeaconAck CreatePacketFlashBeaconAck()
Expand Down
16 changes: 9 additions & 7 deletions Packets/V5/Packet5FlashWriteReq.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ public Packet5FlashWriteReq(byte[] rawData)
throw new InvalidOperationException();
}

public Packet5FlashWriteReq(ushort chunkNumber, ushort chunkCount, byte[] data)
: this(chunkNumber, chunkCount, data, 0x1d9f8d8a)
public Packet5FlashWriteReq(ushort chunkNumber, ushort chunkCount, byte[] data, int dataLength)
: this(chunkNumber, chunkCount, data, dataLength, 0x1d9f8d8a)
{
}

public Packet5FlashWriteReq(ushort chunkNumber, ushort chunkCount, byte[] data, uint id/*=0x1d9f8d8a*/)
: base(MakePacketBuffer(id, chunkNumber, chunkCount, data, 0x0000))
public Packet5FlashWriteReq(ushort chunkNumber, ushort chunkCount, byte[] data, int dataLength, uint id/*=0x1d9f8d8a*/)
: base(MakePacketBuffer(id, chunkNumber, chunkCount, data, dataLength, 0x0000))
{
if (chunkNumber > chunkCount)
throw new ArgumentOutOfRangeException("chunkNumber");
Expand All @@ -52,13 +52,15 @@ public Packet5FlashWriteReq(ushort chunkNumber, ushort chunkCount, byte[] data,
chunkCount));
}

private static byte[] MakePacketBuffer(uint id, ushort chunkNumber, ushort chunkCount, byte[] data, ushort padding)
private static byte[] MakePacketBuffer(uint id, ushort chunkNumber, ushort chunkCount, byte[] data, int dataLength, ushort padding)
{
if (data.Length > 0x100)
if (data.Length != 0x100)
throw new ArgumentOutOfRangeException("data");
if (dataLength > 0x100)
throw new ArgumentOutOfRangeException("data");
if ((chunkCount & 0xff00) != 0)
throw new ArgumentOutOfRangeException("chunkCount>0x100 is not tested yet");
var length = data.Length;
var length = dataLength;
var buf = new byte[16 + 0x100];
var hdrSize = buf.Length - 4;
if (hdrSize != 0x010c)
Expand Down
168 changes: 93 additions & 75 deletions Packets/V5/ProtocolV5.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ public override PacketFlashVersionReq CreatePacketFlashVersionReq(string version
new Packet5FlashVersionReq(_keyNumber);
}

public override PacketFlashWriteReq CreatePacketFlashWriteReq(ushort chunkNumber, ushort chunkCount, byte[] subData, uint seqId)
public override PacketFlashWriteReq CreatePacketFlashWriteReq(ushort chunkNumber, ushort chunkCount, byte[] data, int dataLength, uint seqId)
{
return new Packet5FlashWriteReq(chunkNumber, chunkCount, subData, seqId);
return new Packet5FlashWriteReq(chunkNumber, chunkCount, data, dataLength, seqId);
}

public override PacketFlashBeaconAck CreatePacketFlashBeaconAck()
Expand All @@ -35,99 +35,117 @@ public override PacketFlashWriteAck CreatePacketFlashWriteAck(ushort chunkNumber
return new Packet5FlashWriteAck(chunkNumber, sequenceId);
}

/*
key 00: e16e0d29e0c83418987f9433f5ff620e
iv 00: 14b7a2be0223e259b2066d8886977e36
key 01: b0d93af7761a50cacb966eb8a805bcbb
iv 01: 916c50fb9e480693b155b2e555cb780a
key 02: 1357cc24d138a57799dd0eec5b9a18f9
iv 02: fb149d4c45e7d7a95aa64bc22765a8c0
key 03: a8ef4f917688c5c1487f2a6a811e554d
iv 03: 32c860dfce65ca302ec534aa5f88884b
key 04: 97a387567b234b55c921cd82f65f0087
iv 04: e8ab4e0e344cb5a0b1e7db7a05d468c3
key 05: f0240e02ed5da965539d815306f2d34b
iv 05: c6116bd5bea38673746205ee534d589f
key 06: 734dd8ac84c0c422cbca28fec8856473
iv 06: 4bfe35582436578014e970ed8dab9ea0
key 07: 7f67480570ba5254d7cee59ca2a483b5
iv 07: 96858cdf59454a5fa84faa3663748b8d
key 08: 67ec7c38cefa9edb38bb89b58986eb1a
iv 08: 2f9a34bcec302bc6c5b6bc552c166dc9
key 09: 08ea3a7dc8a6e961b7061f7e52ec8e6d
iv 09: 5f6f2ecd835d760b5c1e78f0be1a8787
key 10: 4e324c565e32be8a6aaf26d9ff37ee87
iv 10: 3d680b15a6274c72bc3313a183f1372b
key 11: 4fd9d7243916543c23ad995937e4bb7c
iv 11: a10be7032960c6d5dcbdc6b4ecfbe31a
key 12: a1d6c61c8c5c2281dc7e371ca3c3f168
iv 12: 925eb778232e8b4b1f06a582498b2149
key 13: 11e065ed3f9e8b96bae61f79a7d8a317
iv 13: 8b67978471fbd371dcf44ed92f56562d
key 14: c4636958e1830f23c6d9ce15b2ddc35a
iv 14: 5b7237ddb9c5290e15519018ceacba76
key 15: bf9862d680f948195f5c90545e1d8578
iv 15: 8172ea14916b606855ff2aabe52e993c
*/

private Aes _aes;
private static byte[][] _keys = new byte[][]
{
Utils.FromHex("e16e0d29e0c83418987f9433f5ff620e"), // key 00
Utils.FromHex("14b7a2be0223e259b2066d8886977e36"), // iv 00
Utils.FromHex("b0d93af7761a50cacb966eb8a805bcbb"), // key 01
Utils.FromHex("916c50fb9e480693b155b2e555cb780a"), // iv 01
Utils.FromHex("1357cc24d138a57799dd0eec5b9a18f9"), // key 02
Utils.FromHex("fb149d4c45e7d7a95aa64bc22765a8c0"), // iv 02
Utils.FromHex("a8ef4f917688c5c1487f2a6a811e554d"), // key 03
Utils.FromHex("32c860dfce65ca302ec534aa5f88884b"), // iv 03
Utils.FromHex("97a387567b234b55c921cd82f65f0087"), // key 04
Utils.FromHex("e8ab4e0e344cb5a0b1e7db7a05d468c3"), // iv 04
Utils.FromHex("f0240e02ed5da965539d815306f2d34b"), // key 05
Utils.FromHex("c6116bd5bea38673746205ee534d589f"), // iv 05
Utils.FromHex("734dd8ac84c0c422cbca28fec8856473"), // key 06
Utils.FromHex("4bfe35582436578014e970ed8dab9ea0"), // iv 06
Utils.FromHex("7f67480570ba5254d7cee59ca2a483b5"), // key 07
Utils.FromHex("96858cdf59454a5fa84faa3663748b8d"), // iv 07
Utils.FromHex("67ec7c38cefa9edb38bb89b58986eb1a"), // key 08
Utils.FromHex("2f9a34bcec302bc6c5b6bc552c166dc9"), // iv 08
Utils.FromHex("08ea3a7dc8a6e961b7061f7e52ec8e6d"), // key 09
Utils.FromHex("5f6f2ecd835d760b5c1e78f0be1a8787"), // iv 09
Utils.FromHex("4e324c565e32be8a6aaf26d9ff37ee87"), // key 10
Utils.FromHex("3d680b15a6274c72bc3313a183f1372b"), // iv 10
Utils.FromHex("4fd9d7243916543c23ad995937e4bb7c"), // key 11
Utils.FromHex("a10be7032960c6d5dcbdc6b4ecfbe31a"), // iv 11
Utils.FromHex("a1d6c61c8c5c2281dc7e371ca3c3f168"), // key 12
Utils.FromHex("925eb778232e8b4b1f06a582498b2149"), // iv 12
Utils.FromHex("11e065ed3f9e8b96bae61f79a7d8a317"), // key 13
Utils.FromHex("8b67978471fbd371dcf44ed92f56562d"), // iv 13
Utils.FromHex("c4636958e1830f23c6d9ce15b2ddc35a"), // key 14
Utils.FromHex("5b7237ddb9c5290e15519018ceacba76"), // iv 14
Utils.FromHex("bf9862d680f948195f5c90545e1d8578"), // key 15
Utils.FromHex("8172ea14916b606855ff2aabe52e993c"), // iv 15
};

private ICryptoTransform _aesEncoder;

public override void WriteFlashInit()
public override void EncryptFlashInit()
{
// WARNING: do not remove exception, with no proper key it can brick your radio
throw new NotImplementedException("AES key initialization not implemented yet");
var key = Utils.FromHex("e16e0d29e0c83418987f9433f5ff620e");
var iv = Utils.FromHex("14b7a2be0223e259b2066d8886977e36");
//TODO: implement key2 generation from (key,iv) pair, or just put pre-computed value
var key2 = Utils.FromHex("00000000000000000000000000000000");
_aes = Aes.Create();
_aes.Key = key;
_aes.IV = iv;
_aes.Mode = CipherMode.CBC;
_aes.Padding = PaddingMode.None;
_aes.BlockSize = 128;
_aesEncoder = _aes.CreateEncryptor(key2, iv);
throw new NotImplementedException("AES encrypted flash upload is not tested yet");
var key = _keys[_keyNumber * 2 + 0];
var iv = _keys[_keyNumber * 2 + 1];
using (var aes = Aes.Create())
{
aes.Key = ReverseBytesU32(key);
aes.IV = ReverseBytesU32(iv);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
aes.BlockSize = 128;
_aesEncoder = aes.CreateEncryptor(key, iv);
}
}

private static byte[] ReverseBytesU32(byte[] data)
{
return Enumerable.Range(0, data.Length / 4)
.Select(arg => BitConverter.ToUInt32(data, arg * 4))
.Select(arg => BitConverter.ToUInt32(BitConverter.GetBytes(arg).Reverse().ToArray(), 0))
.SelectMany(arg => BitConverter.GetBytes(arg))
.ToArray();
}

public override void WriteFlashFinish()
public override void EncryptFlashFinish()
{
if (_aesEncoder != null)
{
_aesEncoder.Dispose();
_aesEncoder = null;
}
if (_aes != null)
}

public override byte[] EncryptFlashProcess(byte[] data)
{
var encoded = new byte[data.Length];
_aesEncoder.TransformBlock(data, 0, data.Length, encoded, 0);
return encoded;
}

private ICryptoTransform _aesDecoder;

public override void DecryptFlashInit()
{
var key = _keys[_keyNumber * 2 + 0];
var iv = _keys[_keyNumber * 2 + 1];
using (var aes = Aes.Create())
{
_aes.Dispose();
_aes = null;
aes.Key = ReverseBytesU32(key);
aes.IV = ReverseBytesU32(iv);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
aes.BlockSize = 128;
_aesDecoder = aes.CreateDecryptor(key, iv);
}
}

public override byte[] WriteFlashEncrypt(byte[] data)
public override void DecryptFlashFinish()
{
// TODO: check if byte order is correct
var subData = Enumerable.Range(0, data.Length / 4)
.Select(arg => BitConverter.ToUInt32(data, arg * 4))
.Select(bswap_32)
.SelectMany(arg => BitConverter.GetBytes(arg))
.ToArray();
var encoded = new byte[subData.Length];
_aesEncoder.TransformBlock(subData, 0, subData.Length, encoded, 0);
subData = Enumerable.Range(0, encoded.Length / 4)
.Select(i => BitConverter.ToUInt32(data, i * 4))
.Select(bswap_32)
.SelectMany(arg => BitConverter.GetBytes(arg))
.ToArray();
return subData;
if (_aesDecoder != null)
{
_aesDecoder.Dispose();
_aesDecoder = null;
}
}

private static uint bswap_32(uint x)
public override byte[] DecryptFlashProcess(byte[] data)
{
return ((x & 0x000000FF) << 24) |
((x & 0x0000FF00) << 8) |
((x & 0x00FF0000) >> 8) |
((x & 0xFF000000) >> 24);
var encoded = new byte[data.Length];
_aesDecoder.TransformBlock(data, 0, data.Length, encoded, 0);
return encoded;
}
}
}
Loading

0 comments on commit f8fa965

Please sign in to comment.