Example #1
0
        public string KeyChallengeResponse2(string cc, string cr, KeyContainerClass kc, int securityLevel, out bool authorised)
        {
            string response = null;

            this.cc = cc;
            this.cr = KeePassLib.Utility.MemUtil.ByteArrayToHexString(Utils.Hash("1" + kc.Key + this.sc + this.cc)).ToLower();
            if (cr != this.cr)
            {
                authorised = false;
                KPRPCMessage data2client = new KPRPCMessage();
                data2client.protocol = "setup";
                data2client.version  = ProtocolVersion;
                data2client.error    = new Error(ErrorCode.AUTH_FAILED, new string[] { "Keys do not match" });
                response             = Jayrock.Json.Conversion.JsonConvert.ExportToString(data2client);
            }
            else
            {
                this.sr    = KeePassLib.Utility.MemUtil.ByteArrayToHexString(Utils.Hash("0" + kc.Key + this.sc + this.cc)).ToLower();
                authorised = true;

                KPRPCMessage data2client = new KPRPCMessage();
                data2client.protocol          = "setup";
                data2client.key               = new KeyParams();
                data2client.key.sr            = this.sr;
                data2client.key.securityLevel = securityLevel;
                data2client.version           = ProtocolVersion;
                response = Jayrock.Json.Conversion.JsonConvert.ExportToString(data2client);
            }
            return(response);
        }
Example #2
0
        public string KeyChallengeResponse2(string cc, string cr, KeyContainerClass kc, int securityLevel, out bool authorised)
        {
            string response = null;
            this.cc = cc;
            this.cr = KeePassLib.Utility.MemUtil.ByteArrayToHexString(Utils.Hash("1" + kc.Key + this.sc + this.cc)).ToLower();
            if (cr != this.cr)
            {
                authorised = false;
                KPRPCMessage data2client = new KPRPCMessage();
                data2client.protocol = "setup";
                data2client.version = ProtocolVersion;
                data2client.error = new Error(ErrorCode.AUTH_FAILED, new string[] { "Keys do not match" });
                response = Jayrock.Json.Conversion.JsonConvert.ExportToString(data2client);
            }
            else
            {
                this.sr = KeePassLib.Utility.MemUtil.ByteArrayToHexString(Utils.Hash("0" + kc.Key + this.sc + this.cc)).ToLower();
                authorised = true;

                KPRPCMessage data2client = new KPRPCMessage();
                data2client.protocol = "setup";
                data2client.key = new KeyParams();
                data2client.key.sr = this.sr;
                data2client.key.securityLevel = securityLevel;
                data2client.version = ProtocolVersion;
                response = Jayrock.Json.Conversion.JsonConvert.ExportToString(data2client);
            }
            return response;
        }
Example #3
0
        string SRPProofToServer(KPRPCMessage srpem)
        {
            SRPParams srp = srpem.srp;

            KPRPCMessage data2client = new KPRPCMessage();

            data2client.protocol  = "setup";
            data2client.srp       = new SRPParams();
            data2client.srp.stage = "proofToClient";
            data2client.version   = ProtocolVersion;

            if (string.IsNullOrEmpty(srp.M))
            {
                data2client.error = new Error(ErrorCode.AUTH_MISSING_PARAM, new string[] { "M" });
            }
            else
            {
                _srp.Authenticate(srp.M);

                if (!_srp.Authenticated)
                {
                    data2client.error = new Error(ErrorCode.AUTH_FAILED, new string[] { "Keys do not match" });
                }
                else
                {
                    data2client.srp.M2            = _srp.M2;
                    data2client.srp.securityLevel = securityLevel;
                    KeyContainer = new KeyContainerClass(_srp.Key, DateTime.UtcNow.AddSeconds(KeyExpirySeconds), userName, clientName);
                    Authorised   = true;
                    // We assume the user has checked the client name as part of the initial SRP setup so it's fairly safe to use it to determine the type of client connection to which we want to promote our null connection
                    KPRPC.PromoteNullRPCClient(this, clientName);
                    KPRPC.InvokeMainThread(new HideAuthDialogDelegate(HideAuthDialog));

                    // If we've never shown the user the welcome screen and have never
                    // known a KeeFox add-on from the previous KPRPC protocol, show it now
                    bool welcomeDisplayed = KPRPC._host.CustomConfig.GetBool("KeePassRPC.KeeFoxWelcomeDisplayed", false);
                    if (!welcomeDisplayed &&
                        string.IsNullOrEmpty(KPRPC._host.CustomConfig.GetString("KeePassRPC.knownClients.KeeFox Firefox add-on")))
                    {
                        KPRPC.InvokeMainThread(new KeePassRPCExt.WelcomeKeeFoxUserDelegate(KPRPC.WelcomeKeeFoxUser));
                    }
                    if (!welcomeDisplayed)
                    {
                        KPRPC._host.CustomConfig.SetBool("KeePassRPC.KeeFoxWelcomeDisplayed", true);
                    }
                }
            }

            return(Jayrock.Json.Conversion.JsonConvert.ExportToString(data2client));
        }
