Example #1
0
    def sign(self, confirm=False):
        url, headers = self.merge_and_headers(self.url)
        try:
            check_result = self.simple_check(
                url,
                token=headers.get("X-TOKEN"),
                session=self.session,
            )
            attestation = check_result["attestation"]
            errored = set(map(lambda x: x[0], check_result["errors"]))
            key_list = check_result["key_list"]
            key_hashes = set(map(lambda x: x[0], key_list))
        except CheckError as exc:
            if not exc.key_list or not exc.errored or not exc.attestation:
                raise exc
            attestation = exc.attestation
            errored = set(map(lambda x: x[0], exc.errored))
            key_list = exc.key_list
            key_hashes = set(map(lambda x: x[0], key_list))

        if self.hash_key_public not in key_hashes:
            raise CheckError("Key is not part of chain")

        if not confirm:
            return (self.hash_key_public in errored, key_list)
        # change to update url
        postbox_update = merge_get_url(replace_action(self.url, "update/"),
                                       raw="embed",
                                       search="\x1etype=PostBox\x1e")
        # retrieve csrftoken
        response = self.session.get(postbox_update, headers=headers)
        graph = Graph()
        graph.parse(data=response.content, format="html")
        csrftoken = list(
            graph.objects(predicate=spkcgraph["csrftoken"]))[0].toPython()

        fields = dict(
            map(
                lambda x: (x[0].toPython(), x[1].toPython()),
                graph.query(
                    """
                    SELECT DISTINCT ?fieldname ?value
                    WHERE {
                        ?base spkc:fieldname ?fieldname ;
                              spkc:value ?value .
                    }
                """,
                    initNs={"spkc": spkcgraph},
                )))

        fields["signatures"] = []
        own_signature = None
        for key in self.client_list:
            if key[0] != self.hash_key_public:
                signature = key[2]
            else:
                # currently only one priv key is supported
                signature = self.priv_key.sign(
                    attestation,
                    padding.PSS(mgf=padding.MGF1(self.hash_algo),
                                salt_length=padding.PSS.MAX_LENGTH),
                    self.hash_algo)
            if signature:
                fields["signatures"].append({
                    "hash":
                    f"{self.hash_algo.name}={key[0].hex()}",
                    "signature":
                    "{}={}".format(self.hash_algo.name,
                                   base64.b64encode(signature).decode("ascii"))
                })
            if key[0] == self.hash_key_public:
                own_signature = fields["signatures"][-1]["signature"]

        fields["signatures"] = json.dumps(fields["signatures"])
        # update
        response = self.session.post(postbox_update,
                                     data=fields,
                                     headers={
                                         "X-CSRFToken": csrftoken,
                                         **headers
                                     })
        try:
            response.raise_for_status()
            graph = Graph()
            graph.parse(data=response.content, format="html")
            vals = set(extract_property(graph, "signature").values())
            breakpoint()
            if own_signature not in vals:
                raise SrcException("could not update signature", own_signature)
        except SrcException as exc:
            raise exc
        except Exception as exc:
            raise SrcException("could not update signature",
                               own_signature) from exc
        return (self.hash_key_public in errored, key_list)
