/* * * Code derived and adapted from the Jitsi client side STUN framework. * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.restcomm.media.stun.messages.attributes.general; import java.util.zip.CRC32; import org.restcomm.media.stun.StunException; import org.restcomm.media.stun.messages.attributes.StunAttribute; /** * The FINGERPRINT attribute is used to distinguish STUN packets from packets of * other protocols. It 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. * <p> * The FINGERPRINT attribute can aid in distinguishing STUN packets from packets * of other protocols. See Section 8. * <p> * As with MESSAGE-INTEGRITY, the CRC used in the FINGERPRINT attribute covers * the length field from the STUN message header. Therefore, this value must be * correct and include the CRC attribute as part of the message length, prior to * computation of the CRC. When using the FINGERPRINT attribute in a message, * the attribute is first placed into the message with a dummy value, then the * CRC is computed, and then the value of the attribute is updated. If the * MESSAGE-INTEGRITY attribute is also present, then it must be present with the * correct message-integrity value before the CRC is computed, since the CRC is * done over the value of the MESSAGE-INTEGRITY attribute as well. */ public class FingerprintAttribute extends StunAttribute implements ContextDependentAttribute { public static final String NAME = "FINGERPRINT"; private static final char DATA_LENGTH = 4; /** * The value that we need to XOR the CRC with. The XOR helps in cases where * an application packet is also using CRC-32 in it). */ public static final byte[] XOR_MASK = { 0x53, 0x54, 0x55, 0x4e }; /** * The CRC32 checksum that this attribute is carrying. Only used in incoming * messages. */ private byte[] crc; /** * Returns the CRC32 checksum that this attribute is carrying. Only makes * sense for incoming messages and hence only set for them. * * @return the CRC32 checksum that this attribute is carrying or * <tt>null</tt> if it has not been set. */ public byte[] getChecksum() { return crc; } public FingerprintAttribute() { super(StunAttribute.FINGERPRINT); } @Override public char getDataLength() { return DATA_LENGTH; } @Override public String getName() { return NAME; } @Override public boolean equals(Object other) { if (other == null || !(other instanceof FingerprintAttribute)) { return false; } if (other == this) { return true; } FingerprintAttribute att = (FingerprintAttribute) other; if (att.getAttributeType() != this.getAttributeType() || att.getDataLength() != this.getDataLength()) { return false; } return true; } @Override public byte[] encode() throws UnsupportedOperationException { throw new UnsupportedOperationException( "This operation is not supported because the FINGERPRINT " + "attribute requires information from a context."); } @Override protected void decodeAttributeBody(byte[] data, char offset, char length) throws StunException { if (length != DATA_LENGTH) { throw new StunException("Invalid lenght " + length); } byte[] incomingCrcBytes = new byte[4]; incomingCrcBytes[0] = data[offset]; incomingCrcBytes[1] = data[offset + 1]; incomingCrcBytes[2] = data[offset + 2]; incomingCrcBytes[3] = data[offset + 3]; this.crc = incomingCrcBytes; } public byte[] encode(byte[] data, int offset, int length) { char type = getAttributeType(); byte binValue[] = new byte[HEADER_LENGTH + getDataLength()]; // Type binValue[0] = (byte) (type >> 8); binValue[1] = (byte) (type & 0x00FF); // Length binValue[2] = (byte) (getDataLength() >> 8); binValue[3] = (byte) (getDataLength() & 0x00FF); // calculate the check sum byte[] xorCrc32 = calculateXorCRC32(data, offset, length); // copy into the attribute; binValue[4] = xorCrc32[0]; binValue[5] = xorCrc32[1]; binValue[6] = xorCrc32[2]; binValue[7] = xorCrc32[3]; return binValue; } /** * Calculates and returns the CRC32 checksum for <tt>message</tt> after * applying the <tt>XOR_MASK</tt> specified by RFC 5389. * * @param message * the message whose checksum we'd like to have * @param offset * the location in <tt>message</tt> where the actual message * starts. * @param len * the number of message bytes in <tt>message</tt> * * @return the CRC value that should be sent in a <tt>FINGERPRINT</tt> * attribute traveling in the <tt>message</tt> message. */ public static byte[] calculateXorCRC32(byte[] message, int offset, int len) { // Check whether the CRC really is what it is supposed to be. // Re-calculate the check sum CRC32 checksum = new CRC32(); checksum.update(message, offset, len); long crc = checksum.getValue(); byte[] xorCRC32 = new byte[4]; xorCRC32[0] = (byte) ((byte) ((crc >> 24) & 0xff) ^ XOR_MASK[0]); xorCRC32[1] = (byte) ((byte) ((crc >> 16) & 0xff) ^ XOR_MASK[1]); xorCRC32[2] = (byte) ((byte) ((crc >> 8) & 0xff) ^ XOR_MASK[2]); xorCRC32[3] = (byte) ((byte) (crc & 0xff) ^ XOR_MASK[3]); return xorCRC32; } }