more tests

This commit is contained in:
NIIBE Yutaka
2012-06-27 14:15:51 +09:00
parent 92d500d4b5
commit 839b0156a9
8 changed files with 210 additions and 5 deletions

View File

@@ -1,5 +1,8 @@
2012-06-27 Niibe Yutaka <gniibe@fsij.org>
* test/features/101_decryption.feature: New.
* test/features/100_compute_signature.feature: New.
* src/openpgp-do.c (gpg_do_chks_prvkey): Call flash_do_release before
flash_do_write.
(gpg_do_write_prvkey): Bug fix when GC occurs.

View File

@@ -1,7 +1,9 @@
This is functionality test suite for Gnuk.
You need python-nose, python-freshen, and python-crypto as well as
python-usb.
You need python-nose, python-freshen as well as python-usb.
Besides, python-crypto is needed when you use generate_keys.py to
update *.key.
Type:

View File

@@ -46,3 +46,11 @@ Feature: import keys to token
Given a timestamp of OPENPGP.3 key
And put the data to d0
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

View File

@@ -0,0 +1,31 @@
Feature: compute digital signature
In order to use a token
A token should compute digital signature properly
Scenario: compute digital signature by OPENPGP.1 key (1)
Given a message "This is a test message."
And let a token compute digital signature
And compute digital signature on host with RSA key pair 0
Then results should be same
Scenario: compute digital signature by OPENPGP.1 key (2)
Given a message "This is another test message.\nMultiple lines.\n"
And let a token compute digital signature
And compute digital signature on host with RSA key pair 0
Then results should be same
Scenario: compute digital signature by OPENPGP.3 key (1)
Given a message "This is a test message."
And let a token authenticate
And compute digital signature on host with RSA key pair 2
Then results should be same
Scenario: compute digital signature by OPENPGP.3 key (2)
Given a message "This is another test message.\nMultiple lines.\n"
And let a token authenticate
And compute digital signature on host with RSA key pair 2
Then results should be same
Scenario: data object ds counter
When requesting ds counter: 93
Then you should get: \x00\x00\x02

View File

@@ -0,0 +1,16 @@
Feature: decryption
In order to use a token
A token should decrypt encrypted data
Scenario: decrypt by OPENPGP.2 key (1)
Given a plain text "This is a test message."
And encrypt it on host with RSA key pair 1
And let a token decrypt encrypted data
Then decrypted data should be same as a plain text
Scenario: decrypt by OPENPGP.2 key (2)
Given a plain text "RSA decryption is as easy as pie."
And encrypt it on host with RSA key pair 1
And let a token decrypt encrypted data
Then decrypted data should be same as a plain text

View File

@@ -1,6 +1,7 @@
from freshen import *
from freshen.checks import *
from nose.tools import assert_regexp_matches
from binascii import hexlify
import ast
@@ -63,6 +64,38 @@ def cmd_put_data_with_result(tag_str):
tagl = tag & 0xff
scc.result = ftc.token.cmd_put_data(tagh, tagl, scc.result)
@Given("a message (\".*\")")
def set_msg(content_str_repr):
msg = ast.literal_eval(content_str_repr)
scc.digestinfo = rsa_keys.compute_digestinfo(msg)
@Given("let a token compute digital signature")
def compute_signature():
scc.sig = int(hexlify(ftc.token.cmd_pso(0x9e, 0x9a, scc.digestinfo)),16)
@Given("let a token authenticate")
def internal_authenticate():
scc.sig = int(hexlify(ftc.token.cmd_internal_authenticate(scc.digestinfo)),16)
@Given("compute digital signature on host with RSA key pair (.*)")
def compute_signature_on_host(keyno_str):
keyno = int(keyno_str)
scc.result = rsa_keys.compute_signature(keyno, scc.digestinfo)
@Given("a plain text (\".*\")")
def set_plaintext(content_str_repr):
scc.plaintext = ast.literal_eval(content_str_repr)
@Given("encrypt it on host with RSA key pair (.*)")
def encrypt_on_host(keyno_str):
keyno = int(keyno_str)
scc.ciphertext = rsa_keys.encrypt(keyno, scc.plaintext)
@Given("let a token decrypt encrypted data")
def decrypt():
scc.result = ftc.token.cmd_pso_longdata(0x80, 0x86, scc.ciphertext)
@When("requesting (.+): ([0-9a-fA-F]+)")
def get_data(name, tag_str):
tag = int(tag_str, 16)
@@ -92,3 +125,11 @@ def check_null():
@Then("data should match: (.*)")
def check_regexp(re):
assert_regexp_matches(scc.result, re)
@Then("results should be same")
def check_signature():
assert_equal(scc.sig, scc.result)
@Then("decrypted data should be same as a plain text")
def check_decrypt():
assert_equal(scc.plaintext, scc.result)

View File

@@ -284,6 +284,42 @@ class gnuk_token(object):
raise ValueError("%02x%02x" % (sw[0], sw[1]))
return True
def cmd_pso(self, p1, p2, data):
cmd_data = iso7816_compose(0x2a, p1, p2, data)
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_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.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)
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.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 compare(data_original, data_in_device):

View File

@@ -1,7 +1,9 @@
from binascii import unhexlify
from binascii import hexlify, unhexlify
from time import time
from struct import pack
from hashlib import sha1
from hashlib import sha1, sha256
import string
from os import urandom
def read_key_from_file(file):
f = open(file)
@@ -16,7 +18,7 @@ def read_key_from_file(file):
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))
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())
@@ -69,3 +71,69 @@ def build_privkey_template_for_remove(openpgp_keyno):
else:
keyspec = '\xa4'
return '\x4d\02' + keyspec + '\0x00'
def compute_digestinfo(msg):
digest = sha256(msg).digest()
prefix = '\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 = '\x00' + '\x01' + string.ljust('', 256 - 19 - 32 - 3, '\xff') \
+ '\x00' + digestinfo
return int(hexlify(byte_repr), 16)
def pkcs1_pad(msg):
padlen = 256 - 3 - len(msg)
byte_repr = '\x00' + '\x02' \
+ string.replace(urandom(padlen),'\x00','\x01') + '\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(i):
s = hex(i)[2:]
s = s.rstrip('L')
if len(s) & 1:
s = '0' + s
return unhexlify(s)
def encrypt(keyno, plaintext):
e = key[keyno][4]
n = key[keyno][7]
m = pkcs1_pad(plaintext)
return '\x00' + integer_to_bytes(pow(m, e, n))