Exemple #1
0
def open_pkcs11_session(lib_location, slot_no=None, token_label=None,
                        user_pin=None) -> Session:
    """
    Open a PKCS#11 session

    :param lib_location:
        Path to the PKCS#11 module.
    :param slot_no:
        Slot number to use. If not specified, the first slot containing a token
        labelled ``token_label`` will be used.
    :param token_label:
        Label of the token to use. If ``None``, there is no constraint.
    :param user_pin:
        User PIN to use.

        .. note::
            Some PKCS#11 implementations do not require PIN when the token
            is opened, but will prompt for it out-of-band when signing.
    :return:
        An open PKCS#11 session object.
    """
    lib = pkcs11_lib(lib_location)

    slots = lib.get_slots()
    token = None
    if slot_no is None:
        for slot in slots:
            try:
                token = slot.get_token()
                if token_label is None or token.label == token_label:
                    break
            except PKCS11Error:
                continue
        if token is None:
            raise PKCS11Error(
                f'No token with label {token_label} found'
                if token_label is not None else 'No token found'
            )
    else:
        token = slots[slot_no].get_token()
        if token_label is not None and token.label != token_label:
            raise PKCS11Error('Token in slot %d is not BELPIC.' % slot_no)

    kwargs = {}
    if user_pin is not None:
        kwargs['user_pin'] = user_pin

    return token.open(**kwargs)
Exemple #2
0
def _pull_cert(pkcs11_session: Session,
               label: Optional[str] = None,
               cert_id: Optional[bytes] = None):

    query_params = {Attribute.CLASS: ObjectClass.CERTIFICATE}
    if label is not None:
        query_params[Attribute.LABEL] = label
    if cert_id is not None:
        query_params[Attribute.ID] = cert_id
    q = pkcs11_session.get_objects(query_params)

    # need to run through the full iterator to make sure the operation
    # terminates
    results = list(q)
    if len(results) == 1:
        cert_obj = results[0]
        return x509.Certificate.load(cert_obj[Attribute.VALUE])
    else:
        info_strs = []
        if label is not None:
            info_strs.append(f"label '{label}'")
        if cert_id is not None:
            info_strs.append(
                f"ID '{binascii.hexlify(cert_id).decode('ascii')}'")
        qualifier = f" with {', '.join(info_strs)}" if info_strs else ""
        if not results:
            err = f"Could not find cert{qualifier}."
        else:
            err = f"Found more than one cert{qualifier}."
        raise PKCS11Error(err)
Exemple #3
0
    def _load_objects(self):
        if self._loaded:
            return

        self._init_cert_registry()
        self._signing_cert = _pull_cert(self.pkcs11_session, self.cert_label)

        q = self.pkcs11_session.get_objects({
            Attribute.LABEL: self.key_label,
            Attribute.CLASS: ObjectClass.PRIVATE_KEY
        })
        try:
            kh, = list(q)
        except ValueError as e:
            raise PKCS11Error(
                "Could not determine private key handle."
            ) from e
        if not kh[Attribute.SIGN]:
            logger.warning(
                f"The PKCS#11 device reports that the key with label "
                f"{self.key_label} cannot be used for signing!"
            )
        self._key_handle = kh

        self._loaded = True
Exemple #4
0
def open_beid_session(lib_location, slot_no=None) -> Session:
    """
    Open a PKCS#11 session

    :param lib_location:
        Path to the shared library file containing the eID PKCS#11 module.
        Usually, the file is named ``libbeidpkcs11.so``,
        ``libbeidpkcs11.dylib`` or ``beidpkcs11.dll``, depending on your
        operating system.
    :param slot_no:
        Slot number to use. If not specified, the first slot containing a token
        labelled ``BELPIC`` will be used.
    :return:
        An open PKCS#11 session object.
    """
    lib = pkcs11_lib(lib_location)

    slots = lib.get_slots()
    token = None
    if slot_no is None:
        for slot in slots:
            try:
                token = slot.get_token()
                if token.label == 'BELPIC':
                    break
            except PKCS11Error:
                continue
        if token is None:
            raise PKCS11Error('No BELPIC token found')
    else:
        token = slots[slot_no].get_token()
        if token.label != 'BELPIC':
            raise PKCS11Error('Token in slot %d is not BELPIC.' % slot_no)

    # the middleware will prompt for the user's PIN when we attempt
    # to sign later, so there's no need to specify it here
    return token.open()
