incubator-heraldry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ket...@apache.org
Subject svn commit: r463060 [1/6] - in /incubator/heraldry/libraries/python/openid/trunk: ./ admin/ examples/ openid/ openid/consumer/ openid/server/ openid/store/ openid/test/ openid/test/data/ openid/yadis/
Date Wed, 11 Oct 2006 23:22:36 GMT
Author: keturn
Date: Wed Oct 11 16:22:33 2006
New Revision: 463060

URL: http://svn.apache.org/viewvc?view=rev&rev=463060
Log:
bring trunk up to date with current development

Added:
    incubator/heraldry/libraries/python/openid/trunk/openid/fetchers.py
    incubator/heraldry/libraries/python/openid/trunk/openid/message.py
    incubator/heraldry/libraries/python/openid/trunk/openid/store/nonce.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/data/
    incubator/heraldry/libraries/python/openid/trunk/openid/test/discoverdata.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/helper.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_accept.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_association.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_consumer.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_etxrd.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_fetchers.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_message.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_nonce.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_parsehtml.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_server.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_symbol.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_urinorm.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_xrd.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_xri.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_xrires.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_yadis_discover.py   (with props)
    incubator/heraldry/libraries/python/openid/trunk/openid/test/urinorm.txt
    incubator/heraldry/libraries/python/openid/trunk/openid/urinorm.py
    incubator/heraldry/libraries/python/openid/trunk/openid/yadis/
    incubator/heraldry/libraries/python/openid/trunk/openid/yadis/__init__.py
    incubator/heraldry/libraries/python/openid/trunk/openid/yadis/accept.py
    incubator/heraldry/libraries/python/openid/trunk/openid/yadis/constants.py
    incubator/heraldry/libraries/python/openid/trunk/openid/yadis/discover.py
    incubator/heraldry/libraries/python/openid/trunk/openid/yadis/etxrd.py
    incubator/heraldry/libraries/python/openid/trunk/openid/yadis/filters.py
    incubator/heraldry/libraries/python/openid/trunk/openid/yadis/manager.py
    incubator/heraldry/libraries/python/openid/trunk/openid/yadis/parsehtml.py
    incubator/heraldry/libraries/python/openid/trunk/openid/yadis/services.py
    incubator/heraldry/libraries/python/openid/trunk/openid/yadis/xrd.py
    incubator/heraldry/libraries/python/openid/trunk/openid/yadis/xri.py
    incubator/heraldry/libraries/python/openid/trunk/openid/yadis/xrires.py
Removed:
    incubator/heraldry/libraries/python/openid/trunk/openid/test/association.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/consumer.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/server.py
Modified:
    incubator/heraldry/libraries/python/openid/trunk/admin/runtests
    incubator/heraldry/libraries/python/openid/trunk/examples/consumer.py
    incubator/heraldry/libraries/python/openid/trunk/examples/server.py
    incubator/heraldry/libraries/python/openid/trunk/openid/association.py
    incubator/heraldry/libraries/python/openid/trunk/openid/consumer/consumer.py
    incubator/heraldry/libraries/python/openid/trunk/openid/consumer/discover.py
    incubator/heraldry/libraries/python/openid/trunk/openid/cryptutil.py
    incubator/heraldry/libraries/python/openid/trunk/openid/dh.py
    incubator/heraldry/libraries/python/openid/trunk/openid/oidutil.py
    incubator/heraldry/libraries/python/openid/trunk/openid/server/server.py
    incubator/heraldry/libraries/python/openid/trunk/openid/server/trustroot.py
    incubator/heraldry/libraries/python/openid/trunk/openid/store/dumbstore.py
    incubator/heraldry/libraries/python/openid/trunk/openid/store/filestore.py
    incubator/heraldry/libraries/python/openid/trunk/openid/store/sqlstore.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/_memstore.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/oidutil.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/storetest.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_discover.py
    incubator/heraldry/libraries/python/openid/trunk/openid/test/test_openidyadis.py
    incubator/heraldry/libraries/python/openid/trunk/setup.py

Modified: incubator/heraldry/libraries/python/openid/trunk/admin/runtests
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/openid/trunk/admin/runtests?view=diff&rev=463060&r1=463059&r2=463060
==============================================================================
--- incubator/heraldry/libraries/python/openid/trunk/admin/runtests (original)
+++ incubator/heraldry/libraries/python/openid/trunk/admin/runtests Wed Oct 11 16:22:33 2006
@@ -49,18 +49,34 @@
     from openid.test import test_htmldiscover
     from openid.test import test_openidyadis
     from openid.test import test_discover
-    from openid.test import consumer
+    from openid.test import test_consumer
+    from openid.test import test_message
+    from openid.test import test_server
+    from openid.test import test_symbol
     from openid.test import kvform
-    from openid.test import server
     from openid.test import oidutil
     from openid.test import linkparse
     from openid.test import trustroot
-    from openid.test import association
+    from openid.test import test_association
+    from openid.test import test_fetchers
+    from openid.test import test_urinorm
+    from openid.test import test_nonce
+    # yadis tests
+    from openid.test import test_parsehtml
+    from openid.test import test_yadis_discover
+    from openid.test import test_accept
+    from openid.test import test_etxrd
+    from openid.test import test_xri
+    from openid.test import test_xrires
 
     pyunit_modules = [
-        server,
-        consumer,
-        association,
+        test_server,
+        test_consumer,
+        test_message,
+        test_symbol,
+        test_etxrd,
+        test_xri,
+        test_xrires,
         ]
 
     # Some modules have data-driven tests, and they use custom methods
@@ -72,7 +88,15 @@
         test_openidyadis,
         test_discover,
         test_htmldiscover,
+        test_association,
         kvform,
+        test_parsehtml,
+        test_discover,
+        test_accept,
+        test_fetchers,
+        test_urinorm,
+        test_yadis_discover,
+        test_nonce,
         ]
 
     loader = unittest.TestLoader()
@@ -82,7 +106,13 @@
         s.addTest(loader.loadTestsFromModule(m))
 
     for m in custom_modules:
-        s.addTest(m.pyUnitTests())
+        try:
+            s.addTest(m.pyUnitTests())
+        except AttributeError, ex:
+            # because the AttributeError doesn't actually say which
+            # object it was.
+            print "Error loading tests from %s:" % (m,)
+            raise
 
     runner = unittest.TextTestRunner() # verbosity=2)
 

Modified: incubator/heraldry/libraries/python/openid/trunk/examples/consumer.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/openid/trunk/examples/consumer.py?view=diff&rev=463060&r1=463059&r2=463060
==============================================================================
--- incubator/heraldry/libraries/python/openid/trunk/examples/consumer.py (original)
+++ incubator/heraldry/libraries/python/openid/trunk/examples/consumer.py Wed Oct 11 16:22:33 2006
@@ -23,7 +23,7 @@
 try:
     import openid
 except ImportError:
-    print >>sys.stderr, """
+    sys.stderr.write("""
 Failed to import the OpenID library. In order to use this example, you
 must either install the library (see INSTALL in the root of the
 distribution) or else add the library to python's import path (the
@@ -31,15 +31,18 @@
 
 For more information, see the README in the root of the library
 distribution or http://www.openidenabled.com/
-"""
+""")
     sys.exit(1)
 
 from openid.store import filestore
 from openid.consumer import consumer
 from openid.oidutil import appendArgs
 from openid.cryptutil import randomString
-from yadis.discover import DiscoveryFailure
-from urljr.fetchers import HTTPFetchingError
+from openid.yadis.discover import DiscoveryFailure
+from openid.fetchers import HTTPFetchingError
+
+SREG_URI = 'http://openid.net/sreg/1.0'
+
 
 class OpenIDHTTPServer(HTTPServer):
     """http server that contains a reference to an OpenID consumer and
@@ -178,11 +181,20 @@
                 # user's identity, and get a token that allows us to
                 # communicate securely with the identity server.
 
+                self.requestRegistrationData(request)
+
                 trust_root = self.server.base_url
                 return_to = self.buildURL('process')
-                redirect_url = request.redirectURL(trust_root, return_to)
+                form_html = request.formMarkup(trust_root, return_to, 
+                    form_tag_attrs={'id':'openid_message'})
+
+                self.autoSubmit(form_html, 'openid_message')
 
-                self.redirect(redirect_url)
+    def requestRegistrationData(self, request):
+        required = ','.join(['nickname'])
+        optional = ','.join(['fullname', 'email'])
+        request.addExtensionArg(SREG_URI, 'required', required)
+        request.addExtensionArg(SREG_URI, 'optional', optional)
 
     def doProcess(self):
         """Handle the redirect from the OpenID server.
@@ -195,6 +207,7 @@
         # the return type.
         info = oidconsumer.complete(self.query)
 
+        sreg = None
         css_class = 'error'
         if info.status == consumer.FAILURE and info.identity_url:
             # In the case of failure, if info is non-None, it is the
@@ -213,6 +226,7 @@
             # comment posting, etc. here.
             fmt = "You have successfully verified %s as your identity."
             message = fmt % (cgi.escape(info.identity_url),)
+            sreg = info.message.getArgs(SREG_URI)
             if info.endpoint.canonicalID:
                 # You should authorize i-name users by their canonicalID,
                 # rather than their more human-friendly identifiers.  That
