Skip to content

Commit

Permalink
Release 0.5.0
Browse files Browse the repository at this point in the history
  • Loading branch information
emlun committed Dec 15, 2017
2 parents e8b2dea + af6f6e9 commit d51705b
Show file tree
Hide file tree
Showing 34 changed files with 1,195 additions and 605 deletions.
3 changes: 2 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
repos:
- repo: git://github.com/pre-commit/pre-commit-hooks
sha: v0.8.0
sha: v1.1.1
hooks:
- id: flake8
- id: double-quote-string-fixer
27 changes: 27 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
* Version 0.5.0
** API breaking changes:
*** OATH: New API more similar to yubioath-android
** CLI breaking changes:
*** OATH: Touch prompt now written to stderr instead of stdout
*** OATH: `-a|--algorithm` option to `list` command removed
*** OATH: Columns in `code` command are now dymanically spaced depending on contents
*** OATH: `delete` command now requires confirmation or `-f|--force` argument
*** OATH: IDs printed by `list` command now include TOTP period if not 30
*** Changed outputs:
**** INFO: "Device name" output changed to "Device type"
**** PIV: "Management key is stored on device" output changed to "Management key is stored on the YubiKey"
**** PIV: "All PIV data have been cleared from the device" output changed to "All PIV data have been cleared from your YubiKey"
**** PIV: "The current management key is stored on the device" prompt changed to "The current management key is stored on the YubiKey"
**** SLOT: "blank to use device serial" prompt changed to "blank to use YubiKey serial number"
**** SLOT: "Using device serial" output changed to "Using YubiKey device serial"
**** Lots of failure case outputs changed
** New features:
*** Support for multiple devices via new top-level option `-d|--device`
*** New top-level option `-l|--log-level` to enable logging
*** OATH: Support for remembering passwords locally.
*** OATH: New option `-s|--single` for `code` command
*** PIV: `set-pin-retries` command now warns that PIN and PUK will be reset to factory defaults, and prints those defaults after resetting
** API bug fixes:
*** OATH: `valid_from` and `valid_to` for `Code`s are now absolute instead of relative to the credential period
*** OATH: `period` for non-TOTP `Code`s is now `None`

* Version 0.4.6 (released 2017-10-17)
** Will now attempt to open device 3 times before failing
** OpenPGP: Don't say data is removed when not
Expand Down
4 changes: 2 additions & 2 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
yubikey-manager (0.2.0~ppa1) xenial; urgency=low
yubikey-manager (0.5.0) xenial; urgency=low

* Build for ppa

-- Dag Heyman <dag@yubico.com> Fri, 18 Nov 2016 14:14:54 +0100
-- Emil Lundberg <emil@yubico.com> Tue, 17 Oct 2017 17:19:03 +0200
3 changes: 2 additions & 1 deletion debian/control
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
Source: yubikey-manager
Maintainer: Debian Authentication Maintainers <[email protected]>
Uploaders: Dag Heyman <[email protected]>,
Dain Nilsson <[email protected]>
Dain Nilsson <[email protected]>,
Emil Lundberg <[email protected]>
Section: utils
Priority: optional
Standards-Version: 3.9.7
Expand Down
19 changes: 19 additions & 0 deletions doc/development.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,22 @@ To run integration tests:

A Vagrantfile with a development environment based on Ubuntu 16.04 is included in the repository.
Modify the Vagrantfile to set up a USB filter to capture the device with VirtualBox.


== Publishing to Ubuntu PPA

1. Update version number and signoff in `debian/changelog`.
2. Build and upload package.

For (2) you can use the Vagrant VM in `vagrant/ppa`. You'll need to set up the
VM to capture the YubiKey containing your signing key. If you use VirtualBox,
you can do this by uncommenting the USB filter included in the `Vagrantfile`.
Then:

