File: yubikey-totp

package info (click to toggle)
python-yubico 1.1.0-2
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd, wheezy
  • size: 288 kB
  • ctags: 434
  • sloc: python: 1,893; ansic: 128; sh: 67; makefile: 4
file content (133 lines) | stat: -rwxr-xr-x 3,974 bytes parent folder | download
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())