Ejemplo n.º 1
0
    def pki_status(self, status: PKIStatus, failure_info: FailInfo = None):
        """Set the PKI status of the operation.

        Args:
              status (PKIStatus): A valid pkiStatus value
              failure_info (FailInfo): A failure info type, which must be present if PKIStatus is failure.
        Returns:
              PKIMessageBuilder: This instance
        See Also:
              - `draft-gutmann-scep Section 3.2.1.3.
                <https://datatracker.ietf.org/doc/draft-gutmann-scep/?include_text=1>`_.
        """
        attr = CMSAttribute({
            'type': 'pki_status',
            'values': [PrintableString(status.value)],
        })
        self._cms_attributes.append(attr)

        if status == PKIStatus.FAILURE:
            if failure_info is None:
                raise ValueError('You cannot specify failure without failure info')

            fail_attr = CMSAttribute({
                'type': 'fail_info',
                'values': [PrintableString(failure_info.value)],
            })
            self._cms_attributes.append(fail_attr)

        return self
Ejemplo n.º 2
0
def make_signed_attrs(digest: bytes, hash_type: int) -> CMSAttributes:
    content_type = CMSAttribute({
        "type": CMSAttributeType.unmap("content_type"),
        "values": [ContentType.unmap("data")],
    })

    time_now = UTCTime()
    time_now.set(datetime.now(timezone.utc))
    signing_time = CMSAttribute({
        "type": CMSAttributeType.unmap("signing_time"),
        "values": [time_now]
    })

    message_digest = CMSAttribute({
        "type":
        CMSAttributeType.unmap("message_digest"),
        "values": [OctetString(digest)],
    })

    ha_v1 = make_hash_agility_v1(digest)

    ha_v2 = make_hash_agility_v2(digest, hash_type)

    return CMSAttributes(
        [content_type, signing_time, message_digest, ha_v1, ha_v2])
Ejemplo n.º 3
0
    def sender_nonce(self, nonce=None):
        """Add a sender nonce.

        Args:
              nonce (bytes or OctetString): Sender nonce
        Returns:
              PKIMessageBuilder: This instance
        See Also:
              - `draft-gutmann-scep Section 3.2.1.5.
                <https://datatracker.ietf.org/doc/draft-gutmann-scep/?include_text=1>`_.
        """
        if isinstance(nonce, bytes):
            nonce = OctetString(nonce)
        elif nonce is None:
            nonce = OctetString(os.urandom(16))

        attr = CMSAttribute({
            'type': u'sender_nonce',
            'values': [nonce],
        })

        logger.debug("{:<20}: {}".format('Sender Nonce',
                                         b64encode(nonce.native)))

        self._cms_attributes.append(attr)
        return self
Ejemplo n.º 4
0
def make_hash_agility_v2(digest: bytes, hash_type: int) -> CMSAttribute:
    """
    CMSAttribute:
        type: HASH_AGILITY_V2_OID
        values: Set of HashAgility
            type: DigestAlgorithmId
            data: digest
    """
    dg_algo = _get_digest_algo(hash_type)
    ha = HashAgility({"type": dg_algo, "data": OctetString(digest)})
    return CMSAttribute({"type": HASH_AGILITY_V2_OID, "values": [ha]})
