<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" /><style type="text/css"><!--
#msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; }
#msg ul, pre { overflow: auto; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<title>[1509] PyOpenDirectory/branches/users/cdaboo/better-errors-1318/test_auth.
py</title>
</head>
<body>
<div id="msg">
<dl>
<dt>Revision</dt> <dd><a href="http://trac.macosforge.org/projects/calendarserver/changeset/1509">1509</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2007-05-02 12:19:42 -0700 (Wed, 02 May 2007)</dd>
</dl>
<h3>Log Message</h3>
<pre>Stress test DS authentication for basic and digest.</pre>
<h3>Added Paths</h3>
<ul>
<li><a href="#PyOpenDirectorybranchesuserscdaboobettererrors1318test_authpy">PyOpenDirectory/branches/users/cdaboo/better-errors-1318/test_auth.py</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="PyOpenDirectorybranchesuserscdaboobettererrors1318test_authpy"></a>
<div class="addfile"><h4>Added: PyOpenDirectory/branches/users/cdaboo/better-errors-1318/test_auth.py (0 => 1509)</h4>
<pre class="diff"><span>
<span class="info">--- PyOpenDirectory/branches/users/cdaboo/better-errors-1318/test_auth.py         (rev 0)
+++ PyOpenDirectory/branches/users/cdaboo/better-errors-1318/test_auth.py        2007-05-02 19:19:42 UTC (rev 1509)
</span><span class="lines">@@ -0,0 +1,201 @@
</span><ins>+#!/usr/bin/env python
+
+from getpass import getpass
+import opendirectory
+import dsattributes
+import md5
+import sha
+
+algorithms = {
+ 'md5': md5.new,
+ 'md5-sess': md5.new,
+ 'sha': sha.new,
+}
+
+# DigestCalcHA1
+def calcHA1(
+ pszAlg,
+ pszUserName,
+ pszRealm,
+ pszPassword,
+ pszNonce,
+ pszCNonce,
+ preHA1=None
+):
+ """
+ @param pszAlg: The name of the algorithm to use to calculate the digest.
+ Currently supported are md5 md5-sess and sha.
+
+ @param pszUserName: The username
+ @param pszRealm: The realm
+ @param pszPassword: The password
+ @param pszNonce: The nonce
+ @param pszCNonce: The cnonce
+
+ @param preHA1: If available this is a str containing a previously
+ calculated HA1 as a hex string. If this is given then the values for
+ pszUserName, pszRealm, and pszPassword are ignored.
+ """
+
+ if (preHA1 and (pszUserName or pszRealm or pszPassword)):
+ raise TypeError(("preHA1 is incompatible with the pszUserName, "
+ "pszRealm, and pszPassword arguments"))
+
+ if preHA1 is None:
+ # We need to calculate the HA1 from the username:realm:password
+ m = algorithms[pszAlg]()
+ m.update(pszUserName)
+ m.update(":")
+ m.update(pszRealm)
+ m.update(":")
+ m.update(pszPassword)
+ HA1 = m.digest()
+ else:
+ # We were given a username:realm:password
+ HA1 = preHA1.decode('hex')
+
+ if pszAlg == "md5-sess":
+ m = algorithms[pszAlg]()
+ m.update(HA1)
+ m.update(":")
+ m.update(pszNonce)
+ m.update(":")
+ m.update(pszCNonce)
+ HA1 = m.digest()
+
+ return HA1.encode('hex')
+
+# DigestCalcResponse
+def calcResponse(
+ HA1,
+ algo,
+ pszNonce,
+ pszNonceCount,
+ pszCNonce,
+ pszQop,
+ pszMethod,
+ pszDigestUri,
+ pszHEntity,
+):
+ m = algorithms[algo]()
+ m.update(pszMethod)
+ m.update(":")
+ m.update(pszDigestUri)
+ if pszQop == "auth-int":
+ m.update(":")
+ m.update(pszHEntity)
+ HA2 = m.digest().encode('hex')
+
+ m = algorithms[algo]()
+ m.update(HA1)
+ m.update(":")
+ m.update(pszNonce)
+ m.update(":")
+ if pszNonceCount and pszCNonce and pszQop:
+ m.update(pszNonceCount)
+ m.update(":")
+ m.update(pszCNonce)
+ m.update(":")
+ m.update(pszQop)
+ m.update(":")
+ m.update(HA2)
+ respHash = m.digest().encode('hex')
+ return respHash
+
+
+attempts = 100
+
+realm = "/Search"
+nonce = "128446648710842461101646794502"
+nc = "00000001"
+cnonce = "0a4f113b12345"
+uri = "/principals/"
+method = "GET"
+
+def doAuthDigest(username, password, qop, algorithm):
+ failures = 0
+
+ result = opendirectory.queryRecordsWithAttribute(
+ od,
+ dsattributes.kDSNAttrRecordName,
+ username,
+ dsattributes.eDSExact,
+ False,
+ dsattributes.kDSStdRecordTypeUsers,
+ [dsattributes.kDS1AttrGeneratedUID])
+ guid = result[username][dsattributes.kDS1AttrGeneratedUID]
+
+ expected = calcResponse(
+ calcHA1(algorithm, username, realm, password, nonce, cnonce),
+ algorithm, nonce, nc, cnonce, qop, method, uri, None
+ )
+ #print expected
+
+ if qop:
+ challenge = 'Digest realm="%s", nonce="%s", algorithm=%s, qop="%s"' % (realm, nonce, algorithm, qop,)
+ else:
+ challenge = 'Digest realm="%s", nonce="%s", algorithm=%s' % (realm, nonce, algorithm,)
+ if qop:
+ response = ('Digest username="%s", realm="%s", '
+ 'nonce="%s", digest-uri="%s", '
+ 'response=%s, algorithm=%s, cnonce="%s", qop=%s, nc=%s' % (username, realm, nonce, uri, expected, algorithm, cnonce, qop, nc, ))
+ else:
+ response = ('Digest username="%s", realm="%s", '
+ 'nonce="%s", digest-uri="%s", '
+ 'response=%s, algorithm=%s' % (username, realm, nonce, uri, expected, algorithm, ))
+
+ print " Challenge: %s" % (challenge,)
+ print " Response: %s" % (response, )
+
+ for x in xrange(attempts):
+ success = opendirectory.authenticateUserDigest(
+ od,
+ guid,
+ username,
+ challenge,
+ response,
+ method
+ )
+
+ if not success:
+ failures += 1
+
+ print "\n%d failures out of %d attempts for Digest.\n\n" % (failures, attempts)
+
+def doAuthBasic(username, password):
+ failures = 0
+
+ result = opendirectory.queryRecordsWithAttribute(
+ od,
+ dsattributes.kDSNAttrRecordName,
+ username,
+ dsattributes.eDSExact,
+ False,
+ dsattributes.kDSStdRecordTypeUsers,
+ [dsattributes.kDS1AttrGeneratedUID])
+ guid = result[username][dsattributes.kDS1AttrGeneratedUID]
+
+ for x in xrange(attempts):
+ success = opendirectory.authenticateUserBasic(
+ od,
+ guid,
+ username,
+ password,
+ )
+
+ if not success:
+ failures += 1
+
+ print "\n%d failures out of %d attempts for Basic.\n\n" % (failures, attempts)
+
+search = raw_input("DS search path: ")
+user = raw_input("User: ")
+pswd = getpass("Password: ")
+
+od = opendirectory.odInit(search)
+
+doAuthBasic(user, pswd)
+doAuthDigest(user, pswd, None, "md5")
+#doAuth(user, pswd, "auth", "md5")
+#doAuth(user, pswd, "auth", "md5-sess")
+
</ins></span></pre>
</div>
</div>
</body>
</html>