From 2c24c348b53571e48b1c7f5293ca9c014d0b7691 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Thu, 2 Feb 2023 23:08:49 +0100 Subject: [PATCH] Adding tests for OpenPGP. All tests are taken from gnuk. Reader is migrated from pyusb to pyscard. Signed-off-by: Pol Henarejos --- tests/000_config/test_000_config_card.py | 1 + .../test_000_initial_card.py | 1 + .../test_001_initial_card.py | 1 + .../010_kdfnone/test_010_adminfull_kdfnone.py | 2 + .../010_kdfnone/test_011_adminfull_kdfnone.py | 2 + .../010_kdfnone/test_012_adminfull_kdfnone.py | 2 + .../010_kdfnone/test_013_adminfull_kdfnone.py | 2 + tests/010_kdfnone/test_014_keygen_kdfnone.py | 24 + tests/010_kdfnone/test_015_keygen_kdfnone.py | 24 + tests/010_kdfnone/test_016_keygen_kdfnone.py | 24 + .../010_kdfnone/test_017_adminfull_kdfnone.py | 2 + .../010_kdfnone/test_018_adminfull_kdfnone.py | 2 + .../010_kdfnone/test_019_adminfull_kdfnone.py | 2 + .../010_kdfnone/test_040_adminless_kdfnone.py | 25 + .../010_kdfnone/test_041_adminless_kdfnone.py | 25 + .../010_kdfnone/test_042_adminless_kdfnone.py | 25 + .../010_kdfnone/test_043_adminless_kdfnone.py | 25 + .../010_kdfnone/test_044_adminless_kdfnone.py | 25 + .../010_kdfnone/test_045_adminless_kdfnone.py | 25 + .../010_kdfnone/test_046_adminless_kdfnone.py | 25 + .../test_050_adminfull_kdffull.py | 23 + .../test_051_adminfull_kdffull.py | 23 + .../001_rsa2k/test_rsa2k_import.py | 1 + .../002_nistp256r1/test_nistp256r1_import.py | 1 + .../test_brainpoolp256r1_import.py | 1 + .../004_secp256k1/test_secp256k1_import.py | 1 + .../005_nistp384r1/test_nistp384r1_import.py | 1 + .../test_brainpoolp384r1_import.py | 1 + .../008_nistp521r1/test_nistp521r1_import.py | 1 + .../test_brainpoolp521r1_import.py | 1 + .../010_curve25519/test_curve25519_import.py | 1 + .../04_keygen/001_rsa2k/test_rsa2k_keygen.py | 1 + .../002_nistp256r1/test_nistp256r1_keygen.py | 1 + .../test_brainpoolp256r1_keygen.py | 1 + .../004_secp256k1/test_secp256k1_keygen.py | 1 + .../005_nistp384r1/test_nistp384r1_keygen.py | 1 + .../test_brainpoolp384r1_keygen.py | 1 + .../008_nistp521r1/test_nistp521r1_keygen.py | 1 + .../test_brainpoolp521r1_keygen.py | 1 + .../010_curve25519/test_curve25519_keygen.py | 1 + .../05_finalize/test_000_reset_to_TEST4.py | 15 + .../05_finalize/test_057_adminfull_kdffull.py | 23 + .../05_finalize/test_058_adminfull_kdffull.py | 23 + .../05_finalize/test_059_adminfull_kdffull.py | 23 + tests/020_kdffull/conftest.py | 1 + .../test_060_adminfull_kdfsingle.py | 24 + .../test_061_adminfull_kdfsingle.py | 25 + .../test_062_adminfull_kdfsingle.py | 24 + .../test_063_adminfull_kdfsingle.py | 24 + .../test_064_adminfull_kdfsingle.py | 24 + .../test_065_adminfull_kdfsingle.py | 24 + .../test_066_adminfull_kdfsingle.py | 24 + .../test_070_adminless_kdfsingle.py | 2 + .../test_071_adminless_kdfsingle.py | 2 + .../test_072_adminless_kdfsingle.py | 2 + .../test_073_adminless_kdfsingle.py | 2 + .../test_074_adminless_kdfsingle.py | 2 + .../test_075_adminless_kdfsingle.py | 2 + .../test_076_adminless_kdfsingle.py | 2 + tests/090_finalize/test_080_kdf_none.py | 42 ++ tests/090_finalize/test_091_reset_attr.py | 23 + tests/brainpoolp256r1_keys.py | 148 +++++ tests/brainpoolp384r1_keys.py | 157 ++++++ tests/brainpoolp512r1_keys.py | 166 ++++++ tests/card_const.py | 46 ++ tests/card_reader.py | 67 +++ tests/card_test_0_set_attr.py | 41 ++ tests/card_test_1_import_keys.py | 184 +++++++ tests/card_test_1_keygen.py | 99 ++++ tests/card_test_2_pkop.py | 62 +++ tests/card_test_2_pkop_kg.py | 51 ++ tests/card_test_3_ds_counter1.py | 31 ++ tests/card_test_3_ds_counter2.py | 31 ++ tests/card_test_check_card.py | 80 +++ tests/card_test_ds_counter1.py | 31 ++ tests/card_test_ds_counter2.py | 31 ++ tests/card_test_empty_card.py | 271 +++++++++ tests/card_test_kdf_full.py | 34 ++ tests/card_test_kdf_single.py | 33 ++ tests/card_test_keygen.py | 70 +++ tests/card_test_kg_pko_dsc_brainpoolp256r1.py | 40 ++ tests/card_test_kg_pko_dsc_brainpoolp384r1.py | 40 ++ tests/card_test_kg_pko_dsc_brainpoolp512r1.py | 40 ++ tests/card_test_kg_pko_dsc_curve25519.py | 40 ++ tests/card_test_kg_pko_dsc_nistp256r1.py | 40 ++ tests/card_test_kg_pko_dsc_nistp384r1.py | 40 ++ tests/card_test_kg_pko_dsc_nistp521r1.py | 40 ++ tests/card_test_kg_pko_dsc_rsa2k.py | 41 ++ tests/card_test_kg_pko_dsc_rsa4k.py | 13 + tests/card_test_kg_pko_dsc_secp256k1.py | 40 ++ tests/card_test_ki_pko_dsc_brainpoolp256r1.py | 40 ++ tests/card_test_ki_pko_dsc_brainpoolp384r1.py | 40 ++ tests/card_test_ki_pko_dsc_brainpoolp512r1.py | 40 ++ tests/card_test_ki_pko_dsc_curve25519.py | 40 ++ tests/card_test_ki_pko_dsc_nistp256r1.py | 40 ++ tests/card_test_ki_pko_dsc_nistp384r1.py | 40 ++ tests/card_test_ki_pko_dsc_nistp521r1.py | 40 ++ tests/card_test_ki_pko_dsc_rsa2k.py | 41 ++ tests/card_test_ki_pko_dsc_rsa4k.py | 13 + tests/card_test_ki_pko_dsc_secp256k1.py | 40 ++ tests/card_test_personalize_admin_less_1.py | 190 +++++++ tests/card_test_personalize_admin_less_2.py | 121 ++++ tests/card_test_personalize_card_1.py | 82 +++ tests/card_test_personalize_card_2.py | 200 +++++++ tests/card_test_personalize_reset.py | 85 +++ tests/card_test_public_key_operations.py | 81 +++ tests/card_test_public_key_operations_alt.py | 83 +++ tests/card_test_public_key_operations_kg.py | 58 ++ tests/card_test_remove_keys.py | 45 ++ tests/card_test_reset_attr.py | 69 +++ tests/card_test_reset_pw3.py | 46 ++ tests/card_test_set_attr.py | 41 ++ tests/conftest.py | 18 + tests/constants_for_test.py | 17 + tests/curve25519_keys.py | 124 +++++ tests/kdf_calc.py | 1 + tests/nistp256r1_keys.py | 152 ++++++ tests/nistp384r1_keys.py | 158 ++++++ tests/nistp521r1_keys.py | 174 ++++++ tests/openpgp_card.py | 515 ++++++++++++++++++ tests/pk_25519_with_libgcrypt.py | 165 ++++++ tests/pk_signed_mpi_with_libgcrypt.py | 189 +++++++ tests/pubkey_crypto.py | 26 + tests/rsa_keys.py | 284 ++++++++++ tests/secp256k1_keys.py | 153 ++++++ tests/skip_gnuk_only_tests.py | 6 + tests/skip_if_kdfreq.py | 6 + tests/skip_if_no_kdf_support.py | 6 + tests/util.py | 34 ++ 129 files changed, 6171 insertions(+) create mode 100644 tests/000_config/test_000_config_card.py create mode 100644 tests/001_initial_check/test_000_initial_card.py create mode 100644 tests/001_initial_check/test_001_initial_card.py create mode 100644 tests/010_kdfnone/test_010_adminfull_kdfnone.py create mode 100644 tests/010_kdfnone/test_011_adminfull_kdfnone.py create mode 100644 tests/010_kdfnone/test_012_adminfull_kdfnone.py create mode 100644 tests/010_kdfnone/test_013_adminfull_kdfnone.py create mode 100644 tests/010_kdfnone/test_014_keygen_kdfnone.py create mode 100644 tests/010_kdfnone/test_015_keygen_kdfnone.py create mode 100644 tests/010_kdfnone/test_016_keygen_kdfnone.py create mode 100644 tests/010_kdfnone/test_017_adminfull_kdfnone.py create mode 100644 tests/010_kdfnone/test_018_adminfull_kdfnone.py create mode 100644 tests/010_kdfnone/test_019_adminfull_kdfnone.py create mode 100644 tests/010_kdfnone/test_040_adminless_kdfnone.py create mode 100644 tests/010_kdfnone/test_041_adminless_kdfnone.py create mode 100644 tests/010_kdfnone/test_042_adminless_kdfnone.py create mode 100644 tests/010_kdfnone/test_043_adminless_kdfnone.py create mode 100644 tests/010_kdfnone/test_044_adminless_kdfnone.py create mode 100644 tests/010_kdfnone/test_045_adminless_kdfnone.py create mode 100644 tests/010_kdfnone/test_046_adminless_kdfnone.py create mode 100644 tests/020_kdffull/01_initial_check/test_050_adminfull_kdffull.py create mode 100644 tests/020_kdffull/02_personalization/test_051_adminfull_kdffull.py create mode 100644 tests/020_kdffull/03_key_import/001_rsa2k/test_rsa2k_import.py create mode 100644 tests/020_kdffull/03_key_import/002_nistp256r1/test_nistp256r1_import.py create mode 100644 tests/020_kdffull/03_key_import/003_brainpoolp256r1/test_brainpoolp256r1_import.py create mode 100644 tests/020_kdffull/03_key_import/004_secp256k1/test_secp256k1_import.py create mode 100644 tests/020_kdffull/03_key_import/005_nistp384r1/test_nistp384r1_import.py create mode 100644 tests/020_kdffull/03_key_import/006_brainpoolp384r1/test_brainpoolp384r1_import.py create mode 100644 tests/020_kdffull/03_key_import/008_nistp521r1/test_nistp521r1_import.py create mode 100644 tests/020_kdffull/03_key_import/009_brainpoolp512r1/test_brainpoolp521r1_import.py create mode 100644 tests/020_kdffull/03_key_import/010_curve25519/test_curve25519_import.py create mode 100644 tests/020_kdffull/04_keygen/001_rsa2k/test_rsa2k_keygen.py create mode 100644 tests/020_kdffull/04_keygen/002_nistp256r1/test_nistp256r1_keygen.py create mode 100644 tests/020_kdffull/04_keygen/003_brainpoolp256r1/test_brainpoolp256r1_keygen.py create mode 100644 tests/020_kdffull/04_keygen/004_secp256k1/test_secp256k1_keygen.py create mode 100644 tests/020_kdffull/04_keygen/005_nistp384r1/test_nistp384r1_keygen.py create mode 100644 tests/020_kdffull/04_keygen/006_brainpoolp384r1/test_brainpoolp384r1_keygen.py create mode 100644 tests/020_kdffull/04_keygen/008_nistp521r1/test_nistp521r1_keygen.py create mode 100644 tests/020_kdffull/04_keygen/009_brainpoolp512r1/test_brainpoolp521r1_keygen.py create mode 100644 tests/020_kdffull/04_keygen/010_curve25519/test_curve25519_keygen.py create mode 100644 tests/020_kdffull/05_finalize/test_000_reset_to_TEST4.py create mode 100644 tests/020_kdffull/05_finalize/test_057_adminfull_kdffull.py create mode 100644 tests/020_kdffull/05_finalize/test_058_adminfull_kdffull.py create mode 100644 tests/020_kdffull/05_finalize/test_059_adminfull_kdffull.py create mode 100644 tests/020_kdffull/conftest.py create mode 100644 tests/030_kdfsingle/test_060_adminfull_kdfsingle.py create mode 100644 tests/030_kdfsingle/test_061_adminfull_kdfsingle.py create mode 100644 tests/030_kdfsingle/test_062_adminfull_kdfsingle.py create mode 100644 tests/030_kdfsingle/test_063_adminfull_kdfsingle.py create mode 100644 tests/030_kdfsingle/test_064_adminfull_kdfsingle.py create mode 100644 tests/030_kdfsingle/test_065_adminfull_kdfsingle.py create mode 100644 tests/030_kdfsingle/test_066_adminfull_kdfsingle.py create mode 100644 tests/030_kdfsingle/test_070_adminless_kdfsingle.py create mode 100644 tests/030_kdfsingle/test_071_adminless_kdfsingle.py create mode 100644 tests/030_kdfsingle/test_072_adminless_kdfsingle.py create mode 100644 tests/030_kdfsingle/test_073_adminless_kdfsingle.py create mode 100644 tests/030_kdfsingle/test_074_adminless_kdfsingle.py create mode 100644 tests/030_kdfsingle/test_075_adminless_kdfsingle.py create mode 100644 tests/030_kdfsingle/test_076_adminless_kdfsingle.py create mode 100644 tests/090_finalize/test_080_kdf_none.py create mode 100644 tests/090_finalize/test_091_reset_attr.py create mode 100644 tests/brainpoolp256r1_keys.py create mode 100644 tests/brainpoolp384r1_keys.py create mode 100644 tests/brainpoolp512r1_keys.py create mode 100644 tests/card_const.py create mode 100644 tests/card_reader.py create mode 100644 tests/card_test_0_set_attr.py create mode 100644 tests/card_test_1_import_keys.py create mode 100644 tests/card_test_1_keygen.py create mode 100644 tests/card_test_2_pkop.py create mode 100644 tests/card_test_2_pkop_kg.py create mode 100644 tests/card_test_3_ds_counter1.py create mode 100644 tests/card_test_3_ds_counter2.py create mode 100644 tests/card_test_check_card.py create mode 100644 tests/card_test_ds_counter1.py create mode 100644 tests/card_test_ds_counter2.py create mode 100644 tests/card_test_empty_card.py create mode 100644 tests/card_test_kdf_full.py create mode 100644 tests/card_test_kdf_single.py create mode 100644 tests/card_test_keygen.py create mode 100644 tests/card_test_kg_pko_dsc_brainpoolp256r1.py create mode 100644 tests/card_test_kg_pko_dsc_brainpoolp384r1.py create mode 100644 tests/card_test_kg_pko_dsc_brainpoolp512r1.py create mode 100644 tests/card_test_kg_pko_dsc_curve25519.py create mode 100644 tests/card_test_kg_pko_dsc_nistp256r1.py create mode 100644 tests/card_test_kg_pko_dsc_nistp384r1.py create mode 100644 tests/card_test_kg_pko_dsc_nistp521r1.py create mode 100644 tests/card_test_kg_pko_dsc_rsa2k.py create mode 100644 tests/card_test_kg_pko_dsc_rsa4k.py create mode 100644 tests/card_test_kg_pko_dsc_secp256k1.py create mode 100644 tests/card_test_ki_pko_dsc_brainpoolp256r1.py create mode 100644 tests/card_test_ki_pko_dsc_brainpoolp384r1.py create mode 100644 tests/card_test_ki_pko_dsc_brainpoolp512r1.py create mode 100644 tests/card_test_ki_pko_dsc_curve25519.py create mode 100644 tests/card_test_ki_pko_dsc_nistp256r1.py create mode 100644 tests/card_test_ki_pko_dsc_nistp384r1.py create mode 100644 tests/card_test_ki_pko_dsc_nistp521r1.py create mode 100644 tests/card_test_ki_pko_dsc_rsa2k.py create mode 100644 tests/card_test_ki_pko_dsc_rsa4k.py create mode 100644 tests/card_test_ki_pko_dsc_secp256k1.py create mode 100644 tests/card_test_personalize_admin_less_1.py create mode 100644 tests/card_test_personalize_admin_less_2.py create mode 100644 tests/card_test_personalize_card_1.py create mode 100644 tests/card_test_personalize_card_2.py create mode 100644 tests/card_test_personalize_reset.py create mode 100644 tests/card_test_public_key_operations.py create mode 100644 tests/card_test_public_key_operations_alt.py create mode 100644 tests/card_test_public_key_operations_kg.py create mode 100644 tests/card_test_remove_keys.py create mode 100644 tests/card_test_reset_attr.py create mode 100644 tests/card_test_reset_pw3.py create mode 100644 tests/card_test_set_attr.py create mode 100644 tests/conftest.py create mode 100644 tests/constants_for_test.py create mode 100644 tests/curve25519_keys.py create mode 120000 tests/kdf_calc.py create mode 100644 tests/nistp256r1_keys.py create mode 100644 tests/nistp384r1_keys.py create mode 100644 tests/nistp521r1_keys.py create mode 100644 tests/openpgp_card.py create mode 100644 tests/pk_25519_with_libgcrypt.py create mode 100644 tests/pk_signed_mpi_with_libgcrypt.py create mode 100644 tests/pubkey_crypto.py create mode 100644 tests/rsa_keys.py create mode 100644 tests/secp256k1_keys.py create mode 100644 tests/skip_gnuk_only_tests.py create mode 100644 tests/skip_if_kdfreq.py create mode 100644 tests/skip_if_no_kdf_support.py create mode 100644 tests/util.py diff --git a/tests/000_config/test_000_config_card.py b/tests/000_config/test_000_config_card.py new file mode 100644 index 0000000..d80f64d --- /dev/null +++ b/tests/000_config/test_000_config_card.py @@ -0,0 +1 @@ +from card_test_check_card import * diff --git a/tests/001_initial_check/test_000_initial_card.py b/tests/001_initial_check/test_000_initial_card.py new file mode 100644 index 0000000..0410be2 --- /dev/null +++ b/tests/001_initial_check/test_000_initial_card.py @@ -0,0 +1 @@ +from card_test_empty_card import * diff --git a/tests/001_initial_check/test_001_initial_card.py b/tests/001_initial_check/test_001_initial_card.py new file mode 100644 index 0000000..ff9cda3 --- /dev/null +++ b/tests/001_initial_check/test_001_initial_card.py @@ -0,0 +1 @@ +from card_test_set_attr import * diff --git a/tests/010_kdfnone/test_010_adminfull_kdfnone.py b/tests/010_kdfnone/test_010_adminfull_kdfnone.py new file mode 100644 index 0000000..61fd57f --- /dev/null +++ b/tests/010_kdfnone/test_010_adminfull_kdfnone.py @@ -0,0 +1,2 @@ +from skip_if_kdfreq import * +from card_test_personalize_card_1 import * diff --git a/tests/010_kdfnone/test_011_adminfull_kdfnone.py b/tests/010_kdfnone/test_011_adminfull_kdfnone.py new file mode 100644 index 0000000..93fc68f --- /dev/null +++ b/tests/010_kdfnone/test_011_adminfull_kdfnone.py @@ -0,0 +1,2 @@ +from skip_if_kdfreq import * +from card_test_personalize_card_2 import * diff --git a/tests/010_kdfnone/test_012_adminfull_kdfnone.py b/tests/010_kdfnone/test_012_adminfull_kdfnone.py new file mode 100644 index 0000000..8e26aa2 --- /dev/null +++ b/tests/010_kdfnone/test_012_adminfull_kdfnone.py @@ -0,0 +1,2 @@ +from skip_if_kdfreq import * +from card_test_public_key_operations import * diff --git a/tests/010_kdfnone/test_013_adminfull_kdfnone.py b/tests/010_kdfnone/test_013_adminfull_kdfnone.py new file mode 100644 index 0000000..ccac7a6 --- /dev/null +++ b/tests/010_kdfnone/test_013_adminfull_kdfnone.py @@ -0,0 +1,2 @@ +from skip_if_kdfreq import * +from card_test_ds_counter2 import * diff --git a/tests/010_kdfnone/test_014_keygen_kdfnone.py b/tests/010_kdfnone/test_014_keygen_kdfnone.py new file mode 100644 index 0000000..aeba7c9 --- /dev/null +++ b/tests/010_kdfnone/test_014_keygen_kdfnone.py @@ -0,0 +1,24 @@ +""" +test_005_keygen.py - test key generation + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_if_kdfreq import * +from card_test_keygen import * diff --git a/tests/010_kdfnone/test_015_keygen_kdfnone.py b/tests/010_kdfnone/test_015_keygen_kdfnone.py new file mode 100644 index 0000000..f887bdb --- /dev/null +++ b/tests/010_kdfnone/test_015_keygen_kdfnone.py @@ -0,0 +1,24 @@ +""" +test_005_keygen.py - test key generation + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_if_kdfreq import * +from card_test_public_key_operations_kg import * diff --git a/tests/010_kdfnone/test_016_keygen_kdfnone.py b/tests/010_kdfnone/test_016_keygen_kdfnone.py new file mode 100644 index 0000000..fdfe410 --- /dev/null +++ b/tests/010_kdfnone/test_016_keygen_kdfnone.py @@ -0,0 +1,24 @@ +""" +test_005_keygen.py - test key generation + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_if_kdfreq import * +from card_test_ds_counter1 import * diff --git a/tests/010_kdfnone/test_017_adminfull_kdfnone.py b/tests/010_kdfnone/test_017_adminfull_kdfnone.py new file mode 100644 index 0000000..34e729e --- /dev/null +++ b/tests/010_kdfnone/test_017_adminfull_kdfnone.py @@ -0,0 +1,2 @@ +from skip_if_kdfreq import * +from card_test_personalize_reset import * diff --git a/tests/010_kdfnone/test_018_adminfull_kdfnone.py b/tests/010_kdfnone/test_018_adminfull_kdfnone.py new file mode 100644 index 0000000..aaf73b2 --- /dev/null +++ b/tests/010_kdfnone/test_018_adminfull_kdfnone.py @@ -0,0 +1,2 @@ +from skip_if_kdfreq import * +from card_test_remove_keys import * diff --git a/tests/010_kdfnone/test_019_adminfull_kdfnone.py b/tests/010_kdfnone/test_019_adminfull_kdfnone.py new file mode 100644 index 0000000..219a458 --- /dev/null +++ b/tests/010_kdfnone/test_019_adminfull_kdfnone.py @@ -0,0 +1,2 @@ +from skip_if_kdfreq import * +from card_test_reset_pw3 import * diff --git a/tests/010_kdfnone/test_040_adminless_kdfnone.py b/tests/010_kdfnone/test_040_adminless_kdfnone.py new file mode 100644 index 0000000..c6fb3c0 --- /dev/null +++ b/tests/010_kdfnone/test_040_adminless_kdfnone.py @@ -0,0 +1,25 @@ +""" +test_005_adminless_kdfnone.py - test admin-less mode + +Copyright (C) 2016, 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_if_kdfreq import * +from skip_gnuk_only_tests import * +from card_test_personalize_admin_less_1 import * diff --git a/tests/010_kdfnone/test_041_adminless_kdfnone.py b/tests/010_kdfnone/test_041_adminless_kdfnone.py new file mode 100644 index 0000000..9e864ba --- /dev/null +++ b/tests/010_kdfnone/test_041_adminless_kdfnone.py @@ -0,0 +1,25 @@ +""" +test_005_adminless_kdfnone.py - test admin-less mode + +Copyright (C) 2016, 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_if_kdfreq import * +from skip_gnuk_only_tests import * +from card_test_public_key_operations_alt import * diff --git a/tests/010_kdfnone/test_042_adminless_kdfnone.py b/tests/010_kdfnone/test_042_adminless_kdfnone.py new file mode 100644 index 0000000..5d93e0a --- /dev/null +++ b/tests/010_kdfnone/test_042_adminless_kdfnone.py @@ -0,0 +1,25 @@ +""" +test_005_adminless_kdfnone.py - test admin-less mode + +Copyright (C) 2016, 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_if_kdfreq import * +from skip_gnuk_only_tests import * +from card_test_ds_counter1 import * diff --git a/tests/010_kdfnone/test_043_adminless_kdfnone.py b/tests/010_kdfnone/test_043_adminless_kdfnone.py new file mode 100644 index 0000000..74d7da6 --- /dev/null +++ b/tests/010_kdfnone/test_043_adminless_kdfnone.py @@ -0,0 +1,25 @@ +""" +test_005_adminless_kdfnone.py - test admin-less mode + +Copyright (C) 2016, 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_if_kdfreq import * +from skip_gnuk_only_tests import * +from card_test_personalize_admin_less_2 import * diff --git a/tests/010_kdfnone/test_044_adminless_kdfnone.py b/tests/010_kdfnone/test_044_adminless_kdfnone.py new file mode 100644 index 0000000..a290b46 --- /dev/null +++ b/tests/010_kdfnone/test_044_adminless_kdfnone.py @@ -0,0 +1,25 @@ +""" +test_005_adminless_kdfnone.py - test admin-less mode + +Copyright (C) 2016, 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_if_kdfreq import * +from skip_gnuk_only_tests import * +from card_test_personalize_reset import * diff --git a/tests/010_kdfnone/test_045_adminless_kdfnone.py b/tests/010_kdfnone/test_045_adminless_kdfnone.py new file mode 100644 index 0000000..ca02bfb --- /dev/null +++ b/tests/010_kdfnone/test_045_adminless_kdfnone.py @@ -0,0 +1,25 @@ +""" +test_005_adminless_kdfnone.py - test admin-less mode + +Copyright (C) 2016, 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_if_kdfreq import * +from skip_gnuk_only_tests import * +from card_test_remove_keys import * diff --git a/tests/010_kdfnone/test_046_adminless_kdfnone.py b/tests/010_kdfnone/test_046_adminless_kdfnone.py new file mode 100644 index 0000000..5b0b496 --- /dev/null +++ b/tests/010_kdfnone/test_046_adminless_kdfnone.py @@ -0,0 +1,25 @@ +""" +test_005_adminless_kdfnone.py - test admin-less mode + +Copyright (C) 2016, 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_if_kdfreq import * +from skip_gnuk_only_tests import * +from card_test_reset_pw3 import * diff --git a/tests/020_kdffull/01_initial_check/test_050_adminfull_kdffull.py b/tests/020_kdffull/01_initial_check/test_050_adminfull_kdffull.py new file mode 100644 index 0000000..1347eb8 --- /dev/null +++ b/tests/020_kdffull/01_initial_check/test_050_adminfull_kdffull.py @@ -0,0 +1,23 @@ +""" +test_011_adminfull_kdffull.py - test KDF data object + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from card_test_kdf_full import * diff --git a/tests/020_kdffull/02_personalization/test_051_adminfull_kdffull.py b/tests/020_kdffull/02_personalization/test_051_adminfull_kdffull.py new file mode 100644 index 0000000..192240a --- /dev/null +++ b/tests/020_kdffull/02_personalization/test_051_adminfull_kdffull.py @@ -0,0 +1,23 @@ +""" +test_011_adminfull_kdffull.py - test KDF data object + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from card_test_personalize_card_1 import * diff --git a/tests/020_kdffull/03_key_import/001_rsa2k/test_rsa2k_import.py b/tests/020_kdffull/03_key_import/001_rsa2k/test_rsa2k_import.py new file mode 100644 index 0000000..1cceb27 --- /dev/null +++ b/tests/020_kdffull/03_key_import/001_rsa2k/test_rsa2k_import.py @@ -0,0 +1 @@ +from card_test_ki_pko_dsc_rsa2k import * diff --git a/tests/020_kdffull/03_key_import/002_nistp256r1/test_nistp256r1_import.py b/tests/020_kdffull/03_key_import/002_nistp256r1/test_nistp256r1_import.py new file mode 100644 index 0000000..29970dc --- /dev/null +++ b/tests/020_kdffull/03_key_import/002_nistp256r1/test_nistp256r1_import.py @@ -0,0 +1 @@ +from card_test_ki_pko_dsc_nistp256r1 import * diff --git a/tests/020_kdffull/03_key_import/003_brainpoolp256r1/test_brainpoolp256r1_import.py b/tests/020_kdffull/03_key_import/003_brainpoolp256r1/test_brainpoolp256r1_import.py new file mode 100644 index 0000000..890041a --- /dev/null +++ b/tests/020_kdffull/03_key_import/003_brainpoolp256r1/test_brainpoolp256r1_import.py @@ -0,0 +1 @@ +from card_test_ki_pko_dsc_brainpoolp256r1 import * diff --git a/tests/020_kdffull/03_key_import/004_secp256k1/test_secp256k1_import.py b/tests/020_kdffull/03_key_import/004_secp256k1/test_secp256k1_import.py new file mode 100644 index 0000000..ba2a52f --- /dev/null +++ b/tests/020_kdffull/03_key_import/004_secp256k1/test_secp256k1_import.py @@ -0,0 +1 @@ +from card_test_ki_pko_dsc_secp256k1 import * diff --git a/tests/020_kdffull/03_key_import/005_nistp384r1/test_nistp384r1_import.py b/tests/020_kdffull/03_key_import/005_nistp384r1/test_nistp384r1_import.py new file mode 100644 index 0000000..5fa2872 --- /dev/null +++ b/tests/020_kdffull/03_key_import/005_nistp384r1/test_nistp384r1_import.py @@ -0,0 +1 @@ +from card_test_ki_pko_dsc_nistp384r1 import * diff --git a/tests/020_kdffull/03_key_import/006_brainpoolp384r1/test_brainpoolp384r1_import.py b/tests/020_kdffull/03_key_import/006_brainpoolp384r1/test_brainpoolp384r1_import.py new file mode 100644 index 0000000..c3e38b4 --- /dev/null +++ b/tests/020_kdffull/03_key_import/006_brainpoolp384r1/test_brainpoolp384r1_import.py @@ -0,0 +1 @@ +from card_test_ki_pko_dsc_brainpoolp384r1 import * diff --git a/tests/020_kdffull/03_key_import/008_nistp521r1/test_nistp521r1_import.py b/tests/020_kdffull/03_key_import/008_nistp521r1/test_nistp521r1_import.py new file mode 100644 index 0000000..eb007b6 --- /dev/null +++ b/tests/020_kdffull/03_key_import/008_nistp521r1/test_nistp521r1_import.py @@ -0,0 +1 @@ +from card_test_ki_pko_dsc_nistp521r1 import * diff --git a/tests/020_kdffull/03_key_import/009_brainpoolp512r1/test_brainpoolp521r1_import.py b/tests/020_kdffull/03_key_import/009_brainpoolp512r1/test_brainpoolp521r1_import.py new file mode 100644 index 0000000..553664a --- /dev/null +++ b/tests/020_kdffull/03_key_import/009_brainpoolp512r1/test_brainpoolp521r1_import.py @@ -0,0 +1 @@ +from card_test_ki_pko_dsc_brainpoolp512r1 import * diff --git a/tests/020_kdffull/03_key_import/010_curve25519/test_curve25519_import.py b/tests/020_kdffull/03_key_import/010_curve25519/test_curve25519_import.py new file mode 100644 index 0000000..dece1a9 --- /dev/null +++ b/tests/020_kdffull/03_key_import/010_curve25519/test_curve25519_import.py @@ -0,0 +1 @@ +from card_test_ki_pko_dsc_curve25519 import * diff --git a/tests/020_kdffull/04_keygen/001_rsa2k/test_rsa2k_keygen.py b/tests/020_kdffull/04_keygen/001_rsa2k/test_rsa2k_keygen.py new file mode 100644 index 0000000..2bd998b --- /dev/null +++ b/tests/020_kdffull/04_keygen/001_rsa2k/test_rsa2k_keygen.py @@ -0,0 +1 @@ +from card_test_kg_pko_dsc_rsa2k import * diff --git a/tests/020_kdffull/04_keygen/002_nistp256r1/test_nistp256r1_keygen.py b/tests/020_kdffull/04_keygen/002_nistp256r1/test_nistp256r1_keygen.py new file mode 100644 index 0000000..29970dc --- /dev/null +++ b/tests/020_kdffull/04_keygen/002_nistp256r1/test_nistp256r1_keygen.py @@ -0,0 +1 @@ +from card_test_ki_pko_dsc_nistp256r1 import * diff --git a/tests/020_kdffull/04_keygen/003_brainpoolp256r1/test_brainpoolp256r1_keygen.py b/tests/020_kdffull/04_keygen/003_brainpoolp256r1/test_brainpoolp256r1_keygen.py new file mode 100644 index 0000000..c36c41d --- /dev/null +++ b/tests/020_kdffull/04_keygen/003_brainpoolp256r1/test_brainpoolp256r1_keygen.py @@ -0,0 +1 @@ +from card_test_kg_pko_dsc_brainpoolp256r1 import * diff --git a/tests/020_kdffull/04_keygen/004_secp256k1/test_secp256k1_keygen.py b/tests/020_kdffull/04_keygen/004_secp256k1/test_secp256k1_keygen.py new file mode 100644 index 0000000..5998378 --- /dev/null +++ b/tests/020_kdffull/04_keygen/004_secp256k1/test_secp256k1_keygen.py @@ -0,0 +1 @@ +from card_test_kg_pko_dsc_secp256k1 import * diff --git a/tests/020_kdffull/04_keygen/005_nistp384r1/test_nistp384r1_keygen.py b/tests/020_kdffull/04_keygen/005_nistp384r1/test_nistp384r1_keygen.py new file mode 100644 index 0000000..ce5716f --- /dev/null +++ b/tests/020_kdffull/04_keygen/005_nistp384r1/test_nistp384r1_keygen.py @@ -0,0 +1 @@ +from card_test_kg_pko_dsc_nistp384r1 import * diff --git a/tests/020_kdffull/04_keygen/006_brainpoolp384r1/test_brainpoolp384r1_keygen.py b/tests/020_kdffull/04_keygen/006_brainpoolp384r1/test_brainpoolp384r1_keygen.py new file mode 100644 index 0000000..fbb2c45 --- /dev/null +++ b/tests/020_kdffull/04_keygen/006_brainpoolp384r1/test_brainpoolp384r1_keygen.py @@ -0,0 +1 @@ +from card_test_kg_pko_dsc_brainpoolp384r1 import * diff --git a/tests/020_kdffull/04_keygen/008_nistp521r1/test_nistp521r1_keygen.py b/tests/020_kdffull/04_keygen/008_nistp521r1/test_nistp521r1_keygen.py new file mode 100644 index 0000000..c502bcf --- /dev/null +++ b/tests/020_kdffull/04_keygen/008_nistp521r1/test_nistp521r1_keygen.py @@ -0,0 +1 @@ +from card_test_kg_pko_dsc_nistp521r1 import * diff --git a/tests/020_kdffull/04_keygen/009_brainpoolp512r1/test_brainpoolp521r1_keygen.py b/tests/020_kdffull/04_keygen/009_brainpoolp512r1/test_brainpoolp521r1_keygen.py new file mode 100644 index 0000000..1c5b49c --- /dev/null +++ b/tests/020_kdffull/04_keygen/009_brainpoolp512r1/test_brainpoolp521r1_keygen.py @@ -0,0 +1 @@ +from card_test_kg_pko_dsc_brainpoolp512r1 import * diff --git a/tests/020_kdffull/04_keygen/010_curve25519/test_curve25519_keygen.py b/tests/020_kdffull/04_keygen/010_curve25519/test_curve25519_keygen.py new file mode 100644 index 0000000..dece1a9 --- /dev/null +++ b/tests/020_kdffull/04_keygen/010_curve25519/test_curve25519_keygen.py @@ -0,0 +1 @@ +from card_test_ki_pko_dsc_curve25519 import * diff --git a/tests/020_kdffull/05_finalize/test_000_reset_to_TEST4.py b/tests/020_kdffull/05_finalize/test_000_reset_to_TEST4.py new file mode 100644 index 0000000..369871d --- /dev/null +++ b/tests/020_kdffull/05_finalize/test_000_reset_to_TEST4.py @@ -0,0 +1,15 @@ +import pytest +from card_const import * +from constants_for_test import * + +def test_setup_pw1_4(card): + r = card.change_passwd(1, FACTORY_PASSPHRASE_PW1, PW1_TEST4) + assert r + +def test_verify_pw1_4(card): + v = card.verify(1, PW1_TEST4) + assert v + +def test_verify_pw1_4_2(card): + v = card.verify(2, PW1_TEST4) + assert v diff --git a/tests/020_kdffull/05_finalize/test_057_adminfull_kdffull.py b/tests/020_kdffull/05_finalize/test_057_adminfull_kdffull.py new file mode 100644 index 0000000..e68921e --- /dev/null +++ b/tests/020_kdffull/05_finalize/test_057_adminfull_kdffull.py @@ -0,0 +1,23 @@ +""" +test_011_adminfull_kdffull.py - test KDF data object + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from card_test_personalize_reset import * diff --git a/tests/020_kdffull/05_finalize/test_058_adminfull_kdffull.py b/tests/020_kdffull/05_finalize/test_058_adminfull_kdffull.py new file mode 100644 index 0000000..3a1ad03 --- /dev/null +++ b/tests/020_kdffull/05_finalize/test_058_adminfull_kdffull.py @@ -0,0 +1,23 @@ +""" +test_011_adminfull_kdffull.py - test KDF data object + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from card_test_remove_keys import * diff --git a/tests/020_kdffull/05_finalize/test_059_adminfull_kdffull.py b/tests/020_kdffull/05_finalize/test_059_adminfull_kdffull.py new file mode 100644 index 0000000..a96dcdc --- /dev/null +++ b/tests/020_kdffull/05_finalize/test_059_adminfull_kdffull.py @@ -0,0 +1,23 @@ +""" +test_011_adminfull_kdffull.py - test KDF data object + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from card_test_reset_pw3 import * diff --git a/tests/020_kdffull/conftest.py b/tests/020_kdffull/conftest.py new file mode 100644 index 0000000..9e0109c --- /dev/null +++ b/tests/020_kdffull/conftest.py @@ -0,0 +1 @@ +from skip_if_no_kdf_support import * diff --git a/tests/030_kdfsingle/test_060_adminfull_kdfsingle.py b/tests/030_kdfsingle/test_060_adminfull_kdfsingle.py new file mode 100644 index 0000000..419392d --- /dev/null +++ b/tests/030_kdfsingle/test_060_adminfull_kdfsingle.py @@ -0,0 +1,24 @@ +""" +test_016_adminfull_kdfsingle.py - test KDF data object + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_gnuk_only_tests import * +from card_test_kdf_single import * diff --git a/tests/030_kdfsingle/test_061_adminfull_kdfsingle.py b/tests/030_kdfsingle/test_061_adminfull_kdfsingle.py new file mode 100644 index 0000000..3e7dbf2 --- /dev/null +++ b/tests/030_kdfsingle/test_061_adminfull_kdfsingle.py @@ -0,0 +1,25 @@ +""" +test_016_adminfull_kdfsingle.py - test KDF data object + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_gnuk_only_tests import * +from card_test_personalize_card_1 import * +from card_test_personalize_card_2 import * diff --git a/tests/030_kdfsingle/test_062_adminfull_kdfsingle.py b/tests/030_kdfsingle/test_062_adminfull_kdfsingle.py new file mode 100644 index 0000000..bbde30b --- /dev/null +++ b/tests/030_kdfsingle/test_062_adminfull_kdfsingle.py @@ -0,0 +1,24 @@ +""" +test_016_adminfull_kdfsingle.py - test KDF data object + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_gnuk_only_tests import * +from card_test_public_key_operations import * diff --git a/tests/030_kdfsingle/test_063_adminfull_kdfsingle.py b/tests/030_kdfsingle/test_063_adminfull_kdfsingle.py new file mode 100644 index 0000000..7bc9e09 --- /dev/null +++ b/tests/030_kdfsingle/test_063_adminfull_kdfsingle.py @@ -0,0 +1,24 @@ +""" +test_016_adminfull_kdfsingle.py - test KDF data object + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_gnuk_only_tests import * +from card_test_ds_counter2 import * diff --git a/tests/030_kdfsingle/test_064_adminfull_kdfsingle.py b/tests/030_kdfsingle/test_064_adminfull_kdfsingle.py new file mode 100644 index 0000000..13b9cfd --- /dev/null +++ b/tests/030_kdfsingle/test_064_adminfull_kdfsingle.py @@ -0,0 +1,24 @@ +""" +test_016_adminfull_kdfsingle.py - test KDF data object + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_gnuk_only_tests import * +from card_test_personalize_reset import * diff --git a/tests/030_kdfsingle/test_065_adminfull_kdfsingle.py b/tests/030_kdfsingle/test_065_adminfull_kdfsingle.py new file mode 100644 index 0000000..2a26aea --- /dev/null +++ b/tests/030_kdfsingle/test_065_adminfull_kdfsingle.py @@ -0,0 +1,24 @@ +""" +test_016_adminfull_kdfsingle.py - test KDF data object + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_gnuk_only_tests import * +from card_test_remove_keys import * diff --git a/tests/030_kdfsingle/test_066_adminfull_kdfsingle.py b/tests/030_kdfsingle/test_066_adminfull_kdfsingle.py new file mode 100644 index 0000000..7de0685 --- /dev/null +++ b/tests/030_kdfsingle/test_066_adminfull_kdfsingle.py @@ -0,0 +1,24 @@ +""" +test_016_adminfull_kdfsingle.py - test KDF data object + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_gnuk_only_tests import * +from card_test_reset_pw3 import * diff --git a/tests/030_kdfsingle/test_070_adminless_kdfsingle.py b/tests/030_kdfsingle/test_070_adminless_kdfsingle.py new file mode 100644 index 0000000..f0d96e9 --- /dev/null +++ b/tests/030_kdfsingle/test_070_adminless_kdfsingle.py @@ -0,0 +1,2 @@ +from skip_gnuk_only_tests import * +from card_test_personalize_admin_less_1 import * diff --git a/tests/030_kdfsingle/test_071_adminless_kdfsingle.py b/tests/030_kdfsingle/test_071_adminless_kdfsingle.py new file mode 100644 index 0000000..941ce0c --- /dev/null +++ b/tests/030_kdfsingle/test_071_adminless_kdfsingle.py @@ -0,0 +1,2 @@ +from skip_gnuk_only_tests import * +from card_test_public_key_operations_alt import * diff --git a/tests/030_kdfsingle/test_072_adminless_kdfsingle.py b/tests/030_kdfsingle/test_072_adminless_kdfsingle.py new file mode 100644 index 0000000..6d65d38 --- /dev/null +++ b/tests/030_kdfsingle/test_072_adminless_kdfsingle.py @@ -0,0 +1,2 @@ +from skip_gnuk_only_tests import * +from card_test_ds_counter1 import * diff --git a/tests/030_kdfsingle/test_073_adminless_kdfsingle.py b/tests/030_kdfsingle/test_073_adminless_kdfsingle.py new file mode 100644 index 0000000..21a8768 --- /dev/null +++ b/tests/030_kdfsingle/test_073_adminless_kdfsingle.py @@ -0,0 +1,2 @@ +from skip_gnuk_only_tests import * +from card_test_personalize_admin_less_2 import * diff --git a/tests/030_kdfsingle/test_074_adminless_kdfsingle.py b/tests/030_kdfsingle/test_074_adminless_kdfsingle.py new file mode 100644 index 0000000..0cd0538 --- /dev/null +++ b/tests/030_kdfsingle/test_074_adminless_kdfsingle.py @@ -0,0 +1,2 @@ +from skip_gnuk_only_tests import * +from card_test_personalize_reset import * diff --git a/tests/030_kdfsingle/test_075_adminless_kdfsingle.py b/tests/030_kdfsingle/test_075_adminless_kdfsingle.py new file mode 100644 index 0000000..f15e1eb --- /dev/null +++ b/tests/030_kdfsingle/test_075_adminless_kdfsingle.py @@ -0,0 +1,2 @@ +from skip_gnuk_only_tests import * +from card_test_remove_keys import * diff --git a/tests/030_kdfsingle/test_076_adminless_kdfsingle.py b/tests/030_kdfsingle/test_076_adminless_kdfsingle.py new file mode 100644 index 0000000..86fcfb7 --- /dev/null +++ b/tests/030_kdfsingle/test_076_adminless_kdfsingle.py @@ -0,0 +1,2 @@ +from skip_gnuk_only_tests import * +from card_test_reset_pw3 import * diff --git a/tests/090_finalize/test_080_kdf_none.py b/tests/090_finalize/test_080_kdf_none.py new file mode 100644 index 0000000..655384e --- /dev/null +++ b/tests/090_finalize/test_080_kdf_none.py @@ -0,0 +1,42 @@ +""" +test_025_kdf_none.py - test KDF data object + +Copyright (C) 2018 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from skip_if_no_kdf_support import * + +from card_const import * +from constants_for_test import * + +def test_verify_pw3(card): + v = card.verify(3, FACTORY_PASSPHRASE_PW3) + assert v + +def test_kdf_put_none(card): + if card.is_yubikey: + KDF_SETUP_NONE=b"\x81\x01\x00" + else: + KDF_SETUP_NONE=b"" + r = card.configure_kdf(KDF_SETUP_NONE) + assert r + +def test_verify_pw3_1(card): + v = card.verify(3, FACTORY_PASSPHRASE_PW3) + assert v diff --git a/tests/090_finalize/test_091_reset_attr.py b/tests/090_finalize/test_091_reset_attr.py new file mode 100644 index 0000000..d12a05e --- /dev/null +++ b/tests/090_finalize/test_091_reset_attr.py @@ -0,0 +1,23 @@ +""" +test_091_reset_attr.py - test resetting key attributes + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from card_test_reset_attr import * diff --git a/tests/brainpoolp256r1_keys.py b/tests/brainpoolp256r1_keys.py new file mode 100644 index 0000000..71be20a --- /dev/null +++ b/tests/brainpoolp256r1_keys.py @@ -0,0 +1,148 @@ +from time import time +from struct import pack +from hashlib import sha1, sha256 +from pk_signed_mpi_with_libgcrypt import PK_libgcrypt +from card_const import KEY_ATTRIBUTES_ECDH_BRAINPOOLP256R1, KEY_ATTRIBUTES_ECDSA_BRAINPOOLP256R1 +lg_bp256 = PK_libgcrypt(32, "brainpoolP256r1") + + +class PK_Crypto(object): + @staticmethod + def pk_from_pk_info(pk_info): + return pk_info[5:] + + @staticmethod + def compute_digestinfo(msg): + return sha256(msg).digest() + + @staticmethod + def enc_data(enc_info): + return b'\xa6\x46\x7f\x49\x43\x86\x41' + enc_info[1] + + @staticmethod + def enc_check(enc_info, s): + point = enc_info[0] + # It's 04 || X || Y, extract X + return point[1:33] == s + + + def __init__(self, keyno=None, pk_info=None, data=None): + if keyno == None: + # Just for name space + return + + self.keyno = keyno + self.for_encryption = (self.keyno == 1) + self.timestamp = pack('>I', int(time())) + if pk_info: + # Public part only (no private data) from card + self.q = pk_info[5:] + else: + # Private part (in big endian) + self.d = data[0] + self.q = data[1] + self.fpr = self.calc_fpr() + + def calc_fpr(self): + m_len = 6 + 2 + 65 + ver = b'\x04' + algo = b'\x12' if self.for_encryption else b'\x16' + m = b'\x99' + pack('>H', m_len) + ver + self.timestamp + algo \ + + pack('>H', 512+3) + self.q + return sha1(m).digest() + + def build_privkey_template(self, is_yubikey): + openpgp_keyno = self.keyno + 1 + if openpgp_keyno == 1: + keyspec = b'\xb6' + elif openpgp_keyno == 2: + keyspec = b'\xb8' + else: + keyspec = b'\xa4' + key_template = b'\x92\x20' + exthdr = keyspec + b'\x00' + b'\x7f\x48' + b'\x02' + key_template + suffix = b'\x5f\x48' + b'\x20' + return b'\x4d' + b'\x2a' + exthdr + suffix + self.d + + def compute_signature(self, digestinfo): + return lg_bp256.call_pk_sign(self.d, digestinfo) + + def verify_signature(self, digestinfo, sig): + return lg_bp256.call_pk_verify(self.q, digestinfo, sig) + + def encrypt(self, plaintext): + # Do ECDH + return lg_bp256.call_pk_encrypt(self.q, plaintext) + + def get_fpr(self): + return self.fpr + + def get_timestamp(self): + return self.timestamp + + def get_pk(self): + return self.q + +key = [ None, None, None ] + +# https://datatracker.ietf.org/doc/html/rfc6932#appendix-A.2 + +bp256_data0 = ( + b'\x04\x1e\xb8\xb1\xe2\xbc\x68\x1b\xce\x8e\x39\x96\x3b\x2e\x9f\xc4' + b'\x15\xb0\x52\x83\x31\x3d\xd1\xa8\xbc\xc0\x55\xf1\x1a\xe4\x96\x99', + b'\x04' + b'\x78\x02\x84\x96\xb5\xec\xaa\xb3\xc8\xb6\xc1\x2e\x45\xdb\x1e\x02' + b'\xc9\xe4\xd2\x6b\x41\x13\xbc\x4f\x01\x5f\x60\xc5\xcc\xc0\xd2\x06' + b'\xa2\xae\x17\x62\xa3\x83\x1c\x1d\x20\xf0\x3f\x8d\x1e\x3c\x0c\x39' + b'\xaf\xe6\xf0\x9b\x4d\x44\xbb\xe8\x0c\xd1\x00\x98\x7b\x05\xf9\x2b' +) + +bp256_data2 = ( + b'\x06\xf5\x24\x0e\xac\xdb\x98\x37\xbc\x96\xd4\x82\x74\xc8\xaa\x83' + b'\x4b\x6c\x87\xba\x9c\xc3\xee\xdd\x81\xf9\x9a\x16\xb8\xd8\x04\xd3', + b'\x04' + b'\x8e\x07\xe2\x19\xba\x58\x89\x16\xc5\xb0\x6a\xa3\x0a\x2f\x46\x4c' + b'\x2f\x2a\xcf\xc1\x61\x0a\x3b\xe2\xfb\x24\x0b\x63\x53\x41\xf0\xdb' + b'\x14\x8e\xa1\xd7\xd1\xe7\xe5\x4b\x95\x55\xb6\xc9\xac\x90\x62\x9c' + b'\x18\xb6\x3b\xee\x5d\x7a\xa6\x94\x9e\xbb\xf4\x7b\x24\xfd\xe4\x0d' +) + +# https://tools.ietf.org/html/rfc7027#appendix-A.1 +bp256_data1 = ( + b'\x81\xdb\x1e\xe1\x00\x15\x0f\xf2\xea\x33\x8d\x70\x82\x71\xbe\x38' + b'\x30\x0c\xb5\x42\x41\xd7\x99\x50\xf7\x7b\x06\x30\x39\x80\x4f\x1d', + b'\x04' + b'\x44\x10\x6e\x91\x3f\x92\xbc\x02\xa1\x70\x5d\x99\x53\xa8\x41\x4d' + b'\xb9\x5e\x1a\xaa\x49\xe8\x1d\x9e\x85\xf9\x29\xa8\xe3\x10\x0b\xe5' + b'\x8a\xb4\x84\x6f\x11\xca\xcc\xb7\x3c\xe4\x9c\xbd\xd1\x20\xf5\xa9' + b'\x00\xa6\x9f\xd3\x2c\x27\x22\x23\xf7\x89\xef\x10\xeb\x08\x9b\xdc' +) + +key[0] = PK_Crypto(0, data=bp256_data0) +key[1] = PK_Crypto(1, data=bp256_data1) +key[2] = PK_Crypto(2, data=bp256_data2) + +PLAIN_TEXT0=b"In this test, we verify card generated result by libgcrypt." +PLAIN_TEXT1=b"Signature is non-deterministic (it uses nonce K internally)." +PLAIN_TEXT2=b"We don't use brainpoolp256r1 test vectors (it specifies K of ECDSA)." +PLAIN_TEXT3=b"NOTE: Our test is not for ECDSA implementation itself." + +ENCRYPT_TEXT0 = sha256(b"encrypt me please").digest() +ENCRYPT_TEXT1 = sha256(b"encrypt me please, another").digest() +ENCRYPT_TEXT2 = sha256(b"encrypt me please, the other").digest() + +test_vector = { + 'sign_0' : PLAIN_TEXT0, + 'sign_1' : PLAIN_TEXT1, + 'auth_0' : PLAIN_TEXT2, + 'auth_1' : PLAIN_TEXT3, + 'decrypt_0' : ENCRYPT_TEXT0, + 'decrypt_1' : ENCRYPT_TEXT1, + 'encrypt_0' : ENCRYPT_TEXT2, +} + +brainpoolp256r1_pk = PK_Crypto() +brainpoolp256r1_pk.test_vector = test_vector +brainpoolp256r1_pk.key_list = key +brainpoolp256r1_pk.key_attr_list = [KEY_ATTRIBUTES_ECDSA_BRAINPOOLP256R1, KEY_ATTRIBUTES_ECDH_BRAINPOOLP256R1, KEY_ATTRIBUTES_ECDSA_BRAINPOOLP256R1] +brainpoolp256r1_pk.PK_Crypto = PK_Crypto diff --git a/tests/brainpoolp384r1_keys.py b/tests/brainpoolp384r1_keys.py new file mode 100644 index 0000000..3b1e27a --- /dev/null +++ b/tests/brainpoolp384r1_keys.py @@ -0,0 +1,157 @@ +from time import time +from struct import pack +from hashlib import sha1, sha384 +from pk_signed_mpi_with_libgcrypt import PK_libgcrypt +from card_const import KEY_ATTRIBUTES_ECDH_BRAINPOOLP384R1, KEY_ATTRIBUTES_ECDSA_BRAINPOOLP384R1 + +lg_bp384 = PK_libgcrypt(48, "brainpoolP384r1") + +class PK_Crypto(object): + @staticmethod + def pk_from_pk_info(pk_info): + return pk_info[5:] + + @staticmethod + def compute_digestinfo(msg): + return sha384(msg).digest() + + @staticmethod + def enc_data(enc_info): + return b'\xa6\x66\x7f\x49\x63\x86\x61' + enc_info[1] + + @staticmethod + def enc_check(enc_info, s): + point = enc_info[0] + # It's 04 || X || Y, extract X + return point[1:49] == s + + + def __init__(self, keyno=None, pk_info=None, data=None): + if keyno == None: + # Just for name space + return + + self.keyno = keyno + self.for_encryption = (self.keyno == 1) + self.timestamp = pack('>I', int(time())) + if pk_info: + # Public part only (no private data) from card + self.q = pk_info[5:] + else: + # Private part (in big endian) + self.d = data[0] + self.q = data[1] + self.fpr = self.calc_fpr() + + def calc_fpr(self): + m_len = 6 + 2 + 97 + ver = b'\x04' + algo = b'\x12' if self.for_encryption else b'\x16' + m = b'\x99' + pack('>H', m_len) + ver + self.timestamp + algo \ + + pack('>H', 768+3) + self.q + return sha1(m).digest() + + def build_privkey_template(self, is_yubikey): + openpgp_keyno = self.keyno + 1 + if openpgp_keyno == 1: + keyspec = b'\xb6' + elif openpgp_keyno == 2: + keyspec = b'\xb8' + else: + keyspec = b'\xa4' + key_template = b'\x92\x30' + exthdr = keyspec + b'\x00' + b'\x7f\x48' + b'\x02' + key_template + suffix = b'\x5f\x48' + b'\x30' + return b'\x4d' + b'\x3a' + exthdr + suffix + self.d + + def compute_signature(self, digestinfo): + return lg_bp384.call_pk_sign(self.d, digestinfo) + + def verify_signature(self, digestinfo, sig): + return lg_bp384.call_pk_verify(self.q, digestinfo, sig) + + def encrypt(self, plaintext): + # Do ECDH + return lg_bp384.call_pk_encrypt(self.q, plaintext) + + def get_fpr(self): + return self.fpr + + def get_timestamp(self): + return self.timestamp + + def get_pk(self): + return self.q + +key = [ None, None, None ] + +# https://datatracker.ietf.org/doc/html/rfc6932#appendix-A.3 + +bp384_data0 = ( + b'\x01\x4e\xc0\x75\x5b\x78\x59\x4b\xa4\x7f\xb0\xa5\x6f\x61\x73\x04' + b'\x5b\x43\x31\xe7\x4b\xa1\xa6\xf4\x73\x22\xe7\x0d\x79\xd8\x28\xd9' + b'\x7e\x09\x58\x84\xca\x72\xb7\x3f\xda\xbd\x59\x10\xdf\x0f\xa7\x6a', + b'\x04' + b'\x45\xcb\x26\xe4\x38\x4d\xaf\x6f\xb7\x76\x88\x53\x07\xb9\xa3\x8b' + b'\x7a\xd1\xb5\xc6\x92\xe0\xc3\x2f\x01\x25\x33\x27\x78\xf3\xb8\xd3' + b'\xf5\x0c\xa3\x58\x09\x9b\x30\xde\xb5\xee\x69\xa9\x5c\x05\x8b\x4e' + b'\x81\x73\xa1\xc5\x4a\xff\xa7\xe7\x81\xd0\xe1\xe1\xd1\x2c\x0d\xc2' + b'\xb7\x4f\x4d\xf5\x8e\x4a\x4e\x3a\xf7\x02\x6c\x5d\x32\xdc\x53\x0a' + b'\x2c\xd8\x9c\x85\x9b\xb4\xb4\xb7\x68\x49\x7f\x49\xab\x8c\xc8\x59' +) + +bp384_data2 = ( + b'\x6b\x46\x1c\xb7\x9b\xd0\xea\x51\x9a\x87\xd6\x82\x88\x15\xd8\xce' + b'\x7c\xd9\xb3\xca\xa0\xb5\xa8\x26\x2c\xbc\xd5\x50\xa0\x15\xc9\x00' + b'\x95\xb9\x76\xf3\x52\x99\x57\x50\x6e\x12\x24\xa8\x61\x71\x1d\x54', + b'\x04' + b'\x01\xbf\x92\xa9\x2e\xe4\xbe\x8d\xed\x1a\x91\x11\x25\xc2\x09\xb0' + b'\x3f\x99\xe3\x16\x1c\xfc\xc9\x86\xdc\x77\x11\x38\x3f\xc3\x0a\xf9' + b'\xce\x28\xca\x33\x86\xd5\x9e\x2c\x8d\x72\xce\x1e\x7b\x46\x66\xe8' + b'\x32\x89\xc4\xa3\xa4\xfe\xe0\x35\xe3\x9b\xdb\x88\x5d\x50\x9d\x22' + b'\x4a\x14\x2f\xf9\xfb\xcc\x5c\xfe\x5c\xcb\xb3\x02\x68\xee\x47\x48' + b'\x7e\xd8\x04\x48\x58\xd3\x1d\x84\x8f\x7a\x95\xc6\x35\xa3\x47\xac' +) + +# https://tools.ietf.org/html/rfc7027#appendix-A.2 +bp384_data1 = ( + b'\x1e\x20\xf5\xe0\x48\xa5\x88\x6f\x1f\x15\x7c\x74\xe9\x1b\xde\x2b' + b'\x98\xc8\xb5\x2d\x58\xe5\x00\x3d\x57\x05\x3f\xc4\xb0\xbd\x65\xd6' + b'\xf1\x5e\xb5\xd1\xee\x16\x10\xdf\x87\x07\x95\x14\x36\x27\xd0\x42', + b'\x04' + b'\x68\xb6\x65\xdd\x91\xc1\x95\x80\x06\x50\xcd\xd3\x63\xc6\x25\xf4' + b'\xe7\x42\xe8\x13\x46\x67\xb7\x67\xb1\xb4\x76\x79\x35\x88\xf8\x85' + b'\xab\x69\x8c\x85\x2d\x4a\x6e\x77\xa2\x52\xd6\x38\x0f\xca\xf0\x68' + b'\x55\xbc\x91\xa3\x9c\x9e\xc0\x1d\xee\x36\x01\x7b\x7d\x67\x3a\x93' + b'\x12\x36\xd2\xf1\xf5\xc8\x39\x42\xd0\x49\xe3\xfa\x20\x60\x74\x93' + b'\xe0\xd0\x38\xff\x2f\xd3\x0c\x2a\xb6\x7d\x15\xc8\x5f\x7f\xaa\x59' +) + +key[0] = PK_Crypto(0, data=bp384_data0) +key[1] = PK_Crypto(1, data=bp384_data1) +key[2] = PK_Crypto(2, data=bp384_data2) + +PLAIN_TEXT0=b"In this test, we verify card generated result by libgcrypt." +PLAIN_TEXT1=b"Signature is non-deterministic (it uses nonce K internally)." +PLAIN_TEXT2=b"We don't use brainpoolp384r1 test vectors (it specifies K of ECDSA)." +PLAIN_TEXT3=b"NOTE: Our test is not for ECDSA implementation itself." + +ENCRYPT_TEXT0 = sha384(b"encrypt me please").digest() +ENCRYPT_TEXT1 = sha384(b"encrypt me please, another").digest() +ENCRYPT_TEXT2 = sha384(b"encrypt me please, the other").digest() + +test_vector = { + 'sign_0' : PLAIN_TEXT0, + 'sign_1' : PLAIN_TEXT1, + 'auth_0' : PLAIN_TEXT2, + 'auth_1' : PLAIN_TEXT3, + 'decrypt_0' : ENCRYPT_TEXT0, + 'decrypt_1' : ENCRYPT_TEXT1, + 'encrypt_0' : ENCRYPT_TEXT2, +} + +brainpoolp384r1_pk = PK_Crypto() +brainpoolp384r1_pk.test_vector = test_vector +brainpoolp384r1_pk.key_list = key +brainpoolp384r1_pk.key_attr_list = [KEY_ATTRIBUTES_ECDSA_BRAINPOOLP384R1, KEY_ATTRIBUTES_ECDH_BRAINPOOLP384R1, KEY_ATTRIBUTES_ECDSA_BRAINPOOLP384R1] +brainpoolp384r1_pk.PK_Crypto = PK_Crypto diff --git a/tests/brainpoolp512r1_keys.py b/tests/brainpoolp512r1_keys.py new file mode 100644 index 0000000..ab9b278 --- /dev/null +++ b/tests/brainpoolp512r1_keys.py @@ -0,0 +1,166 @@ +from time import time +from struct import pack +from hashlib import sha1, sha512 +from pk_signed_mpi_with_libgcrypt import PK_libgcrypt +from card_const import KEY_ATTRIBUTES_ECDH_BRAINPOOLP512R1, KEY_ATTRIBUTES_ECDSA_BRAINPOOLP512R1 + +lg_bp512 = PK_libgcrypt(64, "brainpoolP512r1") + +class PK_Crypto(object): + @staticmethod + def pk_from_pk_info(pk_info): + return pk_info[7:] + + @staticmethod + def compute_digestinfo(msg): + return sha512(msg).digest() + + @staticmethod + def enc_data(enc_info): + return b'\xa6\x81\x88\x7f\x49\x81\x84\x86\x81\x81' + enc_info[1] + + @staticmethod + def enc_check(enc_info, s): + point = enc_info[0] + # It's 04 || X || Y, extract X + return point[1:65] == s + + + def __init__(self, keyno=None, pk_info=None, data=None): + if keyno == None: + # Just for name space + return + + self.keyno = keyno + self.for_encryption = (self.keyno == 1) + self.timestamp = pack('>I', int(time())) + if pk_info: + # Public part only (no private data) from card + self.q = pk_info[7:] + else: + # Private part (in big endian) + self.d = data[0] + self.q = data[1] + self.fpr = self.calc_fpr() + + def calc_fpr(self): + m_len = 6 + 2 + 129 + ver = b'\x04' + algo = b'\x12' if self.for_encryption else b'\x16' + m = b'\x99' + pack('>H', m_len) + ver + self.timestamp + algo \ + + pack('>H', 1024+3) + self.q + return sha1(m).digest() + + def build_privkey_template(self, is_yubikey): + openpgp_keyno = self.keyno + 1 + if openpgp_keyno == 1: + keyspec = b'\xb6' + elif openpgp_keyno == 2: + keyspec = b'\xb8' + else: + keyspec = b'\xa4' + key_template = b'\x92\x40' + exthdr = keyspec + b'\x00' + b'\x7f\x48' + b'\x02' + key_template + suffix = b'\x5f\x48' + b'\x40' + return b'\x4d' + b'\x4a' + exthdr + suffix + self.d + + def compute_signature(self, digestinfo): + return lg_bp512.call_pk_sign(self.d, digestinfo) + + def verify_signature(self, digestinfo, sig): + return lg_bp512.call_pk_verify(self.q, digestinfo, sig) + + def encrypt(self, plaintext): + # Do ECDH + return lg_bp512.call_pk_encrypt(self.q, plaintext) + + def get_fpr(self): + return self.fpr + + def get_timestamp(self): + return self.timestamp + + def get_pk(self): + return self.q + +key = [ None, None, None ] + +# https://datatracker.ietf.org/doc/html/rfc6932#appendix-A.4 + +bp512_data0 = ( + b'\x63\x6b\x6b\xe0\x48\x2a\x6c\x1c\x41\xaa\x7a\xe7\xb2\x45\xe9\x83' + b'\x39\x2d\xb9\x4c\xec\xea\x26\x60\xa3\x79\xcf\xe1\x59\x55\x9e\x35' + b'\x75\x81\x82\x53\x91\x17\x5f\xc1\x95\xd2\x8b\xac\x0c\xf0\x3a\x78' + b'\x41\xa3\x83\xb9\x5c\x26\x2b\x98\x37\x82\x87\x4c\xce\x6f\xe3\x33', + b'\x04' + b'\x05\x62\xe6\x8b\x9a\xf7\xcb\xfd\x55\x65\xc6\xb1\x68\x83\xb7\x77' + b'\xff\x11\xc1\x99\x16\x1e\xcc\x42\x7a\x39\xd1\x7e\xc2\x16\x64\x99' + b'\x38\x95\x71\xd6\xa9\x94\x97\x7c\x56\xad\x82\x52\x65\x8b\xa8\xa1' + b'\xb7\x2a\xe4\x2f\x4f\xb7\x53\x21\x51\xaf\xc3\xef\x09\x71\xcc\xda' + b'\xa7\xca\x2d\x81\x91\xe2\x17\x76\xa8\x98\x60\xaf\xbc\x1f\x58\x2f' + b'\xaa\x30\x8d\x55\x1c\x1d\xc6\x13\x3a\xf9\xf9\xc3\xca\xd5\x99\x98' + b'\xd7\x00\x79\x54\x81\x40\xb9\x0b\x1f\x31\x1a\xfb\x37\x8a\xa8\x1f' + b'\x51\xb2\x75\xb2\xbe\x6b\x7d\xee\x97\x8e\xfc\x73\x43\xea\x64\x2e' +) + +bp512_data2 = ( + b'\x0a\xf4\xe7\xf6\xd5\x2e\xdd\x52\x90\x7b\xb8\xdb\xab\x39\x92\xa0' + b'\xbb\x69\x6e\xc1\x0d\xf1\x18\x92\xff\x20\x5b\x66\xd3\x81\xec\xe7' + b'\x23\x14\xe6\xa6\xea\x07\x9c\xea\x06\x96\x1d\xba\x5a\xe6\x42\x2e' + b'\xf2\xe9\xee\x80\x3a\x1f\x23\x6f\xb9\x6a\x17\x99\xb8\x6e\x5c\x8b', + b'\x04' + b'\x5a\x79\x54\xe3\x26\x63\xdf\xf1\x1a\xe2\x47\x12\xd8\x74\x19\xf2' + b'\x6b\x70\x8a\xc2\xb9\x28\x77\xd6\xbf\xee\x2b\xfc\x43\x71\x4d\x89' + b'\xbb\xdb\x6d\x24\xd8\x07\xbb\xd3\xae\xb7\xf0\xc3\x25\xf8\x62\xe8' + b'\xba\xde\x4f\x74\x63\x6b\x97\xea\xac\xe7\x39\xe1\x17\x20\xd3\x23' + b'\x96\xd1\x46\x21\xa9\x28\x3a\x1b\xed\x84\xde\x8d\xd6\x48\x36\xb2' + b'\xc0\x75\x8b\x11\x44\x11\x79\xdc\x0c\x54\xc0\xd4\x9a\x47\xc0\x38' + b'\x07\xd1\x71\xdd\x54\x4b\x72\xca\xae\xf7\xb7\xce\x01\xc7\x75\x3e' + b'\x2c\xad\x1a\x86\x1e\xca\x55\xa7\x19\x54\xee\x1b\xa3\x5e\x04\xbe' +) + +# https://tools.ietf.org/html/rfc7027#appendix-A.3 +bp512_data1 = ( + b'\x16\x30\x2f\xf0\xdb\xbb\x5a\x8d\x73\x3d\xab\x71\x41\xc1\xb4\x5a' + b'\xcb\xc8\x71\x59\x39\x67\x7f\x6a\x56\x85\x0a\x38\xbd\x87\xbd\x59' + b'\xb0\x9e\x80\x27\x96\x09\xff\x33\x3e\xb9\xd4\xc0\x61\x23\x1f\xb2' + b'\x6f\x92\xee\xb0\x49\x82\xa5\xf1\xd1\x76\x4c\xad\x57\x66\x54\x22', + b'\x04' + b'\x0a\x42\x05\x17\xe4\x06\xaa\xc0\xac\xdc\xe9\x0f\xcd\x71\x48\x77' + b'\x18\xd3\xb9\x53\xef\xd7\xfb\xec\x5f\x7f\x27\xe2\x8c\x61\x49\x99' + b'\x93\x97\xe9\x1e\x02\x9e\x06\x45\x7d\xb2\xd3\xe6\x40\x66\x8b\x39' + b'\x2c\x2a\x7e\x73\x7a\x7f\x0b\xf0\x44\x36\xd1\x16\x40\xfd\x09\xfd' + b'\x72\xe6\x88\x2e\x8d\xb2\x8a\xad\x36\x23\x7c\xd2\x5d\x58\x0d\xb2' + b'\x37\x83\x96\x1c\x8d\xc5\x2d\xfa\x2e\xc1\x38\xad\x47\x2a\x0f\xce' + b'\xf3\x88\x7c\xf6\x2b\x62\x3b\x2a\x87\xde\x5c\x58\x83\x01\xea\x3e' + b'\x5f\xc2\x69\xb3\x73\xb6\x07\x24\xf5\xe8\x2a\x6a\xd1\x47\xfd\xe7' +) + +key[0] = PK_Crypto(0, data=bp512_data0) +key[1] = PK_Crypto(1, data=bp512_data1) +key[2] = PK_Crypto(2, data=bp512_data2) + +PLAIN_TEXT0=b"In this test, we verify card generated result by libgcrypt." +PLAIN_TEXT1=b"Signature is non-deterministic (it uses nonce K internally)." +PLAIN_TEXT2=b"We don't use brainpoolp512r1 test vectors (it specifies K of ECDSA)." +PLAIN_TEXT3=b"NOTE: Our test is not for ECDSA implementation itself." + +ENCRYPT_TEXT0 = sha512(b"encrypt me please").digest() +ENCRYPT_TEXT1 = sha512(b"encrypt me please, another").digest() +ENCRYPT_TEXT2 = sha512(b"encrypt me please, the other").digest() + +test_vector = { + 'sign_0' : PLAIN_TEXT0, + 'sign_1' : PLAIN_TEXT1, + 'auth_0' : PLAIN_TEXT2, + 'auth_1' : PLAIN_TEXT3, + 'decrypt_0' : ENCRYPT_TEXT0, + 'decrypt_1' : ENCRYPT_TEXT1, + 'encrypt_0' : ENCRYPT_TEXT2, +} + +brainpoolp512r1_pk = PK_Crypto() +brainpoolp512r1_pk.test_vector = test_vector +brainpoolp512r1_pk.key_list = key +brainpoolp512r1_pk.key_attr_list = [KEY_ATTRIBUTES_ECDSA_BRAINPOOLP512R1, KEY_ATTRIBUTES_ECDH_BRAINPOOLP512R1, KEY_ATTRIBUTES_ECDSA_BRAINPOOLP512R1] +brainpoolp512r1_pk.PK_Crypto = PK_Crypto diff --git a/tests/card_const.py b/tests/card_const.py new file mode 100644 index 0000000..a8e8764 --- /dev/null +++ b/tests/card_const.py @@ -0,0 +1,46 @@ +FACTORY_PASSPHRASE_PW1=b"123456" +FACTORY_PASSPHRASE_PW3=b"12345678" +KEY_ATTRIBUTES_RSA4K=b"\x01\x10\x00\x00\x20\x00" +KEY_ATTRIBUTES_RSA2K=b"\x01\x08\x00\x00\x20\x00" +KEY_ATTRIBUTES_RSA2K_ALT=b"\x01\x08\x00\x00\x11\x00" +KEY_ATTRIBUTES_ECDH_NISTP256R1=b"\x12\x2a\x86\x48\xce\x3d\x03\x01\x07" +KEY_ATTRIBUTES_ECDH_NISTP384R1=b"\x12\x2b\x81\x04\x00\x22" +KEY_ATTRIBUTES_ECDH_NISTP521R1=b"\x12\x2b\x81\x04\x00\x23" +KEY_ATTRIBUTES_ECDH_BRAINPOOLP256R1=b"\x12\x2b\x24\x03\x03\x02\x08\x01\x01\x07" +KEY_ATTRIBUTES_ECDH_BRAINPOOLP384R1=b"\x12\x2b\x24\x03\x03\x02\x08\x01\x01\x0b" +KEY_ATTRIBUTES_ECDH_BRAINPOOLP512R1=b"\x12\x2b\x24\x03\x03\x02\x08\x01\x01\x0d" +KEY_ATTRIBUTES_ECDSA_NISTP256R1=b"\x13\x2a\x86\x48\xce\x3d\x03\x01\x07" +KEY_ATTRIBUTES_ECDSA_NISTP384R1=b"\x13\x2b\x81\x04\x00\x22" +KEY_ATTRIBUTES_ECDSA_NISTP521R1=b"\x13\x2b\x81\x04\x00\x23" +KEY_ATTRIBUTES_ECDSA_BRAINPOOLP256R1=b"\x13\x2b\x24\x03\x03\x02\x08\x01\x01\x07" +KEY_ATTRIBUTES_ECDSA_BRAINPOOLP384R1=b"\x13\x2b\x24\x03\x03\x02\x08\x01\x01\x0b" +KEY_ATTRIBUTES_ECDSA_BRAINPOOLP512R1=b"\x13\x2b\x24\x03\x03\x02\x08\x01\x01\x0d" +KEY_ATTRIBUTES_CV25519=b"\x12\x2b\x06\x01\x04\x01\x97\x55\x01\x05\x01" +KEY_ATTRIBUTES_ED25519=b"\x16\x2b\x06\x01\x04\x01\xda\x47\x0f\x01" +KEY_ATTRIBUTES_ECDH_SECP256K1=b"\x12\x2b\x81\x04\x00\x0a" +KEY_ATTRIBUTES_ECDSA_SECP256K1=b"\x13\x2b\x81\x04\x00\x0a" + +def default_key(card, is_for_encr): + if card.is_gnuk: + if is_for_encr: + return KEY_ATTRIBUTES_CV25519 + else: + return KEY_ATTRIBUTES_ED25519 + else: + # if is_for_encr: + # return KEY_ATTRIBUTES_ECDH_BRAINPOOLP512R1 + # else: + # return KEY_ATTRIBUTES_ECDSA_BRAINPOOLP512R1 + return KEY_ATTRIBUTES_RSA2K + +def alt_key(card, is_for_encr): + if card.is_gnuk or card.is_yubikey: + if is_for_encr: + return KEY_ATTRIBUTES_ECDH_SECP256K1 + else: + return KEY_ATTRIBUTES_ECDSA_SECP256K1 + else: + if is_for_encr: + return KEY_ATTRIBUTES_ECDH_BRAINPOOLP256R1 + else: + return KEY_ATTRIBUTES_ECDSA_BRAINPOOLP256R1 diff --git a/tests/card_reader.py b/tests/card_reader.py new file mode 100644 index 0000000..bb539a7 --- /dev/null +++ b/tests/card_reader.py @@ -0,0 +1,67 @@ +""" +card_reader.py - a library for smartcard reader + +Copyright (C) 2016, 2017, 2019 Free Software Initiative of Japan +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from struct import pack +from binascii import hexlify +import sys + +try: + from smartcard.CardType import AnyCardType + from smartcard.CardRequest import CardRequest + from smartcard.Exceptions import CardRequestTimeoutException, CardConnectionException +except ModuleNotFoundError: + print('ERROR: smarctard module not found! Install pyscard package.\nTry with `pip install pyscard`') + sys.exit(-1) + +class CardReader(object): + def __init__(self): + """ + __init__() -> None + Initialize the reader + device: usb.core.Device object. + """ + + cardtype = AnyCardType() + try: + # request card insertion + cardrequest = CardRequest(timeout=10, cardType=cardtype) + self.__card = cardrequest.waitforcard() + + # connect to the card and perform a few transmits + self.__card.connection.connect() + + except CardRequestTimeoutException: + raise Exception('time-out: no card inserted during last 10s') + + def reset_device(self): + self.__card.connection.reconnect() + + def send_cmd(self, cmd): + response, sw1, sw2 = self.__card.connection.transmit(list(bytearray(cmd))) + return bytes(response + [sw1,sw2]) + + def ccid_power_off(self): + self.__card.connection.disconnect() + +def get_ccid_device(): + return CardReader() + diff --git a/tests/card_test_0_set_attr.py b/tests/card_test_0_set_attr.py new file mode 100644 index 0000000..08e37b1 --- /dev/null +++ b/tests/card_test_0_set_attr.py @@ -0,0 +1,41 @@ +""" +card_test_set_attr.py - test setting key attributes + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from card_const import * +import pytest + +class Test_Set_ATTRS(object): + #def test_verify_pw3(self, card): + # v = card.verify(3, FACTORY_PASSPHRASE_PW3) + # assert v + + def test_keyattr_set_1(self, card, pk): + r = card.cmd_put_data(0x00, 0xc1, pk.key_attr_list[0]) + assert r + + def test_keyattr_set_2(self, card, pk): + r = card.cmd_put_data(0x00, 0xc2, pk.key_attr_list[1]) + assert r + + def test_keyattr_set_3(self, card, pk): + r = card.cmd_put_data(0x00, 0xc3, pk.key_attr_list[2]) + assert r diff --git a/tests/card_test_1_import_keys.py b/tests/card_test_1_import_keys.py new file mode 100644 index 0000000..6526704 --- /dev/null +++ b/tests/card_test_1_import_keys.py @@ -0,0 +1,184 @@ +""" +card_test_personalize_card.py - test personalizing card + +Copyright (C) 2016, 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from struct import pack +from re import match, DOTALL +from util import * +from card_const import * +from constants_for_test import * +import pytest + +class Test_Card_Personalize_Card_2(object): + def test_import_key_1(self, card, pk): + t = pk.key_list[0].build_privkey_template(card.is_yubikey) + r = card.cmd_put_data_odd(0x3f, 0xff, t) + assert r + + def test_import_key_2(self, card, pk): + t = pk.key_list[1].build_privkey_template(card.is_yubikey) + r = card.cmd_put_data_odd(0x3f, 0xff, t) + assert r + + def test_import_key_3(self, card, pk): + t = pk.key_list[2].build_privkey_template(card.is_yubikey) + r = card.cmd_put_data_odd(0x3f, 0xff, t) + assert r + + def test_fingerprint_1_put(self, card, pk): + fpr1 = pk.key_list[0].get_fpr() + r = card.cmd_put_data(0x00, 0xc7, fpr1) + assert r + + def test_fingerprint_2_put(self, card, pk): + fpr2 = pk.key_list[1].get_fpr() + r = card.cmd_put_data(0x00, 0xc8, fpr2) + assert r + + def test_fingerprint_3_put(self, card, pk): + fpr3 = pk.key_list[2].get_fpr() + r = card.cmd_put_data(0x00, 0xc9, fpr3) + assert r + + def test_timestamp_1_put(self, card, pk): + timestamp1 = pk.key_list[0].get_timestamp() + r = card.cmd_put_data(0x00, 0xce, timestamp1) + assert r + + def test_timestamp_2_put(self, card, pk): + timestamp2 = pk.key_list[1].get_timestamp() + r = card.cmd_put_data(0x00, 0xcf, timestamp2) + assert r + + def test_timestamp_3_put(self, card, pk): + timestamp3 = pk.key_list[2].get_timestamp() + r = card.cmd_put_data(0x00, 0xd0, timestamp3) + assert r + + def test_ds_counter_0(self, card): + c = get_data_object(card, 0x7a) + assert c == b'\x93\x03\x00\x00\x00' + + def test_pw1_status(self, card): + s = get_data_object(card, 0xc4) + assert match(b'\x01...\x03[\x00\x03]\x03', s, DOTALL) + + def test_app_data(self, card): + if card.is_yubikey: + pytest.skip("Yubikey raises 6e82 error for composed data object 6E") + else: + app_data = get_data_object(card, 0x6e) + hist_len = app_data[20] + # FIXME: parse and check DO of C0, C1, C2, C3, C4, and C6 + assert app_data[0:8] == b"\x4f\x10\xd2\x76\x00\x01\x24\x01" and \ + app_data[18:18+2] == b"\x5f\x52" + + def test_public_key_1(self, card, pk): + pk_info = card.cmd_get_public_key(1) + assert pk.key_list[0].get_pk() == pk.pk_from_pk_info(pk_info) + + def test_public_key_2(self, card, pk): + pk_info = card.cmd_get_public_key(2) + assert pk.key_list[1].get_pk() == pk.pk_from_pk_info(pk_info) + + def test_public_key_3(self, card, pk): + pk_info = card.cmd_get_public_key(3) + assert pk.key_list[2].get_pk() == pk.pk_from_pk_info(pk_info) + + def test_setup_pw1_0(self, card): + r = card.change_passwd(1, FACTORY_PASSPHRASE_PW1, PW1_TEST0) + assert r + + def test_verify_pw1_0(self, card): + v = card.verify(1, PW1_TEST0) + assert v + + def test_verify_pw1_0_2(self, card): + v = card.verify(2, PW1_TEST0) + assert v + + def test_setup_pw1_1(self, card): + r = card.change_passwd(1, PW1_TEST0, PW1_TEST1) + assert r + + def test_verify_pw1_1(self, card): + v = card.verify(1, PW1_TEST1) + assert v + + def test_verify_pw1_1_2(self, card): + v = card.verify(2, PW1_TEST1) + assert v + + def test_setup_reset_code(self, card): + r = card.setup_reset_code(RESETCODE_TEST) + assert r + + def test_reset_code(self, card): + r = card.reset_passwd_by_resetcode(RESETCODE_TEST, PW1_TEST2) + assert r + + def test_verify_pw1_2(self, card): + v = card.verify(1, PW1_TEST2) + assert v + + def test_verify_pw1_2_2(self, card): + v = card.verify(2, PW1_TEST2) + assert v + + def test_setup_pw3_1(self, card): + r = card.change_passwd(3, PW3_TEST0, PW3_TEST1) + assert r + + def test_verify_pw3_1(self, card): + v = card.verify(3, PW3_TEST1) + assert v + + def test_reset_userpass_admin(self, card): + r = card.reset_passwd_by_admin(PW1_TEST3) + assert r + + def test_verify_pw1_3(self, card): + v = card.verify(1, PW1_TEST3) + assert v + + def test_verify_pw1_3_2(self, card): + v = card.verify(2, PW1_TEST3) + assert v + + def test_setup_pw1_4(self, card): + r = card.change_passwd(1, PW1_TEST3, FACTORY_PASSPHRASE_PW1) + assert r + + def test_verify_pw1_4(self, card): + v = card.verify(1, FACTORY_PASSPHRASE_PW1) + assert v + + def test_verify_pw1_4_2(self, card): + v = card.verify(2, FACTORY_PASSPHRASE_PW1) + assert v + + def test_setup_pw3_2(self, card): + r = card.change_passwd(3, PW3_TEST1, PW3_TEST0) + assert r + + def test_verify_pw3_2(self, card): + v = card.verify(3, PW3_TEST0) + assert v diff --git a/tests/card_test_1_keygen.py b/tests/card_test_1_keygen.py new file mode 100644 index 0000000..b146e18 --- /dev/null +++ b/tests/card_test_1_keygen.py @@ -0,0 +1,99 @@ +""" +card_test_keygen.py - test key generation + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from binascii import hexlify +from card_const import * +from constants_for_test import * +import pytest + +class Test_Card_Keygen(object): + def test_000_setup_pw1_0(self, card): + if card.is_gnuk: + pytest.skip("Gnuk doesn't support passphrase with no key") + else: + r = card.change_passwd(1, FACTORY_PASSPHRASE_PW1, PW1_TEST4) + assert r + + def test_verify_pw1_0_1(self, card): + if card.is_gnuk: + pytest.skip("Gnuk doesn't support passphrase with no key") + else: + v = card.verify(1, PW1_TEST4) + assert v + + def test_verify_pw1_0_2(self, card): + if card.is_gnuk: + pytest.skip("Gnuk doesn't support passphrase with no key") + else: + v = card.verify(2, PW1_TEST4) + assert v + + def test_keygen_1(self, card, pk): + pk_info = card.cmd_genkey(1) + k = pk.PK_Crypto(keyno=0, pk_info=pk_info) + r = card.cmd_put_data(0x00, 0xc7, k.get_fpr()) + if r: + r = card.cmd_put_data(0x00, 0xce, k.get_timestamp()) + assert r + + def test_keygen_2(self, card, pk): + pk_info = card.cmd_genkey(2) + k = pk.PK_Crypto(keyno=1, pk_info=pk_info) + r = card.cmd_put_data(0x00, 0xc8, k.get_fpr()) + if r: + r = card.cmd_put_data(0x00, 0xcf, k.get_timestamp()) + assert r + + def test_keygen_3(self, card, pk): + pk_info = card.cmd_genkey(3) + k = pk.PK_Crypto(keyno=2, pk_info=pk_info) + r = card.cmd_put_data(0x00, 0xc9, k.get_fpr()) + if r: + r = card.cmd_put_data(0x00, 0xd0, k.get_timestamp()) + assert r + + def test_setup_pw1_0(self, card): + if card.is_gnuk: + r = card.change_passwd(1, FACTORY_PASSPHRASE_PW1, PW1_TEST4) + assert r + else: + pytest.skip("Gnuk resets passphrase on keygen, so, change passwd") + + def test_verify_pw1(self, card): + v = card.verify(1, PW1_TEST4) + assert v + + def test_verify_pw1_2(self, card): + v = card.verify(2, PW1_TEST4) + assert v + + def test_setup_pw1_reset(self, card): + r = card.change_passwd(1, PW1_TEST4, FACTORY_PASSPHRASE_PW1) + assert r + + def test_verify_pw1_reset_1(self, card): + v = card.verify(1, FACTORY_PASSPHRASE_PW1) + assert v + + def test_verify_pw1_reset_2(self, card): + v = card.verify(2, FACTORY_PASSPHRASE_PW1) + assert v diff --git a/tests/card_test_2_pkop.py b/tests/card_test_2_pkop.py new file mode 100644 index 0000000..bdb993a --- /dev/null +++ b/tests/card_test_2_pkop.py @@ -0,0 +1,62 @@ +""" +card_test_public_key_operations.py - test the sign/dec/auth + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from struct import pack +from re import match, DOTALL +from util import * +from card_const import * +from constants_for_test import * + +class Test_Card_PK_OPs(object): + def test_sign_0(self, card, pk): + digestinfo = pk.compute_digestinfo(pk.test_vector['sign_0']) + sig = card.cmd_pso(0x9e, 0x9a, digestinfo) + r = pk.key_list[0].verify_signature(digestinfo, sig) + assert r + + def test_sign_1(self, card, pk): + digestinfo = pk.compute_digestinfo(pk.test_vector['sign_1']) + sig = card.cmd_pso(0x9e, 0x9a, digestinfo) + r = pk.key_list[0].verify_signature(digestinfo, sig) + assert r + + def test_decrypt_0(self, card, pk): + encrypted_data = pk.key_list[1].encrypt(pk.test_vector['decrypt_0']) + r = card.cmd_pso(0x80, 0x86, pk.enc_data(encrypted_data)) + assert pk.enc_check(encrypted_data, r) + + def test_decrypt_1(self, card, pk): + encrypted_data = pk.key_list[1].encrypt(pk.test_vector['decrypt_1']) + r = card.cmd_pso(0x80, 0x86, pk.enc_data(encrypted_data)) + assert pk.enc_check(encrypted_data, r) + + def test_auth_0(self, card, pk): + digestinfo = pk.compute_digestinfo(pk.test_vector['auth_0']) + sig = card.cmd_internal_authenticate(digestinfo) + r = pk.key_list[2].verify_signature(digestinfo, sig) + assert r + + def test_auth_1(self, card, pk): + digestinfo = pk.compute_digestinfo(pk.test_vector['auth_1']) + sig = card.cmd_internal_authenticate(digestinfo) + r = pk.key_list[2].verify_signature(digestinfo, sig) + assert r diff --git a/tests/card_test_2_pkop_kg.py b/tests/card_test_2_pkop_kg.py new file mode 100644 index 0000000..044a671 --- /dev/null +++ b/tests/card_test_2_pkop_kg.py @@ -0,0 +1,51 @@ +""" +card_test_public_key_operations_kg.py - test the sign/dec/auth + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from struct import pack +from re import match, DOTALL +from util import * +from card_const import * +from constants_for_test import * + +class Test_Card_PK_OPs_KG(object): + def test_signature_sigkey(self, card, pk): + pk_info = card.cmd_get_public_key(1) + k = pk.PK_Crypto(keyno=0, pk_info=pk_info) + digestinfo = pk.compute_digestinfo(pk.test_vector['sign_0']) + sig_bytes = card.cmd_pso(0x9e, 0x9a, digestinfo) + r = k.verify_signature(digestinfo, sig_bytes) + assert r + + def test_decryption(self, card, pk): + pk_info = card.cmd_get_public_key(2) + k = pk.PK_Crypto(keyno=1, pk_info=pk_info) + encrypted_data = k.encrypt(pk.test_vector['encrypt_0']) + r = card.cmd_pso(0x80, 0x86, pk.enc_data(encrypted_data)) + assert pk.enc_check(encrypted_data, r) + + def test_signature_authkey(self, card, pk): + pk_info = card.cmd_get_public_key(3) + k = pk.PK_Crypto(2, pk_info=pk_info) + digestinfo = pk.compute_digestinfo(pk.test_vector['sign_0']) + sig_bytes = card.cmd_internal_authenticate(digestinfo) + r = k.verify_signature(digestinfo, sig_bytes) + assert r diff --git a/tests/card_test_3_ds_counter1.py b/tests/card_test_3_ds_counter1.py new file mode 100644 index 0000000..3bb8ca5 --- /dev/null +++ b/tests/card_test_3_ds_counter1.py @@ -0,0 +1,31 @@ +""" +card_test_ds_counter.py - test the result of DS counter + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from struct import pack +from re import match, DOTALL +from util import * +from constants_for_test import * + +class Test_Card_DS_Counter(object): + def test_ds_counter_1(self, card): + c = get_data_object(card, 0x7a) + assert c == b'\x93\x03\x00\x00\x01' diff --git a/tests/card_test_3_ds_counter2.py b/tests/card_test_3_ds_counter2.py new file mode 100644 index 0000000..9e24df0 --- /dev/null +++ b/tests/card_test_3_ds_counter2.py @@ -0,0 +1,31 @@ +""" +card_test_ds_counter.py - test the result of DS counter + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from struct import pack +from re import match, DOTALL +from util import * +from constants_for_test import * + +class Test_Card_DS_Counter(object): + def test_ds_counter_2(self, card): + c = get_data_object(card, 0x7a) + assert c == b'\x93\x03\x00\x00\x02' diff --git a/tests/card_test_check_card.py b/tests/card_test_check_card.py new file mode 100644 index 0000000..5915a2b --- /dev/null +++ b/tests/card_test_check_card.py @@ -0,0 +1,80 @@ +""" +card_test_check_card.py - test configuration of card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from binascii import hexlify +from util import * +from card_const import * +import pytest + +def print_key_attr(keyno, p): + algo = p[0] + if algo == 0x01 and len(p) >= 6: + # RSA + nbits = (p[1] << 8) | p[2] + ebits = (p[3] << 8) | p[4] + flags = p[5] + print(keyno) + print("RSA") + print(nbits) + print(ebits) + print(flags) + elif len(p) >= 2 and (algo == 0x12 or algo == 0x13 or algo == 0x16): + # ECC + if p[-1] == 0x00 or p[-1] == 0x00: + flag = True # Pubkey required + curve = p[1:-1] + else: + flag = False # Pubkey is not required + curve = p[1:] + print(keyno) + print("ECDSA" if algo == 0x13 else "ECDH" if algo == 0x12 else "EDDSA") + print(curve) + print(flag) + else: + print("Unknown algo attr") + + +def parse_list_of_key_attributes(card, p, printout=False): + while True: + if len(p) < 2: + break + tag = p[0] + length = p[1] + if tag < 0xc1: + p = p[2:] + continue + if tag == 0xda: + keyno = 0x81 + else: + keyno = tag - 0xc1 + 1 + if len(p) - 2 < length: + break + attr = p[2:2+length] + if printout: + print_key_attr(keyno, attr) + p = p[2+length:] + if keyno >= 1 and keyno <= 3: + card.add_to_key_attrlist(keyno - 1, attr) + +def test_list_of_key_attributes(card): + a = get_data_object(card, 0xfa) + parse_list_of_key_attributes(card, a, True) diff --git a/tests/card_test_ds_counter1.py b/tests/card_test_ds_counter1.py new file mode 100644 index 0000000..3bb8ca5 --- /dev/null +++ b/tests/card_test_ds_counter1.py @@ -0,0 +1,31 @@ +""" +card_test_ds_counter.py - test the result of DS counter + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from struct import pack +from re import match, DOTALL +from util import * +from constants_for_test import * + +class Test_Card_DS_Counter(object): + def test_ds_counter_1(self, card): + c = get_data_object(card, 0x7a) + assert c == b'\x93\x03\x00\x00\x01' diff --git a/tests/card_test_ds_counter2.py b/tests/card_test_ds_counter2.py new file mode 100644 index 0000000..9e24df0 --- /dev/null +++ b/tests/card_test_ds_counter2.py @@ -0,0 +1,31 @@ +""" +card_test_ds_counter.py - test the result of DS counter + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from struct import pack +from re import match, DOTALL +from util import * +from constants_for_test import * + +class Test_Card_DS_Counter(object): + def test_ds_counter_2(self, card): + c = get_data_object(card, 0x7a) + assert c == b'\x93\x03\x00\x00\x02' diff --git a/tests/card_test_empty_card.py b/tests/card_test_empty_card.py new file mode 100644 index 0000000..c81c74b --- /dev/null +++ b/tests/card_test_empty_card.py @@ -0,0 +1,271 @@ +""" +test_empty_card.py - test empty card + +Copyright (C) 2016, 2018 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from binascii import hexlify +from re import match, DOTALL +from struct import pack +from util import * +from card_const import * +import pytest + +EMPTY_60=bytes(60) + +def test_login(card): + login = get_data_object(card, 0x5e) + assert check_null(login) + +""" +def test_name(card): + name = get_data_object(card, 0x5b) + assert check_null(name) + +def test_lang(card): + lang = get_data_object(card, 0x5f2d) + assert check_null(lang) + +def test_sex(card): + sex = get_data_object(card, 0x5f35) + assert check_null(sex) +""" + +def test_name_lang_sex(card): + name = b"" + lang = b"" + lang_de = b"de" + sex = b"9" + sex_alt = b"0" + expected = b'\x5b' + pack('B', len(name)) + name \ + + b'\x5f\x2d' + pack('B', len(lang)) + lang \ + + b'\x5f\x35' + pack('B', len(sex)) + sex + expected_de = b'\x5b' + pack('B', len(name)) + name \ + + b'\x5f\x2d' + pack('B', len(lang_de)) + lang_de \ + + b'\x5f\x35' + pack('B', len(sex)) + sex + expected_de_alt = b'\x5b' + pack('B', len(name)) + name \ + + b'\x5f\x2d' + pack('B', len(lang_de)) + lang_de \ + + b'\x5f\x35' + pack('B', len(sex_alt)) + sex_alt + name_lang_sex = get_data_object(card, 0x65) + assert name_lang_sex == b'' or name_lang_sex == expected \ + or name_lang_sex == expected_de or name_lang_sex == expected_de_alt + +def test_app_data(card): + if card.is_yubikey: + pytest.skip("Yubikey raises 6e82 error for composed data object 6E") + else: + app_data = get_data_object(card, 0x6e) + hist_len = app_data[20] + # FIXME: parse and check DO of C0, C1, C2, C3, C4, and C6 + assert app_data[0:8] == b"\x4f\x10\xd2\x76\x00\x01\x24\x01" and \ + app_data[18:18+2] == b"\x5f\x52" + +def test_url(card): + url = get_data_object(card, 0x5f50) + assert check_null(url) + +def test_ds_counter(card): + c = get_data_object(card, 0x7a) + assert c == b'\x93\x03\x00\x00\x00' + +def test_pw1_status(card): + s = get_data_object(card, 0xc4) + assert match(b'....\x03[\x00\x03]\x03', s, DOTALL) + +def test_fingerprint_all(card): + if card.is_yubikey: + pytest.skip("Yubikey returns 6B00 when no key") + else: + fprlist = get_data_object(card, 0xC5) + assert fprlist == None or fprlist == EMPTY_60 + +def test_fingerprint_1(card): + if card.is_yubikey: + pytest.skip("Yubikey returns 6B00 when no key") + else: + fpr = get_data_object(card, 0xC7) + assert check_null(fpr) + +def test_fingerprint_2(card): + if card.is_yubikey: + pytest.skip("Yubikey returns 6B00 when no key") + else: + fpr = get_data_object(card, 0xC8) + assert check_null(fpr) + +def test_fingerprint_3(card): + if card.is_yubikey: + pytest.skip("Yubikey returns 6B00 when no key") + else: + fpr = get_data_object(card, 0xC9) + assert check_null(fpr) + +def test_ca_fingerprint_all(card): + if card.is_yubikey: + pytest.skip("Yubikey returns 6B00 when no key") + else: + cafprlist = get_data_object(card, 0xC6) + assert cafprlist == None or cafprlist == EMPTY_60 + +def test_ca_fingerprint_1(card): + if card.is_yubikey: + pytest.skip("Yubikey returns 6B00 when no key") + else: + cafp = get_data_object(card, 0xCA) + assert check_null(cafp) + +def test_ca_fingerprint_2(card): + if card.is_yubikey: + pytest.skip("Yubikey returns 6B00 when no key") + else: + cafp = get_data_object(card, 0xCB) + assert check_null(cafp) + +def test_ca_fingerprint_3(card): + if card.is_yubikey: + pytest.skip("Yubikey returns 6B00 when no key") + else: + cafp = get_data_object(card, 0xCC) + assert check_null(cafp) + +def test_timestamp_all(card): + if card.is_yubikey: + pytest.skip("Yubikey returns 6B00 when no key") + else: + t = get_data_object(card, 0xCD) + assert t == None or t == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + +def test_timestamp_1(card): + if card.is_yubikey: + pytest.skip("Yubikey returns 6B00 when no key") + else: + t = get_data_object(card, 0xCE) + assert check_null(t) + +def test_timestamp_2(card): + if card.is_yubikey: + pytest.skip("Yubikey returns 6B00 when no key") + else: + t = get_data_object(card, 0xCF) + assert check_null(t) + +def test_timestamp_3(card): + if card.is_yubikey: + pytest.skip("Yubikey returns 6B00 when no key") + else: + t = get_data_object(card, 0xD0) + assert check_null(t) + +def test_verify_pw1_1(card): + v = card.cmd_verify(1, FACTORY_PASSPHRASE_PW1) + assert v + +def test_verify_pw1_2(card): + v = card.cmd_verify(2, FACTORY_PASSPHRASE_PW1) + assert v + +def test_verify_pw3(card): + v = card.cmd_verify(3, FACTORY_PASSPHRASE_PW3) + assert v + +def test_historical_bytes(card): + h = get_data_object(card, 0x5f52) + if card.is_yubikey: + assert h == b'\x00\x73\x00\x00\x80\x05\x90\x00' or \ + h == b'\x00\x73\x00\x00\xe0\x05\x90\x00' + else: + assert h == b'\x001\xc5s\xc0\x01@\x05\x90\x00' or \ + h == b'\x00\x31\x84\x73\x80\x01\x80\x00\x90\x00' or \ + h == b'\x00\x31\x84\x73\x80\x01\x80\x05\x90\x00' or \ + h == b'\x00\x31\xf5\x73\xc0\x01\x60\x05\x90\x00' + +def test_extended_capabilities(card): + if card.is_yubikey: + pytest.skip("Yubikey returns 6B00 when no key") + else: + a = get_data_object(card, 0xc0) + assert a == None or match(b'[\x70\x74\x75]\x00\x00\x20[\x00\x08]\x00\x00\xff\x01\x00', a) + +def test_key_attributes_1(card): + if card.is_yubikey: + a = None + else: + a = get_data_object(card, 0xc1) + assert a == None or a == b'\x01\x08\x00\x00\x20\x00' or a == b'\x16\x2b\x06\x01\x04\x01\xda\x47\x0f\x01' + if not a: + a = KEY_ATTRIBUTES_RSA2K + card.save_algo_attribute(1, a) + if card.is_yubikey: + pytest.skip("Yubikey returns 6B00 when no key") + +def test_key_attributes_2(card): + if card.is_yubikey: + a = None + else: + a = get_data_object(card, 0xc2) + assert a == None or a == b'\x01\x08\x00\x00\x20\x00' or a == b'\x12\x2b\x06\x01\x04\x01\x97\x55\x01\x05\x01' + if not a: + a = KEY_ATTRIBUTES_RSA2K + card.save_algo_attribute(2, a) + if card.is_yubikey: + pytest.skip("Yubikey returns 6B00 when no key") + +def test_key_attributes_3(card): + if card.is_yubikey: + a = None + else: + a = get_data_object(card, 0xc3) + assert a == None or a == b'\x01\x08\x00\x00\x20\x00' or a == b'\x16\x2b\x06\x01\x04\x01\xda\x47\x0f\x01' + if not a: + a = KEY_ATTRIBUTES_RSA2K + card.save_algo_attribute(3, a) + if card.is_yubikey: + pytest.skip("Yubikey returns 6B00 when no key") + +def test_public_key_1(card): + with pytest.raises(Exception) as excinfo: + pk = card.cmd_get_public_key(1) + if card.is_yubikey: + assert excinfo.value.args[0] == "6581" + else: + assert excinfo.value.args[0] == "6a88" + +def test_public_key_2(card): + with pytest.raises(Exception) as excinfo: + pk = card.cmd_get_public_key(2) + if card.is_yubikey: + assert excinfo.value.args[0] == "6581" + else: + assert excinfo.value.args[0] == "6a88" + +def test_public_key_3(card): + with pytest.raises(Exception) as excinfo: + pk = card.cmd_get_public_key(3) + if card.is_yubikey: + assert excinfo.value.args[0] == "6581" + else: + assert excinfo.value.args[0] == "6a88" + +def test_AID(card): + a = get_data_object(card, 0x4f) + print() + print("OpenPGP card version: %d.%d" % (a[6], a[7])) + print("Card Manufacturer: ", hexlify(a[8:10]).decode("UTF-8")) + print("Card serial: ", hexlify(a[10:14]).decode("UTF-8")) + assert match(b'\xd2\x76\x00\x01\\$\x01........\x00\x00', a, DOTALL) diff --git a/tests/card_test_kdf_full.py b/tests/card_test_kdf_full.py new file mode 100644 index 0000000..0d99473 --- /dev/null +++ b/tests/card_test_kdf_full.py @@ -0,0 +1,34 @@ +""" +card_test_kdf_full.py - test KDF data object + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from card_const import * +from constants_for_test import * + +class Test_Card_KDF_full(object): + + def test_verify_pw3(self, card): + v = card.verify(3, FACTORY_PASSPHRASE_PW3) + assert v + + def test_kdf_put_full(self, card): + r = card.configure_kdf(KDF_FULL) + assert r diff --git a/tests/card_test_kdf_single.py b/tests/card_test_kdf_single.py new file mode 100644 index 0000000..5044400 --- /dev/null +++ b/tests/card_test_kdf_single.py @@ -0,0 +1,33 @@ +""" +card_test_kdf_single.py - test KDF data object + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from card_const import * +from constants_for_test import * + +class Test_Card_KDF_Single(object): + def test_verify_pw3(self, card): + v = card.verify(3, FACTORY_PASSPHRASE_PW3) + assert v + + def test_kdf_put_single(self, card): + r = card.configure_kdf(KDF_SINGLE) + assert r diff --git a/tests/card_test_keygen.py b/tests/card_test_keygen.py new file mode 100644 index 0000000..5e5d2dc --- /dev/null +++ b/tests/card_test_keygen.py @@ -0,0 +1,70 @@ +""" +card_test_keygen.py - test key generation + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from binascii import hexlify +from pubkey_crypto import get_PK_Crypto +from card_const import * +from constants_for_test import * +import pytest + +class Test_Card_Keygen(object): + def test_keygen_1(self, card): + PK_Crypto = get_PK_Crypto(card) + pk_info = card.cmd_genkey(1) + k = PK_Crypto(0, pk_info=pk_info) + r = card.cmd_put_data(0x00, 0xc7, k.get_fpr()) + if r: + r = card.cmd_put_data(0x00, 0xce, k.get_timestamp()) + assert r + + def test_keygen_2(self, card): + PK_Crypto = get_PK_Crypto(card) + pk_info = card.cmd_genkey(2) + k = PK_Crypto(1, pk_info=pk_info) + r = card.cmd_put_data(0x00, 0xc8, k.get_fpr()) + if r: + r = card.cmd_put_data(0x00, 0xcf, k.get_timestamp()) + assert r + + def test_keygen_3(self, card): + PK_Crypto = get_PK_Crypto(card) + pk_info = card.cmd_genkey(3) + k = PK_Crypto(2, pk_info=pk_info) + r = card.cmd_put_data(0x00, 0xc9, k.get_fpr()) + if r: + r = card.cmd_put_data(0x00, 0xd0, k.get_timestamp()) + assert r + + def test_setup_pw1_0(self, card): + if card.is_gnuk: + r = card.change_passwd(1, FACTORY_PASSPHRASE_PW1, PW1_TEST4) + assert r + else: + pytest.skip("Gnuk resets passphrase on keygen, so, change passwd") + + def test_verify_pw1(self, card): + v = card.verify(1, PW1_TEST4) + assert v + + def test_verify_pw1_2(self, card): + v = card.verify(2, PW1_TEST4) + assert v diff --git a/tests/card_test_kg_pko_dsc_brainpoolp256r1.py b/tests/card_test_kg_pko_dsc_brainpoolp256r1.py new file mode 100644 index 0000000..e5efcc0 --- /dev/null +++ b/tests/card_test_kg_pko_dsc_brainpoolp256r1.py @@ -0,0 +1,40 @@ +""" +card_test_kg_pko_dsc.py - test personalizing card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_ECDH_BRAINPOOLP256R1 +from brainpoolp256r1_keys import brainpoolp256r1_pk + +@pytest.fixture(scope="module",autouse=True) +def check_brainpoolp256r1(card): + if not KEY_ATTRIBUTES_ECDSA_BRAINPOOLP256R1 in card.supported_key_attrlist[0]: + pytest.skip("Test for brainpoolP256r1", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select brainpoolP256r1 for testing key generation") + return brainpoolp256r1_pk + +from card_test_0_set_attr import * +from card_test_1_keygen import * +from card_test_2_pkop_kg import * +from card_test_3_ds_counter1 import * diff --git a/tests/card_test_kg_pko_dsc_brainpoolp384r1.py b/tests/card_test_kg_pko_dsc_brainpoolp384r1.py new file mode 100644 index 0000000..cb50505 --- /dev/null +++ b/tests/card_test_kg_pko_dsc_brainpoolp384r1.py @@ -0,0 +1,40 @@ +""" +card_test_kg_pko_dsc.py - test personalizing card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_ECDH_BRAINPOOLP384R1 +from brainpoolp384r1_keys import brainpoolp384r1_pk + +@pytest.fixture(scope="module",autouse=True) +def check_brainpoolp384r1(card): + if not KEY_ATTRIBUTES_ECDSA_BRAINPOOLP384R1 in card.supported_key_attrlist[0]: + pytest.skip("Test for brainpoolP384r1", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select brainpoolP384r1 for testing key generation") + return brainpoolp384r1_pk + +from card_test_0_set_attr import * +from card_test_1_keygen import * +from card_test_2_pkop_kg import * +from card_test_3_ds_counter1 import * diff --git a/tests/card_test_kg_pko_dsc_brainpoolp512r1.py b/tests/card_test_kg_pko_dsc_brainpoolp512r1.py new file mode 100644 index 0000000..c9a600c --- /dev/null +++ b/tests/card_test_kg_pko_dsc_brainpoolp512r1.py @@ -0,0 +1,40 @@ +""" +card_test_kg_pko_dsc.py - test personalizing card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_ECDH_BRAINPOOLP512R1 +from brainpoolp512r1_keys import brainpoolp512r1_pk + +@pytest.fixture(scope="module",autouse=True) +def check_brainpoolp512r1(card): + if not KEY_ATTRIBUTES_ECDSA_BRAINPOOLP512R1 in card.supported_key_attrlist[0]: + pytest.skip("Test for brainpoolP512r1", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select brainpoolP512r1 for testing key generation") + return brainpoolp512r1_pk + +from card_test_0_set_attr import * +from card_test_1_keygen import * +from card_test_2_pkop_kg import * +from card_test_3_ds_counter1 import * diff --git a/tests/card_test_kg_pko_dsc_curve25519.py b/tests/card_test_kg_pko_dsc_curve25519.py new file mode 100644 index 0000000..1791626 --- /dev/null +++ b/tests/card_test_kg_pko_dsc_curve25519.py @@ -0,0 +1,40 @@ +""" +card_test_kg_pko_dsc.py - test personalizing card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_CV25519 +from curve25519_keys import curve25519_pk + +@pytest.fixture(scope="module",autouse=True) +def check_curve25519(card): + if not KEY_ATTRIBUTES_ED25519 in card.supported_key_attrlist[0]: + pytest.skip("Test for Ed25519/Cv25519", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select Ed25519/Cv25519 for testing key import") + return curve25519_pk + +from card_test_0_set_attr import * +from card_test_1_keygen import * +from card_test_2_pkop_kg import * +from card_test_3_ds_counter1 import * diff --git a/tests/card_test_kg_pko_dsc_nistp256r1.py b/tests/card_test_kg_pko_dsc_nistp256r1.py new file mode 100644 index 0000000..5952d94 --- /dev/null +++ b/tests/card_test_kg_pko_dsc_nistp256r1.py @@ -0,0 +1,40 @@ +""" +card_test_kg_pko_dsc.py - test keygeneration card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_ECDH_NISTP256R1 +from nistp256r1_keys import nistp256r1_pk + +@pytest.fixture(scope="module",autouse=True) +def check_nistp256r1(card): + if not KEY_ATTRIBUTES_ECDSA_NISTP256R1 in card.supported_key_attrlist[0]: + pytest.skip("Test for NIST P-256", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select NIST P-256 for testing key import") + return nistp256r1_pk + +from card_test_0_set_attr import * +from card_test_1_keygen import * +from card_test_2_pkop_kg import * +from card_test_3_ds_counter1 import * diff --git a/tests/card_test_kg_pko_dsc_nistp384r1.py b/tests/card_test_kg_pko_dsc_nistp384r1.py new file mode 100644 index 0000000..5f213b0 --- /dev/null +++ b/tests/card_test_kg_pko_dsc_nistp384r1.py @@ -0,0 +1,40 @@ +""" +card_test_kg_pko_dsc.py - test personalizing card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_ECDH_NISTP384R1 +from nistp384r1_keys import nistp384r1_pk + +@pytest.fixture(scope="module",autouse=True) +def check_nistp384r1(card): + if not KEY_ATTRIBUTES_ECDSA_NISTP384R1 in card.supported_key_attrlist[0]: + pytest.skip("Test for NIST P-384", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select NIST P-384 for testing key generation") + return nistp384r1_pk + +from card_test_0_set_attr import * +from card_test_1_keygen import * +from card_test_2_pkop_kg import * +from card_test_3_ds_counter1 import * diff --git a/tests/card_test_kg_pko_dsc_nistp521r1.py b/tests/card_test_kg_pko_dsc_nistp521r1.py new file mode 100644 index 0000000..e6f2050 --- /dev/null +++ b/tests/card_test_kg_pko_dsc_nistp521r1.py @@ -0,0 +1,40 @@ +""" +card_test_kg_pko_dsc.py - test personalizing card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_ECDH_NISTP521R1 +from nistp521r1_keys import nistp521r1_pk + +@pytest.fixture(scope="module",autouse=True) +def check_nistp521r1(card): + if not KEY_ATTRIBUTES_ECDSA_NISTP521R1 in card.supported_key_attrlist[0]: + pytest.skip("Test for NIST P-521", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select NIST P-521 for testing key generation") + return nistp521r1_pk + +from card_test_0_set_attr import * +from card_test_1_keygen import * +from card_test_2_pkop_kg import * +from card_test_3_ds_counter1 import * diff --git a/tests/card_test_kg_pko_dsc_rsa2k.py b/tests/card_test_kg_pko_dsc_rsa2k.py new file mode 100644 index 0000000..13f5994 --- /dev/null +++ b/tests/card_test_kg_pko_dsc_rsa2k.py @@ -0,0 +1,41 @@ +""" +card_test_kg_pko_dsc.py - test keygeneration card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_RSA2K, KEY_ATTRIBUTES_RSA2K_ALT +from rsa_keys import rsa_pk + +@pytest.fixture(scope="module",autouse=True) +def check_rsa2k(card): + if not (KEY_ATTRIBUTES_RSA2K in card.supported_key_attrlist[0] + or KEY_ATTRIBUTES_RSA2K_ALT in card.supported_key_attrlist[0]): + pytest.skip("Test for RSA-2048", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select RSA-2048 for testing key generation") + return rsa_pk + +from card_test_0_set_attr import * +from card_test_1_keygen import * +from card_test_2_pkop_kg import * +from card_test_3_ds_counter1 import * diff --git a/tests/card_test_kg_pko_dsc_rsa4k.py b/tests/card_test_kg_pko_dsc_rsa4k.py new file mode 100644 index 0000000..3aa7c8a --- /dev/null +++ b/tests/card_test_kg_pko_dsc_rsa4k.py @@ -0,0 +1,13 @@ +import pytest +from card_const import KEY_ATTRIBUTES_RSA4K + +@pytest.fixture(scope="module",autouse=True) +def check_rsa4k(card): + print("RSA-4096 keygen") + if not KEY_ATTRIBUTES_RSA4K in card.supported_key_attrlist[0]: + pytest.skip("Test for RSA-4096", allow_module_level=True) + +from card_test_0_set_attr_rsa4k import * +from card_test_1_keygen import * +from card_test_2_pkop_kg import * +from card_test_3_ds_counter1 import * diff --git a/tests/card_test_kg_pko_dsc_secp256k1.py b/tests/card_test_kg_pko_dsc_secp256k1.py new file mode 100644 index 0000000..5f599c0 --- /dev/null +++ b/tests/card_test_kg_pko_dsc_secp256k1.py @@ -0,0 +1,40 @@ +""" +card_test_kg_pko_dsc.py - test personalizing card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_ECDSA_SECP256K1 +from secp256k1_keys import secp256k1_pk + +@pytest.fixture(scope="module",autouse=True) +def check_secp256k1(card): + if not KEY_ATTRIBUTES_ECDSA_SECP256K1 in card.supported_key_attrlist[0]: + pytest.skip("Test for secp256k1", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select secp256k1 for testing key import") + return secp256k1_pk + +from card_test_0_set_attr import * +from card_test_1_keygen import * +from card_test_2_pkop_kg import * +from card_test_3_ds_counter1 import * diff --git a/tests/card_test_ki_pko_dsc_brainpoolp256r1.py b/tests/card_test_ki_pko_dsc_brainpoolp256r1.py new file mode 100644 index 0000000..b6b9dfe --- /dev/null +++ b/tests/card_test_ki_pko_dsc_brainpoolp256r1.py @@ -0,0 +1,40 @@ +""" +card_test_ki_pko_dsc.py - test personalizing card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_ECDH_BRAINPOOLP256R1 +from brainpoolp256r1_keys import brainpoolp256r1_pk + +@pytest.fixture(scope="module",autouse=True) +def check_brainpoolp256r1(card): + if not KEY_ATTRIBUTES_ECDSA_BRAINPOOLP256R1 in card.supported_key_attrlist[0]: + pytest.skip("Test for brainpoolP256r1", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select brainpoolP256r1 for testing key import") + return brainpoolp256r1_pk + +from card_test_0_set_attr import * +from card_test_1_import_keys import * +from card_test_2_pkop import * +from card_test_3_ds_counter2 import * diff --git a/tests/card_test_ki_pko_dsc_brainpoolp384r1.py b/tests/card_test_ki_pko_dsc_brainpoolp384r1.py new file mode 100644 index 0000000..c8b6c6a --- /dev/null +++ b/tests/card_test_ki_pko_dsc_brainpoolp384r1.py @@ -0,0 +1,40 @@ +""" +card_test_ki_pko_dsc.py - test personalizing card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_ECDH_BRAINPOOLP384R1 +from brainpoolp384r1_keys import brainpoolp384r1_pk + +@pytest.fixture(scope="module",autouse=True) +def check_brainpoolp384r1(card): + if not KEY_ATTRIBUTES_ECDSA_BRAINPOOLP384R1 in card.supported_key_attrlist[0]: + pytest.skip("Test for brainpoolP384r1", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select brainpoolP384r1 for testing key import") + return brainpoolp384r1_pk + +from card_test_0_set_attr import * +from card_test_1_import_keys import * +from card_test_2_pkop import * +from card_test_3_ds_counter2 import * diff --git a/tests/card_test_ki_pko_dsc_brainpoolp512r1.py b/tests/card_test_ki_pko_dsc_brainpoolp512r1.py new file mode 100644 index 0000000..4469c4b --- /dev/null +++ b/tests/card_test_ki_pko_dsc_brainpoolp512r1.py @@ -0,0 +1,40 @@ +""" +card_test_ki_pko_dsc.py - test personalizing card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_ECDH_BRAINPOOLP512R1 +from brainpoolp512r1_keys import brainpoolp512r1_pk + +@pytest.fixture(scope="module",autouse=True) +def check_brainpoolp512r1(card): + if not KEY_ATTRIBUTES_ECDSA_BRAINPOOLP512R1 in card.supported_key_attrlist[0]: + pytest.skip("Test for brainpoolP512r1", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select brainpoolP512r1 for testing key import") + return brainpoolp512r1_pk + +from card_test_0_set_attr import * +from card_test_1_import_keys import * +from card_test_2_pkop import * +from card_test_3_ds_counter2 import * diff --git a/tests/card_test_ki_pko_dsc_curve25519.py b/tests/card_test_ki_pko_dsc_curve25519.py new file mode 100644 index 0000000..e4c982a --- /dev/null +++ b/tests/card_test_ki_pko_dsc_curve25519.py @@ -0,0 +1,40 @@ +""" +card_test_ki_pko_dsc.py - test personalizing card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_CV25519 +from curve25519_keys import curve25519_pk + +@pytest.fixture(scope="module",autouse=True) +def check_curve25519(card): + if not KEY_ATTRIBUTES_ED25519 in card.supported_key_attrlist[0]: + pytest.skip("Test for Ed25519/Cv25519", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select Ed25519/Cv25519 for testing key import") + return curve25519_pk + +from card_test_0_set_attr import * +from card_test_1_import_keys import * +from card_test_2_pkop import * +from card_test_3_ds_counter2 import * diff --git a/tests/card_test_ki_pko_dsc_nistp256r1.py b/tests/card_test_ki_pko_dsc_nistp256r1.py new file mode 100644 index 0000000..bdab159 --- /dev/null +++ b/tests/card_test_ki_pko_dsc_nistp256r1.py @@ -0,0 +1,40 @@ +""" +card_test_ki_pko_dsc.py - test personalizing card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_ECDH_NISTP256R1 +from nistp256r1_keys import nistp256r1_pk + +@pytest.fixture(scope="module",autouse=True) +def check_nistp256r1(card): + if not KEY_ATTRIBUTES_ECDSA_NISTP256R1 in card.supported_key_attrlist[0]: + pytest.skip("Test for NIST P-256", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select NIST P-256 for testing key import") + return nistp256r1_pk + +from card_test_0_set_attr import * +from card_test_1_import_keys import * +from card_test_2_pkop import * +from card_test_3_ds_counter2 import * diff --git a/tests/card_test_ki_pko_dsc_nistp384r1.py b/tests/card_test_ki_pko_dsc_nistp384r1.py new file mode 100644 index 0000000..768b85c --- /dev/null +++ b/tests/card_test_ki_pko_dsc_nistp384r1.py @@ -0,0 +1,40 @@ +""" +card_test_ki_pko_dsc.py - test personalizing card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_ECDH_NISTP384R1 +from nistp384r1_keys import nistp384r1_pk + +@pytest.fixture(scope="module",autouse=True) +def check_nistp384r1(card): + if not KEY_ATTRIBUTES_ECDSA_NISTP384R1 in card.supported_key_attrlist[0]: + pytest.skip("Test for NIST P-384", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select NIST P-384 for testing key import") + return nistp384r1_pk + +from card_test_0_set_attr import * +from card_test_1_import_keys import * +from card_test_2_pkop import * +from card_test_3_ds_counter2 import * diff --git a/tests/card_test_ki_pko_dsc_nistp521r1.py b/tests/card_test_ki_pko_dsc_nistp521r1.py new file mode 100644 index 0000000..4e64fd6 --- /dev/null +++ b/tests/card_test_ki_pko_dsc_nistp521r1.py @@ -0,0 +1,40 @@ +""" +card_test_ki_pko_dsc.py - test personalizing card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_ECDH_NISTP521R1 +from nistp521r1_keys import nistp521r1_pk + +@pytest.fixture(scope="module",autouse=True) +def check_nistp521r1(card): + if not KEY_ATTRIBUTES_ECDSA_NISTP521R1 in card.supported_key_attrlist[0]: + pytest.skip("Test for NIST P-521", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select NIST P-521 for testing key import") + return nistp521r1_pk + +from card_test_0_set_attr import * +from card_test_1_import_keys import * +from card_test_2_pkop import * +from card_test_3_ds_counter2 import * diff --git a/tests/card_test_ki_pko_dsc_rsa2k.py b/tests/card_test_ki_pko_dsc_rsa2k.py new file mode 100644 index 0000000..a5fe83e --- /dev/null +++ b/tests/card_test_ki_pko_dsc_rsa2k.py @@ -0,0 +1,41 @@ +""" +card_test_ki_pko_dsc.py - test personalizing card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_RSA2K, KEY_ATTRIBUTES_RSA2K_ALT +from rsa_keys import rsa_pk + +@pytest.fixture(scope="module",autouse=True) +def check_rsa2k(card): + if not (KEY_ATTRIBUTES_RSA2K in card.supported_key_attrlist[0] + or KEY_ATTRIBUTES_RSA2K_ALT in card.supported_key_attrlist[0]): + pytest.skip("Test for RSA-2048", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select RSA-2048 for testing key import") + return rsa_pk + +from card_test_0_set_attr import * +from card_test_1_import_keys import * +from card_test_2_pkop import * +from card_test_3_ds_counter2 import * diff --git a/tests/card_test_ki_pko_dsc_rsa4k.py b/tests/card_test_ki_pko_dsc_rsa4k.py new file mode 100644 index 0000000..b7fe15e --- /dev/null +++ b/tests/card_test_ki_pko_dsc_rsa4k.py @@ -0,0 +1,13 @@ +import pytest +from card_const import KEY_ATTRIBUTES_RSA4K + +@pytest.fixture(scope="module",autouse=True) +def check_rsa4k(card): + print("RSA-4096 key import") + if not KEY_ATTRIBUTES_RSA4K in card.supported_key_attrlist[0]: + pytest.skip("Test for RSA-4096", allow_module_level=True) + +from card_test_0_set_attr_rsa4k import * +from card_test_1_import_keys import * +from card_test_2_pkop import * +from card_test_3_ds_counter2 import * diff --git a/tests/card_test_ki_pko_dsc_secp256k1.py b/tests/card_test_ki_pko_dsc_secp256k1.py new file mode 100644 index 0000000..413965c --- /dev/null +++ b/tests/card_test_ki_pko_dsc_secp256k1.py @@ -0,0 +1,40 @@ +""" +card_test_ki_pko_dsc.py - test personalizing card + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import pytest +from card_const import KEY_ATTRIBUTES_ECDSA_SECP256K1 +from secp256k1_keys import secp256k1_pk + +@pytest.fixture(scope="module",autouse=True) +def check_secp256k1(card): + if not KEY_ATTRIBUTES_ECDSA_SECP256K1 in card.supported_key_attrlist[0]: + pytest.skip("Test for secp256k1", allow_module_level=True) + +@pytest.fixture(scope="module") +def pk(card): + print("Select secp256k1 for testing key import") + return secp256k1_pk + +from card_test_0_set_attr import * +from card_test_1_import_keys import * +from card_test_2_pkop import * +from card_test_3_ds_counter2 import * diff --git a/tests/card_test_personalize_admin_less_1.py b/tests/card_test_personalize_admin_less_1.py new file mode 100644 index 0000000..ac8101c --- /dev/null +++ b/tests/card_test_personalize_admin_less_1.py @@ -0,0 +1,190 @@ +""" +card_test_personalize_admin_less_1.py - test admin-less mode + +Copyright (C) 2016, 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from struct import pack +from re import match, DOTALL +from util import * +from pubkey_crypto import get_PK_Crypto, get_key +from card_const import * +from constants_for_test import * + +class Test_Card_Personalize_Adminless_FIRST(object): + def test_verify_pw3_0(self, card): + v = card.verify(3, FACTORY_PASSPHRASE_PW3) + assert v + + def test_import_key_1(self, card): + key = get_key(card) + t = key[0].build_privkey_template(card.is_yubikey) + r = card.cmd_put_data_odd(0x3f, 0xff, t) + assert r + + def test_import_key_2(self, card): + key = get_key(card) + t = key[1].build_privkey_template(card.is_yubikey) + r = card.cmd_put_data_odd(0x3f, 0xff, t) + assert r + + def test_import_key_3(self, card): + key = get_key(card) + t = key[2].build_privkey_template(card.is_yubikey) + r = card.cmd_put_data_odd(0x3f, 0xff, t) + assert r + + def test_fingerprint_1_put(self, card): + key = get_key(card) + fpr1 = key[0].get_fpr() + r = card.cmd_put_data(0x00, 0xc7, fpr1) + assert r + + def test_fingerprint_2_put(self, card): + key = get_key(card) + fpr2 = key[1].get_fpr() + r = card.cmd_put_data(0x00, 0xc8, fpr2) + assert r + + def test_fingerprint_3_put(self, card): + key = get_key(card) + fpr3 = key[2].get_fpr() + r = card.cmd_put_data(0x00, 0xc9, fpr3) + assert r + + def test_timestamp_1_put(self, card): + key = get_key(card) + timestamp1 = key[0].get_timestamp() + r = card.cmd_put_data(0x00, 0xce, timestamp1) + assert r + + def test_timestamp_2_put(self, card): + key = get_key(card) + timestamp2 = key[1].get_timestamp() + r = card.cmd_put_data(0x00, 0xcf, timestamp2) + assert r + + def test_timestamp_3_put(self, card): + key = get_key(card) + timestamp3 = key[2].get_timestamp() + r = card.cmd_put_data(0x00, 0xd0, timestamp3) + assert r + + def test_ds_counter_0(self, card): + c = get_data_object(card, 0x7a) + assert c == b'\x93\x03\x00\x00\x00' + + def test_pw1_status(self, card): + s = get_data_object(card, 0xc4) + assert match(b'\x00...\x03[\x00\x03]\x03', s, DOTALL) + + def test_app_data(self, card): + app_data = get_data_object(card, 0x6e) + hist_len = app_data[20] + # FIXME: parse and check DO of C0, C1, C2, C3, C4, and C6 + assert app_data[0:8] == b"\x4f\x10\xd2\x76\x00\x01\x24\x01" and \ + app_data[18:18+2] == b"\x5f\x52" + + def test_public_key_1(self, card): + key = get_key(card) + PK_Crypto = get_PK_Crypto(card) + pk_info = card.cmd_get_public_key(1) + assert key[0].get_pk() == PK_Crypto.pk_from_pk_info(pk_info) + + def test_public_key_2(self, card): + key = get_key(card) + PK_Crypto = get_PK_Crypto(card) + pk_info = card.cmd_get_public_key(2) + assert key[1].get_pk() == PK_Crypto.pk_from_pk_info(pk_info) + + def test_public_key_3(self, card): + key = get_key(card) + PK_Crypto = get_PK_Crypto(card) + pk_info = card.cmd_get_public_key(3) + assert key[2].get_pk() == PK_Crypto.pk_from_pk_info(pk_info) + + # Changing PW1 to admin-less mode + + def test_setup_pw1_0(self, card): + r = card.change_passwd(1, FACTORY_PASSPHRASE_PW1, PW1_TEST0) + assert r + + # Now, it's admin-less mode, auth-status admin cleared + + def test_verify_pw3_fail_1(self, card): + try: + v = card.verify(3, FACTORY_PASSPHRASE_PW3) + except ValueError as e: + v = False + assert not v + + def test_verify_pw1_0(self, card): + v = card.verify(1, PW1_TEST0) + assert v + + def test_verify_pw1_0_2(self, card): + v = card.verify(2, PW1_TEST0) + assert v + + def test_setup_pw1_1(self, card): + r = card.change_passwd(1, PW1_TEST0, PW1_TEST1) + assert r + + def test_verify_pw1_1(self, card): + v = card.verify(1, PW1_TEST1) + assert v + + def test_verify_pw1_1_2(self, card): + v = card.verify(2, PW1_TEST1) + assert v + + def test_verify_pw3_admin_less_1(self, card): + v = card.verify(3, PW1_TEST1) + assert v + + def test_setup_reset_code(self, card): + r = card.setup_reset_code(RESETCODE_TEST) + assert r + + def test_reset_code(self, card): + r = card.reset_passwd_by_resetcode(RESETCODE_TEST, PW1_TEST2) + assert r + + # Changing PW1, auth status for admin cleared + def test_login_put_fail(self, card): + try: + r = card.cmd_put_data(0x00, 0x5e, b"gpg_user") + except ValueError as e: + r = e.args[0] + assert r == "6982" + + def test_verify_pw1_2(self, card): + v = card.verify(1, PW1_TEST2) + assert v + + def test_verify_pw1_2_2(self, card): + v = card.verify(2, PW1_TEST2) + assert v + + def test_verify_pw3_fail_2(self, card): + try: + v = card.verify(3, FACTORY_PASSPHRASE_PW3) + except ValueError as e: + v = e.args[0] + assert v == "6982" diff --git a/tests/card_test_personalize_admin_less_2.py b/tests/card_test_personalize_admin_less_2.py new file mode 100644 index 0000000..67bf003 --- /dev/null +++ b/tests/card_test_personalize_admin_less_2.py @@ -0,0 +1,121 @@ +""" +card_test_personalize_admin_less.py - test admin-less mode + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from struct import pack +from re import match, DOTALL +from util import * +from card_const import * +from constants_for_test import * + +class Test_Card_Personalize_Adminless_SECOND(object): + def test_verify_pw3_admin_less_2(self, card): + v = card.verify(3, PW1_TEST2) + assert v + + def test_login_put(self, card): + r = card.cmd_put_data(0x00, 0x5e, b"gpg_user") + assert r + + def test_name_put(self, card): + r = card.cmd_put_data(0x00, 0x5b, b"GnuPG User") + assert r + + def test_lang_put(self, card): + r = card.cmd_put_data(0x5f, 0x2d, b"ja") + assert r + + def test_sex_put(self, card): + r = card.cmd_put_data(0x5f, 0x35, b"1") + assert r + + def test_url_put(self, card): + r = card.cmd_put_data(0x5f, 0x50, b"https://www.fsij.org/gnuk/") + assert r + + def test_pw1_status_put(self, card): + r = card.cmd_put_data(0x00, 0xc4, b"\x01") + assert r + + def test_login(self, card): + login = get_data_object(card, 0x5e) + assert login == b"gpg_user" + + def test_name_lang_sex(self, card): + name = b"GnuPG User" + lang = b"ja" + sex = b"1" + expected = b'\x5b' + pack('B', len(name)) + name \ + + b'\x5f\x2d' + pack('B', len(lang)) + lang \ + + b'\x5f\x35' + pack('B', len(sex)) + sex + name_lang_sex = get_data_object(card, 0x65) + assert name_lang_sex == expected + + def test_url(self, card): + url = get_data_object(card, 0x5f50) + assert url == b"https://www.fsij.org/gnuk/" + + def test_pw1_status(self, card): + s = get_data_object(card, 0xc4) + assert match(b'\x01...\x03[\x00\x03]\x03', s, DOTALL) + + # Setting PW3, changed to admin-full mode + + def test_setup_pw3_1(self, card): + r = card.change_passwd(3, PW1_TEST2, PW3_TEST1) + assert r + + def test_verify_pw3_1(self, card): + v = card.verify(3, PW3_TEST1) + assert v + + def test_reset_userpass_admin(self, card): + r = card.reset_passwd_by_admin(PW1_TEST3) + assert r + + def test_verify_pw1_3(self, card): + v = card.verify(1, PW1_TEST3) + assert v + + def test_verify_pw1_3_2(self, card): + v = card.verify(2, PW1_TEST3) + assert v + + def test_setup_pw1_4(self, card): + r = card.change_passwd(1, PW1_TEST3, PW1_TEST4) + assert r + + def test_verify_pw1_4(self, card): + v = card.verify(1, PW1_TEST4) + assert v + + def test_verify_pw1_4_2(self, card): + v = card.verify(2, PW1_TEST4) + assert v + + def test_setup_pw3_2(self, card): + r = card.change_passwd(3, PW3_TEST1, PW3_TEST0) + assert r + + def test_verify_pw3_2(self, card): + v = card.verify(3, PW3_TEST0) + assert v + diff --git a/tests/card_test_personalize_card_1.py b/tests/card_test_personalize_card_1.py new file mode 100644 index 0000000..d13b09f --- /dev/null +++ b/tests/card_test_personalize_card_1.py @@ -0,0 +1,82 @@ +""" +card_test_personalize_card.py - test personalizing card + +Copyright (C) 2016, 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from struct import pack +from re import match, DOTALL +from util import * +from card_const import * +from constants_for_test import * + +class Test_Card_Personalize_Card_1(object): + def test_setup_pw3_0(self, card): + r = card.change_passwd(3, FACTORY_PASSPHRASE_PW3, PW3_TEST0) + assert r + + def test_verify_pw3_0(self, card): + v = card.verify(3, PW3_TEST0) + assert v + + def test_login_put(self, card): + r = card.cmd_put_data(0x00, 0x5e, b"gpg_user") + assert r + + def test_name_put(self, card): + r = card.cmd_put_data(0x00, 0x5b, b"GnuPG User") + assert r + + def test_lang_put(self, card): + r = card.cmd_put_data(0x5f, 0x2d, b"ja") + assert r + + def test_sex_put(self, card): + r = card.cmd_put_data(0x5f, 0x35, b"1") + assert r + + def test_url_put(self, card): + r = card.cmd_put_data(0x5f, 0x50, b"https://www.fsij.org/gnuk/") + assert r + + def test_pw1_status_put(self, card): + r = card.cmd_put_data(0x00, 0xc4, b"\x01") + assert r + + def test_login(self, card): + login = get_data_object(card, 0x5e) + assert login == b"gpg_user" + + def test_name_lang_sex(self, card): + name = b"GnuPG User" + lang = b"ja" + sex = b"1" + expected = b'\x5b' + pack('B', len(name)) + name \ + + b'\x5f\x2d' + pack('B', len(lang)) + lang \ + + b'\x5f\x35' + pack('B', len(sex)) + sex + name_lang_sex = get_data_object(card, 0x65) + assert name_lang_sex == expected + + def test_url(self, card): + url = get_data_object(card, 0x5f50) + assert url == b"https://www.fsij.org/gnuk/" + + def test_pw1_status(self, card): + s = get_data_object(card, 0xc4) + assert match(b'\x01...\x03[\x00\x03]\x03', s, DOTALL) diff --git a/tests/card_test_personalize_card_2.py b/tests/card_test_personalize_card_2.py new file mode 100644 index 0000000..ce9f10d --- /dev/null +++ b/tests/card_test_personalize_card_2.py @@ -0,0 +1,200 @@ +""" +card_test_personalize_card.py - test personalizing card + +Copyright (C) 2016, 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from struct import pack +from re import match, DOTALL +from util import * +from pubkey_crypto import get_PK_Crypto, get_key +from card_const import * +from constants_for_test import * +import pytest + +class Test_Card_Personalize_Card_2(object): + def test_import_key_1(self, card): + key = get_key(card) + t = key[0].build_privkey_template(card.is_yubikey) + r = card.cmd_put_data_odd(0x3f, 0xff, t) + assert r + + def test_import_key_2(self, card): + key = get_key(card) + t = key[1].build_privkey_template(card.is_yubikey) + r = card.cmd_put_data_odd(0x3f, 0xff, t) + assert r + + def test_import_key_3(self, card): + key = get_key(card) + t = key[2].build_privkey_template(card.is_yubikey) + r = card.cmd_put_data_odd(0x3f, 0xff, t) + assert r + + def test_fingerprint_1_put(self, card): + key = get_key(card) + fpr1 = key[0].get_fpr() + r = card.cmd_put_data(0x00, 0xc7, fpr1) + assert r + + def test_fingerprint_2_put(self, card): + key = get_key(card) + fpr2 = key[1].get_fpr() + r = card.cmd_put_data(0x00, 0xc8, fpr2) + assert r + + def test_fingerprint_3_put(self, card): + key = get_key(card) + fpr3 = key[2].get_fpr() + r = card.cmd_put_data(0x00, 0xc9, fpr3) + assert r + + def test_timestamp_1_put(self, card): + key = get_key(card) + timestamp1 = key[0].get_timestamp() + r = card.cmd_put_data(0x00, 0xce, timestamp1) + assert r + + def test_timestamp_2_put(self, card): + key = get_key(card) + timestamp2 = key[1].get_timestamp() + r = card.cmd_put_data(0x00, 0xcf, timestamp2) + assert r + + def test_timestamp_3_put(self, card): + key = get_key(card) + timestamp3 = key[2].get_timestamp() + r = card.cmd_put_data(0x00, 0xd0, timestamp3) + assert r + + def test_ds_counter_0(self, card): + c = get_data_object(card, 0x7a) + assert c == b'\x93\x03\x00\x00\x00' + + def test_pw1_status(self, card): + s = get_data_object(card, 0xc4) + assert match(b'\x01...\x03[\x00\x03]\x03', s, DOTALL) + + def test_app_data(self, card): + if card.is_yubikey: + pytest.skip("Yubikey raises 6e82 error for composed data object 6E") + else: + app_data = get_data_object(card, 0x6e) + hist_len = app_data[20] + # FIXME: parse and check DO of C0, C1, C2, C3, C4, and C6 + assert app_data[0:8] == b"\x4f\x10\xd2\x76\x00\x01\x24\x01" and \ + app_data[18:18+2] == b"\x5f\x52" + + def test_public_key_1(self, card): + key = get_key(card) + PK_Crypto = get_PK_Crypto(card) + pk_info = card.cmd_get_public_key(1) + assert key[0].get_pk() == PK_Crypto.pk_from_pk_info(pk_info) + + def test_public_key_2(self, card): + key = get_key(card) + PK_Crypto = get_PK_Crypto(card) + pk_info = card.cmd_get_public_key(2) + assert key[1].get_pk() == PK_Crypto.pk_from_pk_info(pk_info) + + def test_public_key_3(self, card): + key = get_key(card) + PK_Crypto = get_PK_Crypto(card) + pk_info = card.cmd_get_public_key(3) + assert key[2].get_pk() == PK_Crypto.pk_from_pk_info(pk_info) + + def test_setup_pw1_0(self, card): + r = card.change_passwd(1, FACTORY_PASSPHRASE_PW1, PW1_TEST0) + assert r + + def test_verify_pw1_0(self, card): + v = card.verify(1, PW1_TEST0) + assert v + + def test_verify_pw1_0_2(self, card): + v = card.verify(2, PW1_TEST0) + assert v + + def test_setup_pw1_1(self, card): + r = card.change_passwd(1, PW1_TEST0, PW1_TEST1) + assert r + + def test_verify_pw1_1(self, card): + v = card.verify(1, PW1_TEST1) + assert v + + def test_verify_pw1_1_2(self, card): + v = card.verify(2, PW1_TEST1) + assert v + + def test_setup_reset_code(self, card): + r = card.setup_reset_code(RESETCODE_TEST) + assert r + + def test_reset_code(self, card): + r = card.reset_passwd_by_resetcode(RESETCODE_TEST, PW1_TEST2) + assert r + + def test_verify_pw1_2(self, card): + v = card.verify(1, PW1_TEST2) + assert v + + def test_verify_pw1_2_2(self, card): + v = card.verify(2, PW1_TEST2) + assert v + + def test_setup_pw3_1(self, card): + r = card.change_passwd(3, PW3_TEST0, PW3_TEST1) + assert r + + def test_verify_pw3_1(self, card): + v = card.verify(3, PW3_TEST1) + assert v + + def test_reset_userpass_admin(self, card): + r = card.reset_passwd_by_admin(PW1_TEST3) + assert r + + def test_verify_pw1_3(self, card): + v = card.verify(1, PW1_TEST3) + assert v + + def test_verify_pw1_3_2(self, card): + v = card.verify(2, PW1_TEST3) + assert v + + def test_setup_pw1_4(self, card): + r = card.change_passwd(1, PW1_TEST3, PW1_TEST4) + assert r + + def test_verify_pw1_4(self, card): + v = card.verify(1, PW1_TEST4) + assert v + + def test_verify_pw1_4_2(self, card): + v = card.verify(2, PW1_TEST4) + assert v + + def test_setup_pw3_2(self, card): + r = card.change_passwd(3, PW3_TEST1, PW3_TEST0) + assert r + + def test_verify_pw3_2(self, card): + v = card.verify(3, PW3_TEST0) + assert v diff --git a/tests/card_test_personalize_reset.py b/tests/card_test_personalize_reset.py new file mode 100644 index 0000000..11676b4 --- /dev/null +++ b/tests/card_test_personalize_reset.py @@ -0,0 +1,85 @@ +""" +card_test_personalize_reset.py - test resetting personalization of card + +Copyright (C) 2016, 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from struct import pack +from re import match, DOTALL +from util import * +from card_const import * +from constants_for_test import * +import pytest + +class Test_Personalize_Reset(object): + def test_login_put(self, card): + r = card.cmd_put_data(0x00, 0x5e, b"") + assert r + + def test_name_put(self, card): + r = card.cmd_put_data(0x00, 0x5b, b"") + assert r + + def test_lang_put(self, card): + r = card.cmd_put_data(0x5f, 0x2d, b"") + assert r + + def test_sex_put(self, card): + try: + # Gnuk + r = card.cmd_put_data(0x5f, 0x35, b"") + except ValueError: + # OpenPGP card which doesn't allow b"" + r = card.cmd_put_data(0x5f, 0x35, b"9") + assert r + + def test_url_put(self, card): + r = card.cmd_put_data(0x5f, 0x50, b"") + assert r + + def test_pw1_status_put(self, card): + r = card.cmd_put_data(0x00, 0xc4, b"\x00") + assert r + + def test_setup_pw3_0(self, card): + r = card.change_passwd(3, PW3_TEST0, FACTORY_PASSPHRASE_PW3) + assert r + + def test_verify_pw3_0(self, card): + v = card.verify(3, FACTORY_PASSPHRASE_PW3) + assert v + + def test_setup_pw1_0(self, card): + r = card.change_passwd(1, PW1_TEST4, FACTORY_PASSPHRASE_PW1) + assert r + + def test_verify_pw1_0(self, card): + v = card.verify(1, FACTORY_PASSPHRASE_PW1) + assert v + + def test_verify_pw1_0_2(self, card): + v = card.verify(2, FACTORY_PASSPHRASE_PW1) + assert v + + def test_delete_reset_code(self, card): + if card.is_yubikey: + pytest.skip("Yubikey raises 6a80 error for data object D3") + else: + r = card.cmd_put_data(0x00, 0xd3, b"") + assert r diff --git a/tests/card_test_public_key_operations.py b/tests/card_test_public_key_operations.py new file mode 100644 index 0000000..07c248e --- /dev/null +++ b/tests/card_test_public_key_operations.py @@ -0,0 +1,81 @@ +""" +card_test_public_key_operations.py - test the sign/dec/auth + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from struct import pack +from re import match, DOTALL +from util import * +from pubkey_crypto import get_PK_Crypto, get_key, get_test_vector +from card_const import * +from constants_for_test import * + +class Test_Card_PK_OPs(object): + def test_sign_0(self, card): + test_vector = get_test_vector(card) + key = get_key(card) + PK_Crypto = get_PK_Crypto(card) + digestinfo = PK_Crypto.compute_digestinfo(test_vector['sign_0']) + sig = card.cmd_pso(0x9e, 0x9a, digestinfo) + r = key[0].verify_signature(digestinfo, sig) + assert r + + def test_sign_1(self, card): + test_vector = get_test_vector(card) + key = get_key(card) + PK_Crypto = get_PK_Crypto(card) + digestinfo = PK_Crypto.compute_digestinfo(test_vector['sign_1']) + sig = card.cmd_pso(0x9e, 0x9a, digestinfo) + r = key[0].verify_signature(digestinfo, sig) + assert r + + def test_decrypt_0(self, card): + test_vector = get_test_vector(card) + key = get_key(card) + PK_Crypto = get_PK_Crypto(card) + encrypted_data = key[1].encrypt(test_vector['decrypt_0']) + r = card.cmd_pso(0x80, 0x86, PK_Crypto.enc_data(encrypted_data)) + assert PK_Crypto.enc_check(encrypted_data, r) + + def test_decrypt_1(self, card): + test_vector = get_test_vector(card) + key = get_key(card) + PK_Crypto = get_PK_Crypto(card) + encrypted_data = key[1].encrypt(test_vector['decrypt_1']) + r = card.cmd_pso(0x80, 0x86, PK_Crypto.enc_data(encrypted_data)) + assert PK_Crypto.enc_check(encrypted_data, r) + + def test_auth_0(self, card): + test_vector = get_test_vector(card) + key = get_key(card) + PK_Crypto = get_PK_Crypto(card) + digestinfo = PK_Crypto.compute_digestinfo(test_vector['auth_0']) + sig = card.cmd_internal_authenticate(digestinfo) + r = key[2].verify_signature(digestinfo, sig) + assert r + + def test_auth_1(self, card): + test_vector = get_test_vector(card) + key = get_key(card) + PK_Crypto = get_PK_Crypto(card) + digestinfo = PK_Crypto.compute_digestinfo(test_vector['auth_1']) + sig = card.cmd_internal_authenticate(digestinfo) + r = key[2].verify_signature(digestinfo, sig) + assert r diff --git a/tests/card_test_public_key_operations_alt.py b/tests/card_test_public_key_operations_alt.py new file mode 100644 index 0000000..aa90245 --- /dev/null +++ b/tests/card_test_public_key_operations_alt.py @@ -0,0 +1,83 @@ +""" +card_test_public_key_operations_alt.py - test the sign/dec/auth + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from struct import pack +from re import match, DOTALL +from util import * +from pubkey_crypto import get_PK_Crypto, get_key, get_test_vector +from card_const import * +from constants_for_test import * + +class Test_Card_PK_OPs(object): + def test_sign_0(self, card): + test_vector = get_test_vector(card) + key = get_key(card) + PK_Crypto = get_PK_Crypto(card) + digestinfo = PK_Crypto.compute_digestinfo(test_vector['sign_0']) + r = card.cmd_pso(0x9e, 0x9a, digestinfo) + sig_bytes = key[0].compute_signature(digestinfo) + assert r == sig_bytes + + # Since forcesig setting, failed + def test_sign_1(self, card): + test_vector = get_test_vector(card) + PK_Crypto = get_PK_Crypto(card) + digestinfo = PK_Crypto.compute_digestinfo(test_vector['sign_1']) + try: + r = card.cmd_pso(0x9e, 0x9a, digestinfo) + except ValueError as e: + r = e.args[0] + assert r == "6982" + + def test_decrypt_0(self, card): + test_vector = get_test_vector(card) + key = get_key(card) + encrypted_data = key[1].encrypt(test_vector['decrypt_0']) + PK_Crypto = get_PK_Crypto(card) + r = card.cmd_pso(0x80, 0x86, PK_Crypto.enc_data(encrypted_data)) + assert PK_Crypto.enc_check(encrypted_data, r) + + def test_decrypt_1(self, card): + test_vector = get_test_vector(card) + key = get_key(card) + PK_Crypto = get_PK_Crypto(card) + encrypted_data = key[1].encrypt(test_vector['decrypt_1']) + r = card.cmd_pso(0x80, 0x86, PK_Crypto.enc_data(encrypted_data)) + assert PK_Crypto.enc_check(encrypted_data, r) + + def test_auth_0(self, card): + test_vector = get_test_vector(card) + key = get_key(card) + PK_Crypto = get_PK_Crypto(card) + digestinfo = PK_Crypto.compute_digestinfo(test_vector['auth_0']) + r = card.cmd_internal_authenticate(digestinfo) + sig_bytes = key[2].compute_signature(digestinfo) + assert r == sig_bytes + + def test_auth_1(self, card): + test_vector = get_test_vector(card) + key = get_key(card) + PK_Crypto = get_PK_Crypto(card) + digestinfo = PK_Crypto.compute_digestinfo(test_vector['auth_1']) + r = card.cmd_internal_authenticate(digestinfo) + sig_bytes = key[2].compute_signature(digestinfo) + assert r == sig_bytes diff --git a/tests/card_test_public_key_operations_kg.py b/tests/card_test_public_key_operations_kg.py new file mode 100644 index 0000000..d77729a --- /dev/null +++ b/tests/card_test_public_key_operations_kg.py @@ -0,0 +1,58 @@ +""" +card_test_public_key_operations_kg.py - test the sign/dec/auth + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from struct import pack +from re import match, DOTALL +from util import * +from pubkey_crypto import get_PK_Crypto, get_test_vector +from card_const import * +from constants_for_test import * + +class Test_Card_PK_OPs_KG(object): + def test_signature_sigkey(self, card): + test_vector = get_test_vector(card) + PK_Crypto = get_PK_Crypto(card) + pk_info = card.cmd_get_public_key(1) + k = PK_Crypto(0, pk_info=pk_info) + digestinfo = PK_Crypto.compute_digestinfo(test_vector['sign_0']) + sig_bytes = card.cmd_pso(0x9e, 0x9a, digestinfo) + r = k.verify_signature(digestinfo, sig_bytes) + assert r + + def test_decryption(self, card): + test_vector = get_test_vector(card) + PK_Crypto = get_PK_Crypto(card) + pk_info = card.cmd_get_public_key(2) + k = PK_Crypto(1, pk_info=pk_info) + encrypted_data = k.encrypt(test_vector['encrypt_0']) + r = card.cmd_pso(0x80, 0x86, PK_Crypto.enc_data(encrypted_data)) + assert PK_Crypto.enc_check(encrypted_data, r) + + def test_signature_authkey(self, card): + test_vector = get_test_vector(card) + PK_Crypto = get_PK_Crypto(card) + pk_info = card.cmd_get_public_key(3) + k = PK_Crypto(2, pk_info=pk_info) + digestinfo = PK_Crypto.compute_digestinfo(test_vector['sign_0']) + sig_bytes = card.cmd_internal_authenticate(digestinfo) + r = k.verify_signature(digestinfo, sig_bytes) + assert r diff --git a/tests/card_test_remove_keys.py b/tests/card_test_remove_keys.py new file mode 100644 index 0000000..133bcc6 --- /dev/null +++ b/tests/card_test_remove_keys.py @@ -0,0 +1,45 @@ +""" +card_test_remove_keys.py - test removing keys on card + +Copyright (C) 2016, 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +# Remove a key material on card by changing algorithm attributes of the key + +from card_const import * + +class Test_Remove_Keys(object): + + def test_keyattr_change_1(self, card): + r = card.cmd_put_data(0x00, 0xc1, alt_key(card, False)) + if r: + r = card.cmd_put_data(0x00, 0xc1, default_key(card, False)) + assert r + + def test_keyattr_change_2(self, card): + r = card.cmd_put_data(0x00, 0xc2, alt_key(card, True)) + if r: + r = card.cmd_put_data(0x00, 0xc2, default_key(card, True)) + assert r + + def test_keyattr_change_3(self, card): + r = card.cmd_put_data(0x00, 0xc3, alt_key(card, False)) + if r: + r = card.cmd_put_data(0x00, 0xc3, default_key(card, False)) + assert r diff --git a/tests/card_test_reset_attr.py b/tests/card_test_reset_attr.py new file mode 100644 index 0000000..653d3b9 --- /dev/null +++ b/tests/card_test_reset_attr.py @@ -0,0 +1,69 @@ +""" +card_test_reset_attr.py - test resetting key attributes + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from util import * +from card_const import * +import pytest + +class Test_Reset_ATTRS(object): + def test_verify_pw3(self, card): + v = card.verify(3, FACTORY_PASSPHRASE_PW3) + assert v + + def test_keyattr_reset_1(self, card): + a = card.saved_attribute(1) + r = card.cmd_put_data(0x00, 0xc1, a) + assert r + if card.is_yubikey: + pytest.skip("Yubikey returns no attr when no key") + else: + a1 = get_data_object(card, 0xc1) + if card.is_gnuk: + assert a1 == a + else: + pytest.skip("Zeitcontrol returns None when no key") + + def test_keyattr_reset_2(self, card): + a = card.saved_attribute(2) + r = card.cmd_put_data(0x00, 0xc2, a) + assert r + if card.is_yubikey: + pytest.skip("Yubikey returns no attr when no key") + else: + a1 = get_data_object(card, 0xc2) + if card.is_gnuk: + assert a1 == a + else: + pytest.skip("Zeitcontrol returns None when no key") + + def test_keyattr_reset_3(self, card): + a = card.saved_attribute(3) + r = card.cmd_put_data(0x00, 0xc3, a) + assert r + if card.is_yubikey: + pytest.skip("Yubikey returns no attr when no key") + else: + a1 = get_data_object(card, 0xc3) + if card.is_gnuk: + assert a1 == a + else: + pytest.skip("Zeitcontrol returns None when no key") diff --git a/tests/card_test_reset_pw3.py b/tests/card_test_reset_pw3.py new file mode 100644 index 0000000..a757f01 --- /dev/null +++ b/tests/card_test_reset_pw3.py @@ -0,0 +1,46 @@ +""" +card_test_reset_pw3.py - test resetting pw3 + +Copyright (C) 2018, 2019 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from card_const import * +import pytest + +class Test_Reset_PW3(object): + # Gnuk specific feature of clear PW3 + def test_setup_pw3_null(self, card): + if card.is_gnuk: + r = card.change_passwd(3, FACTORY_PASSPHRASE_PW3, b'', kdf_change=-1) + assert r + else: + pytest.skip("Gnuk only feature of clearing PW3") + + def test_verify_pw3(self, card): + v = card.verify(3, FACTORY_PASSPHRASE_PW3) + assert v + + # Check PW1 again to see the possiblity of admin-less mode + def test_verify_pw1(self, card): + v = card.verify(1, FACTORY_PASSPHRASE_PW1) + assert v + + def test_verify_pw1_2(self, card): + v = card.verify(2, FACTORY_PASSPHRASE_PW1) + assert v diff --git a/tests/card_test_set_attr.py b/tests/card_test_set_attr.py new file mode 100644 index 0000000..4598d19 --- /dev/null +++ b/tests/card_test_set_attr.py @@ -0,0 +1,41 @@ +""" +card_test_set_attr.py - test setting key attributes + +Copyright (C) 2021 g10 Code GmbH +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from card_const import * +import pytest + +class Test_Set_ATTRS(object): + def test_verify_pw3(self, card): + v = card.verify(3, FACTORY_PASSPHRASE_PW3) + assert v + + def test_keyattr_set_1(self, card): + r = card.cmd_put_data(0x00, 0xc1, default_key(card, False)) + assert r + + def test_keyattr_set_2(self, card): + r = card.cmd_put_data(0x00, 0xc2, default_key(card, True)) + assert r + + def test_keyattr_set_3(self, card): + r = card.cmd_put_data(0x00, 0xc3, default_key(card, False)) + assert r diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..aea50bb --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,18 @@ +import pytest +from card_reader import get_ccid_device +from openpgp_card import OpenPGP_Card + +def pytest_addoption(parser): + parser.addoption("--reader", dest="reader", type=str, action="store", + default="gnuk", help="specify reader: gnuk or gemalto") + +@pytest.fixture(scope="session") +def card(): + print() + print("Test start!") + reader = get_ccid_device() + card = OpenPGP_Card(reader) + card.cmd_select_openpgp() + yield card + del card + reader.ccid_power_off() diff --git a/tests/constants_for_test.py b/tests/constants_for_test.py new file mode 100644 index 0000000..96ef64e --- /dev/null +++ b/tests/constants_for_test.py @@ -0,0 +1,17 @@ +PW1_TEST0=b"another user pass phrase" +PW1_TEST1=b"PASSPHRASE SHOULD BE LONG" +PW1_TEST2=b"new user pass phrase" +PW1_TEST3=b"next user pass phrase" +PW1_TEST4=b"another user pass phrase" +PW3_TEST0=b"admin pass phrase" +PW3_TEST1=b"another admin pass phrase" + +RESETCODE_TEST=b"example reset code 000" + +KDF_FULL=b"\x81\x01\x03\x82\x01\x08\x83\x04\x00\xC8\x00\x00\x84\x08\xC5\x1F\xB7\xF9\xEE\xF3\xD3\xE8\x85\x08\x75\x1A\x2A\x70\xC0\x7C\xB1\x81\x86\x08\xE6\xB2\x4E\x0C\xEE\x92\xAB\x93\x87\x20\xA2\x08\x89\x16\x09\xD3\xE4\x3D\x48\xAC\x5B\xAA\xBD\xE9\xF1\xB8\xD2\xFC\xD8\x3C\x5F\x35\xCB\xEB\xDA\xFB\x75\x92\x88\xC6\x8B\x2D\x88\x20\xF1\x34\x00\xD2\x5F\x28\x57\xE6\x66\xAA\x9E\xBB\xE0\x7C\x57\x4B\x84\x8B\xD6\x52\x14\xE7\x31\x99\x1A\x3D\x2D\xAA\x3F\x8A\x7F\x03" +KDF_FULL_HASH_PW1=b"\xA2\x08\x89\x16\x09\xD3\xE4\x3D\x48\xAC\x5B\xAA\xBD\xE9\xF1\xB8\xD2\xFC\xD8\x3C\x5F\x35\xCB\xEB\xDA\xFB\x75\x92\x88\xC6\x8B\x2D" +KDF_FULL_HASH_PW3=b"\xF1\x34\x00\xD2\x5F\x28\x57\xE6\x66\xAA\x9E\xBB\xE0\x7C\x57\x4B\x84\x8B\xD6\x52\x14\xE7\x31\x99\x1A\x3D\x2D\xAA\x3F\x8A\x7F\x03" + +KDF_SINGLE=b"\x81\x01\x03\x82\x01\x08\x83\x04\x00\xC8\x00\x00\x84\x08\x69\x9E\xDA\xAD\x5A\x72\x5F\x4C\x87\x20\x12\xDB\x16\x9A\x9D\x1A\x56\xCA\x2B\x9E\x96\x7F\xC4\xDA\xB8\xAE\x70\x41\x51\x8E\x3C\xEF\x49\x7E\xC2\x56\x60\x32\x2F\x4C\x03\x07\x88\x20\x1C\x93\x99\xC3\x6D\x49\x92\x27\x54\x39\x76\x7E\xA7\xB4\xEE\xE5\x16\xDA\x92\xC0\x0E\xF4\x74\xBD\x01\x28\x0F\x0C\x30\x45\x4B\xBB" +KDF_SINGLE_HASH_PW1=b"\x12\xDB\x16\x9A\x9D\x1A\x56\xCA\x2B\x9E\x96\x7F\xC4\xDA\xB8\xAE\x70\x41\x51\x8E\x3C\xEF\x49\x7E\xC2\x56\x60\x32\x2F\x4C\x03\x07" +KDF_SINGLE_HASH_PW3=b"\x1C\x93\x99\xC3\x6D\x49\x92\x27\x54\x39\x76\x7E\xA7\xB4\xEE\xE5\x16\xDA\x92\xC0\x0E\xF4\x74\xBD\x01\x28\x0F\x0C\x30\x45\x4B\xBB" diff --git a/tests/curve25519_keys.py b/tests/curve25519_keys.py new file mode 100644 index 0000000..571fbf2 --- /dev/null +++ b/tests/curve25519_keys.py @@ -0,0 +1,124 @@ +from time import time +from struct import pack +from hashlib import sha1, sha256 +from pk_25519_with_libgcrypt import fixup_scalar_cv25519, call_pk_sign, call_pk_encrypt, call_pk_verify +from card_const import KEY_ATTRIBUTES_CV25519, KEY_ATTRIBUTES_ED25519 + +class PK_Crypto(object): + @staticmethod + def pk_from_pk_info(pk_info): + return b'\x40' + pk_info[5:] + + @staticmethod + def compute_digestinfo(msg): + return sha256(msg).digest() + + @staticmethod + def enc_data(enc_info): + return b'\xa6\x25\x7f\x49\x22\x86\x20' + enc_info[1] + + @staticmethod + def enc_check(enc_info, s): + return enc_info[0] == s + + + def __init__(self, keyno=None, pk_info=None, data=None): + if keyno == None: + # Just for name space + return + + self.keyno = keyno + self.for_encryption = (self.keyno == 1) + self.timestamp = pack('>I', int(time())) + if pk_info: + # Public part only (no private data) from card + self.q = pk_info[5:] + else: + if self.for_encryption: + # Private part (in big endian), while DATA is native (little) endian + d = data[0] + self.d = fixup_scalar_cv25519(d) + else: + # Private part (in little (native) endian) + self.d = data[0] + self.q = data[1] + self.fpr = self.calc_fpr() + + def calc_fpr(self): + m_len = 6 + 2 + 33 + ver = b'\x04' + algo = b'\x12' if self.for_encryption else b'\x16' + m = b'\x99' + pack('>H', m_len) + ver + self.timestamp + algo \ + + pack('>H', 256+7) + b'\x40' + self.q + return sha1(m).digest() + + def build_privkey_template(self, is_yubikey): + openpgp_keyno = self.keyno + 1 + if openpgp_keyno == 1: + keyspec = b'\xb6' + elif openpgp_keyno == 2: + keyspec = b'\xb8' + else: + keyspec = b'\xa4' + key_template = b'\x92\x20' + exthdr = keyspec + b'\x00' + b'\x7f\x48' + b'\x02' + key_template + suffix = b'\x5f\x48' + b'\x20' + return b'\x4d' + b'\x2a' + exthdr + suffix + self.d + + def compute_signature(self, digestinfo): + return call_pk_sign(self.d, digestinfo) + + def verify_signature(self, digestinfo, sig): + return call_pk_verify(self.q, digestinfo, sig) + + def encrypt(self, plaintext): + # Do ECDH + return call_pk_encrypt(self.q, plaintext) + + def get_fpr(self): + return self.fpr + + def get_timestamp(self): + return self.timestamp + + def get_pk(self): + return b'\x40' + self.q + +key = [ None, None, None ] + +ed25519_data = (b'\x83\x3f\xe6\x24\x09\x23\x7b\x9d\x62\xec\x77\x58\x75\x20\x91\x1e' + b'\x9a\x75\x9c\xec\x1d\x19\x75\x5b\x7d\xa9\x01\xb9\x6d\xca\x3d\x42', + b'\xec\x17\x2b\x93\xad\x5e\x56\x3b\xf4\x93\x2c\x70\xe1\x24\x50\x34' + b'\xc3\x54\x67\xef\x2e\xfd\x4d\x64\xeb\xf8\x19\x68\x34\x67\xe2\xbf') + +cv25519_data = (b'\x77\x07\x6d\x0a\x73\x18\xa5\x7d\x3c\x16\xc1\x72\x51\xb2\x66\x45' + b'\xdf\x4c\x2f\x87\xeb\xc0\x99\x2a\xb1\x77\xfb\xa5\x1d\xb9\x2c\x2a', + b'\x85\x20\xf0\x09\x89\x30\xa7\x54\x74\x8b\x7d\xdc\xb4\x3e\xf7\x5a' + b'\x0d\xbf\x3a\x0d\x26\x38\x1a\xf4\xeb\xa4\xa9\x8e\xaa\x9b\x4e\x6a') + +key[0] = PK_Crypto(0, data=ed25519_data) +key[1] = PK_Crypto(1, data=cv25519_data) +key[2] = PK_Crypto(2, data=ed25519_data) + +PLAIN_TEXT0=b"This is a test message." +PLAIN_TEXT1=b"cryptography is as easy as pie." + +ENCRYPT_TEXT0 = sha256(b"encrypt me please").digest() +ENCRYPT_TEXT1 = sha256(b"encrypt me please, another").digest() +ENCRYPT_TEXT2 = sha256(b"encrypt me please, the other").digest() + +test_vector = { + 'sign_0' : PLAIN_TEXT0, + 'sign_1' : PLAIN_TEXT1, + 'auth_0' : PLAIN_TEXT0, + 'auth_1' : PLAIN_TEXT1, + 'decrypt_0' : ENCRYPT_TEXT0, + 'decrypt_1' : ENCRYPT_TEXT1, + 'encrypt_0' : ENCRYPT_TEXT2, +} + +curve25519_pk = PK_Crypto() +curve25519_pk.test_vector = test_vector +curve25519_pk.key_list = key +curve25519_pk.key_attr_list = [KEY_ATTRIBUTES_ED25519, KEY_ATTRIBUTES_CV25519, KEY_ATTRIBUTES_ED25519] +curve25519_pk.PK_Crypto = PK_Crypto diff --git a/tests/kdf_calc.py b/tests/kdf_calc.py new file mode 120000 index 0000000..e0ae7fb --- /dev/null +++ b/tests/kdf_calc.py @@ -0,0 +1 @@ +../tool/kdf_calc.py \ No newline at end of file diff --git a/tests/nistp256r1_keys.py b/tests/nistp256r1_keys.py new file mode 100644 index 0000000..7c0b6f9 --- /dev/null +++ b/tests/nistp256r1_keys.py @@ -0,0 +1,152 @@ +from time import time +from struct import pack +from hashlib import sha1, sha256 +from pk_signed_mpi_with_libgcrypt import PK_libgcrypt +from card_const import KEY_ATTRIBUTES_ECDH_NISTP256R1, KEY_ATTRIBUTES_ECDSA_NISTP256R1 + +lg_nistp256 = PK_libgcrypt(32, "NIST P-256") + +class PK_Crypto(object): + @staticmethod + def pk_from_pk_info(pk_info): + return pk_info[5:] + + @staticmethod + def compute_digestinfo(msg): + return sha256(msg).digest() + + @staticmethod + def enc_data(enc_info): + return b'\xa6\x46\x7f\x49\x43\x86\x41' + enc_info[1] + + @staticmethod + def enc_check(enc_info, s): + point = enc_info[0] + # Gnuk returns point, instead of X + if len(s) == len(point): + return point == s + else: + # It's 04 || X || Y, extract X + return point[1:33] == s + + + def __init__(self, keyno=None, pk_info=None, data=None): + if keyno == None: + # Just for name space + return + + self.keyno = keyno + self.for_encryption = (self.keyno == 1) + self.timestamp = pack('>I', int(time())) + if pk_info: + # Public part only (no private data) from card + self.q = pk_info[5:] + else: + # Private part (in big endian) + self.d = data[0] + self.q = data[1] + self.fpr = self.calc_fpr() + + def calc_fpr(self): + m_len = 6 + 2 + 65 + ver = b'\x04' + algo = b'\x12' if self.for_encryption else b'\x16' + m = b'\x99' + pack('>H', m_len) + ver + self.timestamp + algo \ + + pack('>H', 512+3) + self.q + return sha1(m).digest() + + def build_privkey_template(self, is_yubikey): + openpgp_keyno = self.keyno + 1 + if openpgp_keyno == 1: + keyspec = b'\xb6' + elif openpgp_keyno == 2: + keyspec = b'\xb8' + else: + keyspec = b'\xa4' + key_template = b'\x92\x20' + exthdr = keyspec + b'\x00' + b'\x7f\x48' + b'\x02' + key_template + suffix = b'\x5f\x48' + b'\x20' + return b'\x4d' + b'\x2a' + exthdr + suffix + self.d + + def compute_signature(self, digestinfo): + return lg_nistp256.call_pk_sign(self.d, digestinfo) + + def verify_signature(self, digestinfo, sig): + return lg_nistp256.call_pk_verify(self.q, digestinfo, sig) + + def encrypt(self, plaintext): + # Do ECDH + return lg_nistp256.call_pk_encrypt(self.q, plaintext) + + def get_fpr(self): + return self.fpr + + def get_timestamp(self): + return self.timestamp + + def get_pk(self): + return self.q + +key = [ None, None, None ] + +# https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/components/186-3ecdsasiggencomponenttestvectors.zip + +nistp256_data0 = ( + b'\x51\x9b\x42\x3d\x71\x5f\x8b\x58\x1f\x4f\xa8\xee\x59\xf4\x77\x1a' + b'\x5b\x44\xc8\x13\x0b\x4e\x3e\xac\xca\x54\xa5\x6d\xda\x72\xb4\x64', + b'\x04' + b'\x1c\xcb\xe9\x1c\x07\x5f\xc7\xf4\xf0\x33\xbf\xa2\x48\xdb\x8f\xcc' + b'\xd3\x56\x5d\xe9\x4b\xbf\xb1\x2f\x3c\x59\xff\x46\xc2\x71\xbf\x83' + b'\xce\x40\x14\xc6\x88\x11\xf9\xa2\x1a\x1f\xdb\x2c\x0e\x61\x13\xe0' + b'\x6d\xb7\xca\x93\xb7\x40\x4e\x78\xdc\x7c\xcd\x5c\xa8\x9a\x4c\xa9' +) + +nistp256_data2 = ( + b'\x0f\x56\xdb\x78\xca\x46\x0b\x05\x5c\x50\x00\x64\x82\x4b\xed\x99' + b'\x9a\x25\xaa\xf4\x8e\xbb\x51\x9a\xc2\x01\x53\x7b\x85\x47\x98\x13', + b'\x04' + b'\xe2\x66\xdd\xfd\xc1\x26\x68\xdb\x30\xd4\xca\x3e\x8f\x77\x49\x43' + b'\x2c\x41\x60\x44\xf2\xd2\xb8\xc1\x0b\xf3\xd4\x01\x2a\xef\xfa\x8a' + b'\xbf\xa8\x64\x04\xa2\xe9\xff\xe6\x7d\x47\xc5\x87\xef\x7a\x97\xa7' + b'\xf4\x56\xb8\x63\xb4\xd0\x2c\xfc\x69\x28\x97\x3a\xb5\xb1\xcb\x39' +) + +# https://tools.ietf.org/html/rfc5903#section-8.1 +nistp256_data1 = ( + b'\xc8\x8f\x01\xf5\x10\xd9\xac\x3f\x70\xa2\x92\xda\xa2\x31\x6d\xe5' + b'\x44\xe9\xaa\xb8\xaf\xe8\x40\x49\xc6\x2a\x9c\x57\x86\x2d\x14\x33', + b'\x04' + b'\xda\xd0\xb6\x53\x94\x22\x1c\xf9\xb0\x51\xe1\xfe\xca\x57\x87\xd0' + b'\x98\xdf\xe6\x37\xfc\x90\xb9\xef\x94\x5d\x0c\x37\x72\x58\x11\x80' + b'\x52\x71\xa0\x46\x1c\xdb\x82\x52\xd6\x1f\x1c\x45\x6f\xa3\xe5\x9a' + b'\xb1\xf4\x5b\x33\xac\xcf\x5f\x58\x38\x9e\x05\x77\xb8\x99\x0b\xb3' +) + +key[0] = PK_Crypto(0, data=nistp256_data0) +key[1] = PK_Crypto(1, data=nistp256_data1) +key[2] = PK_Crypto(2, data=nistp256_data2) + +PLAIN_TEXT0=b"In this test, we verify card generated result by libgcrypt." +PLAIN_TEXT1=b"Signature is non-deterministic (it uses nonce K internally)." +PLAIN_TEXT2=b"We don't use NIST P-256 test vectors (it specifies K of ECDSA)." +PLAIN_TEXT3=b"NOTE: Our test is not for ECDSA implementation itself." + +ENCRYPT_TEXT0 = sha256(b"encrypt me please").digest() +ENCRYPT_TEXT1 = sha256(b"encrypt me please, another").digest() +ENCRYPT_TEXT2 = sha256(b"encrypt me please, the other").digest() + +test_vector = { + 'sign_0' : PLAIN_TEXT0, + 'sign_1' : PLAIN_TEXT1, + 'auth_0' : PLAIN_TEXT2, + 'auth_1' : PLAIN_TEXT3, + 'decrypt_0' : ENCRYPT_TEXT0, + 'decrypt_1' : ENCRYPT_TEXT1, + 'encrypt_0' : ENCRYPT_TEXT2, +} + +nistp256r1_pk = PK_Crypto() +nistp256r1_pk.test_vector = test_vector +nistp256r1_pk.key_list = key +nistp256r1_pk.key_attr_list = [KEY_ATTRIBUTES_ECDSA_NISTP256R1, KEY_ATTRIBUTES_ECDH_NISTP256R1, KEY_ATTRIBUTES_ECDSA_NISTP256R1] +nistp256r1_pk.PK_Crypto = PK_Crypto diff --git a/tests/nistp384r1_keys.py b/tests/nistp384r1_keys.py new file mode 100644 index 0000000..c421e9c --- /dev/null +++ b/tests/nistp384r1_keys.py @@ -0,0 +1,158 @@ +from time import time +from struct import pack +from hashlib import sha1, sha384 +from pk_signed_mpi_with_libgcrypt import PK_libgcrypt +from card_const import KEY_ATTRIBUTES_ECDH_NISTP384R1, KEY_ATTRIBUTES_ECDSA_NISTP384R1 + +lg_nistp384 = PK_libgcrypt(48, "NIST P-384") + +class PK_Crypto(object): + @staticmethod + def pk_from_pk_info(pk_info): + return pk_info[5:] + + @staticmethod + def compute_digestinfo(msg): + return sha384(msg).digest() + + @staticmethod + def enc_data(enc_info): + return b'\xa6\x66\x7f\x49\x63\x86\x61' + enc_info[1] + + @staticmethod + def enc_check(enc_info, s): + point = enc_info[0] + # It's 04 || X || Y, extract X + return point[1:49] == s + + + def __init__(self, keyno=None, pk_info=None, data=None): + if keyno == None: + # Just for name space + return + + self.keyno = keyno + self.for_encryption = (self.keyno == 1) + self.timestamp = pack('>I', int(time())) + if pk_info: + # Public part only (no private data) from card + self.q = pk_info[5:] + else: + # Private part (in big endian) + self.d = data[0] + self.q = data[1] + self.fpr = self.calc_fpr() + + def calc_fpr(self): + m_len = 6 + 2 + 97 + ver = b'\x04' + algo = b'\x12' if self.for_encryption else b'\x16' + m = b'\x99' + pack('>H', m_len) + ver + self.timestamp + algo \ + + pack('>H', 768+3) + self.q + return sha1(m).digest() + + def build_privkey_template(self, is_yubikey): + openpgp_keyno = self.keyno + 1 + if openpgp_keyno == 1: + keyspec = b'\xb6' + elif openpgp_keyno == 2: + keyspec = b'\xb8' + else: + keyspec = b'\xa4' + key_template = b'\x92\x30' + exthdr = keyspec + b'\x00' + b'\x7f\x48' + b'\x02' + key_template + suffix = b'\x5f\x48' + b'\x30' + return b'\x4d' + b'\x3a' + exthdr + suffix + self.d + + def compute_signature(self, digestinfo): + return lg_nistp384.call_pk_sign(self.d, digestinfo) + + def verify_signature(self, digestinfo, sig): + return lg_nistp384.call_pk_verify(self.q, digestinfo, sig) + + def encrypt(self, plaintext): + # Do ECDH + return lg_nistp384.call_pk_encrypt(self.q, plaintext) + + def get_fpr(self): + return self.fpr + + def get_timestamp(self): + return self.timestamp + + def get_pk(self): + return self.q + +key = [ None, None, None ] + +# https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/components/186-3ecdsasiggencomponenttestvectors.zip + +nistp384_data0 = ( + b'\x20\x1b\x43\x2d\x8d\xf1\x43\x24\x18\x2d\x62\x61\xdb\x3e\x4b\x3f' + b'\x46\xa8\x28\x44\x82\xd5\x2e\x37\x0d\xa4\x1e\x6c\xbd\xf4\x5e\xc2' + b'\x95\x2f\x5d\xb7\xcc\xbc\xe3\xbc\x29\x44\x9f\x4f\xb0\x80\xac\x97', + b'\x04' + b'\xc2\xb4\x79\x44\xfb\x5d\xe3\x42\xd0\x32\x85\x88\x01\x77\xca\x5f' + b'\x7d\x0f\x2f\xca\xd7\x67\x8c\xce\x42\x29\xd6\xe1\x93\x2f\xca\xc1' + b'\x1b\xfc\x3c\x3e\x97\xd9\x42\xa3\xc5\x6b\xf3\x41\x23\x01\x3d\xbf' + b'\x37\x25\x79\x06\xa8\x22\x38\x66\xed\xa0\x74\x3c\x51\x96\x16\xa7' + b'\x6a\x75\x8a\xe5\x8a\xee\x81\xc5\xfd\x35\xfb\xf3\xa8\x55\xb7\x75' + b'\x4a\x36\xd4\xa0\x67\x2d\xf9\x5d\x6c\x44\xa8\x1c\xf7\x62\x0c\x2d' +) + +nistp384_data2 = ( + b'\x23\xd9\xf4\xea\x6d\x87\xb7\xd6\x16\x3d\x64\x25\x6e\x34\x49\x25' + b'\x5d\xb1\x47\x86\x40\x1a\x51\xda\xa7\x84\x71\x61\xbf\x56\xd4\x94' + b'\x32\x5a\xd2\xac\x8b\xa9\x28\x39\x4e\x01\x06\x1d\x88\x2c\x35\x28', + b'\x04' + b'\x5d\x42\xd6\x30\x1c\x54\xa4\x38\xf6\x59\x70\xba\xe2\xa0\x98\xcb' + b'\xc5\x67\xe9\x88\x40\x00\x6e\x35\x62\x21\x96\x6c\x86\xd8\x2e\x8e' + b'\xca\x51\x5b\xca\x85\x0e\xaa\x3c\xd4\x1f\x17\x5f\x03\xa0\xcb\xfd' + b'\x4a\xef\x5a\x0c\xee\xce\x95\xd3\x82\xbd\x70\xab\x5c\xe1\xcb\x77' + b'\x40\x8b\xae\x42\xb5\x1a\x08\x81\x6d\x5e\x5e\x1d\x3d\xa8\xc1\x8f' + b'\xcc\x95\x56\x4a\x75\x27\x30\xb0\xaa\xbe\xa9\x83\xcc\xea\x4e\x2e' +) + +# https://tools.ietf.org/html/rfc5903#section-8.1 +nistp384_data1 = ( + b'\x09\x9f\x3c\x70\x34\xd4\xa2\xc6\x99\x88\x4d\x73\xa3\x75\xa6\x7f' + b'\x76\x24\xef\x7c\x6b\x3c\x0f\x16\x06\x47\xb6\x74\x14\xdc\xe6\x55' + b'\xe3\x5b\x53\x80\x41\xe6\x49\xee\x3f\xae\xf8\x96\x78\x3a\xb1\x94', + b'\x04' + b'\x66\x78\x42\xd7\xd1\x80\xac\x2c\xde\x6f\x74\xf3\x75\x51\xf5\x57' + b'\x55\xc7\x64\x5c\x20\xef\x73\xe3\x16\x34\xfe\x72\xb4\xc5\x5e\xe6' + b'\xde\x3a\xc8\x08\xac\xb4\xbd\xb4\xc8\x87\x32\xae\xe9\x5f\x41\xaa' + b'\x94\x82\xed\x1f\xc0\xee\xb9\xca\xfc\x49\x84\x62\x5c\xcf\xc2\x3f' + b'\x65\x03\x21\x49\xe0\xe1\x44\xad\xa0\x24\x18\x15\x35\xa0\xf3\x8e' + b'\xeb\x9f\xcf\xf3\xc2\xc9\x47\xda\xe6\x9b\x4c\x63\x45\x73\xa8\x1c' +) + +key[0] = PK_Crypto(0, data=nistp384_data0) +key[1] = PK_Crypto(1, data=nistp384_data1) +key[2] = PK_Crypto(2, data=nistp384_data2) + +PLAIN_TEXT0=b"In this test, we verify card generated result by libgcrypt." +PLAIN_TEXT1=b"Signature is non-deterministic (it uses nonce K internally)." +PLAIN_TEXT2=b"We don't use NIST P-384 test vectors (it specifies K of ECDSA)." +PLAIN_TEXT3=b"NOTE: Our test is not for ECDSA implementation itself." + +ENCRYPT_TEXT0 = sha384(b"!encrypt me please").digest() +ENCRYPT_TEXT1 = sha384(b"!!!encrypt me please, another").digest() +ENCRYPT_TEXT2 = sha384(b"!encrypt me please, the other").digest() + +test_vector = { + 'sign_0' : PLAIN_TEXT0, + 'sign_1' : PLAIN_TEXT1, + 'auth_0' : PLAIN_TEXT2, + 'auth_1' : PLAIN_TEXT3, + 'decrypt_0' : ENCRYPT_TEXT0, + 'decrypt_1' : ENCRYPT_TEXT1, + 'encrypt_0' : ENCRYPT_TEXT2, +} + + +nistp384r1_pk = PK_Crypto() +nistp384r1_pk.test_vector = test_vector +nistp384r1_pk.key_list = key +nistp384r1_pk.key_attr_list = [KEY_ATTRIBUTES_ECDSA_NISTP384R1, KEY_ATTRIBUTES_ECDH_NISTP384R1, KEY_ATTRIBUTES_ECDSA_NISTP384R1] +nistp384r1_pk.PK_Crypto = PK_Crypto diff --git a/tests/nistp521r1_keys.py b/tests/nistp521r1_keys.py new file mode 100644 index 0000000..d5799c2 --- /dev/null +++ b/tests/nistp521r1_keys.py @@ -0,0 +1,174 @@ +from time import time +from struct import pack +from hashlib import sha1, sha512 +from pk_signed_mpi_with_libgcrypt import PK_libgcrypt +from card_const import KEY_ATTRIBUTES_ECDH_NISTP521R1, KEY_ATTRIBUTES_ECDSA_NISTP521R1 +lg_nistp521 = PK_libgcrypt(66, "NIST P-521") + +class PK_Crypto(object): + @staticmethod + def pk_from_pk_info(pk_info): + return pk_info[7:] + + @staticmethod + def compute_digestinfo(msg): + return sha512(msg).digest() + + @staticmethod + def enc_data(enc_info): + return b'\xa6\x81\x8c\x7f\x49\x81\x88\x86\x81\x85' + enc_info[1] + + @staticmethod + def enc_check(enc_info, s): + point = enc_info[0] + # It's 04 || X || Y, extract X + return point[1:67] == s + + + def __init__(self, keyno=None, pk_info=None, data=None): + if keyno == None: + # Just for name space + return + + self.keyno = keyno + self.for_encryption = (self.keyno == 1) + self.timestamp = pack('>I', int(time())) + if pk_info: + # Public part only (no private data) from card + self.q = pk_info[7:] + else: + # Private part (in big endian) + self.d = data[0] + self.q = data[1] + self.fpr = self.calc_fpr() + + def calc_fpr(self): + m_len = 6 + 2 + 133 + ver = b'\x04' + algo = b'\x12' if self.for_encryption else b'\x16' + m = b'\x99' + pack('>H', m_len) + ver + self.timestamp + algo \ + + pack('>H', 1056+3) + self.q + return sha1(m).digest() + + def build_privkey_template(self, is_yubikey): + openpgp_keyno = self.keyno + 1 + if openpgp_keyno == 1: + keyspec = b'\xb6' + elif openpgp_keyno == 2: + keyspec = b'\xb8' + else: + keyspec = b'\xa4' + key_template = b'\x92\x42' + exthdr = keyspec + b'\x00' + b'\x7f\x48' + b'\x02' + key_template + suffix = b'\x5f\x48' + b'\x42' + return b'\x4d' + b'\x4c' + exthdr + suffix + self.d + + def compute_signature(self, digestinfo): + return lg_nistp521.call_pk_sign(self.d, digestinfo) + + def verify_signature(self, digestinfo, sig): + return lg_nistp521.call_pk_verify(self.q, digestinfo, sig) + + def encrypt(self, plaintext): + # Do ECDH + return lg_nistp521.call_pk_encrypt(self.q, plaintext) + + def get_fpr(self): + return self.fpr + + def get_timestamp(self): + return self.timestamp + + def get_pk(self): + return self.q + +key = [ None, None, None ] + +# https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/components/186-3ecdsasiggencomponenttestvectors.zip + +nistp521_data0 = ( + b'\x00\xf7\x49\xd3\x27\x04\xbc\x53\x3c\xa8\x2c\xef\x0a\xcf\x10\x3d' + b'\x8f\x4f\xba\x67\xf0\x8d\x26\x78\xe5\x15\xed\x7d\xb8\x86\x26\x7f' + b'\xfa\xf0\x2f\xab\x00\x80\xdc\xa2\x35\x9b\x72\xf5\x74\xcc\xc2\x9a' + b'\x0f\x21\x8c\x86\x55\xc0\xcc\xcf\x9f\xee\x6c\x5e\x56\x7a\xa1\x4c' + b'\xb9\x26', + b'\x04' + b'\x00\x61\x38\x7f\xd6\xb9\x59\x14\xe8\x85\xf9\x12\xed\xfb\xb5\xfb' + b'\x27\x46\x55\x02\x7f\x21\x6c\x40\x91\xca\x83\xe1\x93\x36\x74\x0f' + b'\xd8\x1a\xed\xfe\x04\x7f\x51\xb4\x2b\xdf\x68\x16\x11\x21\x01\x3e' + b'\x0d\x55\xb1\x17\xa1\x4e\x43\x03\xf9\x26\xc8\xde\xbb\x77\xa7\xfd' + b'\xaa\xd1' + b'\x00\xe7\xd0\xc7\x5c\x38\x62\x6e\x89\x5c\xa2\x15\x26\xb9\xf9\xfd' + b'\xf8\x4d\xce\xcb\x93\xf2\xb2\x33\x39\x05\x50\xd2\xb1\x46\x3b\x7e' + b'\xe3\xf5\x8d\xf7\x34\x64\x35\xff\x04\x34\x19\x95\x83\xc9\x7c\x66' + b'\x5a\x97\xf1\x2f\x70\x6f\x23\x57\xda\x4b\x40\x28\x8d\xef\x88\x8e' + b'\x59\xe6' +) + +nistp521_data2 = ( + b'\x01\xa4\xd2\x62\x3a\x7d\x59\xc5\x5f\x40\x83\x31\xba\x8d\x15\x23' + b'\xb9\x4d\x6b\xf8\xac\x83\x37\x5c\xeb\x57\xa2\xb3\x95\xa5\xbc\xf9' + b'\x77\xcf\xc1\x62\x34\xd4\xa9\x7d\x6f\x6e\xe2\x5a\x99\xaa\x5b\xff' + b'\x15\xff\x53\x58\x91\xbc\xb7\xae\x84\x9a\x58\x3e\x01\xac\x49\xe0' + b'\xe9\xb6', + b'\x04' + b'\x00\x4d\x5c\x8a\xfe\xe0\x38\x98\x4d\x2e\xa9\x66\x81\xec\x0d\xcc' + b'\xb6\xb5\x2d\xfa\x4e\xe2\xe2\xa7\x7a\x23\xc8\xcf\x43\xef\x19\x90' + b'\x5a\x34\xd6\xf5\xd8\xc5\xcf\x09\x81\xed\x80\x4d\x89\xd1\x75\xb1' + b'\x7d\x1a\x63\x52\x2c\xeb\x1e\x78\x5c\x0f\x5a\x1d\x2f\x3d\x15\xe5' + b'\x13\x52' + b'\x00\x14\x36\x8b\x8e\x74\x68\x07\xb2\xb6\x8f\x36\x15\xcd\x78\xd7' + b'\x61\xa4\x64\xdd\xd7\x91\x8f\xc8\xdf\x51\xd2\x25\x96\x2f\xdf\x1e' + b'\x3d\xc2\x43\xe2\x65\x10\x0f\xf0\xec\x13\x33\x59\xe3\x32\xe4\x4d' + b'\xd4\x9a\xfd\x8e\x5f\x38\xfe\x86\x13\x35\x73\x43\x2d\x33\xc0\x2f' + b'\xa0\xa3' +) + +# https://tools.ietf.org/html/rfc5903#section-8.1 +nistp521_data1 = ( + b'\x00\x37\xad\xe9\x31\x9a\x89\xf4\xda\xbd\xb3\xef\x41\x1a\xac\xcc' + b'\xa5\x12\x3c\x61\xac\xab\x57\xb5\x39\x3d\xce\x47\x60\x81\x72\xa0' + b'\x95\xaa\x85\xa3\x0f\xe1\xc2\x95\x2c\x67\x71\xd9\x37\xba\x97\x77' + b'\xf5\x95\x7b\x26\x39\xba\xb0\x72\x46\x2f\x68\xc2\x7a\x57\x38\x2d' + b'\x4a\x52', + b'\x04' + b'\x00\x15\x41\x7e\x84\xdb\xf2\x8c\x0a\xd3\xc2\x78\x71\x33\x49\xdc' + b'\x7d\xf1\x53\xc8\x97\xa1\x89\x1b\xd9\x8b\xab\x43\x57\xc9\xec\xbe' + b'\xe1\xe3\xbf\x42\xe0\x0b\x8e\x38\x0a\xea\xe5\x7c\x2d\x10\x75\x64' + b'\x94\x18\x85\x94\x2a\xf5\xa7\xf4\x60\x17\x23\xc4\x19\x5d\x17\x6c' + b'\xed\x3e' + b'\x01\x7c\xae\x20\xb6\x64\x1d\x2e\xeb\x69\x57\x86\xd8\xc9\x46\x14' + b'\x62\x39\xd0\x99\xe1\x8e\x1d\x5a\x51\x4c\x73\x9d\x7c\xb4\xa1\x0a' + b'\xd8\xa7\x88\x01\x5a\xc4\x05\xd7\x79\x9d\xc7\x5e\x7b\x7d\x5b\x6c' + b'\xf2\x26\x1a\x6a\x7f\x15\x07\x43\x8b\xf0\x1b\xeb\x6c\xa3\x92\x6f' + b'\x95\x82' +) + +key[0] = PK_Crypto(0, data=nistp521_data0) +key[1] = PK_Crypto(1, data=nistp521_data1) +key[2] = PK_Crypto(2, data=nistp521_data2) + +PLAIN_TEXT0=b"In this test, we verify card generated result by libgcrypt." +PLAIN_TEXT1=b"Signature is non-deterministic (it uses nonce K internally)." +PLAIN_TEXT2=b"We don't use NIST P-512 test vectors (it specifies K of ECDSA)." +PLAIN_TEXT3=b"NOTE: Our test is not for ECDSA implementation itself." + +ENCRYPT_TEXT0 = sha512(b"!encrypt me please").digest() +ENCRYPT_TEXT1 = sha512(b"!!!encrypt me please, another").digest() +ENCRYPT_TEXT2 = sha512(b"!encrypt me please, the other").digest() + +test_vector = { + 'sign_0' : PLAIN_TEXT0, + 'sign_1' : PLAIN_TEXT1, + 'auth_0' : PLAIN_TEXT2, + 'auth_1' : PLAIN_TEXT3, + 'decrypt_0' : ENCRYPT_TEXT0, + 'decrypt_1' : ENCRYPT_TEXT1, + 'encrypt_0' : ENCRYPT_TEXT2, +} + +nistp521r1_pk = PK_Crypto() +nistp521r1_pk.test_vector = test_vector +nistp521r1_pk.key_list = key +nistp521r1_pk.key_attr_list = [KEY_ATTRIBUTES_ECDSA_NISTP521R1, KEY_ATTRIBUTES_ECDH_NISTP521R1, KEY_ATTRIBUTES_ECDSA_NISTP521R1] +nistp521r1_pk.PK_Crypto = PK_Crypto diff --git a/tests/openpgp_card.py b/tests/openpgp_card.py new file mode 100644 index 0000000..786d057 --- /dev/null +++ b/tests/openpgp_card.py @@ -0,0 +1,515 @@ +""" +openpgp_card.py - a library for OpenPGP card + +Copyright (C) 2011, 2012, 2013, 2015, 2016, 2018, 2019 + Free Software Initiative of Japan +Author: NIIBE Yutaka + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from struct import pack, unpack +from kdf_calc import kdf_calc +from card_const import FACTORY_PASSPHRASE_PW1, FACTORY_PASSPHRASE_PW3 + +def iso7816_compose(ins, p1, p2, data, cls=0x00, le=None): + data_len = len(data) + if data_len == 0: + if not le: + return pack('>BBBB', cls, ins, p1, p2) + elif le < 256: + return pack('>BBBBB', cls, ins, p1, p2, le) + else: + return pack('>BBBBBH', cls, ins, p1, p2, 0, le) + else: + if not le: + if data_len <= 255: + return pack('>BBBBB', cls, ins, p1, p2, data_len) + data + else: + return pack('>BBBBBH', cls, ins, p1, p2, 0, data_len) \ + + data + else: + if data_len <= 255 and le < 256: + return pack('>BBBBB', cls, ins, p1, p2, data_len) \ + + data + pack('>B', le) + else: + return pack('>BBBBBH', cls, ins, p1, p2, 0, data_len) \ + + data + pack('>H', le) + +class OpenPGP_Card(object): + def __init__(self, reader): + """ + __init__(reader) -> None + Initialize a OpenPGP card with a CardReader. + reader: CardReader object. + """ + + self.supported_key_attrlist = [ [], [], [] ] + self.__reader = reader + self.attr = [ None, None, None ] + self.__kdf_iters = None + self.__kdf_salt_user = None + self.__kdf_salt_reset = None + self.__kdf_salt_admin = None + self.is_gnuk = False + self.is_yubikey = False + self.kdf_required = False + + self.initialize_kdf() + + def add_to_key_attrlist(self, no, attr): + self.supported_key_attrlist[no].append(attr) + + def initialize_kdf(self): + try: + self.kdf_data = self.cmd_get_data(0x00, 0xf9) + if not self.kdf_data: + self.kdf_data = b"" + self.kdf_supported = True + except: + self.kdf_data = b"" + self.kdf_supported = True + + def configure_kdf(self, kdf_config): + self.kdf_data = kdf_config + r = self.cmd_put_data(0x00, 0xf9, kdf_config) + if self.kdf_data == b"" or self.kdf_data == b"\x81\x01\x00": + if not self.is_gnuk and not self.is_yubikey: + self.change_passwd(1, FACTORY_PASSPHRASE_PW1, + FACTORY_PASSPHRASE_PW1, -1) + self.change_passwd(3, FACTORY_PASSPHRASE_PW3, + FACTORY_PASSPHRASE_PW3, -1) + self.__kdf_iters = None + self.__kdf_salt_user = None + self.__kdf_salt_reset = None + self.__kdf_salt_admin = None + else: + algo, subalgo, iters, salt_user, salt_reset, salt_admin, hash_user, hash_admin = parse_kdf_data(self.kdf_data) + self.__kdf_iters = iters + self.__kdf_salt_user = salt_user + self.__kdf_salt_reset = salt_reset + self.__kdf_salt_admin = salt_admin + if not self.is_gnuk and not self.is_yubikey: + self.change_passwd(1, FACTORY_PASSPHRASE_PW1, + FACTORY_PASSPHRASE_PW1, 1) + self.change_passwd(3, FACTORY_PASSPHRASE_PW3, + FACTORY_PASSPHRASE_PW3, 1) + return r + + def save_algo_attribute(self, keyno, attr): + self.attr[keyno - 1] = attr + + def saved_attribute(self, keyno): + return self.attr[keyno - 1] + + # Higher layer VERIFY possibly using KDF Data Object + def verify(self, who, passwd): + if self.__kdf_iters: + salt = self.__kdf_salt_user + if who == 3 and self.__kdf_salt_admin: + salt = self.__kdf_salt_admin + pw_hash = kdf_calc(passwd, salt, self.__kdf_iters) + return self.cmd_verify(who, pw_hash) + else: + return self.cmd_verify(who, passwd) + + # Higher layer CHANGE_PASSWD possibly using KDF Data Object + # KDF_CHANGE: 0 no-change, -1 to be cleared, 1 to be configured + def change_passwd(self, who, passwd_old, passwd_new, kdf_change=0): + if self.__kdf_iters: + salt = self.__kdf_salt_user + if who == 3 and self.__kdf_salt_admin: + salt = self.__kdf_salt_admin + if kdf_change <= 0: + hash_old = kdf_calc(passwd_old, salt, self.__kdf_iters) + else: + hash_old = passwd_old + if kdf_change >= 0: + hash_new = kdf_calc(passwd_new, salt, self.__kdf_iters) + else: + hash_new = passwd_new + return self.cmd_change_reference_data(who, hash_old + hash_new) + else: + return self.cmd_change_reference_data(who, passwd_old + passwd_new) + + # Higher layer SETUP_RESET_CODE possibly using KDF Data Object + def setup_reset_code(self, resetcode): + if self.__kdf_iters: + salt = self.__kdf_salt_user + if self.__kdf_salt_reset: + salt = self.__kdf_salt_user + reset_hash = kdf_calc(resetcode, salt, self.__kdf_iters) + return self.cmd_put_data(0x00, 0xd3, reset_hash) + else: + return self.cmd_put_data(0x00, 0xd3, resetcode) + + # Higher layer reset passwd possibly using KDF Data Object + def reset_passwd_by_resetcode(self, resetcode, pw1): + if self.__kdf_iters: + salt = self.__kdf_salt_user + if self.__kdf_salt_reset: + salt = self.__kdf_salt_user + reset_hash = kdf_calc(resetcode, salt, self.__kdf_iters) + pw1_hash = kdf_calc(pw1, self.__kdf_salt_user, self.__kdf_iters) + return self.cmd_reset_retry_counter(0, 0x81, reset_hash + pw1_hash) + else: + return self.cmd_reset_retry_counter(0, 0x81, resetcode + pw1) + + # Higher layer reset passwd possibly using KDF Data Object + def reset_passwd_by_admin(self, pw1): + if self.__kdf_iters: + pw1_hash = kdf_calc(pw1, self.__kdf_salt_user, self.__kdf_iters) + return self.cmd_reset_retry_counter(2, 0x81, pw1_hash) + else: + return self.cmd_reset_retry_counter(2, 0x81, pw1) + + def cmd_get_response(self, expected_len): + result = b"" + while True: + cmd_data = iso7816_compose(0xc0, 0x00, 0x00, b'') + pack('>B', expected_len) + response = self.__reader.send_cmd(cmd_data) + result += response[:-2] + sw = response[-2:] + if sw[0] == 0x90 and sw[1] == 0x00: + return result + elif sw[0] != 0x61: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + else: + expected_len = sw[1] + + def cmd_verify(self, who, passwd): + cmd_data = iso7816_compose(0x20, 0x00, 0x80+who, passwd) + sw = self.__reader.send_cmd(cmd_data) + if len(sw) != 2: + raise ValueError(sw) + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError("%02x%02x" % (sw[0], sw[1])) + return True + + def cmd_read_binary(self, fileid): + cmd_data = iso7816_compose(0xb0, 0x80+fileid, 0x00, b'') + sw = self.__reader.send_cmd(cmd_data) + if len(sw) != 2: + raise ValueError(sw) + if sw[0] != 0x61: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + return self.cmd_get_response(sw[1]) + + def cmd_write_binary(self, fileid, data, is_update): + count = 0 + data_len = len(data) + if is_update: + ins = 0xd6 + else: + ins = 0xd0 + while count*256 < data_len: + if count == 0: + if len(data) < 128: + cmd_data0 = iso7816_compose(ins, 0x80+fileid, 0x00, data[:128]) + cmd_data1 = None + else: + cmd_data0 = iso7816_compose(ins, 0x80+fileid, 0x00, data[:128], 0x10) + cmd_data1 = iso7816_compose(ins, 0x80+fileid, 0x00, data[128:256]) + else: + if len(data[256*count:256*count+128]) < 128: + cmd_data0 = iso7816_compose(ins, count, 0x00, data[256*count:256*count+128]) + cmd_data1 = None + else: + cmd_data0 = iso7816_compose(ins, count, 0x00, data[256*count:256*count+128], 0x10) + cmd_data1 = iso7816_compose(ins, count, 0x00, data[256*count+128:256*(count+1)]) + sw = self.__reader.send_cmd(cmd_data0) + if len(sw) != 2: + raise ValueError("cmd_write_binary 0") + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError("cmd_write_binary 0", "%02x%02x" % (sw[0], sw[1])) + if cmd_data1: + sw = self.__reader.send_cmd(cmd_data1) + if len(sw) != 2: + raise ValueError("cmd_write_binary 1", sw) + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError("cmd_write_binary 1", "%02x%02x" % (sw[0], sw[1])) + count += 1 + + def cmd_select_openpgp(self): + cmd_data = iso7816_compose(0xa4, 0x04, 0x00, b"\xD2\x76\x00\x01\x24\x01") + r = self.__reader.send_cmd(cmd_data) + if len(r) < 2: + raise ValueError(r) + sw = r[-2:] + r = r[0:-2] + if sw[0] == 0x61: + self.cmd_get_response(sw[1]) + return True + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError("%02x%02x" % (sw[0], sw[1])) + return True + + def cmd_get_data(self, tagh, tagl): + if tagh == 0 and tagl == 0xfa and (not self.is_gnuk): + # Special DO with larger data + cmd_data = iso7816_compose(0xca, tagh, tagl, b"", le=1024) + elif self.is_yubikey: + cmd_data = iso7816_compose(0xca, tagh, tagl, b"") + else: + cmd_data = iso7816_compose(0xca, tagh, tagl, b"", le=254) + sw = self.__reader.send_cmd(cmd_data) + if len(sw) < 2: + raise ValueError(sw) + if sw[0] == 0x61: + return self.cmd_get_response(sw[1]) + if (sw[-2] == 0x61): + return self.cmd_get_response(sw[-1]) + elif sw[-2] == 0x90 and sw[-1] == 0x00: + return sw[0:-2] + if sw[0] == 0x6a and sw[1] == 0x88: + return None + else: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + + def cmd_change_reference_data(self, who, data): + cmd_data = iso7816_compose(0x24, 0x00, 0x80+who, data) + sw = self.__reader.send_cmd(cmd_data) + if len(sw) != 2: + raise ValueError(sw) + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError("%02x%02x" % (sw[0], sw[1])) + return True + + def cmd_put_data(self, tagh, tagl, content): + cmd_data = iso7816_compose(0xda, tagh, tagl, content) + sw = self.__reader.send_cmd(cmd_data) + if len(sw) != 2: + raise ValueError(sw) + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError("%02x%02x" % (sw[0], sw[1])) + return True + + def cmd_put_data_odd(self, tagh, tagl, content): + if (not self.is_gnuk) or len(content) <= 128: + cmd_data = iso7816_compose(0xdb, tagh, tagl, content) + sw = self.__reader.send_cmd(cmd_data) + else: + cmd_data0 = iso7816_compose(0xdb, tagh, tagl, content[:128], 0x10) + cmd_data1 = iso7816_compose(0xdb, tagh, tagl, content[128:]) + sw = self.__reader.send_cmd(cmd_data0) + if len(sw) != 2: + raise ValueError(sw) + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError("%02x%02x" % (sw[0], sw[1])) + sw = self.__reader.send_cmd(cmd_data1) + if len(sw) != 2: + raise ValueError(sw) + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError("%02x%02x" % (sw[0], sw[1])) + return True + + def cmd_reset_retry_counter(self, how, who, data): + cmd_data = iso7816_compose(0x2c, how, who, data) + sw = self.__reader.send_cmd(cmd_data) + if len(sw) != 2: + raise ValueError(sw) + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError("%02x%02x" % (sw[0], sw[1])) + return True + + def cmd_pso(self, p1, p2, data): + if not self.is_gnuk: + cmd_data = iso7816_compose(0x2a, p1, p2, data, le=256) + r = self.__reader.send_cmd(cmd_data) + if len(r) < 2: + raise ValueError(r) + sw = r[-2:] + r = r[0:-2] + if sw[0] == 0x61: + return self.cmd_get_response(sw[1]) + elif sw[0] == 0x90 and sw[1] == 0x00: + return r + else: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + else: + if len(data) > 128: + cmd_data0 = iso7816_compose(0x2a, p1, p2, data[:128], 0x10) + cmd_data1 = iso7816_compose(0x2a, p1, p2, data[128:]) + sw = self.__reader.send_cmd(cmd_data0) + if len(sw) != 2: + raise ValueError(sw) + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError("%02x%02x" % (sw[0], sw[1])) + sw = self.__reader.send_cmd(cmd_data1) + if len(sw) != 2: + raise ValueError(sw) + elif sw[0] != 0x61: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + return self.cmd_get_response(sw[1]) + else: + cmd_data = iso7816_compose(0x2a, p1, p2, data) + sw = self.__reader.send_cmd(cmd_data) + if len(sw) != 2: + raise ValueError(sw) + if sw[0] == 0x90 and sw[1] == 0x00: + return b"" + elif sw[0] != 0x61: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + return self.cmd_get_response(sw[1]) + + def cmd_internal_authenticate(self, data): + if not self.is_gnuk: + cmd_data = iso7816_compose(0x88, 0, 0, data, le=256) + else: + cmd_data = iso7816_compose(0x88, 0, 0, data) + r = self.__reader.send_cmd(cmd_data) + if len(r) < 2: + raise ValueError(r) + sw = r[-2:] + r = r[0:-2] + if sw[0] == 0x61: + return self.cmd_get_response(sw[1]) + elif sw[0] == 0x90 and sw[1] == 0x00: + return r + else: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + + def cmd_genkey(self, keyno): + if keyno == 1: + data = b'\xb6\x00' + elif keyno == 2: + data = b'\xb8\x00' + else: + data = b'\xa4\x00' + if not self.is_gnuk: + cmd_data = iso7816_compose(0x47, 0x80, 0, data, le=512) + else: + cmd_data = iso7816_compose(0x47, 0x80, 0, data) + sw = self.__reader.send_cmd(cmd_data) + if len(sw) < 2: + raise ValueError(sw) + if sw[-2] == 0x61: + pk = self.cmd_get_response(sw[1]) + elif sw[-2] == 0x90 and sw[-1] == 0x00: + pk = sw + else: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + return pk + + def cmd_get_public_key(self, keyno): + if keyno == 1: + data = b'\xb6\x00' + elif keyno == 2: + data = b'\xb8\x00' + else: + data = b'\xa4\x00' + if not self.is_gnuk: + cmd_data = iso7816_compose(0x47, 0x81, 0, data, le=512) + else: + cmd_data = iso7816_compose(0x47, 0x81, 0, data) + r = self.__reader.send_cmd(cmd_data) + if len(r) < 2: + raise ValueError(r) + sw = r[-2:] + r = r[0:-2] + if sw[0] == 0x61: + pk = self.cmd_get_response(sw[1]) + elif sw[0] == 0x90 and sw[1] == 0x00: + pk = r + else: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + return pk + + def cmd_put_data_remove(self, tagh, tagl): + cmd_data = iso7816_compose(0xda, tagh, tagl, b"") + sw = self.__reader.send_cmd(cmd_data) + if sw[0] != 0x90 and sw[1] != 0x00: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + + def cmd_put_data_key_import_remove(self, keyno): + if keyno == 1: + keyspec = b"\xb6\x00" # SIG + elif keyno == 2: + keyspec = b"\xb8\x00" # DEC + else: + keyspec = b"\xa4\x00" # AUT + cmd_data = iso7816_compose(0xdb, 0x3f, 0xff, b"\x4d\x02" + keyspec) + sw = self.__reader.send_cmd(cmd_data) + if sw[0] != 0x90 and sw[1] != 0x00: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + + def cmd_get_challenge(self): + cmd_data = iso7816_compose(0x84, 0x00, 0x00, '') + sw = self.__reader.send_cmd(cmd_data) + if len(sw) != 2: + raise ValueError(sw) + if sw[0] != 0x61: + raise ValueError("%02x%02x" % (sw[0], sw[1])) + return self.cmd_get_response(sw[1]) + + def cmd_external_authenticate(self, keyno, signed): + cmd_data = iso7816_compose(0x82, 0x00, keyno, signed[0:128], cls=0x10) + sw = self.__reader.send_cmd(cmd_data) + if len(sw) != 2: + raise ValueError(sw) + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError("%02x%02x" % (sw[0], sw[1])) + cmd_data = iso7816_compose(0x82, 0x00, keyno, signed[128:]) + sw = self.__reader.send_cmd(cmd_data) + if len(sw) != 2: + raise ValueError(sw) + if not (sw[0] == 0x90 and sw[1] == 0x00): + raise ValueError("%02x%02x" % (sw[0], sw[1])) + +def parse_kdf_data(kdf_data): + if len(kdf_data) == 90: + single_salt = True + elif len(kdf_data) == 110: + single_salt = False + else: + raise ValueError("length does not much", kdf_data) + + if kdf_data[0:2] != b'\x81\x01': + raise ValueError("data does not much") + algo = kdf_data[2] + if kdf_data[3:5] != b'\x82\x01': + raise ValueError("data does not much") + subalgo = kdf_data[5] + if kdf_data[6:8] != b'\x83\x04': + raise ValueError("data does not much") + iters = unpack(">I", kdf_data[8:12])[0] + if kdf_data[12:14] != b'\x84\x08': + raise ValueError("data does not much") + salt = kdf_data[14:22] + if single_salt: + salt_reset = None + salt_admin = None + if kdf_data[22:24] != b'\x87\x20': + raise ValueError("data does not much") + hash_user = kdf_data[24:56] + if kdf_data[56:58] != b'\x88\x20': + raise ValueError("data does not much") + hash_admin = kdf_data[58:90] + else: + if kdf_data[22:24] != b'\x85\x08': + raise ValueError("data does not much") + salt_reset = kdf_data[24:32] + if kdf_data[32:34] != b'\x86\x08': + raise ValueError("data does not much") + salt_admin = kdf_data[34:42] + if kdf_data[42:44] != b'\x87\x20': + raise ValueError("data does not much") + hash_user = kdf_data[44:76] + if kdf_data[76:78] != b'\x88\x20': + raise ValueError("data does not much") + hash_admin = kdf_data[78:110] + return ( algo, subalgo, iters, salt, salt_reset, salt_admin, + hash_user, hash_admin ) diff --git a/tests/pk_25519_with_libgcrypt.py b/tests/pk_25519_with_libgcrypt.py new file mode 100644 index 0000000..5c4638e --- /dev/null +++ b/tests/pk_25519_with_libgcrypt.py @@ -0,0 +1,165 @@ +from cffi import FFI + +ffi = FFI() + +DEF_gcry_sexp=""" +typedef unsigned long long size_t; +typedef void *gcry_sexp_t; +typedef unsigned int gcry_error_t; +gcry_error_t gcry_sexp_build (gcry_sexp_t *R_SEXP, size_t *ERROFF, const char *FORMAT, ...); +void gcry_sexp_release (gcry_sexp_t SEXP); +void gcry_sexp_dump (gcry_sexp_t SEXP); +gcry_sexp_t gcry_sexp_find_token (const gcry_sexp_t LIST, const char *TOKEN, size_t TOKLEN); +const char * gcry_sexp_nth_data (const gcry_sexp_t LIST, int NUMBER, size_t *DATALEN); +""" + +DEF_gcry_pk_sign=""" +typedef void *gcry_sexp_t; +typedef unsigned int gcry_error_t; +gcry_error_t gcry_pk_sign (gcry_sexp_t *R_SIG, gcry_sexp_t DATA, gcry_sexp_t SKEY); +""" + +DEF_gcry_pk_verify=""" +typedef void *gcry_sexp_t; +typedef unsigned int gcry_error_t; +gcry_error_t gcry_pk_verify (gcry_sexp_t SIG, gcry_sexp_t DATA, gcry_sexp_t PKEY); +""" + +DEF_gcry_pk_encrypt=""" +typedef void *gcry_sexp_t; +typedef unsigned int gcry_error_t; +gcry_error_t gcry_pk_encrypt (gcry_sexp_t *R_CIPH, gcry_sexp_t DATA, gcry_sexp_t PKEY); +""" + +ffi.cdef(DEF_gcry_sexp) +ffi.cdef(DEF_gcry_pk_sign, override=True) +ffi.cdef(DEF_gcry_pk_verify, override=True) +ffi.cdef(DEF_gcry_pk_encrypt, override=True) +libgcrypt = ffi.dlopen("libgcrypt.20.dylib") + +def fixup_scalar_cv25519(k): + # Fixup is the responsibility for caller for Curve25519 + first_byte = int.to_bytes((k[0] & 0xf8), 1, 'big') + last_byte = int.to_bytes(((k[-1] & 0x7f) | 0x40), 1, 'big') + k_fixed_up = (first_byte + k[1:-1] + last_byte) + return k_fixed_up[::-1] + +FORMAT_KEY_CV25519_D=b"(private-key(ecc(curve Curve25519)(flags djb-tweak)(d%b)))" +FORMAT_KEY_ED25519_D=b"(private-key(ecc(curve Ed25519)(flags eddsa)(d%b)))" + +def make_skey_by_secret(d,is_encr): + sexp = ffi.new("void **") + off = ffi.new("unsigned long long *") + if is_encr: + secret = ffi.new("char []", fixup_scalar_cv25519(d)) + r = libgcrypt.gcry_sexp_build(sexp, off, FORMAT_KEY_CV25519_D, + ffi.cast("int", 32), secret) + else: + secret = ffi.new("char []", d) + r = libgcrypt.gcry_sexp_build(sexp, off, FORMAT_KEY_ED25519_D, + ffi.cast("int", 32), secret) + if r != 0: + raise ValueError("libgcrypt error", r) + # libgcrypt.gcry_sexp_dump(sexp[0]) + return sexp[0] + +FORMAT_DATA_ED25519=b"(data(flags eddsa)(hash-algo sha512)(value %b))" + +def call_pk_sign(d, data): + skey = make_skey_by_secret(d, False) + + data_in_c = ffi.new("char []", data) + data_sexp = ffi.new("void **") + off = ffi.new("unsigned long long *") + r = libgcrypt.gcry_sexp_build(data_sexp, off, FORMAT_DATA_ED25519, + ffi.cast("int", 32), data_in_c) + if r != 0: + raise ValueError("libgcrypt error", r) + # + sig_sexp = ffi.new("void **") + libgcrypt.gcry_pk_sign(sig_sexp, data_sexp[0], skey) + if r != 0: + raise ValueError("libgcrypt error", r) + # + token_in_c = ffi.new("char []", b"r") + r_sexp = libgcrypt.gcry_sexp_find_token(sig_sexp[0], token_in_c, 1) + length = ffi.new("size_t *") + sig_r = libgcrypt.gcry_sexp_nth_data(r_sexp, 1, length) + token_in_c = ffi.new("char []", b"s") + s_sexp = libgcrypt.gcry_sexp_find_token(sig_sexp[0], token_in_c, 1) + sig_s = libgcrypt.gcry_sexp_nth_data(s_sexp, 1, length) + return ffi.unpack(sig_r,32) + ffi.unpack(sig_s,32) + +FORMAT_KEY_CV25519_Q=b"(private-key(ecc(curve Curve25519)(flags djb-tweak)(q%b)))" +FORMAT_KEY_ED25519_Q=b"(private-key(ecc(curve Ed25519)(flags eddsa)(q%b)))" + +def make_skey_by_public(q,is_encr): + public = ffi.new("char []", b'\x40' + q) + sexp = ffi.new("void **") + off = ffi.new("unsigned long long *") + if is_encr: + r = libgcrypt.gcry_sexp_build(sexp, off, FORMAT_KEY_CV25519_Q, + ffi.cast("int", 33), public) + else: + r = libgcrypt.gcry_sexp_build(sexp, off, FORMAT_KEY_ED25519_Q, + ffi.cast("int", 33), public) + if r != 0: + raise ValueError("libgcrypt error", r) + # libgcrypt.gcry_sexp_dump(sexp[0]) + return sexp[0] + +def call_pk_encrypt(q, ecdh_scalar): + skey = make_skey_by_public(q, True) + # + shared_in_c = ffi.new("char []", fixup_scalar_cv25519(ecdh_scalar)) + data_sexp = ffi.new("void **") + off = ffi.new("unsigned long long *") + r = libgcrypt.gcry_sexp_build(data_sexp, off, b"%b", + ffi.cast("int", 32), shared_in_c) + if r != 0: + raise ValueError("libgcrypt error", r) + # libgcrypt.gcry_sexp_dump(data_sexp[0]) + + ct_sexp = ffi.new("void **") + r = libgcrypt.gcry_pk_encrypt(ct_sexp, data_sexp[0], skey) + if r != 0: + raise ValueError("libgcrypt error", r) + # libgcrypt.gcry_sexp_dump(ct_sexp[0]) + token_in_c = ffi.new("char []", b"s") + s_sexp = libgcrypt.gcry_sexp_find_token(ct_sexp[0], token_in_c, 1) + length = ffi.new("size_t *") + enc_s = libgcrypt.gcry_sexp_nth_data(s_sexp, 1, length) + # + token_in_c = ffi.new("char []", b"e") + e_sexp = libgcrypt.gcry_sexp_find_token(ct_sexp[0], token_in_c, 1) + enc_e = libgcrypt.gcry_sexp_nth_data(e_sexp, 1, length) + # + return (ffi.unpack(enc_s,33)[1:], ffi.unpack(enc_e,33)[1:]) + +FORMAT_SIG_ED25519=b"(sig-val(eddsa(r %b)(s %b)))" + +def call_pk_verify(q, data, sig): + skey = make_skey_by_public(q, False) + + data_in_c = ffi.new("char []", data) + data_sexp = ffi.new("void **") + off = ffi.new("unsigned long long *") + r = libgcrypt.gcry_sexp_build(data_sexp, off, FORMAT_DATA_ED25519, + ffi.cast("int", 32), data_in_c) + if r != 0: + raise ValueError("libgcrypt error", r) + + sig_r_in_c = ffi.new("char []", sig[0:32]) + sig_s_in_c = ffi.new("char []", sig[32:]) + sig_sexp = ffi.new("void **") + off = ffi.new("unsigned long long *") + r = libgcrypt.gcry_sexp_build(sig_sexp, off, FORMAT_SIG_ED25519, + ffi.cast("int", 32), sig_r_in_c, + ffi.cast("int", 32), sig_s_in_c) + if r != 0: + raise ValueError("libgcrypt error", r) + + # libgcrypt.gcry_sexp_dump(sig_sexp[0]) + # libgcrypt.gcry_sexp_dump(data_sexp[0]) + r = libgcrypt.gcry_pk_verify(sig_sexp[0], data_sexp[0], skey) + return r == 0 diff --git a/tests/pk_signed_mpi_with_libgcrypt.py b/tests/pk_signed_mpi_with_libgcrypt.py new file mode 100644 index 0000000..f0b1e26 --- /dev/null +++ b/tests/pk_signed_mpi_with_libgcrypt.py @@ -0,0 +1,189 @@ +from cffi import FFI + +DEF_gcry_sexp=""" +typedef unsigned long long size_t; +typedef void *gcry_sexp_t; +typedef unsigned int gcry_error_t; +gcry_error_t gcry_sexp_build (gcry_sexp_t *R_SEXP, size_t *ERROFF, const char *FORMAT, ...); +void gcry_sexp_release (gcry_sexp_t SEXP); +void gcry_sexp_dump (gcry_sexp_t SEXP); +gcry_sexp_t gcry_sexp_find_token (const gcry_sexp_t LIST, const char *TOKEN, size_t TOKLEN); +const char * gcry_sexp_nth_data (const gcry_sexp_t LIST, int NUMBER, size_t *DATALEN); +""" + +DEF_gcry_pk_sign=""" +typedef void *gcry_sexp_t; +typedef unsigned int gcry_error_t; +gcry_error_t gcry_pk_sign (gcry_sexp_t *R_SIG, gcry_sexp_t DATA, gcry_sexp_t SKEY); +""" + +DEF_gcry_pk_verify=""" +typedef void *gcry_sexp_t; +typedef unsigned int gcry_error_t; +gcry_error_t gcry_pk_verify (gcry_sexp_t SIG, gcry_sexp_t DATA, gcry_sexp_t PKEY); +""" + +DEF_gcry_pk_encrypt=""" +typedef void *gcry_sexp_t; +typedef unsigned int gcry_error_t; +gcry_error_t gcry_pk_encrypt (gcry_sexp_t *R_CIPH, gcry_sexp_t DATA, gcry_sexp_t PKEY); +""" + +ffi = FFI() + +ffi.cdef(DEF_gcry_sexp) +ffi.cdef(DEF_gcry_pk_sign, override=True) +ffi.cdef(DEF_gcry_pk_verify, override=True) +ffi.cdef(DEF_gcry_pk_encrypt, override=True) +libgcrypt = ffi.dlopen("libgcrypt.20.dylib") + +FORMAT_DATA=b"(data(value %b))" +FORMAT_SIG=b"(sig-val(ecdsa(r %b)(s %b)))" + +FORMAT_KEY_D_TMPL='(private-key(ecc(curve {0}:{1})(d%b)))' +FORMAT_KEY_Q_TMPL='(private-key(ecc(curve {0}:{1})(q%b)))' + +class PK_libgcrypt(object): + def __init__(self, dlen, cn): + cn_len = len(cn) + self.format_key_d = bytes(FORMAT_KEY_D_TMPL.format(cn_len,cn),"utf-8") + self.format_key_q = bytes(FORMAT_KEY_Q_TMPL.format(cn_len,cn),"utf-8") + self.dlen = dlen + + # Unfourtunately, in libgcrypt, D is signed + def make_skey_by_secret(self, d): + if d[0] >= 128: + d = b'\x00' + d + + sexp = ffi.new("void **") + off = ffi.new("unsigned long long *") + secret = ffi.new("char []", d) + r = libgcrypt.gcry_sexp_build(sexp, off, self.format_key_d, + ffi.cast("int", len(d)), secret) + if r != 0: + raise ValueError("libgcrypt error", r) + # libgcrypt.gcry_sexp_dump(sexp[0]) + return sexp[0] + + # Unfourtunately, in libgcrypt, DATA is signed + def call_pk_sign(self, d, data): + skey = self.make_skey_by_secret(d) + + if data[0] >= 128: + data = b'\x00' + data + + data_in_c = ffi.new("char []", data) + data_sexp = ffi.new("void **") + off = ffi.new("unsigned long long *") + r = libgcrypt.gcry_sexp_build(data_sexp, off, FORMAT_DATA, + ffi.cast("int", len(data)), data_in_c) + if r != 0: + raise ValueError("libgcrypt error", r) + # + sig_sexp = ffi.new("void **") + libgcrypt.gcry_pk_sign(sig_sexp, data_sexp[0], skey) + if r != 0: + raise ValueError("libgcrypt error", r) + # + token_in_c = ffi.new("char []", b"r") + r_sexp = libgcrypt.gcry_sexp_find_token(sig_sexp[0], token_in_c, 1) + # libgcrypt.gcry_sexp_dump(sig_sexp[0]) + length = ffi.new("size_t *") + sig_r = libgcrypt.gcry_sexp_nth_data(r_sexp, 1, length) + len_sig_r = length[0] + token_in_c = ffi.new("char []", b"s") + s_sexp = libgcrypt.gcry_sexp_find_token(sig_sexp[0], token_in_c, 1) + sig_s = libgcrypt.gcry_sexp_nth_data(s_sexp, 1, length) + len_sig_s = length[0] + # + sig = bytes(self.dlen-len_sig_r) + sig += ffi.unpack(sig_r,len_sig_r) + sig += bytes(self.dlen-len_sig_s) + sig += ffi.unpack(sig_s,len_sig_s) + return sig + + # Unfourtunately, in libgcrypt, Q is signed, but luckily it always + # starts with 0x04 + def make_skey_by_public(self, q): + public = ffi.new("char []", q) + sexp = ffi.new("void **") + off = ffi.new("unsigned long long *") + r = libgcrypt.gcry_sexp_build(sexp, off, self.format_key_q, + ffi.cast("int", len(q)), public) + if r != 0: + raise ValueError("libgcrypt error", r) + # libgcrypt.gcry_sexp_dump(sexp[0]) + return sexp[0] + + def call_pk_encrypt(self, q, ecdh_scalar): + skey = self.make_skey_by_public(q) + # + if ecdh_scalar[0] >= 128: + ecdh_scalar = b'\x00' + ecdh_scalar + + len_shared = len(ecdh_scalar) + shared_in_c = ffi.new("char []", ecdh_scalar) + data_sexp = ffi.new("void **") + off = ffi.new("unsigned long long *") + r = libgcrypt.gcry_sexp_build(data_sexp, off, b"%b", + ffi.cast("int", len_shared), shared_in_c) + if r != 0: + raise ValueError("libgcrypt error", r) + # libgcrypt.gcry_sexp_dump(data_sexp[0]) + + ct_sexp = ffi.new("void **") + r = libgcrypt.gcry_pk_encrypt(ct_sexp, data_sexp[0], skey) + if r != 0: + raise ValueError("libgcrypt error", r) + # libgcrypt.gcry_sexp_dump(ct_sexp[0]) + token_in_c = ffi.new("char []", b"s") + s_sexp = libgcrypt.gcry_sexp_find_token(ct_sexp[0], token_in_c, 1) + length = ffi.new("size_t *") + enc_s = libgcrypt.gcry_sexp_nth_data(s_sexp, 1, length) + len_s = length[0] + # + token_in_c = ffi.new("char []", b"e") + e_sexp = libgcrypt.gcry_sexp_find_token(ct_sexp[0], token_in_c, 1) + enc_e = libgcrypt.gcry_sexp_nth_data(e_sexp, 1, length) + len_e = length[0] + # + return (ffi.unpack(enc_s,len_s), ffi.unpack(enc_e,len_e)) + + # Unfourtunately, in libgcrypt, DATA, R and S is signed + def call_pk_verify(self, q, data, sig): + skey = self.make_skey_by_public(q) + + if data[0] >= 128: + data = b'\x00' + data + + data_in_c = ffi.new("char []", data) + data_sexp = ffi.new("void **") + off = ffi.new("unsigned long long *") + r = libgcrypt.gcry_sexp_build(data_sexp, off, FORMAT_DATA, + ffi.cast("int", len(data)), data_in_c) + if r != 0: + raise ValueError("libgcrypt error", r) + + sig_r = sig[0:self.dlen] + if sig_r[0] >= 128: + sig_r = b'\x00' + sig_r + + sig_s = sig[self.dlen:] + if sig_s[0] >= 128: + sig_s = b'\x00' + sig_s + + sig_r_in_c = ffi.new("char []", sig_r) + sig_s_in_c = ffi.new("char []", sig_s) + + sig_sexp = ffi.new("void **") + off = ffi.new("unsigned long long *") + r = libgcrypt.gcry_sexp_build(sig_sexp, off, FORMAT_SIG, + ffi.cast("int", len(sig_r)), sig_r_in_c, + ffi.cast("int", len(sig_s)), sig_s_in_c) + if r != 0: + raise ValueError("libgcrypt error", r) + + # libgcrypt.gcry_sexp_dump(sig_sexp[0]) + # libgcrypt.gcry_sexp_dump(data_sexp[0]) + r = libgcrypt.gcry_pk_verify(sig_sexp[0], data_sexp[0], skey) + return r == 0 diff --git a/tests/pubkey_crypto.py b/tests/pubkey_crypto.py new file mode 100644 index 0000000..92a7768 --- /dev/null +++ b/tests/pubkey_crypto.py @@ -0,0 +1,26 @@ +# from nistp256_keys import * +# from nistp384_keys import * +# from nistp521_keys import * +# from brainpoolp256r1_keys import * +# from brainpoolp384r1_keys import * +# from brainpoolp512r1_keys import * +import rsa_keys +import curve25519_keys + +def get_PK_Crypto(card): + if card.is_gnuk: + return curve25519_keys.PK_Crypto + else: + return rsa_keys.PK_Crypto + +def get_key(card): + if card.is_gnuk: + return curve25519_keys.key + else: + return rsa_keys.key + +def get_test_vector(card): + if card.is_gnuk: + return curve25519_keys.test_vector + else: + return rsa_keys.test_vector diff --git a/tests/rsa_keys.py b/tests/rsa_keys.py new file mode 100644 index 0000000..57a5ac7 --- /dev/null +++ b/tests/rsa_keys.py @@ -0,0 +1,284 @@ +from binascii import hexlify, unhexlify +from time import time +from struct import pack +from hashlib import sha1, sha256 +from os import urandom +from card_const import KEY_ATTRIBUTES_RSA2K + +def build_privkey_template_for_remove(openpgp_keyno): + if openpgp_keyno == 1: + keyspec = b'\xb6' + elif openpgp_keyno == 2: + keyspec = b'\xb8' + else: + keyspec = b'\xa4' + return b'\x4d\x02' + keyspec + b'\x00' + +# egcd and modinv are from wikibooks +# https://en.wikibooks.org/wiki/Algorithm_Implementation/Mathematics/Extended_Euclidean_algorithm + +def egcd(a, b): + if a == 0: + return (b, 0, 1) + else: + g, y, x = egcd(b % a, a) + return (g, x - (b // a) * y, y) + +def modinv(a, m): + g, x, y = egcd(a, m) + if g != 1: + raise Exception('modular inverse does not exist') + else: + return x % m + +def pkcs1_pad_for_sign(digestinfo): + byte_repr = b'\x00' + b'\x01' + bytes.ljust(b'', 256 - 19 - 32 - 3, b'\xff') \ + + b'\x00' + digestinfo + return int(hexlify(byte_repr), 16) + +def pkcs1_pad_for_crypt(msg): + padlen = 256 - 3 - len(msg) + byte_repr = b'\x00' + b'\x02' \ + + bytes.replace(urandom(padlen), b'\x00', b'\x01') + b'\x00' + msg + return int(hexlify(byte_repr), 16) + +def integer_to_bytes_256(i): + return i.to_bytes(256, byteorder='big') + + +class PK_Crypto(object): + @staticmethod + def pk_from_pk_info(pk_info): + return pk_info[9:9+256] + + @staticmethod + def compute_digestinfo(msg): + digest = sha256(msg).digest() + prefix = b'\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20' + return prefix + digest + @staticmethod + def enc_data(enc_info): + return enc_info[1] + + @staticmethod + def enc_check(enc_info, s): + return enc_info[0] == s + + + def __init__(self, keyno=None, pk_info=None, data=None): + if keyno == None: + # Just for name space + return + + self.keyno = keyno + self.timestamp = pack('>I', int(time())) + if pk_info: + # Public part only (no private data) + self.bytes_n = pk_info[9:9+256] + self.bytes_e = pk_info[9+256+2:] + self.e = int.from_bytes(self.bytes_e, "big") + self.n = int.from_bytes(self.bytes_n, "big") + else: + # Public/Private part from data + self.bytes_e = data[0] + self.bytes_n = data[1] + self.bytes_p = data[2] + self.bytes_q = data[3] + self.e = int.from_bytes(self.bytes_e, "big") + self.n = int.from_bytes(self.bytes_n, "big") + self.p = int.from_bytes(self.bytes_p, "big") + self.q = int.from_bytes(self.bytes_q, "big") + if self.n != self.p * self.q: + raise ValueError("wrong key", self.p, self.q, self.n) + self.fpr = self.calc_fpr() + + def calc_fpr(self): + m_len = 6 + 2 + 256 + 2 + 4 + m = b'\x99' + pack('>H', m_len) + b'\x04' + self.timestamp + b'\x01' + \ + pack('>H', 2048) + self.bytes_n + pack('>H', 17) + self.bytes_e + return sha1(m).digest() + + def build_privkey_template(self, is_yubikey): + openpgp_keyno = self.keyno + 1 + if openpgp_keyno == 1: + keyspec = b'\xb6' + elif openpgp_keyno == 2: + keyspec = b'\xb8' + else: + keyspec = b'\xa4' + + key_template = b'\x91' + (b'\x03' if is_yubikey else b'\x04') \ + + b'\x92\x81\x80' + b'\x93\x81\x80' + exthdr = keyspec + b'\x00' + b'\x7f\x48' + b'\x08' + key_template + suffix = b'\x5f\x48' + b'\x82\x01' + (b'\x03' if is_yubikey else b'\x04') + return b'\x4d' + b'\x82\x01' + (b'\x15' if is_yubikey else b'\x16') \ + + exthdr + suffix \ + + (b'' if is_yubikey else b'\x00') + self.bytes_e \ + + self.bytes_p + self.bytes_q + + def compute_signature(self, digestinfo): + p1 = self.p - 1 + q1 = self.q - 1 + h = p1 * q1 + d = modinv(self.e, h) + dp = d % p1 + dq = d % q1 + qp = modinv(self.q, self.p) + input = pkcs1_pad_for_sign(digestinfo) + t1 = pow(input, dp, self.p) + t2 = pow(input, dq, self.q) + t = ((t1 - t2) * qp) % self.p + sig = t2 + t * self.q + return sig.to_bytes(int((sig.bit_length()+7)/8), byteorder='big') + + def verify_signature(self, digestinfo, sig_bytes): + sig = int(hexlify(sig_bytes),16) + di_pkcs1 = pow(sig, self.e, self.n) + m = pkcs1_pad_for_sign(digestinfo) + return di_pkcs1 == m + + def encrypt(self, plaintext): + m = pkcs1_pad_for_crypt(plaintext) + return (plaintext, b'\x00' + integer_to_bytes_256(pow(m, self.e, self.n))) + + def get_fpr(self): + return self.fpr + + def get_timestamp(self): + return self.timestamp + + def get_pk(self): + return self.bytes_n + +key0_data = ( + b'\x01\x00\x01', + b'\xc6\xc8\x77\xdf\xd3\xb4\x41\xf8\xfb\x1b\x8d\xc5\x04\x09\x3a\x51' + b'\xc2\xef\xe4\x88\x3f\xe0\xa6\x37\x92\x05\xac\xc6\xe6\x73\x70\x99' + b'\x05\xe4\xd7\x67\xdd\xf4\x61\x43\xc5\x35\xcc\x6d\x7f\x10\xb6\x16' + b'\xf5\x20\xd8\x34\x63\x20\xef\x69\xff\x4a\x2c\x4f\x4a\x14\x8e\xdc' + b'\x65\xf7\xad\x24\xed\x7d\x4f\xe2\x3b\xb8\x62\xa0\xae\x71\xf4\xf7' + b'\x90\x4a\xba\xc0\x39\x7a\xbf\x32\x13\xdf\x91\x32\x6b\x1a\x25\x55' + b'\x4b\x3b\x18\xcf\x54\x58\x4d\x8b\xf2\x20\x16\x9f\xc9\x2b\x2a\xa5' + b'\x11\xe8\x31\x39\x83\xe7\x2b\x4c\x91\x10\xb3\xa1\xae\xa0\x87\xae' + b'\xbe\xf9\x58\x73\x86\x56\x08\xe8\xfa\xea\x9e\xf1\x0e\x7f\x7f\x3a' + b'\x66\xca\x8d\xef\x2d\x49\x9c\x31\x49\xc1\x27\x49\x1e\x0e\x43\x39' + b'\xfd\x6a\xbe\x10\xbf\xc6\xc1\x3e\x43\xd5\x22\x00\x4f\x14\x85\x76' + b'\x73\x28\xea\xbe\x35\xd6\xff\xa8\xdf\x4c\x15\xf0\xfb\xcd\x4e\xb1' + b'\xc0\x7c\xc6\xd8\x5e\x27\x51\x39\xac\x69\xe2\x96\x22\x73\xae\x98' + b'\x72\x36\x92\x6d\xd6\xc1\x14\x4f\xce\x3e\x7a\xe5\x67\xfa\x58\xea' + b'\x60\x62\x0d\xfa\xfc\x52\xf9\x52\x99\xfe\xa6\x01\x73\x9f\xce\x27' + b'\xee\x71\xee\xa9\x78\xd0\x07\x4f\x21\xe7\x08\x6f\x60\xba\x83\x31', + b'\xcc\x36\x5b\x57\x02\x71\x4b\xf2\x03\xe8\xc4\x9b\x0b\x8a\xfa\x8d' + b'\xad\x58\x6e\x92\x9c\xf5\xed\xca\x38\xad\x07\xfa\x45\xef\xd5\xc2' + b'\xd8\x90\x22\xd2\x9f\x40\x28\x3a\x57\xe5\x0c\xa2\x4c\x5f\x28\xc8' + b'\xe9\x11\xa7\x4f\xaa\xf7\x96\xf1\x12\xe7\xe4\x81\x95\x95\x6f\x9a' + b'\x4d\xf7\x66\x8a\x53\x42\x52\x3b\x27\x17\x9c\xec\x95\x8f\x36\x32' + b'\x11\xee\x11\xd0\xec\x0e\x0e\x1b\x92\xca\x00\x7a\x61\xe8\xc9\xac' + b'\x14\xe0\x02\x29\xb9\xa7\x62\x48\x50\x19\x9e\x66\x67\xaf\xa1\xa4' + b'\x4d\xb8\xf3\xc5\xde\x0a\x8e\xef\x0e\x6d\xe0\x50\xac\x0a\xc6\x33', + b'\xf9\x31\xa3\xc1\x2f\x0e\x3a\x52\x76\xf7\x12\xb7\x70\x65\x90\xba' + b'\x02\xe1\x4a\x97\xff\x9b\x8c\xe3\x15\x2a\xf0\xfc\x4d\x9c\xdc\x69' + b'\x0e\xa9\xbc\x4c\x82\xcb\x16\xc7\xd2\x31\x36\xcb\xda\xb5\x8f\xbe' + b'\xc6\x98\x80\xa8\x8b\xca\x85\xc4\x21\x4d\xf0\x10\x45\x08\x2c\xbe' + b'\x9f\x41\x92\xe3\xe3\x9c\x79\x89\x65\x33\xc3\x7d\xad\x9e\xb9\xe7' + b'\x3c\x26\x43\xb9\xc0\xa7\x04\xa4\xf9\x3d\x81\x57\x35\x37\x96\x3d' + b'\x6b\x6e\x51\x40\xa2\x4c\x70\x2d\x9f\x26\xe0\x6a\x20\x95\xde\x90' + b'\x6d\xaa\x88\x24\x17\x2a\x6b\x39\xf5\x63\xb7\x15\x39\x07\x05\x0b' +) +key1_data = ( + b'\x01\x00\x01', + b'\xd3\x92\x71\x4c\x29\x73\x8a\xac\x63\x72\xf2\xc8\x65\x4a\x08\xc2' + b'\x5a\x12\x99\xfe\xd7\x00\x4b\xd5\x12\xcd\x24\x52\xb5\x03\xeb\xad' + b'\x63\x01\x13\x08\x16\xac\x52\x5b\xa5\x28\xdc\x15\x5b\xe6\x34\x7a' + b'\x5c\x70\x40\x7f\xb4\xfb\xda\xed\x75\x1d\xfc\x0a\x7c\xd5\xe3\x91' + b'\x02\x72\xff\x23\x6c\x4e\xd1\xce\x5d\xe6\x62\x0b\x19\x1a\x17\x2e' + b'\x5b\x24\x73\x47\xb8\xca\xb7\x3a\x43\xd7\x92\x21\x70\x87\x55\xc9' + b'\x59\xa2\xf8\x3f\x48\x64\x39\xda\x30\x91\x73\x84\x55\x43\x31\x53' + b'\x2a\xab\xc8\x32\x6d\xb4\x88\x66\xf8\xc9\x11\x98\x83\x4a\x86\xab' + b'\x94\x67\x9f\x61\x75\xdb\x73\x7b\xdf\x39\x9e\x3f\x0b\x73\x7d\xcb' + b'\x1f\x42\x08\x27\x9d\x3e\x1c\xc6\x94\xe7\x86\x86\x78\x5e\x4f\x36' + b'\x3a\x37\x7d\xec\x91\x2b\x7c\x2f\x75\x7b\x14\x22\xd8\x66\xfb\x9f' + b'\xa8\x5c\x96\xb8\x3a\xdf\xd6\xa2\x23\x98\x9a\x9a\x02\x98\x8b\xde' + b'\xe8\x1a\xd1\x7e\xff\x63\x85\xe7\xb3\x8c\xec\x86\x11\xfd\xf3\x67' + b'\xba\x4a\xc8\xe9\x0d\x5f\x48\xac\x77\x15\xc5\xf4\x7a\xea\x06\xa4' + b'\xa3\x7c\xda\xa3\x02\x9c\xe5\x9d\x29\xbc\x66\x85\x3b\xf6\x75\x8e' + b'\xf4\xa7\xda\x5a\x59\x53\xf5\xe5\x57\xa5\xa2\x2f\x67\xc3\x68\xc3', + b'\xda\xe0\x85\x95\x2c\x5b\xee\xe3\x8f\x25\xf0\x9b\xc3\x7a\x4c\xa2' + b'\x43\x4c\x31\xf7\x80\x55\x46\x9d\x0d\x5f\x0b\xf3\x33\x7e\x3a\x70' + b'\xba\x6c\x91\x73\x4f\x19\x5b\x74\x2e\x21\x1a\x5f\xe2\x83\xbe\xfd' + b'\xf6\x68\x20\x00\x8e\x6e\xf2\xc8\xca\x54\xa9\x19\x22\x83\x8f\xce' + b'\x07\xd9\xe3\x3a\x33\x1c\xe2\x0d\xac\x36\x80\x3e\x77\x7d\x5e\xe2' + b'\x19\x5e\xd2\x8d\x6a\x40\x45\xe2\x86\x23\xa6\xa6\x0b\x06\x61\xe4' + b'\x5f\x7c\x4f\x84\xae\x2b\x1d\xfa\xd0\xcf\x1e\xc3\x06\x05\x15\x83' + b'\x23\x38\x2a\x81\x9e\x73\x0c\x09\xa3\x3f\xad\x70\x4d\xd6\x75\x01', + b'\xf7\x74\xbe\x43\xea\x19\x8a\xa2\xf0\x89\x27\x4e\x4f\xff\xd7\xd0' + b'\x09\x2e\xe7\xb3\x5a\x1d\x2f\x85\x4c\xdb\x16\x6f\x69\x8c\xaa\xb7' + b'\x2f\xde\xb0\x99\xe6\x90\xe7\x84\x38\xb2\xe0\x43\xe4\x52\xd4\xd2' + b'\xf1\x9d\x7f\x44\xba\x6b\x28\x66\x42\xf0\xce\x52\x04\x96\x6f\xf9' + b'\x8e\xcd\x9e\x3b\x44\x88\x77\x32\x46\x31\x36\x5d\xc8\x60\x79\x74' + b'\x29\xb9\x41\x4a\x21\xa7\xe1\x66\xd5\x04\xca\xce\x15\x65\x88\xb9' + b'\xa1\x45\x65\x7e\xeb\x1a\xfb\x43\xb8\xff\x65\xd8\xd6\xd9\x3c\xea' + b'\x2b\xa4\xef\x8a\xab\x04\x78\x85\xc4\xde\x64\xff\xef\x0b\x49\xc3' +) +key2_data = ( + b'\x01\x00\x01', + b'\x9c\xf7\x19\x2b\x51\xa5\x74\xd1\xad\x3c\xcb\x08\xba\x09\xb8\x7f' + b'\x22\x85\x73\x89\x3e\xee\x35\x55\x29\xff\x24\x3e\x90\xfd\x4b\x86' + b'\xf7\x9a\x82\x09\x7c\xc7\x92\x2c\x04\x85\xbe\xd1\x61\x6b\x16\x56' + b'\xa9\xb0\xb1\x9e\xf7\x8e\xa8\xec\x34\xc3\x84\x01\x9a\xdc\x5d\x5b' + b'\xf4\xdb\x2d\x2a\x0a\x2d\x9c\xf1\x42\x77\xbd\xcb\x70\x56\xf4\x8b' + b'\x81\x21\x4e\x3f\x7f\x77\x42\x23\x1e\x29\x67\x39\x66\xf9\xb1\x10' + b'\x68\x62\x11\x2c\xc7\x98\xdb\xa8\xd4\xa1\x38\xbb\x5a\xbf\xc6\xd4' + b'\xc1\x2d\x53\xa5\xd3\x9b\x2f\x78\x3d\xa9\x16\xda\x20\x85\x2e\xe1' + b'\x39\xbb\xaf\xda\x61\xd4\x29\xca\xf2\xa4\xf3\x08\x47\xce\x7e\x7a' + b'\xe3\x2a\xb4\x06\x1e\x27\xdd\x9e\x4d\x00\xd6\x09\x10\x24\x9d\xb8' + b'\xd8\x55\x9d\xd8\x5f\x7c\xa5\x96\x59\xef\x40\x0c\x8f\x63\x18\x70' + b'\x0f\x4e\x97\xf0\xc6\xf4\x16\x5d\xe8\x06\x41\x49\x04\x33\xc8\x8d' + b'\xa8\x68\x2b\xef\xe6\x8e\xb3\x11\xf5\x4a\xf2\xb0\x7d\x97\xac\x74' + b'\xed\xb5\x39\x9c\xf0\x54\x76\x42\x11\x69\x4f\xbb\x8d\x1d\x33\x3f' + b'\x32\x69\xf2\x35\xab\xe0\x25\x06\x7f\x81\x1f\xf8\x3a\x22\x24\x82' + b'\x62\x19\xb3\x09\xea\x3e\x6c\x96\x8f\x42\xb3\xe5\x2f\x24\x5d\xc9', + b'\xb5\xab\x7b\x15\x92\x20\xb1\x8e\x36\x32\x58\xf6\x1e\xbd\xe0\x8b' + b'\xae\x83\xd6\xce\x2d\xbf\xe4\xad\xc1\x43\x62\x8c\x52\x78\x87\xac' + b'\xde\x9d\xe0\x9b\xf9\xb4\x9f\x43\x80\x19\x00\x4d\x71\x85\x5f\x30' + b'\xc2\xd6\x9b\x6c\x29\xbb\x98\x82\xab\x64\x1b\x33\x87\x40\x9f\xe9' + b'\x19\x94\x64\xa7\xfa\xa4\xb5\x23\x0c\x56\xd9\xe1\x7c\xd9\xed\x07' + b'\x4b\xc0\x01\x80\xeb\xed\x62\xba\xe3\xaf\x28\xe6\xff\x2a\xc2\x65' + b'\x4a\xd9\x68\x83\x4c\x5d\x5c\x88\xf8\xd9\xd3\xcc\x5e\x16\x7b\x10' + b'\x45\x3b\x04\x9d\x4e\x45\x4a\x57\x61\xfb\x0a\xc7\x17\x18\x59\x07', + b'\xdd\x2f\xff\xa9\x81\x42\x96\x15\x6a\x69\x26\xcd\x17\xb6\x55\x64' + b'\x18\x7e\x42\x4d\xca\xdc\xe9\xb0\x32\x24\x6a\xd7\xe4\x64\x48\xbb' + b'\x0f\x9e\x0f\xf3\xc6\x4f\x98\x74\x24\xb1\xa4\x0b\xc6\x94\xe2\xe9' + b'\xac\x4f\xb1\x93\x0d\x16\x35\x82\xd7\xac\xf2\x06\x53\xa1\xc4\x4b' + b'\x97\x84\x6c\x1c\x5f\xd8\xa7\xb1\x9b\xb2\x25\xfb\x39\xc3\x0e\x25' + b'\x41\x04\x83\xde\xaf\x8c\x25\x38\xd2\x22\xb7\x48\xc4\xd8\x10\x3b' + b'\x11\xce\xc0\x4f\x66\x6a\x5c\x0d\xbc\xbf\x5d\x5f\x62\x5f\x15\x8f' + b'\x65\x74\x6c\x3f\xaf\xe6\x41\x81\x45\xf7\xcf\xfa\x5f\xad\xee\xaf' +) + +key = [ + PK_Crypto(keyno=0, data=key0_data), + PK_Crypto(keyno=1, data=key1_data), + PK_Crypto(keyno=2, data=key1_data) +] + +PLAIN_TEXT0=b"This is a test message." +PLAIN_TEXT1=b"RSA decryption is as easy as pie." +PLAIN_TEXT2=b"This is another test message.\nMultiple lines.\n" +ENCRYPT_TEXT0 = b"encrypt me please" + +test_vector = { + 'sign_0' : PLAIN_TEXT0, + 'sign_1' : PLAIN_TEXT1, + 'auth_0' : PLAIN_TEXT0, + 'auth_1' : PLAIN_TEXT1, + 'decrypt_0' : PLAIN_TEXT0, + 'decrypt_1' : PLAIN_TEXT1, + 'encrypt_0' : ENCRYPT_TEXT0, +} + +rsa_pk = PK_Crypto() +rsa_pk.test_vector = test_vector +rsa_pk.key_list = key +rsa_pk.key_attr_list = [KEY_ATTRIBUTES_RSA2K, KEY_ATTRIBUTES_RSA2K, KEY_ATTRIBUTES_RSA2K] +rsa_pk.PK_Crypto = PK_Crypto diff --git a/tests/secp256k1_keys.py b/tests/secp256k1_keys.py new file mode 100644 index 0000000..b345b10 --- /dev/null +++ b/tests/secp256k1_keys.py @@ -0,0 +1,153 @@ +from time import time +from struct import pack +from hashlib import sha1, sha256 +from pk_signed_mpi_with_libgcrypt import PK_libgcrypt +from card_const import KEY_ATTRIBUTES_ECDH_SECP256K1, KEY_ATTRIBUTES_ECDSA_SECP256K1 +lg_secp256k1 = PK_libgcrypt(32, "secp256k1") + +class PK_Crypto(object): + @staticmethod + def pk_from_pk_info(pk_info): + return pk_info[5:] + + @staticmethod + def compute_digestinfo(msg): + return sha256(msg).digest() + + @staticmethod + def enc_data(enc_info): + return b'\xa6\x46\x7f\x49\x43\x86\x41' + enc_info[1] + + @staticmethod + def enc_check(enc_info, s): + point = enc_info[0] + # Gnuk returns point, instead of X + if len(s) == len(point): + return point == s + else: + # It's 04 || X || Y, extract X + return point[1:33] == s + + + def __init__(self, keyno=None, pk_info=None, data=None): + if keyno == None: + # Just for name space + return + + self.keyno = keyno + self.for_encryption = (self.keyno == 1) + self.timestamp = pack('>I', int(time())) + if pk_info: + # Public part only (no private data) from card + self.q = pk_info[5:] + else: + # Private part (in big endian) + self.d = data[0] + self.q = data[1] + self.fpr = self.calc_fpr() + + def calc_fpr(self): + m_len = 6 + 2 + 65 + ver = b'\x04' + algo = b'\x12' if self.for_encryption else b'\x16' + m = b'\x99' + pack('>H', m_len) + ver + self.timestamp + algo \ + + pack('>H', 512+3) + self.q + return sha1(m).digest() + + def build_privkey_template(self, is_yubikey): + openpgp_keyno = self.keyno + 1 + if openpgp_keyno == 1: + keyspec = b'\xb6' + elif openpgp_keyno == 2: + keyspec = b'\xb8' + else: + keyspec = b'\xa4' + key_template = b'\x92\x20' + exthdr = keyspec + b'\x00' + b'\x7f\x48' + b'\x02' + key_template + suffix = b'\x5f\x48' + b'\x20' + return b'\x4d' + b'\x2a' + exthdr + suffix + self.d + + def compute_signature(self, digestinfo): + return lg_secp256k1.call_pk_sign(self.d, digestinfo) + + def verify_signature(self, digestinfo, sig): + return lg_secp256k1.call_pk_verify(self.q, digestinfo, sig) + + def encrypt(self, plaintext): + # Do ECDH + return lg_secp256k1.call_pk_encrypt(self.q, plaintext) + + def get_fpr(self): + return self.fpr + + def get_timestamp(self): + return self.timestamp + + def get_pk(self): + return self.q + +key = [ None, None, None ] + +# We don't have definitive test vectors for secp256k1 + +# Private key values are from +# https://github.com/bitcoinjs/tiny-secp256k1/ +# tests/fixtures/points.json + +secp256k1_data0 = ( + b'\xb1\x12\x1e\x40\x88\xa6\x6a\x28\xf5\xb6\xb0\xf5\x84\x49\x43\xec' + b'\xd9\xf6\x10\x19\x6d\x7b\xb8\x3b\x25\x21\x4b\x60\x45\x2c\x09\xaf', + b'\x04' + b'\xb0\x7b\xa9\xdc\xa9\x52\x3b\x7e\xf4\xbd\x97\x70\x3d\x43\xd2\x03' + b'\x99\xeb\x69\x8e\x19\x47\x04\x79\x1a\x25\xce\x77\xa4\x00\xdf\x99' + b'\x8f\x04\x0d\xc5\x12\xf1\xfa\xd4\x3c\x6f\x93\x4a\x7c\xd9\x0c\xd1' + b'\x68\x4e\x60\xe7\x04\x9e\x63\x15\xc9\x88\xcc\x78\xcc\x38\x29\x54') + +secp256k1_data1 = ( + b'\x07\x05\xe4\xb4\x9e\xa2\x54\x78\xd6\x0b\xa7\x28\x7c\xf2\xcc\x02' + b'\x0c\x07\x4d\xd9\x7d\x47\x8c\x7f\x84\xa3\xcf\xba\xb8\xc3\x76\xe6', + b'\x04' + b'\x45\x54\x28\x85\x28\xe2\xc4\xf7\x61\x82\x62\x55\x48\x2f\x20\xab' + b'\xe3\xd7\xa8\xca\xf7\x47\xf2\x17\x09\x84\x11\x0b\x2f\x64\x54\x67' + b'\x77\xfa\x52\xf2\x70\x6c\xd8\xc3\x0f\x03\x01\x94\x39\x69\xb4\x46' + b'\x49\x40\x58\x18\xda\x26\x69\xd0\xf8\x21\xaf\x31\x59\x73\xfa\xa1' +) + +secp256k1_data2 = ( + b'\xbd\x66\x07\x4d\xae\x02\x76\xb2\x9d\xd5\xd1\x13\x6f\x53\x29\x3e' + b'\x68\xea\xc9\x4c\xeb\x82\xa2\xd2\x26\x64\x11\xb2\x2e\xc0\xf5\x9c', + b'\x04' + b'\x69\xe3\x3b\x06\x24\x0f\xfd\x20\x46\x79\x20\xfd\xb9\xa6\x20\x89' + b'\x56\x83\x27\xfe\x66\x8e\xb2\xd1\x7c\xb0\x2b\x8c\xe8\xe6\x08\xa5' + b'\xc7\xdd\x47\xaa\x62\xc7\x8e\xff\xaf\x82\xc4\xda\x7f\xd3\x93\x45' + b'\xcb\x7a\xd0\x5a\xd3\x2b\xcc\xd4\xa5\xe9\x56\x7b\x2d\x11\xe2\x4b' +) + +key[0] = PK_Crypto(0, data=secp256k1_data0) +key[1] = PK_Crypto(1, data=secp256k1_data1) +key[2] = PK_Crypto(2, data=secp256k1_data2) + +PLAIN_TEXT0=b"In this test, we verify card generated result by libgcrypt." +PLAIN_TEXT1=b"Signature is non-deterministic (it uses nonce K internally)." +PLAIN_TEXT2=b"We don't use secp256k1 test vectors (it specifies K of ECDSA)." +PLAIN_TEXT3=b"NOTE: Our test is not for ECDSA implementation itself." + +ENCRYPT_TEXT0 = sha256(b"!encrypt me please").digest() +ENCRYPT_TEXT1 = sha256(b"!!!encrypt me please, another").digest() +ENCRYPT_TEXT2 = sha256(b"!encrypt me please, the other").digest() + +test_vector = { + 'sign_0' : PLAIN_TEXT0, + 'sign_1' : PLAIN_TEXT1, + 'auth_0' : PLAIN_TEXT2, + 'auth_1' : PLAIN_TEXT3, + 'decrypt_0' : ENCRYPT_TEXT0, + 'decrypt_1' : ENCRYPT_TEXT1, + 'encrypt_0' : ENCRYPT_TEXT2, +} + +secp256k1_pk = PK_Crypto() +secp256k1_pk.test_vector = test_vector +secp256k1_pk.key_list = key +secp256k1_pk.key_attr_list = [KEY_ATTRIBUTES_ECDSA_SECP256K1, KEY_ATTRIBUTES_ECDH_SECP256K1, KEY_ATTRIBUTES_ECDSA_SECP256K1] +secp256k1_pk.PK_Crypto = PK_Crypto diff --git a/tests/skip_gnuk_only_tests.py b/tests/skip_gnuk_only_tests.py new file mode 100644 index 0000000..2fa9b36 --- /dev/null +++ b/tests/skip_gnuk_only_tests.py @@ -0,0 +1,6 @@ +import pytest + +@pytest.fixture(scope="module",autouse=True) +def check_gnuk_only(card): + if not card.is_gnuk: + pytest.skip("Gnuk only feature", allow_module_level=True) diff --git a/tests/skip_if_kdfreq.py b/tests/skip_if_kdfreq.py new file mode 100644 index 0000000..414ab5b --- /dev/null +++ b/tests/skip_if_kdfreq.py @@ -0,0 +1,6 @@ +import pytest + +@pytest.fixture(scope="module",autouse=True) +def check_kdfreq(card): + if card.kdf_required: + pytest.skip("Token requires KDF setup", allow_module_level=True) diff --git a/tests/skip_if_no_kdf_support.py b/tests/skip_if_no_kdf_support.py new file mode 100644 index 0000000..0954b91 --- /dev/null +++ b/tests/skip_if_no_kdf_support.py @@ -0,0 +1,6 @@ +import pytest + +@pytest.fixture(scope="module",autouse=True) +def check_kdf_support(card): + if not card.kdf_supported: + pytest.skip("No KDF support", allow_module_level=True) diff --git a/tests/util.py b/tests/util.py new file mode 100644 index 0000000..232a42e --- /dev/null +++ b/tests/util.py @@ -0,0 +1,34 @@ +def skip_tag_if_any(tagh, tagl, data): + if data == None: + return None + if len(data) == 0: + return b'' + # No tag, return DATA itself + if tagh == 0x00: + if tagl != data[0]: + return data + else: + if tagh != data[0]: + return data + elif tagl != data[1]: + raise ValueError(data) + data_len_b0 = data[1 if tagh==0 else 2] + if data_len_b0 == 0x81: + data_len = data[2 if tagh==0 else 3] + elif data_len_b0 == 0x82: + data_len = (data[2 if tagh==0 else 3] << 8)| data[3 if tagh==0 else 4] + else: + data_len = data_len_b0 + return data[len(data)-data_len:] + +def get_data_object(card, tag): + tagh = tag >> 8 + tagl = tag & 0xff + result = card.cmd_get_data(tagh, tagl) + if card.is_yubikey: + return skip_tag_if_any(tagh, tagl, result) + else: + return result + +def check_null(data_object): + return data_object == None or len(data_object) == 0