Ejemplo n.º 5
0
    def make_signature(self):
        assert self.sig.code_dir_blob

        # Redo the code hashes
        self._set_code_hashes()

        # Make the signature
        signed_attrs: CMSAttributes = make_signed_attrs(
            self.sig.code_dir_blob.get_hash(self.hash_type), self.hash_type)
        actual_privkey = load_private_key(self.privkey)
        signature = rsa_pkcs1v15_sign(actual_privkey, signed_attrs.dump(),
                                      self.hash_type_str)

        # Get the timestamp from Apple
        digest = get_hash(signature, self.hash_type)
        tst = CMSAttribute({
            "type":
            CMSAttributeType("signature_time_stamp_token"),
            "values": [get_timestamp_token(digest, self.hash_type)],
        })

        # Make the CMS
        self.sig.sig_blob = SignatureBlob()
        self.sig.sig_blob.cms = make_cms(self.cert, self.hash_type,
                                         signed_attrs, signature,
                                         CMSAttributes([tst]))

        # Get the CodeSignature section. It should be the last in the binary
        cs_sec = self.macho.sect[-1]
        assert cs_sec == self.get_linkedit_segment().sect[-1]
        assert isinstance(cs_sec, CodeSignature)
        sig_cmd = self.get_sig_command()

        # Serialize the signature
        f = BytesIO()
        self.sig.serialize(f)
        f.write((sig_cmd.datasize - f.tell()) * b"\x00")

        if self.detach_target:
            target_dir = os.path.join(self.detach_target, "Contents", "MacOS")
            os.makedirs(target_dir, exist_ok=True)
            target_file = os.path.join(
                target_dir,
                os.path.basename(self.filename) +
                f".{CPU_NAMES[self.macho.Mhdr.cputype]}sign",
            )
            with open(target_file, "wb") as tf:
                tf.write(f.getvalue())
                self.files_modified.append(target_file)
        else:
            # Set the section's content to be the signature
            cs_sec.content = StrPatchwork(f.getvalue())
Ejemplo n.º 6
0
def make_hash_agility_v1(digest: bytes) -> CMSAttribute:
    """
    CMSAttribue:
        type: HASH_AGILITY_V1_OID
        values: Set of 1 XML Plist
            dict: {
                "cdhashes": [digset truncated to 20 bytes]
            }
    """
    plist_dict = {"cdhashes": [digest[:20]]}
    plist_bytes = plistlib.dumps(plist_dict, fmt=plistlib.FMT_XML)
    return CMSAttribute({
        "type": HASH_AGILITY_V1_OID,
        "values": [OctetString(plist_bytes)]
    })
Ejemplo n.º 7
0
    def message_type(self, message_type: MessageType):
        """Set the SCEP Message Type Attribute.

        Args:
              message_type (MessageType): A valid PKIMessage messageType
        Returns:
              PKIMessageBuilder: This instance
        See Also:
              - `draft-gutmann-scep Section 3.2.1.2.
                <https://datatracker.ietf.org/doc/draft-gutmann-scep/?include_text=1>`_.
        """
        attr = CMSAttribute({
            'type': 'message_type',
            'values': [PrintableString(message_type.value)],
        })
        self._cms_attributes.append(attr)

        return self
Ejemplo n.º 8
0
    def transaction_id(self, trans_id: Union[str, PrintableString] = None):
        """Add a transaction ID.

        Args:
              trans_id (str or PrintableString): Transaction ID. If omitted, one is generated
        Returns:
              PKIMessageBuilder: This instance
        See Also:
              - `draft-gutmann-scep Section 3.2.1.1.
                <https://datatracker.ietf.org/doc/draft-gutmann-scep/?include_text=1>`_.
        """
        if isinstance(trans_id, str):
            trans_id = PrintableString(trans_id)
        elif trans_id is None:
            trans_id = PrintableString(str(uuid4()))

        attr = CMSAttribute({'type': 'transaction_id', 'values': [trans_id]})

        self._cms_attributes.append(attr)
        return self
Ejemplo n.º 9
0
    def recipient_nonce(self, nonce: Union[bytes, OctetString]):
        """Add a recipient nonce.

        Args:
              nonce (bytes or OctetString): Recipient nonce
        Returns:
              PKIMessageBuilder: This instance
        See Also:
              - `draft-gutmann-scep Section 3.2.1.5.
                <https://datatracker.ietf.org/doc/draft-gutmann-scep/?include_text=1>`_.
        """
        if isinstance(nonce, bytes):
            nonce = OctetString(nonce)

        attr = CMSAttribute({
            'type': 'recipient_nonce',
            'values': [nonce],
        })

        self._cms_attributes.append(attr)
        return self
