def random(n, trng=True): """ This function generates a random number :param n: how much randomness to generate. Valid values are integers from 8 to 256 :param trng: If True the a True Random Generator will be used, otherwise Deterministic Random Number Generator :raises: - TypeError - when any of the parameters are of the wrong type - OSError - when an error is returned by the chip initialisation library :returns: Bytes object with randomness """ ot = optiga.Chip() api = ot.api api.exp_optiga_crypt_random.argtypes = c_byte, POINTER(c_ubyte), c_ushort api.exp_optiga_crypt_random.restype = c_int p = (c_ubyte * n)() if trng is True: ret = api.exp_optiga_crypt_random(ot.rng.TRNG.value, p, len(p)) else: ret = api.exp_optiga_crypt_random(ot.rng.DRNG.value, p, len(p)) if ret == 0: return bytes(p) else: return bytes(0)
def test_chip_control_set_wrong_current_limit(): optiga = ot.Chip() with pytest.raises(ValueError): optiga.current_limit = 0 with pytest.raises(ValueError): optiga.current_limit = 20
def pkcs1v15_sign(key_object, data, hash_algorithm='sha256'): """ This function signs given data based on the provided RsaKey object :param key_object: Key Object on the OPTIGA Chip, which should be used as a source of the private key storage. Should be of type :class:`~optigatrust.objects.RSAKey` :param data: Data to sign :param hash_algorithm: Hash algorithm which should be used to sign data. SHA256 by default :raises: - TypeError - when any of the parameters are of the wrong type - OSError - when an error is returned by the core initialisation library :returns: :class:`~optigatrust.objects.PKCS1v15Signature` object or None """ api = optiga.Chip().api if not isinstance(data, bytes) and not isinstance(data, bytearray): if isinstance(data, str): _d = bytes(data.encode()) warnings.warn("data will be converted to bytes type before signing") else: raise TypeError('Data to sign should be either bytes or str type, you gave {0}'.format(type(data))) else: _d = data api.exp_optiga_crypt_rsa_sign.restype = c_int if hash_algorithm == 'sha256': digest = (c_ubyte * 32)(*hashlib.sha256(_d).digest()) s = (c_ubyte * 320)() # Signature schemes RSA SSA PKCS1-v1.5 with SHA256 digest sign_scheme = 0x01 elif hash_algorithm == 'sha384': digest = (c_ubyte * 48)(*hashlib.sha384(_d).digest()) s = (c_ubyte * 320)() # Signature schemes RSA SSA PKCS1-v1.5 with SHA384 digest sign_scheme = 0x02 else: raise ValueError('This key isze is not supported, you typed {0} supported are [\'sha256\', \'sha384\']' .format(hash_algorithm)) c_slen = c_uint(len(s)) ret = api.exp_optiga_crypt_rsa_sign(sign_scheme, digest, len(digest), key_object.id, s, byref(c_slen), 0) if ret == 0: signature = (c_ubyte * c_slen.value)() memmove(addressof(signature), s, c_slen.value) return PKCS1v15Signature(hash_algorithm, key_object.id, bytes(signature)) else: raise IOError('Function can\'t be executed. Error {0}'.format(hex(ret)))
def test_chip_control_set_current_limit(): optiga = ot.Chip() optiga.current_limit = 6 optiga.current_limit = 15 print(optiga.security_event_counter) print(optiga.uid) print(optiga.name) print(optiga.global_lifecycle_state) print(optiga.sleep_activation_delay) print(optiga.security_monitor) print(optiga.security_status)
def _to_xml(meta): """ This function will sequentially read all metadata from all available OIDs and return an xml compliant string :param: meta json formatted metadata :raises: - ValueError - when any of the parameters contain an invalid value - TypeError - when any of the parameters are of the wrong type - OSError - when an error is returned by the chip initialisation library :returns: an xml string """ opt = optiga.Chip() path = os.path.dirname(os.path.abspath(__file__)) template_env = Environment(autoescape=False, loader=FileSystemLoader( os.path.join(path, 'enums')), trim_blocks=False) fname = "conf_template.xml" new_meta_list = list() for key, value in meta.items(): # some OIDs are not supported by the OTC if key in ["f1c1", "e0c2", "e0c0", "e0c1", "e0c5", "e0c6"]: continue entry = {'id': key.upper(), 'meta': value['metadata'][4:].upper()} if 'data' in value: if 'used_size' in value['pretty_metadata']: entry['data'] = value['data'] new_meta_list.append(entry) context = {'name': opt.name, 'param': new_meta_list} output = template_env.get_template(fname).render(context) return output
def to_json(): """ This function will secentially read all metadata from all available OIDs and return a dictionary with all entrie :raises: - ValueError - when any of the parameters contain an invalid value - TypeError - when any of the parameters are of the wrong type - OSError - when an error is returned by the chip initialisation library :returns: a dictionary will all available metadata per object; e.g. :: { "e0f1": { "metadata":'200fc00101d001ffd301ffe00103e10121', "pretty_metadata": { "lcso": "creation", "change": "never", "execute": "never", "algorithm": "secp256r1", "key_usage": ['authentication', 'key_agreement'] } }, "e0c2": { "metadata":"2009c4011bd001ffd10100", "pretty_metadata": { "max_size": 27, "change": "never", "read": "always" } "data":"cd16338201001c000500000a091b5c0007006200ad801010710809" } } """ opt = optiga.Chip() output = dict() # Read metadata from available keys for oid in opt.key_id_values: #print('Reading: {0}'.format(hex(oid))) key = optiga.Object(oid) raw_meta = key.read_raw_meta().hex() if len(raw_meta) == 0: continue output[hex(oid)[2:]] = { "metadata": raw_meta, "pretty_metadata": key.meta } del key for oid in opt.object_id_values: #print('Reading: {0}'.format(hex(oid))) key = optiga.Object(oid) raw_meta = key.read_raw_meta().hex() try: data = key.read().hex() except IOError: print('Data in {0} is not readable - skip.'.format(hex(oid))) data = "" if len(raw_meta) == 0: continue output[hex(oid)[2:]] = { "metadata": raw_meta, "pretty_metadata": key.meta, "data": data } del key return output
def hkdf(key_object, key_length, salt=None, info=None, hash_algorithm='sha256', export=False): """ This function derives a key (HKDF) using the secret stored on OPTIGA .. note:: Only OPTIGA™ Trust M3 relevant :param key_object: Key Object on the OPTIGA Chip, which should be used as a source of the private key storage. Can be one of the following classes :class:`~optigatrust.objects.AppData`, :class:`~optigatrust.objects.Session`, or :class:`~optigatrust.objects.AcquiredSession` :param key_length: Size of the requested key. Minimum Length = 16 byte; maximum length = 66 bytes (in case of OPTIGA™ Trust M V1, = 48 bytes) in case of session reference; maximum length = 256 byte in case of returned secret :param salt: Optional salt, should be bytestring :param info: Optional info, should be bytestring :param hash_algorithm: Hash algorithm which should be used to sign data. 'sha256' by default :param export: set it to True, if you would like to export the resulting. In other case the key will e stored in the :class:`~optigatrust.objects.AcquiredSession` :raises: - TypeError - when any of the parameters are of the wrong type - ValueError - when any of the parameters not expected - OSError - when an error is returned by the core initialisation library :returns: byte string with the key if requested, otherwise None """ api = optiga.Chip().api if not isinstance(key_object, (objects.AppData, objects.Session, objects.AcquiredSession)): raise TypeError( 'key_object should be either {0}, {1}, or {2} types'.format( objects.AppData, objects.Session, objects.AcquiredSession ) ) if isinstance(key_object, objects.AppData): try: if key_object.meta['type'] != 'pre_sh_secret': raise ValueError( 'Selected object doesn\'t have a proper setup.' 'Should have PRESHSEC type, you have {0}'.format(key_object.meta['type']) ) except KeyError: raise ValueError( 'Selected object doesn\'t have a proper setup.' 'Should have PRESHSEC type' ) _hash_map = { 'sha256': 0x08, 'sha384': 0x09, 'sha512': 0x0a } if hash_algorithm not in _hash_map: raise ValueError( 'Hash algorithm should be one of the following {}'.format(_hash_map.keys()) ) if salt is None: salt_len = c_ushort(0) else: salt_len = c_ushort(len(salt)) if info is None: info_len = c_ushort(0) else: info_len = c_ushort(len(info)) if export: derived_key = (c_ubyte * key_length)() else: derived_key = None ret = api.exp_optiga_crypt_hkdf(_hash_map[hash_algorithm], key_object.id, salt, byref(salt_len), info, byref(info_len), key_length, int(export), derived_key) if ret == 0: if export: return bytes(derived_key) else: raise IOError('Function can\'t be executed. Error {0}'.format(hex(ret)))
def hmac(key_object, data, hash_algorithm='sha256'): """ This function calculates a HMAC over a given data using the secret stored on OPTIGA .. note:: Only OPTIGA™ Trust M3 relevant :param key_object: Key Object on the OPTIGA Chip, which should be used as a source of the private key storage. Can be one of the following classes :class:`~optigatrust.objects.AppData`, :class:`~optigatrust.objects.Session`, or :class:`~optigatrust.objects.AcquiredSession` :param data: A byte string data :param hash_algorithm: Hash algorithm which should be used to sign data. 'sha256' by default :raises: - TypeError - when any of the parameters are of the wrong type - ValueError - when any of the parameters not expected - OSError - when an error is returned by the core initialisation library :returns: byte string with the resulating MAC """ api = optiga.Chip().api if not isinstance(key_object, (objects.AppData, objects.Session, objects.AcquiredSession)): raise TypeError( 'key_object should be either {0}, {1}, or {2} types'.format( objects.AppData, objects.Session, objects.AcquiredSession ) ) if isinstance(key_object, objects.AppData): try: if key_object.meta['type'] != 'pre_sh_secret': raise ValueError( 'Selected object doesn\'t have a proper setup.' 'Should have PRESHSEC type, you have {0}'.format(key_object.meta['type']) ) except KeyError: raise ValueError( 'Selected object doesn\'t have a proper setup.' 'Should have PRESHSEC type' ) _hash_map = { 'sha256': (0x20, 32), 'sha384': (0x21, 48), 'sha512': (0x22, 64) } if hash_algorithm not in _hash_map: raise ValueError( 'Hash algorithm should be one of the following {}'.format(_hash_map.keys()) ) if not isinstance(data, (bytearray, bytes)): raise TypeError( 'Data should be byte string, {0} provided.'.format(type(data)) ) _data = (c_ubyte * len(data))(*data) mac = (c_ubyte * _hash_map[hash_algorithm][1])() mac_len = c_uint(_hash_map[hash_algorithm][1]) ret = api.exp_optiga_crypt_hmac(_hash_map[hash_algorithm][0], key_object.id, _data, len(_data), mac, byref(mac_len)) if ret == 0: return bytes(mac) else: raise IOError('Function can\'t be executed. Error {0}'.format(hex(ret)))
def ecdh(key_object, external_pkey, export=False): """ This function derives a shared secret using Diffie-Hellman Key-Exchange. This function assumes the instance of the key from which this method will be called represents the private key on the system used for ECDH :param key_object: Key Object on the OPTIGA Chip, which should be used as a source of the private key storage :class:`~optigatrust.objects.ECCKey` :param external_pkey: a bytearray with a public key You can submit public keys with parameters as per openssl output in DER format :: from asn1crypto import pem # Option 1 pem_string = '-----BEGIN PUBLIC KEY-----\\n' + \\ 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhqPByq/2I5Xv1jqSZbBzS8fptkdP\\n' + \\ 'fArs2+l6SZ8IfOIukkf/wHiww0FV+jxehrVyzW+cy9+KftBobalw3iXN2A==\\n' + \\ '-----END PUBLIC KEY-----' if pem.detect(pem_string): type_name, headers, der_bytes = pem.unarmor(pem_string) # Option 2 hex_string ='3059301306072a8648ce3d020106082a' + \\ '8648ce3d0301070342000486a3c1caaf' + \\ 'f62395efd63a9265b0734bc7e9b6474f' + \\ '7c0aecdbe97a499f087ce22e9247ffc0' + \\ '78b0c34155fa3c5e86b572cd6f9ccbdf' + \\ '8a7ed0686da970de25cdd8' der_bytes = bytes().from_hex(hex_string) :param export: defines whether the resulting secret should be exported or not :raises: - TypeError - when any of the parameters are of the wrong type - OSError - when an error is returned by the core initialisation library :returns: in case `export` set to True returns a shared secret """ if not isinstance(key_object, objects.ECCKey): raise TypeError( 'key_object is not supported. You provided {0}, expected {1}'.format(type(key_object), objects.ECCKey) ) if not isinstance(external_pkey, bytes) and not isinstance(external_pkey, bytearray): raise TypeError( 'Public Key should be either bytes or ' 'bytearray type, you gave {0}'.format(type(external_pkey)) ) api = optiga.Chip().api # OPTIGA doesn't understand the asn.1 encoded parameters field external_pkey = _pkcs_to_native(pkey=external_pkey, algorithm=key_object.curve) # Extract the curve from the object metadata try: curve = key_object.meta['algorithm'] except KeyError: raise ValueError('Given object does\'t have a key populated.') api.exp_optiga_crypt_ecdh.argtypes = c_ushort, POINTER(PublicKeyFromHost), c_ubyte, POINTER(c_ubyte) api.exp_optiga_crypt_ecdsa_sign.restype = c_int pkey = PublicKeyFromHost() pkey.public_key = (c_ubyte * len(external_pkey))() memmove(pkey.public_key, external_pkey, len(external_pkey)) pkey.length = len(external_pkey) pkey.key_type = _str2curve(curve, return_value=True) if export: # Pubkey comprises 4 bytes of asn.1 tags and two coordinates, each of key size shared_secret = (c_ubyte * ((len(external_pkey) - 4) >> 1))() else: shared_secret = None ret = api.exp_optiga_crypt_ecdh(key_object.id, byref(pkey), int(export), shared_secret) if ret == 0: if export: return bytes(shared_secret) else: raise IOError('Function can\'t be executed. Error {0}'.format(hex(ret)))
def ecdsa_sign(key_object, data): """ This function signs given data based on the provided EccKey object. Hash algorithm is selected based on the size of the key :param key_object: Key Object on the OPTIGA Chip, which should be used as a source of the private key storage :class:`~optigatrust.objects.ECCKey` :param data: Data to sign, the data will be hashed based on the used curve. If secp256r1 then sha256, secp384r1 sha384 etc. :raises: - TypeError - when any of the parameters are of the wrong type - OSError - when an error is returned by the core initialisation library :returns: EcdsaSignature object or None """ if not isinstance(key_object, objects.ECCKey): raise TypeError( 'key_object is not supported. You provided {0}, expected {1}'.format(type(key_object), objects.ECCKey) ) api = optiga.Chip().api if not isinstance(data, bytes) and not isinstance(data, bytearray): if isinstance(data, str): _d = bytes(data.encode()) warnings.warn("data will be converted to bytes type before signing") else: raise TypeError('Data to sign should be either bytes or str type, you gave {0}'.format(type(data))) else: _d = data api.exp_optiga_crypt_ecdsa_sign.argtypes = POINTER(c_ubyte), c_ubyte, c_ushort, POINTER(c_ubyte), POINTER(c_ubyte) api.exp_optiga_crypt_ecdsa_sign.restype = c_int _map = { 'secp256r1': [hashlib.sha256, 32, 'sha256'], 'secp384r1': [hashlib.sha384, 48, 'sha384'], 'secp521r1': [hashlib.sha512, 64, 'sha512'], 'brainpoolp256r1': [hashlib.sha256, 32, 'sha256'], 'brainpoolp384r1': [hashlib.sha384, 48, 'sha384'], 'brainpoolp512r1': [hashlib.sha512, 64, 'sha512'] } # The curve should be one of supported, so no need for extra check param = _map[key_object.curve] # This lines are evaluates as following; i.e. # digest = (c_ubyte * 32)(*hashlib.sha256(_d).digest()) # s = (c_ubyte * ((32*2 + 2) + 6))() # hash_algorithm = 'sha256' digest = (c_ubyte * param[1])(*param[0](_d).digest()) # We reserve two extra bytes for nistp512r1 curve, shich has signature r/s values longer than a hash size s = (c_ubyte * ((param[1] * 2 + 2) + 6))() hash_algorithm = param[2] c_slen = c_ubyte(len(s)) ret = api.exp_optiga_crypt_ecdsa_sign(digest, len(digest), key_object.id, s, byref(c_slen)) if ret == 0: signature = (c_ubyte * (c_slen.value + 2))() signature[0] = 0x30 signature[1] = c_slen.value memmove(addressof(signature) + 2, s, c_slen.value) return ECDSASignature(hash_algorithm, key_object.id, bytes(signature)) else: raise IOError('Function can\'t be executed. Error {0}'.format(hex(ret)))
def _generate_rsa_pair(key_object, key_size=1024, key_usage=None, export=False): handle = optiga.Chip() _allowed_key_usages = { 'key_agreement': handle.key_usage.KEY_AGR, 'authentication': handle.key_usage.AUTH, 'encryption': handle.key_usage.ENCRYPT, 'signature': handle.key_usage.SIGN } _key_usage = list() priv_key = None if key_usage is None: _key_usage = [handle.key_usage.KEY_AGR, handle.key_usage.SIGN] else: for entry in key_usage: if entry not in _allowed_key_usages: raise ValueError( 'Wrong Key Usage value {0}, supported are {1}'.format(entry, _allowed_key_usages.keys()) ) _key_usage.append(_allowed_key_usages[entry]) _bytes = None api = handle.api allowed_key_sizes = (1024, 2048) if key_size not in allowed_key_sizes: raise ValueError('This key size is not supported, you typed {0} (type {1}) supported are [1024, 2048]'. format(key_size, type(key_size))) api.exp_optiga_crypt_rsa_generate_keypair.argtypes = c_int, c_ubyte, c_bool, c_void_p, POINTER( c_ubyte), POINTER(c_ushort) api.exp_optiga_crypt_rsa_generate_keypair.restype = c_int if key_size == 1024: c_keytype = 0x41 rsa_header = b'0\x81\x9f0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00' else: c_keytype = 0x42 rsa_header = b'0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00' c_keyusage = c_ubyte(sum(map(lambda ku: ku.value, _key_usage))) pkey = (c_ubyte * 320)() if export: # https://github.com/Infineon/optiga-trust-m/wiki/Data-format-examples#RSA-Private-Key key = (c_ubyte * (100 + 4))() else: key = byref(c_ushort(key_object.id)) c_plen = c_ushort(len(pkey)) ret = api.exp_optiga_crypt_ecc_generate_keypair(c_keytype, c_keyusage, int(export), key, pkey, byref(c_plen)) if export: priv_key = (c_ubyte * (100 + 4))() memmove(priv_key, key, 100 + 4) pub_key = (c_ubyte * c_plen.value)() memmove(pub_key, pkey, c_plen.value) else: pub_key = (c_ubyte * c_plen.value)() memmove(pub_key, pkey, c_plen.value) if ret == 0: _pkey = rsa_header + bytes(pub_key) _key = None key_object.key_size = key_size if export: _key = bytes(priv_key) return _pkey, _key else: raise IOError('Function can\'t be executed. Error {0}'.format(hex(ret)))
def _generate_ecc_pair(key_object, curve, key_usage=None, export=False): opt = optiga.Chip() _allowed_key_usage = { 'key_agreement': opt.key_usage.KEY_AGR, 'authentication': opt.key_usage.AUTH, 'signature': opt.key_usage.SIGN } _key_sizes = { 'secp256r1': (68, 34), 'secp384r1': (100, 50), 'secp521r1': (137, 67), 'brainpoolp256r1': (68, 34), 'brainpoolp384r1': (100, 50), 'brainpoolp512r1': (133, 66) } _key_usage = list() priv_key = None if key_usage is None: _key_usage = [opt.key_usage.KEY_AGR, opt.key_usage.SIGN] else: for entry in key_usage: if entry not in _allowed_key_usage: raise ValueError( 'Wrong Key Usage value {0}, supported are {1}'.format(entry, _allowed_key_usage.keys()) ) _key_usage.append(_allowed_key_usage[entry]) c = _str2curve(curve, return_value=True) if c not in opt.curves_values: raise TypeError( "object_id not found. \n\r Supported = {0},\n\r " "Provided = {1}".format(list(opt.curves_values), c)) opt.api.exp_optiga_crypt_ecc_generate_keypair.argtypes = c_int, c_ubyte, c_bool, c_void_p, POINTER( c_ubyte), POINTER(c_ushort) opt.api.exp_optiga_crypt_ecc_generate_keypair.restype = c_int c_keyusage = c_ubyte(sum(map(lambda ku: ku.value, _key_usage))) pkey = (c_ubyte * _key_sizes[curve][0])() c_plen = c_ushort(len(pkey)) if export: # https://github.com/Infineon/optiga-trust-m/wiki/Data-format-examples#RSA-Private-Key key = (c_ubyte * _key_sizes[curve][1])() else: key = byref(c_ushort(key_object.id)) ret = opt.api.exp_optiga_crypt_ecc_generate_keypair(c, c_keyusage, int(export), key, pkey, byref(c_plen)) if export: priv_key = (c_ubyte * _key_sizes[curve][1])() memmove(priv_key, key, _key_sizes[curve][1]) pub_key = (c_ubyte * c_plen.value)() memmove(pub_key, pkey, c_plen.value) else: pub_key = (c_ubyte * c_plen.value)() memmove(pub_key, pkey, c_plen.value) if ret == 0: key_object.curve = curve if export: public_key, private_key = _native_to_pkcs(key=bytes(priv_key), pkey=bytes(pub_key), algorithm=curve) return public_key, private_key else: public_key, _ = _native_to_pkcs(key=None, pkey=bytes(pub_key), algorithm=curve) return public_key, None else: raise IOError('Function can\'t be executed. Error {0}'.format(hex(ret)))