Example #2
0
    def send(self,
             inp,
             receivers,
             headers=b"\n",
             mode=SendMethod.shared,
             aes_key=None):
        if not self.ok:
            raise NotReady()
        if isinstance(receivers, str):
            receivers = [receivers]

        if isinstance(inp, (bytes, memoryview)):
            inp = io.BytesIO(bytes(inp))
        elif isinstance(inp, str):
            inp = io.BytesIO(inp.encode("utf8"))

        # 256 bit
        if not aes_key:
            aes_key = os.urandom(32)
        assert len(aes_key) == 32
        nonce = os.urandom(13)
        fencryptor = Cipher(algorithms.AES(aes_key),
                            modes.GCM(nonce),
                            backend=default_backend()).encryptor()
        src_key_list = {}
        if mode == SendMethod.stealth:
            pass
        elif mode == SendMethod.private:
            enc = self.priv_key.public_key().encrypt(
                aes_key,
                padding.OAEP(mgf=padding.MGF1(algorithm=self.hash_algo),
                             algorithm=self.hash_algo,
                             label=None))
            # encrypt decryption key
            src_key_list["%s=%s" % (self.hash_algo.name, self.pub_key_hash
                                    )] = base64.b64encode(enc).decode("ascii")
        elif mode == SendMethod.shared:
            for k in self.client_list:
                enc = k[1].encrypt(
                    aes_key,
                    padding.OAEP(mgf=padding.MGF1(algorithm=self.hash_algo),
                                 algorithm=self.hash_algo,
                                 label=None))
                # encrypt decryption key
                src_key_list["%s=%s" %
                             (self.hash_algo.name, k[0].hex()
                              )] = base64.b64encode(enc).decode("ascii")
        else:
            raise NotImplementedError()

        # remove raw as we parse html
        message_create_url, src_headers = self.merge_and_headers(
            replace_action(self.component_url, "add/MessageContent/"),
            raw=None)
        response = self.session.get(message_create_url, headers=src_headers)
        try:
            response.raise_for_status()
        except Exception as exc:
            raise SrcException("retrieval csrftoken failed",
                               response.text) from exc
        g = Graph()
        g.parse(data=response.content, format="html")
        csrftoken = list(g.objects(predicate=spkcgraph["csrftoken"]))[0]
        # create message object
        response = self.session.post(
            message_create_url,
            data={
                "own_hash": self.hash_key_public,
                "key_list": json.dumps(src_key_list),
                "amount_tokens": len(receivers)
            },
            headers={
                "X-CSRFToken": csrftoken,
                **src_headers  # only for src
            },
            files={
                "encrypted_content": EncryptedFile(fencryptor, inp, nonce,
                                                   headers)
            })
        try:
            response.raise_for_status()
        except Exception as exc:
            raise SrcException("Message creation failed",
                               response.text) from exc
        if message_create_url == response.url:
            raise SrcException("Message creation failed", response.text)
        g = Graph()
        g.parse(data=response.content, format="html")
        fetch_url = list(
            map(
                lambda x: x.value,
                g.query("""
                SELECT ?value
                WHERE {
                    ?property spkc:name ?name ;
                            spkc:value ?value .
                }
            """,
                        initNs={"spkc": spkcgraph},
                        initBindings={
                            "name": Literal("fetch_url", datatype=XSD.string)
                        })))

        tokens = list(
            map(
                lambda x: x.value,
                g.query("""
                SELECT ?value
                WHERE {
                    ?property spkc:name ?name ;
                            spkc:value ?value .
                }
            """,
                        initNs={"spkc": spkcgraph},
                        initBindings={
                            "name": Literal("tokens", datatype=XSD.string)
                        })))

        if not fetch_url or not tokens:
            raise SrcException("Message creation failed", response.text)
        fetch_url = fetch_url[0].toPython()
        exceptions = []
        final_fetch_urls = []
        for receiver, token in zip(receivers, tokens):
            furl = merge_get_url(fetch_url, token=token.toPython())
            try:
                self._send_dest(aes_key, furl, receiver)
                final_fetch_urls.append(furl)
            except Exception as exc:
                exceptions.append(exc)
                # for autoremoval simulate access
                self.session.get(furl)
        return exceptions, final_fetch_urls, aes_key
