WebRTC笔记2-STUN算法

XOR-MAPPED-ADDRESS

  • IP真实IP地址 XOR magic cookie (2112a442)
  • PORT,真实端口 XOR magic cookie (2112a442) 前2字节

MESSAGE-INTEGRITY

anwser
a=ice-ufrag:SDZV
a=ice-pwd:n31QqnImNpUctZbD+1ZwLZBF

offer
a=ice-ufrag:o2lH
a=ice-pwd:E+LjzA6PVnYpSwqCl6mG01

SDP 有关STUN认证的参数。

  • 上面的是local的,下面的是remote的。
  • 本机发送到远端需要使用remote的desc。

下图所示


WebRTC笔记2-STUN算法_第1张图片
屏幕快照 2017-02-20 19.45.26.png

size为:96
header size为:20
message size为:76
原始:

00 01 00 4c 21 12 a4 42 53 56 79 33 73 54 53 6f
2b 7a 45 67 00 06 00 09 6f 32 6c 48 3a 53 44 5a
56 00 00 00 c0 57 00 04 00 03 00 0a 80 29 00 08
76 17 87 96 ae 1e 0c f1 00 24 00 04 6e 7e 1e ff
00 08 00 14 53 1c 5d 34 b1 3b 2f b4 a0 3e fe 55
92 de 93 a8 72 6f 76 eb 80 28 00 04 4d 10 60 2d

去除 MESSAGE-INTEGRITY 之下的 Attributes之后,
message size应该是68
size - header size - other attr size
96-20-8=68

下面标粗的部分(这里的message size跟实际长度并不匹配)
去除之后的应该是

00 01 00 44 21 12 a4 42 53 56 79 33 73 54 53 6f
2b 7a 45 67 00 06 00 09 6f 32 6c 48 3a 53 44 5a
56 00 00 00 c0 57 00 04 00 03 00 0a 80 29 00 08
76 17 87 96 ae 1e 0c f1 00 24 00 04 6e 7e 1e ff

hmac-sha1计算结果
https://www.liavaag.org/English/SHA-Generator/HMAC

WebRTC笔记2-STUN算法_第2张图片
屏幕快照 2017-02-20 20.19.37.png

代码

// Verifies a STUN message has a valid MESSAGE-INTEGRITY attribute, using the
// procedure outlined in RFC 5389, section 15.4.
bool StunMessage::ValidateMessageIntegrity(const char* data, size_t size,
                                           const std::string& password) {
  // Verifying the size of the message.
  if ((size % 4) != 0) {
    return false;
  }

  // Getting the message length from the STUN header.
  uint16 msg_length = talk_base::GetBE16(&data[2]);
  if (size != (msg_length + kStunHeaderSize)) {
    return false;
  }

  // Finding Message Integrity attribute in stun message.
  size_t current_pos = kStunHeaderSize;
  bool has_message_integrity_attr = false;
  while (current_pos < size) {
    uint16 attr_type, attr_length;
    // Getting attribute type and length.
    attr_type = talk_base::GetBE16(&data[current_pos]);
    attr_length = talk_base::GetBE16(&data[current_pos + sizeof(attr_type)]);

    // If M-I, sanity check it, and break out.
    if (attr_type == STUN_ATTR_MESSAGE_INTEGRITY) {
      if (attr_length != kStunMessageIntegritySize ||
          current_pos + attr_length > size) {
        return false;
      }
      has_message_integrity_attr = true;
      break;
    }

    // Otherwise, skip to the next attribute.
    current_pos += sizeof(attr_type) + sizeof(attr_length) + attr_length;
    if ((attr_length % 4) != 0) {
      current_pos += (4 - (attr_length % 4));
    }
  }

  if (!has_message_integrity_attr) {
    return false;
  }

  // Getting length of the message to calculate Message Integrity.
  size_t mi_pos = current_pos;
  talk_base::scoped_ptr temp_data(new char[current_pos]);
  memcpy(temp_data.get(), data, current_pos);
  if (size > mi_pos + kStunAttributeHeaderSize + kStunMessageIntegritySize) {
    // Stun message has other attributes after message integrity.
    // Adjust the length parameter in stun message to calculate HMAC.
    size_t extra_offset = size -
        (mi_pos + kStunAttributeHeaderSize + kStunMessageIntegritySize);
    size_t new_adjusted_len = size - extra_offset - kStunHeaderSize;

    // Writing new length of the STUN message @ Message Length in temp buffer.
    //      0                   1                   2                   3
    //      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    //     |0 0|     STUN Message Type     |         Message Length        |
    //     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    talk_base::SetBE16(temp_data.get() + 2,
                       static_cast(new_adjusted_len));
  }

  char hmac[kStunMessageIntegritySize];
  size_t ret = talk_base::ComputeHmac(talk_base::DIGEST_SHA_1,
                                      password.c_str(), password.size(),
                                      temp_data.get(), mi_pos,
                                      hmac, sizeof(hmac));
  ASSERT(ret == sizeof(hmac));
  if (ret != sizeof(hmac))
    return false;

  // Comparing the calculated HMAC with the one present in the message.
  return (std::memcmp(data + current_pos + kStunAttributeHeaderSize,
                      hmac, sizeof(hmac)) == 0);
}

