Sunday, January 06, 2008

GPG Encryption for TextMate

Here is a wxPython based encryption script for TextMate, it presents a dialog box for selection of the key. Set default_gpg_user to the user you normally use for encryption.


#!/usr/bin/env python
import os, sys, tempfile, traceback
from subprocess import *
import wx

default_gpg_user = "brianlane"

exit_discard = 200
exit_show_tooltip = 206

def GetRecipient():
keys = GetGpgKeys()
key_list = []
user_idx = 0
i=0
for k in keys:
if k[0] == "uid":
s = " %s %s" % (k[9],k[5])
else:
s = "%s %s %s" % (k[9],k[5],k[4])
key_list += [s]
if k[9].find(default_gpg_user) > -1:
user_idx = i
i += 1

dialog = wx.SingleChoiceDialog(None, "Select a GPG Key", "Encrypt Text with GPG", key_list)
dialog.SetSelection(user_idx)
dialog.SetSize((400,200))
key_info = None
if dialog.ShowModal() == wx.ID_OK:
key_info = keys[dialog.GetSelection()]
dialog.Destroy()

return key_info


def GpgEncryptFile( gpg_file, gpg_recipient ):
cmd = 'gpg -qeaR "%s" --batch --output - %s' % (gpg_recipient, gpg_file)
p = Popen( cmd, shell=True, stdout=PIPE, stderr=PIPE )
output = ""
for line in p.stdout:
output += line
stderr = ""
for line in p.stderr:
stderr += line

return output, stderr


def rmfile( file_path ):
if os.path.exists(file_path):
os.unlink(file_path)

def GetGpgKeys():
cmd = "gpg --list-keys --with-colons"
p = Popen( cmd, shell=True, stdout=PIPE, stderr=PIPE )
key_list = []
for k in p.stdout:
key_args = k.strip().split(':')
if key_args[0] in ['pub']:
key_list += [key_args]
return key_list


app = wx.PySimpleApp()

key_info = GetRecipient()
if not key_info:
exit(exit_discard)

# Get the input for gpg
gpg_input = sys.stdin.read()

# Write it to a secure file
(out_fp, out_path) = tempfile.mkstemp()
os.write(out_fp,gpg_input)
os.close(out_fp)

try:
gpg_armour, gpg_error = GpgEncryptFile(out_path, key_info[4])
if not gpg_armour:
rmfile(out_path)
sys.stdout.write(gpg_error)
exit(exit_show_tooltip)
sys.stdout.write(gpg_armour)
except:
rmfile(out_path)
traceback.print_exc(file=sys.stdout)
exit(exit_show_tooltip)

rmfile(out_path)

GPG Decryption for TextMate

Its been a while since my last post. I've switched desktops to OSX (a dual-core Mac mini), settled on wxPython as a good GUI development environment and switched to using TextMate as my main text editor.