Example #3
0
    def receive(self,
                message_id,
                outfp=None,
                access_method=AccessMethod.view,
                extra_key_hashes=None,
                max_size=None):
        if not self.ok:
            raise NotReady()

        merged_url, headers = self.merge_and_headers(self.url, raw="embed")

        response = self.session.get(merge_get_url(self.url, raw="embed"),
                                    headers=headers)
        response.raise_for_status()
        graph = Graph()
        graph.parse(data=response.content, format="turtle")
        self.retrieve_missing(graph, merged_url)
        result = list(
            graph.query(
                """
                SELECT DISTINCT ?base ?hash_algorithm ?type
                WHERE {
                    ?base a <https://spkcspider.net/static/schemes/spkcgraph#spkc:Content> ;
                                  spkc:type ?type ;
                                  spkc:properties ?propalg , ?propid .
                    ?propid spkc:name ?idname ;
                            spkc:value ?idvalue .
                    ?propalg spkc:name ?algname ;
                             spkc:value ?hash_algorithm .
                }
            """,  # noqa E501
                initNs={"spkc": spkcgraph},
                initBindings={
                    "idvalue": Literal(message_id),
                    "algname": Literal("hash_algorithm", datatype=XSD.string),
                    "idname": Literal("id", datatype=XSD.string),
                }))
        if not result or result[0].type.toPython() not in {
                "WebReference", "MessageContent"
        }:
            raise SrcException("No Message")
        # every object has it's own copy of the hash algorithm, used
        hash_algo = getattr(hashes, result[0].hash_algorithm.upper())()

        if not outfp:
            outfp = tempfile.TempFile()

        if hash_algo == self.hash_algo:
            pub_key_hashalg = "%s=%s" % (hash_algo.name,
                                         self.hash_key_public.hex())
        else:
            digest = hashes.Hash(hash_algo, backend=default_backend())
            digest.update(self.pem_key_public)
            pub_key_hashalg = "%s=%s" % (hash_algo.name,
                                         digest.finalize().hex())

        key_hashes = list()
        if extra_key_hashes:
            extra_key_hashes = set(extra_key_hashes)
            extra_key_hashes.discard(pub_key_hashalg)
            for key in self.client_list:
                if key[0] in extra_key_hashes:
                    if hash_algo == self.hash_algo:
                        key_hashalg = "%s=%s" % (hash_algo.name, key[0])
                    else:
                        digest = hashes.Hash(hash_algo,
                                             backend=default_backend())
                        digest.update(key[1])
                        key_hashalg = "%s=%s" % (hash_algo.name,
                                                 digest.finalize().hex())
                    key_hashes.append(key_hashalg)

        if access_method == AccessMethod.view:
            key_hashes.append(pub_key_hashalg)

        retrieve_url, headers = self.merge_and_headers(
            replace_action(
                result[0].base, "bypass/" if
                (access_method == AccessMethod.bypass) else "message/"))
        data = {}
        if access_method != AccessMethod.bypass:
            data.update({"max_size": max_size or "", "keyhash": key_hashes})
        response = self.session.post(retrieve_url,
                                     stream=True,
                                     headers=headers,
                                     data=data)
        try:
            response.raise_for_status()
        except Exception as exc:
            raise DestException("Message retrieval failed",
                                response.text) from exc
        key_list = json.loads(response.headers["X-KEYLIST"])
        key = key_list.get(pub_key_hashalg, None)
        if not key:
            raise WrongRecipient("message not for me")
        decrypted_key = self.priv_key.decrypt(
            base64.b64decode(key),
            padding.OAEP(mgf=padding.MGF1(algorithm=hash_algo),
                         algorithm=hash_algo,
                         label=None))

        headblock = b""
        fdecryptor = None
        eparser = emailparser.BytesFeedParser(policy=policy.default)
        headers = None
        for chunk in response.iter_content(chunk_size=256):
            blob = None
            if not fdecryptor:
                headblock = b"%b%b" % (headblock, chunk)
                if b"\0" in headblock:
                    nonce, headblock = headblock.split(b"\0", 1)
                    nonce = base64.b64decode(nonce)
                    fdecryptor = Cipher(algorithms.AES(decrypted_key),
                                        modes.GCM(nonce),
                                        backend=default_backend()).decryptor()
                    blob = fdecryptor.update(headblock[:-16])
                    headblock = headblock[-16:]
                else:
                    continue
            else:
                blob = fdecryptor.update(b"%b%b" % (headblock, chunk[:-16]))
                headblock = chunk[-16:]
            if not headers:
                if b"\n\n" not in blob:
                    eparser.feed(blob)
                    continue
                headersrest, blob = blob.split(b"\n\n", 1)
                eparser.feed(headersrest)
                headers = eparser.close()
                # check  what to do
                t = headers.get("SPKC-Type", MessageType.email)
                if t == MessageType.email:
                    outfp.write(
                        headers.as_bytes(unixfrom=True, policy=policy.SMTP))
            outfp.write(blob)
        outfp.write(fdecryptor.finalize_with_tag(headblock))
        return outfp, headers, decrypted_key
