auth2_oath: comletely remove this module, as it does not depend entirely on Entr'ouvert copyright

We will recreate it as an external plugin.
This commit is contained in:
Benjamin Dauvergne 2013-10-02 17:54:09 +02:00
parent b67842207b
commit 493c89eb6b
24 changed files with 0 additions and 727 deletions

View File

@ -663,7 +663,5 @@ if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.
External modules oath and totp-js modules are licensed under a BSD-like licence.
OpenID idp module is derived of the project django_openid_provider which is
distributed under the Apache 2.0 license.

View File

@ -1,60 +0,0 @@
import logging
from django.db import transaction
from django.conf import settings
from authentic2.compat import get_user_model
import authentic2.vendor.oath.hotp as hotp
from authentic2.nonce import accept_nonce
import models
logger = logging.getLogger('authentic.auth.auth2_oath')
NONCE_TIMEOUT = getattr(settings, 'OATH_NONCE_TIMEOUT',
getattr(settings, 'NONCE_TIMEOUT', 3600))
class OATHTOTPBackend:
supports_object_permissions = False
supports_anonymous_user = False
@transaction.commit_on_success()
def authenticate(self, username, oath_otp, format='dec6'):
'''Lookup the TOTP or HOTP secret for the user and try to authenticate
the proposed OTP using it.
'''
User = get_user_model()
try:
secret = models.OATHTOTPSecret.objects.get(user__username=username)
except models.OATHTOTPSecret.DoesNotExist:
return None
try:
accepted, drift = hotp.accept_totp(secret.key, oath_otp, format=format)
except Exception, e:
logger.exception('hotp.accept_totp raised', e)
raise
if accepted:
if not accept_nonce(oath_otp, 'OATH_OTP', NONCE_TIMEOUT):
logger.error('already used OTP %r', oath_otp)
return None
secret.drift = drift
return User.objects.get(username=username)
else:
return None
def get_user(self, user_id):
"""
simply return the user object. That way, we only need top look-up the
certificate once, when loggin in
"""
User = get_user_model()
try:
return User.objects.get(id=user_id)
except User.DoesNotExist:
return None
def setup_totp(self, user):
'''Create a model containing a TOTP secret for the given user and the
current time drift which initially is zero
'''
pass

View File

@ -1,64 +0,0 @@
from django.utils.translation import ugettext as _
from django.utils.translation import gettext_noop
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import login, authenticate
from django.http import HttpResponseRedirect
import django.forms as forms
import authentic2.auth2_auth.models as models
import views
# Only difference with login/password form is the user of 'otp' intead of
# password as an argument to the authenticate() method
# So you need an OTP enabled backend to this to work
class OATHOTPHAuthenticationForm(AuthenticationForm):
def clean(self):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
if username and password:
self.user_cache = authenticate(username=username, oath_otp=password)
if self.user_cache is None:
raise forms.ValidationError(_("Please enter a correct username and one-time password. Note that both fields are case-sensitive."))
elif not self.user_cache.is_active:
raise forms.ValidationError(_("This account is inactive."))
# TODO: determine whether this should move to its own method.
if self.request:
if not self.request.session.test_cookie_worked():
raise forms.ValidationError(_("Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in."))
return self.cleaned_data
class OATHOTPFrontend(object):
def enabled(self):
return True
def name(self):
return gettext_noop('One time password')
def id(self):
return 'oath-otp'
def form(self):
return OATHOTPHAuthenticationForm
def post(self, request, form, nonce, next):
# Login the user
login(request, form.get_user())
# Keep a trace
if 'HTTPS' in request.environ.get('HTTPS','').lower() == 'on':
how = 'oath-totp-on-https'
else:
how = 'oath-totp'
if nonce:
models.AuthenticationEvent(who=form.get_user().username, how=how,
nonce=nonce).save()
return HttpResponseRedirect(next)
def profile(self, request, next=''):
return views.totp_profile(request, next)
def template(self):
return 'auth/login_form_oath.html'

View File

