GPG with Confirmation

I got tired of Evolution email client giving me those horrid error messages when ever I try to email someone who’s key isn’t in my current list of keys.

The design of this is appallingly bad. It discourages the use of GPG rather than encouraging the importing of keys and it makes no mention of helping you acquire keys if possible. It also allows for no additional or optional footer to explain to the recipient that their message couldn’t be encrypted because they don’t use GPG.

While I couldn’t do much about the later, without hacking on the evolution codebase directly. I did do a bit of hacking on the former with a gpg middlware. Yes, when I say hack, I mean HACK. A dangerous and potentially devastating way of wrapping the gpg binary with my own python script that could intercept the evolution call and do work to search, display and add keys to encourage the use of encryption overall.

The design was simple. When we are asked to encrypt for a person who we don’t have the keys for, we do a search. The results are shown in a GUI to the user and they can select a key to use. This then is added to the key ring and used to encrypt the email.

This setup allows for experimentation with user prompting and workflow. It’s not something I would recommend be installed on user’s computers. But for designers and developers, this sort of match-stick making is a valuable platform to build, try, test and rebuild quickly.

I use zenity for the user interface. This is a Gtk command line tool that lets you launch a window from the command line and the interface is good enough to support photos in lists and returning which item was selected. Very cool.

Bellow you will find the script I created for this hack, this is saved to /usr/bin/gpg and gpg is moved to gpg.orig:

#!/usr/bin/python
#
# Wrap the gpg command to provide evolution with a bit of extra functionality
# This is certainly a hack and you should feel very bad about using it.
#
# Public Domain, Authored by Martin Owens  2016
#
import os
import sys
import atexit

from collections import defaultdict
from subprocess import Popen, PIPE, call
from tempfile import mkdtemp, mktemp
from datetime import date
from shutil import rmtree

to_date = lambda d: date(*[int(p) for p in d.split('-')])


