I’m trying to build my own .Net Matter controller.
I’m writing these posts as I go, figuring the stuff out and blogging how and what I’m doing.
In the previous post in this series, I started looking at how to exchange PASE messages. I got to the point where I could send a message, but my ESP32-H2 didn’t like it.
15 32 01 20 B1 38 5D D4 BA 65 76 43 69 29 3E 20 A5 D0 A5 01 AC F9 5B 08 B8 CA A3 F2 8A E0 3F B7 48 95 14 19 25 02 12 34 25 03 00 00 28 04 18
This is what my TLV encoder spits out. You can see it starts with 0x15 and ends with 0x18. These correspond with starting a structure and ending a container. Exactly what I’d *think* it should be.
I expanded this logging to the *whole* message.
04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 16 00 00 00 15 32 01 20 83 37 31 A4 D3 5E 76 E6 4A 7F 85 D7 12 C7 3E 7D B3 32 9B FE B3 93 D5 38 D2 25 EB 47 BB D0 09 68 25 02 38 F4 25 03 00 00 28 04 18
I spotted an issue. This sequence, 00 20 16 00, is the start of the MessagePayload part. 20 is the Protocol ID, but we shouldn’t have 00 before it. This indicates our Exchange Flags are wrong. According to the documentation, the I flag should be set!
I rectified this by setting the flag.
messagePayload.ExchangeFlags |= ExchangeFlags.Initiator;
Unfortunately, this resulted in the same error

I started to think about what I was missing and one thing was the SourceNodeId.
In the specification, I should be providing this as I’m setting the S Flag to 1.
I set it to 0x99 and ran the application again.

Progress! As even, errors, but different kinds of errors. Now it’s complaining about the PASE session setup. If you look closely, you’ll see there are <<< meaning it’s trying to send me message back!! A StatusReport.
Checking the specification reveals this:

This means I’m probably getting a PakeFinished. The code 22 might point to this: CHIP_ERROR_TLV_UNDERRUN. It took me this long to think to actually look at the header file in connectedhomeip.
An issue with the TLV wouldn’t be a surprise. This issue indicates my encoded ended unexpectedly. Focusing on length, I found this nugget!
For UTF-8 and octet string types the bottom two bits of the element type field signal the width of the
length field as follows:
- 00 — 1 octet
- 01 — 2 octets
- 10 — 4 octets
- 11 — 8 octets
I was just doing this (setting the length as a single byte)
_values.Add((0x1 << 5) | 0x12); // Octet String, 4-octet length
_values.Add((byte)tagNumber);
_values.Add((byte)value.Length);
_values.AddRange(value);
What I should have been doing was this:
_values.AddRange(BitConverter.GetBytes((uint)value.Length));
This gets the length as 4 octets.
Running my code again:

Success!
I also received a new type of response from the ESP32. A packet that wasn’t just an acknowledgment!

