more tests
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
2012-06-27 Niibe Yutaka <gniibe@fsij.org>
|
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
|
* src/openpgp-do.c (gpg_do_chks_prvkey): Call flash_do_release before
|
||||||
flash_do_write.
|
flash_do_write.
|
||||||
(gpg_do_write_prvkey): Bug fix when GC occurs.
|
(gpg_do_write_prvkey): Bug fix when GC occurs.
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
This is functionality test suite for Gnuk.
|
This is functionality test suite for Gnuk.
|
||||||
|
|
||||||
You need python-nose, python-freshen, and python-crypto as well as
|
You need python-nose, python-freshen as well as python-usb.
|
||||||
python-usb.
|
|
||||||
|
Besides, python-crypto is needed when you use generate_keys.py to
|
||||||
|
update *.key.
|
||||||
|
|
||||||
|
|
||||||
Type:
|
Type:
|
||||||
|
|||||||
@@ -46,3 +46,11 @@ Feature: import keys to token
|
|||||||
Given a timestamp of OPENPGP.3 key
|
Given a timestamp of OPENPGP.3 key
|
||||||
And put the data to d0
|
And put the data to d0
|
||||||
Then it should get success
|
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 import *
|
||||||
from freshen.checks import *
|
from freshen.checks import *
|
||||||
from nose.tools import assert_regexp_matches
|
from nose.tools import assert_regexp_matches
|
||||||
|
from binascii import hexlify
|
||||||
|
|
||||||
import ast
|
import ast
|
||||||
|
|
||||||
@@ -63,6 +64,38 @@ def cmd_put_data_with_result(tag_str):
|
|||||||
tagl = tag & 0xff
|
tagl = tag & 0xff
|
||||||
scc.result = ftc.token.cmd_put_data(tagh, tagl, scc.result)
|
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]+)")
|
@When("requesting (.+): ([0-9a-fA-F]+)")
|
||||||
def get_data(name, tag_str):
|
def get_data(name, tag_str):
|
||||||
tag = int(tag_str, 16)
|
tag = int(tag_str, 16)
|
||||||
@@ -92,3 +125,11 @@ def check_null():
|
|||||||
@Then("data should match: (.*)")
|
@Then("data should match: (.*)")
|
||||||
def check_regexp(re):
|
def check_regexp(re):
|
||||||
assert_regexp_matches(scc.result, 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]))
|
raise ValueError("%02x%02x" % (sw[0], sw[1]))
|
||||||
return True
|
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):
|
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 time import time
|
||||||
from struct import pack
|
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):
|
def read_key_from_file(file):
|
||||||
f = open(file)
|
f = open(file)
|
||||||
@@ -16,7 +18,7 @@ def read_key_from_file(file):
|
|||||||
n = int(n_str, 16)
|
n = int(n_str, 16)
|
||||||
if n != p * q:
|
if n != p * q:
|
||||||
raise ValueError("wrong key", p, q, n)
|
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):
|
def calc_fpr(n,e):
|
||||||
timestamp = int(time())
|
timestamp = int(time())
|
||||||
@@ -69,3 +71,69 @@ def build_privkey_template_for_remove(openpgp_keyno):
|
|||||||
else:
|
else:
|
||||||
keyspec = '\xa4'
|
keyspec = '\xa4'
|
||||||
return '\x4d\02' + keyspec + '\0x00'
|
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