more tests
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
31
test/features/100_compute_signature.feature
Normal file
31
test/features/100_compute_signature.feature
Normal 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
|
||||
16
test/features/101_decryption.feature
Normal file
16
test/features/101_decryption.feature
Normal 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
|
||||
|
||||
@@ -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)
|
||||
|
||||
36
test/gnuk.py
36
test/gnuk.py
@@ -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):
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user