In WebRTC passing the ice-ufrag and ice-pwd attributes to the client in an SDP answer will result in the client sending those values back as part of the STUN binding request.

For example an answer SDP like:

v=0
...
a=ice-pwd:fpllngzieyoh43e0133ols
a=ice-ufrag:6k1hh2gd
...

Will result in a STUN binding request that looks similar to:

00000000  00 01 00 50 21 12 a4 42  a5 89 a1 52 2c f6 d6 1f  |...P!..B...R,...|
00000010  12 f2 55 69 00 06 00 11  36 6b 31 68 68 32 67 64  |..Ui....6k1hh2gd|
00000020  3a 38 62 63 31 64 62 61  34 00 00 00 00 25 00 00  |:8bc1dba4....%..|
00000030  00 24 00 04 6e 7f 00 ff  80 2a 00 08 e4 53 2a 52  |.$..n....*...S*R|
00000040  ba 06 bd e9 00 08 00 14  bd e0 f2 7b f7 d8 71 70  |...........{..qp|
00000050  67 44 86 9a a2 d9 a3 a0  27 d6 a9 d1 80 28 00 04  |gD......'....(..|
00000060  04 36 14 1e 

Finding the USERNAME

According to the STUN protocol the USERNAME is defined by the bytes 0x0006 followed by a two byte pad 0x00 and the length of the user name (0x11 or 17). Everything in STUN is in TLV format (type, length, value). So the bytes of the USERNAME attribute in this STUN binding request are

0x00 0x06                               # This is a USERNAME 
0x00                                    # Padding
0x11                                    # The length is 17 bytes long
0x36 0x6b 0x31 0x68 0x68 0x32 0x67 0x64 # Answer User name 6k1hh2gd
0x3a                                    # Colon separator
0x38 0x62 0x63 0x31 0x64 0x62 0x61 0x34 # Offer User name 8bc1dba4

The user name after the colon (8bc1dba4) is the user defined by the client when it made its initial SDP offer. I’m not going to go into the offer/answer flow of SDP in this post but just know that this is a value that is handed to you by the client.

Finding the MESSAGE-INTEGRITY

To verify no one has tampered with this STUN binding request we need to compute the same MESSAGE-INTEGRITY hash that’s in the request. The MESSAGE-INTEGRITY attribute is identified by the bytes 0x0008 followed by a two byte pad 0x00 and the length of the hash (0x14 or 20). So the MESSAGE-INTEGRITY attribute of our request is

0x00 0x08                               # This is a MESSAGE-INTEGRITY hash 
0x00                                    # Padding
0x14                                    # The length is 20 bytes long
0xbd 0xe0 0xf2 0x7b 0xf7 0xd8 0x71 0x70 # This is the hash
0x67 0x44 0x86 0x9a 0xa2 0xd9 0xa3 0xa0 
0x27 0xd6 0xa9 0xd1

Verifying the request

We calculate this by HMAC-SHA1ing all the bytes up to the MESSAGE-INTEGITY bytes with the ice-pwd attribute that was sent to the client in the SDP answer (fpllngzieyoh43e0133ols).

This is the subset of our example request excluding the MESSAGE-INTEGRITY bytes as well as everything after that.

0x00 0x01 0x00 0x50 0x21 0x12 0xa4 0x42  
0xa5 0x89 0xa1 0x52 0x2c 0xf6 0xd6 0x1f
0x12 0xf2 0x55 0x69 0x00 0x06 0x00 0x11  
0x36 0x6b 0x31 0x68 0x68 0x32 0x67 0x64
0x3a 0x38 0x62 0x63 0x31 0x64 0x62 0x61  
0x34 0x00 0x00 0x00 0x00 0x25 0x00 0x00
0x00 0x24 0x00 0x04 0x6e 0x7f 0x00 0xff  
0x80 0x2a 0x00 0x08 0xe4 0x53 0x2a 0x52
0xba 0x06 0xbd 0xe9

Unfortunately we can’t just throw that subset into the HMAC function and call it a day. We need to alter the length byte of the subset so that it’s the subset’s length and not the length of the full request. This length byte is at index 3 and, in our example, is 0x50 which is 80.

So we’ll calculate the subset length and, even though it’s not in our subset, we include the length of the MESSAGE-INTEGRITY section header in our calculation. It will always have a length of 4 bytes since everything is in TLV format.

LEN(
    0x00 0x01 0x00 0x50 0x21 0x12 0xa4 0x42  
    0xa5 0x89 0xa1 0x52 0x2c 0xf6 0xd6 0x1f
    0x12 0xf2 0x55 0x69 0x00 0x06 0x00 0x11  
    0x36 0x6b 0x31 0x68 0x68 0x32 0x67 0x64
    0x3a 0x38 0x62 0x63 0x31 0x64 0x62 0x61  
    0x34 0x00 0x00 0x00 0x00 0x25 0x00 0x00
    0x00 0x24 0x00 0x04 0x6e 0x7f 0x00 0xff  
    0x80 0x2a 0x00 0x08 0xe4 0x53 0x2a 0x52
    0xba 0x06 0xbd 0xe9
) + 4

That gives us a length of 72 which in hex is 0x48. We’ll take that value and replace the old length byte (0x50) in our subset with it.

Now we can HMAC the subset.

HMAC-SHA1(
    0x00 0x01 0x00 0x48 0x21 0x12 0xa4 0x42  
    0xa5 0x89 0xa1 0x52 0x2c 0xf6 0xd6 0x1f
    0x12 0xf2 0x55 0x69 0x00 0x06 0x00 0x11  
    0x36 0x6b 0x31 0x68 0x68 0x32 0x67 0x64
    0x3a 0x38 0x62 0x63 0x31 0x64 0x62 0x61  
    0x34 0x00 0x00 0x00 0x00 0x25 0x00 0x00
    0x00 0x24 0x00 0x04 0x6e 0x7f 0x00 0xff  
    0x80 0x2a 0x00 0x08 0xe4 0x53 0x2a 0x52
    0xba 0x06 0xbd 0xe9,
    fpllngzieyoh43e0133ols
)

Doing that results in the hash

0xbd 0xe0 0xf2 0x7b 0xf7 0xd8 0x71 0x70 
0x67 0x44 0x86 0x9a 0xa2 0xd9 0xa3 0xa0 
0x27 0xd6 0xa9 0xd1 

Which is equivalent to the request’s MESSAGE-INTEGRITY hash. 👍