Ejemplo n.º 10
0
    def sign(self,
             data: bytes,
             content_type: ContentType,
             content_digest: bytes,
             cms_attributes: List[CMSAttribute]) -> SignerInfo:
        """Generate a signature encrypted with the signer's private key and return the SignerInfo."""
        
        # The CMS standard requires that the content-type authenticatedAttribute and the message-digest
        # attribute must be present if any authenticatedAttribute exists at all.
        self.signed_attributes = cms_attributes

        self.signed_attributes.insert(0, CMSAttribute({
            'type': 'signing_time',
            'values': [GeneralizedTime(datetime.datetime.utcnow())]
        }))

        self.signed_attributes.insert(0, CMSAttribute({
            'type': 'message_digest',
            'values': [OctetString(content_digest)],
        }))

        # This refers to whatever the content of EncapsulatedContentInfo is
        self.signed_attributes.insert(0, CMSAttribute({
            'type': 'content_type',
            'values': [content_type],
        }))

        cms_attributes = CMSAttributes(self.signed_attributes)

        # NOTE: no need to calculate this digest as .signer() does the hashing

        # RFC5652
        # The message digest is
        # computed on either the content being signed or the content
        # together with the signed attributes using the process described in
        # Section 5.4.
        # digest = hashes.Hash(hashes.SHA256(), backend=default_backend())

        # the initial input is the encapContentInfo eContent OCTET STRING
        # RFC5652 Section 5.4 - When the field (signed_attrs) is present, however, the result is the message
        # digest of the complete DER encoding of the SignedAttrs value
        # contained in the signedAttrs field.
        # NOTE: it is not clear whether data is included
        #digest.update(data)
        # digest.update(cms_attributes.dump())
        # d = digest.finalize()

        # Make DigestInfo from result
        # NOTE: It is not clear whether this applies: RFC5652 - Section 5.5.
        # digest_info = DigestInfo({
        #     'digest_algorithm': self.digest_algorithm,
        #     'digest': d,
        # })

        # Get the RSA key to sign the digestinfo
        digest_function = {
            'sha1': hashes.SHA1,  # macOS
            'sha256': hashes.SHA256,
            'sha512': hashes.SHA512
        }[self.digest_algorithm_id.native]

        signer = self.private_key.signer(
            asympad.PKCS1v15(),
            digest_function(),
        )

        # NOTE: this is not the digest `d` above because crypto.io already hashes stuff for us!!
        signer.update(cms_attributes.dump())
        signature = signer.finalize()

        signer_info = SignerInfo({
            # Version must be 1 if signer uses IssuerAndSerialNumber as sid
            'version': CMSVersion(1),
            'sid': self.sid,
            
            'digest_algorithm': self.digest_algorithm,
            'signed_attrs': cms_attributes,

            # Referred to as ``digestEncryptionAlgorithm`` in the RFC
            'signature_algorithm': self.signed_digest_algorithm,

            # Referred to as ``encryptedDigest`` in the RFC
            'signature': OctetString(signature),
        })

        return signer_info
