/// <summary>
 /// Initializes a new instance of the <see cref="GNTPRequest"/> class.
 /// </summary>
 /// <param name="version">The version of the GNTP request.</param>
 /// <param name="directive">The type of GNTP request.</param>
 /// <param name="key">The key used to validate and encrypt the message.</param>
 /// <param name="headers">The collection of headers parsed from the current request.</param>
 /// <param name="applicationName">The name of the application sending the request.</param>
 /// <param name="notificationsToBeRegistered">A collection of the groups of headers for each notification type to be registered.</param>
 /// <param name="callbackContext">The callback context associated with the request.</param>
 public GNTPRequest(string version, RequestType directive, Key key, HeaderCollection headers, string applicationName, List<HeaderCollection> notificationsToBeRegistered, CallbackContext callbackContext)
 {
     this.version = version;
     this.directive = directive;
     this.key = key;
     this.headers = headers;
     this.applicationName = applicationName;
     this.notificationsToBeRegistered = notificationsToBeRegistered;
     this.callbackContext = callbackContext;
 }
        private void ProcessBuffer()
        {
            try
            {
                Data data = new Data(buffer.ToArray());
                string s = data.ToString();
                alreadyReceived.Append(s);

                if (tag == START_TAG)
                {
                    // start looking for GNTP header
                    tag = GNTP_IDENTIFIER_TAG;
                    ContinueProcessing();
                }

                else if (tag == GNTP_IDENTIFIER_TAG)
                {
                    string line = s;
                    Match match = ParseGNTPHeaderLine(line, this.passwordRequired);

                    if (match.Success)
                    {
                        this.version = match.Groups["Version"].Value;
                        if (version == MessageParser.GNTP_SUPPORTED_VERSION)
                        {
                            string d = match.Groups["Directive"].Value;
                            if (Enum.IsDefined(typeof(RequestType), d))
                            {
                                this.directive = (RequestType)Enum.Parse(typeof(RequestType), d);

                                // check for supported but not allowed requests
                                if (this.directive == RequestType.SUBSCRIBE && !this.allowSubscriptions)
                                {
                                    OnError(ErrorCode.NOT_AUTHORIZED, ErrorDescription.SUBSCRIPTIONS_NOT_ALLOWED);
                                }
                                else
                                {
                                    this.encryptionAlgorithm = Cryptography.GetEncryptionType(match.Groups["EncryptionAlgorithm"].Value);
                                    this.ivHex = (match.Groups["IV"] != null ? match.Groups["IV"].Value : null);
                                    if (!String.IsNullOrEmpty(this.ivHex)) this.iv = Cryptography.HexUnencode(this.ivHex);
                                    string keyHash = match.Groups["KeyHash"].Value.ToUpper();

                                    bool authorized = false;
                                    // Any of the following criteria require a password:
                                    //    1. the request did not originate on the local machine or LAN
                                    //    2. the request came from the LAN, but LAN passwords are required
                                    //    2. it is a SUBSCRIBE request (all subscriptions require a password)
                                    //    3. the user's preferences require even local requests to supply a password
                                    // Additionally, even if a password is not required, it will be validated if the
                                    // sending appplication includes one
                                    string errorDescription = ErrorDescription.INVALID_KEY;
                                    if (this.passwordRequired || this.directive == RequestType.SUBSCRIBE || !String.IsNullOrEmpty(keyHash))
                                    {
                                        if (String.IsNullOrEmpty(keyHash))
                                        {
                                            errorDescription = ErrorDescription.MISSING_KEY;
                                        }
                                        else
                                        {
                                            string keyHashAlgorithmType = match.Groups["KeyHashAlgorithm"].Value;
                                            this.keyHashAlgorithm = Cryptography.GetKeyHashType(keyHashAlgorithmType);
                                            string salt = match.Groups["Salt"].Value.ToUpper();
                                            authorized = this.passwordManager.IsValid(keyHash, salt, this.keyHashAlgorithm, this.encryptionAlgorithm, out this.key);
                                        }
                                    }
                                    else
                                    {
                                        authorized = true;
                                        this.key = Key.None;
                                    }

                                    if (authorized)
                                    {
                                        if (this.encryptionAlgorithm == Cryptography.SymmetricAlgorithmType.PlainText)
                                        {
                                            tag = HEADER_TAG;
                                            ContinueProcessing();
                                        }
                                        else
                                        {
                                            tag = ENCRYPTED_HEADERS_TAG;
                                            ContinueProcessing();
                                        }
                                    }
                                    else
                                    {
                                        OnError(ErrorCode.NOT_AUTHORIZED, errorDescription);
                                    }
                                }
                            }
                            else
                            {
                                OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.UNSUPPORTED_DIRECTIVE, d);
                            }
                        }
                        else
                        {
                            OnError(ErrorCode.UNKNOWN_PROTOCOL_VERSION, ErrorDescription.UNSUPPORTED_VERSION, version);
                        }
                    }
                    else
                    {
                        OnError(ErrorCode.UNKNOWN_PROTOCOL, ErrorDescription.MALFORMED_REQUEST);
                    }
                }

                else if (tag == HEADER_TAG)
                {
                    if (s == MessageParser.BLANK_LINE)
                    {
                        // if this is a REGISTER message, check Notifications-Count value
                        // to see how many notification sections to expect
                        if (this.directive == RequestType.REGISTER)
                        {
                            if (this.expectedNotifications > 0)
                            {
                                tag = NOTIFICATION_TYPE_TAG;
                                ContinueProcessing();
                            }
                            else
                            {
                                // a REGISTER request with no notifications is not valid
                                OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.NO_NOTIFICATIONS_REGISTERED);
                            }
                        }
                        else
                        {
                            // otherwise, check the number of resource pointers we got and start reading those
                            this.pointersExpected = GetNumberOfPointers();
                            if (this.pointersExpected > 0)
                            {
                                this.pointersExpectedRemaining = this.pointersExpected;
                                this.currentPointer = 1;
                                tag = RESOURCE_HEADER_TAG;
                                ContinueProcessing();
                            }
                            else
                            {
                                OnMessageParsed();
                            }
                        }
                    }
                    else
                    {
                        Header header = Header.ParseHeader(s);
                        if (header != null)
                        {
                            bool addHeader = true;
                            if (header.Name == Header.APPLICATION_NAME)
                            {
                                this.applicationName = header.Value;
                            }
                            if (header.Name == Header.NOTIFICATIONS_COUNT)
                            {
                                this.expectedNotifications = Convert.ToInt32(header.Value);
                                this.expectedNotificationsRemaining = this.expectedNotifications;
                                this.currentNotification = 1;
                            }
                            if (header.Name == Header.NOTIFICATION_CALLBACK_CONTEXT)
                            {
                                this.callbackData = header.Value;
                            }
                            if (header.Name == Header.NOTIFICATION_CALLBACK_CONTEXT_TYPE)
                            {
                                this.callbackDataType = header.Value;
                            }
                            if (header.Name == Header.NOTIFICATION_CALLBACK_TARGET || header.Name == Header.NOTIFICATION_CALLBACK_CONTEXT_TARGET)   // left in for compatibility
                            {
                                this.callbackUrl = header.Value;
                            }
                            if (header.Name == Header.RECEIVED)
                            {
                                this.requestInfo.PreviousReceivedHeaders.Add(header);
                                addHeader = false;
                            }

                            if (addHeader) this.headers.AddHeader(header);
                        }
                        tag = HEADER_TAG;
                        ContinueProcessing();
                    }
                }

                else if (tag == NOTIFICATION_TYPE_TAG)
                {
                    if (s == MessageParser.BLANK_LINE)
                    {
                        this.expectedNotificationsRemaining--;
                        if (this.expectedNotificationsRemaining > 0)
                        {
                            this.currentNotification++;
                            tag = NOTIFICATION_TYPE_TAG;
                            ContinueProcessing();
                        }
                        else
                        {
                            // otherwise, check the number of resource pointers we got and start reading those
                            this.pointersExpected = GetNumberOfPointers();
                            if (this.pointersExpected > 0)
                            {
                                this.pointersExpectedRemaining = this.pointersExpected;
                                this.currentPointer = 1;
                                tag = RESOURCE_HEADER_TAG;
                                ContinueProcessing();
                            }
                            else
                            {
                                OnMessageParsed();
                            }
                        }
                    }
                    else
                    {
                        if (this.notificationsToBeRegistered.Count < this.currentNotification)
                        {
                            this.notificationsToBeRegistered.Add(new HeaderCollection());
                        }

                        Header header = Header.ParseHeader(s);
                        this.notificationsToBeRegistered[this.currentNotification - 1].AddHeader(header);
                        tag = NOTIFICATION_TYPE_TAG;
                        ContinueProcessing();
                    }
                }

                else if (tag == RESOURCE_HEADER_TAG)
                {
                    if (s == MessageParser.BLANK_LINE)
                    {
                        // we should have found an Identifier header and Length header, or we are just starting a new section
                        Pointer p = this.pointers[this.currentPointer - 1];
                        if (p.Identifier != null && p.Length > 0)
                        {
                            // read #of bytes
                            int length = this.pointers[this.currentPointer - 1].Length;
                            tag = RESOURCE_TAG;
                            ContinueProcessing();
                        }
                        else
                        {
                            tag = RESOURCE_HEADER_TAG;
                            ContinueProcessing();
                        }
                    }
                    else
                    {
                        Header header = Header.ParseHeader(s);
                        // should be Identifer or Length
                        if (header != null)
                        {
                            bool validHeader = false;
                            if (header.Name == Header.RESOURCE_IDENTIFIER)
                            {
                                this.pointers[this.currentPointer - 1].Identifier = header.Value;
                                validHeader = true;
                            }
                            else if (header.Name == Header.RESOURCE_LENGTH)
                            {
                                this.pointers[this.currentPointer - 1].Length = Convert.ToInt32(header.Value);
                                validHeader = true;
                            }
                            else
                            {
                                OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.UNRECOGNIZED_RESOURCE_HEADER, header.Name);
                            }

                            if (validHeader)
                            {
                                tag = RESOURCE_HEADER_TAG;
                                ContinueProcessing();
                            }
                        }
                        else
                        {
                            OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.UNRECOGNIZED_RESOURCE_HEADER);
                        }
                    }
                }

                else if (tag == RESOURCE_TAG)
                {
                    // deal with data bytes
                    byte[] bytes = this.key.Decrypt(data.ByteArray, this.iv);

                    Pointer pointer = this.pointers[this.currentPointer - 1];
                    pointer.ByteArray = bytes;
                    BinaryData binaryData = new BinaryData(pointer.Identifier, pointer.ByteArray);
                    ResourceCache.Add(this.applicationName, binaryData);

                    this.pointersExpectedRemaining--;
                    if (this.pointersExpectedRemaining > 0)
                    {
                        this.currentPointer++;
                        tag = RESOURCE_HEADER_TAG;
                        ContinueProcessing();
                    }
                    else
                    {
                        OnMessageParsed();
                    }
                }

                else if (tag == ENCRYPTED_HEADERS_TAG)
                {
                    // see if a length was specified (the original spec did not require the main encrypted headers portion to specify a length)
                    if (s.StartsWith(Header.RESOURCE_LENGTH))
                    {
                        Header header = Header.ParseHeader(s);
                        if (header != null)
                        {
                            int len = Convert.ToInt32(header.Value);
                            tag = ENCRYPTED_HEADERS_TAG;
                            ContinueProcessing();
                        }
                    }

                    ParseEncryptedMessage(data.ByteArray);
                    if (this.pointersExpected > 0)
                    {
                        tag = RESOURCE_HEADER_TAG;
                        ContinueProcessing();
                    }
                    else
                    {
                        OnMessageParsed();
                    }
                }

                else
                {
                    OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.MALFORMED_REQUEST);
                }
            }
            catch (GrowlException gEx)
            {
                OnError(gEx.ErrorCode, gEx.Message, gEx.AdditionalInfo);
            }
            catch (Exception ex)
            {
                OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.MALFORMED_REQUEST, ex.Message);
            }
        }
 /*
 // TODO: do we ever want to encrypt responses?
 public MessageBuilder(ResponseType messageType, Key key, Cryptography.HashAlgorithmType keyHashAlgorithm, Cryptography.SymmetricAlgorithmType encryptionAlgorithm)
     : this("-" + messageType.ToString(), key, false, Cryptography.HashAlgorithmType.MD5, Cryptography.SymmetricAlgorithmType.PlainText)
 {
 }
  * */
 /// <summary>
 /// Creates a new instance of the <see cref="MessageBuilder"/> class
 /// </summary>
 /// <param name="messageType">The type of message (directive)</param>
 /// <param name="key">The <see cref="Key"/> used to authorize and encrypt the message</param>
 /// <param name="includeKeyHash">Indicates if the key hash should be included in the message or not</param>
 protected MessageBuilder(string messageType, Key key, bool includeKeyHash)
     : base()
 {
     this.messageType = messageType;
     this.key = (key != null ? key : Key.None);
     this.includeKeyHash = (includeKeyHash && (key != null && key != Key.None));
 }
 /// <summary>
 /// Creates a new instance of the <see cref="MessageBuilder"/> class
 /// used to build a request message.
 /// </summary>
 /// <param name="messageType">The <see cref="RequestType"/> of the message</param>
 /// <param name="key">The <see cref="Key"/> used to authorize and encrypt the message</param>
 public MessageBuilder(RequestType messageType, Key key)
     : this(messageType.ToString(), key, true)
 {
 }