Example #4
0
        public string Decrypt(JSONRPCContainer jsonrpcEncrypted)
        {
            if (string.IsNullOrEmpty(jsonrpcEncrypted.message) ||
                string.IsNullOrEmpty(jsonrpcEncrypted.iv) ||
                string.IsNullOrEmpty(jsonrpcEncrypted.hmac))
            {
                return(null);
            }

            KeyContainerClass kc = this.KeyContainer;
            SHA1 sha             = new SHA1CryptoServiceProvider();

            byte[] rawKeyBytes;
            byte[] keyBytes;
            byte[] messageBytes;
            byte[] IVBytes;

            // Get the raw bytes that are used to calculate the HMAC
            try
            {
                rawKeyBytes  = KeePassLib.Utility.MemUtil.HexStringToByteArray(kc.Key);
                keyBytes     = sha.ComputeHash(rawKeyBytes);
                messageBytes = Convert.FromBase64String(jsonrpcEncrypted.message);
                IVBytes      = Convert.FromBase64String(jsonrpcEncrypted.iv);
            }
            catch (FormatException)
            {
                // Should only happen if there is a fault with the client end
                // of the protocol or if an attacker tries to inject invalid data
                return(null);
            }
            catch (ArgumentNullException)
            {
                // kc.Key must = null
                return(null);
            }

            // These calls can throw a variety of different exceptions but
            // I can't see why they would so we will not try to differentiate the cause of them
            try
            {
                byte[] ourHmacSourceBytes = new byte[keyBytes.Length + messageBytes.Length + IVBytes.Length];
                Array.Copy(keyBytes, ourHmacSourceBytes, keyBytes.Length);
                Array.Copy(messageBytes, 0, ourHmacSourceBytes, keyBytes.Length, messageBytes.Length);
                Array.Copy(IVBytes, 0, ourHmacSourceBytes, keyBytes.Length + messageBytes.Length, IVBytes.Length);

                // Calculate the HMAC
                byte[] ourHmac = sha.ComputeHash(ourHmacSourceBytes);

                // Check our HMAC against the one supplied by the client
                if (Convert.ToBase64String(ourHmac) != jsonrpcEncrypted.hmac)
                {
                    //TODO2: If we ever want/need to include some DOS protection we
                    // could use this error condition to throttle requests from badly behaved clients
                    if (KPRPC.logger != null)
                    {
                        KPRPC.logger.WriteLine("HMAC did not match");
                    }
                    return(null);
                }
            }
            catch (ArgumentNullException)
            {
                return(null);
            }
            catch (RankException)
            {
                return(null);
            }
            catch (ArrayTypeMismatchException)
            {
                return(null);
            }
            catch (ArgumentOutOfRangeException)
            {
                return(null);
            }
            catch (ArgumentException)
            {
                return(null);
            }
            catch (ObjectDisposedException)
            {
                return(null);
            }

            // Decrypt the client's message
            RijndaelManaged  myRijndael   = new RijndaelManaged();
            ICryptoTransform decryptor    = myRijndael.CreateDecryptor(rawKeyBytes, IVBytes);
            MemoryStream     msDecrypt    = new MemoryStream();
            CryptoStream     cryptoStream = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write);

            try
            {
                cryptoStream.Write(messageBytes, 0, messageBytes.Length);
            }
            catch (ArgumentException)
            {
                //The sum of the count and offset parameters is longer than the length of the buffer.
                return(null);
            }
            catch (NotSupportedException)
            {
                // Underlying stream does not support writing (not sure how this could happen)
                return(null);
            }

            try
            {
                cryptoStream.FlushFinalBlock();
            }
            catch (NotSupportedException)
            {
                //  The current stream is not writable. -or- The final block has already been transformed.
                return(null);
            }
            catch (CryptographicException)
            {
                // The key is corrupt which can cause invalid padding to the stream.
                return(null);
            }

            byte[] decrypted = msDecrypt.ToArray();
            string result    = Encoding.UTF8.GetString(decrypted);

            return(result);
        }
