pull in additional changes from 2.0 branch.
[monitor.git] / monitor / Rpyc / Authentication.py
diff --git a/monitor/Rpyc/Authentication.py b/monitor/Rpyc/Authentication.py
new file mode 100644 (file)
index 0000000..afa1172
--- /dev/null
@@ -0,0 +1,42 @@
+"""\r
+Challenge-Response authentication algorithm over channels: the client sends the\r
+username, recvs a challenge, generates a response based on the challenge and \r
+password, sends it back to the server, which also calculates the expected response.\r
+the server returns "WRONG" or "OKAY". this way the password is never sent over\r
+the wire.\r
+\r
+* servers should call the accept function over a channel, with a dict of \r
+(username, password) pairs.\r
+* clients should call login over a channel, with the username and password\r
+"""\r
+import md5\r
+import random\r
+\r
+def get_bytes(num_bytes):\r
+       ret = []\r
+       for n in xrange(num_bytes):\r
+               ret.append( chr(random.randint(0,255)) )\r
+       return ''.join(ret)\r
+\r
+def accept(chan, users):\r
+    username = chan.recv()\r
+    challenge = get_bytes(16)\r
+    chan.send(challenge)\r
+    response = chan.recv()\r
+    if username not in users:\r
+        chan.send("WRONG")\r
+        return False\r
+    expected_response = md5.new(users[username] + challenge).digest()\r
+    if response != expected_response:\r
+        chan.send("WRONG")\r
+        return False\r
+    chan.send("OKAY")\r
+    return True\r
+\r
+def login(chan, username, password):\r
+    chan.send(username)\r
+    challenge = chan.recv()\r
+    response = md5.new(password + challenge).digest()\r
+    chan.send(response)\r
+    return chan.recv() == "OKAY"\r
+\r