summaryrefslogtreecommitdiff
path: root/django/srpproject/srp/views.py
blob: 4200e8c97e9f93a21272506062b0e094ef6b0d81 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# Create your views here.

from django.http import HttpResponse

from srp import models

###
### General methods
###

# We need randomly generated salts. This is about 100 bits of entropy.
def generate_salt():
    import string, random   
    randomgen = random.SystemRandom()
    salt_chars = "./" + string.ascii_letters + string.digits
    return "".join([randomgen.choice(salt_chars) for i in range(0,16)])

# We want to avoid information leakage. For users that don't exist, we need salts to be consistent.
# These "fake" salts are seeded with the username and the django secret_key. They're not as random
# as true salts should be, but they should be indistinguishable to a hacker who isn't sure whether
# or not an account exists.
def generate_fake_salt(I):
    import string, random, settings, hashlib
    random.seed("%s:%s" % (I, settings.SECRET_KEY))
    salt_chars = "./" + string.ascii_letters + string.digits    
    salt = "".join([random.choice(salt_chars) for i in range(0,16)])
    return salt, int(hashlib.sha256("%s:%s" % (salt, settings.SECRET_KEY)).hexdigest(), 16)
    
def login_page(request):
    from django.shortcuts import render_to_response
    return render_to_response('login.html',{'static_files': "http://%s/srp-test/javascript" % request.get_host()})

def register_page(request):
    from django.shortcuts import render_to_response
    return render_to_response('register.html',{'static_files': "http://%s/srp-test/javascript" % request.get_host()})

###
### User Registration
###

# Step 1. A client submits a username. If the username is available, we generate a salt, store it, and return it.
# Otherwise, we return an error.
def register_salt(request):
    if models.User.objects.filter(name=request.POST["I"]).count() > 0:
        return HttpResponse("<error>Username already in use</error>", mimetype="text/xml")
    request.session["srp_name"] = request.POST["I"]
    request.session["srp_salt"] = generate_salt()
    return HttpResponse("<salt>%s</salt>" % request.session["srp_salt"], mimetype="text/xml")
    
# Step 2. The client creates the password verifier and sends it to the server, along with a username.
def register_user(request):
    from django.contrib import auth
    models.User(salt=request.session["srp_salt"], name=request.session["srp_name"], verifier=request.POST["v"]).save()
    auth.models.User.objects.create_user(request.session["srp_name"],'', str(request.POST["v"]))
    del request.session["srp_salt"]
    del request.session["srp_name"]
    return HttpResponse("<ok/>", mimetype="text/xml");
    
# Step 3: The client initiates the login process.

###
### User Login
###

# Step 1: The user sends an identifier and public ephemeral key, A
# The server responds with the salt and public ephemeral key, B
def handshake(request):
    import random, hashlib
    randomgen = random.SystemRandom()
    request.session["srp_I"] = request.POST["I"]
    A = int(request.POST["A"], 16)
    request.session["srp_A"] = request.POST["A"]
    g = 2
    N = 125617018995153554710546479714086468244499594888726646874671447258204721048803
    k = 88846390364205216646376352624313659232912717719075174937149043299744712465496
    if A % N == 0:
        return HttpResponse("<error>Invalid ephemeral key.</error>", mimetype="text/xml")
    else:
        try:
            user = models.User.objects.get(name=request.session["srp_I"])
            salt = user.salt
            v = int(user.verifier, 16)
        # We don't want to leak that the username doesn't exist. Make up a fake salt and verifier.
        except models.User.DoesNotExist:
            salt, x = generate_fake_salt(request.POST["I"])
            v = pow(g, x, N)
            
        request.session["srp_v"] = hex(v)[2:-1]

        # Ensure that B%N != 0
        while True:
            b = randomgen.getrandbits(32)
            B = k*v + pow(g,b,N)
            u =  int(hashlib.sha256("%s%s" % (hex(A)[2:-1],hex(B)[2:-1])).hexdigest(), 16)
            if B % N != 0 and u % N != 0: break

        response = "<r s='%s' B='%s' />" % (salt, hex(B)[2:-1])
        # Ideally, we could return this response and then calculate M concurrently with the user
        # Unfortunately, django isn't designed to do computations after responding.
        # Maybe someone will find a way.
        S = pow(A*pow(v,u,N), b, N)
        request.session["srp_S"] = hex(S)[2:-1]
        Mstr = "%s%s%s" % (hex(A)[2:-1],hex(B)[2:-1],hex(S)[2:-1])
        response = "<r s='%s' B='%s' />" % (salt, hex(B)[2:-1])
        request.session["srp_M"] = hashlib.sha256(Mstr).hexdigest()
    return HttpResponse(response, mimetype="text/xml")

# Step 2: The client sends its proof of S. The server confirms, and sends its proof of S.    
def verify(request):
    import hashlib
    from django.contrib import auth
    if request.POST["M"] == request.session["srp_M"]:
        # H(A, M, K)
        user = auth.authenticate(username=request.session["srp_I"], password=str(request.session["srp_v"]))
        if user != None:
            response = "<M>%s</M>" % hashlib.sha256("%s%s%s" % (request.session["srp_A"], request.session["srp_M"], request.session["srp_S"])).hexdigest()
            auth.login(request, user)
        else:
            # This should only happen when authentication is successful with SRP, but the user isn't in the auth table.
            response = "<error>Authentication failed. This is likely a server problem.</error>"
    else:
        response = "<error>Invalid username or password.</error>"
    try:
        del request.session["srp_I"]
        del request.session["srp_v"]
        del request.session["srp_M"]
        del request.session["srp_S"]
        del request.session["srp_A"]
    except KeyError:
        pass
    return HttpResponse(response, mimetype="text/xml")