alice@work $ cd yubikey-manager/vagrant/ppa
alice@work $ vagrant up
alice@work $ vagrant ssh
ubuntu@ubuntu-xenial $ gpg2 --recv-keys ABCDEF78
ubuntu@ubuntu-xenial $ gpg2 --card-status
ubuntu@ubuntu-xenial $ cd yubikey-manager
ubuntu@ubuntu-xenial $ ~/scripts/make-ppa -k ABCDEF78 -p gpg2
2 changes: 1 addition & 1 deletion docker/xenial/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ RUN mkdir /deb
RUN mv /yubikey-manager_* /deb
RUN mv /python3-yubikey-manager_* /deb
RUN mv /python-yubikey-manager_* /deb
RUN cd / && tar czf yubikey-manager-debian-builds.tar.gz deb
RUN cd / && tar czf yubikey-manager-debian-packages.tar.gz deb
27 changes: 14 additions & 13 deletions test/test_cli_commands_on_yubikey.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
click.confirm(
'Run integration tests? This will erase data on the YubiKey,'
' make sure it is a key used for development.', abort=True)
_one_yubikey = len(list(get_descriptors())) == 1
_one_yubikey = len(get_descriptors()) == 1
except Exception:
sys.exit()
_skip = False
Expand All @@ -36,7 +36,7 @@
def _has_mode(mode):
if not _one_yubikey:
return False
yubikeys = list(get_descriptors())
yubikeys = get_descriptors()
if len(yubikeys) is not 1:
return False
return yubikeys[0].mode.has_transport(mode)
Expand All @@ -45,7 +45,7 @@ def _has_mode(mode):
def _get_version():
if not _one_yubikey:
return None
return list(get_descriptors())[0].version
return get_descriptors()[0].version


def _is_NEO():
Expand Down Expand Up @@ -76,7 +76,7 @@ class TestYkmanInfo(unittest.TestCase):
def test_ykman_info(self):
time.sleep(3)
info = ykman_cli('info')
self.assertIn('Device name:', info)
self.assertIn('Device type:', info)
self.assertIn('Serial number:', info)
self.assertIn('Firmware version:', info)

Expand Down Expand Up @@ -105,9 +105,9 @@ class TestSlotProgramming(unittest.TestCase):

def test_ykman_program_otp_slot_2(self):
output = ykman_cli('slot', 'otp', '2', '-f')
self.assertIn('Using device serial as public ID:', output)
self.assertIn('Using a randomly generated private ID:', output)
self.assertIn('Using a randomly generated secret key:', output)
self.assertIn('Using YubiKey serial as public ID', output)
self.assertIn('Using a randomly generated private ID', output)
self.assertIn('Using a randomly generated secret key', output)
self._check_slot_2_programmed()

def test_ykman_program_chalresp_slot_2(self):
Expand Down Expand Up @@ -145,6 +145,7 @@ def test_delete_slot_2(self):

def test_access_code_slot_2(self):
ykman_cli('slot', '--access-code', '111111111111', 'static', '2', '-f')
self._check_slot_2_programmed()
ykman_cli('slot', '--access-code', '111111111111', 'delete', '2', '-f')
status = ykman_cli('slot', 'info')
self.assertIn('Slot 2: empty', status)
Expand Down Expand Up @@ -205,7 +206,7 @@ class TestOATH(unittest.TestCase):

def test_oath_info(self):
output = ykman_cli('oath', 'info')
self.assertIn('OATH version:', output)
self.assertIn('version:', output)

def test_oath_add_credential(self):
ykman_cli('oath', 'add', 'test-name', 'abba')
Expand Down Expand Up @@ -246,9 +247,8 @@ def test_oath_code_query(self):

def test_oath_reset(self):
output = ykman_cli('oath', 'reset', '-f')
self.assertIn(
'Success! All credentials have been cleared from the device.',
output)
self.assertIn('Success! All OATH credentials have been cleared from '
'your YubiKey', output)

def test_oath_hotp_code(self):
ykman_cli('oath', 'add', '-o', 'HOTP', 'hotp-cred', 'abba')
Expand All @@ -262,7 +262,7 @@ def test_oath_hotp_steam_code(self):

def test_oath_delete(self):
ykman_cli('oath', 'add', 'delete-me', 'abba')
ykman_cli('oath', 'delete', 'delete-me')
ykman_cli('oath', 'delete', 'delete-me', '-f')
self.assertNotIn('delete-me', ykman_cli('oath', 'list'))