Exemple #5
0
        /// <summary>
        /// Compares the provides keyHash and salt to the supplied password to see if they are a match.
        /// </summary>
        /// <param name="password">The password to compare to.</param>
        /// <param name="keyHash">The hex-encoded key hash</param>
        /// <param name="salt">The hex-encoded salt value</param>
        /// <param name="hashAlgorithm">The <see cref="Cryptography.HashAlgorithmType"/> used to generate the key hash</param>
        /// <param name="encryptionAlgorithm">The <see cref="Cryptography.SymmetricAlgorithmType"/> used by this key to do encryption/decryption</param>
        /// <param name="matchingKey">If a match is found, returns the matching <see cref="Key"/>;if no match is found, returns <c>null</c>.</param>
        /// <returns>
        /// <c>true</c> if the key hash and salt match the password;
        /// <c>false</c> otherwise
        /// </returns>
        public static bool Compare(string password, string keyHash, string salt, Cryptography.HashAlgorithmType hashAlgorithm, Cryptography.SymmetricAlgorithmType encryptionAlgorithm, out Key matchingKey)
        {
            matchingKey = null;
            if (!String.IsNullOrEmpty(password))
            {
                byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(password);
                byte[] saltBytes = Cryptography.HexUnencode(salt);
                byte[] keyBasis = new byte[passwordBytes.Length + saltBytes.Length];
                Array.Copy(passwordBytes, 0, keyBasis, 0, passwordBytes.Length);
                Array.Copy(saltBytes, 0, keyBasis, passwordBytes.Length, saltBytes.Length);

                byte[] keyBytes = Cryptography.ComputeHash(keyBasis, hashAlgorithm);
                byte[] keyHashBytes = Cryptography.ComputeHash(keyBytes, hashAlgorithm);
                string actualKeyHash = Cryptography.HexEncode(keyHashBytes);

                if (keyHash == actualKeyHash)
                {
                    matchingKey = new Key();
                    matchingKey.password = password;
                    matchingKey.salt = salt;
                    matchingKey.keyHash = keyHash;
                    matchingKey.hashAlgorithm = hashAlgorithm;
                    matchingKey.encryptionKey = keyBytes;
                    matchingKey.encryptionAlgorithm = encryptionAlgorithm;
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Checks the supplied <paramref name="keyHash"/> against all of the stored passwords to 
        /// see if the hash is valid, and retuns the matching <see cref="Key"/> if a match is found.
        /// </summary>
        /// <param name="keyHash">The hex-encoded hash to validate</param>
        /// <param name="salt">The hex-encoded salt value</param>
        /// <param name="hashAlgorithm">The <see cref="Cryptography.HashAlgorithmType"/> used to generate the hash</param>
        /// <param name="encryptionAlgorithm">The <see cref="Cryptography.SymmetricAlgorithmType"/> used by this key to do encryption/decryption</param>
        /// <param name="matchingKey">Contains the matching <see cref="Key"/> if a match is found</param>
        /// <returns>
        /// <c>true</c> if the hash matches one of the stored password/key values;
        /// <c>false</c> if no match is found
        /// If no match is found, <paramref name="matchingKey"/> will return <c>null</c>.
        /// </returns>
        public bool IsValid(string keyHash, string salt, Cryptography.HashAlgorithmType hashAlgorithm, Cryptography.SymmetricAlgorithmType encryptionAlgorithm, out Key matchingKey)
        {
            matchingKey = null;

            if (String.IsNullOrEmpty(keyHash)) return false;

            keyHash = keyHash.ToUpper();
            foreach (Password password in this.passwords.Values)
            {
                bool match = Key.Compare(password.ActualPassword, keyHash, salt, hashAlgorithm, encryptionAlgorithm, out matchingKey);
                if (match)
                {
                    return true;
                }
            }
            return false;
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="SubscriberKey"/> class.
 /// </summary>
 /// <param name="key">The <see cref="Key"/> to base this key upon.</param>
 /// <param name="subscriberID">The unique subscriber ID</param>
 /// <param name="hashAlgorithm">The <see cref="Cryptography.HashAlgorithmType"/> used when hashing values</param>
 /// <param name="encryptionAlgorithm">The <see cref="Cryptography.SymmetricAlgorithmType"/> used when encrypting values</param>
 public SubscriberKey(Key key, string subscriberID, Cryptography.HashAlgorithmType hashAlgorithm, Cryptography.SymmetricAlgorithmType encryptionAlgorithm)
     : base(key.Password + subscriberID, hashAlgorithm, encryptionAlgorithm)
 {
 }