TextMate has a really nice feature called Bundles, these are scripts that you can use to filter the text in a document or selection. Inspired by this blog posting I set out to add GPG encryption/decryption to TextMate. After fighting with cut&paste problems (for some reason the code wouldn't run when cut from the website and pasted into the Bundle Editor. His code works fine, but has one serious drawback -- it exposes the passphrase on the command line when decrypting the GPG message.

After whacking around with bash a bit I finally just wrote a python script to do it safely. I use wxPython for the passphrase prompt dialog
and standard python modules for everything else (I am using Python 2.5).

(Sorry! Blogspot seems to mangle the formatting!)


#!/usr/bin/env python
''' TextMate Bundle for Decrypting GPG messages
Copyright 2008 by coderpunk@gmail.com
All Rights Reserved
GPL V2.0
'''
#!/usr/bin/env python
import os, sys, tempfile, traceback
from subprocess import *
import wx

exit_discard = 200
exit_show_tooltip = 206

def GetPassword():
passphrase = None
dialog = wx.TextEntryDialog( None,
"GPG Passphrase",
"Encrypt Text with GPG",
"",
style=wx.OK|wx.CANCEL|wx.PASSWORD)
if dialog.ShowModal() == wx.ID_OK:
passphrase = dialog.GetValue()
dialog.Destroy()
return passphrase


def GpgDecryptFile( gpg_file, passphrase ):
cmd = "gpg -qd --batch --passphrase-fd 0 %s" % (gpg_file)
p = Popen( cmd, shell=True, stdout=PIPE, stderr=PIPE, stdin=PIPE )
(stdout, stderr) = p.communicate( passphrase )
return stdout

def rmfile( file_path ):
if os.path.exists(file_path):
os.unlink(file_path)

app = wx.PySimpleApp()
passphrase = GetPassword()
if not passphrase:
exit(exit_discard)

# Get the input for gpg
gpg_input = sys.stdin.read()

# Write it to a secure file
(out_fp, out_path) = tempfile.mkstemp()
os.write(out_fp,gpg_input)
os.close(out_fp)

try:
gpg_output = GpgDecryptFile( out_path, passphrase )
if not gpg_output:
rmfile(out_path)
exit(exit_show_tooltip)
sys.stdout.write(gpg_output)
except:
rmfile(out_path)
traceback.print_exc(file=sys.stdout)
exit(exit_show_tooltip)

rmfile(out_path)

Tuesday, February 13, 2007

Python: Select from True/False



I just ran across this neat technique while reading wxPython in Action- A common operation is to output a string based on some expression being True or False. Being an assembly programmer at heart I usully code it as something like this:

if borked:
print "Its Borked"
else:
print "Its not Borked Yet"
In 'C' you can do this:
borked ? "Its Borked" : "Its not Borked Yet"
An alternative way in Python is to do it like this:
borked and "Its Borked" or "Its not Borked Yet"
The expression is evaluated from left to right and it returns the result of the and if both are true, or the result of the or if the and fails. In Python instead of returning True for 'borked and "Its Borked" it returns the 2nd object, a string in this case, which is exactly what you want if borked is true. When it is false it goes on to the or expression and returns "Its not Borked Yet"

Sunday, February 11, 2007

Productivity for Programmers, #1: Trusted Systems

Bob Walsh and Matt Cornell have an excellent post over at MyMicroISV. They talk about 5 systems that every productive programmer needs to have in their toolbox:
  • Task System
  • Decision Logs
  • Version Control
  • Code Snippets
  • Your Bugs
Personally, I use a combination of Trac, Subversion and Gtodo.

Saturday, February 10, 2007

This is the information I've been waiting for - NIST is going to have a competion for a new secure hashing standard. For a year or so there has been information that the SHA-1 hash has been 'broken', I have been waiting for Bruce Schneier's take on the issue:

The hash function you're most likely to use routinely is SHA-1. Invented by the National Security Agency, it's been around since 1995. Recently, though, there have been some pretty impressive cryptanalytic attacks against the algorithm. The best attack is barely on the edge of feasibility, and not effective against all applications of SHA-1. But there's an old saying inside the NSA: "Attacks always get better; they never get worse." It's past time to abandon SHA-1.
You can read the rest of his essay from Wired here at his blog.

Secure Remote Filesystems

Accessing files on remote systems can sometimes be a big pain in the ass (BPITA). You can use Samba to mount filesystems, but that means setting up a Samba server on the remote system and being susceptible to security problems. There is an alternative, called SSHfs that uses SSH to mount the remote filesystem as if it were a local directory.

In Ubuntu it is easy to get working:
sudo apt-get install sshfs
sudo joe /etc/modules
Add fuse on a line by itself
sudo modprobe fuse (only needed to get it loaded now)
sudo gpasswd -a username fuse
newgrp fuse
mkdir ./mnt/remote
sshfs user@remote.system: ./mnt/remote/
Now your remote account's files are accessible right there on the local system, via a secure link. This works for the MAC as well, if you install MACfuse and Secure Remote Disk.

Unmount the remote filesystem using the unmount command:
fusermount -u ./mnt/remote
This is better than Samba because you are using secure link to transfer the data, it is less complicated and therefore less likely to be compromised than Samba. Building on top of things that do 'just 1 thing' is the Unix way and is a large part of why Unix systems and programming practices are more secure and more flexible than those you see with primarily GUI centric systems.

wxPython

Back in July I wrote that I had settled on pyGTK as the cross-platform development tool for me. Well, I've change my mind a little. With pyGTK you don't get the native 'look-and-feel' on other systems like Windows and MAC. This isn't a problem for me, but for normal users it makes the program appear amateur. Good news, though! The wxWidgets project provides a cross platform C++ environment that uses the native widgets for each platform. And, there is a python project, wxPython, that integrates nicely with it.

So, now I can develop cross platform applications, using my favorite language (Python) and they will look like native applications, keeping the natives happy.

Sunday, July 16, 2006

Petals Around the Rose



Here is a fun little brain teaser that took me about an hour to figure it out. Bill Gates took longer, but didn't have the advantage of looking at a series of previous rolls (he memorized his). The answer to the roll above is 2.

Sunday, July 09, 2006

Chaos Manor Reviews

One of my favorite writers, Jerry Pournelle, has been publishing his Chaos Manor column in Byte Magazine for darn near forever. It was one of the first magazines I subscribed to in High School and only gave up the subscription after Steve Ciarcia moved on to create Circuit Cellar Ink. Jerry has one of the first 'blogs' at his www.jerrypournelle.com site and I have been reading that for years now -- well before there were such things as 'blogs'.

CMP, the owner of Byte, has finally pulled the plug on it and now Jerry and some of the other columnists from Byte are running their own site at Chaos Manor Reviews. Its worth checking on a daily basis, as is the main Jerry Pournelle site.

pyGTK for Cross Platform Development

I've been debating the pros and cons of various development environments, from VC++ 6 to the new C# Express from Microsoft to MinGW with GTK+ or wxWidgets. I've finally settled on Python and GTK+ and Glade as the best choice.

This wil provide me access to the massive Windows client base while retaining my love for Linux and elegant designs. Python allows rapid application development, can be packaged up as only the compiled python code for distribution and can be optimized by writing processor intensive sections in c and linking them in.

That said, I now need to go write some code.

Friday, July 07, 2006

Grokking the source

Back in the 'good old days' if you wanted to tweak a program on your system you downloaded the source from the author (if you hadn't already), made your changes and were done. It isn't quite as straightforward these days.

I'm trying to tweak cpufreq to idle down the CPU when the laptop lid is closed. Normally that would be a good use of the acpid event system. Execpt that my lid only generated an even when its closed, not when it is opened back up! So I'm going to add an option to cpufreq to check the lid status in /proc/acpi/button/lid/LID/state and when its closed set the CPU to its minimum frequency. Should be easy, right?

Nope. Not when you are running Fedora Core and their modified version of cpufreq. Apparently their startup script passes it a nice level (-n ). This wouldn't be so bad except that it gets passed by the daemon function and doesn't appear in my debugging echos. Arrgh.

So, I guess I'll do things the 'redhat way' and create a new diff for my changes and rebuild it as a rpm.

Progress? Maybe. But I'm starting to feel like a unix curmudgeon.

ssh and the GNOME Desktop

As much as I hate the bloat of the GNOME desktop, I tend to use it on most of my machine. Its easier than trying to maintin something else. I also use openssh extensivly, and normally have a shell or 10 open to different machines. My laptop is seutp to run a backup once a day to one of these remote machines, it uses rsync over ssh so I have to either enter my password (hard to do in a script launched from cron) or the ssh agent needs to have the key loaded.

The easy way to do this is to have it ask you when you login. This is really easy to do:

Go to Desktop->Preferences->More Preferences->Sessions->Startup Programs

Click on 'add' and enter /usr/bin/ssh-add then click on close. Log out and back in and you should have a dialog asking you for your ssh password.

Now if I could just convince Firefox and Thunderbird to use ssh-agent for their authentication...

.cp