Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add YubiHSM Auth application #563

Merged
merged 17 commits into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
286 changes: 286 additions & 0 deletions tests/device/cli/test_hsmauth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
# -*- coding: utf-8 -*-
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec

from yubikit.management import CAPABILITY
from .. import condition

import pytest
import re
import os
import tempfile

DEFAULT_MANAGEMENT_KEY = "00000000000000000000000000000000"
NON_DEFAULT_MANAGEMENT_KEY = "11111111111111111111111111111111"


def generate_pem_eccp256_keypair():
pk = ec.generate_private_key(ec.SECP256R1(), default_backend())
return (
pk.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption(),
),
pk.public_key().public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
),
)


@pytest.fixture()
def eccp256_keypair():
tmp = tempfile.NamedTemporaryFile(delete=False)
private_key, public_key = generate_pem_eccp256_keypair()
tmp.write(private_key)
tmp.close()
yield tmp.name, public_key
os.remove(tmp.name)


@pytest.fixture()
def tmp_file():
tmp = tempfile.NamedTemporaryFile(delete=False)
yield tmp
tmp.close()
os.remove(tmp.name)


@pytest.fixture(autouse=True)
@condition.capability(CAPABILITY.OATH)
@condition.min_version(5, 4, 3)
def preconditions(ykman_cli):
ykman_cli("hsmauth", "reset", "-f")


class TestOATH:
def test_hsmauth_info(self, ykman_cli):
output = ykman_cli("hsmauth", "info").output
assert "version:" in output

def test_hsmauth_reset(self, ykman_cli):
output = ykman_cli("hsmauth", "reset", "-f").output
assert (
"Success! All YubiHSM Auth data have been cleared from the YubiKey."
in output
)


class TestCredentials:
def test_import_credential_symmetric(self, ykman_cli):
ykman_cli(
"hsmauth",
"credentials",
"symmetric",
"test-name-sym",
"-c",
"123456",
"-E",
os.urandom(16).hex(),
"-M",
os.urandom(16).hex(),
"-m",
DEFAULT_MANAGEMENT_KEY,
)
creds = ykman_cli("hsmauth", "credentials", "list").output
assert "test-name-sym" in creds

def test_import_credential_symmetric_generate(self, ykman_cli):
output = ykman_cli(
"hsmauth",
"credentials",
"symmetric",
"test-name-sym-gen",
"-c",
"123456",
"-g",
"-m",
DEFAULT_MANAGEMENT_KEY,
).output

assert "Generated ENC and MAC keys" in output

def test_import_credential_symmetric_derived(self, ykman_cli):
ykman_cli(
"hsmauth",
"credentials",
"derive",
"test-name-sym-derived",
"-c",
"123456",
"-d",
"password",
)
creds = ykman_cli("hsmauth", "credentials", "list").output
assert "test-name-sym-derived" in creds

@condition.min_version(5, 6)
def test_import_credential_asymmetric(self, ykman_cli):
pair = generate_pem_eccp256_keypair()
ykman_cli(
"hsmauth",
"credentials",
"import",
"test-name-asym",
"-c",
"123456",
"-m",
DEFAULT_MANAGEMENT_KEY,
"-",
input=pair[0],
)
creds = ykman_cli("hsmauth", "credentials", "list").output
assert "test-name-asym" in creds

public_key_exported = ykman_cli(
"hsmauth", "credentials", "export", "test-name-asym", "-"
).stdout_bytes
assert pair[1] == public_key_exported

@condition.min_version(5, 6)
def test_generate_credential_asymmetric(self, ykman_cli):
ykman_cli(
"hsmauth",
"credentials",
"generate",
"test-name-asym-generated",
"-c",
"123456",
"-m",
DEFAULT_MANAGEMENT_KEY,
)

creds = ykman_cli("hsmauth", "credentials", "list").output
assert "test-name-asym-generated" in creds

def test_import_credential_touch_required(self, ykman_cli):
ykman_cli(
"hsmauth",
"credentials",
"derive",
"test-name-touch",
"-c",
"123456",
"-d",
"password",
"-t",
)

creds = ykman_cli("hsmauth", "credentials", "list").output
assert "On" in creds
assert "test-name-touch" in creds

@condition.min_version(5, 6)
def test_export_public_key_to_file(self, ykman_cli, eccp256_keypair, tmp_file):
private_key_file, public_key = eccp256_keypair
ykman_cli(
"hsmauth",
"credentials",
"import",
"test-name-asym",
"-c",
"123456",
"-m",
DEFAULT_MANAGEMENT_KEY,
private_key_file,
)

ykman_cli(
"hsmauth",
"credentials",
"export",
"test-name-asym",
tmp_file.name,
)

public_key_from_file = tmp_file.read()
assert public_key_from_file == public_key

@condition.min_version(5, 6)
def test_export_public_key_symmetric_credential(self, ykman_cli):
ykman_cli(
"hsmauth",
"credentials",
"derive",
"test-name-sym",
"-c",
"123456",
"-d",
"password",
"-m",
DEFAULT_MANAGEMENT_KEY,
)

with pytest.raises(SystemExit):
ykman_cli("hsmauth", "credentials", "export", "test-name-sym")

def test_delete_credential(self, ykman_cli):
ykman_cli(
"hsmauth",
"credentials",
"derive",
"delete-me",
"-c",
"123456",
"-d",
"password",
"-m",
DEFAULT_MANAGEMENT_KEY,
)
old_creds = ykman_cli("hsmauth", "credentials", "list").output
assert "delete-me" in old_creds
ykman_cli("hsmauth", "credentials", "delete", "delete-me", "-f")
new_creds = ykman_cli("hsmauth", "credentials", "list").output
assert "delete-me" not in new_creds


class TestManagementKey:
def test_change_management_key(self, ykman_cli):
ykman_cli(
"hsmauth",
"access",
"change-management-key",
"-m",
DEFAULT_MANAGEMENT_KEY,
"-n",
NON_DEFAULT_MANAGEMENT_KEY,
)

with pytest.raises(SystemExit):
# Should fail - wrong current key
ykman_cli(
"hsmauth",
"access",
"change-management-key",
"-m",
DEFAULT_MANAGEMENT_KEY,
"-n",
DEFAULT_MANAGEMENT_KEY,
)

# Should succeed
ykman_cli(
"hsmauth",
"access",
"change-management-key",
"-m",
NON_DEFAULT_MANAGEMENT_KEY,
"-n",
DEFAULT_MANAGEMENT_KEY,
)

def test_change_management_key_generate(self, ykman_cli):
output = ykman_cli(
"hsmauth",
"access",
"change-management-key",
"-m",
DEFAULT_MANAGEMENT_KEY,
"-g",
).output

assert re.match(
r"^Generated management key: [a-f0-9]{16}", output, re.MULTILINE
)
Loading
Loading