+2012-06-26 Niibe Yutaka <gniibe@fsij.org>
+
+ * test: New.
+
2012-06-25 Niibe Yutaka <gniibe@fsij.org>
* tool/usb_strings.py: New.
--- /dev/null
+This is functionality test suite for Gnuk.
+
+You need python-nose, python-freshen, and python-crypto as well as
+python-usb.
+
+
+Type:
+
+ $ nosetests --with-freshen .
+
+or
+
+ $ nosetests -v --with-freshen .
+
+to run the test suite.
--- /dev/null
+Feature: confirm empty token
+ In order to start tests
+ A token should be empty (no data, no keys)
+
+ Scenario: data object Login
+ When requesting login data: 5e
+ Then you should get NULL
+
+ Scenario: data object Name
+ When requesting name: 5b
+ Then you should get NULL
+
+ Scenario: data object Language preference
+ When requesting anguage preference: 5f2d
+ Then you should get NULL
+
+ Scenario: data object Sex
+ When requesting sex: 5f35
+ Then you should get NULL
+
+ Scenario: data object URL
+ When requesting URL: 5f50
+ Then you should get NULL
+
+ Scenario: data object ds counter
+ When requesting ds counter: 93
+ Then you should get: \x00\x00\x00
+
+ Scenario: data object pw1 status bytes
+ When requesting pw1 status bytes: c4
+ Then you should get: \x00\x7f\x7f\x7f\x03\x03\x03
+
+ Scenario: data object finger print 0
+ When requesting finger print: c5
+ Then you should get: \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
+
+ Scenario: data object finger print 1
+ When requesting finger print: c7
+ Then you should get NULL
+
+ Scenario: data object finger print 2
+ When requesting finger print: c8
+ Then you should get NULL
+
+ Scenario: data object finger print 3
+ When requesting finger print: c9
+ Then you should get NULL
+
+ Scenario: data object CA finger print 0
+ When requesting finger print: c6
+ Then you should get: \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
+
+ Scenario: data object CA finger print 1
+ When requesting finger print: ca
+ Then you should get NULL
+
+ Scenario: data object CA finger print 2
+ When requesting finger print: cb
+ Then you should get NULL
+
+ Scenario: data object CA finger print 3
+ When requesting finger print: cc
+ Then you should get NULL
+
+ Scenario: data object date/time of key pair 0
+ When requesting date/time of key pair: cd
+ Then you should get: \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
+
+ Scenario: data object date/time of key pair 1
+ When requesting date/time of key pair: ce
+ Then you should get NULL
+
+ Scenario: data object date/time of key pair 2
+ When requesting date/time of key pair: cf
+ Then you should get NULL
+
+ Scenario: data object date/time of key pair 3
+ When requesting date/time of key pair: d0
+ Then you should get NULL
--- /dev/null
+Feature: confirm empty token
+ In order to start tests
+ A token should be empty (no pass phrase)
+
+ Scenario: verify PW1 factory setting (1)
+ Given cmd_verify with 1 and "123456"
+ Then it should get success
+
+ Scenario: verify PW1 factory setting (2)
+ Given cmd_verify with 2 and "123456"
+ Then it should get success
+
+ Scenario: verify PW3 factory setting
+ Given cmd_verify with 3 and "12345678"
+ Then it should get success
--- /dev/null
+Feature: command GET DATA
+ In order to conform OpenPGP card 2.0 specification
+ A token should support all mandatory features of the specification
+
+ Scenario: data object historical bytes
+ When requesting historical bytes: 5f52
+ Then you should get: \x00\x31\x84\x73\x80\x01\x80\x00\x90\x00
+
+ Scenario: data object extended capabilities
+ When requesting extended capabilities: c0
+ Then you should get: \x30\x00\x00\x00\x00\x00\x00\xff\x01\x00
+
+ Scenario: data object algorithm attributes 1
+ When requesting algorithm attributes 1: c1
+ Then you should get: \x01\x08\x00\x00\x20\x00
+
+ Scenario: data object algorithm attributes 2
+ When requesting algorithm attributes 2: c2
+ Then you should get: \x01\x08\x00\x00\x20\x00
+
+ Scenario: data object algorithm attributes 3
+ When requesting algorighm attributes 3: c3
+ Then you should get: \x01\x08\x00\x00\x20\x00
+
+ Scenario: data object AID
+ When requesting AID: 4f
+ Then data should match: \xd2\x76\x00\x01\x24\x01\x02\x00......\x00\x00
--- /dev/null
+Feature: setup pass phrase
+ In order to conform OpenPGP card 2.0 specification
+ A token should support pass phrase: PW1, PW3 and reset code
+
+ Scenario: setup PW1 (admin-less mode)
+ Given cmd_change_reference_data with 1 and "123456user pass phrase"
+ Then it should get success
+
+ Scenario: verify PW1 (1)
+ Given cmd_verify with 1 and "user pass phrase"
+ Then it should get success
+
+ Scenario: verify PW1 (2)
+ Given cmd_verify with 2 and "user pass phrase"
+ Then it should get success
+
+ Scenario: verify PW3 (admin-less mode)
+ Given cmd_verify with 3 and "user pass phrase"
+ Then it should get success
+
+ Scenario: setup reset code (in admin-less mode)
+ Given cmd_put_data with d3 and "example reset code 000"
+ Then it should get success
+
+ Scenario: reset pass phrase by reset code (in admin-less mode)
+ Given cmd_reset_retry_counter with 0 and "example reset code 000new user pass phrase"
+ Then it should get success
+
+ Scenario: verify PW1 (1) again
+ Given cmd_verify with 1 and "new user pass phrase"
+ Then it should get success
+
+ Scenario: verify PW1 (2) again
+ Given cmd_verify with 2 and "new user pass phrase"
+ Then it should get success
+
+ Scenario: verify PW3 (admin-less mode) again
+ Given cmd_verify with 3 and "new user pass phrase"
+ Then it should get success
+
+ Scenario: setup PW3 (admin-full mode)
+ Given cmd_change_reference_data with 3 and "new user pass phraseadmin pass phrase"
+ Then it should get success
+
+ Scenario: verify PW3 (admin-full mode)
+ Given cmd_verify with 3 and "admin pass phrase"
+ Then it should get success
+
+ Scenario: setup reset code (in admin-full mode)
+ Given cmd_put_data with d3 and "another reset code 000"
+ Then it should get success
+
+ Scenario: reset pass phrase by reset code (in admin-full mode)
+ Given cmd_reset_retry_counter with 0 and "another reset code 000another user pass phrase"
+ Then it should get success
+
+ Scenario: verify PW1 (1) again
+ Given cmd_verify with 1 and "another user pass phrase"
+ Then it should get success
+
+ Scenario: verify PW1 (2) again
+ Given cmd_verify with 2 and "another user pass phrase"
+ Then it should get success
--- /dev/null
+Feature: personalize token write
+ In order to use a token
+ A token should be personalized with name, sex, url, etc.
+
+ Scenario: data object Login
+ Given cmd_put_data with 5e and "gpg_user"
+ Then it should get success
+
+ Scenario: data object Name
+ Given cmd_put_data with 5b and "GnuPG User"
+ Then it should get success
+
+ Scenario: data object Language preference
+ Given cmd_put_data with 5f2d and "ja"
+ Then it should get success
+
+ Scenario: data object Sex
+ Given cmd_put_data with 5f35 and "1"
+ Then it should get success
+
+ Scenario: data object URL
+ Given cmd_put_data with 5f50 and "http://www.fsij.org/gnuk/"
+ Then it should get success
+
+ Scenario: data object pw1 status bytes
+ Given cmd_put_data with c4 and "\x01"
+ Then it should get success
--- /dev/null
+Feature: personalize token read
+ In order to use a token
+ A token should be personalized with name, sex, url, etc.
+
+ Scenario: data object Login
+ When requesting login data: 5e
+ Then you should get: gpg_user
+
+ Scenario: data object Name
+ When requesting name: 5b
+ Then you should get: GnuPG User
+
+ Scenario: data object Language preference
+ When requesting anguage preference: 5f2d
+ Then you should get: ja
+
+ Scenario: data object Sex
+ When requesting sex: 5f35
+ Then you should get: 1
+
+ Scenario: data object URL
+ When requesting URL: 5f50
+ Then you should get: http://www.fsij.org/gnuk/
+
+ Scenario: data object pw1 status bytes
+ When requesting pw1 status bytes: c4
+ Then you should get: \x01\x7f\x7f\x7f\x03\x03\x03
--- /dev/null
+Feature: import keys to token
+ In order to use a token
+ A token should have keys
+
+ Scenario: importing OPENPGP.1 key (sign)
+ Given a RSA key pair 0
+ And importing it to the token as OPENPGP.1
+ Then it should get success
+
+ Scenario: importing OPENPGP.2 key (decrypt)
+ Given a RSA key pair 1
+ And importing it to the token as OPENPGP.2
+ Then it should get success
+
+ Scenario: importing OPENPGP.3 key (authentication)
+ Given a RSA key pair 2
+ And importing it to the token as OPENPGP.3
+ Then it should get success
--- /dev/null
+Feature: key removal
+ In order to use a token
+ A token should have keys
+
+ Scenario: remove OPENPGP.1 key (sign)
+ When removing a key OPENPGP.1
+ Then it should get success
+
+ Scenario: remove OPENPGP.2 key (decrypt)
+ When removing a key OPENPGP.2
+ Then it should get success
+
+ Scenario: remove OPENPGP.3 key (authentication)
+ When removing a key OPENPGP.3
+ Then it should get success
+
+ Scenario: remove data object Finger print sig
+ Given cmd_put_data with c7 and ""
+ Then it should get success
+
+ Scenario: remove data object Finger print dec
+ Given cmd_put_data with c8 and ""
+ Then it should get success
+
+ Scenario: remove data object Finger print aut
+ Given cmd_put_data with c9 and ""
+ Then it should get success
+
+ Scenario: remove data object keygeneration data/time sig
+ Given cmd_put_data with ce and ""
+ Then it should get success
+
+ Scenario: remove data object keygeneration data/time dec
+ Given cmd_put_data with cf and ""
+ Then it should get success
+
+ Scenario: remove data object keygeneration data/time aut
+ Given cmd_put_data with d0 and ""
+ Then it should get success
--- /dev/null
+Feature: removal of data objects
+ In order to use a token
+ A token should have personalized data
+
+ Scenario: remove data object Login
+ Given cmd_put_data with 5e and ""
+ Then it should get success
+
+ Scenario: remove data object Name
+ Given cmd_put_data with 5b and ""
+ Then it should get success
+
+ Scenario: remove data object Language preference
+ Given cmd_put_data with 5f2d and ""
+ Then it should get success
+
+ Scenario: remove data object Sex
+ Given cmd_put_data with 5f35 and ""
+ Then it should get success
+
+ Scenario: remove data object URL
+ Given cmd_put_data with 5f50 and ""
+ Then it should get success
+
+ Scenario: remove data object pw1 status bytes
+ Given cmd_put_data with c4 and "\x00"
+ Then it should get success
--- /dev/null
+Feature: reset pass phrase
+ In order to conform OpenPGP card 2.0 specification
+ A token should support pass phrase: PW1, PW3 and reset code
+
+ Scenario: setup PW3 (admin-full mode)
+ Given cmd_change_reference_data with 3 and "admin pass phrase"
+ Then it should get success
--- /dev/null
+from freshen import *
+from freshen.checks import *
+from nose.tools import assert_regexp_matches
+
+import ast
+
+import gnuk
+import rsa_keys
+
+@Before
+def ini(sc):
+ if not ftc.token:
+ ftc.token = gnuk.get_gnuk_device()
+ ftc.token.cmd_select_openpgp()
+
+@Given("cmd_verify with (.*) and \"(.*)\"")
+def cmd_verify(who_str,pass_str):
+ who = int(who_str)
+ scc.result = ftc.token.cmd_verify(who, pass_str)
+
+@Given("cmd_change_reference_data with (.*) and \"(.*)\"")
+def cmd_change_reference_data(who_str,pass_str):
+ who = int(who_str)
+ scc.result = ftc.token.cmd_change_reference_data(who, pass_str)
+
+@Given("cmd_put_data with (.*) and \"(.*)\"")
+def cmd_put_data(tag_str,content_str):
+ tag = int(tag_str, 16)
+ tagh = tag >> 8
+ tagl = tag & 0xff
+ scc.result = ftc.token.cmd_put_data(tagh, tagl, content_str)
+
+@Given("cmd_reset_retry_counter with (.*) and \"(.*)\"")
+def cmd_reset_retry_counter(how_str, data):
+ how = int(how_str)
+ scc.result = ftc.token.cmd_reset_retry_counter(how, data)
+
+@Given("a RSA key pair (.*)")
+def set_rsa_key(keyno_str):
+ scc.keyno = int(keyno_str)
+
+@Given("importing it to the token as OPENPGP.(.*)")
+def import_key(openpgp_keyno_str):
+ openpgp_keyno = int(openpgp_keyno_str)
+ t = rsa_keys.build_privkey_template(openpgp_keyno, scc.keyno)
+ scc.result = ftc.token.cmd_put_data_odd(0x3f, 0xff, t)
+
+@When("requesting (.+): ([0-9a-fA-F]+)")
+def get_data(name, tag_str):
+ tag = int(tag_str, 16)
+ tagh = tag >> 8
+ tagl = tag & 0xff
+ scc.result = ftc.token.cmd_get_data(tagh, tagl)
+
+@When("removing a key OPENPGP.(.*)")
+def remove_key(openpgp_keyno_str):
+ openpgp_keyno = int(openpgp_keyno_str)
+ t = rsa_keys.build_privkey_template_for_remove(openpgp_keyno)
+ scc.result = ftc.token.cmd_put_data_odd(0x3f, 0xff, t)
+
+@Then("you should get: (.*)")
+def check_result(v):
+ value = ast.literal_eval("'" + v + "'")
+ assert_equal(scc.result, value)
+
+@Then("it should get success")
+def check_success():
+ assert_equal(scc.result, True)
+
+@Then("you should get NULL")
+def check_null():
+ assert_equal(scc.result, "")
+
+@Then("data should match: (.*)")
+def check_regexp(re):
+ assert_regexp_matches(scc.result, re)
--- /dev/null
+from Crypto import Random
+from Crypto.PublicKey import RSA
+from binascii import hexlify
+
+def print_key_in_hex(k):
+ prv = k.exportKey(format='DER', pkcs=8)
+ n = prv[38:38+256]
+ e = prv[38+256+2:38+256+2+3]
+ p = prv[38+256+2+3+4+257+4:38+256+2+3+4+257+4+128]
+ q = prv[38+256+2+3+4+257+4+128+4:38+256+2+3+4+257+4+128+4+128]
+ n_str = hexlify(n)
+ e_str = hexlify(e)
+ p_str = hexlify(p)
+ q_str = hexlify(q)
+ if int(p_str, 16)*int(q_str, 16) != int(n_str, 16):
+ raise ValueError("wrong key", k)
+ print n_str
+ print e_str
+ print p_str
+ print q_str
+
+rng = Random.new().read
+key = RSA.generate(2048, rng)
+
+print_key_in_hex(key)
--- /dev/null
+"""
+gnuk.py - a library for Gnuk Token
+This tool is for importing certificate, writing serial number, etc.
+
+Copyright (C) 2011, 2012 Free Software Initiative of Japan
+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 *
+import string
+
+# Assume only single CCID device is attached to computer, and it's Gnuk Token
+
+import usb
+
+# USB class, subclass, protocol
+CCID_CLASS = 0x0B
+CCID_SUBCLASS = 0x00
+CCID_PROTOCOL_0 = 0x00
+
+def icc_compose(msg_type, data_len, slot, seq, param, data):
+ return pack('<BiBBBH', msg_type, data_len, slot, seq, 0, param) + data
+
+def iso7816_compose(ins, p1, p2, data, cls=0x00):
+ data_len = len(data)
+ if data_len == 0:
+ return pack('>BBBB', cls, ins, p1, p2)
+ else:
+ return pack('>BBBBB', cls, ins, p1, p2, data_len) + data
+
+def list_to_string(l):
+ return string.join([chr(c) for c in l], '')
+
+class gnuk_token(object):
+ def __init__(self, device, configuration, interface):
+ """
+ __init__(device, configuration, interface) -> None
+ Initialize the device.
+ device: usb.Device object.
+ configuration: configuration number.
+ interface: usb.Interface object representing the interface and altenate setting.
+ """
+ if interface.interfaceClass != CCID_CLASS:
+ raise ValueError("Wrong interface class")
+ if interface.interfaceSubClass != CCID_SUBCLASS:
+ raise ValueError("Wrong interface sub class")
+ self.__devhandle = device.open()
+ try:
+ self.__devhandle.setConfiguration(configuration)
+ except:
+ pass
+ self.__devhandle.claimInterface(interface)
+ self.__devhandle.setAltInterface(interface)
+
+ self.__intf = interface.interfaceNumber
+ self.__alt = interface.alternateSetting
+ self.__conf = configuration
+
+ self.__bulkout = 1
+ self.__bulkin = 0x81
+
+ self.__timeout = 10000
+ self.__seq = 0
+
+ def reset_device(self):
+ try:
+ self.__devhandle.reset()
+ except:
+ pass
+
+ def release_gnuk(self):
+ self.__devhandle.releaseInterface()
+
+ def icc_get_result(self):
+ msg = self.__devhandle.bulkRead(self.__bulkin, 1024, self.__timeout)
+ if len(msg) < 10:
+ print msg
+ raise ValueError("icc_get_result")
+ msg_type = msg[0]
+ data_len = msg[1] + (msg[2]<<8) + (msg[3]<<16) + (msg[4]<<24)
+ slot = msg[5]
+ seq = msg[6]
+ status = msg[7]
+ error = msg[8]
+ chain = msg[9]
+ data = msg[10:]
+ # XXX: check msg_type, data_len, slot, seq, error
+ return (status, chain, data)
+
+ def icc_get_status(self):
+ msg = icc_compose(0x65, 0, 0, self.__seq, 0, "")
+ self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
+ self.__seq += 1
+ status, chain, data = self.icc_get_result()
+ # XXX: check chain, data
+ return status
+
+ def icc_power_on(self):
+ msg = icc_compose(0x62, 0, 0, self.__seq, 0, "")
+ self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
+ self.__seq += 1
+ status, chain, data = self.icc_get_result()
+ # XXX: check status, chain
+ self.atr = list_to_string(data) # ATR
+
+ def icc_power_off(self):
+ msg = icc_compose(0x63, 0, 0, self.__seq, 0, "")
+ self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
+ self.__seq += 1
+ status, chain, data = self.icc_get_result()
+ # XXX: check chain, data
+ return status
+
+ def icc_send_data_block(self, data):
+ msg = icc_compose(0x6f, len(data), 0, self.__seq, 0, data)
+ self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
+ self.__seq += 1
+ return self.icc_get_result()
+
+ def icc_send_cmd(self, data):
+ status, chain, data_rcv = self.icc_send_data_block(data)
+ if chain == 0:
+ return data_rcv
+ elif chain == 1:
+ d = data_rcv
+ while True:
+ msg = icc_compose(0x6f, 0, 0, self.__seq, 0x10, "")
+ self.__devhandle.bulkWrite(self.__bulkout, msg, self.__timeout)
+ self.__seq += 1
+ status, chain, data_rcv = self.icc_get_result()
+ # XXX: check status
+ d += data_rcv
+ if chain == 2:
+ break
+ elif chain == 3:
+ continue
+ else:
+ raise ValueError("icc_send_cmd chain")
+ return d
+ else:
+ raise ValueError("icc_send_cmd")
+
+ def cmd_get_response(self, expected_len):
+ result = []
+ while True:
+ cmd_data = iso7816_compose(0xc0, 0x00, 0x00, '') + pack('>B', expected_len)
+ response = self.icc_send_cmd(cmd_data)
+ result += response[:-2]
+ sw = response[-2:]
+ if sw[0] == 0x90 and sw[1] == 0x00:
+ return list_to_string(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.icc_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, '')
+ sw = self.icc_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.icc_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.icc_send_cmd(cmd_data1)
+ if len(sw) != 2:
+ raise ValueError("cmd_write_binary", sw)
+ if not (sw[0] == 0x90 and sw[1] == 0x00):
+ raise ValueError("cmd_write_binary", "%02x%02x" % (sw[0], sw[1]))
+ count += 1
+
+ def cmd_select_openpgp(self):
+ cmd_data = iso7816_compose(0xa4, 0x04, 0x0c, "\xD2\x76\x00\x01\x24\x01")
+ sw = self.icc_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_get_data(self, tagh, tagl):
+ cmd_data = iso7816_compose(0xca, tagh, tagl, "")
+ sw = self.icc_send_cmd(cmd_data)
+ if len(sw) != 2:
+ raise ValueError, sw
+ if sw[0] == 0x90 and sw[1] == 0x00:
+ return ""
+ elif sw[0] != 0x61:
+ raise ValueError("%02x%02x" % (sw[0], sw[1]))
+ return self.cmd_get_response(sw[1])
+
+ def cmd_change_reference_data(self, who, data):
+ cmd_data = iso7816_compose(0x24, 0x00, 0x80+who, data)
+ sw = self.icc_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.icc_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):
+ cmd_data0 = iso7816_compose(0xdb, tagh, tagl, content[:128], 0x10)
+ cmd_data1 = iso7816_compose(0xdb, tagh, tagl, content[128:])
+ sw = self.icc_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.icc_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)
+ sw = self.icc_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 compare(data_original, data_in_device):
+ i = 0
+ for d in data_original:
+ if ord(d) != data_in_device[i]:
+ raise ValueError, "verify failed at %08x" % i
+ i += 1
+
+def gnuk_devices():
+ busses = usb.busses()
+ for bus in busses:
+ devices = bus.devices
+ for dev in devices:
+ for config in dev.configurations:
+ for intf in config.interfaces:
+ for alt in intf:
+ if alt.interfaceClass == CCID_CLASS and \
+ alt.interfaceSubClass == CCID_SUBCLASS and \
+ alt.interfaceProtocol == CCID_PROTOCOL_0:
+ yield dev, config, alt
+
+def get_gnuk_device():
+ icc = None
+ for (dev, config, intf) in gnuk_devices():
+ try:
+ icc = gnuk_token(dev, config, intf)
+ print "Device: ", dev.filename
+ print "Configuration: ", config.value
+ print "Interface: ", intf.interfaceNumber
+ break
+ except:
+ pass
+ if not icc:
+ raise ValueError("No ICC present")
+ status = icc.icc_get_status()
+ if status == 0:
+ pass # It's ON already
+ elif status == 1:
+ icc.icc_power_on()
+ else:
+ raise ValueError("Unknown ICC status", status)
+ return icc
--- /dev/null
+9cf7192b51a574d1ad3ccb08ba09b87f228573893eee355529ff243e90fd4b86f79a82097cc7922c0485bed1616b1656a9b0b19ef78ea8ec34c384019adc5d5bf4db2d2a0a2d9cf14277bdcb7056f48b81214e3f7f7742231e29673966f9b1106862112cc798dba8d4a138bb5abfc6d4c12d53a5d39b2f783da916da20852ee139bbafda61d429caf2a4f30847ce7e7ae32ab4061e27dd9e4d00d60910249db8d8559dd85f7ca59659ef400c8f6318700f4e97f0c6f4165de80641490433c88da8682befe68eb311f54af2b07d97ac74edb5399cf054764211694fbb8d1d333f3269f235abe025067f811ff83a2224826219b309ea3e6c968f42b3e52f245dc9
+010001
+b5ab7b159220b18e363258f61ebde08bae83d6ce2dbfe4adc143628c527887acde9de09bf9b49f438019004d71855f30c2d69b6c29bb9882ab641b3387409fe9199464a7faa4b5230c56d9e17cd9ed074bc00180ebed62bae3af28e6ff2ac2654ad968834c5d5c88f8d9d3cc5e167b10453b049d4e454a5761fb0ac717185907
+dd2fffa9814296156a6926cd17b65564187e424dcadce9b032246ad7e46448bb0f9e0ff3c64f987424b1a40bc694e2e9ac4fb1930d163582d7acf20653a1c44b97846c1c5fd8a7b19bb225fb39c30e25410483deaf8c2538d222b748c4d8103b11cec04f666a5c0dbcbf5d5f625f158f65746c3fafe6418145f7cffa5fadeeaf
--- /dev/null
+d392714c29738aac6372f2c8654a08c25a1299fed7004bd512cd2452b503ebad6301130816ac525ba528dc155be6347a5c70407fb4fbdaed751dfc0a7cd5e3910272ff236c4ed1ce5de6620b191a172e5b247347b8cab73a43d79221708755c959a2f83f486439da30917384554331532aabc8326db48866f8c91198834a86ab94679f6175db737bdf399e3f0b737dcb1f4208279d3e1cc694e78686785e4f363a377dec912b7c2f757b1422d866fb9fa85c96b83adfd6a223989a9a02988bdee81ad17eff6385e7b38cec8611fdf367ba4ac8e90d5f48ac7715c5f47aea06a4a37cdaa3029ce59d29bc66853bf6758ef4a7da5a5953f5e557a5a22f67c368c3
+010001
+dae085952c5beee38f25f09bc37a4ca2434c31f78055469d0d5f0bf3337e3a70ba6c91734f195b742e211a5fe283befdf66820008e6ef2c8ca54a91922838fce07d9e33a331ce20dac36803e777d5ee2195ed28d6a4045e28623a6a60b0661e45f7c4f84ae2b1dfad0cf1ec30605158323382a819e730c09a33fad704dd67501
+f774be43ea198aa2f089274e4fffd7d0092ee7b35a1d2f854cdb166f698caab72fdeb099e690e78438b2e043e452d4d2f19d7f44ba6b286642f0ce5204966ff98ecd9e3b448877324631365dc860797429b9414a21a7e166d504cace156588b9a145657eeb1afb43b8ff65d8d6d93cea2ba4ef8aab047885c4de64ffef0b49c3
--- /dev/null
+c6c877dfd3b441f8fb1b8dc504093a51c2efe4883fe0a6379205acc6e673709905e4d767ddf46143c535cc6d7f10b616f520d8346320ef69ff4a2c4f4a148edc65f7ad24ed7d4fe23bb862a0ae71f4f7904abac0397abf3213df91326b1a25554b3b18cf54584d8bf220169fc92b2aa511e8313983e72b4c9110b3a1aea087aebef95873865608e8faea9ef10e7f7f3a66ca8def2d499c3149c127491e0e4339fd6abe10bfc6c13e43d522004f1485767328eabe35d6ffa8df4c15f0fbcd4eb1c07cc6d85e275139ac69e2962273ae987236926dd6c1144fce3e7ae567fa58ea60620dfafc52f95299fea601739fce27ee71eea978d0074f21e7086f60ba8331
+010001
+cc365b5702714bf203e8c49b0b8afa8dad586e929cf5edca38ad07fa45efd5c2d89022d29f40283a57e50ca24c5f28c8e911a74faaf796f112e7e48195956f9a4df7668a5342523b27179cec958f363211ee11d0ec0e0e1b92ca007a61e8c9ac14e00229b9a7624850199e6667afa1a44db8f3c5de0a8eef0e6de050ac0ac633
+f931a3c12f0e3a5276f712b7706590ba02e14a97ff9b8ce3152af0fc4d9cdc690ea9bc4c82cb16c7d23136cbdab58fbec69880a88bca85c4214df01045082cbe9f4192e3e39c79896533c37dad9eb9e73c2643b9c0a704a4f93d81573537963d6b6e5140a24c702d9f26e06a2095de906daa8824172a6b39f563b7153907050b
--- /dev/null
+from binascii import unhexlify
+
+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))
+
+key = [ 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')
+
+def build_privkey_template(openpgp_keyno, keyno):
+ n_str = key[keyno][0]
+ e_str = '\x00' + key[keyno][1]
+ p_str = key[keyno][2]
+ q_str = key[keyno][3]
+
+ if openpgp_keyno == 1:
+ keyspec = '\xb6'
+ elif openpgp_keyno == 2:
+ keyspec = '\xb8'
+ else:
+ keyspec = '\xa4'
+
+ key_template = '\x91\x04'+ '\x92\x81\x80' + '\x93\x81\x80'
+
+ exthdr = keyspec + '\x00' + '\x7f\x48' + '\x08' + key_template
+
+ suffix = '\x5f\x48' + '\x82\x01\x04'
+
+ t = '\x4d' + '\x82\01\16' + exthdr + suffix + e_str + p_str + q_str
+ return t
+
+def build_privkey_template_for_remove(openpgp_keyno):
+ if openpgp_keyno == 1:
+ keyspec = '\xb6'
+ elif openpgp_keyno == 2:
+ keyspec = '\xb8'
+ else:
+ keyspec = '\xa4'
+ return '\x4d\02' + keyspec + '\0x00'