Example #4
0
    def _send_dest(self, aes_key, fetch_url, dest):
        dest_url = merge_get_url(dest,
                                 raw="embed",
                                 search="\x1etype=PostBox\x1e")
        response_dest = self.session.get(dest_url, timeout=60)
        try:
            response_dest.raise_for_status()
            g_dest = Graph()
            g_dest.parse(data=response_dest.content, format="turtle")
            self.retrieve_missing(g_dest, dest_url)
        except Exception as exc:
            raise DestException("postbox retrieval failed") from exc

        dest_postboxes = get_postboxes(g_dest)
        if len(dest_postboxes) != 1:
            raise DestException("No postbox found/more than one found")
        dest_postbox_url, dest_options = next(iter(dest_postboxes.items()))
        webref_url = replace_action(dest_postbox_url, "push_webref/")
        attestation = dest_options["attestation"]

        bdomain = dest_postbox_url.split("?", 1)[0]
        result_dest, errors, dest_keys = self.attestation_checker.check(
            bdomain,
            map(lambda x: (x["key"], x["signature"]),
                dest_options["signatures"].values()),
            attestation=attestation,
            algo=dest_options["hash_algorithm"],
            auto_add=True)
        if result_dest == AttestationResult.domain_unknown:
            logger.info("add domain: %s", bdomain)
            self.attestation_checker.attestation.add(
                bdomain,
                dest_keys,
                algo=dest_options["hash_algorithm"],
                embed=True)
        elif result_dest == AttestationResult.error:
            if len(dest_keys) == 0:
                raise DestException("No keys found")
            raise DestSecurityException("dest contains invalid keys", errors)

        dest_key_list = {}

        for k in dest_keys:
            enc = k[1].encrypt(
                aes_key,
                padding.OAEP(
                    mgf=padding.MGF1(algorithm=dest_options["hash_algorithm"]),
                    algorithm=dest_options["hash_algorithm"],
                    label=None))
            # encrypt decryption key
            dest_key_list["%s=%s" %
                          (dest_options["hash_algorithm"].name,
                           k[0].hex())] = base64.b64encode(enc).decode("ascii")

        try:
            response_dest = self.session.post(webref_url,
                                              data={
                                                  "url":
                                                  fetch_url,
                                                  "key_list":
                                                  json.dumps(dest_key_list)
                                              },
                                              timeout=60)
            response_dest.raise_for_status()
        except Exception as exc:
            raise DestException("post webref failed") from exc
    def update_content(self,
                       inp,
                       newob,
                       update_url=None,
                       x_token=None,
                       timeout=60,
                       session=None,
                       hash_algo=None):
        """
        [summary]

        Arguments:
            inp {url,graph,keys} -- [description]
            update_url {[type]} -- [description]
            newob {[type]} -- [description]

        Keyword Arguments:
            x_token {[type]} -- [description] (default: {None})
            timeout {int} -- [description] (default: {60})
            session {[type]} -- [description] (default: {None})
            hash_algo {} -- (default: {None})
        """
        if not session:
            session = requests.Session()
        if isinstance(inp, (list, tuple)):
            # be careful to not include thirdparty keys if incompatible
            keys = set(inp)
            assert hash_algo and update_url
        else:
            graph, update_url = self.retrieve_missing(
                inp, ["\x1etype=PublicKey\x1e"],
                x_token=x_token,
                timeout=timeout,
                session=session)
            info = extract_property(graph)
            allow_thirdparty = "\x1eunlisted\x1e" not in info
            keys, hash_algo = map_keys(graph, hash_algo=hash_algo or None)
            if not allow_thirdparty:
                keys = filter(lambda x: x["thirdparty"], keys.values())
            keys = list(map(lambda x: x["pubkey"], keys))

        update_url = replace_action(update_url, "update/")
        update_graph = self.retrieve_missing(update_url, [],
                                             x_token=None,
                                             timeout=timeout,
                                             session=session)
        csrftoken = list(update_graph.objects(
            predicate=spkcgraph["csrftoken"]))[0].toPython()

        body = dict(newob["unencrypted"]["values"])
        files = dict(newob["unencrypted"]["files"])
        nonce = os.urandom(13)
        nonce_b64 = base64.b64encode(nonce)
        aes_key = newob.get("aes_key") or os.urandom(32)
        nonce_name = newob.get("nonce_name")
        key_list_name = newob.get("key_list_name", "key_list")
        body[nonce_name] = nonce

        cipher = Cipher(algorithms.AES(aes_key),
                        modes.GCM(nonce),
                        backend=default_backend())
        for k, item in newob["encrypted"]["values"].items():
            assert nonce_name != k
            e = cipher.encryptor()
            if not isinstance(item, (list, set, tuple)):
                item = [item]
            body[k] = []
            for el in item:
                if isinstance(el, str):
                    el = el.encode("utf8")
                el = e.update(el)
                if not nonce_name:
                    el = b"%s\0%s" % (nonce_b64, el)
                body[k].append(el)

        for k, item in newob["encrypted"]["files"].items():
            assert nonce_name != k
            files[k] = EncryptedFile(cipher.encryptor(),
                                     item,
                                     nonce=nonce if nonce_name else None)
        body[key_list_name] = {}

        for k in keys:
            enc = k[1].encrypt(
                aes_key,
                padding.OAEP(mgf=padding.MGF1(algorithm=hash_algo),
                             algorithm=hash_algo,
                             label=None))
            # encrypt decryption key
            body[key_list_name]["%s=%s" %
                                (hash_algo.name, k[0].hex()
                                 )] = base64.b64encode(enc).decode("ascii")
        body[key_list_name] = json.dumps(body[key_list_name])
        # create message object
        response = session.post(update_url,
                                data=body,
                                headers={
                                    "X-CSRFToken": csrftoken,
                                },
                                files=files)
        try:
            response.raise_for_status()
        except Exception as exc:
            raise HttpError("Message creation failed", response.text) from exc