@ -1,120 +0,0 @@
# French translation of Authentic
# Copyright (C) 2010, 2011 Entr'ouvert
# This file is distributed under the same license as the Authentic package.
# Frederic Peters <fpeters@entrouvert.com>, 2010.
#
msgid ""
msgstr ""
"Project-Id-Version: Authentic\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-07-23 17:44+0200\n"
"PO-Revision-Date: 2013-07-23 17:42+0200\n"
"Last-Translator: Mikaël Ates <mates@entrouvert.com>\n"
"Language-Team: None\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n>1;\n"
#: frontend.py:22
msgid ""
"Please enter a correct username and one-time password. Note that both fields "
"are case-sensitive."
msgstr ""
"Veuillez taper un nom d'utilisateur correct et un mot de passe à usage "
"unique. Notez que les deux champs sont sensibles à la casse."
#: frontend.py:24
msgid "This account is inactive."
msgstr "Ce compte est inactif."
#: frontend.py:29
msgid ""
"Your Web browser doesn't appear to have cookies enabled. Cookies are "
"required for logging in."
msgstr ""
"Il semblerait que votre navigateur ne supporte pas les cookies. Les cookies "
"sont requis pour se connecter."
#: frontend.py:39
msgid "One time password"
msgstr "Mot de passe à usage unique"
#: templates/auth/login_form_oath.html:5
msgid ""
"\n"
"Once you have created your account, log in with an other authentication "
"method.\n"
"Then, in account management, follow the instructions to deploy the\n"
"One Time password authentication method.\n"
msgstr ""
"\n"
"Une fois votre compte créé, connectez-vous à l'aide d'une autre méthode.\n"
"Alors, dans l'interface de gestion de votre compte, suivez les instructions "
"pour déployer la\n"
"méthode d'authentification basée sur le mot de passe à usage unique.\n"
#: templates/auth/login_form_oath.html:16
msgid "Log in"
msgstr "S'identifier"
#: templates/auth/login_form_oath.html:18
msgid "Cancel"
msgstr "Annuler"
#: templates/auth/login_form_oath.html:24
msgid "Forgot password?"
msgstr "Mot de passe oublié ?"
#: templates/auth/login_form_oath.html:24
msgid "Reset it!"
msgstr "Le réinitialiser !"
#: templates/auth/login_form_oath.html:25
msgid "Not a member?"
msgstr "Pas un membre ?"
#: templates/auth/login_form_oath.html:25
msgid "Register!"
msgstr "S'inscrire !"
#: templates/oath/totp_profile.html:5
msgid "Time based one-time password"
msgstr "Mot de passe à usage unique basé sur le temps"
#: templates/oath/totp_profile.html:8
msgid "Secret"
msgstr "Secret"
#: templates/oath/totp_profile.html:10
msgid "Google authenticator"
msgstr "Google authenticator"
#: templates/oath/totp_profile.html:12
msgid "Bookmarklet"
msgstr "Marque-page générateur de mot de passe"
#: templates/oath/totp_profile.html:13
msgid ""
"Copy this link to your bookmarks. When clicking on it it will generate a new "
"one-time password which will allow you to login"
msgstr ""
"Copier ce lien dans vos marque-pages. Cliquez le pour générer un nouveau mot "
"de passe à usage unique pour vous connecter."
#: templates/oath/totp_profile.html:16
msgid "Delete this credential"
msgstr "Supprimer ce moyen d'authentification"
#: templates/oath/totp_profile.html:19
msgid ""
"This kind of authentication is actually not possible, because you do not "
"have any secret setup."
msgstr ""
"Ce moyen d'authentification n'est pas disponible actuellement car vous "
"n'avez pas encore généré de secret."
#: templates/oath/totp_profile.html:23
msgid "Generate a new credential"
msgstr "Générer un nouveau secret"

View File

@ -1,39 +0,0 @@
# encoding: utf-8
from south.db import db
from south.v2 import SchemaMigration
from authentic.compat import user_model_label
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'OATHTOTPSecret'
db.create_table('auth2_oath_oathtotpsecret', (
('user', self.gf('django.db.models.fields.related.OneToOneField')(related_name='oath_totp_secret', unique=True, primary_key=True, to=orm[user_model_label])),
('key', self.gf('django.db.models.fields.CharField')(max_length=40)),
('drift', self.gf('django.db.models.fields.IntegerField')(default=0, max_length=4)),
))
db.send_create_signal('auth2_oath', ['OATHTOTPSecret'])
def backwards(self, orm):
# Deleting model 'OATHTOTPSecret'
db.delete_table('auth2_oath_oathtotpsecret')
models = {
user_model_label: {
'Meta': {'object_name': user_model_label.split('.')[-1]},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
},
'auth2_oath.oathtotpsecret': {
'Meta': {'object_name': 'OATHTOTPSecret'},
'drift': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '4'}),
'key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'oath_totp_secret'", 'unique': 'True', 'primary_key': 'True', 'to': "orm['%s']" % user_model_label})
},
}
complete_apps = ['auth2_oath']