class GPG(object):
    keyserver = 'hkp://pgp.mit.edu'
    remote_commands = ['--search-keys', '--recv-keys']

    def __init__(self, cmd='/usr/bin/gpg', local=False):
        self.command = cmd
        self.photos = []
        self.local = local
        self.homedir = mkdtemp() if local else None
        atexit.register(self.at_exit)

    def at_exit(self):
        """Remove any temporary files and cleanup"""
        # Clean up any used local home directory (only if it's local)
        if self.local and self.homedir and os.path.isdir(self.homedir):
            rmtree(self.homedir)

        # Clean up any downloaded photo-ids
        for photo in self.photos:
            if os.path.isfile(photo):
                os.unlink(photo)
            try:
                os.rmdir(os.path.dirname(photo))
            except OSError:
                pass

    def __call__(self, *args):
        """Call gpg command for result"""
        # Add key server if required
        if any([cmd in args for cmd in self.remote_commands]):
            args = ('--keyserver', self.keyserver) + args
        if self.homedir:
            args = ('--homedir', self.homedir) + args

        command = Popen([self.command, '--batch'] + list(args), stdout=PIPE)
        (out, err) = command.communicate()
        self.status = command.returncode
        return out

    def list_keys(self, *keys, **options):
        """Returns a list of keys (with photos if needed)"""
        with_photos = options.get('photos', False)
        args = ()
        if with_photos:
            args += ('--list-options', 'show-photos',
                     '--photo-viewer', 'echo PHOTO:%I')
        out = self(*(args + ('--list-keys',) + keys))

        # Processing the output with this parser
        units = []
        current = defaultdict(list)
        for line in out.split('\n'):
            if not line.strip():
                # We should always output entries if they have a uid and key
                if current and 'uid' in current and 'key' in current:
                    # But ignore revoked keys if revoked option is True
                    if not (current.get('revoked', False) and options.get('revoked', False)):
                        units.append(dict(current))

                current = defaultdict(list)

            elif line.startswith('PHOTO:'):
                current['photo'] = line.split(':', 1)[-1]
                self.photos.append(current['photo'])
            elif ' of size ' in line:
                continue
            elif '   ' in line:
                (kind, line) = line.split('   ', 1)
                if kind == 'pub':
                    current['expires'] = False
                    current['revoked'] = False

                    if '[' in line:
                        (line, mod) = line.strip().split('[', 1)
                        (mod, _) = mod.split(']', 1)
                        if ': ' in mod:
                            (mod, edited) = mod.split(': ', 1)
                            current[mod] = to_date(edited)

                    (key, created) = line.split(' ', 1)
                    current['created'] = to_date(created)
                    (current['bits'], current['key']) = key.split('/', 1)
                elif kind in ('uid', 'sub'):
                    current[kind].append(line.strip())
                else:
                    current[kind] = line.strip()

        return units

    @property
    def default_photo(self):
        if not hasattr(self, '_photo'):
            self._photo = mktemp('.svg')
            with open(self._photo, 'w') as fhl:
                fhl.write("""
  
""")
            self.photos.append(self._photo)
        return self._photo

    def recieve_keys(self, *keys, **options):
        """Present the opotunity to add the key to the user:
         
        Returns
          - True if the key was already or is now imported.
          - False if keys were available but the user canceled.
          - None if no keys were found within the search.

        """
        keys = self.search_keys(*keys)
        if not keys:
            return None # User doesn't have GPG

        # Always use a temporary gpg home to review keys
        gpg = GPG(cmd=self.command, local=True) if not self.local else self

        # B. Import each of the keys
        gpg('--recv-keys', *zip(*keys)[0])

        # C. List keys (with photo options)
        choices = []
        for key in gpg.list_keys(photos=True):
            choices.append(key.get('photo', self.default_photo))
            choices.append('\n'.join(key['uid']))
            choices.append(key['key'])
            choices.append(str(key['expires']))

        if len(choices) / 4 == 1:
            title = "Can I use this GPG key to encrypt for this user?"
        else:
            title = "Please select the GPG key to use for encryption"

        # Show using gtk zenity (easier than gtk3 directly)
        p = Popen(['zenity',
            '--width', '900', '--height', '700', '--title', title,
            '--list', '--imagelist', '--print-column', '3',
              '--column', 'Photo ID',
              '--column', 'ID',
              '--column', 'Key',
              '--column', 'Expires',
            ] + choices, stdout=PIPE, stderr=PIPE)

        # Returncode is generated after communicate!
        key = p.communicate()[0].strip()

        # Select the default first key if one choice.
        # (person pressed ok without looking)
        if not key and len(choices) == 4:
            key = choices[2]

        if p.returncode != 0:
            # Cancel was pressed
            return False

        # E. Import the selected key
        self('--recv-keys', key)
        return self.status == 0

    def is_key_available(self, search):
        """Return False if the email is not found in the local key list"""
        self('--list-keys', search)
        if self.status == 2: # Keys not found
            return False
        # We return true, even if gpg returned some other kind of error
        # Because this prevents us running more commands to a broken gpg
        return True

    def search_keys(self, *keys):
        """Returns a list of (key_id, info) tuples from a search"""
        out = self('--search-keys', *keys)
        found = []
        prev = []
        for line in out.split("\n"):
            if line.startswith('gpg:'):
                continue
            if 'created:' in line:
                key_id = line.split('key ')[-1].split(',')[0]
                if '(revoked)' not in line:
                    found.append((key_id, prev))
                prev = []
            else:
                prev.append(line)
        return found


if __name__ == '__main__':
    cmd = sys.argv[0] + '.orig'
    if not os.path.isfile(cmd):
        sys.stderr.write("Can't find pass-through command '%s'\n" % args[0])
        sys.exit(-13)

    args = [cmd] + sys.argv[1:]
    # Check to see if call is from an application
    if 'GIO_LAUNCHED_DESKTOP_FILE' in os.environ:
        # We use our moved gpg command file
        gpg = GPG(cmd=cmd)
        # Check if we've got a missing key during an encryption, we get the
        # very next argument after a -r or -R argument (which should be
        # the email address)
        for recipient in [args[i+1] for (i, v) in enumerate(args) if v in ('-r', '-R')]:
            # Only check email addresses
            if '@' in recipient:
                if not gpg.is_key_available(recipient):
                    if gpg.recieve_keys(recipient) is None:
                        pass
                        # We can add a footer to the message here explaining GPG
                        # We can't do this, evolution will wrap it all up in a
                        # message structure.
                        #msg = sys.stdin.read()
                        #if msg:
                        #    msg += GPG_TRIED_FOOTER
                        #sys.stdout.write(msg)
                        #sys.exit(0)

    # We call and do not PIPE anything (pass-through)
    try:
        sys.exit(call(args))
    except KeyboardInterrupt:
        sys.exit(-14)

Sincerity of the Pin

