more tests (incomplete)
authorNIIBE Yutaka <gniibe@fsij.org>
Wed, 12 Oct 2016 01:22:57 +0000 (10:22 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Wed, 12 Oct 2016 01:22:57 +0000 (10:22 +0900)
12 files changed:
tests/README
tests/card_reader.py
tests/conftest.py
tests/openpgp_card.py
tests/rsa-aut.key [new file with mode: 0644]
tests/rsa-dec.key [new file with mode: 0644]
tests/rsa-sig.key [new file with mode: 0644]
tests/rsa_keys.py [new file with mode: 0644]
tests/test_empty_card.py
tests/test_personalize_card.py [new file with mode: 0644]
tests/test_personalize_reset_card.py [new file with mode: 0644]
tests/util.py [new file with mode: 0644]

index e51c643..204d68c 100644 (file)
@@ -7,3 +7,28 @@ You need to install:
 Please run test by typing:
 
     $ py.test-3 -x
+
+test_empty_card.py
+000
+001
+002
+
+010
+020
+021
+030
+040
+100
+101
+200
+201
+202
+203
+210
+211
+370
+380
+490
+
+
+003
index d3d360f..1e38fb8 100644 (file)
@@ -140,6 +140,9 @@ class CardReader(object):
         except:
             pass
 
+    def is_tpdu_reader(self):
+        return not self.__use_APDU
+
     def ccid_get_result(self):
         msg = self.__dev.read(self.__bulkin, 1024, self.__timeout)
         if len(msg) < 10:
@@ -255,7 +258,7 @@ class CardReader(object):
             return self.ccid_send_cmd(cmd)
         # TPDU case
         while len(cmd) > 254:
-            blk = cmd[0:253]
+            blk = cmd[0:254]
             cmd = cmd[254:]
             while True:
                 self.send_tpdu(info=blk,more=1)
index 51aea58..7b85ecf 100644 (file)
@@ -1,3 +1,15 @@
+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():
+    reader = get_ccid_device()
+    card = OpenPGP_Card(reader)
+    card.cmd_select_openpgp()
+    yield card
+    del card
index 5f30d42..84307df 100644 (file)
@@ -32,10 +32,18 @@ def iso7816_compose(ins, p1, p2, data, cls=0x00, le=None):
             return pack('>BBBBB', cls, ins, p1, p2, le)
     else:
         if not le:
-            return pack('>BBBBB', cls, ins, p1, p2, data_len) + data
+            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:
-            return pack('>BBBBB', cls, ins, p1, p2, data_len) \
-                + data + pack('>B', le)
+            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):
@@ -156,22 +164,26 @@ class OpenPGP_Card(object):
         return True
 
     def cmd_put_data_odd(self, tagh, tagl, content):
-        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 self.__reader.is_tpdu_reader():
+            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, data):
-        cmd_data = iso7816_compose(0x2c, how, 0x00, data)
+    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)
@@ -191,30 +203,48 @@ class OpenPGP_Card(object):
         return self.cmd_get_response(sw[1])
 
     def cmd_pso_longdata(self, p1, p2, data):
-        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])
+        if self.__reader.is_tpdu_reader():
+            cmd_data = iso7816_compose(0x2a, p1, p2, data, le=256)
+            print(cmd_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]))
+        else:
+            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])
 
     def cmd_internal_authenticate(self, data):
-        cmd_data = iso7816_compose(0x88, 0, 0, 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:
+        cmd_data = iso7816_compose(0x88, 0, 0, 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]))
-        return self.cmd_get_response(sw[1])
 
     def cmd_genkey(self, keyno):
         if keyno == 1:
