Source

TwilioFluent / TwilioFluent.TwiML / TwilioSignature.cs

Full commit
//   Copyright 2011 Nicholas Sivo
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.


namespace TwilioFluent.TwiML {
    using System;

    /// <summary>
    /// Represents a Twilio signature.
    /// </summary>
    public struct TwilioSignature : IEquatable<TwilioSignature> {
        private const int SignatureSize = 20;   // SHA1
        private readonly byte[] _signature;

        private TwilioSignature(byte[] signature) {
            _signature = signature;
        }

        /// <summary>
        /// Creates a TwilioSignature from the header passed by Twilio.
        /// </summary>
        /// <param name="headerValue">The signature header value passed by Twlio.</param>
        public static TwilioSignature FromHeader(string headerValue) {
            if (headerValue == null)
                throw new ArgumentNullException("headerValue");
            return new TwilioSignature(Convert.FromBase64String(headerValue));
        }

        /// <summary>
        /// Creates a TwilioSignature from a byte array.
        /// </summary>
        /// <param name="signature">The signature.</param>
        public static TwilioSignature FromHash(byte[] signature) {
            if (signature == null)
                throw new ArgumentNullException("signature");
            if (signature.Length != SignatureSize)
                throw new ArgumentOutOfRangeException("signature", string.Format("signature must be {0} bytes.", SignatureSize));
            return new TwilioSignature(signature);
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        public bool Equals(TwilioSignature other) {
            // Time invariant comparison (always the same number of operations)
            // Prevents things like this: http://rdist.root.org/2009/05/28/timing-attack-in-google-keyczar-library/
            var errors = 0;
            for (var i = 0; i < _signature.Length; ++i) {
                errors |= (_signature[i] ^ other._signature[i]);
            }
            return (errors == 0);
        }

        /// <summary>
        /// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
        /// </summary>
        /// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
        /// <returns>
        ///   <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
        /// </returns>
        public override bool Equals(object obj) {
            return obj != null
                && obj is TwilioSignature
                && Equals((TwilioSignature)obj);
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        public static bool operator ==(TwilioSignature a, TwilioSignature b) {
            return a.Equals(b);
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        public static bool operator !=(TwilioSignature a, TwilioSignature b) {
            return !a.Equals(b);
        }

        /// <summary>
        /// Returns a hash code for this instance.
        /// </summary>
        /// <returns>
        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
        /// </returns>
        public override int GetHashCode() {
            var code = 0;
            for (var i = 0; i < _signature.Length; ++i)
                code ^= BitConverter.ToInt32(_signature, i);
            return code;
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString() {
            return Convert.ToBase64String(_signature);
        }
    }
}