More tests.
authorNIIBE Yutaka <gniibe@fsij.org>
Wed, 4 Apr 2018 07:34:07 +0000 (16:34 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Wed, 4 Apr 2018 07:34:07 +0000 (16:34 +0900)
tests/README
tests/openpgp_card.py
tests/test_001_personalize_card.py
tests/test_004_reset_pw3.py
tests/test_007_kdf.py
tool/kdf_calc.py

index 8aefe70..8155758 100644 (file)
@@ -6,7 +6,7 @@ Gnuk Token is supported as well.
 
 You need to install:
 
-   $ sudo apt-get install python3-pytest python3-usb
+   $ sudo apt install python3-pytest python3-usb python3-cffi
 
 Please run test by typing:
 
index 1b63b29..6dc0c73 100644 (file)
@@ -1,7 +1,7 @@
 """
 openpgp_card.py - a library for OpenPGP card
 
-Copyright (C) 2011, 2012, 2013, 2015, 2016
+Copyright (C) 2011, 2012, 2013, 2015, 2016, 2018
               Free Software Initiative of Japan
 Author: NIIBE Yutaka <gniibe@fsij.org>
 
@@ -21,7 +21,8 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 """
 
-from struct import pack
+from struct import pack, unpack
+from kdf_calc import kdf_calc
 
 def iso7816_compose(ins, p1, p2, data, cls=0x00, le=None):
     data_len = len(data)
@@ -54,6 +55,52 @@ class OpenPGP_Card(object):
         """
 
         self.__reader = reader
+        self.__kdf_iters = None
+        self.__kdf_salt_user = None
+        self.__kdf_salt_reset = None
+        self.__kdf_salt_admin = None
+
+    def configure_with_kdf(self):
+        kdf_data = self.cmd_get_data(0x00, 0xf9)
+        if kdf_data != b"":
+            algo, subalgo, iters, salt_user, salt_reset, salt_admin, hash_user, hash_admin = parse_kdf_data(kdf_data)
+            self.__kdf_iters = iters
+            self.__kdf_salt_user = salt_user
+            self.__kdf_salt_reset = salt_reset
+            self.__kdf_salt_admin = salt_admin
+        else:
+            self.__kdf_iters = None
+            self.__kdf_salt_user = None
+            self.__kdf_salt_reset = None
+            self.__kdf_salt_admin = None
+
+    # 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
+    def change_passwd(self, who, passwd_old, passwd_new):
+        if self.__kdf_iters:
+            salt = self.__kdf_salt_user
+            if who == 3 and self.__kdf_salt_admin:
+                    salt = self.__kdf_salt_admin
+            hash_old = kdf_calc(passwd_old, salt, self.__kdf_iters)
+            if passwd_new:
+                hash_new = kdf_calc(passwd_new, salt, self.__kdf_iters)
+            else:
+                hash_new = b""
+            return self.cmd_change_reference_data(who, hash_old + hash_new)
+        else:
+            if not passwd_new:
+                passwd_new = b""
+            return self.cmd_change_reference_data(who, passwd_old + passwd_new)
 
     def cmd_get_response(self, expected_len):
         result = b""
@@ -336,3 +383,48 @@ class OpenPGP_Card(object):
             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 )
index 928e9ae..c168c98 100644 (file)
@@ -28,7 +28,7 @@ from card_const import *
 from constants_for_test import *
 
 def test_setup_pw3_0(card):
-    r = card.cmd_change_reference_data(3, FACTORY_PASSPHRASE_PW3 + PW3_TEST0)
+    r = card.change_passwd(3, FACTORY_PASSPHRASE_PW3, PW3_TEST0)
     assert r
 
 def test_verify_pw3_0(card):
@@ -154,7 +154,7 @@ def test_public_key_3(card):
     assert rsa_keys.key[2][0] == pk[9:9+256]
 
 def test_setup_pw1_0(card):
-    r = card.cmd_change_reference_data(1, FACTORY_PASSPHRASE_PW1 + PW1_TEST0)
+    r = card.change_passwd(1, FACTORY_PASSPHRASE_PW1, PW1_TEST0)
     assert r
 
 def test_verify_pw1_0(card):
@@ -166,7 +166,7 @@ def test_verify_pw1_0_2(card):
     assert v
 
 def test_setup_pw1_1(card):
-    r = card.cmd_change_reference_data(1, PW1_TEST0 + PW1_TEST1)
+    r = card.change_passwd(1, PW1_TEST0, PW1_TEST1)
     assert r
 
 def test_verify_pw1_1(card):
@@ -194,7 +194,7 @@ def test_verify_pw1_2_2(card):
     assert v
 
 def test_setup_pw3_1(card):
-    r = card.cmd_change_reference_data(3, PW3_TEST0 + PW3_TEST1)
+    r = card.change_passwd(3, PW3_TEST0, PW3_TEST1)
     assert r
 
 def test_verify_pw3_1(card):
@@ -214,7 +214,7 @@ def test_verify_pw1_3_2(card):
     assert v
 
 def test_setup_pw1_4(card):
-    r = card.cmd_change_reference_data(1, PW1_TEST3 + PW1_TEST4)
+    r = card.change_passwd(1, PW1_TEST3, PW1_TEST4)
     assert r
 
 def test_verify_pw1_4(card):
@@ -226,7 +226,7 @@ def test_verify_pw1_4_2(card):
     assert v
 
 def test_setup_pw3_2(card):
-    r = card.cmd_change_reference_data(3, PW3_TEST1 + PW3_TEST0)
+    r = card.change_passwd(3, PW3_TEST1, PW3_TEST0)
     assert r
 
 def test_verify_pw3_2(card):
index 0d72169..7477d8a 100644 (file)
@@ -24,7 +24,7 @@ from card_const import *
 
 # Gnuk specific feature of clear PW3
 def test_setup_pw3_null(card):
-    r = card.cmd_change_reference_data(3, FACTORY_PASSPHRASE_PW3)
+    r = card.change_passwd(3, FACTORY_PASSPHRASE_PW3, None)
     assert r
 
 def test_verify_pw3(card):
index 37c34e8..e587c82 100644 (file)
@@ -24,29 +24,35 @@ from card_const import *
 from constants_for_test import *
 
 def test_verify_pw3_0(card):
-    v = card.cmd_verify(3, FACTORY_PASSPHRASE_PW3)
+    v = card.verify(3, FACTORY_PASSPHRASE_PW3)
     assert v
 
 def test_kdf_put_full(card):
     r = card.cmd_put_data(0x00, 0xf9, KDF_FULL)
+    if r:
+        card.configure_with_kdf()
     assert r
 
 def test_verify_pw3_1(card):
-    v = card.cmd_verify(3, KDF_FULL_HASH_PW3)
+    v = card.verify(3, FACTORY_PASSPHRASE_PW3)
     assert v
 
 def test_kdf_put_single(card):
     r = card.cmd_put_data(0x00, 0xf9, KDF_SINGLE)
+    if r:
+        card.configure_with_kdf()
     assert r
 
 def test_verify_pw3_2(card):
-    v = card.cmd_verify(3, KDF_SINGLE_HASH_PW3)
+    v = card.verify(3, FACTORY_PASSPHRASE_PW3)
     assert v
 
 def test_kdf_put_none(card):
     r = card.cmd_put_data(0x00, 0xf9, b"")
+    if r:
+        card.configure_with_kdf()
     assert r
 
 def test_verify_pw3_3(card):
-    v = card.cmd_verify(3, FACTORY_PASSPHRASE_PW3)
+    v = card.verify(3, FACTORY_PASSPHRASE_PW3)
     assert v
index ff4a643..aceef6c 100644 (file)
@@ -37,7 +37,11 @@ def kdf_calc(pw_string, salt_byte, iterations):
     ffi = FFI()
     ffi.cdef(DEF_gcry_kdf_derive)
     libgcrypt = ffi.dlopen("libgcrypt.so")
-    pw=ffi.new("char []", pw_string.encode('UTF-8'))
+    if isinstance(pw_string, str):
+        pw_byte = pw_string.encode('UTF-8')
+    else:
+        pw_byte = pw_string
+    pw=ffi.new("char []", pw_byte)
     salt = ffi.new("char []", salt_byte)
     kb = ffi.new("char []", 32)
     r = libgcrypt.gcry_kdf_derive(pw, len(pw_string), GCRY_KDF_ITERSALTED_S2K,