Index: django/contrib/admin/media/js/login_encrypt.js
===================================================================
--- django/contrib/admin/media/js/login_encrypt.js	(revision 0)
+++ django/contrib/admin/media/js/login_encrypt.js	(revision 0)
@@ -0,0 +1,165 @@
+var login_form = document.getElementById('id_login_form');
+if (login_form) {
+	var salt = document.getElementById('id_login_salt');
+	if (salt && salt.value) {
+		// hide "sending plain text" message
+		var no_encryption = document.getElementById('id_no_encryption');
+		if (no_encryption) {
+			no_encryption.style.display = 'none';
+		}
+		// add encryption method
+		addEvent(login_form, 'submit', encrypt_password);
+	}
+}
+
+function encrypt_password() {
+	// encrypt with login salt
+	var login_salt = document.getElementById('id_login_salt');
+	var username = document.getElementById('id_username');
+	var password = document.getElementById('id_password');
+	var salt = hex_sha1(username.value).substring(0,5);
+	var hsh = hex_sha1(password.value+salt);
+	// make random salt
+	var salt = hex_sha1(Math.random().toString()).substring(0,5);
+	// put the hashed hash in password & new salt value
+	password.value = '';
+	login_salt.value = salt+'$'+hex_sha1(hsh+login_salt.value+salt);
+}
+
+
+// cut down version of sha1.js from http://pajhome.org.uk/crypt/md5/sha1src.html
+
+/*
+ * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
+ * in FIPS PUB 180-1
+ * Version 2.1a Copyright Paul Johnston 2000 - 2002.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for details.
+ */
+
+/*
+ * Configurable variables. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ */
+var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
+
+/*
+ * These are the functions you'll usually want to call
+ */
+function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));}
+
+/*
+ * Calculate the SHA-1 of an array of big-endian words, and a bit length
+ */
+function core_sha1(x, len)
+{
+  /* append padding */
+  x[len >> 5] |= 0x80 << (24 - len % 32);
+  x[((len + 64 >> 9) << 4) + 15] = len;
+
+  var w = Array(80);
+  var a =  1732584193;
+  var b = -271733879;
+  var c = -1732584194;
+  var d =  271733878;
+  var e = -1009589776;
+
+  for(var i = 0; i < x.length; i += 16)
+  {
+    var olda = a;
+    var oldb = b;
+    var oldc = c;
+    var oldd = d;
+    var olde = e;
+
+    for(var j = 0; j < 80; j++)
+    {
+      if(j < 16) w[j] = x[i + j];
+      else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
+      var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
+                       safe_add(safe_add(e, w[j]), sha1_kt(j)));
+      e = d;
+      d = c;
+      c = rol(b, 30);
+      b = a;
+      a = t;
+    }
+
+    a = safe_add(a, olda);
+    b = safe_add(b, oldb);
+    c = safe_add(c, oldc);
+    d = safe_add(d, oldd);
+    e = safe_add(e, olde);
+  }
+  return Array(a, b, c, d, e);
+
+}
+
+/*
+ * Perform the appropriate triplet combination function for the current
+ * iteration
+ */
+function sha1_ft(t, b, c, d)
+{
+  if(t < 20) return (b & c) | ((~b) & d);
+  if(t < 40) return b ^ c ^ d;
+  if(t < 60) return (b & c) | (b & d) | (c & d);
+  return b ^ c ^ d;
+}
+
+/*
+ * Determine the appropriate additive constant for the current iteration
+ */
+function sha1_kt(t)
+{
+  return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
+         (t < 60) ? -1894007588 : -899497514;
+}
+
+/*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+function safe_add(x, y)
+{
+  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+  return (msw << 16) | (lsw & 0xFFFF);
+}
+
+/*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+function rol(num, cnt)
+{
+  return (num << cnt) | (num >>> (32 - cnt));
+}
+
+/*
+ * Convert an 8-bit or 16-bit string to an array of big-endian words
+ * In 8-bit function, characters >255 have their hi-byte silently ignored.
+ */
+function str2binb(str)
+{
+  var bin = Array();
+  var mask = (1 << chrsz) - 1;
+  for(var i = 0; i < str.length * chrsz; i += chrsz)
+    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32);
+  return bin;
+}
+
+/*
+ * Convert an array of big-endian words to a hex string.
+ */
+function binb2hex(binarray)
+{
+  var hex_tab = "0123456789abcdef";
+  var str = "";
+  for(var i = 0; i < binarray.length * 4; i++)
+  {
+    str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
+           hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8  )) & 0xF);
+  }
+  return str;
+}
Index: django/contrib/admin/templates/admin/login.html
===================================================================
--- django/contrib/admin/templates/admin/login.html	(revision 2524)
+++ django/contrib/admin/templates/admin/login.html	(working copy)
@@ -9,7 +9,7 @@
 <p class="errornote">{{ error_message }}</p>
 {% endif %}
 <div id="content-main">