@@ -230,7 +244,44 @@
             # information in a log.
             message = 'Verification failed.'
 
-        self.render(message, css_class, info.identity_url)
+        self.render(message, css_class, info.identity_url, sreg_data=sreg)
+
+    def renderSREG(self, sreg_data):
+        if not sreg_data:
+            self.wfile.write(
+                '<div class="alert">No registration data was returned</div>')
+        else:
+            sreg_list = sreg_data.items()
+            sreg_list.sort()
+            sreg_fields = {
+                'fullname':'Full Name',
+                'nickname':'Nickname',
+                'dob':'Date of Birth',
+                'email':'E-mail Address',
+                'gender':'Gender',
+                'postcode':'Postal Code',
+                'country':'Country',
+                'language':'Language',
+                'timezone':'Time Zone',
+                }
+            self.wfile.write(
+                '<h2>Registration Data</h2>'
+                '<table class="sreg">'
+                '<thead><tr><th>Field</th><th>Value</th></tr></thead>'
+                '<tbody>')
+
+            odd = ' class="odd"'
+            for k, v in sreg_list:
+                field_name = sreg_fields.get(k, k)
+                value = cgi.escape(v)
+                self.wfile.write(
+                    '<tr%s><td>%s</td><td>%s</td></tr>' % (odd, field_name, value))
+                if odd:
+                    odd = ''
+                else:
+                    odd = ' class="odd"'
+
+            self.wfile.write('</tbody></table>')
 
     def buildURL(self, action, **query):
         """Build a URL relative to the server base_url, with the given
@@ -238,15 +289,14 @@
         base = urlparse.urljoin(self.server.base_url, action)
         return appendArgs(base, query)
 
-    def redirect(self, redirect_url):
-        """Send a redirect response to the given URL to the browser."""
+    def autoSubmit(self, form, id):
+        """Send a page containing an auto-submitting form."""
         response = """\
-Location: %s
-Content-type: text/plain
-
-Redirecting to %s""" % (redirect_url, redirect_url)
-        self.send_response(302)
-        self.setSessionCookie()
+<html><head><title>OpenID transaction in progress</title></head>
+<body onload='document.getElementById("%s").submit()'>
+%s
+</body></html>
+"""%(id, form)
         self.wfile.write(response)
 
     def notFound(self):
@@ -257,7 +307,8 @@
         self.render(msg, 'error', openid_url, status=404)
 
     def render(self, message=None, css_class='alert', form_contents=None,
-               status=200, title="Python OpenID Consumer Example"):
+               status=200, title="Python OpenID Consumer Example",
+               sreg_data=None):
         """Render a page."""
         self.send_response(status)
         self.pageHeader(title)
@@ -265,6 +316,10 @@
             self.wfile.write("<div class='%s'>" % (css_class,))
             self.wfile.write(message)
             self.wfile.write("</div>")
+
+        if sreg_data is not None:
+            self.renderSREG(sreg_data)
+
         self.pageFooter(form_contents)
 
     def pageHeader(self, title):
@@ -285,6 +340,20 @@
       }
       div {
         padding: .5em;
+      }
+      tr.odd td {
+        background-color: #dddddd;
+      }
+      table.sreg {
+        border: 1px solid black;
+        border-collapse: collapse;
+      }
+      table.sreg th {
+        border-bottom: 1px solid black;
+      }
+      table.sreg td, table.sreg th {
+        padding: 0.5em;
+        text-align: left;
       }
       table {
         margin: none;

Modified: incubator/heraldry/libraries/python/openid/trunk/examples/server.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/openid/trunk/examples/server.py?view=diff&rev=463060&r1=463059&r2=463060
==============================================================================
--- incubator/heraldry/libraries/python/openid/trunk/examples/server.py (original)
+++ incubator/heraldry/libraries/python/openid/trunk/examples/server.py Wed Oct 11 16:22:33 2006
@@ -18,7 +18,7 @@
 try:
     import openid
 except ImportError:
-    print >>sys.stderr, """
+    sys.stderr.write("""
 Failed to import the OpenID library. In order to use this example, you
 must either install the library (see INSTALL in the root of the
 distribution) or else add the library to python's import path (the
@@ -26,10 +26,9 @@
 
 For more information, see the README in the root of the library
 distribution or http://www.openidenabled.com/
-"""
+""")
     sys.exit(1)
 
-from openid import oidutil
 from openid.server import server
 from openid.store.filestore import FileOpenIDStore
 
@@ -77,8 +76,15 @@
                 self.showLoginPage('/', '/')
             elif path == '/loginsubmit':
                 self.doLogin()
-            else:
+            elif path.startswith('/id/'):
                 self.showIdPage(path)
+            elif path.startswith('/yadis/'):
+                self.showYadis(path[7:])
+            elif path == '/serveryadis':
+                self.showServerYadis()
+            else:
+                self.send_response(404)
+                self.end_headers()
 
         except (KeyboardInterrupt, SystemExit):
             raise
@@ -124,20 +130,23 @@
         request = self.server.lastCheckIDRequest.get(self.user)
 
         if 'yes' in query:
-            identity = request.identity
+            if 'login_as' in query:
+                self.user = self.query['login_as']
+            
             trust_root = request.trust_root
             if self.query.get('remember', 'no') == 'yes':
                 duration = 'always'
             else:
                 duration = 'once'
+            if request.idSelect():
+                identity = self.server.base_url + 'id/' + query['identifier']
+                response = request.answer(True, identity = identity)
+            else:
+                identity = request.identity
+                response = request.answer(True)
 
             self.server.approved[(identity, trust_root)] = duration
 
-            if 'login_as' in query:
-                self.user = self.query['login_as']
-
-            response = request.answer(True)
-
         elif 'no' in query:
             response = request.answer(False)
 
@@ -307,9 +316,37 @@
     def showDecidePage(self, request):
         expected_user = request.identity[len(self.server.base_url):]
 
-        if expected_user == self.user:
+        if request.idSelect(): # We are being asked to select an ID
             msg = '''\
-            <p>A new site has asked for your identity.  If you
+            <p>A site has asked for your identity.  You may select an
+            identifier by which you would like this site to know you.
+            On a production site this would likely be a drop down list
+            of pre-created accounts or have the facility to generate
+            a random anonymous identifier.
+            </p>
+            '''
+            fdata = {
+                'id_url_base': self.server.base_url+'id/',
+                'trust_root': request.trust_root,
+                }
+            form = '''\
+            <form method="POST" action="/allow">
+            <table>
+              <tr><td>Identity:</td>
+                 <td>%(id_url_base)s<input type='text' name='identifier'></td></tr>
+              <tr><td>Trust Root:</td><td>%(trust_root)s</td></tr>
+            </table>
+            <p>Allow this authentication to proceed?</p>
+            <input type="checkbox" id="remember" name="remember" value="yes"
+                checked="checked" /><label for="remember">Remember this
+                decision</label><br />
+            <input type="submit" name="yes" value="yes" />
+            <input type="submit" name="no" value="no" />
+            </form>
+            '''%fdata
+        elif expected_user == self.user:
+            msg = '''\
+            <p>A new site has asked to confirm your identity.  If you
             approve, the site represented by the trust root below will
             be told that you control identity URL listed below. (If
             you are using a delegated identity, the site will take
@@ -367,8 +404,11 @@
         self.showPage(200, 'Approve OpenID request?', msg=msg, form=form)
 
     def showIdPage(self, path):
-        tag = '<link rel="openid.server" href="%sopenidserver">' %\
+        link_tag = '<link rel="openid.server" href="%sopenidserver">' %\
               self.server.base_url
+        yadis_loc_tag = '<meta http-equiv="x-xrds-location" content="%s">'%\
+            (self.server.base_url+'yadis/'+path[4:])
+        disco_tags = link_tag + yadis_loc_tag
         ident = self.server.base_url + path[1:]
 
         approved_trust_roots = []
@@ -385,14 +425,64 @@
         else:
             msg = ''
 
-        self.showPage(200, 'An Identity Page', link_tag=tag, msg='''\
+        self.showPage(200, 'An Identity Page', head_extras=disco_tags, msg='''\
         <p>This is an identity page for %s.</p>
         %s
         ''' % (ident, msg))
 
+    def showYadis(self, user):
+        self.send_response(200)
+        self.send_header('Content-type', 'application/xrds+xml')
+        self.end_headers()
+        
+        endpoint_url = self.server.base_url + 'openidserver'
+        user_url = self.server.base_url + 'id/' + user
+        self.wfile.write("""\
+<?xml version="1.0" encoding="UTF-8"?>
+<xrds:XRDS
+    xmlns:xrds="xri://$xrds"
+    xmlns:openid="http://openid.net/xmlns/2.0"
+    xmlns="xri://$xrd*($v*2.0)">
+  <XRD>
+
+    <Service priority="0">
+      <Type>http://openid.net/signon/2.0</Type>
+      <URI>%s</URI>
+      <openid:Delegate>%s</openid:Delegate>
+    </Service>
+
+  </XRD>
+</xrds:XRDS>
+"""%(endpoint_url, user_url))
+
+    def showServerYadis(self):
+        self.send_response(200)
+        self.send_header('Content-type', 'application/xrds+xml')
+        self.end_headers()
+        
+        endpoint_url = self.server.base_url + 'openidserver'
+        self.wfile.write("""\
+<?xml version="1.0" encoding="UTF-8"?>
+<xrds:XRDS
+    xmlns:xrds="xri://$xrds"
+    xmlns:openid="http://openid.net/xmlns/2.0"
+    xmlns="xri://$xrd*($v*2.0)">
+  <XRD>
+
+    <Service priority="0">
+      <Type>http://openid.net/server/2.0</Type>
+      <URI>%s</URI>
+    </Service>
+
+  </XRD>
+</xrds:XRDS>
+"""%endpoint_url)    
+
     def showMainPage(self):
