def parse_message(data, integrity_key=None):
    """
    Parses a STUN message.

    If the ``integrity_key`` parameter is given, the message's HMAC will be verified.
    """
    if len(data) < HEADER_LENGTH:
        raise ValueError('STUN message length is less than 20 bytes')
    message_type, length, cookie, transaction_id = unpack('!HHI12s', data[0:HEADER_LENGTH])
    if len(data) != HEADER_LENGTH + length:
        raise ValueError('STUN message length does not match')

    attributes = OrderedDict()
    pos = HEADER_LENGTH
    while pos <= len(data) - 4:
        attr_type, attr_len = unpack('!HH', data[pos:pos + 4])
        v = data[pos + 4:pos + 4 + attr_len]
        pad_len = 4 * ((attr_len + 3) // 4) - attr_len
        if attr_type in ATTRIBUTES_BY_TYPE:
            _, attr_name, attr_pack, attr_unpack = ATTRIBUTES_BY_TYPE[attr_type]
            if attr_unpack == unpack_xor_address:
                attributes[attr_name] = attr_unpack(v, transaction_id=transaction_id)
            else:
                attributes[attr_name] = attr_unpack(v)

            if attr_name == 'FINGERPRINT':
                if attributes[attr_name] != message_fingerprint(data[0:pos]):
                    raise ValueError('STUN message fingerprint does not match')
            elif attr_name == 'MESSAGE-INTEGRITY':
                if (integrity_key is not None and
                   attributes[attr_name] != message_integrity(data[0:pos], integrity_key)):
                    raise ValueError('STUN message integrity does not match')

        pos += 4 + attr_len + pad_len
    return Message(
        message_method=message_type & 0x3eef,
        message_class=message_type & 0x0110,
        transaction_id=transaction_id,
        attributes=attributes)
Parses a STUN message.

    If the ``integrity_key`` parameter is given, the message's HMAC will be verified.