public static string VerifyCookie(string input, out long signTime, IPAddress remoteIp = null, uint validTime = uint.MaxValue) { if (hmacKey == null) { throw new InvalidOperationException(); } signTime = long.MinValue; if (input == null) { return(null); } if (input.Length < 80) //Minimum length (60 byte sig+metadata) { return(null); } if ((input.Length & 3) != 0) //Length of a base64 string must be of multiples of 4 { return(null); } try { var data = Convert.FromBase64String(input.Replace('.', '+').Replace('_', '/').Replace('-', '=')); //throws FormatException var computed = hmac.Value.ComputeHash(data, SigLength, data.Length - SigLength); //Compute signature int trash = 0; for (int i = 0; i < SigLength; i++) //Compare the sig. (Avoiding timing attack) { trash |= data[i] ^ computed[i]; } if (trash != 0) //sig mismatch, reject. { return(null); } signTime = data.GetInt64(TsOffset); var currentTs = UnixTimestamp.CurrentMillisecondTimestamp; if (signTime > currentTs) //Reject anything with a timestamp from the future { return(null); } //Compute the validity period for this verification. //Subtract 1 to treat value 0 (defined as infinite) as greatest. validTime = Math.Min(data.GetUInt32(ValidityOffset) - 1, validTime - 1); if (validTime != uint.MaxValue) { if ((signTime + validTime) < currentTs) //Should be <=, but we've subtracted 1 from the original value. { return(null); } } //all zeroes denotes don't care. if (!data.TrueForRange(IpAddrOffset, IpAddrLength, x => x == 0)) { if (remoteIp == null) //No remote ip provided while a verification is required, reject. { return(null); } if (remoteIp.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) { remoteIp = remoteIp.MapToIPv6(); } var ip = remoteIp.GetAddressBytes(); if (!ArrayEx.RangeEquals(data, IpAddrOffset, ip, 0, IpAddrLength)) { return(null); } } return(cookieEncoding.GetString(data, DataOffset, data.Length - DataOffset)); //throws ArgumentException } catch (FormatException) { return(null); } catch (ArgumentException) { return(null); } }