+        yadis_tag = '<meta http-equiv="x-xrds-location" content="%s">'%\
+            (self.server.base_url + 'serveryadis')
         if self.user:
-            openid_url = self.server.base_url + self.user
+            openid_url = self.server.base_url + 'id/' + self.user
             user_message = """\
             <p>You are logged in as %s. Your OpenID identity URL is
             <tt><a href=%s>%s</a></tt>. Enter that URL at an OpenID
@@ -404,7 +494,7 @@
             order to simulate a standard Web user experience. You are
             not <a href='/login'>logged in</a>.</p>"""
 
-        self.showPage(200, 'Main Page', msg='''\
+        self.showPage(200, 'Main Page', head_extras = yadis_tag, msg='''\
         <p>This is a simple OpenID server implemented using the <a
         href="http://openid.schtuff.com/">Python OpenID
         library</a>.</p>
@@ -435,7 +525,7 @@
         ''' % (success_to, fail_to))
 
     def showPage(self, response_code, title,
-                 link_tag='', msg=None, err=None, form=None):
+                 head_extras='', msg=None, err=None, form=None):
 
         if self.user is None:
             user_link = '<a href="/login">not logged in</a>.'
@@ -468,7 +558,7 @@
 
         contents = {
             'title': 'Python OpenID Server Example - ' + title,
-            'link_tag': link_tag,
+            'head_extras': head_extras,
             'body': body,
             'user_link': user_link,
             }
@@ -482,7 +572,7 @@
 <html>
   <head>
     <title>%(title)s</title>
-    %(link_tag)s
+    %(head_extras)s
   </head>
   <style type="text/css">
       h1 a:link {

Modified: incubator/heraldry/libraries/python/openid/trunk/openid/association.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/openid/trunk/openid/association.py?view=diff&rev=463060&r1=463059&r2=463060
==============================================================================
--- incubator/heraldry/libraries/python/openid/trunk/openid/association.py (original)
+++ incubator/heraldry/libraries/python/openid/trunk/openid/association.py Wed Oct 11 16:22:33 2006
@@ -1,3 +1,4 @@
+# -*- test-case-name: openid.test.test_association -*-
 """
 This module contains code for dealing with associations between
 consumers and servers.
@@ -8,6 +9,124 @@
 from openid import cryptutil
 from openid import kvform
 from openid import oidutil
+from openid.message import OPENID_NS
+
+all_association_types = [
+    'HMAC-SHA1',
+    'HMAC-SHA1-SIGNALL',
+    'HMAC-SHA256-SIGNALL',
+    ]
+
+signall_association_types = [
+    'HMAC-SHA1-SIGNALL',
+    'HMAC-SHA256-SIGNALL',
+    ]
+
+if hasattr(cryptutil, 'hmacSha256'):
+    supported_association_types = list(all_association_types)
+
+    default_association_order = [
+        ('HMAC-SHA1', 'DH-SHA1'),
+        ('HMAC-SHA1-SIGNALL', 'DH-SHA1'),
+        ('HMAC-SHA256-SIGNALL', 'DH-SHA256'),
+        ('HMAC-SHA1', 'no-encryption'),
+        ('HMAC-SHA1-SIGNALL', 'no-encryption'),
+        ('HMAC-SHA256-SIGNALL', 'no-encryption'),
+        ]
+
+    only_encrypted_association_order = [
+        ('HMAC-SHA1', 'DH-SHA1'),
+        ('HMAC-SHA1-SIGNALL', 'DH-SHA1'),
+        ('HMAC-SHA256-SIGNALL', 'DH-SHA256'),
+        ]
+else:
+    supported_association_types = ['HMAC-SHA1']
+
+    default_association_order = [
+        ('HMAC-SHA1', 'DH-SHA1'),
+        ('HMAC-SHA1-SIGNALL', 'DH-SHA1'),
+        ('HMAC-SHA1', 'no-encryption'),
+        ('HMAC-SHA1-SIGNALL', 'no-encryption'),
+        ]
+
+    only_encrypted_association_order = [
+        ('HMAC-SHA1', 'DH-SHA1'),
+        ('HMAC-SHA1-SIGNALL', 'DH-SHA1'),
+        ]
+
+def getSessionTypes(assoc_type):
+    """Return the allowed session types for a given association type"""
+    assoc_to_session = {
+        'HMAC-SHA1': ['DH-SHA1', 'no-encryption'],
+        'HMAC-SHA256-SIGNALL': ['DH-SHA256', 'no-encryption'],
+        }
+    assoc_to_session['HMAC-SHA1-SIGNALL'] = assoc_to_session['HMAC-SHA1']
+    return assoc_to_session.get(assoc_type, [])
+
+def checkSessionType(assoc_type, session_type):
+    """Check to make sure that this pair of assoc type and session
+    type are allowed"""
+    if session_type not in getSessionTypes(assoc_type):
+        raise ValueError(
+            'Session type %r not valid for assocation type %r'
+            % (session_type, assoc_type))
+
+class SessionNegotiator(object):
+    def __init__(self, allowed_types):
+        self.allowed_types = allowed_types
+
+    def copy(self):
+        return self.__class__(list(self.allowed_types))
+
+    def setAllowedTypes(self, allowed_types):
+        """Set the allowed association types, checking to make sure
+        each combination is valid."""
+        for (assoc_type, session_type) in allowed_types:
+            checkSessionType(assoc_type, session_type)
+
+        self.allowed_types = allowed_types
+
+    def addAllowedType(self, assoc_type, session_type=None):
+        """Add an association type and session type to the allowed
+        types list. The assocation/session pairs are tried in the
+        order that they are added."""
+        if self.allowed_types is None:
+            self.allowed_types = []
+
+        if session_type is None:
+            for session_type in getSessionTypes(assoc_type):
+                self.addAllowedType(assoc_type, session_type)
+            else:
+                raise ValueError('No session available for association type %r'
+                                 % (assoc_type,))
+        else:
+            checkSessionType(assoc_type, session_type)
+            self.allowed_types.append((assoc_type, session_type))
+
+
+    def isAllowed(self, assoc_type, session_type):
+        """Is this combination of association type and session type allowed?"""
+        assoc_good = (assoc_type, session_type) in self.allowed_types
+        matches = session_type in getSessionTypes(assoc_type)
+        return assoc_good and matches
+
+    def getAllowedType(self):
+        """Get a pair of assocation type and session type that are supported"""
+        for pair in self.allowed_types:
+            return pair
+
+        return (None, None)
+
+default_negotiator = SessionNegotiator(default_association_order)
+encrypted_negotiator = SessionNegotiator(only_encrypted_association_order)
+
+def getSecretSize(assoc_type):
+    if assoc_type in ('HMAC-SHA1', 'HMAC-SHA1-SIGNALL'):
+        return 20
+    elif assoc_type == 'HMAC-SHA256-SIGNALL':
+        return 32
+    else:
+        raise ValueError('Unsupported association type: %r' % (assoc_type,))
 
 class Association(object):
     """
@@ -52,13 +171,14 @@
     @type assoc_type: C{str}
 
 
+    @ivar sign_all: True if this association type signs all fields, as
+        opposed to those listed in an C{openid.signed} list.
+    @type sign_all: bool
+    
     @sort: __init__, fromExpiresIn, getExpiresIn, __eq__, __ne__,
         handle, secret, issued, lifetime, assoc_type
     """
 
-    # This is a HMAC-SHA1 specific value.
-    SIG_LENGTH = 20
-
     # The ordering and name of keys as stored by serialize
     assoc_keys = [
         'version',
@@ -69,6 +189,14 @@
         'assoc_type',
         ]
 
+
+    _macs = {
+        'HMAC-SHA1': cryptutil.hmacSha1,
+        'HMAC-SHA256-SIGNALL': cryptutil.hmacSha256,
+        }
+    _macs['HMAC-SHA1-SIGNALL'] = _macs['HMAC-SHA1']
+
+
     def fromExpiresIn(cls, expires_in, handle, secret, assoc_type):
         """
         This is an alternate constructor used by the OpenID consumer
@@ -147,15 +275,21 @@
 
         @type assoc_type: C{str}
         """
-        if assoc_type != 'HMAC-SHA1':
-            fmt = 'HMAC-SHA1 is the only supported association type (got %r)'
+        if assoc_type not in all_association_types:
+            fmt = '%r is not a supported association type'
             raise ValueError(fmt % (assoc_type,))
 
+#         secret_size = getSecretSize(assoc_type)
+#         if len(secret) != secret_size:
+#             fmt = 'Wrong size secret (%s bytes) for association type %s'
+#             raise ValueError(fmt % (len(secret), assoc_type))
+
         self.handle = handle
         self.secret = secret
         self.issued = issued
         self.lifetime = lifetime
         self.assoc_type = assoc_type
+        self.sign_all = assoc_type in signall_association_types
 
     def getExpiresIn(self, now=None):
         """
@@ -274,48 +408,109 @@
 
         @rtype: str
         """
-        assert self.assoc_type == 'HMAC-SHA1'
         kv = kvform.seqToKV(pairs)
-        return cryptutil.hmacSha1(self.secret, kv)
 
-    def signDict(self, fields, data, prefix='openid.'):
-        """
-        Generate a signature for some fields in a dictionary
-
-
-        @param fields: The fields to sign, in order
-
-        @type fields: sequence of str
+        try:
+            mac = self._macs[self.assoc_type]
+        except KeyError:
+            raise ValueError(
+                'Unknown association type: %r' % (self.assoc_type,))
 
+        return mac(self.secret, kv)
 
-        @param data: Dictionary of values to sign
 
-        @type data: {str:str}
+    def getMessageSignature(self, message):
+        """Return the signature of a message.
 
+        If I am not a sign-all association, the message must have a
+        signed list.
 
         @return: the signature, base64 encoded
 
         @rtype: str
-        """
-        pairs = []
-        for field in fields:
-            pairs.append((field, data.get(prefix + field, '')))
 
+        @raises ValueError: If there is no signed list and I am not a sign-all
+            type of association.
+        """
+        pairs = self._makePairs(message)
         return oidutil.toBase64(self.sign(pairs))
 
-    def addSignature(self, fields, data, prefix='openid.'):
-        sig = self.signDict(fields, data, prefix)
-        signed = ','.join(fields)
-        data[prefix + 'sig'] = sig
-        data[prefix + 'signed'] = signed
+    def signMessage(self, message):
+        """Add a signature (and a signed list) to a message.
 
-    def checkSignature(self, data, prefix='openid.'):
-        try:
-            signed = data[prefix + 'signed']
-            fields = signed.split(',')
-            expected_sig = self.signDict(fields, data, prefix)
-            request_sig = data[prefix + 'sig']
-        except KeyError:
-            return False
+        @return: a new Message object with a signature
+        @rtype: L{openid.message.Message}
+        """
+        if (message.hasKey(OPENID_NS, 'sig') or
+            message.hasKey(OPENID_NS, 'signed')):
+            raise ValueError('Message already has signed list or signature')
+
+        extant_handle = message.getArg(OPENID_NS, 'assoc_handle')
+        if extant_handle and extant_handle != self.handle:
+            raise ValueError("Message has a different association handle")
+
+        signed_message = message.copy()
+        signed_message.setArg(OPENID_NS, 'assoc_handle', self.handle)
+        message_keys = signed_message.toPostArgs().keys()
+        if not self.sign_all:
+            signed_list = [k[7:] for k in message_keys
+                           if k.startswith('openid.')]
+            signed_list.append('signed')
+            signed_list.sort()
+            signed_message.setArg(OPENID_NS, 'signed', ','.join(signed_list))
+        sig = self.getMessageSignature(signed_message)
+        signed_message.setArg(OPENID_NS, 'sig', sig)
+        return signed_message
+
+    def checkMessageSignature(self, message):
+        """Given a message with a signature, calculate a new signature
+        and return whether it matches the signature in the message.
+
+        @raises ValueError: if the message has no signature or no signature
+            can be calculated for it.
+        """        
+        message_sig = message.getArg(OPENID_NS, 'sig')
+        if not message_sig:
+            raise ValueError("%s has no sig." % (message,))
+        calculated_sig = self.getMessageSignature(message)
+        return calculated_sig == message_sig
+
+
+    def _makePairs(self, message):
+        # Feels a bit kludgy to dispatch here instead of of letting the class
+        # have the method appropriately defined, but this seemed to be the
+        # least complicated alternative at the time.
+        if self.sign_all:
+            return self._makePairsSignAll(message)
+        else:
+            return self._makePairsSignedList(message)
+
+
+    def _makePairsSignAll(self, message):
+        pairs = message.toPostArgs()
+        if 'openid.sig' in pairs:
+            # A sig does not sign itself.
+            del pairs['openid.sig']
+        pairs = pairs.items()
+        pairs.sort()
+        return pairs
+
+
+    def _makePairsSignedList(self, message):
+        signed = message.getArg(OPENID_NS, 'signed')
+        if not signed:
+            raise ValueError('Message has no signed list: %s' % (message,))
 
-        return request_sig == expected_sig
+        signed_list = signed.split(',')
+        pairs = []
+        data = message.toPostArgs()
+        for field in signed_list:
+            pairs.append((field, data.get('openid.' + field, '')))
+        return pairs
+
+    def __repr__(self):
+        return "<%s.%s %s %s>" % (
+            self.__class__.__module__,
+            self.__class__.__name__,
+            self.assoc_type,
+            self.handle)

Modified: incubator/heraldry/libraries/python/openid/trunk/openid/consumer/consumer.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/openid/trunk/openid/consumer/consumer.py?view=diff&rev=463060&r1=463059&r2=463060
==============================================================================
--- incubator/heraldry/libraries/python/openid/trunk/openid/consumer/consumer.py (original)
+++ incubator/heraldry/libraries/python/openid/trunk/openid/consumer/consumer.py Wed Oct 11 16:22:33 2006
@@ -1,4 +1,4 @@
-# -*- test-case-name: openid.test.consumer -*-
+# -*- test-case-name: openid.test.test_consumer -*-
 """
 This module documents the main interface with the OpenID consumer
 library.  The only part of the library which has to be used and isn't
@@ -181,32 +181,33 @@
     objects.
 """
 
-import string
 import time
 import urllib
 import cgi
 from urlparse import urlparse
 
-from urljr import fetchers
+from openid import fetchers
 
-from openid.consumer.discover import discover as openIDDiscover
+from openid.consumer.discover import discover as discoverURL
 from openid.consumer.discover import discoverXRI
-from openid.consumer.discover import yadis_available, DiscoveryFailure
+from openid.consumer.discover import DiscoveryFailure
+from openid.message import Message, OPENID_NS, OPENID2_NS, OPENID1_NS, \
+     IDENTIFIER_SELECT
 from openid import cryptutil
 from openid import kvform
 from openid import oidutil
-from openid.association import Association
+from openid.association import Association, default_negotiator
 from openid.dh import DiffieHellman
+from openid.store.nonce import mkNonce, split as splitNonce
+from openid.yadis.manager import Discovery
+from openid.yadis import xri
+
 
 __all__ = ['AuthRequest', 'Consumer', 'SuccessResponse',
            'SetupNeededResponse', 'CancelResponse', 'FailureResponse',
            'SUCCESS', 'FAILURE', 'CANCEL', 'SETUP_NEEDED',
            ]
 
-if yadis_available:
-    from yadis.manager import Discovery
-    from yadis import xri
-
 class Consumer(object):
     """An OpenID consumer implementation that performs discovery and
     does session management.
@@ -250,7 +251,7 @@
         self.consumer = GenericConsumer(store)
         self._token_key = self.session_key_prefix + self._token
 
-    def begin(self, user_url):
+    def begin(self, user_url, anonymous=False):
         """Start the OpenID authentication process. See steps 1-2 in
         the overview at the top of this file.
 
@@ -278,36 +279,26 @@
             is available, L{openid.consumer.discover.DiscoveryFailure} is
             an alias for C{yadis.discover.DiscoveryFailure}.
         """
-        if yadis_available and xri.identifierScheme(user_url) == "XRI":
+        if xri.identifierScheme(user_url) == "XRI":
             discoverMethod = discoverXRI
-            openid_url = user_url
         else:
-            discoverMethod = openIDDiscover
-            openid_url = oidutil.normalizeUrl(user_url)
+            discoverMethod = discoverURL
 
-        if yadis_available:
-            try:
-                disco = Discovery(self.session,
-                                  openid_url,
-                                  self.session_key_prefix)
-                service = disco.getNextService(discoverMethod)
-            except fetchers.HTTPFetchingError, e:
-                raise DiscoveryFailure('Error fetching XRDS document', e)
-        else:
-            # XXX - Untested branch!
-            _, services = openIDDiscover(user_url)
-            if not services:
-                service = None
-            else:
-                service = services[0]
+        try:
+            disco = Discovery(self.session,
+                              user_url,
+                              self.session_key_prefix)
+            service = disco.getNextService(discoverMethod)
+        except fetchers.HTTPFetchingError, e:
+            raise DiscoveryFailure('Error fetching XRDS document', e)
 
         if service is None:
             raise DiscoveryFailure(
-                'No usable OpenID services found for %s' % (openid_url,), None)
+                'No usable OpenID services found for %s' % (user_url,), None)
         else:
-            return self.beginWithoutDiscovery(service)
+            return self.beginWithoutDiscovery(service, anonymous)
 
-    def beginWithoutDiscovery(self, service):
+    def beginWithoutDiscovery(self, service, anonymous=False):
         """Start OpenID verification without doing OpenID server
         discovery. This method is used internally by Consumer.begin
         after discovery is performed, and exists to provide an
@@ -330,6 +321,7 @@
         """
         auth_req = self.consumer.begin(service)
         self.session[self._token_key] = auth_req.endpoint
+        auth_req.anonymous = anonymous
         return auth_req
 
     def complete(self, query):
@@ -354,11 +346,11 @@
         if endpoint is None:
             response = FailureResponse(None, 'No session state found')
         else:
-            response = self.consumer.complete(query, endpoint)
+            message = Message.fromPostArgs(query)
+            response = self.consumer.complete(message, endpoint)
             del self.session[self._token_key]
 
         if (response.status in ['success', 'cancel'] and
-            yadis_available and
             response.identity_url is not None):
 
             disco = Discovery(self.session,
@@ -370,8 +362,10 @@
 
         return response
 
-class DiffieHellmanConsumerSession(object):
+class DiffieHellmanSHA1ConsumerSession(object):
     session_type = 'DH-SHA1'
+    hash_func = staticmethod(cryptutil.sha1)
+    allowed_assoc_types = ['HMAC-SHA1']
 
     def __init__(self, dh=None):
         if dh is None:
@@ -395,10 +389,16 @@
     def extractSecret(self, response):
         spub = cryptutil.base64ToLong(response['dh_server_public'])
         enc_mac_key = oidutil.fromBase64(response['enc_mac_key'])
-        return self.dh.xorSecret(spub, enc_mac_key)
+        return self.dh.xorSecret(spub, enc_mac_key, self.hash_func)
+
+class DiffieHellmanSHA256ConsumerSession(DiffieHellmanSHA1ConsumerSession):
+    session_type = 'DH-SHA256'
+    hash_func = staticmethod(cryptutil.sha256)
+    allowed_assoc_types = ['HMAC-SHA256']
 
 class PlainTextConsumerSession(object):
-    session_type = None
+    session_type = 'no-encryption'
+    allowed_assoc_types = ['HMAC-SHA1', 'HMAC-SHA256']
 
     def getRequest(self):
         return {}
@@ -406,70 +406,86 @@
     def extractSecret(self, response):
         return oidutil.fromBase64(response['mac_key'])
 
+class UnsupportedAssocType(Exception):
+    """Exception raised when the server tells us that the session type
+    in our request was not supported."""
+    def __init__(self, message,
+                 supported_assoc_type=None, supported_session_type=None):
+        Exception.__init__(self, message)
+        self.message = message
+        self.supported_assoc_type = supported_assoc_type
+        self.supported_session_type = supported_session_type
+
 class GenericConsumer(object):
     """This is the implementation of the common logic for OpenID
     consumers. It is unaware of the application in which it is
     running.
     """
 
-    NONCE_LEN = 8
-    NONCE_CHRS = string.ascii_letters + string.digits
+    session_types = {
+        'DH-SHA1':DiffieHellmanSHA1ConsumerSession,
+        'DH-SHA256':DiffieHellmanSHA256ConsumerSession,
+        'no-encryption':PlainTextConsumerSession,
+        }
 
     def __init__(self, store):
         self.store = store
+        self.negotiator = default_negotiator.copy()
 
     def begin(self, service_endpoint):
-        nonce = self._createNonce()
         assoc = self._getAssociation(service_endpoint.server_url)
         request = AuthRequest(service_endpoint, assoc)
-        request.return_to_args['nonce'] = nonce
+        request.return_to_args['nonce'] = mkNonce()
         return request
 
-    def complete(self, query, endpoint):
-        mode = query.get('openid.mode', '<no mode specified>')
-
-        if isinstance(mode, list):
-            raise TypeError("query dict must have one value for each key, "
-                            "not lists of values.  Query is %r" % (query,))
+    def complete(self, message, endpoint):
+        mode = message.getArg(OPENID_NS, 'mode', '<No mode set>')
 
         if mode == 'cancel':
             return CancelResponse(endpoint)
         elif mode == 'error':
-            error = query.get('openid.error')
+            error = message.getArg(OPENID_NS, 'error')
             return FailureResponse(endpoint, error)
         elif mode == 'id_res':
-            if endpoint.identity_url is None:
-                return FailureResponse(endpoint, 'No session state found')
             try:
-                response = self._doIdRes(query, endpoint)
+                response = self._doIdRes(message, endpoint)
             except fetchers.HTTPFetchingError, why:
                 message = 'HTTP request failed: %s' % (str(why),)
                 return FailureResponse(endpoint, message)
             else:
                 if response.status == 'success':
-                    return self._checkNonce(response, query.get('nonce'))
+                    return self._checkNonce(endpoint.server_url, response)
                 else:
                     return response
         else:
             return FailureResponse(endpoint,
                                    'Invalid openid.mode: %r' % (mode,))
 
-    def _checkNonce(self, response, nonce):
-        parsed_url = urlparse(response.getReturnTo())
-        query = parsed_url[4]
-        for k, v in cgi.parse_qsl(query):
-            if k == 'nonce':
-                if v != nonce:
-                    return FailureResponse(response, 'Nonce mismatch')
-                else:
+    def _checkNonce(self, server_url, response):
+        nonce = response.getNonce()
+        if nonce is None:
+            # Assume that this is an OpenID 1.X response and
+            # use/extract the nonce that we generated.
+            return_to = response.getReturnTo()
+            parsed_url = urlparse(return_to)
+            query = parsed_url[4]
+            for k, v in cgi.parse_qsl(query):
+                if k == 'nonce':
+                    server_url = '' # came from us
+                    nonce = v
                     break
-        else:
-            return FailureResponse(response, 'Nonce missing from return_to: %r'
-                                   % (response.getReturnTo()))
+            else:
+                msg = 'Nonce missing from return_to: %r' % (
+                    response.getReturnTo())
+                return FailureResponse(response.endpoint, msg)
 
         # The nonce matches the signed nonce in the openid.return_to
         # response parameter
-        if not self.store.useNonce(nonce):
+        try:
+            timestamp, salt = splitNonce(nonce)
+        except ValueError:
+            return FailureResponse(response.endpoint, 'Malformed nonce')
+        if not self.store.useNonce(server_url, timestamp, salt):
             return FailureResponse(response,
                                    'Nonce missing from store')
 
@@ -477,11 +493,6 @@
         # response
         return response
 
-    def _createNonce(self):
-        nonce = cryptutil.randomString(self.NONCE_LEN, self.NONCE_CHRS)
-        self.store.storeNonce(nonce)
-        return nonce
-
     def _makeKVPost(self, args, server_url):
         mode = args['openid.mode']
         body = urllib.urlencode(args)
@@ -505,73 +516,135 @@
 
         return response
 
-    def _doIdRes(self, query, endpoint):
+    def _doIdRes(self, message, endpoint):
         """Handle id_res responses.
 
-        @param query: the response paramaters.
-        @param consumer_id: The normalized Claimed Identifier.
-        @param server_id: The Delegate Identifier.
-        @param server_url: OpenID server endpoint URL.
+        @param message: the response paramaters.
+        @param endpoint: the discovered endpoint object. May be None.
 
         @returntype: L{Response}
         """
-        user_setup_url = query.get('openid.user_setup_url')
+        user_setup_url = message.getArg(OPENID_NS, 'user_setup_url')
         if user_setup_url is not None:
             return SetupNeededResponse(endpoint, user_setup_url)
 
-        return_to = query.get('openid.return_to')
-        server_id2 = query.get('openid.identity')
-        assoc_handle = query.get('openid.assoc_handle')
-
-        if return_to is None or server_id2 is None or assoc_handle is None:
-            return FailureResponse(endpoint, 'Missing required field')
+        try:
+            signed_list = self._idResCheckSignature(message,
+                                                    endpoint.server_url)
+            self._idResCheckForFields(message, signed_list)
+        except ValueError, e:
+            return FailureResponse(endpoint, e.args[0])
 
-        if endpoint.getServerID() != server_id2:
-            return FailureResponse(endpoint, 'Server ID (delegate) mismatch')
+        return_to = message.getArg(OPENID_NS, 'return_to')
+        server_id2 = message.getArg(OPENID_NS, 'identity')
+        assoc_handle = message.getArg(OPENID_NS, 'assoc_handle')
 
-        signed = query.get('openid.signed')
+        # IdP-driven identifier selection requires another round of discovery:
+        if not endpoint.delegate and server_id2:
+            try:
+                endpoint = self._verifyDiscoveryResults(
+                    server_id2, endpoint.server_url)
+            except DiscoveryFailure, exc:
+                return FailureResponse(endpoint, exc.args[0])
+        elif endpoint.delegate != server_id2:
+            fmt = 'Mismatch between delegate (%r) and server (%r) response'
+            return FailureResponse(
+                endpoint, fmt % (endpoint.delegate, server_id2))
+
+        signed_fields = ['openid.' + f for f in signed_list]
+        return SuccessResponse(endpoint, message, signed_fields)
+
+
+    def _idResCheckSignature(self, message, server_url):
+        assoc_handle = message.getArg(OPENID_NS, 'assoc_handle')
+        assoc = self.store.getAssociation(server_url, assoc_handle)
+
+        if assoc:
+            if assoc.getExpiresIn() <= 0:
+                # XXX: It might be a good idea sometimes to re-start the
+                # authentication with a new association. Doing it
+                # automatically opens the possibility for
+                # denial-of-service by a server that just returns expired
+                # associations (or really short-lived associations)
+                raise ValueError('Association with %s expired' % (server_url,))
 
-        assoc = self.store.getAssociation(endpoint.server_url, assoc_handle)
+            if not assoc.checkMessageSignature(message):
+                raise ValueError('Bad signature')
 
-        if assoc is None:
-            # It's not an association we know about.  Dumb mode is our
+            if assoc.sign_all:
+                signed_list = message.toPostArgs().keys()
+            else:
+                signed_list = message.getArg(OPENID_NS, 'signed').split(',')
+                
+        else:
+            # It's not an association we know about.  Stateless mode is our
             # only possible path for recovery.
-            if self._checkAuth(query, endpoint.server_url):
-                return SuccessResponse.fromQuery(endpoint, query, signed)
+            # XXX - async framework will not want to block on this call to
+            # _checkAuth.
+            if not self._checkAuth(message, server_url):
+                raise ValueError('Server denied check_authentication')
+
+            # XXX: Danger!  We don't know what the server's signing algorithm
+            # is.  We assume that if there is no signed list, the server
+            # is not so brain-dead as to return is_valid=true unless it is
+            # using sign-all.
+            signed_list = message.getArg(OPENID_NS, 'signed')
+            if not signed_list:
+                signed_list = message.toPostArgs().keys()
             else:
-                return FailureResponse(endpoint,
-                                       'Server denied check_authentication')
+                signed_list = signed_list.split(',')
+
+        return signed_list
+
+
+    def _idResCheckForFields(self, message, signed_list):
+        # XXX: whether or not 'signed' is required depends on the assoc type.
+        required_fields = ['return_to', 'assoc_handle', 'sig']
+        require_sigs = ['return_to', 'identity', 'nonce']
 
-        if assoc.expiresIn <= 0:
-            # XXX: It might be a good idea sometimes to re-start the
-            # authentication with a new association. Doing it
-            # automatically opens the possibility for
-            # denial-of-service by a server that just returns expired
-            # associations (or really short-lived associations)
-            msg = 'Association with %s expired' % (endpoint.server_url,)
-            return FailureResponse(endpoint, msg)
-
-        # Check the signature
-        sig = query.get('openid.sig')
-        if sig is None or signed is None:
-            return FailureResponse(endpoint, 'Missing argument signature')
-
-        signed_list = signed.split(',')
-
-        # Fail if the identity field is present but not signed
-        if endpoint.identity_url is not None and 'identity' not in signed_list:
-            msg = '"openid.identity" not signed'
-            return FailureResponse(endpoint, msg)
-
-        v_sig = assoc.signDict(signed_list, query)
+        for field in required_fields:
+            if not message.hasKey(OPENID_NS, field):
+                raise ValueError('Missing required field %r' % (field,))
 
-        if v_sig != sig:
-            return FailureResponse(endpoint, 'Bad signature')
+        for field in require_sigs:
+            # Field is present and not in signed list
+            if message.hasKey(OPENID_NS, field) and field not in signed_list:
+                # I wish I could just raise a FailureResponse here, but
+                # they're not exceptions.  :-/
+                raise ValueError('"%s" not signed' % (field,))
 
-        return SuccessResponse.fromQuery(endpoint, query, signed)
 
-    def _checkAuth(self, query, server_url):
-        request = self._createCheckAuthRequest(query)
+    def _verifyDiscoveryResults(self, identifier, server_url):
+        """
+
+        @param identifier: the identifier to perform discovery on.
+        @param server_url: the server endpoint I hope to discover.
+        """
+        if xri.identifierScheme(identifier) == "XRI":
+            discoverMethod = discoverXRI
+        else:
+            discoverMethod = discoverURL
+
+        discovered_id, services = discoverMethod(identifier)
+
+        def serviceMatches(endpoint):
+            return (
+                (endpoint.server_url == server_url) and
+                # Delegate must be equivalent to the discovered URL.
+                ((endpoint.getServerID() == endpoint.identity_url) or
+                 (endpoint.getServerID() == identifier)))
+
+        services = filter(serviceMatches, services)
+
+        if not services:
+            msg = ("Discovery information for %r does not include "
+                   "server %r." % (identifier, server_url))
+            raise DiscoveryFailure(msg, None)
+
+        return services[0]
+
+    def _checkAuth(self, message, server_url):
+        request = self._createCheckAuthRequest(message)
         if request is None:
             return False
         response = self._makeKVPost(request, server_url)
@@ -579,23 +652,48 @@
             return False
         return self._processCheckAuthResponse(response, server_url)
 
-    def _createCheckAuthRequest(self, query):
-        signed = query.get('openid.signed')
-        if signed is None:
-            oidutil.log('No signature present; checkAuth aborted')
-            return None
+    def _createCheckAuthRequest(self, message):
+        signed = message.getArg(OPENID_NS, 'signed')
+        if signed:
+            return self._createCheckAuthRequestSignedList(message)
+        else:
+            # XXX: assuming that no signed list means sign all.
+            return self._createCheckAuthRequestSignAll(message)
+
 
+    def _createCheckAuthRequestSignedList(self, message):
         # Arguments that are always passed to the server and not
         # included in the signature.
         whitelist = ['assoc_handle', 'sig', 'signed', 'invalidate_handle']
-        signed = signed.split(',') + whitelist
 
-        check_args = dict([(k, v) for k, v in query.iteritems()
-                           if k.startswith('openid.') and k[7:] in signed])
+        # XXX: should really build a Message object here
+        check_args = {}
+        for k in whitelist:
+            val = message.getArg(OPENID_NS, k)
+            if val is not None:
+                check_args['openid.' + k] = val
+
+        signed = message.getArg(OPENID_NS, 'signed')
+        if signed:
+            for k in signed.split(','):
+                val = message.getArg(OPENID_NS, k)
+
+                # Signed value is missing
+                if val is None:
+                    return None
+
+                check_args['openid.' + k] = val
 
         check_args['openid.mode'] = 'check_authentication'
         return check_args
 
+
+    def _createCheckAuthRequestSignAll(self, message):
+        check_args = message.toPostArgs()
+        check_args['openid.mode'] = 'check_authentication'
+        return check_args
+
+
     def _processCheckAuthResponse(self, response, server_url):
         is_valid = response.get('is_valid', 'false')
 
@@ -616,40 +714,78 @@
         assoc = self.store.getAssociation(server_url)
 
         if assoc is None or assoc.expiresIn <= 0:
-            assoc_session, args = self._createAssociateRequest(server_url)
-            try:
-                response = self._makeKVPost(args, server_url)
-            except fetchers.HTTPFetchingError, why:
-                oidutil.log('openid.associate request failed: %s' %
-                            (str(why),))
-                assoc = None
+            (assoc_type, session_type) = self.negotiator.getAllowedType()
+            tried_types = []
+            while (assoc_type, session_type) not in tried_types:
+                assoc_session, args = self._createAssociateRequest(
+                    server_url,
+                    assoc_type,
+                    session_type)
+                try:
+                    response = self._makeKVPost(args, server_url)
+                except fetchers.HTTPFetchingError, why:
+                    oidutil.log('openid.associate request failed: %s' %
+                                (str(why),))
+                    assoc = None
+                    break
+                else:
+                    if response is None:
+                        oidutil.log('openid.associate request failed: ' +
+                                    'no reason given.')
+                        assoc = None
+                        break
+
+                    try:
+                        assoc = self._parseAssociation(
+                            response, assoc_session, server_url)
+                    except UnsupportedAssocType, why:
+                        oidutil.log(
+                            'Unsupported assoc type: %s' % (why.message,))
+                        assoc_type = why.supported_assoc_type
+                        session_type = why.supported_session_type
+                        if assoc_type is None or session_type is None:
+                            oidutil.log('No allowed session type specified')
+                            assoc = None
+                            break
+
+                        if not self.negotiator.isAllowed(assoc_type,
+                                                         session_type):
+                            msg = (
+                                'Server sent unsupported session type %s '
+                                'for association type %s'
+                                ) % (session_type, assoc_type)
+                            oidutil.log(msg)
+                            assoc = None
+                            break
+                    else:
+                        break
             else:
-                assoc = self._parseAssociation(
-                    response, assoc_session, server_url)
+                fmt = 'No association created. Tried types: %s'
+                oidutil.log(fmt % (tried_types,))
+                assoc = None
 
         return assoc
 
-    def _createAssociateRequest(self, server_url):
-        proto = urlparse(server_url)[0]
-        if proto == 'https':
-            session_type = PlainTextConsumerSession
-        else:
-            session_type = DiffieHellmanConsumerSession
-
-        assoc_session = session_type()
+    def _createAssociateRequest(self, server_url, assoc_type, session_type):
+        session_type_class = self.session_types[session_type]
+        assoc_session = session_type_class()
 
         args = {
             'openid.mode': 'associate',
-            'openid.assoc_type':'HMAC-SHA1',
+            'openid.assoc_type': assoc_type,
             }
 
-        if assoc_session.session_type is not None:
+        if assoc_session.session_type != 'no-encryption':
             args['openid.session_type'] = assoc_session.session_type
 
         args.update(assoc_session.getRequest())
         return assoc_session, args
 
     def _parseAssociation(self, results, assoc_session, server_url):
+        error_code = results.get('error_code')
+        if error_code is not None:
+            return self._associateError(results)
+
         try:
             assoc_type = results['assoc_type']
             assoc_handle = results['assoc_handle']
@@ -659,22 +795,10 @@
             oidutil.log(fmt % (server_url, e[0]))
             return None
 
-        if assoc_type != 'HMAC-SHA1':
-            fmt = 'Unsupported assoc_type returned from server %s: %s'
-            oidutil.log(fmt % (server_url, assoc_type))
-            return None
-
-        try:
-            expires_in = int(expires_in_str)
-        except ValueError, e:
-            fmt = 'Getting Association: invalid expires_in field: %s'
-            oidutil.log(fmt % (e[0],))
-            return None
-
         session_type = results.get('session_type')
         if session_type != assoc_session.session_type:
-            if session_type is None:
-                oidutil.log('Falling back to plain text association '
+            if session_type is None or session_type == 'no-encryption':
+                oidutil.log('Falling back to no-encryption association '
                             'session from %s' % assoc_session.session_type)
                 assoc_session = PlainTextConsumerSession()
             else:
@@ -682,6 +806,21 @@
                             (assoc_session.session_type, session_type))
                 return None
 
+        if assoc_type not in assoc_session.allowed_assoc_types:
+            msg = (
+                'Unsupported assoc_type for session %s returned '
+                'from server %s: %s'
+                ) % (server_url, assoc_session.session_type, assoc_type)
+            oidutil.log(msg)
+            return None
+
+        try:
+            expires_in = int(expires_in_str)
+        except ValueError, e:
+            fmt = 'Getting Association: invalid expires_in field: %s'
+            oidutil.log(fmt % (e[0],))
+            return None
+
         try:
             secret = assoc_session.extractSecret(results)
         except ValueError, why:
@@ -699,6 +838,23 @@
 
         return assoc
 
+    def _associateError(self, results):
+        error_code = results['error_code']
+        default_message = 'associate error from server: %s' % (error_code,)
+        message = results.get('error', default_message)
+        if error_code == 'unsupported-type':
+            supported_assoc_type = results.get('assoc_type')
+            supported_session_type = results.get('session_type')
+            raise UnsupportedAssocType(
+                message,
+                supported_assoc_type,
+                supported_session_type,
+                )
+        else:
+            fmt = 'Error response to associate request from server: %s'
+            oidutil.log(fmt % (message,))
+            return None
+
 class AuthRequest(object):
     def __init__(self, endpoint, assoc):
         """
@@ -711,8 +867,10 @@
         """
         self.assoc = assoc
         self.endpoint = endpoint
-        self.extra_args = {}
         self.return_to_args = {}
+        self.message = Message()
+        self.message.setOpenIDNamespace(endpoint.preferredNamespace())
+        self.anonymous = False
 
     def addExtensionArg(self, namespace, key, value):
         """Add an extension argument to this OpenID authentication
@@ -739,29 +897,52 @@
 
         @type value: str
         """
-        arg_name = '.'.join(['openid', namespace, key])
-        self.extra_args[arg_name] = value
+        self.message.setArg(namespace, key, value)
+
+    def getMessage(self, trust_root, return_to, immediate=False):
+        return_to = oidutil.appendArgs(return_to, self.return_to_args)
 
-    def redirectURL(self, trust_root, return_to, immediate=False):
         if immediate:
             mode = 'checkid_immediate'
         else:
             mode = 'checkid_setup'
 
-        return_to = oidutil.appendArgs(return_to, self.return_to_args)
+        message = self.message.copy()
+        message.updateArgs(OPENID_NS,
+            {'mode':mode,
+             'return_to':return_to,
+             'trust_root':trust_root,
+             })
+
+        if not self.anonymous:
+            request_identity = self.endpoint.getServerID()
+            if request_identity is None:
+                request_identity = IDENTIFIER_SELECT
 
-        redir_args = {
-            'openid.mode': mode,
-            'openid.identity': self.endpoint.getServerID(),
-            'openid.return_to': return_to,
-            'openid.trust_root': trust_root,
-            }
+            message.setArg(OPENID_NS, 'identity', request_identity)
 
         if self.assoc:
-            redir_args['openid.assoc_handle'] = self.assoc.handle
+            message.setArg(OPENID_NS, 'assoc_handle', self.assoc.handle)
+
+        return message
+
+    def redirectURL(self, trust_root, return_to, immediate=False):
+        message = self.getMessage(trust_root, return_to, immediate)
+        return message.toURL(self.endpoint.server_url)
 
-        redir_args.update(self.extra_args)
-        return oidutil.appendArgs(self.endpoint.server_url, redir_args)
+    def formMarkup(self, trust_root, return_to, immediate=False,
+            form_tag_attrs=None):
+        """Get html for a form to submit this request to the IDP.
+         
+        @param form_tag_attrs: Dictionary of attributes to be added to
+            the form tag. 'accept-charset' and 'enctype' have defaults
+            that can be overridden. If a value is supplied for
+            'action' or 'method', it will be replaced.
+        @type form_tag_attrs: {unicode: unicode}
+        """
+        message = self.getMessage(trust_root, return_to, immediate)
+        return message.toFormMarkup(self.endpoint.server_url, 
+                    form_tag_attrs)
 
 FAILURE = 'failure'
 SUCCESS = 'success'
@@ -771,6 +952,13 @@
 class Response(object):
     status = None
 
+    def setEndpoint(self, endpoint):
+        self.endpoint = endpoint
+        if endpoint is None:
+            self.identity_url = None
+        else:
+            self.identity_url = endpoint.identity_url
+
 class SuccessResponse(Response):
     """A response with a status of SUCCESS. Indicates that this request is a
     successful acknowledgement from the OpenID server that the
@@ -783,7 +971,7 @@
         such as the CanonicalID of an XRI, through this object.
     @type endpoint: L{OpenIDServiceEndpoint<openid.consumer.discover.OpenIDServiceEndpoint>}
 
-    @ivar signed_args: The arguments in the server's response that
+    @ivar signed_fields: The arguments in the server's response that
         were signed and verified.
 
     @cvar status: SUCCESS
@@ -791,35 +979,32 @@
 
     status = SUCCESS
 
-    def __init__(self, endpoint, signed_args):
+    def __init__(self, endpoint, message, signed_fields=None):
+        # Don't use setEndpoint, because endpoint should never be None
+        # for a successfull transaction.
         self.endpoint = endpoint
         self.identity_url = endpoint.identity_url
-        self.signed_args = signed_args
 
-    def fromQuery(cls, endpoint, query, signed):
-        signed_args = {}
-        for field_name in signed.split(','):
-            field_name = 'openid.' + field_name
-            signed_args[field_name] = query.get(field_name, '')
-        return cls(endpoint, signed_args)
-
-    fromQuery = classmethod(fromQuery)
-
-    def extensionResponse(self, prefix):
-        """extract signed extension data from the server's response.
-
-        @param prefix: The extension namespace from which to extract
-            the extension data.
-        """
-        response = {}
-        prefix = 'openid.%s.' % (prefix,)
-        prefix_len = len(prefix)
-        for k, v in self.signed_args.iteritems():
-            if k.startswith(prefix):
-                response_key = k[prefix_len:]
-                response[response_key] = v
+        self.message = message
 
-        return response
+        if signed_fields is None:
+            signed_fields = []
+        self.signed_fields = signed_fields
+
+    def isSigned(self, ns_uri, ns_key):
+        """Return whether a particular key is signed, regardless of
+        its namespace alias
+        """
+        return self.message.getKey(ns_uri, ns_key) in self.signed_fields
+
+    def getSigned(self, ns_uri, ns_key, default=None):
+        """Return the specified signed field if available,
+        otherwise return default
+        """
+        if self.isSigned(ns_uri, ns_key):
+            return self.message.getArg(ns_uri, ns_key, default)
+        else:
+            return default
 
     def getReturnTo(self):
         """Get the openid.return_to argument from this response.
@@ -833,9 +1018,19 @@
 
         @returntype: str
         """
-        return self.signed_args.get('openid.return_to', None)
+        return self.getSigned(OPENID_NS, 'return_to')
 
+    def getNonce(self):
+        """Get the openid.nonce argument from this response.
 
+        This is useful for preventing replay attacks.
+
+        @returns: The nonce generated by the server, or C{None} if the
+            response did not contain an C{openid.nonce} argument.
+
+        @returntype: str
+        """
+        return self.getSigned(OPENID2_NS, 'nonce')
 
 class FailureResponse(Response):
     """A response with a status of FAILURE. Indicates that the OpenID
@@ -853,14 +1048,9 @@
     status = FAILURE
 
     def __init__(self, endpoint, message=None):
-        self.endpoint = endpoint
-        if endpoint is not None:
-            self.identity_url = endpoint.identity_url
-        else:
-            self.identity_url = None
+        self.setEndpoint(endpoint)
         self.message = message
 
-
     def __repr__(self):
         return "<%s.%s id=%r message=%r>" % (
             self.__class__.__module__, self.__class__.__name__,
@@ -880,8 +1070,7 @@
     status = CANCEL
 
     def __init__(self, endpoint):
-        self.endpoint = endpoint
-        self.identity_url = endpoint.identity_url
+        self.setEndpoint(endpoint)
 
 class SetupNeededResponse(Response):
     """A response with a status of SETUP_NEEDED. Indicates that the
@@ -902,6 +1091,5 @@
     status = SETUP_NEEDED
 
     def __init__(self, endpoint, setup_url=None):
-        self.endpoint = endpoint
-        self.identity_url = endpoint.identity_url
+        self.setEndpoint(endpoint)
         self.setup_url = setup_url

Modified: incubator/heraldry/libraries/python/openid/trunk/openid/consumer/discover.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/openid/trunk/openid/consumer/discover.py?view=diff&rev=463060&r1=463059&r2=463060
==============================================================================
--- incubator/heraldry/libraries/python/openid/trunk/openid/consumer/discover.py (original)
+++ incubator/heraldry/libraries/python/openid/trunk/openid/consumer/discover.py Wed Oct 11 16:22:33 2006
@@ -1,40 +1,29 @@
 # -*- test-case-name: openid.test.test_discover -*-
 
-from urljr import fetchers
+import urlparse
 
-from openid import oidutil
+from openid import oidutil, fetchers, urinorm
 
-# If the Yadis library is available, use it. Otherwise, only use
-# old-style discovery.
-try:
-    import yadis
-except ImportError:
-    yadis_available = False
-
-    oidutil.log('Consumer operating without Yadis support '
-                '(failed to import Yadis library)')
-
-    class DiscoveryFailure(RuntimeError):
-        """A failure to discover an OpenID server.
-
-        When the C{yadis} package is available, this is
-        C{yadis.discover.DiscoveryFailure}."""
-else:
-    yadis_available = True
-    from yadis.etxrd import nsTag, XRDSError
-    from yadis.services import applyFilter as extractServices
-    from yadis.discover import discover as yadisDiscover
-    from yadis.discover import DiscoveryFailure
-    from yadis import xrires, filters
+from openid import yadis
+from openid.yadis.etxrd import nsTag, XRDSError
+from openid.yadis.services import applyFilter as extractServices
+from openid.yadis.discover import discover as yadisDiscover
+from openid.yadis.discover import DiscoveryFailure
+from openid.yadis import xrires, filters
 
 from openid.consumer.parse import openIDDiscover as parseOpenIDLinkRel
 from openid.consumer.parse import ParseError
 
 OPENID_1_0_NS = 'http://openid.net/xmlns/1.0'
+OPENID_IDP_2_0_TYPE = 'http://openid.net/server/2.0'
+OPENID_2_0_TYPE = 'http://openid.net/signon/2.0'
 OPENID_1_2_TYPE = 'http://openid.net/signon/1.2'
 OPENID_1_1_TYPE = 'http://openid.net/signon/1.1'
 OPENID_1_0_TYPE = 'http://openid.net/signon/1.0'
 
+from openid.message import OPENID1_NS as OPENID_1_0_MESSAGE_NS
+from openid.message import OPENID2_NS as OPENID_2_0_MESSAGE_NS
+
 class OpenIDServiceEndpoint(object):
     """Object representing an OpenID service endpoint.
 
@@ -42,11 +31,21 @@
     @ivar canonicalID: For XRI, the persistent identifier.
     """
     openid_type_uris = [
+        OPENID_IDP_2_0_TYPE,
+
+        OPENID_2_0_TYPE,
         OPENID_1_2_TYPE,
         OPENID_1_1_TYPE,
         OPENID_1_0_TYPE,
         ]
 
+    def preferredNamespace(self):
+        if (OPENID_IDP_2_0_TYPE in self.type_uris or 
+            OPENID_2_0_TYPE in self.type_uris):
+            return OPENID_2_0_MESSAGE_NS
+        else:
+            return OPENID_1_0_MESSAGE_NS
+
     def __init__(self):
         self.identity_url = None
         self.server_url = None
@@ -62,11 +61,17 @@
         """Set the state of this object based on the contents of the
         service element."""
         self.type_uris = type_uris
-        self.identity_url = yadis_url
         self.server_url = uri
-        self.delegate = findDelegate(service_element)
         self.used_yadis = True
 
+        if not (OPENID_IDP_2_0_TYPE in self.type_uris):            
+            # XXX: This has crappy implications for Service elements
+            # that contain both 'server' and 'signon' Types.  But
+            # that's a pathological configuration anyway, so I don't
+            # think I care.
+            self.delegate = findDelegate(service_element)
+            self.identity_url = yadis_url
+
     def getServerID(self):
         """Return the identifier that should be sent as the
         openid.identity_url parameter to the server."""
@@ -130,6 +135,14 @@
 
     return delegate
 
+def normalizeURL(url):
+    """Normalize a URL, converting normalization failures to
+    DiscoveryFailure"""
+    try:
+        return urinorm.urinorm(url)
+    except ValueError, why:
+        raise DiscoveryFailure('Normalizing identifier: %s' % (why[0],), None)
+
 def discoverYadis(uri):
     """Discover OpenID services for a URI. Tries Yadis and falls back
     on old-style <link rel='...'> discovery if Yadis fails.
@@ -179,7 +192,6 @@
 
     return (identity_url, openid_services)
 
-
 def discoverXRI(iname):
     endpoints = []
     try:
@@ -219,7 +231,15 @@
 
     return identity_url, openid_services
 
-if yadis_available:
-    discover = discoverYadis
-else:
-    discover = discoverNoYadis
+def discover(uri):
+    parsed = urlparse.urlparse(uri)
+    if parsed[0] and parsed[1]:
+        if parsed[0] not in ['http', 'https']:
+            raise DiscoveryFailure('URI scheme is not HTTP or HTTPS', None)
+    else:
+        uri = 'http://' + uri
+    
+    uri = normalizeURL(uri)
+    identity_url, openid_services = discoverYadis(uri)
+    identity_url = normalizeURL(identity_url)
+    return identity_url, openid_services

Modified: incubator/heraldry/libraries/python/openid/trunk/openid/cryptutil.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/openid/trunk/openid/cryptutil.py?view=diff&rev=463060&r1=463059&r2=463060
==============================================================================
--- incubator/heraldry/libraries/python/openid/trunk/openid/cryptutil.py (original)
+++ incubator/heraldry/libraries/python/openid/trunk/openid/cryptutil.py Wed Oct 11 16:22:33 2006
@@ -14,16 +14,57 @@
 """
 
 __all__ = ['randrange', 'hmacSha1', 'sha1', 'randomString',
-           'binaryToLong', 'longToBinary', 'longToBase64', 'base64ToLong']
+           'binaryToLong', 'longToBinary', 'longToBase64', 'base64ToLong',
+           'hmacSha256', 'sha256']
 
 import hmac
 import os
 import random
-import sha
 
 from openid.oidutil import toBase64, fromBase64
 
 try:
+    import hashlib
+except ImportError:
+    import sha as sha1_module
+
+    try:
+        from Crypto.Hash import SHA256 as sha256_module
+    except ImportError:
+        sha256_module = None
+
+else:
+    class HashContainer(object):
+        def __init__(self, hash_constructor):
+            self.new = hash_constructor
+
+    sha1_module = HashContainer(hashlib.sha1)
+    sha256_module = HashContainer(hashlib.sha256)
+
+def hmacSha1(key, text):
+    return hmac.new(key, text, sha1_module).digest()
+
+def sha1(s):
+    return sha1_module.new(s).digest()
+
+if sha256_module is not None:
+    def hmacSha256(key, text):
+        return hmac.new(key, text, sha256_module).digest()
+
+    def sha256(s):
+        return sha256_module.new(s).digest()
+
+else:
+    _no_sha256 = NotImplementedError(
+        'Use Python 2.5, install pycrypto or install hashlib to use SHA256')
+
+    def hmacSha256(unused_key, unused_text):
+        raise _no_sha256
+
+    def sha256(s):
+        raise _no_sha256
+
+try:
     from Crypto.Util.number import long_to_bytes, bytes_to_long
 except ImportError:
     import pickle
@@ -149,12 +190,6 @@
                 break
 
         return start + (n % r) * step
-
-def hmacSha1(key, text):
-    return hmac.new(key, text, sha).digest()
-
-def sha1(s):
-    return sha.new(s).digest()
 
 def longToBase64(l):
     return toBase64(longToBinary(l))

Modified: incubator/heraldry/libraries/python/openid/trunk/openid/dh.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/openid/trunk/openid/dh.py?view=diff&rev=463060&r1=463059&r2=463060
==============================================================================
--- incubator/heraldry/libraries/python/openid/trunk/openid/dh.py (original)
+++ incubator/heraldry/libraries/python/openid/trunk/openid/dh.py Wed Oct 11 16:22:33 2006
@@ -36,7 +36,7 @@
     def getSharedSecret(self, composite):
         return pow(composite, self.private, self.modulus)
 
-    def xorSecret(self, composite, secret):
+    def xorSecret(self, composite, secret, hash_func):
         dh_shared = self.getSharedSecret(composite)
-        sha1_dh_shared = cryptutil.sha1(cryptutil.longToBinary(dh_shared))
-        return strxor(secret, sha1_dh_shared)
+        hashed_dh_shared = hash_func(cryptutil.longToBinary(dh_shared))
+        return strxor(secret, hashed_dh_shared)



Mime
View raw message