-<form action="{{ app_path }}" method="post">
+<form action="{{ app_path }}" method="post" id="id_login_form">
 
 <p class="aligned">
 <label for="id_username">{% trans 'Username:' %}</label> <input type="text" name="username" id="id_username" />
@@ -17,8 +17,10 @@
 <p class="aligned">
 <label for="id_password">{% trans 'Password:' %}</label> <input type="password" name="password" id="id_password" />
 <input type="hidden" name="this_is_the_login_form" value="1" />
+{% if login_salt %}<input type="hidden" name="login_salt" value="{{ login_salt }}" id="id_login_salt" />{% endif %}
 <input type="hidden" name="post_data" value="{{ post_data }}" />{% comment %} <span class="help">{% trans 'Have you <a href="/password_reset/">forgotten your password</a>?' %}</span>{% endcomment %}
 </p>
+<p class="help" id="id_no_encryption">{% trans 'Your password will be sent without encryption' %}<noscript> ({% trans 'Javascript is turned off' %})</noscript></p>
 
 <div class="aligned ">
 <label>&nbsp;</label><input type="submit" value="{% trans 'Log in' %}" />
@@ -28,5 +30,7 @@
 <script type="text/javascript">
 document.getElementById('id_username').focus()
 </script>
+<script type="text/javascript" src="{% load adminmedia %}{% admin_media_prefix %}js/core.js"></script>
+<script type="text/javascript" src="{% admin_media_prefix %}js/login_encrypt.js"></script>
 </div>
 {% endblock %}
Index: django/contrib/admin/views/decorators.py
===================================================================
--- django/contrib/admin/views/decorators.py	(revision 2524)
+++ django/contrib/admin/views/decorators.py	(working copy)
@@ -1,13 +1,15 @@
 from django.core.extensions import DjangoContext, render_to_response
 from django.conf.settings import SECRET_KEY
-from django.models.auth import users
+from django.models.auth import users, CURRENT_ALGORITHM
 from django.utils import httpwrappers
 from django.utils.translation import gettext_lazy
 import base64, datetime, md5
 import cPickle as pickle
+import random
 
 ERROR_MESSAGE = gettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.")
 LOGIN_FORM_KEY = 'this_is_the_login_form'
+LOGIN_SALT_KEY = '_session_salt'
 
 def _display_login_form(request, error_message=''):
     request.session.set_test_cookie()
@@ -23,7 +25,8 @@
         'title': _('Log in'),
         'app_path': request.path,
         'post_data': post_data,
-        'error_message': error_message
+        'error_message': error_message,
+        'login_salt': request.session.get(LOGIN_SALT_KEY)
     }, context_instance=DjangoContext(request))
 
 def _encode_post_data(post_data):
@@ -68,6 +71,11 @@
             message = _("Looks like your browser isn't configured to accept cookies. Please enable cookies, reload this page, and try again.")
             return _display_login_form(request, message)
 
+        # Get the previous login salt in case this is a login attempt
+        # then generate a new login salt.
+        last_login_salt = request.session.get(LOGIN_SALT_KEY, '')
+        request.session[LOGIN_SALT_KEY] = users.do_hash(CURRENT_ALGORITHM, str(random.random()))[:5]
+
         # Check the password.
         username = request.POST.get('username', '')
         try:
@@ -86,7 +94,24 @@
 
         # The user data is correct; log in the user in and continue.
         else:
-            if user.check_password(request.POST.get('password', '')):
+            is_correct = False
+            
+            incoming_salt = request.POST.get('login_salt', '')
+            if '$' in incoming_salt:
+                # Javascript encrypted hash and changed salt.
+                incoming_salt, incoming_hash = incoming_salt.split('$',1)
+                is_correct = user.check_password_hash(incoming_hash, last_login_salt+incoming_salt)
+                algo, nothing = user.password.split('$', 1)
+                if algo != CURRENT_ALGORITHM:
+                    message = _("Your password needs to be updated to a more secure format, please type your password again.")
+                    # Set empty salt so next password won't be encrypted.
+                    request.session[LOGIN_SALT_KEY] = ''
+                    return _display_login_form(request, message)
+
+            if not is_correct:
+                is_correct = user.check_password(request.POST.get('password', ''))
+
+            if is_correct:
                 request.session[users.SESSION_KEY] = user.id
                 user.last_login = datetime.datetime.now()
                 user.save()