View File

@ -1,9 +0,0 @@
from django.db import models
from django.conf import settings
class OATHTOTPSecret(models.Model):
user = models.OneToOneField(getattr(settings, 'AUTH_USER_MODEL', 'auth.User'),
primary_key= True, related_name='oath_totp_secret')
# 20 bytes string as hexadecimal
key = models.CharField(max_length=40)
drift = models.IntegerField(default=0,max_length=4)

View File

@ -1,28 +0,0 @@
{% load i18n %}
<div id="login-oath">
<p>
{% blocktrans %}
Once you have created your account, log in with an other authentication method.
Then, in account management, follow the instructions to deploy the
One Time password authentication method.
{% endblocktrans %}
</p>
<div>
<form method="post" action="">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" name="{{ submit_name }}" value="{% trans "Log in" %}"/>
{% if cancel %}
<input type="submit" name="cancel" value="{% trans 'Cancel' %}"/>
{% endif %}
</form>
</div>
<div class="login-actions">
<p>→ {% trans "Forgot password?" %} <a href="{% url 'auth_password_reset' %}">{% trans "Reset it!" %}</a></p>
<!--<p>→ {% trans "Not a member?" %} <a href="{% url 'registration_register' %}">{% trans "Register!" %}</a></p>-->
</div>
</div>

View File

@ -1,32 +0,0 @@
{% load i18n %}
<script src="{{ STATIC_URL }}jquery/js/jquery.js"></script>
<script src="{{ STATIC_URL }}jquery/js/qrcode.js"></script>
<script src="{{ STATIC_URL }}jquery/js/jquery.qrcode.js"></script>
<h4>{% trans "Time based one-time password" %}</h4>
<div>
{% if key %}
<p>{% trans "Secret" %}: {{ key }}</p>
{% if google_authenticator %}
<p>{% trans "Google authenticator" %}: <div class="google_authenticator">{{ google_authenticator|safe }}</div></p>
{% endif %}
<p><a href="{{ bookmarklet|safe }}">{% trans "Bookmarklet" %}</a>
<p>{% trans "Copy this link to your bookmarks. When clicking on it it will generate a new one-time password which will allow you to login" %}</p></p>
<p><form action="{{ base|safe }}/delete_totp_secret{{ next|safe }}" method="post">
{% csrf_token %}
<input type="submit" class="submit-link" value="{% trans "Delete this credential" %}">
</form></p>
{% else %}
<p>{% trans "This kind of authentication is actually not possible, because you do not have any secret setup." %}</p>
{% endif %}
<p><form action="{{ base|safe }}/new_totp_secret{{ next|safe }}" class="submit-link" method="post">
{% csrf_token %}
<input type="submit" value="{% trans "Generate a new credential" %}">
</form></p>
</div>
<script>
$('.google_authenticator').each(function (index, element) {
var content = $(element).text();
$(element).text("");
$(element).qrcode({ "render": "table", "width": 500, "height": 500, "text": content, 'correctLevel': QRErrorCorrectLevel.M, 'typeNumber': 5 });
});
</script>

View File

@ -1,6 +0,0 @@
from django.conf.urls import patterns
import views
urlpatterns = patterns('',
(r'^new_totp_secret$', views.new_totp_secret),
(r'^delete_totp_secret$', views.delete_totp_secret))

View File