Expand Down Expand Up @@ -406,7 +406,8 @@ def test_piv_change_management_key_protect(self):
'-m', DEFAULT_MANAGEMENT_KEY)
output = ykman_cli('piv', 'info')
self.assertIn(
'Management key is stored on device and protected by PIN', output)
'Management key is stored on the YubiKey, protected by PIN',
output)
ykman_cli('piv', 'reset', '-f') # Cleanup, should maybe be done always?

def test_piv_change_pin(self):
Expand Down
94 changes: 63 additions & 31 deletions test/test_oath.py
Original file line number Diff line number Diff line change
@@ -1,62 +1,94 @@
from ykman.oath import Credential
# vim: set fileencoding=utf-8 :

from ykman.oath import Credential, CredentialData, _derive_key, OATH_TYPE, ALGO
import unittest


class TestOathFunctions(unittest.TestCase):

def test_credential_parse_period_and_issuer_and_name(self):
period, issuer, name = Credential.parse_long_name('20/Issuer:name')
issuer, name, period = Credential.parse_key(b'20/Issuer:name')
self.assertEqual(20, period)
self.assertEqual('Issuer', issuer)
self.assertEqual('name', name)

def test_credential_parse_wierd_issuer_and_name(self):
period, issuer, name = Credential.parse_long_name('wierd/Issuer:name')
issuer, name, period = Credential.parse_key(b'wierd/Issuer:name')
self.assertEqual(30, period)
self.assertEqual('wierd/Issuer', issuer)
self.assertEqual('name', name)

def test_credential_parse_issuer_and_name(self):
period, issuer, name = Credential.parse_long_name('Issuer:name')
issuer, name, period = Credential.parse_key(b'Issuer:name')
self.assertEqual(30, period)
self.assertEqual('Issuer', issuer)
self.assertEqual('name', name)

def test_credential_parse_period_and_name(self):
period, issuer, name = Credential.parse_long_name('20/name')
issuer, name, period = Credential.parse_key(b'20/name')
self.assertEqual(20, period)
self.assertEqual(None, issuer)
self.assertIsNone(issuer)
self.assertEqual('name', name)

def test_credential_parse_only_name(self):
period, issuer, name = Credential.parse_long_name('name')
issuer, name, period = Credential.parse_key(b'name')
self.assertEqual(30, period)
self.assertEqual(None, issuer)
self.assertIsNone(issuer)
self.assertEqual('name', name)

def test_credential_serialize_name(self):
self.assertEqual('name', Credential('name').long_name)
self.assertEqual(
'Issuer:name', Credential('name', issuer='Issuer').long_name)
self.assertEqual(
'20/Issuer:name', Credential(
'name', issuer='Issuer', period=20).long_name)
self.assertEqual(
'Issuer:name', Credential(
'name', issuer='Issuer', period=30).long_name)
def test_credential_data_make_key(self):
self.assertEqual(b'name', CredentialData(b'', None, 'name').make_key())
self.assertEqual(b'Issuer:name',
CredentialData(b'', 'Issuer', 'name').make_key())
self.assertEqual(b'20/Issuer:name',
CredentialData(b'', 'Issuer', 'name', period=20
).make_key())
self.assertEqual(b'Issuer:name',
CredentialData(b'', 'Issuer', 'name', period=30
).make_key())
self.assertEqual(b'20/name',
CredentialData(b'', None, 'name', period=20
).make_key())

def test_derive_key(self):
self.assertEqual(
'20/name', Credential('name', period=20).long_name)
b'\xb0}\xa1\xe7\xde\x87\xf8\x9a\x87\xa2\xb5\x98\xea\xa2\x18\x8c',
_derive_key(b'\0\0\0\0\0\0\0\0', u'foobar'))
self.assertEqual(
'Issuer:name', Credential(
'name', issuer='Issuer', period=None).long_name)
b'\xda\x81\x8ek,\xf0\xa2\xd0\xbf\x19\xb3\xdd\xd3K\x83\xf5',
_derive_key(b'12345678', u'Hallå världen!'))
self.assertEqual(
'name', Credential('name', period=None).long_name)