This last two weeks I’ve been wearing a safety pin. It’s a small symbol of my personal commitment to support and interfere with public displays of hatred and intolerance.

In the last week there’s been backlash. Some calling it white guilt, others calling it a twitter action and many being critical of the sincerity of people who want to show that they will do something.

The first criticism that this symbol is a matter of white guilt is completely daft. Firstly because it’s a symbol I’ve seen non-white people wearing and also because I’m not guilty that I’ve had privilege thrust at me, I’m angry about it. I know what it’s like to be on the opposite end of that particular stick and moving between my home town in the English north to the USA has turned the way people see me around. From being an uneducated criminal class waste of space to being a quaint English, articulate, sensible Beatles accented gentleman in the space of a six hour flight.

If anything, I would stand up for the working class of any colour or creed. It’s a built in part of me and it will probably get me into trouble. There might be people out there who will wear it as fashion, or guilt, but I see no value in doubting the sincerity of people. And that’s realyl what the backlash is, a disrespectful doubt that the motives or the carry through will not live up to the symbol.

That leads me to that second point. That this is a Twitter action. That is, a re-share of an idea with no substantive action backing it up.

No.

This is something which will remind me to do something, if that means putting myself in danger, calling the police or just comforting the victim. I have made a vow to myself to be that voice if called upon. I know I live in a liberal city where I will not be called upon often, and that habitation does make it less useful. But there is racism here, there is misogyny here. It’s always been in Boston and I’ll always disprove of it.

But now, if it manifests in public, I’ll have to do something about it.

Inkscape Contest and Spreadshirt

For the past few weeks we’ve been running a contest to pick the next about screen. It’s a regular contest that we run and this one is special for me because it’s the first time we’ve managed to run the contest on inkscape.org instead of the deviantArt page. Because of this, we’ve had really good response this time around and the voting process is now ongoing:

https://inkscape.org/en/gallery/=about-screen-contest/contest-for-092+0/

The other good news is that the Free Software Conservancy has set up a spreadshirts account for inkscape, so the plan is to try and produce some physical goods. At first we’ll just inkscape logo items, but we’ll try and move to getting other items too. Maybe even incorporating artworks. Although that’ll bring up a good discussion we’ll have to have with the artists to make sure they’re happy with the idea, even if they’ve licensed things under CC.

https://shop.spreadshirt.com/inkscape-shop/

2016 Sucked

This year was the focal of manure. We have no idea what the future will hold, but the probability for happiness and a quiet life is diminished.

Good luck everyone.

The Dictator’s Handbook is Self Falsifying

I’ve been reading “The Dictator’s Handbook” this week, a recommendation from CGP-Grey (youtube) and a damn good one. It’s a description of how people who want power, get power and how they keep it once they have it. I won’t go into the nitty gritty, but suffice to say that it has a lot of good things to say about murdering people to get what you want.

The idea I want to explore in this blog post is using understanding, and “Life the Universe and Everything”. For those of you who have not had the pleasure of listening to The Hitchhiker’s Guide to the Galaxy; in the story we are told:

There is a theory which states that if ever anyone discovers exactly what the Universe is for and why it is here, it will instantly disappear and be replaced by something even more bizarre and inexplicable.

There is another theory mentioned, which states that this has already happened.

This is why in the story the answer to the question of Life the Universe and Everything is “42”, but that the question itself was unknown. Knowing both question and answer would cause the above self destruction/recreation and it’s imperative that the characters never find out both.

The nature of understanding in that universe is thus that it is not just non-understandable, but deliberately evasive. The rules of the game will change as soon as you know the rules of the game. Not in some god like way of keeping you in the dark deliberately, as if the universe had agency, but because somehow what you know is tied to how things work.

So how does this relate to Dictators?

Well the book is so good at explaining the mechanics of the interpersonal relationships in ruling a country or business that it may change the behaviour of people who have read the book. It may change their behaviour enough to actually make the book’s premise false. Not that it’s false when you haven’t read the book, only that it’s false when you have.

But, this make one giant Saturn sized assumption. That it is possible to change how you act in a certain circumstance given this knowledge. If it’s not changeable, then knowing it doesn’t matter and no amount of self-help or ingenious insights into the human condition will change our society. But the book’s preface is that knowing the rules that rulers rule by can help improve society, so it expects behaviour to be changeable and if is then someone somewhere will figure out how to exploit this new behaviour.

Once you have the sort of second order exploit, you get a very complicated dance between people who understand, people who do not understand and people who want to exploit either group.