Example #6
0
def action_send(argv, priv_key, pub_key_hash, src_keys, session, g_src):
    postboxes = get_postboxes(g_src)
    if len(postboxes) != 1:
        parser.exit(1, "Source cannot create messages, logged in?")
    component_uriref, src_options = next(postboxes.items())

    dest_url = merge_get_url(
        argv.dest, raw="embed", search="\x1etype=PostBox\x1e"
    )
    response_dest = session.get(dest_url)
    if not response_dest.ok:
        logger.info("Dest returned error: %s", response_dest.text)
        parser.exit(1, "retrieval failed, invalid url?\n")
    g_dest = Graph()
    g_dest.parse(data=response_dest.content, format="turtle")
    for page in get_pages(g_dest)[1]:
        with session.get(
            merge_get_url(dest_url, page=page)
        ) as response:
            response.raise_for_status()
            g_dest.parse(data=response.content, format="turtle")
    dest_postboxes = get_postboxes(g_dest)
    if len(dest_postboxes) != 1:
        parser.exit(1, "Dest has no/too many postboxes")
    dest_postbox_url, dest_options = next(dest_postboxes.items())
    webref_url = replace_action(dest_postbox_url, "push_webref/")
    dest_hash_algo = dest_options["hash_algorithm"]
    attestation = dest_options["attestation"]

    bdomain = dest_postbox_url.split("?", 1)[0]
    result_dest, _, dest_keys = argv.attestation.check(
        bdomain,
        map(
            lambda x: (x["key"], x["signature"]),
            dest_options["signatures"].values()
        ), attestation=attestation,
        algo=dest_hash_algo
    )
    if result_dest == AttestationResult.domain_unknown:
        logger.info("add domain: %s", bdomain)
        argv.attestation.add(
            bdomain,
            dest_keys,
            algo=dest_hash_algo
        )
    elif result_dest == AttestationResult.error:
        logger.critical("Dest base url contains invalid keys.")
        parser.exit(1, "dest contains invalid keys\n")

    # 256 bit
    aes_key = os.urandom(32)
    nonce = os.urandom(13)
    fencryptor = Cipher(
        algorithms.AES(aes_key),
        modes.GCM(nonce),
        backend=default_backend()
    ).encryptor()
    src_key_list = {}
    dest_key_list = {}
    if argv.stealth:
        pass
    elif src_options["shared"]:
        for k in src_keys:
            enc = k[1].encrypt(
                aes_key,
                padding.OAEP(
                    mgf=padding.MGF1(algorithm=argv.src_hash_algo),
                    algorithm=argv.src_hash_algo, label=None
                )
            )
            # encrypt decryption key
            src_key_list[
                "%s=%s" % (argv.src_hash_algo.name, k[0].hex())
            ] = base64.b64encode(enc).decode("ascii")
    else:
        enc = priv_key.public_key().encrypt(
            aes_key,
            padding.OAEP(
                mgf=padding.MGF1(algorithm=argv.src_hash_algo),
                algorithm=argv.src_hash_algo, label=None
            )
        )
        # encrypt decryption key
        src_key_list[
            "%s=%s" % (argv.src_hash_algo.name, pub_key_hash)
        ] = base64.b64encode(enc).decode("ascii")

    for k in dest_keys:
        enc = k[1].encrypt(
            aes_key,
            padding.OAEP(
                mgf=padding.MGF1(algorithm=dest_hash_algo),
                algorithm=dest_hash_algo, label=None
            )
        )
        # encrypt decryption key
        dest_key_list[
            "%s=%s" % (dest_hash_algo.name, k[0].hex())
        ] = base64.b64encode(enc).decode("ascii")
    headers = b"SPKC-Type: %b\n" % MessageType.file

    # remove raw as we parse html
    message_create_url = merge_get_url(
        replace_action(str(component_uriref), "add/MessageContent/"), raw=None
    )
    response = session.get(
        message_create_url, headers={
            "X-TOKEN": argv.token
        }
    )
    if not response.ok:
        logger.error("retrieval csrftoken failed: %s", response.text)
        parser.exit(1, "retrieval csrftoken failed: %s" % response.text)
    g = Graph()
    g.parse(data=response.content, format="html")
    csrftoken = list(g.objects(predicate=spkcgraph["csrftoken"]))[0]
    # create message object
    response = session.post(
        message_create_url, data={
            "own_hash": pub_key_hash,
            "key_list": json.dumps(src_key_list),
            "amount_tokens": 1
        }, headers={
            "X-CSRFToken": csrftoken,
            "X-TOKEN": argv.token  # only for src
        },
        files={
            "encrypted_content": EncryptedFile(
                fencryptor, argv.file, nonce, headers
            )
        }
    )
    if not response.ok or message_create_url == response.url:
        logger.error("Message creation failed: %s", response.text)
        parser.exit(1, "Message creation failed: %s" % response.text)
    g = Graph()
    g.parse(data=response.content, format="html")
    fetch_url = list(map(lambda x: x.value, g.query(
        """
            SELECT ?value
            WHERE {
                ?property spkc:name ?name ;
                          spkc:value ?value .
            }
        """,
        initNs={"spkc": spkcgraph},
        initBindings={
            "name": Literal(
                "fetch_url", datatype=XSD.string
            )
        }
    )))

    tokens = list(map(lambda x: x.value, g.query(
        """
            SELECT ?value
            WHERE {
                ?property spkc:name ?name ;
                          spkc:value ?value .
            }
        """,
        initNs={"spkc": spkcgraph},
        initBindings={
            "name": Literal(
                "tokens", datatype=XSD.string
            )
        }
    )))

    if not fetch_url or not tokens:
        logger.error("Message creation failed: %s", response.text)
        parser.exit(1, "Message creation failed")
    # extract url
    response_dest = session.post(
        webref_url, data={
            "url": merge_get_url(fetch_url[0], token=str(tokens[0])),
            "key_list": json.dumps(dest_key_list)
        }
    )

    if not response_dest.ok:
        logger.error("Sending message failed: %s", response_dest.text)
        parser.exit(1, "Sending message failed")
