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 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
|
: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 cryptographically
strong authentication protocol for password-based, mutual
authentication over an insecure network connection. Successful SRP
authentication requires both sides of the connection to have knowledge
of the user's password. In addition to password verification, the SRP
protocol also performs a secure key exchange during the authentication
process. This key may be used to protect network traffic via symmetric
key encryption.
SRP offers security and deployment advantages over other
challenge-response protocols, such as Kerberos and SSL, in that it
does not require trusted key servers or certificate infrastructures.
Instead, small verification keys derived from each user's password are
stored and used by each SRP server application. SRP provides a
near-ideal solution for many applications requiring simple and secure
password authentication that does not rely on an external
infrastructure.
Another favorable aspect of the SRP protocol is that compromized
verification keys are of little value to an attacker. Possesion of a
verification key does not allow a user to be impersonated
and it 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
-----
SRP usage begins with *create_salted_verification_key()*. This function
creates a salted verification key from the user's password. The resulting salt
and key are stored by the server application 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 default values of SHA1
hashes and 2048 bit prime numbers strike a good balance between
performance and security. These values should be sufficient for most
applications. Regardless of which values are used, 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)
.. function:: rfc5054_enable( enable=True )
*enable* True if compatibility with RFC5054 is required, False otherwise.
For backward compatibility, pysrp by default does not conform to RFC5054. If you need compatibility
with RFC5054, just call this function before using pysrp.
: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=None, 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`. Useful when user generates A
before verifier generates B. If following RFC5054 section 2.2 where
B is generated prior to A, then A can instead later be provided to
Verifier via :func:`Verifier.verify_session`.
*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[, user_A=None] )
Complete the :class:`Verifier` side of the authentication process.
If A was generated after B as in RFC5054 Section 2.2, then bytes_A
can be provided here. If the authentication succeeded, 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. By
verifying the *bytes_H_AMK* value returned by
:meth:`Verifier.verify_session`. 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
---------------------------
The original SRP protocol, known as SRP-3, is defined in
RFC 2945. This implementation, however, uses SRP-6a which is a slight
improvement over SRP-3. The authoritative definition for the SRP-6a
protocol is available at http://srp.stanford.edu. An additional
resource is RFC 5054 which covers the integration of SRP into
TLS. This RFC is the source of hashing strategy and 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]
where public ephemeral key *A* may be provided at this point or later
as part of verification. The server replies with the verifier salt and
challenge::
server -> client: s, B = kv + g^b, [N, g]
where *N* and *g* may be provided by the server or hardcoded in the
user client. 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), [A = g^a]
server -> client: H(A, M, K)
Note that *A* is only provided here if it was not provided during the initial authentication request.
SRP 6a requires the two parties to use the following safeguards (all of which this library automatically checks):
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
Additionally, if the server provides N and g to the user, the user
should verify the safe prime bit length is as expected, that its
highest bit is 1 to ensure it is large, and that it is indeed a safe
prime with the expected generator. These N and g checks are not
currently provided by this library.
|