def test_credential_expiration(self):
cred = Credential('name')
cred.update_expiration(0)
self.assertEqual(30, cred.expiration)
cred.update_expiration(30)
self.assertEqual(60, cred.expiration)
cred.update_expiration(60)
self.assertEqual(90, cred.expiration)
b'\xf3\xdf\xa7\x81T\xc8\x102\x99E\xfb\xc4\xb55\xe57',
_derive_key(b'saltsalt', u'Ťᶒśƫ ᵽĥřӓşḛ'))

def test_parse_uri_issuer(self):
no_issuer = CredentialData.from_uri('otpauth://totp/account'
'?secret=abba')
self.assertIsNone(no_issuer.issuer)

from_param = CredentialData.from_uri('otpauth://totp/account'
'?secret=abba&issuer=Test')
self.assertEqual('Test', from_param.issuer)

from_name = CredentialData.from_uri('otpauth://totp/Test:account'
'?secret=abba')
self.assertEqual('Test', from_name.issuer)

with_both = CredentialData.from_uri('otpauth://totp/TestA:account'
'?secret=abba&issuer=TestB')
self.assertEqual('TestB', with_both.issuer)

def test_parse_uri(self):
data = CredentialData.from_uri('otpauth://totp/Issuer:account'
'?secret=abba&issuer=Issuer'
'&algorithm=SHA256&digits=7'
'&period=20&counter=5')
self.assertEqual(b'\0B', data.secret)
self.assertEqual('Issuer', data.issuer)
self.assertEqual('account', data.name)
self.assertEqual(OATH_TYPE.TOTP, data.oath_type)
self.assertEqual(ALGO.SHA256, data.algorithm)
self.assertEqual(7, data.digits)
self.assertEqual(20, data.period)
self.assertEqual(5, data.counter)
self.assertEqual(False, data.touch)
13 changes: 1 addition & 12 deletions test/test_util.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# vim: set fileencoding=utf-8 :

from ykman.util import (b2len, derive_key, format_code, generate_static_pw,
from ykman.util import (b2len, format_code, generate_static_pw,
hmac_shorten_key, modhex_decode, modhex_encode,
parse_tlvs, parse_truncated, time_challenge, Tlv,
is_pkcs12)
Expand All @@ -23,17 +23,6 @@ def test_b2len(self):
self.assertEqual(0x1234, b2len(b'\x12\x34'))
self.assertEqual(0xcafed00d, b2len(b'\xca\xfe\xd0\x0d'))

def test_derive_key(self):
self.assertEqual(
b'\xb0}\xa1\xe7\xde\x87\xf8\x9a\x87\xa2\xb5\x98\xea\xa2\x18\x8c',
derive_key(b'\0\0\0\0\0\0\0\0', u'foobar'))
self.assertEqual(
b'\xda\x81\x8ek,\xf0\xa2\xd0\xbf\x19\xb3\xdd\xd3K\x83\xf5',
derive_key(b'12345678', u'Hallå världen!'))
self.assertEqual(
b'\xf3\xdf\xa7\x81T\xc8\x102\x99E\xfb\xc4\xb55\xe57',
derive_key(b'saltsalt', u'Ťᶒśƫ ᵽĥřӓşḛ'))

def test_format_code(self):
self.assertEqual('000000', format_code(0))
self.assertEqual('00000000', format_code(0, 8))
Expand Down
8 changes: 5 additions & 3 deletions vagrant/ppa/provision.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ sudo apt-get install -qq \
python-enum34 \
libykpers-1-1 \
libu2f-host0 \
pcscd \
debhelper \
devscripts \
dh-make
dh-make \
gnupg2 \
gnupg-agent \
scdaemon

git clone https://github.com/dainnilsson/scripts
sudo -u ubuntu git clone https://github.com/dainnilsson/scripts
2 changes: 1 addition & 1 deletion ykman/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

__version__ = '0.4.6'
__version__ = '0.5.0'
Loading

0 comments on commit d51705b

Please sign in to comment.