Пример #1
0
def _get_ext_by_oid(cert, oid):
    oid = ObjectIdentifier(oid)
    try:
        extension = cert.extensions.get_extension_for_oid(oid)
        return extension.value.value
    except ExtensionNotFound:
        return None
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

from u2flib_server.attestation.matchers import DEFAULT_MATCHERS
from u2flib_server.attestation.resolvers import create_resolver
from u2flib_server.jsapi import DeviceInfo
from u2flib_server.yubicommon.compat import byte2int

from cryptography.x509 import ObjectIdentifier, ExtensionNotFound
from enum import IntEnum

__all__ = ['Attestation', 'MetadataProvider', 'Transport']

TRANSPORTS_EXT_OID = ObjectIdentifier('1.3.6.1.4.1.45724.2.1.1')


class Transport(IntEnum):
    BT_CLASSIC = 0x01  # Bluetooth Classic
    BLE = 0x02  # Bluetooth Low Energy
    USB = 0x04
    NFC = 0x08


class Attestation(object):
    def __init__(self,
                 trusted,
                 vendor_info=None,
                 device_info=None,
                 cert_transports=0):
Пример #3
0
class ExtensionsObjectIds:
    NODE_TYPE = ObjectIdentifier("1.22.333.4444")
    NODE_ID = ObjectIdentifier("1.22.333.4445")
    ACCOUNT_ID = ObjectIdentifier("1.22.333.4446")
    NODE_PRIVILEGES = ObjectIdentifier("1.22.333.4447")