Hence the mechanism described in the book will “instantly disappear and be replaced by something even more bizarre and inexplicable”. Thanks Douglas Adams.

To drive this idea a bit further. This in my mind creates two quantifiably different types of truth. That which is understandable but unchangeable is solid or foundational truth. Like Mathematics, knowing 2 + 2 = 5 and why doesn’t allow you to change it’s truthfulness. Then there is mutable truth, where knowing how and why something is true allows you to manipulate it into falsity. This is especially true in biological and social sciences where adversarial mechanisms are in constant flux.

What do you think?

Total Aid

In politics, I’m not happy. As the world becomes politically harsher, crueller and more self serving; we hear and see the heart breaking plight of millions of Syrians and others fleeing their war-torn countries.

Someone recently asked me what the difference is between the wars we have today and the First and Second World Wars. Apart from the global nature of the wars, the Syrian war is different because it’s not Total War. This concept involves the participating countries dedicating every part of their military, economic and civic power into fighting the war. This was very different from past wars, where non-combatants didn’t usually get slaughtered, far away factories didn’t get bombed and neutral nations didn’t get invaded for resources.

There’s pressure from both sides of the Atlantic to increase the amount of our country’s power dedicated to fighting Russia, Syria, whoever else. In the name of democracy, or in the name of survival. I’m just not sure it’s helpful for the two western countries doing the least for Syrians to be the ones advocating more violence.

For example. The UK Government is under attack for it’s position that foreign nationals should pay for all National Heath services. Including pregnant women from Syria. At this stage, anyone from Syria can hardly be said to have a country where you could send the bill, anyone who is missing a safe home country should be helped by the health system in the UK and anywhere else in the word. Not just emergency care, but pre-natal and other care too.

Instead of Total War and partial war I’d like to propose the concept of Total Aid and Equivalent Aid. The Later is simply that in times of crisis, we can mobilise our countries to fight, not fight with violence, but fight with compassion. To give everything we have to help, protect, heal and stabilise the people and structures that the crisis is threatening. This doesn’t have to be war, although the Syria crisis prompts me to consider this.

One would imagine us taking in Syrians in large numbers, to take the risk that a rare minority will be criminal or idealogical. Our security and police services ARE strong enough and competent enough to help too and I see no reason they should be missing from a plan for Total Aid. Health care, education, social support and basic structural support can come from all quarters of society, from all classes and all means. Your country needs you, to help another country.

Once mobilised, we our support and teaching would leave many people and their organisational structures able to go back and rebuild (if possible) or relocation and rebuild. It’s easier to rebuild if you have a social structure that’s not torn apart and the friendships and good will created by reaching out and embracing our brother country in whole would lead to better relations even surpassing idealogical and religious tensions.

We would also be leading by example and showing how strong we are that we can help so well and with so much good. Until the crisis is put to bed and the world as a whole can move on.

OK, so maybe you’re not convinced by Total Aid, maybe the troubles in the world could get so great that we need to think about our own lot. For you I offer option two. Equivalent Aid. That is, under agreement, treaty or convention a country will not spend money or take action with military violence without committing the same scale of operating and budget to helping survivors cope with what we have wrought on them.

I’d consider this the bare minimum human effort. The point at which a country goes from being a good country to a bad country. We are far from the point of spending the kinds of trillions on aid that we would have to to meet this target. But if we want to bomb places, if we want to invade things and generally mess up the place. Then we should at least be ready to pay for helping put some of it back together with equal force.

What do you think? Let me know in the comments below.

Free Software Faith for the Long Term

I’ve been an advocate for Free and Open Source Software for a long, long time. When I first got into it, it felt right, just, progressive. I struggle with how to communicate that feeling of freedom to others, to make them understand how important Free Software is.

When we talk of Free Software dialectic conflicts, there are two big fronts; the first is the idea that Proprietary software is a /better/ way to make software. Developers get paid, investors make money, huge profits can be poured into research and development. This is your Microsofts and Apples. Let’s ignore that idea today.

The second battle is practicalism. This idea says that it doesn’t matter how the software was made, just that it needs to work. Let’s explore why this idea is an important conflict. Practical solutions often favour the short term and the local. That is, they are solutions which usually a single person will make a decision about what software to use and the criteria most important is the cost (money, time, effort) to get it working in the here and now.