FINGERPRINT

按照协议描述当MESSAGE-INTEGRITY存在时,FINGERPRINT必须存在。
FINGERPRINT的算法:

  • FINGERPRINT上面的所有部分进行CRC-32运算,然后XOR 0x5354554E
  • The FINGERPRINT attribute MAY be present in all STUN messages. The value of the attribute is computed as the CRC-32 of the STUN message up to (but excluding) the FINGERPRINT attribute itself, XOR'ed with the 32-bit value 0x5354554e (the XOR helps in cases where an application packet is also using CRC-32 in it). The 32-bit CRC is the one defined in ITU V.42 [ITU.V42.2002], which has a generator polynomial of x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1. When present, the FINGERPRINT attribute MUST be the last attribute in the message, and thus will appear after MESSAGE-INTEGRITY.

00 01 00 4c 21 12 a4 42 35 48 65 4d 6c 49 4d 39
41 43 66 38 00 06 00 09 49 53 4b 77 3a 39 75 32
71 00 00 00 c0 57 00 04 00 01 00 0a 80 2a 00 08
18 d2 67 dd 44 ca 9c c9 00 24 00 04 6e 7e 1e fe
00 08 00 14 a2 d7 12 3a e5 77 82 5e 03 4e cc 2a
34 80 33 55 c3 e0 4a d7 80 28 00 04 fc ea f7 45

去掉后面8字节

00 01 00 4c 21 12 a4 42 35 48 65 4d 6c 49 4d 39
41 43 66 38 00 06 00 09 49 53 4b 77 3a 39 75 32
71 00 00 00 c0 57 00 04 00 01 00 0a 80 2a 00 08
18 d2 67 dd 44 ca 9c c9 00 24 00 04 6e 7e 1e fe
00 08 00 14 a2 d7 12 3a e5 77 82 5e 03 4e cc 2a
34 80 33 55 c3 e0 4a d7

进行crc-32运算 选HEX
https://www.lammertbies.nl/comm/info/crc-calculation.html
0xAFBEA20B ^ 0x5354554E
http://xor.pw/
结果
0xfceaf745。对应FINGERPRINT的值

贴代码

// Verifies a message is in fact a STUN message, by performing the checks
// outlined in RFC 5389, section 7.3, including the FINGERPRINT check detailed
// in section 15.5.
bool StunMessage::ValidateFingerprint(const char* data, size_t size) {
  // Check the message length.
  size_t fingerprint_attr_size =
      kStunAttributeHeaderSize + StunUInt32Attribute::SIZE;
  if (size % 4 != 0 || size < kStunHeaderSize + fingerprint_attr_size)
    return false;

  // Skip the rest if the magic cookie isn't present.
  const char* magic_cookie =
      data + kStunTransactionIdOffset - kStunMagicCookieLength;
  if (talk_base::GetBE32(magic_cookie) != kStunMagicCookie)
    return false;

  // Check the fingerprint type and length.
  const char* fingerprint_attr_data = data + size - fingerprint_attr_size;
  if (talk_base::GetBE16(fingerprint_attr_data) != STUN_ATTR_FINGERPRINT ||
      talk_base::GetBE16(fingerprint_attr_data + sizeof(uint16)) !=
          StunUInt32Attribute::SIZE)
    return false;

  // Check the fingerprint value.
  uint32 fingerprint =
      talk_base::GetBE32(fingerprint_attr_data + kStunAttributeHeaderSize);
  return ((fingerprint ^ STUN_FINGERPRINT_XOR_VALUE) ==
      talk_base::ComputeCrc32(data, size - fingerprint_attr_size));
}

const uint32 STUN_FINGERPRINT_XOR_VALUE = 0x5354554E;

bool StunMessage::AddFingerprint() {
  // Add the attribute with a dummy value. Since this is a known attribute,
  // it can't fail.
  StunUInt32Attribute* fingerprint_attr =
     new StunUInt32Attribute(STUN_ATTR_FINGERPRINT, 0);
  VERIFY(AddAttribute(fingerprint_attr));

  // Calculate the CRC-32 for the message and insert it.
  talk_base::ByteBuffer buf;
  if (!Write(&buf))
    return false;

  int msg_len_for_crc32 = static_cast(
      buf.Length() - kStunAttributeHeaderSize - fingerprint_attr->length());
  uint32 c = talk_base::ComputeCrc32(buf.Data(), msg_len_for_crc32);

  // Insert the correct CRC-32, XORed with a constant, into the attribute.
  fingerprint_attr->SetValue(c ^ STUN_FINGERPRINT_XOR_VALUE);
  return true;
}

你可能感兴趣的:(WebRTC笔记2-STUN算法)