Ejemplo n.º 11
0
    def sign(self):
        h = hashes.Hash(hashes.SHA256(), backend=default_backend())
        h.update(self._content_mime.as_bytes())
        message_digest = h.finalize()

        cs = CertificateSet()
        cs.append(load(self._certificate.public_bytes(Encoding.DER)))

        for ca_cert in self._ca:
            cs.append(load(ca_cert.public_bytes(Encoding.DER)))

        ec = ContentInfo({
            'content_type': ContentType('data'),
        })

        sident = SignerIdentifier({
            'issuer_and_serial_number':
            IssuerAndSerialNumber({
                'issuer':
                load(self._issuer_name.public_bytes(default_backend())),
                'serial_number':
                self._cert_serial,
            })
        })

        certv2 = ESSCertIDv2({
            'hash_algorithm':
            DigestAlgorithm({'algorithm': DigestAlgorithmId('sha256')}),
            'cert_hash':
            OctetString(self._certificate.fingerprint(hashes.SHA256())),
            'issuer_serial':
            IssuerSerial({
                'issuer':
                load(self._issuer_name.public_bytes(default_backend())),
                'serial_number':
                self._cert_serial,
            }),
        })

        now = datetime.now().replace(microsecond=0,
                                     tzinfo=pytz.utc)  # .isoformat()

        sattrs = CMSAttributes({
            CMSAttribute({
                'type': CMSAttributeType('content_type'),
                'values': ["data"]
            }),
            CMSAttribute({
                'type': CMSAttributeType('message_digest'),
                'values': [message_digest]
            }),
            CMSAttribute({
                'type': CMSAttributeType('signing_time'),
                'values': (Time({'utc_time': UTCTime(now)}), )
            }),
            CMSAttribute({
                'type':
                CMSAttributeType('signing_certificate_v2'),
                'values': [SigningCertificateV2({'certs': (certv2, )})]
            })
        })

        signature = self._private_key.sign(sattrs.dump(), padding.PKCS1v15(),
                                           hashes.SHA256())  #

        si = SignerInfo({
            'version':
            'v1',
            'sid':
            sident,
            'digest_algorithm':
            DigestAlgorithm({'algorithm': DigestAlgorithmId('sha256')}),
            'signed_attrs':
            sattrs,
            'signature_algorithm':
            SignedDigestAlgorithm(
                {'algorithm': SignedDigestAlgorithmId('rsassa_pkcs1v15')}),
            'signature':
            signature,
        })

        da = DigestAlgorithms(
            (DigestAlgorithm({'algorithm': DigestAlgorithmId('sha256')}), ))
        signed_data = SignedData({
            'version': 'v1',
            'encap_content_info': ec,
            'certificates': cs,
            'digest_algorithms': da,
            'signer_infos': SignerInfos((si, ))
        })

        ci = ContentInfo({
            'content_type': ContentType('signed_data'),
            'content': signed_data
        })

        self._signature_mime = MIMEApplication(ci.dump(),
                                               _subtype="pkcs7-signature",
                                               name="smime.p7s",
                                               policy=email.policy.SMTPUTF8)
        self._signature_mime.add_header('Content-Disposition',
                                        'attachment; filename=smime.p7s')

        super(CADESMIMESignature, self).attach(self._content_mime)
        super(CADESMIMESignature, self).attach(self._signature_mime)
Ejemplo n.º 12
0
    def sign(self):
        h = hashes.Hash(hashes.SHA256(), backend=default_backend())
        h.update(self._content_mime.as_bytes())
        message_digest = h.finalize()

        cs = CertificateSet()
        cs.append(load(self._certificate.public_bytes(Encoding.DER)))

        for ca_cert in self._ca:
            cs.append(load(ca_cert.public_bytes(Encoding.DER)))

        ec = EncapsulatedContentInfo({
            'content_type':
            ContentType('data'),
            'content':
            ParsableOctetString(self._content_mime.as_bytes())
        })

        sident = SignerIdentifier({
            'issuer_and_serial_number':
            IssuerAndSerialNumber({
                'issuer':
                load(self._issuer_name.public_bytes(default_backend())),
                'serial_number':
                self._cert_serial,
            })
        })

        certv2 = ESSCertIDv2({
            'hash_algorithm':
            DigestAlgorithm({'algorithm': DigestAlgorithmId('sha256')}),
            'cert_hash':
            OctetString(self._certificate.fingerprint(hashes.SHA256())),
            'issuer_serial':
            IssuerSerial({
                'issuer':
                load(
                    self._issuer_name.public_bytes(default_backend())
                ),  #[GeneralName({'directory_name': self._issuer_name.public_bytes(default_backend())})],
                'serial_number':
                self._cert_serial,
            }),
        })

        now = datetime.now().replace(microsecond=0, tzinfo=pytz.utc)

        sattrs = CMSAttributes({
            CMSAttribute({
                'type': CMSAttributeType('content_type'),
                'values': ["data"]
            }),
            CMSAttribute({
                'type': CMSAttributeType('message_digest'),
                'values': [message_digest]
            }),
            CMSAttribute({
                'type': CMSAttributeType('signing_time'),
                'values': (Time({'utc_time': UTCTime(now)}), )
            }),
            # isti k v
            CMSAttribute({
                'type':
                CMSAttributeType('signing_certificate_v2'),
                'values': [SigningCertificateV2({'certs': (certv2, )})]
            })
        })

        signature = self._private_key.sign(sattrs.dump(), padding.PKCS1v15(),
                                           hashes.SHA256())

        si = SignerInfo({
            'version':
            'v1',
            'sid':
            sident,
            'digest_algorithm':
            DigestAlgorithm({'algorithm': DigestAlgorithmId('sha256')}),
            'signed_attrs':
            sattrs,
            'signature_algorithm':
            SignedDigestAlgorithm(
                {'algorithm': SignedDigestAlgorithmId('rsassa_pkcs1v15')}),
            'signature':
            signature,
        })

        da = DigestAlgorithms(
            (DigestAlgorithm({'algorithm': DigestAlgorithmId('sha256')}), ))
        signed_data = SignedData({
            'version': 'v3',
            'encap_content_info': ec,
            'certificates': cs,
            'digest_algorithms': da,
            'signer_infos': SignerInfos((si, ))
        })

        ci = ContentInfo({
            'content_type': ContentType('signed_data'),
            'content': signed_data
        })

        self.set_payload(ci.dump())
        encode_base64(self)