@ -1,58 +0,0 @@
import urllib
import random
import base64
from django.http import HttpResponseBadRequest, HttpResponseRedirect
from django.template import RequestContext
from django.template.loader import render_to_string
import models
import authentic2.vendor.totp_js.totp_bookmarklet as totp_bookmarklet
_hexachars = '0123456789abcdef'
def new_totp_secret(request, next_url='/'):
if request.user is None or not hasattr(request.user, '_meta') \
or request.method != 'POST':
return HttpResponseBadRequest()
key = ''.join([random.choice(_hexachars) for x in range(40)])
secret, _ = models.OATHTOTPSecret.objects.get_or_create(user=request.user)
secret.key = key
secret.save()
next_url = request.REQUEST.get('next', next_url)
return HttpResponseRedirect(next_url)
def delete_totp_secret(request, next_url='/'):
if request.user is None or not hasattr(request.user, '_meta') \
or request.method != 'POST':
return HttpResponseBadRequest()
try:
models.OATHTOTPSecret.objects.filter(user=request.user).delete()
except models.OATHTOTPSecret.DoesNotExist:
pass
next_url = request.REQUEST.get('next', next_url)
return HttpResponseRedirect(next_url)
def totp_profile(request, next_url='', template_name='oath/totp_profile.html'):
if request.user is None or not hasattr(request.user, '_meta'):
return ''
if next_url:
next_url = '?next=%s' % urllib.quote(next_url)
google_authenticator, key, bookmarklet = '', '', ''
try:
secret = models.OATHTOTPSecret.objects.get(user=request.user)
key = secret.key
bookmarklet = totp_bookmarklet.otp_doc(secret.key)
google_authenticator = 'otpauth://totp/%(user)s@localhost?secret=%(b32_secret)s' % \
{ 'user': request.user.username,
'domain': request.get_host(),
'b32_secret': base64.b32encode(key.decode('hex')) }
except models.OATHTOTPSecret.DoesNotExist:
pass
return render_to_string(template_name,
{ 'key': key,
'bookmarklet': bookmarklet,
'google_authenticator': google_authenticator,
'next': next_url,
'base': '/oath'},
RequestContext(request))

View File

@ -260,9 +260,6 @@ def build_assertion(request, login, nid_format='transient', attributes=None):
elif backend == \
'authentic2.authsaml2.backends.AuthSAML2TransientBackend':
authn_context = lasso.SAML2_AUTHN_CONTEXT_UNSPECIFIED
elif backend == \
'authentic2.auth2_auth.auth2_oath.backend.OATHTOTPBackend':
authn_context = lasso.SAML2_AUTHN_CONTEXT_TIME_SYNC_TOKEN
else:
backend = load_backend(backend)
if hasattr(backend, 'get_saml2_authn_context'):

View File

@ -317,7 +317,6 @@ ADMIN_TOOLS_MENU = 'authentic2.menu.CustomMenu'
AUTH_SAML2 = 'AUTH_SAML2' in os.environ
AUTH_OPENID = 'AUTH_OPENID' in os.environ
AUTH_SSL = 'AUTH_SSL' in os.environ
AUTH_OATH = 'AUTH_OATH' in os.environ
IDP_SAML2 = 'IDP_SAML2' in os.environ
IDP_OPENID = 'IDP_OPENID' in os.environ
IDP_CAS = 'IDP_CAS' in os.environ
@ -354,11 +353,6 @@ if AUTH_SSL:
AUTH_FRONTENDS += ('authentic2.auth2_auth.auth2_ssl.frontend.SSLFrontend',)
INSTALLED_APPS += ('authentic2.auth2_auth.auth2_ssl',)
if AUTH_OATH:
INSTALLED_APPS += ('authentic2.auth2_auth.auth2_oath',)
AUTHENTICATION_BACKENDS += ('authentic2.auth2_auth.auth2_oath.backend.OATHTOTPBackend',)
AUTH_FRONTENDS += ('authentic2.auth2_auth.auth2_oath.frontend.OATHOTPFrontend',)
if IDP_SAML2:
IDP_BACKENDS += ('authentic2.idp.saml.backend.SamlBackend',)

View File