Reading the response
At this point, all my code is focused on sending a message *to* the device. Aside from the handshake response, I’m not able to *read* the response from the device.
Since I’ve now clearly gotten a BTPFrame back that isn’t handshake related, I need to get its contents. This will then be passed back up the stack to be received as a MessageFrame.
My immediate thoughts here fell to a queue, like producer-consumer. .Net has a nice class for this called BlockingCollection, which lets you read and write, but it doesn’t support async/await. Thankfully, there is a newer class called Channel which does that!
private Channel<BTPFrame> _incomingFrameChannel = Channel.CreateBounded<BTPFrame>(5);
So, as the Value_Changed handler on the Read Characteristic is notified, it will pop the BTPFrames onto a channel.
var frame = new BTPFrame(readData);
await _incomingFrameChannel.Writer.WriteAsync(frame);
The handshake code then waits for a frame to appear:
var handshakeResponseFrame = await _incomingFrameChannel.Reader.ReadAsync();
Using a constructor, also moves some of the BTPFrame parsing away into another class, which is always good.
Of course, handshakes a special case.
I need to move the contents of these BTPFrames up a level and into the MessageExchange. To accomplish this, I add another Channel.
public Channel<MessageFrame> MessageFrameChannel = Channel.CreateBounded<MessageFrame>(5);
This time it’s public, so the MessageExchange can access it.
internal async Task<MessageFrame> ReceiveAsync()
{
// Wait for the btpSession to publish a MessageFrame
//
return await _btpSession.MessageFrameChannel.Reader.ReadAsync();
}
So the Commissioner now does this when sending the first PASE message:
await exchange.SendAsync(messageFrame);
var responseMessageFrame = await exchange.ReceiveAsync();
Of course, this is a little bit of pseudo code at this stage. I need to turn the payload of a BTPFrame back into a MessagePayload and MessageFrame!
To do this, I need to be processing the frames as they come in over the Bluetooth connection. This is via the characteristic Indications. I setup a new C# thread, pointing to the Reader of the _incomingFrameChannel. When a frame is received, it will check its flags.
The only messages I’ve received so far are self-contained (they have IsBeginning and IsEnding) set in the ControlFlags. I will have to worry about multipart BTP messages, but not right now.
BTPFrame btpFrame = await _incomingFrameChannel.Reader.ReadAsync();
var isEnding = (btpFrame.ControlFlags & BTPControlFlags.Ending) != 0;
if(isEnding)
{
MessageFrame message = new MessageFrame(btpFrame.Payload);
await MessageFrameChannel.Writer.WriteAsync(message);
}
In response to my I received this payload (with the BTP headers removed):
05 02 9A 00 01 00 00 00 63 69 99 01 18 1F 48 A1 B2 59 CA 56 00 21 16 00 00 00 15 30 01 20 42 D3 2A E6 8D 0F 2E 16 F3 5B D9 E7 3F 8F 43 16 92 86 E2 2C A0 E4 5C 9B 63 C4 0A 0B AB 3B 2C 10 30 02 20 3A BB FA 4E 9E 8E 84 FF CB FF 96 52 FF 40 6D 14 4B 9D 27 76 18 6C 48 70 53 9C BE 05 A5 A7 D8 B3 25 03 DF A4 35 04 25 01 E8 03 30 02 10 53 50 41 4B 45 32 50 20 4B 65 79 20 53 61 6C 74 18 35 05 25 01 F4 01 25 02 2C 01 25 03 A0 0F 24 04 12 24 05 0B 26 06 00 00 04 01 24 07 01 18 18
I then spend some time trying to parse this back into MessageFrame, MessagePayload and TLV data. It took a few passes, but I managed to get it dumping this:
Message received
MessageFlags: 01
SessionId: 00
SecurityFlags: 00
MessageCounter: CB7591C
ExchangeFlags: 00
Protocol OpCode: 21
Exchange Id: 00
ProtocolId: 16
I’m not 100% convinced I have this all working correctly, but the Protocol OpCode value of 0x21 is spot on!

The ProtocolId should be zero though. Another pass through my code and I realised I was reading two things in the wrong order.
ExchangeFlags: 00
Protocol OpCode: 21
Exchange Id: 16
ProtocolId: 00
Perfect! ExchangeId of 0x16, matches my value of 22 (decimal) and the ProtocolId is not 0x00!
It’s time now to focus on parsing the TLV data returned.
Reading the PBKDFParamResponse

This is the TLV structure of the data being returned.
I started adding methods onto the MatterTLV class, to let me read these. This is what I ended up to start with
var payload = responseMessageFrame.MessagePayload.Payload;
payload.OpenStructure();
var initiatorRandomBytes = payload.GetOctetString(1);
var responderRandomBytes = payload.GetOctetString(2);
var responderSessionId = payload.GetUnsignedShort(3);
This clearly needs improving but it works well enough. The next Tag (4) is where it gets more interesting: Crypto_PBKDFParameterSet. This is a nested structure.
Took a little digging in the PDF (the search is rubbish), but I found the definition:

To read that, I just need to open another structure and read two tags from that, like this
payload.OpenStructure(4);
var iterations = payload.GetUnsignedShort(1);
var salt = payload.GetOctetString(2);
payload.CloseStructure();
This returns a value of 1000 for iterations, which seems like a sensible number. I also got a 32 byte array for the salt.
I must admit, I am struggling to understand the TLV schema, but I’m struggling along.
Doing some cryptography
With iterations and salt read (correctly I’m hoping), the next step was to do some cryptography. I now need to construct a pake-1-struct request.

Section 3.10 of the Specification goes into the details. I’ll be honest, not much of this made any sense. The rule of thumb for crypto is never roll your own.
Unfortunately, after a lot of googling, I couldn’t find anything close 😦
Summary
Made really good progress in this session. More and more building blocks going in. Being able to get an actual MessageFrame back from my ESP32 is a significant milestone to hit.
My TLV code is horrendous, but working well enough.
In my next session, I’ll be having a go at building up some of these SPAKE+ functions so I can continue the PASE steps.
Be sure to subscribe to my blog if you’re following along.




Leave a comment