Example #5
0
        public JSONRPCContainer Encrypt(string plaintext)
        {
            if (string.IsNullOrEmpty(plaintext))
            {
                return(null);
            }

            KeyContainerClass kc = this.KeyContainer;
            SHA1 sha             = new SHA1CryptoServiceProvider();

            byte[] plaintextBytes = Encoding.UTF8.GetBytes(plaintext);

            // Encrypt the client's message
            RijndaelManaged myRijndael = new RijndaelManaged();

            myRijndael.GenerateIV();
            myRijndael.Key = KeePassLib.Utility.MemUtil.HexStringToByteArray(kc.Key);
            ICryptoTransform encryptor    = myRijndael.CreateEncryptor();
            MemoryStream     msEncrypt    = new MemoryStream(100);
            CryptoStream     cryptoStream = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);

            try
            {
                cryptoStream.Write(plaintextBytes, 0, plaintextBytes.Length);
            }
            catch (ArgumentException)
            {
                //The sum of the count and offset parameters is longer than the length of the buffer.
                return(null);
            }
            catch (NotSupportedException)
            {
                // Underlying stream does not support writing (not sure how this could happen)
                return(null);
            }

            try
            {
                cryptoStream.FlushFinalBlock();
            }
            catch (NotSupportedException)
            {
                //  The current stream is not writable. -or- The final block has already been transformed.
                return(null);
            }
            catch (CryptographicException)
            {
                // The key is corrupt which can cause invalid padding to the stream.
                return(null);
            }

            byte[] encrypted = msEncrypt.ToArray();

            // Get the raw bytes that are used to calculate the HMAC

            byte[] HmacKey            = sha.ComputeHash(myRijndael.Key);
            byte[] ourHmacSourceBytes = new byte[HmacKey.Length + encrypted.Length + myRijndael.IV.Length];

            // These calls can throw a variety of different exceptions but
            // I can't see why they would so we will not try to differentiate the cause of them
            try
            {
                //TODO2: HMAC calculations might be stengthened against attacks on SHA
                // and/or gain improved performance through use of algorithms like AES-CMAC or HKDF

                Array.Copy(HmacKey, ourHmacSourceBytes, HmacKey.Length);
                Array.Copy(encrypted, 0, ourHmacSourceBytes, HmacKey.Length, encrypted.Length);
                Array.Copy(myRijndael.IV, 0, ourHmacSourceBytes, HmacKey.Length + encrypted.Length, myRijndael.IV.Length);

                // Calculate the HMAC
                byte[] ourHmac = sha.ComputeHash(ourHmacSourceBytes);

                // Package the data ready for transmission
                JSONRPCContainer cont = new JSONRPCContainer();
                cont.iv      = Convert.ToBase64String(myRijndael.IV);
                cont.message = Convert.ToBase64String(encrypted);
                cont.hmac    = Convert.ToBase64String(ourHmac);

                return(cont);
            }
            catch (ArgumentNullException)
            {
                return(null);
            }
            catch (RankException)
            {
                return(null);
            }
            catch (ArrayTypeMismatchException)
            {
                return(null);
            }
            catch (ArgumentOutOfRangeException)
            {
                return(null);
            }
            catch (ArgumentException)
            {
                return(null);
            }
            catch (ObjectDisposedException)
            {
                return(null);
            }
        }
