gnuk/tests/curve25519_keys.py
NIIBE Yutaka 1ba3b76796 Merge tests from rsa-removal branch.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-11-02 16:15:35 +09:00

125 lines
4.1 KiB
Python

from time import time
from struct import pack
from hashlib import sha1, sha256
from pk_25519_with_libgcrypt import fixup_scalar_cv25519, call_pk_sign, call_pk_encrypt, call_pk_verify
from card_const import KEY_ATTRIBUTES_CV25519, KEY_ATTRIBUTES_ED25519
class PK_Crypto(object):
@staticmethod
def pk_from_pk_info(pk_info):
return b'\x40' + pk_info[5:]
@staticmethod
def compute_digestinfo(msg):
return sha256(msg).digest()
@staticmethod
def enc_data(enc_info):
return b'\xa6\x25\x7f\x49\x22\x86\x20' + enc_info[1]
@staticmethod
def enc_check(enc_info, s):
return enc_info[0] == s
def __init__(self, keyno=None, pk_info=None, data=None):
if keyno == None:
# Just for name space
return
self.keyno = keyno
self.for_encryption = (self.keyno == 1)
self.timestamp = pack('>I', int(time()))
if pk_info:
# Public part only (no private data) from card
self.q = pk_info[5:]
else:
if self.for_encryption:
# Private part (in big endian), while DATA is native (little) endian
d = data[0]
self.d = fixup_scalar_cv25519(d)
else:
# Private part (in little (native) endian)
self.d = data[0]
self.q = data[1]
self.fpr = self.calc_fpr()
def calc_fpr(self):
m_len = 6 + 2 + 33
ver = b'\x04'
algo = b'\x12' if self.for_encryption else b'\x16'
m = b'\x99' + pack('>H', m_len) + ver + self.timestamp + algo \
+ pack('>H', 256+7) + b'\x40' + self.q
return sha1(m).digest()
def build_privkey_template(self, is_yubikey):
openpgp_keyno = self.keyno + 1
if openpgp_keyno == 1:
keyspec = b'\xb6'
elif openpgp_keyno == 2:
keyspec = b'\xb8'
else:
keyspec = b'\xa4'
key_template = b'\x92\x20'
exthdr = keyspec + b'\x00' + b'\x7f\x48' + b'\x02' + key_template
suffix = b'\x5f\x48' + b'\x20'
return b'\x4d' + b'\x2a' + exthdr + suffix + self.d
def compute_signature(self, digestinfo):
return call_pk_sign(self.d, digestinfo)
def verify_signature(self, digestinfo, sig):
return call_pk_verify(self.q, digestinfo, sig)
def encrypt(self, plaintext):
# Do ECDH
return call_pk_encrypt(self.q, plaintext)
def get_fpr(self):
return self.fpr
def get_timestamp(self):
return self.timestamp
def get_pk(self):
return b'\x40' + self.q
key = [ None, None, None ]
ed25519_data = (b'\x83\x3f\xe6\x24\x09\x23\x7b\x9d\x62\xec\x77\x58\x75\x20\x91\x1e'
b'\x9a\x75\x9c\xec\x1d\x19\x75\x5b\x7d\xa9\x01\xb9\x6d\xca\x3d\x42',
b'\xec\x17\x2b\x93\xad\x5e\x56\x3b\xf4\x93\x2c\x70\xe1\x24\x50\x34'
b'\xc3\x54\x67\xef\x2e\xfd\x4d\x64\xeb\xf8\x19\x68\x34\x67\xe2\xbf')
cv25519_data = (b'\x77\x07\x6d\x0a\x73\x18\xa5\x7d\x3c\x16\xc1\x72\x51\xb2\x66\x45'
b'\xdf\x4c\x2f\x87\xeb\xc0\x99\x2a\xb1\x77\xfb\xa5\x1d\xb9\x2c\x2a',
b'\x85\x20\xf0\x09\x89\x30\xa7\x54\x74\x8b\x7d\xdc\xb4\x3e\xf7\x5a'
b'\x0d\xbf\x3a\x0d\x26\x38\x1a\xf4\xeb\xa4\xa9\x8e\xaa\x9b\x4e\x6a')
key[0] = PK_Crypto(0, data=ed25519_data)
key[1] = PK_Crypto(1, data=cv25519_data)
key[2] = PK_Crypto(2, data=ed25519_data)
PLAIN_TEXT0=b"This is a test message."
PLAIN_TEXT1=b"cryptography is as easy as pie."
ENCRYPT_TEXT0 = sha256(b"encrypt me please").digest()
ENCRYPT_TEXT1 = sha256(b"encrypt me please, another").digest()
ENCRYPT_TEXT2 = sha256(b"encrypt me please, the other").digest()
test_vector = {
'sign_0' : PLAIN_TEXT0,
'sign_1' : PLAIN_TEXT1,
'auth_0' : PLAIN_TEXT0,
'auth_1' : PLAIN_TEXT1,
'decrypt_0' : ENCRYPT_TEXT0,
'decrypt_1' : ENCRYPT_TEXT1,
'encrypt_0' : ENCRYPT_TEXT2,
}
curve25519_pk = PK_Crypto()
curve25519_pk.test_vector = test_vector
curve25519_pk.key_list = key
curve25519_pk.key_attr_list = [KEY_ATTRIBUTES_ED25519, KEY_ATTRIBUTES_CV25519, KEY_ATTRIBUTES_ED25519]
curve25519_pk.PK_Crypto = PK_Crypto