diff --git a/tests/rsa-aut.key b/tests/rsa-aut.key
new file mode 100644 (file)
index 0000000..cdf2d5e
--- /dev/null
@@ -0,0 +1,4 @@
+9cf7192b51a574d1ad3ccb08ba09b87f228573893eee355529ff243e90fd4b86f79a82097cc7922c0485bed1616b1656a9b0b19ef78ea8ec34c384019adc5d5bf4db2d2a0a2d9cf14277bdcb7056f48b81214e3f7f7742231e29673966f9b1106862112cc798dba8d4a138bb5abfc6d4c12d53a5d39b2f783da916da20852ee139bbafda61d429caf2a4f30847ce7e7ae32ab4061e27dd9e4d00d60910249db8d8559dd85f7ca59659ef400c8f6318700f4e97f0c6f4165de80641490433c88da8682befe68eb311f54af2b07d97ac74edb5399cf054764211694fbb8d1d333f3269f235abe025067f811ff83a2224826219b309ea3e6c968f42b3e52f245dc9
+010001
+b5ab7b159220b18e363258f61ebde08bae83d6ce2dbfe4adc143628c527887acde9de09bf9b49f438019004d71855f30c2d69b6c29bb9882ab641b3387409fe9199464a7faa4b5230c56d9e17cd9ed074bc00180ebed62bae3af28e6ff2ac2654ad968834c5d5c88f8d9d3cc5e167b10453b049d4e454a5761fb0ac717185907
+dd2fffa9814296156a6926cd17b65564187e424dcadce9b032246ad7e46448bb0f9e0ff3c64f987424b1a40bc694e2e9ac4fb1930d163582d7acf20653a1c44b97846c1c5fd8a7b19bb225fb39c30e25410483deaf8c2538d222b748c4d8103b11cec04f666a5c0dbcbf5d5f625f158f65746c3fafe6418145f7cffa5fadeeaf
diff --git a/tests/rsa-dec.key b/tests/rsa-dec.key
new file mode 100644 (file)
index 0000000..8c2aa47
--- /dev/null
@@ -0,0 +1,4 @@
+d392714c29738aac6372f2c8654a08c25a1299fed7004bd512cd2452b503ebad6301130816ac525ba528dc155be6347a5c70407fb4fbdaed751dfc0a7cd5e3910272ff236c4ed1ce5de6620b191a172e5b247347b8cab73a43d79221708755c959a2f83f486439da30917384554331532aabc8326db48866f8c91198834a86ab94679f6175db737bdf399e3f0b737dcb1f4208279d3e1cc694e78686785e4f363a377dec912b7c2f757b1422d866fb9fa85c96b83adfd6a223989a9a02988bdee81ad17eff6385e7b38cec8611fdf367ba4ac8e90d5f48ac7715c5f47aea06a4a37cdaa3029ce59d29bc66853bf6758ef4a7da5a5953f5e557a5a22f67c368c3
+010001
+dae085952c5beee38f25f09bc37a4ca2434c31f78055469d0d5f0bf3337e3a70ba6c91734f195b742e211a5fe283befdf66820008e6ef2c8ca54a91922838fce07d9e33a331ce20dac36803e777d5ee2195ed28d6a4045e28623a6a60b0661e45f7c4f84ae2b1dfad0cf1ec30605158323382a819e730c09a33fad704dd67501
+f774be43ea198aa2f089274e4fffd7d0092ee7b35a1d2f854cdb166f698caab72fdeb099e690e78438b2e043e452d4d2f19d7f44ba6b286642f0ce5204966ff98ecd9e3b448877324631365dc860797429b9414a21a7e166d504cace156588b9a145657eeb1afb43b8ff65d8d6d93cea2ba4ef8aab047885c4de64ffef0b49c3
diff --git a/tests/rsa-sig.key b/tests/rsa-sig.key
new file mode 100644 (file)
index 0000000..c83179a
--- /dev/null
@@ -0,0 +1,4 @@
+c6c877dfd3b441f8fb1b8dc504093a51c2efe4883fe0a6379205acc6e673709905e4d767ddf46143c535cc6d7f10b616f520d8346320ef69ff4a2c4f4a148edc65f7ad24ed7d4fe23bb862a0ae71f4f7904abac0397abf3213df91326b1a25554b3b18cf54584d8bf220169fc92b2aa511e8313983e72b4c9110b3a1aea087aebef95873865608e8faea9ef10e7f7f3a66ca8def2d499c3149c127491e0e4339fd6abe10bfc6c13e43d522004f1485767328eabe35d6ffa8df4c15f0fbcd4eb1c07cc6d85e275139ac69e2962273ae987236926dd6c1144fce3e7ae567fa58ea60620dfafc52f95299fea601739fce27ee71eea978d0074f21e7086f60ba8331
+010001
+cc365b5702714bf203e8c49b0b8afa8dad586e929cf5edca38ad07fa45efd5c2d89022d29f40283a57e50ca24c5f28c8e911a74faaf796f112e7e48195956f9a4df7668a5342523b27179cec958f363211ee11d0ec0e0e1b92ca007a61e8c9ac14e00229b9a7624850199e6667afa1a44db8f3c5de0a8eef0e6de050ac0ac633
+f931a3c12f0e3a5276f712b7706590ba02e14a97ff9b8ce3152af0fc4d9cdc690ea9bc4c82cb16c7d23136cbdab58fbec69880a88bca85c4214df01045082cbe9f4192e3e39c79896533c37dad9eb9e73c2643b9c0a704a4f93d81573537963d6b6e5140a24c702d9f26e06a2095de906daa8824172a6b39f563b7153907050b
diff --git a/tests/rsa_keys.py b/tests/rsa_keys.py
new file mode 100644 (file)
index 0000000..3da4ade
--- /dev/null
@@ -0,0 +1,148 @@
+from binascii import hexlify, unhexlify
+from time import time
+from struct import pack
+from hashlib import sha1, sha256
+import string
+from os import urandom
+
+def read_key_from_file(file):
+    f = open(file)
+    n_str = f.readline()[:-1]
+    e_str = f.readline()[:-1]
+    p_str = f.readline()[:-1]
+    q_str = f.readline()[:-1]
+    f.close()
+    e = int(e_str, 16)
+    p = int(p_str, 16)
+    q = int(q_str, 16)
+    n = int(n_str, 16)
+    if n != p * q:
+        raise ValueError("wrong key", p, q, n)
+    return (unhexlify(n_str), unhexlify(e_str), unhexlify(p_str), unhexlify(q_str), e, p, q, n)
+
+def calc_fpr(n,e):
+    timestamp = int(time())
+    timestamp_data = pack('>I', timestamp)
+    m_len = 6 + 2 + 256 + 2 + 4
+    m = b'\x99' + pack('>H', m_len) + b'\x04' + timestamp_data + b'\x01' + \
+        pack('>H', 2048) + n + pack('>H', 17) + e
+    fpr = sha1(m).digest()
+    return (fpr, timestamp_data)
+
+key = [ None, None, None ]
+fpr = [ None, None, None ]
+timestamp = [ None, None, None ]
+
+key[0] = read_key_from_file('rsa-sig.key')
+key[1] = read_key_from_file('rsa-dec.key')
+key[2] = read_key_from_file('rsa-aut.key')
+
+(fpr[0], timestamp[0]) = calc_fpr(key[0][0], key[0][1])
+(fpr[1], timestamp[1]) = calc_fpr(key[1][0], key[1][1])
+(fpr[2], timestamp[2]) = calc_fpr(key[2][0], key[2][1])
+
+def build_privkey_template(openpgp_keyno, keyno):
+    n_bytes = key[keyno][0]
+    e_bytes = b'\x00' + key[keyno][1]
+    p_bytes = key[keyno][2]
+    q_bytes = key[keyno][3]
+
+    if openpgp_keyno == 1:
+        keyspec = b'\xb6'
+    elif openpgp_keyno == 2:
+        keyspec = b'\xb8'
+    else:
+        keyspec = b'\xa4'
+
+    key_template = b'\x91\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\x04'
+
+    t = b'\x4d' + b'\x82\x01\x16' + exthdr + suffix + e_bytes + p_bytes + q_bytes
+    return t
+
+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\02' + keyspec + b'\0x00'
+
+def compute_digestinfo(msg):
+    digest = sha256(msg).digest()
+    prefix = b'\x30\31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'
+    return prefix + digest
+
+# 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 compute_signature(keyno, digestinfo):
+    e = key[keyno][4]
+    p = key[keyno][5]
+    q = key[keyno][6]
+    n = key[keyno][7]
+    p1 = p - 1
+    q1 = q - 1
+    h = p1 * q1
+    d = modinv(e, h)
+    dp = d % p1
+    dq = d % q1
+    qp = modinv(q, p)
+
+    input = pkcs1_pad_for_sign(digestinfo)
+    t1 = pow(input, dp, p)
+    t2 = pow(input, dq, q)
+    t = ((t1 - t2) * qp) % p
+    sig = t2 + t * q
+    return sig
+
+def integer_to_bytes_256(i):
+    return i.to_bytes(256, byteorder='big')
+
+def encrypt(keyno, plaintext):
+    e = key[keyno][4]
+    n = key[keyno][7]
+    m = pkcs1_pad_for_crypt(plaintext)
+    return b'\x00' + integer_to_bytes_256(pow(m, e, n))
+
+def encrypt_with_pubkey(pubkey_info, plaintext):
+    n = int(hexlify(pubkey_info[0]), 16)
+    e = int(hexlify(pubkey_info[1]), 16)
+    m = pkcs1_pad_for_crypt(plaintext)
+    return b'\x00' + integer_to_bytes_256(pow(m, e, n))
+
+def verify_signature(pubkey_info, digestinfo, sig):
+    n = int(hexlify(pubkey_info[0]), 16)
+    e = int(hexlify(pubkey_info[1]), 16)
+    di_pkcs1 = pow(sig,e,n)
+    m = pkcs1_pad_for_sign(digestinfo)
+    return di_pkcs1 == m
index c7774f7..a9fd615 100644 (file)
@@ -1,5 +1,5 @@
 """
-test_empty_card.py - testing empty card
+test_empty_card.py - test empty card
 
 Copyright (C) 2016  g10 Code GmbH
 Author: NIIBE Yutaka <gniibe@fsij.org>
@@ -20,35 +20,14 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 """
 
