summaryrefslogtreecommitdiff
path: root/doc/srp.rst
blob: 805793a57066326d4c571a5ddd7d667996b9b789 (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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
:mod:`srp` --- Secure Remote Password
=====================================

.. module:: srp
    :synopsis: Secure Remote Password
    
.. moduleauthor:: Tom Cocagne <tom.cocagne@gmail.com>

.. sectionauthor:: Tom Cocagne <tom.cocagne@gmail.com>


The Secure Remote Password Protocol (SRP) is a simple and secure
protocol for password-based, mutual authentication over an insecure
network connection. Successful SRP authentication can occur only if
both sides of the connection have knowledge of the user's
password. The client side of the connection must have the raw user's
password and the server side must have a verification key that is
derived from the user's password. An advantageous side-effect of
successful SRP authentication is that it results in a
cryptographically strong shared key that can be used to protect
network traffic via symmetric key encryption.

An advantage of SRP over other authentication protocols such as Kerberos and
SSL is that SRP does not require a trusted third party. Instead, SRP server
applications store small, salted verification keys that are derived from each
user's password. These keys are then used during the authentication process to
verify the correctness of the remote user's password.

A favorable aspect of the SRP protocol is that even if the
verification keys are compromized, they are of little value to a
potential attacker. Possesion of a verification key does not allow an
attacker to impersonate the user and cannot be used to obtain the
users password except by way of a computationally infeasible
dictionary attack. A compromized key would, however, allow an attacker
to impersonate the server side of an SRP authenticated
connection. Consequently, care should be taken to prevent unauthorized
access to verification keys for applications in which the client side
relies on the server being genuine.



Usage
-----

Use of SRP begins by using the *create_salted_verification_key()* function to
create a salted verification key from the user's password. The resulting salt
and key are then stored on the server and will be used during the
authentication process.

The authentication process occurs as an exchange of messages between the clent
and the server. The :ref:`example` below provides a simple demonstration of the
protocol. A comprehensive description of the SRP protocol is contained in the
:ref:`protocol-description` section.

The *User* & *Verifier* constructors, as well as the *create_salted_verification_key()*
function, accept optional arguments to specify which hashing algorithm and 
prime number arguments should be used during the authentication process. These
options may be used to tune the security/performance tradeoff for an application.
Generally speaking, specifying arguments with a higher number of bits will result
in a greater level of security. However, it will come at the cost of increased
computation time. The parameters passed to the *User* and *Verifier* constructors
must exactly match those passed to *create_salted_verification_key()*


.. _constants:

Constants
---------

.. table:: Hashing Algorithm Constants

  ==============  ==============
  Hash Algorithm  Number of Bits
  ==============  ==============
  SHA1            160
  SHA224          224
  SHA256          256
  SHA384          384
  SHA512          512
  ==============  ==============

.. note::

  Larger hashing algorithms will result in larger session keys.

.. table:: Prime Number Constants

  ================= ==============
  Prime Number Size Number of Bits
  ================= ==============
  NG_1024           1024
  NG_2048           2048
  NG_4096           4096
  NG_8192           8192
  NG_CUSTOM         User Supplied
  ================= ==============

.. note::

  If NG_CUSTOM is used, the 'n_hex' and 'g_hex' parameters are required.
  These parameters must be ASCII text containing hexidecimal notation of the
  prime number 'n_hex' and the corresponding generator number 'g_hex'. Appendix
  A of RFC 5054 contains several large prime number, generator pairs that may
  be used with NG_CUSTOM.

Functions
---------

.. function:: create_salted_verification_key ( username, password[, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None] )

    *username* Name of the user

    *password* Plaintext user password

    *hash_alg*, *ng_type*, *n_hex*, *g_hex* Refer to the :ref:`constants` section.

    Generate a salted verification key for the given username and password and return the tuple:
    (salt_bytes, verification_key_bytes)
    
    
:class:`Verifier` Objects
-------------------------

A :class:`Verifier` object is used to verify the identity of a remote
user.

.. note::

  The standard SRP 6 protocol allows only one password attempt per 
  connection.

.. class:: Verifier( username, bytes_s, bytes_v, bytes_A[, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None] )

  *username* Name of the remote user being authenticated.
  
  *bytes_s* Salt generated by :func:`create_salted_verification_key`.
  
  *bytes_v* Verification Key generated by :func:`create_salted_verification_key`.
  
  *bytes_A* Challenge from the remote user. Generated by
  :meth:`User.start_authentication`  

  *hash_alg*, *ng_type*, *n_hex*, *g_hex* Refer to the :ref:`constants` section.
  
  .. method:: Verifier.authenticated()
  
    Return True if the authentication succeeded. False
    otherwise.
    
  .. method:: Verifier.get_username()
  
    Return the name of the user this :class:`Verifier` object is for.
    
  .. method:: Verifier.get_session_key()
  
    Return the session key for an authenticated user or None if the
    authentication failed or has not yet completed.
    
  .. method:: Verifier.get_challenge()
  
    Return (bytes_s, bytes_B) on success or (None, None) if
    authentication has failed.
    
  .. method:: Verifier.verify_session( user_M )
  
    Complete the :class:`Verifier` side of the authentication
    process. If the authentication succeded the return result,
    bytes_H_AMK should be returned to the remote user. On failure,
    this method returns None.
    
    
:class:`User` Objects
-------------------------

A :class:`User` object is used to prove a user's identity to a remote :class:`Verifier` and
verifiy that the remote :class:`Verifier` knows the verification key associated with
the user's password.

.. class:: User( username, password[, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None] )

  *username* Name of the user being authenticated.
  
  *password* Password for the user.

  *hash_alg*, *ng_type*, *n_hex*, *g_hex* Refer to the :ref:`constants` section.
    
  .. method:: User.authenticated()
  
    Return True if authentication succeeded. False
    otherwise.
    
  .. method:: User.get_username()
  
    Return the username passed to the constructor.
    
  .. method:: User.get_session_key()
  
    Return the session key if authentication succeeded or None if the
    authentication failed or has not yet completed.
    
  .. method:: User.start_authentication()
  
    Return (username, bytes_A). These should be passed to the
    constructor of the remote :class:`Verifer`
    
  .. method:: User.process_challenge( bytes_s, bytes_B )
  
    Processe the challenge returned
    by :meth:`Verifier.get_challenge` on success this method
    returns bytes_M that should be sent
    to :meth:`Verifier.verify_session` if authentication failed,
    it returns None.
    
  .. method:: User.verify_session( bytes_H_AMK )
  
    Complete the :class:`User` side of the authentication
    process. If the authentication succeded :meth:`authenticated` will
    return True
    
.. _example:

Example
-------

Simple Usage Example::

    import srp
    
    # The salt and verifier returned from srp.create_salted_verification_key() should be
    # stored on the server.
    salt, vkey = srp.create_salted_verification_key( 'testuser', 'testpassword' )

    class AuthenticationFailed (Exception):
        pass
    
    # ~~~ Begin Authentication ~~~
    
    usr      = srp.User( 'testuser', 'testpassword' )
    uname, A = usr.start_authentication()
    
    # The authentication process can fail at each step from this
    # point on. To comply with the SRP protocol, the authentication
    # process should be aborted on the first failure.
    
    # Client => Server: username, A
    svr      = srp.Verifier( uname, salt, vkey, A )
    s,B      = svr.get_challenge()

    if s is None or B is None:
        raise AuthenticationFailed()
    
    # Server => Client: s, B
    M        = usr.process_challenge( s, B )

    if M is None:
        raise AuthenticationFailed()
    
    # Client => Server: M
    HAMK     = svr.verify_session( M )

    if HAMK is None:
        raise AuthenticationFailed()
        
    # Server => Client: HAMK
    usr.verify_session( HAMK )
    
    # At this point the authentication process is complete.
    
    assert usr.authenticated()
    assert svr.authenticated()



Implementation Notes
--------------------

This implementation of SRP consists of both a pure-python module and a C-based
implementation that is approximately 10x faster. By default, the
C-implementation will be used if it is available. An additional benefit of the C
implementation is that it can take advantage of of multiple CPUs. For cases in
which the number of connections per second is an issue, using a small pool of
threads to perform the authentication steps on multi-core systems will yield a
substantial performance increase.


.. _protocol-description:

SRP 6a Protocol Description
---------------------------

For the original, authoritative definition of SRP-6a please refer to
http://srp.stanford.edu. RFC 5054 also contains SRP related information and is
the source of the predefined N and g constants used in this implementation.

The following is a complete description of the SRP-6a protocol as implemented by
this library. Note that the ^ symbol indicates exponentiaion and the | symbol
indicates concatenation.

.. rubric:: Primary Variables used in SRP 6a

========= =================================================================
Variables Description
========= =================================================================
N         A large, safe prime (N = 2q+1, where q is a Sophie Germain prime)
          All arithmetic is performed in the field of integers modulo N
g         A generator modulo N
s         Small salt for the verification key 
I         Username
p         Cleartext password
H()       One-way hash function
a,b       Secret, random values
K         Session key
========= =================================================================
   

.. rubric:: Derived Values used in SRP 6a

======================================  ====================================
Derived Values                          Description
======================================  ====================================
k = H(N,g)                              Multiplier Parameter       
A = g^a                                 Public ephemeral value
B = kv + g^b                            Public ephemeral value
x = H(s, H( I | ':' | p ))              Private key (as defined by RFC 5054)
v = g^x                                 Password verifier
u = H(A,B)                              Random scrambling parameter
M = H(H(N) xor H(g), H(I), s, A, B, K)  Session key verifier
======================================  ====================================


.. rubric:: Protocol Description

The server stores the password verifier *v*. Authentication begins with a 
message from the client::

    client -> server: I, A = g^a
    
The server replies with the verifier salt and challenge::

    server -> client: s, B = kv + g^b

At this point, both the client and server calculate the shared session key::

     client & server: u = H(A,B)
     
::   

              server: K = H( (Av^u) ^ b )
              
::

              client: x = H( s, H( I + ':' + p ) )            
              client: K = H( (B - kg^x) ^ (a + ux) )

Now both parties have a shared, strong session key *K*. To complete 
authentication they need to prove to each other that their keys match::

    client -> server: M = H(H(N) xor H(g), H(I), s, A, B, K)
    server -> client: H(A, M, K)
    
SRP 6a requires the two parties to use the following safeguards:

1. The client will abort if it recieves B == 0 (mod N) or u == 0
2. The server will abort if it detects A == 0 (mod N)
3. The client must show its proof of K first. If the server detects that this
   proof is incorrect it must abort without showing its own proof of K