Example #6
0
        void KPRPCReceiveSetup(KPRPCMessage kprpcm)
        {
            if (this.Authorised)
            {
                KPRPCMessage data2client = new KPRPCMessage();
                data2client.protocol = "setup";
                data2client.srp      = new SRPParams();
                data2client.version  = ProtocolVersion;

                data2client.error = new Error(ErrorCode.AUTH_RESTART, new string[] { "Already authorised" });
                this.Authorised   = false;

                string response = Jayrock.Json.Conversion.JsonConvert.ExportToString(data2client);
                this.WebSocketConnection.Send(response);

                return;
            }



            if (kprpcm.srp != null)
            {
                KPRPCMessage data2client = new KPRPCMessage();
                data2client.protocol = "setup";
                data2client.version  = ProtocolVersion;

                int clientSecurityLevel = kprpcm.srp.securityLevel;

                if (clientSecurityLevel < securityLevelClientMinimum)
                {
                    data2client.error = new Error(ErrorCode.AUTH_CLIENT_SECURITY_LEVEL_TOO_LOW, new string[] { securityLevelClientMinimum.ToString() });

                    /* TODO1.3: need to disconnect/delete/reset this connection once we've decided we are not interested in letting the client connect. Maybe
                     * tie in to finding a way to abort if user clicks a "cancel" button on the auth form.
                     */
                    this.WebSocketConnection.Send(Jayrock.Json.Conversion.JsonConvert.ExportToString(data2client));
                }
                else
                {
                    switch (kprpcm.srp.stage)
                    {
                    case "identifyToServer": this.WebSocketConnection.Send(SRPIdentifyToServer(kprpcm)); break;

                    case "proofToServer": this.WebSocketConnection.Send(SRPProofToServer(kprpcm)); break;

                    default: return;
                    }
                }
            }
            else
            {
                KPRPCMessage data2client = new KPRPCMessage();
                data2client.protocol = "setup";
                data2client.version  = ProtocolVersion;

                int clientSecurityLevel = kprpcm.key.securityLevel;

                if (clientSecurityLevel < securityLevelClientMinimum)
                {
                    data2client.error = new Error(ErrorCode.AUTH_CLIENT_SECURITY_LEVEL_TOO_LOW, new string[] { securityLevelClientMinimum.ToString() });

                    /* TODO1.3: need to disconnect/delete/reset this connection once we've decided we are not interested in letting the client connect. Maybe
                     * tie in to finding a way to abort if user clicks a "cancel" button on the auth form.
                     */
                    this.WebSocketConnection.Send(Jayrock.Json.Conversion.JsonConvert.ExportToString(data2client));
                }
                else
                {
                    if (!string.IsNullOrEmpty(kprpcm.key.username))
                    {
                        // confirm username
                        this.userName = kprpcm.key.username;
                        KeyContainerClass kc = this.KeyContainer;

                        if (kc == null)
                        {
                            this.userName     = null;
                            data2client.error = new Error(ErrorCode.AUTH_FAILED, new string[] { "Stored key not found - Caused by changed Firefox profile or KeePass instance; changed OS user credentials; or KeePass config file may be corrupt" });

                            /* TODO1.3: need to disconnect/delete/reset this connection once we've decided we are not interested in letting the client connect. Maybe
                             * tie in to finding a way to abort if user clicks a "cancel" button on the auth form.
                             */
                            this.WebSocketConnection.Send(Jayrock.Json.Conversion.JsonConvert.ExportToString(data2client));
                            return;
                        }
                        if (kc.Username != this.userName)
                        {
                            this.userName     = null;
                            data2client.error = new Error(ErrorCode.AUTH_FAILED, new string[] { "Username mismatch - KeePass config file is probably corrupt" });

                            /* TODO1.3: need to disconnect/delete/reset this connection once we've decided we are not interested in letting the client connect. Maybe
                             * tie in to finding a way to abort if user clicks a "cancel" button on the auth form.
                             */
                            this.WebSocketConnection.Send(Jayrock.Json.Conversion.JsonConvert.ExportToString(data2client));
                            return;
                        }
                        if (kc.AuthExpires < DateTime.UtcNow)
                        {
                            this.userName     = null;
                            data2client.error = new Error(ErrorCode.AUTH_EXPIRED);

                            /* TODO1.3: need to disconnect/delete/reset this connection once we've decided we are not interested in letting the client connect. Maybe
                             * tie in to finding a way to abort if user clicks a "cancel" button on the auth form.
                             */
                            this.WebSocketConnection.Send(Jayrock.Json.Conversion.JsonConvert.ExportToString(data2client));
                            return;
                        }

                        this.WebSocketConnection.Send(Kcp.KeyChallengeResponse1(this.userName, securityLevel));
                    }
                    else if (!string.IsNullOrEmpty(kprpcm.key.cc) && !string.IsNullOrEmpty(kprpcm.key.cr))
                    {
                        bool authorised = false;
                        this.WebSocketConnection.Send(Kcp.KeyChallengeResponse2(kprpcm.key.cc, kprpcm.key.cr, KeyContainer, securityLevel, out authorised));
                        Authorised = authorised;
                        if (authorised)
                        {
                            // We assume the user has manually verified the client name as part of the initial SRP setup so it's fairly safe to use it to determine the type of client connection to which we want to promote our null connection
                            KPRPC.PromoteNullRPCClient(this, KeyContainer.ClientName);
                        }
                    }
                }
            }
        }