Exemple #5
0
def _pull_cert(pkcs11_session: Session, label: str):
    q = pkcs11_session.get_objects({
        Attribute.LABEL: label,
        Attribute.CLASS: ObjectClass.CERTIFICATE
    })

    # need to run through the full iterator to make sure the operation
    # terminates
    try:
        cert_obj, = list(q)
    except ValueError:
        raise PKCS11Error(
            f"Could not find (unique) cert with label '{label}'."
        )
    return oskeys.parse_certificate(cert_obj[Attribute.VALUE])
Exemple #6
0
    def sign_raw(self, data: bytes, digest_algorithm: str, dry_run=False) \
            -> bytes:
        if dry_run:
            # allocate 4096 bits for the fake signature
            return b'0' * 512

        self._load_objects()
        from pkcs11 import Mechanism, SignMixin, MGF

        kh: SignMixin = self._key_handle
        kwargs = {}
        digest_algorithm = digest_algorithm.lower()
        signature_mechanism = self.get_signature_mechanism(digest_algorithm)
        signature_algo = signature_mechanism.signature_algo
        transform = None
        if signature_algo == 'rsassa_pkcs1v15':
            kwargs['mechanism'] = {
                'sha1': Mechanism.SHA1_RSA_PKCS,
                'sha256': Mechanism.SHA256_RSA_PKCS,
                'sha384': Mechanism.SHA384_RSA_PKCS,
                'sha512': Mechanism.SHA512_RSA_PKCS,
            }[digest_algorithm]
        elif signature_algo == 'ecdsa':
            # TODO test these, SoftHSM does not support these mechanisms
            #  apparently (only raw ECDSA)
            kwargs['mechanism'] = {
                'sha1': Mechanism.ECDSA_SHA1,
                'sha256': Mechanism.ECDSA_SHA256,
                'sha384': Mechanism.ECDSA_SHA384,
                'sha512': Mechanism.ECDSA_SHA512,
            }[digest_algorithm]
            from pkcs11.util.ec import encode_ecdsa_signature
            transform = encode_ecdsa_signature
        elif signature_algo == 'rsassa_pss':
            params: RSASSAPSSParams = signature_mechanism['parameters']
            assert digest_algorithm == \
                   params['hash_algorithm']['algorithm'].native

            # unpack PSS parameters into PKCS#11 language
            kwargs['mechanism'] = {
                'sha1': Mechanism.SHA1_RSA_PKCS_PSS,
                'sha256': Mechanism.SHA256_RSA_PKCS_PSS,
                'sha384': Mechanism.SHA384_RSA_PKCS_PSS,
                'sha512': Mechanism.SHA512_RSA_PKCS_PSS,
            }[digest_algorithm]

            pss_digest_param = {
                'sha1': Mechanism.SHA_1,
                'sha256': Mechanism.SHA256,
                'sha384': Mechanism.SHA384,
                'sha512': Mechanism.SHA512,
            }[digest_algorithm]

            pss_mgf_param = {
                'sha1': MGF.SHA1,
                'sha256': MGF.SHA256,
                'sha384': MGF.SHA384,
                'sha512': MGF.SHA512
            }[params['mask_gen_algorithm']['parameters']['algorithm'].native]
            pss_salt_len = params['salt_length'].native

            kwargs['mechanism_param'] = (
                pss_digest_param, pss_mgf_param, pss_salt_len
            )
        else:
            raise PKCS11Error(
                f"Signature algorithm '{signature_algo}' is not supported."
            )

        signature = kh.sign(data, **kwargs)
        if transform is not None:
            signature = transform(signature)

        return signature
