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); }
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; }
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)); }
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); }
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); } }
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); } } } } }
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); }