-import pytest
 from re import match, DOTALL
+from util import *
 
 EMPTY_60=bytes(60)
 
 FACTORY_PASSPHRASE_PW1=b"123456"
 FACTORY_PASSPHRASE_PW3=b"12345678"
 
-@pytest.fixture(scope="module")
-def card():
-    if pytest.config.option.reader == "gnuk":
-        from card_reader import get_ccid_device
-        reader = get_ccid_device()
-        from openpgp_card import OpenPGP_Card
-        card = OpenPGP_Card(reader)
-    else:
-        raise ValueError("Reader Not Supported")
-    card.cmd_select_openpgp()
-    yield card
-    del card
-
-def get_data_object(card, tag):
-    tagh = tag >> 8
-    tagl = tag & 0xff
-    return card.cmd_get_data(tagh, tagl)
-
-def check_null(data_object):
-    return data_object == None or len(data_object) == 0
-
 def test_login(card):
     login = get_data_object(card, 0x5e)
     assert check_null(login)
diff --git a/tests/test_personalize_card.py b/tests/test_personalize_card.py
new file mode 100644 (file)
index 0000000..0a76199
--- /dev/null
@@ -0,0 +1,259 @@
+"""
+test_personalize_card.py - test personalizing card
+
+Copyright (C) 2016  g10 Code GmbH
+Author: NIIBE Yutaka <gniibe@fsij.org>
+
+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 <http://www.gnu.org/licenses/>.
+"""
+
+from struct import pack
+from re import match, DOTALL
+from util import *
+import rsa_keys
+
+FACTORY_PASSPHRASE_PW1=b"123456"
+FACTORY_PASSPHRASE_PW3=b"12345678"
+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"
+
+def test_setup_pw3_0(card):
+    r = card.cmd_change_reference_data(3, FACTORY_PASSPHRASE_PW3 + PW3_TEST0)
+    assert r
+
+def test_verify_pw3_0(card):
+    v = card.cmd_verify(3, PW3_TEST0)
+    assert v
+
+def test_login_put(card):
+    r = card.cmd_put_data(0x00, 0x5e, b"gpg_user")
+    assert r
+
+def test_name_put(card):
+    r = card.cmd_put_data(0x00, 0x5b, b"GnuPG User")
+    assert r
+
+def test_lang_put(card):
+    r = card.cmd_put_data(0x5f, 0x2d, b"ja")
+    assert r
+
+def test_sex_put(card):
+    r = card.cmd_put_data(0x5f, 0x35, b"1")
+    assert r
+
+def test_url_put(card):
+    r = card.cmd_put_data(0x5f, 0x50, b"https://www.fsij.org/gnuk/")
+    assert r
+
+def test_pw1_status_put(card):
+    r = card.cmd_put_data(0x00, 0xc4, b"\x01")
+    assert r
+
+def test_login(card):
+    login = get_data_object(card, 0x5e)
+    assert login == b"gpg_user"
+
+def test_name_lang_sex(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(card):
+    url = get_data_object(card, 0x5f50)
+    assert url == b"https://www.fsij.org/gnuk/"
+
+def test_pw1_status(card):
+    s = get_data_object(card, 0xc4)
+    assert match(b'\x01...\x03[\x00\x03]\x03', s, DOTALL)
+
+def test_rsa_import_key_1(card):
+    t = rsa_keys.build_privkey_template(1, 0)
+    r = card.cmd_put_data_odd(0x3f, 0xff, t)
+    assert r
+
+def test_rsa_import_key_2(card):
+    t = rsa_keys.build_privkey_template(2, 1)
+    r = card.cmd_put_data_odd(0x3f, 0xff, t)
+    assert r
+
+def test_rsa_import_key_3(card):
+    t = rsa_keys.build_privkey_template(3, 2)
+    r = card.cmd_put_data_odd(0x3f, 0xff, t)
+    assert r
+
+def test_fingerprint_1_put(card):
+    fpr1 = rsa_keys.fpr[0]
+    r = card.cmd_put_data(0x00, 0xc7, fpr1)
+    assert r
+
+def test_fingerprint_2_put(card):
+    fpr2 = rsa_keys.fpr[1]
+    r = card.cmd_put_data(0x00, 0xc8, fpr2)
+    assert r
+
+def test_fingerprint_3_put(card):
+    fpr3 = rsa_keys.fpr[2]
+    r = card.cmd_put_data(0x00, 0xc9, fpr3)
+    assert r
+
+def test_timestamp_1(card):
+    timestamp1 = rsa_keys.timestamp[0]
+    r = card.cmd_put_data(0x00, 0xce, timestamp1)
+    assert r
+
+def test_timestamp_2(card):
+    timestamp2 = rsa_keys.timestamp[1]
+    r = card.cmd_put_data(0x00, 0xcf, timestamp2)
+    assert r
+
+def test_timestamp_3(card):
+    timestamp3 = rsa_keys.timestamp[2]
+    r = card.cmd_put_data(0x00, 0xd0, timestamp3)
+    assert r
+
+def test_setup_pw1_0(card):
+    r = card.cmd_change_reference_data(1, FACTORY_PASSPHRASE_PW1 + PW1_TEST0)
+    assert r
+
+def test_verify_pw1_0(card):
+    v = card.cmd_verify(1, PW1_TEST0)
+    assert v
+
+def test_verify_pw1_0_2(card):
+    v = card.cmd_verify(2, PW1_TEST0)
+    assert v
+
+def test_setup_pw1_1(card):
+    r = card.cmd_change_reference_data(1, PW1_TEST0 + PW1_TEST1)
+    assert r
+
+def test_verify_pw1_1(card):
+    v = card.cmd_verify(1, PW1_TEST1)
+    assert v
+
+def test_verify_pw1_1_2(card):
+    v = card.cmd_verify(2, PW1_TEST1)
+    assert v
+
+def test_setup_reset_code(card):
+    r = card.cmd_put_data(0x00, 0xd3, RESETCODE_TEST)
+    assert r
+
+def test_reset_code(card):
+    r = card.cmd_reset_retry_counter(0, 0x81, RESETCODE_TEST + PW1_TEST2)
+    assert r
+
+def test_verify_pw1_2(card):
+    v = card.cmd_verify(1, PW1_TEST2)
+    assert v
+
+def test_verify_pw1_2_2(card):
+    v = card.cmd_verify(2, PW1_TEST2)
+    assert v
+
+def test_setup_pw3_1(card):
+    r = card.cmd_change_reference_data(3, PW3_TEST0 + PW3_TEST1)
+    assert r
+
+def test_verify_pw3_1(card):
+    v = card.cmd_verify(3, PW3_TEST1)
+    assert v
+
+def test_reset_userpass_admin(card):
+    r = card.cmd_reset_retry_counter(2, 0x81, PW1_TEST3)
+    assert r
+
+def test_verify_pw1_3(card):
+    v = card.cmd_verify(1, PW1_TEST3)
+    assert v
+
+def test_verify_pw1_3_2(card):
+    v = card.cmd_verify(2, PW1_TEST3)
+    assert v
+
+def test_setup_pw1_4(card):
+    r = card.cmd_change_reference_data(1, PW1_TEST3 + PW1_TEST4)
+    assert r
+
+def test_verify_pw1_4(card):
+    v = card.cmd_verify(1, PW1_TEST4)
+    assert v
+
+def test_verify_pw1_4_2(card):
+    v = card.cmd_verify(2, PW1_TEST4)
+    assert v
+
+def test_setup_pw3_2(card):
+    r = card.cmd_change_reference_data(3, PW3_TEST1 + PW3_TEST0)
+    assert r
+
+def test_verify_pw3_2(card):
+    v = card.cmd_verify(3, PW3_TEST0)
+    assert v
+
+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"
+
+def test_sign_0(card):
+    digestinfo = rsa_keys.compute_digestinfo(PLAIN_TEXT0)
+    r = card.cmd_pso_longdata(0x9e, 0x9a, digestinfo)
+    sig = rsa_keys.compute_signature(0, digestinfo)
+    sig_bytes = sig.to_bytes(int((sig.bit_length()+7)/8), byteorder='big')
+    assert r == sig_bytes
+
+def test_sign_1(card):
+    digestinfo = rsa_keys.compute_digestinfo(PLAIN_TEXT1)
+    r = card.cmd_pso_longdata(0x9e, 0x9a, digestinfo)
+    sig = rsa_keys.compute_signature(0, digestinfo)
+    sig_bytes = sig.to_bytes(int((sig.bit_length()+7)/8), byteorder='big')
+    assert r == sig_bytes
+
+def test_sign_auth_0(card):
+    digestinfo = rsa_keys.compute_digestinfo(PLAIN_TEXT0)
+    r = card.cmd_internal_authenticate(digestinfo)
+    sig = rsa_keys.compute_signature(2, digestinfo)
+    sig_bytes = sig.to_bytes(int((sig.bit_length()+7)/8), byteorder='big')
+    assert r == sig_bytes
+
+def test_sign_auth_1(card):
+    digestinfo = rsa_keys.compute_digestinfo(PLAIN_TEXT1)
+    r = card.cmd_internal_authenticate(digestinfo)
+    sig = rsa_keys.compute_signature(2, digestinfo)
+    sig_bytes = sig.to_bytes(int((sig.bit_length()+7)/8), byteorder='big')
+    assert r == sig_bytes
+
+def test_decrypt_0(card):
+    ciphertext = rsa_keys.encrypt(1, PLAIN_TEXT0)
+    r = card.cmd_pso_longdata(0x80, 0x86, ciphertext)
+    assert r == PLAIN_TEXT0
+
+def test_decrypt_1(card):
+    ciphertext = rsa_keys.encrypt(1, PLAIN_TEXT1)
+    r = card.cmd_pso_longdata(0x80, 0x86, ciphertext)
+    assert r == PLAIN_TEXT1
diff --git a/tests/test_personalize_reset_card.py b/tests/test_personalize_reset_card.py
new file mode 100644 (file)
index 0000000..bc5c986
--- /dev/null
@@ -0,0 +1,86 @@
+"""
+test_personalize_reset_card.py - test resetting personalization of card
+
+Copyright (C) 2016  g10 Code GmbH
+Author: NIIBE Yutaka <gniibe@fsij.org>
+
+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 <http://www.gnu.org/licenses/>.
+"""
+
+from struct import pack
+from re import match, DOTALL
+from util import *
+import rsa_keys
+
+FACTORY_PASSPHRASE_PW1=b"123456"
+FACTORY_PASSPHRASE_PW3=b"12345678"
+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"
+
+def test_login_put(card):
+    r = card.cmd_put_data(0x00, 0x5e, b"")
+    assert r
+
+def test_name_put(card):
+    r = card.cmd_put_data(0x00, 0x5b, b"")
+    assert r
+
+def test_lang_put(card):
+    r = card.cmd_put_data(0x5f, 0x2d, b"de")
+    assert r
+
+def test_sex_put(card):
+    r = card.cmd_put_data(0x5f, 0x35, b"0")
+    assert r
+
+def test_url_put(card):
+    r = card.cmd_put_data(0x5f, 0x50, b"")
+    assert r
+
+def test_pw1_status_put(card):
+    r = card.cmd_put_data(0x00, 0xc4, b"\x00")
+    assert r
+
+def test_setup_pw3_0(card):
+    r = card.cmd_change_reference_data(3, PW3_TEST0 + FACTORY_PASSPHRASE_PW3)
+    assert r
+
+def test_verify_pw3_0(card):
+    v = card.cmd_verify(3, FACTORY_PASSPHRASE_PW3)
+    assert v
+
+def test_setup_pw1_0(card):
+    r = card.cmd_change_reference_data(1, PW1_TEST4 + FACTORY_PASSPHRASE_PW1)
+    assert r
+
+def test_verify_pw1_0(card):
+    v = card.cmd_verify(1, FACTORY_PASSPHRASE_PW1)
+    assert v
+
+def test_verify_pw1_0_2(card):
+    v = card.cmd_verify(2, FACTORY_PASSPHRASE_PW1)
+    assert v
+
+def test_setup_reset_code(card):
+    r = card.cmd_put_data(0x00, 0xd3, b"")
+    assert r
diff --git a/tests/util.py b/tests/util.py
new file mode 100644 (file)
index 0000000..9e2726a
--- /dev/null
@@ -0,0 +1,7 @@
+def get_data_object(card, tag):
+    tagh = tag >> 8
+    tagl = tag & 0xff
+    return card.cmd_get_data(tagh, tagl)
+
+def check_null(data_object):
+    return data_object == None or len(data_object) == 0