Example #7
0
        string SRPProofToServer(KPRPCMessage srpem)
        {
            SRPParams srp = srpem.srp;

            KPRPCMessage data2client = new KPRPCMessage();
            data2client.protocol = "setup";
            data2client.srp = new SRPParams();
            data2client.srp.stage = "proofToClient";
            data2client.version = ProtocolVersion;

            if (string.IsNullOrEmpty(srp.M))
            {
                data2client.error = new Error(ErrorCode.AUTH_MISSING_PARAM, new string[] { "M" });
            }
            else
            {
                _srp.Authenticate(srp.M);

                if (!_srp.Authenticated)
                    data2client.error = new Error(ErrorCode.AUTH_FAILED, new string[] { "Keys do not match" });
                else
                {
                    data2client.srp.M2 = _srp.M2;
                    data2client.srp.securityLevel = securityLevel;
                    KeyContainer = new KeyContainerClass(_srp.Key,DateTime.UtcNow.AddSeconds(KeyExpirySeconds),userName,clientName);
                    Authorised = true;
                    // We assume the user has checked the client name as part of the initial SRP setup so it's fairly safe to use it to determine the type of client connection to which we want to promote our null connection
                    KPRPC.PromoteNullRPCClient(this, clientName);
                    KPRPC.InvokeMainThread(new HideAuthDialogDelegate(HideAuthDialog));

                    // If we've never shown the user the welcome screen and have never
                    // known a KeeFox add-on from the previous KPRPC protocol, show it now
                    bool welcomeDisplayed = KPRPC._host.CustomConfig.GetBool("KeePassRPC.KeeFoxWelcomeDisplayed",false);
                    if (!welcomeDisplayed
                        && string.IsNullOrEmpty(KPRPC._host.CustomConfig.GetString("KeePassRPC.knownClients.KeeFox Firefox add-on")))
                        KPRPC.InvokeMainThread(new KeePassRPCExt.WelcomeKeeFoxUserDelegate(KPRPC.WelcomeKeeFoxUser));
                    if (!welcomeDisplayed)
                        KPRPC._host.CustomConfig.SetBool("KeePassRPC.KeeFoxWelcomeDisplayed",true);
                }
            }

            return Jayrock.Json.Conversion.JsonConvert.ExportToString(data2client);
        }