Ejemplo n.º 13
0
    def sign(self, data, content_type, content_digest, cms_attributes):
        """Generate a signature encrypted with the signer's private key and return the SignerInfo."""

        # The CMS standard requires that the content-type authenticatedAttribute and the message-digest
        # attribute must be present if any authenticatedAttribute exists at all.
        self.signed_attributes = cms_attributes

        # NDES does not even include this
        # self.signed_attributes.insert(0, CMSAttribute({
        #     'type': 'signing_time',
        #     'values': [GeneralizedTime(datetime.datetime.utcnow())]
        # }))

        self.signed_attributes.insert(
            0,
            CMSAttribute({
                'type': u'message_digest',
                'values': [OctetString(content_digest)],
            }))

        # This refers to whatever the content of EncapsulatedContentInfo is
        self.signed_attributes.insert(
            0,
            CMSAttribute({
                'type': u'content_type',
                'values': [content_type],
            }))

        cms_attributes = CMSAttributes(self.signed_attributes)

        # NOTE: no need to calculate this digest as .signer() does the hashing

        # RFC5652
        # The message digest is
        # computed on either the content being signed or the content
        # together with the signed attributes using the process described in
        # Section 5.4.
        # digest = hashes.Hash(hashes.SHA256(), backend=default_backend())

        # the initial input is the encapContentInfo eContent OCTET STRING
        # RFC5652 Section 5.4 - When the field (signed_attrs) is present, however, the result is the message
        # digest of the complete DER encoding of the SignedAttrs value
        # contained in the signedAttrs field.
        # NOTE: it is not clear whether data is included
        #digest.update(data)
        # digest.update(cms_attributes.dump())
        # d = digest.finalize()

        # Make DigestInfo from result
        # NOTE: It is not clear whether this applies: RFC5652 - Section 5.5.
        # digest_info = DigestInfo({
        #     'digest_algorithm': self.digest_algorithm,
        #     'digest': d,
        # })

        signature = self.private_key.sign(
            data=cms_attributes.dump(),
            padding_type='pkcs',
            algorithm=self.digest_algorithm_id.native)

        signer_info = SignerInfo({
            # Version must be 1 if signer uses IssuerAndSerialNumber as sid
            'version': CMSVersion(1),
            'sid': self.sid,
            'digest_algorithm': self.digest_algorithm,
            'signed_attrs': cms_attributes,

            # Referred to as ``digestEncryptionAlgorithm`` in the RFC
            'signature_algorithm': self.signed_digest_algorithm,

            # Referred to as ``encryptedDigest`` in the RFC
            'signature': OctetString(signature),
        })

        return signer_info