Skip to content

Commit

Permalink
Tighten authentication logic
Browse files Browse the repository at this point in the history
Use enumerations for specifying authentication support. This will
quickly throw a value error should we hit an unknown authentication
scheme instead of falling back on default behaviours that might not be
correct.

Always throw a key error if part of the authentication header is
missing, instead of defaulting to None or empty strings. This should
also help quickly catch errors if we send out invalid authentication
headers.
  • Loading branch information
vodik committed Oct 16, 2018
1 parent cd3c0e3 commit eacc4b4
Showing 1 changed file with 40 additions and 26 deletions.
66 changes: 40 additions & 26 deletions aiosip/auth.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
import logging

from hashlib import md5
from collections import MutableMapping
from enum import Enum
from hashlib import md5
import logging

from . import utils


LOG = logging.getLogger(__name__)


class Algorithm(Enum):
MD5 = 'md5'
MD5Sess = 'md5-sess'


class Directive(Enum):
Unspecified = ''
Auth = 'auth'
AuthInt = 'auth-int'


def md5digest(*args):
return md5(':'.join(args).encode()).hexdigest()

Expand Down Expand Up @@ -78,35 +89,38 @@ def __parse_digest(cls, header):
return params

def _calculate_response(self, password, payload, username=None, uri=None, cnonce=None, nonce_count=None):
if self.mode != 'Digest':
raise ValueError('Authentication method not supported')

algorithm = Algorithm(self.get('algorithm', 'md5').lower())
qop = Directive(self.get('qop', '').lower())

if username is None:
username = self['username']
if uri is None:
uri = self['uri']
if cnonce is None:
cnonce = self.get('cnonce')
if nonce_count is None:
nonce_count = self.get('nc')

if self.mode == 'Digest':
algorithm = self.get('algorithm', 'md5')
if algorithm == 'md5-sess':
ha1 = md5digest(md5digest(username, self['realm'], password), self['nonce'], cnonce)
else:
ha1 = md5digest(username, self['realm'], password)

qop = self.get('qop', '').lower()
if qop == 'auth-int':
ha2 = md5digest(self['method'], self['uri'], md5digest(payload))
else:
ha2 = md5digest(self['method'], uri)

if qop in ('auth', 'auth-int'):
response = md5digest(ha1, self['nonce'], nonce_count, cnonce, self['qop'], ha2)
else:
response = md5digest(ha1, self['nonce'], ha2)
return response
ha1 = md5digest(username, self['realm'], password)
if algorithm is Algorithm.MD5Sess:
ha1 = md5digest(ha1, self['nonce'], cnonce or self['cnonce'])

if qop is Directive.AuthInt:
ha2 = md5digest(self['method'], uri, md5digest(payload))
else:
raise ValueError('Authentication method not supported')
ha2 = md5digest(self['method'], uri)

# If there's no quality of prootection specified, we can return early,
# our computation is much simpler
if qop is Directive.Unspecified:
return md5digest(ha1, self['nonce'], ha2)

return md5digest(
ha1,
self['nonce'],
nonce_count or self['nc'],
cnonce or self['cnonce'],
self['qop'],
ha2)

# MutableMapping API
def __eq__(self, other):
Expand Down

0 comments on commit eacc4b4

Please sign in to comment.