@ -41,10 +41,6 @@ if getattr(settings, 'IDP_OPENID', False):
urlpatterns += patterns('',
(r'^openid/', include('authentic2.idp.idp_openid.urls')))
if 'authentic2.auth2_auth.auth2_oath' in settings.INSTALLED_APPS:
urlpatterns += patterns('',
(r'^oath/', include('authentic2.auth2_auth.auth2_oath.urls')))
try:
if settings.DISCO_SERVICE:
urlpatterns += patterns('',

View File

@ -1,3 +0,0 @@
- implement accept_hotp
- add truncation functions for hashing algorithm with a larger output like SHA2
variant.

View File

View File

@ -1,133 +0,0 @@
import hashlib
import hmac
import binascii
import time
import datetime
import calendar
'''
Python implementation of HOTP and TOTP algorithms from the OATH project.
Copyright 2010, Benjamin Dauvergne
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.'''
def __truncated_value(h):
bytes = map(ord, h)
offset = bytes[19] & 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 = str(__truncated_value(h))
return v[len(v)-p:]
def __hotp(key, counter, hash=hashlib.sha1):
hex_counter = hex(long(counter))[2:-1]
hex_counter = '0' * (16 - len(hex_counter)) + hex_counter
bin_counter = binascii.unhexlify(hex_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
See http://tools.ietf.org/html/rfc4226
'''
bin_hotp = __hotp(key, counter, hash)
if format == 'hex40':
return binascii.hexlify(bin_hotp[0:5])
elif format == 'dec6':
return dec(bin_hotp, 6)
elif format == 'dec7':
return dec(bin_hotp, 7)
elif format == 'dec8':
return dec(bin_hotp, 8)
else:
raise ValueError('unknown format')
def totp(key, format='dec8', period=30, t=None, hash=hashlib.sha1):
'''Compute a TOTP value as prescribed by OATH specifications.
See http://tools.ietf.org/html/draft-mraihi-totp-timebased-06
'''
if t is None:
t = 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, period=30, format='dec8', hash=hashlib.sha1,
forward_drift=1, backward_drift=1, drift=0, t=None):
'''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.
Return 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 for
user with later call to accept_totp in order to accept a slowly
accumulating drift with a token clock.
'''
t = t or time.time()
for i in range(-backward_drift,forward_drift+1):
d = (drift+i) * period
if totp(key, format=format, period=period, hash=hash, t=t+d) == response:
return True, drift+i
return False, 0
if __name__ == '__main__':
# Test vectors extracted from RFC 4226
secret = '3132333435363738393031323334353637383930'
tvector = [
(0, 'cc93cf18508d94934c64b65d8ba7667fb7cde4b0'),
(1, '75a48a19d4cbe100644e8ac1397eea747a2d33ab'),
(2, '0bacb7fa082fef30782211938bc1c5e70416ff44'),
(3, '66c28227d03a2d5529262ff016a1e6ef76557ece'),
(4, 'a904c900a64b35909874b33e61c5938a8e15ed1c'),
(5, 'a37e783d7b7233c083d4f62926c7a25f238d0316'),
(6, 'bc9cd28561042c83f219324d3c607256c03272ae'),
(7, 'a4fb960c0bc06e1eabb804e5b397cdc4b45596fa'),
(8, '1b3c89f65e6c9e883012052823443f048b4332db'),
(9, '1637409809a679dc698207310c8c7fc07290d9e5'), ]
for counter, value in tvector:
assert(binascii.hexlify(__hotp(secret, counter)) == value)
tvector2 = [
(0, '4c93cf18', '1284755224', '755224',),
(1, '41397eea', '1094287082', '287082',),
(2, '82fef30', '137359152', '359152',),
(3, '66ef7655', '1726969429', '969429',),
(4, '61c5938a', '1640338314', '338314',),
(5, '33c083d4', '868254676', '254676',),
(6, '7256c032', '1918287922', '287922',),
(7, '4e5b397', '82162583', '162583',),
(8, '2823443f', '673399871', '399871',),
(9, '2679dc69', '645520489', '520489',),]
for counter, hexa, deci, trunc in tvector2:
h = __hotp(secret, counter)
v = __truncated_value(h)
assert(hex(v)[2:] == hexa)
assert(str(v) == deci)
assert(dec(h,6) == trunc)
secret = binascii.hexlify('12345678901234567890')
tvector3 = [
(59, hashlib.sha1, '94287082'),
(1111111109, hashlib.sha1, '07081804') ]
for timestamp, hash, value in tvector3:
assert (totp(secret,t=datetime.datetime.utcfromtimestamp(timestamp),hash=hash) == value)
assert(accept_totp(secret, '94287082', t=65) == (True, -1))
assert(accept_totp(secret, '94287082', t=65, drift=-1) == (True, -1))

View File

@ -1,8 +0,0 @@
Simple data document generator containing a TOTP soft token
===========================================================
To use it from your python application just do:
import totp_bookmarklet
html_fragment = '<a href="%s">OTP Bookmarklet</a>' % totp_bookmarklet.otp_doc('my_secret')

File diff suppressed because one or more lines are too long

View File

@ -1,110 +0,0 @@
/*
A simple Javascript HOTP implementation (HMAC-Based One-Time Password Algorithm) as described in RFC 4226.
The library is relying on crypto-js (http://code.google.com/p/crypto-js/) for the javascript HMAC-SHA1 implementation.
The library can be used to create software token (don't forget to protect the key of the token...).
If you want to use the library, you'll need to load the crypto-js (sha1 and hmac) and hotp.js.
Calling the library is easy, you just have to set the hex key of the token, the counter plus the output format.
otp = hotp("3132333435363738393031323334353637383930","4","dec6");
Current output formats are : hex40 (format used by ootp, a free software library) and dec6 (the 6 decimal digit as described in the RFC 4226).
A demo page with the test vector of the RFC 4226 : http://www.foo.be/hotp/example.html*
http://www.gitorious.org/hotp-js/
Copyright (C) 2009 Alexandre Dulaunoy
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
function hotp(key, counter, format) {
function hotp_hexkeytobytestream(s) {
// s is the key to be converted in bytes
var b = new Array();
var last = s.length;
for (var i = 0; i < last; i = i + 2) {
var x = s[i] + s[i + 1];
x.toUpperCase();
x = "0x" + x;
x = parseInt(x);
b[i] = String.fromCharCode(x);
}
var ret = new String();
ret = b.join('');
return ret;
}
function hotp_movingfactortohex(count) {
// count is the moving factor in OTP to be converted in bytes
v = decimaltohex(count, 16);
var decb = new Array();
lhex = Crypto.util.hexToBytes(v);
for (var i = 0; i < lhex.length; i++) {
decb[i] = String.fromCharCode(lhex[i]);
}
var retval = new String();
retval = decb.join('');
return retval;
}
function decimaltohex(d, padding) {
// d is the decimal value
// padding is the padding to apply (O pad)
var hex = Number(d).toString(16);
padding = typeof(padding) === "undefined" || padding === null ? padding = 2 : padding;
while (hex.length < padding) {
hex = "0" + hex;
}
return hex;
}
function truncatedvalue(h, p) {
// h is the hash value
// p is precision
offset = h[19] & 0xf;
v = (h[offset] & 0x7f) << 24 | (h[offset + 1] & 0xff) << 16 | (h[offset + 2] & 0xff) << 8 | (h[offset + 3] & 0xff);
v = "" + v;
v = v.substr(v.length - p, p);
return v;
}
var hmacBytes = Crypto.HMAC(Crypto.SHA1, Crypto.charenc.Binary.stringToBytes((hotp_movingfactortohex(counter))), Crypto.charenc.Binary.stringToBytes(hotp_hexkeytobytestream(key)));
if (format == "hex40") {
return hmacBytes.substring(0, 10);
} else if (format == "dec6") {
return truncatedvalue(Crypto.util.hexToBytes(hmacBytes), 6);
} else if (format == "dec7") {
return truncatedvalue(Crypto.util.hexToBytes(hmacBytes), 7);
} else if (format == "dec8") {
return truncatedvalue(Crypto.util.hexToBytes(hmacBytes), 8);
}
else {
return "unknown format";
}
}
function totp(key,format) {
var T = parseInt(new Date().getTime()/30000);
return hotp(key,T,format);
}

View File

@ -1,5 +0,0 @@
function otp() {
var key = 'FAFA';
alert('Code: ' + totp(key, 'MODE'));
};
otp();

View File

@ -1,30 +0,0 @@
import binascii
import base64
import os.path
def __content(f):
return open(os.path.join(os.path.dirname(__file__), f)).read()
crypto_js = __content('js/crypto.js')
hotp_js = __content('js/hotp.js')
myotp_js = __content('js/my-otp.js')
def dataize(document, type='text/html'):
return 'data:%s;base64,%s' % (type, base64.b64encode(document))
def otp_doc(key,mode='dec6'):
'''Convert an hexadecimal key to a document able to produce TOTP keys using
the dec6 mode
'''
doc = ''''<html>
<body>
<script type="text/javascript">%s;history.back()</script>
</body>
</html>''' % (crypto_js + ';' + hotp_js + ';' + \
myotp_js.replace('FAFA',key).replace('MODE',mode))
return dataize(doc)
if __name__ == '__main__':
import sys
print '''<html><body><a href="%s" title="Drag me to your bookmark">OTP Password</a></body></html>''' % otp_doc(sys.argv[1])