more tests (incomplete)
This commit is contained in:
25
tests/README
25
tests/README
@@ -7,3 +7,28 @@ You need to install:
|
|||||||
Please run test by typing:
|
Please run test by typing:
|
||||||
|
|
||||||
$ py.test-3 -x
|
$ 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
|
||||||
|
|||||||
@@ -140,6 +140,9 @@ class CardReader(object):
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def is_tpdu_reader(self):
|
||||||
|
return not self.__use_APDU
|
||||||
|
|
||||||
def ccid_get_result(self):
|
def ccid_get_result(self):
|
||||||
msg = self.__dev.read(self.__bulkin, 1024, self.__timeout)
|
msg = self.__dev.read(self.__bulkin, 1024, self.__timeout)
|
||||||
if len(msg) < 10:
|
if len(msg) < 10:
|
||||||
@@ -255,7 +258,7 @@ class CardReader(object):
|
|||||||
return self.ccid_send_cmd(cmd)
|
return self.ccid_send_cmd(cmd)
|
||||||
# TPDU case
|
# TPDU case
|
||||||
while len(cmd) > 254:
|
while len(cmd) > 254:
|
||||||
blk = cmd[0:253]
|
blk = cmd[0:254]
|
||||||
cmd = cmd[254:]
|
cmd = cmd[254:]
|
||||||
while True:
|
while True:
|
||||||
self.send_tpdu(info=blk,more=1)
|
self.send_tpdu(info=blk,more=1)
|
||||||
|
|||||||
@@ -1,3 +1,15 @@
|
|||||||
|
import pytest
|
||||||
|
from card_reader import get_ccid_device
|
||||||
|
from openpgp_card import OpenPGP_Card
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
parser.addoption("--reader", dest="reader", type=str, action="store",
|
parser.addoption("--reader", dest="reader", type=str, action="store",
|
||||||
default="gnuk", help="specify reader: gnuk or gemalto")
|
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
|
||||||
|
|||||||
@@ -32,10 +32,18 @@ def iso7816_compose(ins, p1, p2, data, cls=0x00, le=None):
|
|||||||
return pack('>BBBBB', cls, ins, p1, p2, le)
|
return pack('>BBBBB', cls, ins, p1, p2, le)
|
||||||
else:
|
else:
|
||||||
if not le:
|
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:
|
else:
|
||||||
return pack('>BBBBB', cls, ins, p1, p2, data_len) \
|
if data_len <= 255 and le < 256:
|
||||||
+ data + pack('>B', le)
|
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):
|
class OpenPGP_Card(object):
|
||||||
def __init__(self, reader):
|
def __init__(self, reader):
|
||||||
@@ -156,22 +164,26 @@ class OpenPGP_Card(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def cmd_put_data_odd(self, tagh, tagl, content):
|
def cmd_put_data_odd(self, tagh, tagl, content):
|
||||||
cmd_data0 = iso7816_compose(0xdb, tagh, tagl, content[:128], 0x10)
|
if self.__reader.is_tpdu_reader():
|
||||||
cmd_data1 = iso7816_compose(0xdb, tagh, tagl, content[128:])
|
cmd_data = iso7816_compose(0xdb, tagh, tagl, content)
|
||||||
sw = self.__reader.send_cmd(cmd_data0)
|
sw = self.__reader.send_cmd(cmd_data)
|
||||||
if len(sw) != 2:
|
else:
|
||||||
raise ValueError(sw)
|
cmd_data0 = iso7816_compose(0xdb, tagh, tagl, content[:128], 0x10)
|
||||||
if not (sw[0] == 0x90 and sw[1] == 0x00):
|
cmd_data1 = iso7816_compose(0xdb, tagh, tagl, content[128:])
|
||||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
sw = self.__reader.send_cmd(cmd_data0)
|
||||||
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]))
|
||||||
|
sw = self.__reader.send_cmd(cmd_data1)
|
||||||
if len(sw) != 2:
|
if len(sw) != 2:
|
||||||
raise ValueError(sw)
|
raise ValueError(sw)
|
||||||
if not (sw[0] == 0x90 and sw[1] == 0x00):
|
if not (sw[0] == 0x90 and sw[1] == 0x00):
|
||||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def cmd_reset_retry_counter(self, how, data):
|
def cmd_reset_retry_counter(self, how, who, data):
|
||||||
cmd_data = iso7816_compose(0x2c, how, 0x00, data)
|
cmd_data = iso7816_compose(0x2c, how, who, data)
|
||||||
sw = self.__reader.send_cmd(cmd_data)
|
sw = self.__reader.send_cmd(cmd_data)
|
||||||
if len(sw) != 2:
|
if len(sw) != 2:
|
||||||
raise ValueError(sw)
|
raise ValueError(sw)
|
||||||
@@ -191,30 +203,48 @@ class OpenPGP_Card(object):
|
|||||||
return self.cmd_get_response(sw[1])
|
return self.cmd_get_response(sw[1])
|
||||||
|
|
||||||
def cmd_pso_longdata(self, p1, p2, data):
|
def cmd_pso_longdata(self, p1, p2, data):
|
||||||
cmd_data0 = iso7816_compose(0x2a, p1, p2, data[:128], 0x10)
|
if self.__reader.is_tpdu_reader():
|
||||||
cmd_data1 = iso7816_compose(0x2a, p1, p2, data[128:])
|
cmd_data = iso7816_compose(0x2a, p1, p2, data, le=256)
|
||||||
sw = self.__reader.send_cmd(cmd_data0)
|
print(cmd_data)
|
||||||
if len(sw) != 2:
|
r = self.__reader.send_cmd(cmd_data)
|
||||||
raise ValueError(sw)
|
if len(r) < 2:
|
||||||
if not (sw[0] == 0x90 and sw[1] == 0x00):
|
raise ValueError(r)
|
||||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
sw = r[-2:]
|
||||||
sw = self.__reader.send_cmd(cmd_data1)
|
r = r[0:-2]
|
||||||
if len(sw) != 2:
|
if sw[0] == 0x61:
|
||||||
raise ValueError(sw)
|
return self.cmd_get_response(sw[1])
|
||||||
elif sw[0] != 0x61:
|
elif sw[0] == 0x90 and sw[1] == 0x00:
|
||||||
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
return r
|
||||||
return self.cmd_get_response(sw[1])
|
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):
|
def cmd_internal_authenticate(self, data):
|
||||||
cmd_data = iso7816_compose(0x88, 0, 0, data)
|
cmd_data = iso7816_compose(0x88, 0, 0, data, le=256)
|
||||||
sw = self.__reader.send_cmd(cmd_data)
|
r = self.__reader.send_cmd(cmd_data)
|
||||||
if len(sw) != 2:
|
if len(r) < 2:
|
||||||
raise ValueError(sw)
|
raise ValueError(r)
|
||||||
if sw[0] == 0x90 and sw[1] == 0x00:
|
sw = r[-2:]
|
||||||
return b""
|
r = r[0:-2]
|
||||||
elif sw[0] != 0x61:
|
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]))
|
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||||
return self.cmd_get_response(sw[1])
|
|
||||||
|
|
||||||
def cmd_genkey(self, keyno):
|
def cmd_genkey(self, keyno):
|
||||||
if keyno == 1:
|
if keyno == 1:
|
||||||
|
|||||||
4
tests/rsa-aut.key
Normal file
4
tests/rsa-aut.key
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
9cf7192b51a574d1ad3ccb08ba09b87f228573893eee355529ff243e90fd4b86f79a82097cc7922c0485bed1616b1656a9b0b19ef78ea8ec34c384019adc5d5bf4db2d2a0a2d9cf14277bdcb7056f48b81214e3f7f7742231e29673966f9b1106862112cc798dba8d4a138bb5abfc6d4c12d53a5d39b2f783da916da20852ee139bbafda61d429caf2a4f30847ce7e7ae32ab4061e27dd9e4d00d60910249db8d8559dd85f7ca59659ef400c8f6318700f4e97f0c6f4165de80641490433c88da8682befe68eb311f54af2b07d97ac74edb5399cf054764211694fbb8d1d333f3269f235abe025067f811ff83a2224826219b309ea3e6c968f42b3e52f245dc9
|
||||||
|
010001
|
||||||
|
b5ab7b159220b18e363258f61ebde08bae83d6ce2dbfe4adc143628c527887acde9de09bf9b49f438019004d71855f30c2d69b6c29bb9882ab641b3387409fe9199464a7faa4b5230c56d9e17cd9ed074bc00180ebed62bae3af28e6ff2ac2654ad968834c5d5c88f8d9d3cc5e167b10453b049d4e454a5761fb0ac717185907
|
||||||
|
dd2fffa9814296156a6926cd17b65564187e424dcadce9b032246ad7e46448bb0f9e0ff3c64f987424b1a40bc694e2e9ac4fb1930d163582d7acf20653a1c44b97846c1c5fd8a7b19bb225fb39c30e25410483deaf8c2538d222b748c4d8103b11cec04f666a5c0dbcbf5d5f625f158f65746c3fafe6418145f7cffa5fadeeaf
|
||||||
4
tests/rsa-dec.key
Normal file
4
tests/rsa-dec.key
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
d392714c29738aac6372f2c8654a08c25a1299fed7004bd512cd2452b503ebad6301130816ac525ba528dc155be6347a5c70407fb4fbdaed751dfc0a7cd5e3910272ff236c4ed1ce5de6620b191a172e5b247347b8cab73a43d79221708755c959a2f83f486439da30917384554331532aabc8326db48866f8c91198834a86ab94679f6175db737bdf399e3f0b737dcb1f4208279d3e1cc694e78686785e4f363a377dec912b7c2f757b1422d866fb9fa85c96b83adfd6a223989a9a02988bdee81ad17eff6385e7b38cec8611fdf367ba4ac8e90d5f48ac7715c5f47aea06a4a37cdaa3029ce59d29bc66853bf6758ef4a7da5a5953f5e557a5a22f67c368c3
|
||||||
|
010001
|
||||||
|
dae085952c5beee38f25f09bc37a4ca2434c31f78055469d0d5f0bf3337e3a70ba6c91734f195b742e211a5fe283befdf66820008e6ef2c8ca54a91922838fce07d9e33a331ce20dac36803e777d5ee2195ed28d6a4045e28623a6a60b0661e45f7c4f84ae2b1dfad0cf1ec30605158323382a819e730c09a33fad704dd67501
|
||||||
|
f774be43ea198aa2f089274e4fffd7d0092ee7b35a1d2f854cdb166f698caab72fdeb099e690e78438b2e043e452d4d2f19d7f44ba6b286642f0ce5204966ff98ecd9e3b448877324631365dc860797429b9414a21a7e166d504cace156588b9a145657eeb1afb43b8ff65d8d6d93cea2ba4ef8aab047885c4de64ffef0b49c3
|
||||||
4
tests/rsa-sig.key
Normal file
4
tests/rsa-sig.key
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
c6c877dfd3b441f8fb1b8dc504093a51c2efe4883fe0a6379205acc6e673709905e4d767ddf46143c535cc6d7f10b616f520d8346320ef69ff4a2c4f4a148edc65f7ad24ed7d4fe23bb862a0ae71f4f7904abac0397abf3213df91326b1a25554b3b18cf54584d8bf220169fc92b2aa511e8313983e72b4c9110b3a1aea087aebef95873865608e8faea9ef10e7f7f3a66ca8def2d499c3149c127491e0e4339fd6abe10bfc6c13e43d522004f1485767328eabe35d6ffa8df4c15f0fbcd4eb1c07cc6d85e275139ac69e2962273ae987236926dd6c1144fce3e7ae567fa58ea60620dfafc52f95299fea601739fce27ee71eea978d0074f21e7086f60ba8331
|
||||||
|
010001
|
||||||
|
cc365b5702714bf203e8c49b0b8afa8dad586e929cf5edca38ad07fa45efd5c2d89022d29f40283a57e50ca24c5f28c8e911a74faaf796f112e7e48195956f9a4df7668a5342523b27179cec958f363211ee11d0ec0e0e1b92ca007a61e8c9ac14e00229b9a7624850199e6667afa1a44db8f3c5de0a8eef0e6de050ac0ac633
|
||||||
|
f931a3c12f0e3a5276f712b7706590ba02e14a97ff9b8ce3152af0fc4d9cdc690ea9bc4c82cb16c7d23136cbdab58fbec69880a88bca85c4214df01045082cbe9f4192e3e39c79896533c37dad9eb9e73c2643b9c0a704a4f93d81573537963d6b6e5140a24c702d9f26e06a2095de906daa8824172a6b39f563b7153907050b
|
||||||
148
tests/rsa_keys.py
Normal file
148
tests/rsa_keys.py
Normal file
@@ -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
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
test_empty_card.py - testing empty card
|
test_empty_card.py - test empty card
|
||||||
|
|
||||||
Copyright (C) 2016 g10 Code GmbH
|
Copyright (C) 2016 g10 Code GmbH
|
||||||
Author: NIIBE Yutaka <gniibe@fsij.org>
|
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/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import pytest
|
|
||||||
from re import match, DOTALL
|
from re import match, DOTALL
|
||||||
|
from util import *
|
||||||
|
|
||||||
EMPTY_60=bytes(60)
|
EMPTY_60=bytes(60)
|
||||||
|
|
||||||
FACTORY_PASSPHRASE_PW1=b"123456"
|
FACTORY_PASSPHRASE_PW1=b"123456"
|
||||||
FACTORY_PASSPHRASE_PW3=b"12345678"
|
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):
|
def test_login(card):
|
||||||
login = get_data_object(card, 0x5e)
|
login = get_data_object(card, 0x5e)
|
||||||
assert check_null(login)
|
assert check_null(login)
|
||||||
|
|||||||
259
tests/test_personalize_card.py
Normal file
259
tests/test_personalize_card.py
Normal file
@@ -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
|
||||||
86
tests/test_personalize_reset_card.py
Normal file
86
tests/test_personalize_reset_card.py
Normal file
@@ -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
|
||||||
7
tests/util.py
Normal file
7
tests/util.py
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user