Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
sso
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
8
Issues
8
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
Operations
Operations
Incidents
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
ai
sso
Commits
cbe2f151
Commit
cbe2f151
authored
Mar 29, 2014
by
ale
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
bundle a subset of github.com/bdauvergne/python-oath
parent
26f169cd
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
293 additions
and
0 deletions
+293
-0
src/sso_server/sso_server/oath/__init__.py
src/sso_server/sso_server/oath/__init__.py
+4
-0
src/sso_server/sso_server/oath/_hotp.py
src/sso_server/sso_server/oath/_hotp.py
+160
-0
src/sso_server/sso_server/oath/_totp.py
src/sso_server/sso_server/oath/_totp.py
+129
-0
No files found.
src/sso_server/sso_server/oath/__init__.py
0 → 100644
View file @
cbe2f151
from
_totp
import
*
from
_hotp
import
*
__version__
=
VERSION
=
'1.2~ai-sso'
src/sso_server/sso_server/oath/_hotp.py
0 → 100644
View file @
cbe2f151
import
hashlib
import
hmac
import
binascii
'''
HOTP implementation
To compute an HOTP one-time-password:
>>> hotp(key, counter)
where is the hotp is a key given as an hexadecimal string and counter is an
integer. The counter value must be kept synchronized on the server and the
client side.
See also http://tools.ietf.org/html/rfc4226
'''
__all__
=
(
'hotp'
,
'accept_hotp'
)
def
truncated_value
(
h
):
bytes
=
map
(
ord
,
h
)
offset
=
bytes
[
-
1
]
&
0xf
v
=
(
bytes
[
offset
]
&
0x7f
)
<<
24
|
(
bytes
[
offset
+
1
]
&
0xff
)
<<
16
|
\
(
bytes
[
offset
+
2
]
&
0xff
)
<<
8
|
(
bytes
[
offset
+
3
]
&
0xff
)
return
v
def
dec
(
h
,
p
):
v
=
truncated_value
(
h
)
v
=
v
%
(
10
**
p
)
return
'%0*d'
%
(
p
,
v
)
def
int2beint64
(
i
):
hex_counter
=
hex
(
long
(
i
))[
2
:
-
1
]
hex_counter
=
'0'
*
(
16
-
len
(
hex_counter
))
+
hex_counter
bin_counter
=
binascii
.
unhexlify
(
hex_counter
)
return
bin_counter
def
__hotp
(
key
,
counter
,
hash
=
hashlib
.
sha1
):
bin_counter
=
int2beint64
(
counter
)
bin_key
=
binascii
.
unhexlify
(
key
)
return
hmac
.
new
(
bin_key
,
bin_counter
,
hash
).
digest
()
def
hotp
(
key
,
counter
,
format
=
'dec6'
,
hash
=
hashlib
.
sha1
):
'''
Compute a HOTP value as prescribed by RFC4226
:param key:
the HOTP secret key given as an hexadecimal string
:param counter:
the OTP generation counter
:param format:
the output format, can be:
- hex, for a variable length hexadecimal format,
- hex-notrunc, for a 40 characters hexadecimal non-truncated format,
- dec4, for a 4 characters decimal format,
- dec6,
- dec7, or
- dec8
it defaults to dec6.
:param hash:
the hash module (usually from the hashlib package) to use,
it defaults to hashlib.sha1.
:returns:
a string representation of the OTP value (as instructed by the format parameter).
Examples:
>>> hotp('343434', 2, format='dec6')
'791903'
'''
bin_hotp
=
__hotp
(
key
,
counter
,
hash
)
if
format
==
'dec4'
:
return
dec
(
bin_hotp
,
4
)
elif
format
==
'dec6'
:
return
dec
(
bin_hotp
,
6
)
elif
format
==
'dec7'
:
return
dec
(
bin_hotp
,
7
)
elif
format
==
'dec8'
:
return
dec
(
bin_hotp
,
8
)
elif
format
==
'hex'
:
return
hex
(
truncated_value
(
bin_hotp
))[
2
:]
elif
format
==
'hex-notrunc'
:
return
binascii
.
hexlify
(
bin_hotp
)
elif
format
==
'bin'
:
return
bin_hotp
elif
format
==
'dec'
:
return
str
(
truncated_value
(
bin_hotp
))
else
:
raise
ValueError
(
'unknown format'
)
def
accept_hotp
(
key
,
response
,
counter
,
format
=
'dec6'
,
hash
=
hashlib
.
sha1
,
drift
=
3
,
backward_drift
=
0
):
'''
Validate a HOTP value inside a window of
[counter-backward_drift:counter+forward_drift]
:param key:
the shared secret
:type key:
hexadecimal string of even length
:param response:
the OTP to check
:type response:
ASCII string
:param counter:
value of the counter running inside an HOTP token, usually it is
just the count of HOTP value accepted so far for a given shared
secret; see the specifications of HOTP for more details;
:param format:
the output format, can be:
- hex40, for a 40 characters hexadecimal format,
- dec4, for a 4 characters decimal format,
- dec6,
- dec7, or
- dec8
it defaults to dec6.
:param hash:
the hash module (usually from the hashlib package) to use,
it defaults to hashlib.sha1.
:param drift:
how far we can look forward from the current value of the counter
:param backward_drift:
how far we can look backward from the current counter value to
match the response, default to zero as it is usually a bad idea to
look backward as the counter is only advanced when a valid value is
checked (and so the counter on the token side should have been
incremented too)
:returns:
a pair of a boolean and an integer:
- first is True if the response is validated and False otherwise,
- second is the new value for the counter; it can be more than
counter + 1 if the drift window was used; you must store it if
the response was validated.
>>> accept_hotp('343434', '122323', 2, format='dec6')
(False, 2)
>>> hotp('343434', 2, format='dec6')
'791903'
>>> accept_hotp('343434', '791903', 2, format='dec6')
(True, 3)
>>> hotp('343434', 3, format='dec6')
'907279'
>>> accept_hotp('343434', '907279', 2, format='dec6')
(True, 4)
'''
for
i
in
range
(
-
backward_drift
,
drift
+
1
):
if
hotp
(
key
,
counter
+
i
,
format
=
format
,
hash
=
hash
)
==
str
(
response
):
return
True
,
counter
+
i
+
1
return
False
,
counter
src/sso_server/sso_server/oath/_totp.py
0 → 100644
View file @
cbe2f151
import
time
import
hashlib
import
datetime
import
calendar
'''
:mod:`totp` -- RFC6238 - OATH TOTP implementation
=================================================
.. module:: parrot
:platform: any
:synosis: implement a time indexed one-time password algorithm based on a HMAC crypto function as specified in RFC6238
.. moduleauthor:: Benjamin Dauvergne <benjamin.dauvergne@gmail.com>
'''
from
._hotp
import
hotp
__all__
=
(
'totp'
,
'accept_totp'
)
def
totp
(
key
,
format
=
'dec6'
,
period
=
30
,
t
=
None
,
hash
=
hashlib
.
sha1
):
'''
Compute a TOTP value as prescribed by OATH specifications.
:param key:
the TOTP key given as an hexadecimal string
:param format:
the output format, can be:
- hex40, for a 40 characters hexadecimal format,
- dec4, for a 4 characters decimal format,
- dec6,
- dec7, or
- dec8
it default to dec6.
:param period:
a positive integer giving the period between changes of the OTP
value, as seconds, it defaults to 30.
:param t:
a positive integer giving the current time as seconds since EPOCH
(1st January 1970 at 00:00 GMT), if None we use time.time(); it
defaults to None;
:param hash:
the hash module (usually from the hashlib package) to use,
it defaults to hashlib.sha1.
:returns:
a string representation of the OTP value (as instructed by the format parameter).
:type: str
'''
if
t
is
None
:
t
=
int
(
time
.
time
())
else
:
if
isinstance
(
t
,
datetime
.
datetime
):
t
=
calendar
.
timegm
(
t
.
utctimetuple
())
else
:
t
=
int
(
t
)
T
=
int
(
t
/
period
)
return
hotp
(
key
,
T
,
format
=
format
,
hash
=
hash
)
def
accept_totp
(
key
,
response
,
format
=
'dec6'
,
period
=
30
,
t
=
None
,
hash
=
hashlib
.
sha1
,
forward_drift
=
1
,
backward_drift
=
1
,
drift
=
0
):
'''
Validate a TOTP value inside a window of
[drift-bacward_drift:drift+forward_drift] of time steps.
Where drift is the drift obtained during the last call to accept_totp.
:param response:
a string representing the OTP to check, its format should correspond
to the format parameter (it's not mandatory, it is part of the
checks),
:param key:
the TOTP key given as an hexadecimal string
:param format:
the output format, can be:
- hex40, for a 40 characters hexadecimal format,
- dec4, for a 4 characters decimal format,
- dec6,
- dec7, or
- dec8
it default to dec6.
:param period:
a positive integer giving the period between changes of the OTP
value, as seconds, it defaults to 30.
:param t:
a positive integer giving the current time as seconds since EPOCH
(1st January 1970 at 00:00 GMT), if None we use time.time(); it
defaults to None;
:param hash:
the hash module (usually from the hashlib package) to use,
it defaults to hashlib.sha1.
:param forward_drift:
how much we accept the client clock to advance, as a number of
periods, i.e. if the period is 30 seconds, a forward_drift of 2,
allows at most a clock a drift of 90 seconds;
Schema:
.___ Current time
|
0 v + 30s +60s +90s
[ current_period | period+1 | period+2 [
it defaults to 1.
:param backward_drift:
how much we accept the client clock to backstep; it defaults to 1.
:param drift:
an absolute drift of the local clock to the client clock; use it to
keep track of an augmenting drift with a client without augmenting
the size of the window given by forward_drift and backward_dript; it
defaults to 0, you should usually give as value the last value
returned by accept_totp for this client (read further).
:returns:
a pair (v,d) where v is a boolean giving the result, and d the
needed drift to validate the value. The drift value should be saved
relative to the current client. This saved value SHOULD be used in
later calls to accept_totp in order to accept a slowly accumulating
drift in the client token clock; on the server side you should use
reliable source of time like an NTP server.
:rtype: a two element tuple
'''
if
t
is
None
:
t
=
int
(
time
.
time
())
for
i
in
range
(
max
(
-
divmod
(
t
,
period
)[
0
],
-
backward_drift
),
forward_drift
+
1
):
d
=
(
drift
+
i
)
*
period
if
totp
(
key
,
format
=
format
,
period
=
period
,
hash
=
hash
,
t
=
t
+
d
)
==
str
(
response
):
return
True
,
drift
+
i
return
False
,
0
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment