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
|
#!/usr/bin/env python
#
# Copyright (c) 2011, Yubico AB
# See the file COPYING for licence statement.
#
"""
This program lets you use the HMAC-SHA-1 in your YubiKey to produce
OATH TOTP (RFC 6238) codes.
To verify the output of this program, first program a YubiKey with the
RFC 6238 test key "12345678901234567890" (ASCII) :
$ ykpersonalize -2 -ochal-resp -ochal-hmac -ohmac-lt64 \
-o serial-api-visible \
-a 3132333435363738393031323334353637383930
and then examine the OATH codes for the test values (Time) in Appendix B
of RFC 6238 (SHA1) :
Time SHA1
59 -> 94287082
1111111109 -> 07081804
1234567890 -> 89005924
20000000000 -> 65353130
Like this :
$ yubikey-totp --step 30 --digits 8 --time 59
94287082
$
"""
import sys
sys.path.append('Lib')
import time
import struct
import yubico
import argparse
default_slot=2
default_time=int(time.mktime(time.gmtime()))
default_step=30
default_digits=6
def parse_args():
"""
Parse the command line arguments
"""
parser = argparse.ArgumentParser(description = "Generate OATH TOTP codes using a YubiKey",
add_help = True,
formatter_class = argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument('-v', '--verbose',
dest='verbose',
action='store_true', default=False,
help='Enable verbose operation'
)
parser.add_argument('--debug',
dest='debug',
action='store_true', default=False,
help='Enable debug operation'
)
parser.add_argument('--time',
dest='time',
type=int, default=default_time,
required=False,
help='Time to use as number of seconds since epoch',
)
parser.add_argument('--step',
dest='step',
type=int, default=default_step,
required=False,
help='Time step in use (in seconds)',
)
parser.add_argument('--digits',
dest='digits',
type=int, default=default_digits,
required=False,
help='Length of OTP in decimal digits',
)
parser.add_argument('--slot',
dest='slot',
type=int, default=default_slot,
required=False,
help='YubiKey slot configured for Challenge-Response',
)
args = parser.parse_args()
return args
def make_totp(args):
"""
Create an OATH TOTP OTP and return it as a string (to disambiguate leading zeros).
"""
YK = yubico.find_yubikey(debug=args.debug)
if args.debug or args.verbose:
print "Version : %s " % YK.version()
if args.debug:
print "Serial : %i" % YK.serial()
print ""
# Do challenge-response
secret = struct.pack("> Q", args.time / args.step).ljust(64, chr(0x0))
if args.debug:
print "Sending challenge : %s\n" % (secret.encode('hex'))
response = YK.challenge_response(secret, slot=args.slot)
# format with appropriate number of leading zeros
fmt = "%." + str(args.digits) + "i"
totp_str = fmt % (yubico.yubico_util.hotp_truncate(response, length=args.digits))
return totp_str
def main():
""" Main program. """
args = parse_args()
otp = None
try:
otp = make_totp(args)
except yubico.yubico_exception.YubicoError, e:
print "ERROR: %s" % (e.reason)
return 1
if not otp:
return 1
print otp
return 0
if __name__ == '__main__':
sys.exit(main())
|