Пример #4
0
    :raises service_identity.VerificationError: If *certificate* is not valid
        for *hostname*.
    :raises service_identity.CertificateError: If *certificate* contains
        invalid/unexpected data.

    :returns: ``None``
    """
    verify_service_identity(
        cert_patterns=extract_ids(certificate),
        obligatory_ids=[DNS_ID(hostname)],
        optional_ids=[],
    )


ID_ON_DNS_SRV = ObjectIdentifier('1.3.6.1.5.5.7.8.7')  # id_on_dnsSRV


def extract_ids(cert):
    """
    Extract all valid IDs from a certificate for service verification.

    If *cert* doesn't contain any identifiers, the ``CN``s are used as DNS-IDs
    as fallback.

    :param cryptography.x509.Certificate cert: The certificate to be dissected.

    :return: List of IDs.
    """
    ids = []
    try:
Пример #5
0
    def login(self, message):
        if self.state != LOGIN:
            logger.warning("Invalid State")
            return False

        logger.info(f"Loging in")

        data = message.get("data", None)

        self.state = LOGIN_FINISH
        status = False  # status = False -> if login wasn't a success
        if AUTH_TYPE == AUTH_MEM:
            new_otp = base64.b64decode(data["otp"].encode())
            current_otp_client = digest(new_otp, "SHA256")

            message = {
                "type": "ERROR",
                "message": "Invalid credentials for logging in"
            }

            if self.current_otp == current_otp_client:  # success login
                status = True

                if self.clear_credentials:
                    logger.info(
                        "Clearing old credentials and saving new ones.")

                    self.current_otp_index = self.new_index + 1
                    self.current_otp_root = self.new_root
                    new_otp = self.new_otp

                with open(f"credentials/{self.user_id}_index", "wb") as file:
                    file.write(f"{self.current_otp_index - 1}".encode())
                with open(f"credentials/{self.user_id}_root", "wb") as file:
                    file.write(self.current_otp_root)
                with open(f"credentials/{self.user_id}_otp", "wb") as file:
                    file.write(new_otp)

                logger.info(
                    "User logged in with success! Credentials updated.")
            else:
                logger.info(
                    "User not logged in! Wrong credentials where given.")
        elif AUTH_TYPE == AUTH_CC:
            cc_certificate = certificate_object(
                base64.b64decode(data["certificate"].encode()))
            signed_nonce = base64.b64decode(data["sign_nonce"].encode())

            certificates = load_certificates("cc_certificates/")

            chain = []
            chain_completed = construct_certificate_chain(
                chain, cc_certificate, certificates)

            if not chain_completed:
                error_message = "Couldn't complete the certificate chain"
                logger.warning(error_message)
                message = {"type": "ERROR", "message": error_message}
                status = False
            else:
                valid_chain, error_messages = validate_certificate_chain(chain)
                if not valid_chain:
                    logger.error(error_messages)
                    message = {"type": "ERROR", "message": error_messages}
                    status = False
                else:
                    status = verify_signature(cc_certificate, signed_nonce,
                                              self.nonce)

            if status:
                oid = ObjectIdentifier(
                    "2.5.4.5")  # oid of citizens card's CI (civil id)
                self.user_id = cc_certificate.subject.get_attributes_for_oid(
                    oid)[0].value

                logger.info("User logged in with success")
                message = {"type": "OK"}

        # Access verification
        if status:
            access_result = self.check_access()
            if not access_result[0]:
                logger.warning(access_result[1])

                status = False
                message = {"type": "ERROR", "message": access_result[1]}
            else:
                message = {"type": "OK"}
                logger.info(access_result[1])

        self._send(message)
        return status
Пример #6
0
 def lookup_ec_by_oid(service: IOService = Provide[Container.service]):
     dotted_string = service.input(
         "Give the Elliptic Curve's dotted string")
     return ec.get_curve_for_oid(ObjectIdentifier(dotted_string))
Пример #7
0
def verify_android_key(
    *,
    attestation_statement: AttestationStatement,
    attestation_object: bytes,
    client_data_json: bytes,
    credential_public_key: bytes,
    pem_root_certs_bytes: List[bytes],
) -> bool:
    """Verify an "android-key" attestation statement

    See https://www.w3.org/TR/webauthn-2/#sctn-android-key-attestation

    Also referenced: https://source.android.com/security/keystore/attestation
    """
    if not attestation_statement.sig:
        raise InvalidRegistrationResponse(
            "Attestation statement was missing signature (Android Key)")

    if not attestation_statement.alg:
        raise InvalidRegistrationResponse(
            "Attestation statement was missing algorithm (Android Key)")

    if not attestation_statement.x5c:
        raise InvalidRegistrationResponse(
            "Attestation statement was missing x5c (Android Key)")

    # Validate certificate chain
    try:
        # Include known root certificates for this attestation format
        pem_root_certs_bytes.append(google_hardware_attestation_root_1)
        pem_root_certs_bytes.append(google_hardware_attestation_root_2)

        validate_certificate_chain(
            x5c=attestation_statement.x5c,
            pem_root_certs_bytes=pem_root_certs_bytes,
        )
    except InvalidCertificateChain as err:
        raise InvalidRegistrationResponse(f"{err} (Android Key)")

    # Extract attStmt bytes from attestation_object
    attestation_dict = cbor2.loads(attestation_object)
    authenticator_data_bytes = attestation_dict["authData"]

    # Generate a hash of client_data_json
    client_data_hash = hashlib.sha256()
    client_data_hash.update(client_data_json)
    client_data_hash_bytes = client_data_hash.digest()

    verification_data = b"".join([
        authenticator_data_bytes,
        client_data_hash_bytes,
    ])

    # Verify that sig is a valid signature over the concatenation of authenticatorData
    # and clientDataHash using the public key in the first certificate in x5c with the
    # algorithm specified in alg.
    attestation_cert_bytes = attestation_statement.x5c[0]
    attestation_cert = x509.load_der_x509_certificate(attestation_cert_bytes,
                                                      default_backend())
    attestation_cert_pub_key = attestation_cert.public_key()

    try:
        verify_signature(
            public_key=attestation_cert_pub_key,
            signature_alg=attestation_statement.alg,
            signature=attestation_statement.sig,
            data=verification_data,
        )
    except InvalidSignature:
        raise InvalidRegistrationResponse(
            "Could not verify attestation statement signature (Android Key)")

    # Verify that the public key in the first certificate in x5c matches the
    # credentialPublicKey in the attestedCredentialData in authenticatorData.
    attestation_cert_pub_key_bytes = attestation_cert_pub_key.public_bytes(
        Encoding.DER,
        PublicFormat.SubjectPublicKeyInfo,
    )
    # Convert our raw public key bytes into the same format cryptography generates for
    # the cert subject key
    decoded_pub_key = decode_credential_public_key(credential_public_key)
    pub_key_crypto = decoded_public_key_to_cryptography(decoded_pub_key)
    pub_key_crypto_bytes = pub_key_crypto.public_bytes(
        Encoding.DER,
        PublicFormat.SubjectPublicKeyInfo,
    )

    if attestation_cert_pub_key_bytes != pub_key_crypto_bytes:
        raise InvalidRegistrationResponse(
            "Certificate public key did not match credential public key (Android Key)"
        )

    # Verify that the attestationChallenge field in the attestation certificate
    # extension data is identical to clientDataHash.
    ext_key_description_oid = "1.3.6.1.4.1.11129.2.1.17"
    try:
        cert_extensions = attestation_cert.extensions
        ext_key_description: Extension = cert_extensions.get_extension_for_oid(
            ObjectIdentifier(ext_key_description_oid))
    except ExtensionNotFound:
        raise InvalidRegistrationResponse(
            f"Certificate missing extension {ext_key_description_oid} (Android Key)"
        )

    # Peel apart the Extension into an UnrecognizedExtension, then the bytes we actually
    # want
    ext_value_wrapper: UnrecognizedExtension = ext_key_description.value
    ext_value: bytes = ext_value_wrapper.value
    parsed_ext = KeyDescription.load(ext_value)

    # Verify the following using the appropriate authorization list from the attestation
    # certificate extension data:
    software_enforced: AuthorizationList = parsed_ext["softwareEnforced"]
    tee_enforced: AuthorizationList = parsed_ext["teeEnforced"]

    # The AuthorizationList.allApplications field is not present on either authorization
    # list (softwareEnforced nor teeEnforced), since PublicKeyCredential MUST be scoped
    # to the RP ID.
    if software_enforced["allApplications"].native is not None:
        raise InvalidRegistrationResponse(
            "allApplications field was present in softwareEnforced (Android Key)"
        )

    if tee_enforced["allApplications"].native is not None:
        raise InvalidRegistrationResponse(
            "allApplications field was present in teeEnforced (Android Key)")

    # The value in the AuthorizationList.origin field is equal to KM_ORIGIN_GENERATED.
    origin = tee_enforced["origin"].native
    if origin != KeyOrigin.GENERATED:
        raise InvalidRegistrationResponse(
            f"teeEnforced.origin {origin} was not {KeyOrigin.GENERATED}")

    # The value in the AuthorizationList.purpose field is equal to KM_PURPOSE_SIGN.
    purpose = tee_enforced["purpose"].native
    if purpose != [KeyPurpose.SIGN]:
        raise InvalidRegistrationResponse(
            f"teeEnforced.purpose {purpose} was not [{KeyPurpose.SIGN}]")

    return True
Пример #8
0
 def _set_policy_identifier(self, value: ParsablePolicyIdentifier) -> None:
     if isinstance(value, str):
         self._policy_identifier = ObjectIdentifier(value)
     else:
         self._policy_identifier = value
Пример #9
0
class ExtendedKeyUsage(OrderedSetExtension[x509.ExtendedKeyUsage,
                                           Union[ObjectIdentifier,
                                                 str], str, ObjectIdentifier]):
    """Class representing a ExtendedKeyUsage extension."""

    key = "extended_key_usage"
    """Key used in CA_PROFILES."""

    name: ClassVar[str] = "ExtendedKeyUsage"
    oid: ClassVar[x509.ObjectIdentifier] = ExtensionOID.EXTENDED_KEY_USAGE
    CRYPTOGRAPHY_MAPPING = {
        "serverAuth": ExtendedKeyUsageOID.SERVER_AUTH,
        "clientAuth": ExtendedKeyUsageOID.CLIENT_AUTH,
        "codeSigning": ExtendedKeyUsageOID.CODE_SIGNING,
        "emailProtection": ExtendedKeyUsageOID.EMAIL_PROTECTION,
        "timeStamping": ExtendedKeyUsageOID.TIME_STAMPING,
        "OCSPSigning": ExtendedKeyUsageOID.OCSP_SIGNING,
        "anyExtendedKeyUsage": ExtendedKeyUsageOID.ANY_EXTENDED_KEY_USAGE,
        "smartcardLogon": ObjectIdentifier("1.3.6.1.4.1.311.20.2.2"),
        "msKDC": ObjectIdentifier("1.3.6.1.5.2.3.5"),
        # Defined in RFC 3280, occurs in TrustID Server A52 CA
        "ipsecEndSystem": ObjectIdentifier("1.3.6.1.5.5.7.3.5"),
        "ipsecTunnel": ObjectIdentifier("1.3.6.1.5.5.7.3.6"),
        "ipsecUser": ObjectIdentifier("1.3.6.1.5.5.7.3.7"),
    }
    _CRYPTOGRAPHY_MAPPING_REVERSED = {
        v: k
        for k, v in CRYPTOGRAPHY_MAPPING.items()
    }

    KNOWN_PARAMETERS = sorted(CRYPTOGRAPHY_MAPPING)
    """Known values that can be passed to this extension."""

    # Used by the HTML form select field
    CHOICES = (
        ("serverAuth", "SSL/TLS Web Server Authentication"),
        ("clientAuth", "SSL/TLS Web Client Authentication"),
        ("codeSigning", "Code signing"),
        ("emailProtection", "E-mail Protection (S/MIME)"),
        ("timeStamping", "Trusted Timestamping"),
        ("OCSPSigning", "OCSP Signing"),
        ("smartcardLogon", "Smart card logon"),
        ("msKDC", "Kerberos Domain Controller"),
        ("ipsecEndSystem", "IPSec EndSystem"),
        ("ipsecTunnel", "IPSec Tunnel"),
        ("ipsecUser", "IPSec User"),
        ("anyExtendedKeyUsage", "Any Extended Key Usage"),
    )

    def from_extension(self, value: x509.ExtendedKeyUsage) -> None:
        self.value = set(value)

    @property
    def extension_type(self) -> x509.ExtendedKeyUsage:
        # call serialize_item() to ensure consistent sort order
        return x509.ExtendedKeyUsage(
            sorted(self.value, key=self.serialize_item))

    def serialize_item(self, value: x509.ObjectIdentifier) -> str:
        return self._CRYPTOGRAPHY_MAPPING_REVERSED[value]

    def parse_value(self, value: Union[ObjectIdentifier,
                                       str]) -> ObjectIdentifier:
        if isinstance(value, ObjectIdentifier
                      ) and value in self._CRYPTOGRAPHY_MAPPING_REVERSED:
            return value
        if isinstance(value, str) and value in self.CRYPTOGRAPHY_MAPPING:
            return self.CRYPTOGRAPHY_MAPPING[value]
        raise ValueError("Unknown value: %s" % value)
Пример #10
0
    def makepdf(self, prev, udct, algomd, zeros, cert, **params):
        catalog = prev.trailer["/Root"]
        size = prev.trailer["/Size"]
        pages = catalog["/Pages"].getObject()
        page0ref = prev.getPage(udct.get("sigpage", 0)).indirectRef

        self._objects = []
        while len(self._objects) < size - 1:
            self._objects.append(None)


##        if params['mode'] == 'timestamp':
##            # deal with extensions
##            if '/Extensions' not in catalog:
##                extensions = po.DictionaryObject()
##            else:
##                extensions = catalog['/Extensions']
##
##            if '/ESIC' not in extensions:
##                extensions.update({
##                    po.NameObject("/ESIC"): po.DictionaryObject({
##                        po.NameObject('/BaseVersion'): po.NameObject('/1.7'),
##                        po.NameObject('/ExtensionLevel'): po.NumberObject(1),
##                        })
##                    })
##            else:
##                esic = extensions['/ESIC']
##                major, minor = esic['/BaseVersion'].lstrip('/').split('.')
##                if int(major) < 1 or int(minor) < 7:
##                    esic.update({
##                        po.NameObject('/BaseVersion'): po.NameObject('/1.7'),
##                        po.NameObject('/ExtensionLevel'): po.NumberObject(1),
##                        })
##            catalog.update({
##                po.NameObject('/Extensions'): extensions
##                })

# obj12 is the digital signature
        obj12, obj12ref = self._make_signature(
            Type=po.NameObject("/Sig"),
            SubFilter=po.NameObject("/adbe.pkcs7.detached"),
            Contents=UnencryptedBytes(zeros),
        )

        if params['mode'] == 'timestamp':
            # obj12 is a timestamp this time
            obj12.update({
                po.NameObject("/Type"):
                po.NameObject("/DocTimeStamp"),
                po.NameObject("/SubFilter"):
                po.NameObject("/ETSI.RFC3161"),
                po.NameObject("/V"):
                po.NumberObject(0),
            })
        else:
            obj12.update({
                po.NameObject("/Name"):
                po.createStringObject(udct["contact"]),
                po.NameObject("/Location"):
                po.createStringObject(udct["location"]),
                po.NameObject("/Reason"):
                po.createStringObject(udct["reason"]),
            })
            if params.get('use_signingdate'):
                obj12.update({
                    po.NameObject("/M"):
                    po.createStringObject(udct["signingdate"]),
                })

        # obj13 is a combined AcroForm Sig field with Widget annotation
        new_13 = True
        #obj13 = po.DictionaryObject()
        if udct.get('signform', False):
            # Attaching signature to existing field in AcroForm
            if "/AcroForm" in catalog:
                form = catalog["/AcroForm"].getObject()
                if "/Fields" in form:
                    fields = form["/Fields"].getObject()
                    obj13ref = [
                        f for f in fields if f.getObject()['/T'] == udct.get(
                            'sigfield', 'Signature1')
                    ][0]
                    obj13 = obj13ref.getObject()
                    self._objects[obj13ref.idnum - 1] = obj13
                    new_13 = False

        # box is coordinates of the annotation to fill
        box = udct.get("signaturebox", None)

        if new_13:
            obj13, obj13ref = self._make_sig_annotation(
                F=po.NumberObject(udct.get("sigflagsft", 132)),
                T=EncodedString(udct.get("sigfield", "Signature1")),
                Vref=obj12ref,
                Pref=page0ref,
            )
        else:
            # original obj13 is a merged SigField/SigAnnot
            # Setting /V on the AcroForm field sets the signature
            # for the field
            obj13.update({
                po.NameObject("/V"): obj12ref,
            })
            # fill the existing signature field annotation,
            # ignore any other location
            if "/Rect" in obj13:
                box = [float(f) for f in obj13["/Rect"]]

        # add an annotation if there is a field to fill
        if box is not None:
            from endesive.pdf.PyPDF2_annotate.annotations.signature import Signature
            from endesive.pdf.PyPDF2_annotate.config.appearance import Appearance
            from endesive.pdf.PyPDF2_annotate.config.location import Location
            from endesive.pdf.PyPDF2_annotate.util.geometry import identity

            x1, y1, x2, y2 = box
            annotation = Signature(
                Location(x1=x1, y1=y1, x2=x2, y2=y2, page=0),
                Appearance(),
            )
            if 'signature' in udct:
                # Plain text signature with the default font
                # text to render is contained in udct['signature']
                # font parameters are in udct['signature']['text']
                annotationtext = udct["signature"]
                wrap_text = udct.get('text', {}).get('wraptext', True)
                font_size = udct.get('text', {}).get('fontsize', 12)
                text_align = udct.get('text', {}).get('textalign', 'left')
                line_spacing = udct.get('text', {}).get('linespacing', 1.2)

                annotation.add_default_font()
                annotation.set_signature_appearance(
                    ['fill_colour', 0, 0, 0], ['font', 'default', font_size], [
                        'text_box', annotationtext, 'default', 0, 0, x2 - x1,
                        y2 - y1, font_size, wrap_text, text_align, 'middle',
                        line_spacing
                    ])
            elif 'signature_img' in udct:
                # Simple image signature, stretches to fit the box
                # image to render is contained in udct['signature_image']
                annotation.add_image(udct["signature_img"], "Image")
                annotation.set_signature_appearance([
                    'image',
                    "Image",
                    0,
                    0,
                    x2 - x1,
                    y2 - y1,
                    udct.get('signature_img_distort', True),
                    udct.get('signature_img_centred', False),
                ])
            elif 'signature_appearance' in udct:
                # Adobe-inspired signature with text and images
                # Parameters are contained in udct['signature_appearance']
                # If a field is included in the display list, that field
                # will be contained in the annotation.
                #
                # Text and border are the colour specified by outline,
                # and border is the the inset distance from the outer
                # edge of the annotation.  The R G B values range between
                # 0 and 1.
                #
                # Icon is an image to display above the background and
                # border at the left-hand side of the anntoation.  If
                # there is no text, it is centred.
                #
                # The text block is left-aligned to the right of the icon
                # image.  If there is no image, the text is left-aliged
                # with the left-hand border of the annotation
                #
                # display fields:
                #   CN, DN, date, contact, reason, location
                #
                # Dict format:
                #   appearance = dict(
                #       background = Image with alpha / None,
                #       icon = Image with alpha / None,
                #       labels = bool,
                #       display = list,
                #       software = str,
                #       outline = [R, G, B],
                #       border = int,
                #       )
                sig = {}
                for f in ('background', 'icon', 'labels', 'border', 'outline'):
                    if f in udct['signature_appearance']:
                        sig[f] = udct['signature_appearance'][f]

                toggles = udct['signature_appearance'].get('display', [])
                for f in ('contact', 'reason', 'location', 'contact',
                          'signingdate'):
                    if f in toggles:
                        sig[f] = udct.get(f, '{} unknown'.format(f))
                if 'date' in toggles:
                    sig['date'] = udct['signingdate']
                if 'CN' in toggles:
                    from cryptography.x509 import ObjectIdentifier
                    sig['CN'] = cert.subject.get_attributes_for_oid(
                        ObjectIdentifier('2.5.4.3'))[0].value
                if 'DN' in toggles:
                    sig['DN'] = cert.subject.rfc4514_string()
                annotation.simple_signature(sig)
            else:
                # Manual signature annotation creation
                #
                # Make your own appearance with an arbitrary number of
                # images and fonts
                if 'manual_images' in udct:
                    for name, img in udct['manual_images'].items():
                        annotation.add_image(img, name=name)
                if 'manual_fonts' in udct:
                    for name, path in udct['manual_fonts'].items():
                        annotation.add_ttf_font(path, name=name)
                annotation.add_default_font()
                annotation.set_signature_appearance(*udct['signature_manual'])

            pdfa = annotation.as_pdf_object(identity(), page=page0ref)
            objapn = self._extend(pdfa["/AP"]["/N"])
            objapnref = self._addObject(objapn)

            objap = po.DictionaryObject()
            objap[po.NameObject("/N")] = objapnref
            obj13.update({
                po.NameObject("/Rect"):
                po.ArrayObject([
                    po.FloatObject(x1),
                    po.FloatObject(y1),
                    po.FloatObject(x2),
                    po.FloatObject(y2),
                ]),
                po.NameObject("/AP"):
                objap,
                #po.NameObject("/SM"): po.createStringObject("TabletPOSinline"),
            })

            page0 = page0ref.getObject()
            if new_13:
                annots = po.ArrayObject([obj13ref])
                if "/Annots" in page0:
                    page0annots = page0["/Annots"]
                    if isinstance(page0annots, po.IndirectObject):
                        annots.insert(0, page0annots)
                    elif isinstance(page0annots, po.ArrayObject):
                        annots = page0annots
                        annots.append(obj13ref)
            else:
                annots = page0["/Annots"]
            page0.update({po.NameObject("/Annots"): annots})
            self._objects[page0ref.idnum - 1] = page0

        if udct.get("sigandcertify", False) and "/Perms" not in catalog:
            obj10 = po.DictionaryObject()
            obj10ref = self._addObject(obj10)
            obj11 = po.DictionaryObject()
            obj11ref = self._addObject(obj11)
            obj14 = po.DictionaryObject()
            obj14ref = self._addObject(obj14)
            obj14.update({po.NameObject("/DocMDP"): obj12ref})
            obj10.update({
                po.NameObject("/Type"):
                po.NameObject("/TransformParams"),
                po.NameObject("/P"):
                po.NumberObject(udct.get("sigflags", 3)),
                po.NameObject("/V"):
                po.NameObject("/1.2"),
            })
            obj11.update({
                po.NameObject("/Type"):
                po.NameObject("/SigRef"),
                po.NameObject("/TransformMethod"):
                po.NameObject("/DocMDP"),
                po.NameObject("/DigestMethod"):
                po.NameObject("/" + algomd.upper()),
                po.NameObject("/TransformParams"):
                obj10ref,
            })
            obj12[po.NameObject("/Reference")] = po.ArrayObject([obj11ref])
            catalog[po.NameObject("/Perms")] = obj14ref

        if "/AcroForm" in catalog:
            form = catalog["/AcroForm"].getObject()
            if "/Fields" in form:
                fields = form["/Fields"]
                old_field_names = [f.getObject()['/T'] for f in fields]
            else:
                fields = po.ArrayObject()
                old_field_names = []
            if udct.get('auto_sigfield',
                        False) and obj13['/T'] in old_field_names:
                name_base = udct.get('sigfield', 'Signature1')
                checklist = [
                    f[len(name_base):] for f in old_field_names
                    if f.startswith(name_base)
                ]
                for i in range(1, len(checklist) + 1):
                    suffix = '_{}'.format(i)
                    if suffix in checklist:
                        next

                    new_name = '{}{}'.format(name_base, suffix)
                    obj13.update(
                        {po.NameObject("/T"): EncodedString(new_name)})
                    break

            old_flags = int(form.get("/SigFlags", 0))
            new_flags = int(form.get("/SigFlags", 0)) | udct.get("sigflags", 3)
            if new_13:
                fields.append(obj13ref)
                form.update({
                    po.NameObject("/Fields"):
                    fields,
                    po.NameObject("/SigFlags"):
                    po.NumberObject(new_flags),
                })
            elif new_flags > old_flags:
                form.update({
                    po.NameObject("/SigFlags"):
                    po.NumberObject(new_flags),
                })
            formref = catalog.raw_get("/AcroForm")
            if isinstance(formref, po.IndirectObject):
                self._objects[formref.idnum - 1] = form
                form = formref
        else:
            form = po.DictionaryObject()
            form.update({
                po.NameObject("/Fields"):
                po.ArrayObject([obj13ref]),
                po.NameObject("/SigFlags"):
                po.NumberObject(udct.get("sigflags", 3)),
            })
        catalog[po.NameObject("/AcroForm")] = form

        if "/Metadata" in catalog:
            catalog[po.NameObject("/Metadata")] = catalog.raw_get("/Metadata")

        x_root = prev.trailer.raw_get("/Root")
        self._objects[x_root.idnum - 1] = catalog
        self.x_root = po.IndirectObject(x_root.idnum, 0, self)
        self.x_info = prev.trailer.get("/Info")
Пример #11
0
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with django-ca.  If not,
# see <http://www.gnu.org/licenses/>.

# pylint: disable=attribute-defined-outside-init; https://github.com/PyCQA/pylint/issues/3605
# pylint: disable=missing-function-docstring; https://github.com/PyCQA/pylint/issues/3605
"""Module providing extensions for x509 authorities supporting OpenSSH."""

import typing

from cryptography.x509 import Extension
from cryptography.x509 import ObjectIdentifier
from cryptography.x509 import UnrecognizedExtension

SSH_HOST_CA = ObjectIdentifier("1.2.22.2")
SSH_USER_CA = ObjectIdentifier("1.2.22.1")

if typing.TYPE_CHECKING:
    SshHostCaExtensionBase = Extension["SshHostCaType"]
    SshUserCaExtensionBase = Extension["SshUserCaType"]
else:
    SshHostCaExtensionBase = SshUserCaExtensionBase = Extension


class SshHostCaType(UnrecognizedExtension):
    """
    CA Certs with this extension can sign OpenSSH Host keys.
    """
    def __init__(self) -> None:
        super().__init__(SSH_HOST_CA, b"OpenSSH Host CA")
Пример #12
0
def verify_apple(
    *,
    attestation_statement: AttestationStatement,
    attestation_object: bytes,
    client_data_json: bytes,
    credential_public_key: bytes,
    pem_root_certs_bytes: List[bytes],
) -> bool:
    """
    https://www.w3.org/TR/webauthn-2/#sctn-apple-anonymous-attestation
    """

    if not attestation_statement.x5c:
        raise InvalidRegistrationResponse(
            "Attestation statement was missing x5c (Apple)")

    # Validate the certificate chain
    try:
        # Include known root certificates for this attestation format
        pem_root_certs_bytes.append(apple_webauthn_root_ca)

        validate_certificate_chain(
            x5c=attestation_statement.x5c,
            pem_root_certs_bytes=pem_root_certs_bytes,
        )
    except InvalidCertificateChain as err:
        raise InvalidRegistrationResponse(f"{err} (Apple)")

    # Concatenate authenticatorData and clientDataHash to form nonceToHash.
    attestation_dict = cbor2.loads(attestation_object)
    authenticator_data_bytes = attestation_dict["authData"]

    client_data_hash = hashlib.sha256()
    client_data_hash.update(client_data_json)
    client_data_hash_bytes = client_data_hash.digest()

    nonce_to_hash = b"".join([
        authenticator_data_bytes,
        client_data_hash_bytes,
    ])

    # Perform SHA-256 hash of nonceToHash to produce nonce.
    nonce = hashlib.sha256()
    nonce.update(nonce_to_hash)
    nonce_bytes = nonce.digest()

    # Verify that nonce equals the value of the extension with
    # OID 1.2.840.113635.100.8.2 in credCert.
    attestation_cert_bytes = attestation_statement.x5c[0]
    attestation_cert = x509.load_der_x509_certificate(attestation_cert_bytes,
                                                      default_backend())
    cert_extensions = attestation_cert.extensions

    # Still no documented name for this OID...
    ext_1_2_840_113635_100_8_2_oid = "1.2.840.113635.100.8.2"
    try:
        ext_1_2_840_113635_100_8_2: Extension = cert_extensions.get_extension_for_oid(
            ObjectIdentifier(ext_1_2_840_113635_100_8_2_oid))
    except ExtensionNotFound:
        raise InvalidRegistrationResponse(
            f"Certificate missing extension {ext_1_2_840_113635_100_8_2_oid} (Apple)"
        )

    # Peel apart the Extension into an UnrecognizedExtension, then the bytes we actually
    # want
    ext_value_wrapper: UnrecognizedExtension = ext_1_2_840_113635_100_8_2.value
    # Ignore the first six ASN.1 structure bytes that define the nonce as an
    # OCTET STRING. Should trim off '0$\xa1"\x04'
    ext_value: bytes = ext_value_wrapper.value[6:]

    if ext_value != nonce_bytes:
        raise InvalidRegistrationResponse(
            "Certificate nonce was not expected value (Apple)")

    # Verify that the credential public key equals the Subject Public Key of credCert.
    attestation_cert_pub_key = attestation_cert.public_key()
    attestation_cert_pub_key_bytes = attestation_cert_pub_key.public_bytes(
        Encoding.DER,
        PublicFormat.SubjectPublicKeyInfo,
    )
    # Convert our raw public key bytes into the same format cryptography generates for
    # the cert subject key
    decoded_pub_key = decode_credential_public_key(credential_public_key)
    pub_key_crypto = decoded_public_key_to_cryptography(decoded_pub_key)
    pub_key_crypto_bytes = pub_key_crypto.public_bytes(
        Encoding.DER,
        PublicFormat.SubjectPublicKeyInfo,
    )

    if attestation_cert_pub_key_bytes != pub_key_crypto_bytes:
        raise InvalidRegistrationResponse(
            "Certificate public key did not match credential public key (Apple)"
        )

    return True