Exemple #7
0
    async def async_sign_raw(self,
                             data: bytes,
                             digest_algorithm: str,
                             dry_run=False) -> bytes:
        if dry_run:
            # allocate 4096 bits for the fake signature
            return b'0' * 512

        await self.ensure_objects_loaded()
        from pkcs11 import MGF, Mechanism, SignMixin

        kh: SignMixin = self._key_handle
        kwargs = {}
        digest_algorithm = digest_algorithm.lower()
        signature_mechanism = self.get_signature_mechanism(digest_algorithm)
        signature_algo = signature_mechanism.signature_algo
        pre_sign_transform = None
        post_sign_transform = None
        if signature_algo == 'rsassa_pkcs1v15':
            if self.use_raw_mechanism:
                raise NotImplementedError(
                    "RSASSA-PKCS1v15 not available in raw mode")
            kwargs['mechanism'] = {
                'sha1': Mechanism.SHA1_RSA_PKCS,
                'sha224': Mechanism.SHA224_RSA_PKCS,
                'sha256': Mechanism.SHA256_RSA_PKCS,
                'sha384': Mechanism.SHA384_RSA_PKCS,
                'sha512': Mechanism.SHA512_RSA_PKCS,
            }[digest_algorithm]
        elif signature_algo == 'dsa':
            if self.use_raw_mechanism:
                raise NotImplementedError("DSA not available in raw mode")
            kwargs['mechanism'] = {
                'sha1': Mechanism.DSA_SHA1,
                'sha224': Mechanism.DSA_SHA224,
                'sha256': Mechanism.DSA_SHA256,
                # These can't be used in CMS IIRC (since the key sizes required
                # to meaningfully use them are ridiculous),
                # but they're in the PKCS#11 spec, so let's add them for
                # completeness
                'sha384': Mechanism.DSA_SHA384,
                'sha512': Mechanism.DSA_SHA512,
            }[digest_algorithm]
            from pkcs11.util.dsa import encode_dsa_signature
            post_sign_transform = encode_dsa_signature
        elif signature_algo == 'ecdsa':
            if self.use_raw_mechanism:
                kwargs['mechanism'] = Mechanism.ECDSA
                pre_sign_transform = _hash_fully(digest_algorithm)
            else:
                # TODO test these (unsupported in SoftHSMv2 right now)
                kwargs['mechanism'] = {
                    'sha1': Mechanism.ECDSA_SHA1,
                    'sha224': Mechanism.ECDSA_SHA224,
                    'sha256': Mechanism.ECDSA_SHA256,
                    'sha384': Mechanism.ECDSA_SHA384,
                    'sha512': Mechanism.ECDSA_SHA512,
                }[digest_algorithm]

            from pkcs11.util.ec import encode_ecdsa_signature
            post_sign_transform = encode_ecdsa_signature
        elif signature_algo == 'rsassa_pss':
            if self.use_raw_mechanism:
                raise NotImplementedError(
                    "RSASSA-PSS not available in raw mode")
            params: RSASSAPSSParams = signature_mechanism['parameters']
            assert digest_algorithm == \
                   params['hash_algorithm']['algorithm'].native

            # unpack PSS parameters into PKCS#11 language
            kwargs['mechanism'] = {
                'sha1': Mechanism.SHA1_RSA_PKCS_PSS,
                'sha224': Mechanism.SHA224_RSA_PKCS_PSS,
                'sha256': Mechanism.SHA256_RSA_PKCS_PSS,
                'sha384': Mechanism.SHA384_RSA_PKCS_PSS,
                'sha512': Mechanism.SHA512_RSA_PKCS_PSS,
            }[digest_algorithm]

            pss_digest_param = {
                'sha1': Mechanism.SHA_1,
                'sha224': Mechanism.SHA224,
                'sha256': Mechanism.SHA256,
                'sha384': Mechanism.SHA384,
                'sha512': Mechanism.SHA512,
            }[digest_algorithm]

            pss_mgf_param = {
                'sha1': MGF.SHA1,
                'sha224': MGF.SHA224,
                'sha256': MGF.SHA256,
                'sha384': MGF.SHA384,
                'sha512': MGF.SHA512
            }[params['mask_gen_algorithm']['parameters']['algorithm'].native]
            pss_salt_len = params['salt_length'].native

            kwargs['mechanism_param'] = (pss_digest_param, pss_mgf_param,
                                         pss_salt_len)
        else:
            raise PKCS11Error(
                f"Signature algorithm '{signature_algo}' is not supported.")

        if pre_sign_transform is not None:
            data = pre_sign_transform(data)

        def _perform_signature():
            signature = kh.sign(data, **kwargs)
            if post_sign_transform is not None:
                signature = post_sign_transform(signature)
            return signature

        loop = asyncio.get_running_loop()
        return await loop.run_in_executor(None, _perform_signature)