Example #1
0
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)
Example #2
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
Example #3
0
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)))
Example #4
0
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)
Example #5
0
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
Example #6
0
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
Example #7
0
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)))
Example #8
0
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)))
Example #9
0
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)))
Example #10
0
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)))
Example #11
0
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)))
Example #12
0
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)))