Added routines for import key.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
Pol Henarejos 2023-02-13 23:52:58 +01:00
parent af16be64a2
commit 8f6ae52c70
No known key found for this signature in database
GPG key ID: C0095B7870A4CCD3
2 changed files with 99 additions and 7 deletions

View file

@ -1,8 +1,28 @@
"""
/*
* This file is part of the Pico HSM distribution (https://github.com/polhenarejos/pico-hsm).
* Copyright (c) 2022 Pol Henarejos.
*
* This program 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, version 3.
*
* This program 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/>.
*/
"""
import sys
import pytest
import os
from binascii import hexlify
from utils import APDUResponse, DOPrefixes, KeyType, Algorithm, Padding
from utils import APDUResponse, DOPrefixes, KeyType, Algorithm, Padding, int_to_bytes
from const import *
import hashlib
try:
@ -24,14 +44,14 @@ except ModuleNotFoundError:
try:
from cryptography.hazmat.primitives.asymmetric import ec, rsa, utils, padding
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes, cmac
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
from cryptography.hazmat.primitives import hashes, serialization
except ModuleNotFoundError:
print('ERROR: cryptography module not found! Install cryptography package.\nTry with `pip install cryptography`')
sys.exit(-1)
class Device:
class EcDummy:
def __init__(self, name):
@ -104,7 +124,7 @@ class Device:
return e.sw2 & 0x0F
raise e
def initialize(self, pin='648219', sopin='57621880', options=None, retries=3, dkek_shares=None, puk_auts=None, puk_min_auts=None, key_domains=None):
def initialize(self, pin=DEFAULT_PIN, sopin=DEFAULT_SOPIN, options=None, retries=DEFAULT_RETRIES, dkek_shares=None, puk_auts=None, puk_min_auts=None, key_domains=None):
if (retries is not None and not 0 < retries <= 10):
raise ValueError('Retries must be in the range (0,10]')
if (dkek_shares is not None and not 0 <= dkek_shares <= 10):
@ -183,8 +203,8 @@ class Device:
def delete_file(self, fid):
self.send(command=0xE4, data=[fid >> 8, fid & 0xff])
def public_key(self, type, keyid, param=None):
response = self.send(command=0xB1, p1=0xCE, p2=keyid, data=[0x54, 0x02, 0x00, 0x00])
def public_key(self, keyid, param=None):
response = self.send(command=0xB1, p1=DOPrefixes.EE_CERTIFICATE_PREFIX.value, p2=keyid, data=[0x54, 0x02, 0x00, 0x00])
cert = bytearray(response)
roid = CVC().decode(cert).pubkey().oid()
@ -250,6 +270,75 @@ class Device:
resp = self.send(command=0x62, p1=keyid, p2=p2, data=list(data))
return bytes(resp)
def import_dkek(self, dkek):
resp = self.send(cla=0x80, command=0x52, p1=0x0, p2=0x0, data=dkek)
return resp
def import_key(self, pkey, dkek=None):
data = b''
kcv = hashlib.sha256(dkek or b'\x00'*32).digest()[:8]
kenc = hashlib.sha256((dkek or b'\x00'*32) + b'\x00\x00\x00\x01').digest()
kmac = hashlib.sha256((dkek or b'\x00'*32) + b'\x00\x00\x00\x02').digest()
data += kcv
pubnum = pkey.public_key().public_numbers()
if (isinstance(pkey, rsa.RSAPrivateKey)):
data += b'\x05'
algo = b'\x00\x0A\x04\x00\x7F\x00\x07\x02\x02\x02\x01\x02'
elif (isinstance(pkey, ec.EllipticCurvePrivateKey)):
data += b'\x0C'
algo = b'\x00\x0A\x04\x00\x7F\x00\x07\x02\x02\x02\x02\x03'
data += algo
data += b'\x00'*6
kb = os.urandom(8)
if (isinstance(pkey, rsa.RSAPrivateKey)):
kb += int_to_bytes(pkey.key_size, length=2)
pnum = pkey.private_numbers()
kb += int_to_bytes((pnum.d.bit_length()+7)//8, length=2)
kb += int_to_bytes(pnum.d)
kb += int_to_bytes((pubnum.n.bit_length()+7)//8, length=2)
kb += int_to_bytes(pubnum.n)
kb += int_to_bytes((pubnum.e.bit_length()+7)//8, length=2)
kb += int_to_bytes(pubnum.e)
elif (isinstance(pkey, ec.EllipticCurvePrivateKey)):
curve = ec_domain(pkey.curve)
kb += int_to_bytes(len(curve.P)*8, length=2)
kb += int_to_bytes(len(curve.A), length=2)
kb += curve.A
kb += int_to_bytes(len(curve.B), length=2)
kb += curve.B
kb += int_to_bytes(len(curve.P), length=2)
kb += curve.P
kb += int_to_bytes(len(curve.O), length=2)
kb += curve.O
kb += int_to_bytes(len(curve.G), length=2)
kb += curve.G
kb += int_to_bytes((pkey.private_numbers().private_value.bit_length()+7)//8, length=2)
kb += int_to_bytes(pkey.private_numbers().private_value)
p = pkey.public_key().public_bytes(Encoding.X962, PublicFormat.UncompressedPoint)
kb += int_to_bytes(len(p), length=2)
kb += p
kb_len_pad = (len(kb)//16)*16
if (len(kb) % 16 > 0):
kb_len_pad = (len(kb)//16 + 1)*16
if (len(kb) < kb_len_pad):
kb += b'\x80'
kb += b'\x00' * (kb_len_pad-len(kb))
cipher = Cipher(algorithms.AES(kenc), modes.CBC(b'\x00'*16))
encryptor = cipher.encryptor()
ct = encryptor.update(kb) + encryptor.finalize()
data += ct
c = cmac.CMAC(algorithms.AES(kmac))
c.update(data)
data += c.finalize()
p1 = self.get_first_free_id()
resp = self.send(cla=0x80, command=0x74, p1=p1, p2=0x93, data=data)
return p1
@pytest.fixture(scope="session")
def device():
dev = Device()

View file

@ -123,3 +123,6 @@ class Padding(Enum):
RAW = 0x21
PKCS = 0x22
OAEP = 0x23
def int_to_bytes(x, length=None, byteorder='big'):
return x.to_bytes(length or (x.bit_length() + 7) // 8, byteorder=byteorder)