Index: django/models/auth.py
===================================================================
--- django/models/auth.py	(revision 2524)
+++ django/models/auth.py	(working copy)
@@ -2,6 +2,8 @@
 from django.models import core
 from django.utils.translation import gettext_lazy as _
 
+CURRENT_ALGORITHM = 'sha1un'   #user name
+
 class Permission(meta.Model):
     name = meta.CharField(_('name'), maxlength=50)
     package = meta.ForeignKey(core.Package, db_column='package')
@@ -78,10 +80,12 @@
         return full_name.strip()
 
     def set_password(self, raw_password):
-        import sha, random
-        algo = 'sha1'
-        salt = sha.new(str(random.random())).hexdigest()[:5]
-        hsh = sha.new(salt+raw_password).hexdigest()
+        import random
+        from django.models.auth import users, CURRENT_ALGORITHM
+        algo = CURRENT_ALGORITHM
+        #salt = do_hash(algo, str(random.random()))[:5]
+        salt = users.do_hash(algo, self.username)[:5]
+        hsh = users.do_hash(algo, raw_password, salt)
         self.password = '%s$%s$%s' % (algo, salt, hsh)
 
     def check_password(self, raw_password):
@@ -89,24 +93,33 @@
         Returns a boolean of whether the raw_password was correct. Handles
         encryption formats behind the scenes.
         """
-        # Backwards-compatibility check. Older passwords won't include the
+        from django.models.auth import users, CURRENT_ALGORITHM
+        # Backwards-compatibility check. Original MD5 passwords won't include the
         # algorithm or salt.
-        if '$' not in self.password:
-            import md5
-            is_correct = (self.password == md5.new(raw_password).hexdigest())
-            if is_correct:
-                # Convert the password to the new, more secure format.
-                self.set_password(raw_password)
-                self.save()
-            return is_correct
-        algo, salt, hsh = self.password.split('$')
-        if algo == 'md5':
-            import md5
-            return hsh == md5.new(salt+raw_password).hexdigest()
-        elif algo == 'sha1':
-            import sha
-            return hsh == sha.new(salt+raw_password).hexdigest()
-        raise ValueError, "Got unknown password algorithm type in password."
+        algo = 'md5'
+        salt = ''
+        hsh = self.password
+        if '$' in self.password:
+            algo, salt, hsh = self.password.split('$', 2)
+        is_correct = (hsh == users.do_hash(algo, raw_password, salt))
+        # Make sure we are using the current algorithm. Otherwise upgrade.
+        if algo != CURRENT_ALGORITHM and is_correct:
+            # Convert the password to the new, more secure format.
+            self.set_password(raw_password)
+            self.save()
+        return is_correct
+    
+    def check_password_hash(self, incoming_hsh, incoming_salt):
+        """
+        Returns a boolean of whether the hash (done by javascript in the login
+        form) matches.
+        The javascript uses the login_salt + password to get the database hash.
+        Then it hashes this result with it's own random salt.
+        """
+        from django.models.auth.users import do_hash
+        algo, salt, hsh = self.password.split('$', 2)
+        hashed_hash = do_hash(algo, hsh, incoming_salt)
+        return incoming_hsh == hashed_hash
 
     def get_group_permissions(self):
         "Returns a list of permission strings that this user has through his/her groups."
@@ -211,6 +224,21 @@
         from random import choice
         return ''.join([choice(allowed_chars) for i in range(length)])
 
+    def _module_do_hash(algo, value, salt=''):
+        "Hash a value using the given algorithm."
+        if algo == 'md5':
+            import md5
+            return md5.new(salt+value).hexdigest()
+        elif algo == 'sha1':
+            import sha
+            return sha.new(salt+value).hexdigest()
+        # Psuedorandom username salted hash (different algo so existing names
+        # get updated).
+        elif algo == 'sha1un':
+            import sha
+            return sha.new(value+salt).hexdigest()  # note: salt last
+        raise ValueError, "Got unknown algorithm type."
+
 class Message(meta.Model):
     user = meta.ForeignKey(User)
     message = meta.TextField(_('Message'))
