/// <summary> /// Constructs a new StunAttribute /// </summary> /// <param name="type">The type of this StunAttribute</param> /// <param name="typeBytes">The value of the type of this StunAttribute in bytes</param> /// <param name="value">The value of this StunAttribute</param> public StunAttribute(StunAttributeType type, byte[] typeBytes, byte[] value) { if (typeBytes == null) { throw new ArgumentNullException("value", "Cannot be null"); } if (value == null) { throw new ArgumentNullException("value", "Cannot be null"); } switch (type) { case StunAttributeType.Software: case StunAttributeType.Realm: case StunAttributeType.Nonce: case StunAttributeType.ErrorCode: if (value.Length > 763) { throw new ArgumentOutOfRangeException("value", "Cannot be greater than 763 bytes for the given type as described in RFC 5389"); } break; case StunAttributeType.Username: if (value.Length > 513) { throw new ArgumentOutOfRangeException("value", "Cannot be greater than 513 bytes for the given type as described in RFC 5389"); } break; } switch (type) { case StunAttributeType.Realm: case StunAttributeType.Username: String saslPrepValue = new SASLprep().Prepare(StunMessage.Encoder.GetString(value)); value = StunMessage.Encoder.GetBytes(saslPrepValue); break; } this.TypeBytes = typeBytes; this.Type = type; this.Value = value; }
public void AddMessageIntegrity(NetworkSerializer packet) { string saslPassword = new SASLprep().Prepare(password); MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); string valueToHashMD5 = string.Format("{0}:{1}:{2}", username, realm, saslPassword); byte[] hmacSha1Key = md5.ComputeHash(Encoding.UTF8.GetBytes(valueToHashMD5)); HMACSHA1 hmacSha1 = new HMACSHA1(hmacSha1Key); byte[] hmacBytes = hmacSha1.ComputeHash(packet.ByteBuffer, 0, packet.byteLength); packet.Write((ushort)STUNAttribute.MessageIntegrity); packet.Write((ushort)hmacBytes.Length); packet.Write(hmacBytes); }
/// <summary> /// Add the MESSAGE-INTEGRITY attribute to the message. /// This should be fired just before a call to StunClient.SendMessage as it needs existing /// attribute to be computed. But these attributes MUST be added before the MESSAGE-ATTRIBUTE /// except for FINGERPRINT attribute which MUST be added at the end of the StunMessage /// and also used in the MESSAGE-INTEGRITY computation /// </summary> /// <param name="password">The password used to authenticate the StunMessage</param> /// <param name="useLongTermCredentials">True if this StunMessage should authenticate using longterm credentials</param> public void AddMessageIntegrity(String password, Boolean useLongTermCredentials) { password = new SASLprep().Prepare(password); byte[] hmacSha1Key; if (useLongTermCredentials) { if (this.Stun.Username == null) { throw new ArgumentException("USERNAME attribute is mandatory for long-term credentials MESSAGE-INTEGRITY creation", "this.Username"); } if (this.Stun.Realm == null) { throw new ArgumentException("REALM attribute is mandatory for long-term credentials MESSAGE-INTEGRITY creation", "this.Realm"); } using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider()) { String valueToHashMD5 = String.Format(CultureInfo.CurrentCulture, "{0}:{1}:{2}", this.Stun.Username.ValueString, this.Stun.Realm.ValueString, password); hmacSha1Key = md5.ComputeHash(StunMessage.Encoder.GetBytes(valueToHashMD5)); } } else { hmacSha1Key = StunMessage.Encoder.GetBytes(password); } StunAttribute messageIntegrity = new StunAttribute(StunAttributeType.MessageIntegrity, this.ComputeHMAC(hmacSha1Key)); this.SetAttribute(messageIntegrity); }