Import Upstream version 1.3.3
This commit is contained in:
37
examples/configure_neo_ndef
Executable file
37
examples/configure_neo_ndef
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Set up a YubiKey NEO NDEF
|
||||
"""
|
||||
|
||||
import sys
|
||||
import struct
|
||||
|
||||
import six
|
||||
|
||||
import yubico
|
||||
import yubico.yubikey_neo_usb_hid
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
sys.stderr.write("Syntax: %s URL\n" % (sys.argv[0]))
|
||||
sys.exit(1)
|
||||
|
||||
url = sys.argv[1]
|
||||
|
||||
if sys.version_info >= (3, 0):
|
||||
url = url.encode('utf-8')
|
||||
|
||||
try:
|
||||
YK = yubico.yubikey_neo_usb_hid.YubiKeyNEO_USBHID(debug=True)
|
||||
print("Version : %s " % YK.version())
|
||||
|
||||
ndef = yubico.yubikey_neo_usb_hid.YubiKeyNEO_NDEF(data = url)
|
||||
|
||||
user_input = six.moves.input('Write configuration to YubiKey? [y/N] : ')
|
||||
if user_input in ('y', 'ye', 'yes'):
|
||||
YK.write_ndef(ndef)
|
||||
print("\nSuccess!")
|
||||
else:
|
||||
print("\nAborted")
|
||||
except yubico.yubico_exception.YubicoError as inst:
|
||||
print("ERROR: %s" % inst.reason)
|
||||
sys.exit(1)
|
||||
31
examples/configure_nist_test_key
Executable file
31
examples/configure_nist_test_key
Executable file
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Set up a YubiKey with a NIST PUB 198 A.2
|
||||
20 bytes test vector in Slot 2 (variable input)
|
||||
"""
|
||||
|
||||
import sys
|
||||
import struct
|
||||
import yubico
|
||||
import six
|
||||
|
||||
slot=2
|
||||
|
||||
try:
|
||||
YK = yubico.find_yubikey(debug=True)
|
||||
print("Version : %s " % YK.version())
|
||||
|
||||
Cfg = YK.init_config()
|
||||
key = b'h:303132333435363738393a3b3c3d3e3f40414243'
|
||||
Cfg.mode_challenge_response(key, type='HMAC', variable=True)
|
||||
Cfg.extended_flag('SERIAL_API_VISIBLE', True)
|
||||
|
||||
user_input = six.moves.input('Write configuration to slot %i of YubiKey? [y/N] : ' % slot )
|
||||
if user_input in ('y', 'ye', 'yes'):
|
||||
YK.write_config(Cfg, slot=slot)
|
||||
print("\nSuccess!")
|
||||
else:
|
||||
print("\nAborted")
|
||||
except yubico.yubico_exception.YubicoError as inst:
|
||||
print("ERROR: %s" % inst.reason)
|
||||
sys.exit(1)
|
||||
46
examples/nist_challenge_response
Executable file
46
examples/nist_challenge_response
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Test challenge-response, assumes a NIST PUB 198 A.2
|
||||
20 bytes test vector in Slot 2 (variable input)
|
||||
"""
|
||||
|
||||
import sys
|
||||
import yubico
|
||||
|
||||
expected = \
|
||||
b'\x09\x22\xd3\x40\x5f\xaa\x3d\x19\x4f\x82' + \
|
||||
b'\xa4\x58\x30\x73\x7d\x5c\xc6\xc7\x5d\x24'
|
||||
|
||||
# turn on YubiKey debug if -v is given as an argument
|
||||
debug = False
|
||||
if len(sys.argv) > 1:
|
||||
debug = (sys.argv[1] == '-v')
|
||||
|
||||
# Look for and initialize the YubiKey
|
||||
try:
|
||||
YK = yubico.find_yubikey(debug=debug)
|
||||
print("Version : %s " % YK.version())
|
||||
print("Serial : %i" % YK.serial())
|
||||
print("")
|
||||
|
||||
# Do challenge-response
|
||||
secret = b'Sample #2'.ljust(64, b'\0')
|
||||
print("Sending challenge : %s\n" % repr(secret))
|
||||
|
||||
response = YK.challenge_response(secret, slot=2)
|
||||
except yubico.yubico_exception.YubicoError as inst:
|
||||
print("ERROR: %s" % inst.reason)
|
||||
sys.exit(1)
|
||||
|
||||
print("Response :\n%s\n" % yubico.yubico_util.hexdump(response))
|
||||
|
||||
# Workaround for http://bugs.python.org/issue24596
|
||||
del YK
|
||||
|
||||
# Check if the response matched the expected one
|
||||
if response == expected:
|
||||
print("OK! Response matches the NIST PUB 198 A.2 expected response.")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("ERROR! Response does NOT match the NIST PUB 198 A.2 expected response.")
|
||||
sys.exit(1)
|
||||
247
examples/rolling_challenge_response
Executable file
247
examples/rolling_challenge_response
Executable file
@@ -0,0 +1,247 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (c) 2011, Yubico AB
|
||||
# See the file COPYING for licence statement.
|
||||
#
|
||||
"""
|
||||
Demonstrate rolling challenges.
|
||||
|
||||
This is a scheme for generating "one time" HMAC-SHA1 challenges, which
|
||||
works by being able to access the HMAC-SHA1 key on the host computer every
|
||||
time the correct response is provided.
|
||||
|
||||
GPGME would've been used to encrypt the HMAC-SHA1 with the next expected
|
||||
response, but none of the two Python bindings to gpgme I have available
|
||||
at the moment supports symmetric encryption, so for demo purposes AES CBC
|
||||
is used instead.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import hmac
|
||||
import argparse
|
||||
import hashlib
|
||||
import binascii
|
||||
|
||||
import yubico
|
||||
import six
|
||||
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
def parse_args():
|
||||
"""
|
||||
Parse the command line arguments
|
||||
"""
|
||||
parser = argparse.ArgumentParser(description = "Demonstrate rolling challenges",
|
||||
add_help=True
|
||||
)
|
||||
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('--init',
|
||||
dest='init',
|
||||
action='store_true', default=False,
|
||||
help='Initialize demo.'
|
||||
)
|
||||
parser.add_argument('-F', '--filename',
|
||||
dest='filename',
|
||||
required=True,
|
||||
help='State filename.'
|
||||
)
|
||||
parser.add_argument('--challenge-length',
|
||||
dest='challenge_length',
|
||||
type = int, default = 32,
|
||||
help='Length of challenges generated, in bytes.'
|
||||
)
|
||||
parser.add_argument('--slot',
|
||||
dest='slot',
|
||||
type = int, default = 2,
|
||||
help='YubiKey slot to send challenge to.'
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
def init_demo(args):
|
||||
""" Initializes the demo by asking a few questions and creating a new stat file. """
|
||||
hmac_key = six.moves.input("Enter HMAC-SHA1 key as 40 chars of hex (or press enter for random key) : ")
|
||||
if hmac_key:
|
||||
try:
|
||||
hmac_key = binascii.unhexlify(hmac_key)
|
||||
except:
|
||||
sys.stderr.write("Could not decode HMAC-SHA1 key. Please enter 40 hex-chars.\n")
|
||||
sys.exit(1)
|
||||
else:
|
||||
hmac_key = os.urandom(20)
|
||||
if len(hmac_key) != 20:
|
||||
sys.stderr.write("Decoded HMAC-SHA1 key is %i bytes, expected 20.\n" %( len(hmac_key)))
|
||||
sys.exit(1)
|
||||
|
||||
print("To program a YubiKey >= 2.2 for challenge-response with this key, use :")
|
||||
print("")
|
||||
print(" $ ykpersonalize -%i -ochal-resp -ochal-hmac -ohmac-lt64 -a %s" % (args.slot, binascii.hexlify(hmac_key).decode('ascii')))
|
||||
print("")
|
||||
|
||||
passphrase = six.moves.input("Enter the secret passphrase to protect with the rolling challenges : ")
|
||||
|
||||
secret_dict = {"count": 0,
|
||||
"passphrase": passphrase,
|
||||
}
|
||||
roll_next_challenge(args, hmac_key, secret_dict)
|
||||
|
||||
def do_challenge(args):
|
||||
""" Send a challenge to the YubiKey and use the result to decrypt the state file. """
|
||||
outer_j = load_state_file(args)
|
||||
challenge = outer_j["challenge"]
|
||||
print("Challenge : %s" % (challenge))
|
||||
response = get_yubikey_response(args, binascii.unhexlify(outer_j["challenge"]))
|
||||
if args.debug or args.verbose:
|
||||
print("\nGot %i bytes response %s\n" % (len(response), binascii.hexlify(response)))
|
||||
else:
|
||||
print("Response : %s" % binascii.hexlify(response))
|
||||
inner_j = decrypt_with_response(args, outer_j["inner"], response)
|
||||
if args.verbose or args.debug:
|
||||
print("\nDecrypted 'inner' :\n%s\n" % (inner_j))
|
||||
|
||||
secret_dict = {}
|
||||
try:
|
||||
secret_dict = json.loads(inner_j.decode('ascii'))
|
||||
except ValueError:
|
||||
sys.stderr.write("\nCould not parse decoded data as JSON, you probably did not produce the right response.\n")
|
||||
sys.exit(1)
|
||||
|
||||
secret_dict["count"] += 1
|
||||
|
||||
print("\nThe passphrase protected using rolling challenges is :\n")
|
||||
print("\t%s\n\nAccessed %i times.\n" % (secret_dict["passphrase"], secret_dict["count"]))
|
||||
roll_next_challenge(args, binascii.unhexlify(secret_dict["hmac_key"]), secret_dict)
|
||||
|
||||
def get_yubikey_response(args, challenge):
|
||||
"""
|
||||
Do challenge-response with the YubiKey if one is found. Otherwise prompt user to fake a response. """
|
||||
try:
|
||||
YK = yubico.find_yubikey(debug = args.debug)
|
||||
response = YK.challenge_response(challenge.ljust(64, b'\0'), slot = args.slot)
|
||||
return response
|
||||
except yubico.yubico_exception.YubicoError as e:
|
||||
print("YubiKey challenge-response failed (%s)" % e.reason)
|
||||
print("")
|
||||
response = six.moves.input("Assuming you do not have a YubiKey. Enter repsonse manually (hex encoded) : ")
|
||||
return binascii.unhexlify(response)
|
||||
|
||||
def roll_next_challenge(args, hmac_key, inner_dict):
|
||||
"""
|
||||
When we have the HMAC-SHA1 key in clear, generate a random challenge and compute the
|
||||
expected response for that challenge.
|
||||
|
||||
hmac_key is a 20-byte bytestring
|
||||
"""
|
||||
if len(hmac_key) != 20 or not isinstance(hmac_key, bytes):
|
||||
hmac_key = binascii.unhexlify(hmac_key)
|
||||
|
||||
challenge = os.urandom(args.challenge_length)
|
||||
response = get_response(hmac_key, challenge)
|
||||
|
||||
print("Generated challenge : %s" % binascii.hexlify(challenge).decode('ascii'))
|
||||
print("Expected response : %s (sssh, don't tell anyone)" % binascii.hexlify(response).decode('ascii'))
|
||||
print("")
|
||||
if args.debug or args.verbose or args.init:
|
||||
print("To manually verify that your YubiKey produces this response, use :")
|
||||
print("")
|
||||
print(" $ ykchalresp -%i -x %s" % (args.slot, binascii.hexlify(challenge).decode('ascii')))
|
||||
print("")
|
||||
|
||||
inner_dict["hmac_key"] = binascii.hexlify(hmac_key).decode('ascii')
|
||||
inner_j = json.dumps(inner_dict, indent = 4)
|
||||
if args.verbose or args.debug:
|
||||
print("Inner JSON :\n%s\n" % (inner_j))
|
||||
inner_ciphertext = encrypt_with_response(args, inner_j, response)
|
||||
outer_dict = {"challenge": binascii.hexlify(challenge).decode('ascii'),
|
||||
"inner": inner_ciphertext.decode('ascii'),
|
||||
}
|
||||
outer_j = json.dumps(outer_dict, indent = 4)
|
||||
if args.verbose or args.debug:
|
||||
print("\nOuter JSON :\n%s\n" % (outer_j))
|
||||
|
||||
print("Saving 'outer' JSON to file '%s'" % (args.filename))
|
||||
write_state_file(args, outer_j)
|
||||
|
||||
def get_response(hmac_key, challenge):
|
||||
""" Compute the expected response for `challenge', as hexadecimal string """
|
||||
print(binascii.hexlify(hmac_key), binascii.hexlify(challenge), hashlib.sha1)
|
||||
h = hmac.new(hmac_key, challenge, hashlib.sha1)
|
||||
return h.digest()
|
||||
|
||||
def encrypt_with_response(args, data, key):
|
||||
"""
|
||||
Encrypt our secret inner data with the response we expect the next time.
|
||||
|
||||
NOTE: The use of AES CBC has not been validated as cryptographically sound
|
||||
in this application.
|
||||
|
||||
I would have done this with GPGme if it weren't for the fact that neither
|
||||
of the two versions for Python available in Ubuntu 10.10 have support for
|
||||
symmetric encrypt/decrypt (LP: #295918).
|
||||
"""
|
||||
# pad data to multiple of 16 bytes for AES CBC
|
||||
pad = len(data) % 16
|
||||
data += ' ' * (16 - pad)
|
||||
|
||||
# need to pad key as well
|
||||
aes_key = key
|
||||
aes_key += b'\0' * (32 - len(aes_key))
|
||||
if args.debug:
|
||||
print(("AES-CBC encrypting 'inner' with key (%i bytes) : %s" % (len(aes_key), binascii.hexlify(aes_key))))
|
||||
|
||||
obj = AES.new(aes_key, AES.MODE_CBC, b'\0' * 16)
|
||||
ciphertext = obj.encrypt(data)
|
||||
return binascii.hexlify(ciphertext)
|
||||
|
||||
def decrypt_with_response(args, data, key):
|
||||
"""
|
||||
Try to decrypt the secret inner data with the response we got to this challenge.
|
||||
"""
|
||||
aes_key = key
|
||||
try:
|
||||
aes_key = binascii.unhexlify(key)
|
||||
except (TypeError, binascii.Error):
|
||||
# was not hex encoded
|
||||
pass
|
||||
# need to pad key
|
||||
aes_key += b'\0' * (32 - len(aes_key))
|
||||
if args.debug:
|
||||
print(("AES-CBC decrypting 'inner' using key (%i bytes) : %s" % (len(aes_key), binascii.hexlify(aes_key))))
|
||||
|
||||
obj = AES.new(aes_key, AES.MODE_CBC, b'\0' * 16)
|
||||
plaintext = obj.decrypt(binascii.unhexlify(data))
|
||||
return plaintext
|
||||
|
||||
def write_state_file(args, data):
|
||||
""" Save state to file. """
|
||||
f = open(args.filename, 'w')
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
def load_state_file(args):
|
||||
""" Load (and parse) the state file. """
|
||||
return json.loads(open(args.filename).read())
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
if args.init:
|
||||
init_demo(args)
|
||||
else:
|
||||
do_challenge(args)
|
||||
|
||||
print("\nDone\n")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
46
examples/update_cfg_remove_cr
Executable file
46
examples/update_cfg_remove_cr
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Set up a YubiKey for standard OTP with CR, then remove it.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import struct
|
||||
import yubico
|
||||
import six
|
||||
import binascii
|
||||
|
||||
slot=2
|
||||
|
||||
try:
|
||||
YK = yubico.find_yubikey(debug=True)
|
||||
print("Version : %s " % YK.version())
|
||||
print("Status : %s " % YK.status())
|
||||
|
||||
Cfg = YK.init_config()
|
||||
Cfg.extended_flag('ALLOW_UPDATE', True)
|
||||
Cfg.ticket_flag('APPEND_CR', True)
|
||||
Cfg.extended_flag('SERIAL_API_VISIBLE', True)
|
||||
Cfg.uid = binascii.unhexlify('010203040506')
|
||||
Cfg.fixed_string("m:ftccftbbftdd")
|
||||
Cfg.aes_key('h:' + 32 * 'a')
|
||||
|
||||
user_input = six.moves.input('Write configuration to slot %i of YubiKey? [y/N] : ' % slot )
|
||||
if user_input in ('y', 'ye', 'yes'):
|
||||
YK.write_config(Cfg, slot=slot)
|
||||
print("\nSuccess!")
|
||||
print("Status : %s " % YK.status())
|
||||
else:
|
||||
print("\nAborted")
|
||||
sys.exit(0)
|
||||
|
||||
six.moves.input("Press enter to update...")
|
||||
|
||||
Cfg = YK.init_config(update=True)
|
||||
Cfg.ticket_flag('APPEND_CR', False)
|
||||
|
||||
print ("Updating...");
|
||||
YK.write_config(Cfg, slot=slot)
|
||||
print("\nSuccess!")
|
||||
except yubico.yubico_exception.YubicoError as inst:
|
||||
print("ERROR: %s" % inst.reason)
|
||||
sys.exit(1)
|
||||
37
examples/yubikey-inventory
Executable file
37
examples/yubikey-inventory
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Example of how to access more than one connected YubiKey.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import yubico
|
||||
|
||||
def get_all_yubikeys(debug):
|
||||
"""
|
||||
Look for YubiKey with ever increasing `skip' value until an error is returned.
|
||||
|
||||
Return all instances of class YubiKey we got before failing.
|
||||
"""
|
||||
res = []
|
||||
try:
|
||||
skip = 0
|
||||
while skip < 255:
|
||||
YK = yubico.find_yubikey(debug = debug, skip = skip)
|
||||
res.append(YK)
|
||||
skip += 1
|
||||
except yubico.yubikey.YubiKeyError:
|
||||
pass
|
||||
return res
|
||||
|
||||
debug = False
|
||||
if len(sys.argv) > 1:
|
||||
debug = (sys.argv[1] == '-v')
|
||||
keys = get_all_yubikeys(debug)
|
||||
|
||||
if not keys:
|
||||
print("No YubiKey found.")
|
||||
else:
|
||||
n = 1
|
||||
for this in keys:
|
||||
print("YubiKey #%02i : %s %s" % (n, this.description, this.status()))
|
||||
n += 1
|
||||
Reference in New Issue
Block a user