The conflict comes about because often Free Software is a more expensive proposition in the here and now. It’s more expensive because it takes more time to set up, or it’s tools are not tested as much, or not designed as well, or the more insidious reason: the wider world does not support Free Software causing the Free Software solution to be on the loosing end of a powerful network effect.

But proprietary software often has hidden costs. After the initial purchase costs, these are often either societal or long term costs. Societal and long term are the direct opposite of a practical decision. Thus they are not considered, or not valued highly when making the decision.

There is one parallel which I hate to make. Religion. Here is another societal and long term cultural device. Most religions ask participants to give up the bad behaviours in the here and now and be a little more patient for the better life or better after life. I’m horribly simplifying here so please forgive me. But religions mostly work on faith and their evangelicalism pressures people to consider the societal and long term. This is why I think Free Software advocates are so often compared to religious fundermentalists. It’s a cheap shot; it does not follow that faith in Free Software is faith against evidence. That’s just a bad argument.

But it’s worth considering that Free Software is a hard sell precisely because it’s a societal good that requires powerful network effects on it’s side in order to be fully effective. Having a self-sacrificing religion of one is foolish, but a society of good intentions can be a powerful force. We in the Free Software world often have to invest more, pay more and spend more time to make the Free Software world we want to see, and to see it happen for ourselves and our friends and families. But this will only be the case so long as the network effects are against us and I don’t believe they always will be.

Now consider Ubuntu. Here’s platform that tried to move some of the power away from practicamism by making Ubuntu easy to install, easy to use, a joy to behold. Things that are genuinely empowering to Free Software. As it built itself up, the negative network effects started to weaken and Ubuntu users enjoyed for a time, a level of support from the wider world that had not been experienced before.

But that naturally led to the in-fighting. It’s typical for the front runner to be targeted by all the also-ran distributions. The FSF targeted Ubuntu’s practicalist concessions (even though they were fairly minimal), Other distributions ripped Ubuntu and their community apart, trying to block Ubuntu’s success. I’m not saying they meant to do it, or that it was a conspiracy. But that these other communities did not see Ubuntu’s success as their own success and naturally tried to undermine it as humans are likely to do.

So for very human reasons, we’re here with no real champion for Free Software in the practical arena. Ubuntu has fallen for its own hype and is not able to being the Free Software faith with it, even if it was successful. The societal and long term benefits of Free software remain largely unknown to the majority of the world and we wait patiently for a successor that can try again to change the world.

What do you think? Comment below.

Short Text Alert Notification (STAN)

I recently got a new phone, and a new place to keep it. My previous phone was damaged when I sat on it and realized that keeping it in a back pocket was a bad idea. I’m keeping my new phone in a belt pouch with a Velcro seal and it takes me slightly longer to get at the phone than it would if it was in my pocket.

And so I was, a few days ago, messaging people using SMS and then putting my phone away. Several times my interlocutors would send me short affirmative messages which I would have to read by pulling out my phone from it’s pouch, turn it on with it’s (not always first try) security and then read a message that says “OK” or “Yes”.

So I thought; why not have the phone change it’s notification sound for simple messages? A table of possible short messages which would then translate into a different notification sound and allow the user to understand a simple message without having to pick up their phone, unlock it and read the message.

This would certainly make sending short messages slightly more polite as it can seem aggravating sometimes to have to read short messages. Even though it’s not the fault of the sender really.

  1. The idea would require a customized default messaging app for android, or possibly system configuration, I’m unsure how notification sounds are stored.
  2. A research stage where short messages on multiple phones was collected into a corpus of English messages. Each classified into what it probably means.
  3. An additional corpus of emoji messages that can be easily converted into sound. Example a kissing emoji into a kissing sound.
  4. A restriction on the time between the previous message to/from the sender and the notification. Say half an hour. This is to prevent an affirmative messages which arrive a week after the last correspondence from confusing the recipient.
  5. A secondary research phase where a group of people’s would have the app installed (replacing their messaging app) and would self report if the new notification sounds made any difference over a couple of months of use.

This is a sort of imaginary plan, but I could see this being useful. But the only way it would be come mainstream is if it was adopted by Google directly into it’s own messaging app. A stepping stone towards that would be adoption of the modified message app into after market android such as the Cyanogenmod project. The stepping stone would certainly allow more data to be collected about it’s functional use out int he wild.

Another alternative stepping stone would be to approach a manufacturer or telco network. But quite often apps these layers install are seen as bloat-ware and it might be better to avoid that route if possible.

What do you think? Good idea? bad idea?