Example #7
0
def action_check(argv, priv_key, pub_key_hash, session, g):
    src = {}

    postbox = None

    for i in g.query(
        """
            SELECT DISTINCT
            ?postbox ?key_base ?key_base_prop ?key_name ?key_value
            WHERE {
                ?postbox spkc:properties ?postbox_prop .
                ?postbox_prop spkc:name ?postbox_prop_name .
                ?postbox_prop spkc:value ?key_base .
                ?key_base spkc:properties ?key_base_prop .
                ?key_base_prop spkc:name ?key_name ;
                               spkc:value ?key_value .
            }
        """,
        initNs={"spkc": spkcgraph},
        initBindings={
            "postbox_prop_name": Literal(
                "signatures", datatype=XSD.string
            )
        }
    ):
        postbox = i.postbox
        src.setdefault(str(i.key_base), {})
        src[str(i.key_base)][str(i.key_name)] = i.key_value

    src_activator_value, errored, key_list = argv.attestation.check_signatures(
        map(
            lambda x: (x["key"], x["signature"]),
            src.values()
        ), algo=argv.src_hash_algo
    )
    tmp = list(g.query(
        """
            SELECT DISTINCT ?value
            WHERE {
                ?base spkc:name ?name .
                ?base spkc:value ?value .
            }
        """,
        initNs={"spkc": spkcgraph},
        initBindings={
            "name": Literal(
                "attestation", datatype=XSD.string
            ),

        }
    ))
    if tmp[0][0].toPython() != src_activator_value:
        return "activator doesn't match shown activator"
    if errored:
        pub_key_hash_bin = bytes.fromhex(pub_key_hash)
        can_fix = set(filter(
            lambda x: x == pub_key_hash_bin,
            map(lambda x: x[0], errored)
        ))
        if not argv.fix:
            if can_fix and postbox:
                print("Can fix signature")
        else:
            if not can_fix or not postbox:
                return ", ".join(map(lambda x: x.hex(), errored))

            postbox_update = replace_action(
                postbox, "update/"
            )
            # retrieve token
            response = session.get(
                postbox_update, headers={
                    "X-TOKEN": argv.token
                }
            )
            g_token = Graph()
            g_token.parse(data=response.content, format="html")
            csrftoken = list(g_token.objects(
                predicate=spkcgraph["csrftoken"])
            )[0].toPython()
            fields = dict(map(
                lambda x: (x[0].toPython(), x[1].toPython()),
                g_token.query(
                    """
                        SELECT DISTINCT ?fieldname ?value
                        WHERE {
                            ?base spkc:fieldname ?fieldname .
                            ?base spkc:value ?value .
                        }
                    """,
                    initNs={"spkc": spkcgraph},
                )
            ))

            fields["signatures"] = []
            for key in key_list:
                if key[0] not in can_fix:
                    signature = key[2]
                else:
                    # currently only one priv key is supported
                    signature = priv_key.sign(
                        src_activator_value,
                        padding.PSS(
                            mgf=padding.MGF1(argv.src_hash_algo),
                            salt_length=padding.PSS.MAX_LENGTH
                        ),
                        argv.src_hash_algo
                    )
                if signature:
                    fields["signatures"].append(
                        {
                            "hash":
                                f"{argv.src_hash_algo.name}={key[0].hex()}",
                            "signature": "{}={}".format(
                                argv.src_hash_algo.name,
                                base64.b64encode(
                                    signature
                                ).decode("ascii")
                            )
                        }
                    )

            fields["signatures"] = json.dumps(fields["signatures"])
            # update
            response = session.post(
                postbox_update, data=fields, headers={
                    "X-CSRFToken": csrftoken,
                    "X-TOKEN": argv.token
                }
            )
            if not response.ok:
                logger.error("Repair failed: %s", response.text)
                return ", ".join(map(lambda x: x[0].hex(), errored))
            logger.info("Repair succeeded")
            errored = list(filter(lambda x: x not in can_fix, errored))
            if errored == 0:
                return True
        return ", ".join(map(lambda x: x[0].hex(), errored))
    return True
Example #8
0
def action_view(argv, priv_key, pem_public, own_url, session, g_message):
    if argv.message_id is not None:
        assert isinstance(argv.message_id, int)
        result = list(g_message.query(
            """
                SELECT DISTINCT ?base ?hash_algorithm ?type
                WHERE {
                    ?base a <https://spkcspider.net/static/schemes/spkcgraph#spkc:Content> ;
                                  spkc:type ?type ;
                                  spkc:properties ?propalg , ?propid .
                    ?propid spkc:name ?idname ;
                            spkc:value ?idvalue .
                    ?propalg spkc:name ?algname ;
                             spkc:value ?hash_algorithm .
                }
            """,  # noqa E501
            initNs={"spkc": spkcgraph},
            initBindings={
                "idvalue": Literal(argv.message_id),
                "algname": Literal(
                    "hash_algorithm", datatype=XSD.string
                ),
                "idname": Literal(
                    "id", datatype=XSD.string
                ),

            }
        ))
        if not result or result[0].type.toPython() not in {
            "WebReference", "MessageContent"
        }:
            parser.exit(0, "message not found\n")
        pub_key_hasher = getattr(
            hashes, result[0].hash_algorithm.upper()
        )()

        digest = hashes.Hash(pub_key_hasher, backend=default_backend())
        digest.update(pem_public)
        pub_key_hashalg = "%s=%s" % (
            pub_key_hasher.name,
            digest.finalize().hex()
        )
        retrieve_url = merge_get_url(
            replace_action(
                result[0].base, "message/"
            )
        )
        data = {
            "max_size": argv.max
        }
        if argv.action == "view":
            data["keyhash"] = pub_key_hashalg
        response = session.post(
            retrieve_url, stream=True, headers={
                "X-TOKEN": argv.token
            }, data=data
        )
        if not response.ok:
            logger.info("Message retrieval failed: %s", response.text)
            parser.exit(0, "message could not be fetched\n")
        key_list = json.loads(response.headers["X-KEYLIST"])
        key = key_list.get(pub_key_hashalg, None)
        if not key:
            parser.exit(0, "message not for me\n")
        decrypted_key = priv_key.decrypt(
            base64.b64decode(key),
            padding.OAEP(
                mgf=padding.MGF1(algorithm=argv.src_hash_algo),
                algorithm=argv.src_hash_algo,
                label=None
            )
        )

        headblock = b""
        fdecryptor = None
        eparser = emailparser.BytesFeedParser(policy=policy.default)
        headers = None
        for chunk in response.iter_content(chunk_size=256):
            blob = None
            if not fdecryptor:
                headblock = b"%b%b" % (headblock, chunk)
                if b"\0" in headblock:
                    nonce, headblock = headblock.split(b"\0", 1)
                    nonce = base64.b64decode(nonce)
                    fdecryptor = Cipher(
                        algorithms.AES(decrypted_key),
                        modes.GCM(nonce),
                        backend=default_backend()
                    ).decryptor()
                    blob = fdecryptor.update(headblock[:-16])
                    headblock = headblock[-16:]
                else:
                    continue
            else:
                blob = fdecryptor.update(
                    b"%b%b" % (headblock, chunk[:-16])
                )
                headblock = chunk[-16:]
            if not headers:
                if b"\n\n" not in blob:
                    eparser.feed(blob)
                    continue
                headersrest, blob = blob.split(b"\n\n", 1)
                eparser.feed(headersrest)
                headers = eparser.close()
                # check  what to do
                t = headers.get("SPKC-Type", MessageType.email)
                if t == MessageType.email:
                    argv.file.write(headers.as_bytes(
                        unixfrom=True,
                        policy=policy.SMTP
                    ))
            argv.file.write(blob)
        argv.file.write(fdecryptor.finalize_with_tag(headblock))
    else:
        queried_webrefs = {}
        queried_messages = {}
        for i in g_message.query(
            """
            SELECT DISTINCT ?base ?idvalue ?namevalue ?type
            WHERE {
                ?base a <https://spkcspider.net/static/schemes/spkcgraph#spkc:Content> ;
                   spkc:type ?type ;
                   spkc:properties ?propname , ?propid .
                ?propid spkc:name ?idname ;
                        spkc:value ?idvalue .
                ?propname spkc:name ?namename ;
                          spkc:value ?namevalue .
            }
        """,  # noqa E501
            initNs={"spkc": spkcgraph},
            initBindings={
                "idname": Literal(
                    "id", datatype=XSD.string
                ),
                "namename": Literal(
                    "name", datatype=XSD.string
                ),

            }
        ):
            if i.type.toPython() == "WebReference":
                queried = queried_webrefs
            elif i.type.toPython() == "MessageContent":
                queried = queried_messages
            else:
                continue
            queried.setdefault(str(i.base), {})
            queried[str(i.base)]["id"] = i.idvalue
            queried[str(i.base)]["name"] = i.namevalue
        print("Received Messages:")
        for i in sorted(queried_webrefs.values(), key=lambda x: x["id"]):
            print(i["id"], i["name"])

        print("Own Messages:")
        for i in sorted(queried_messages.values(), key=lambda x: x["id"]):
            print(i["id"], i["name"])