Wednesday, February 1, 2012

Smalltalk First Impressions

I've actually been meaning to get around to trying this out ever since the Dynamic Languages Smackdown[1].

So, I dusted off Pharo, which has had at least one major release since I last checked it out.

This time, I didn't pull punches, finding as many practical examples and tutorials to run through as I could. This includes two for Seaside[2], the couple for Pharo proper, and a built-in tutorial named ProfSteph which you can run from the greeting screen.

So here are my first impressions[3]; I'm sure they'll change as I learn more about the language (and I fully intend to learn more about it)

It Passes The Compaq Test

This thoroughly surprised me, because Smalltalk has a reputation for being brilliant and elegant but slow. I guess the people perpetuating this reputation mean "relative to C", because Seaside ran quite snappily off of a rather old machine with 256MB of ram. That was an "M". In fact, as I write this on that same ancient machine, I'm running Seaside in the background along with Slime and getting along perfectly well[4].

It Has "Source" Control

The word source is quoted because it's an image-based system, but a module called Monticello basically does for Smalltalk what git would do for other languages. I wouldn't mention this, except that I remember thinking about it last time, and several other people at the Smackdown expressed similar concerns. So if your main excuse for staying away from Smalltalk is "I don't want to give up source control", you no longer have an excuse.

Fantastic IDE

And this is coming from someone who usually hates IDEs. This one actually fails to get in my way at most opportunities, provides useful information and completions when I need them, is intuitive and well documented internally and externally, and (most importantly) does not take longer than Emacs to load up[5]. For those of you working cross-platform, it's also fully skinnable and comes with themes appropriate for the big three OSes (each of which it runs on beautifully).

Turtles All The Way Down

Everything is an object. Everything is an object. Signs of this show up in the way loops and conditionals are treated, as well as the complete construction of the system[7]. It's kind of an extension of the previous point, but I wanted to emphasize it. That fantastic environment I mentioned? It's built in Smalltalk. The main click-menu (called the World Menu) is actually represented in the image. You can head over to the class browser and find a class called TheWorldMenu. You can also Ctrl + right-click on any component of the menu to activate its halo and fuck with internal variables. You probably shouldn't, but you could. This level of introspection happens for almost[6] every component and sub-component you can see. I imagine this is what it would feel like to work on a full-out lisp machine.

Great GUI Toolkit

I reserve the right to change my mind since I've only gone through some very basic activities, but it looks like it would be very easy to put together desktop applications with Smalltalk. I'm not super clear on how you'd go about deploying them, but there seem to be ways.

That's the stuff that's attracted me. There's downsides too, of course, but they're not enough to give me pause. If you're just looking for an excuse not to try Smalltalk out, one of these should probably be enough.

No Respect for BEDMAS

All of the manuals are quite explicit about this too; the fact that everything is an object means that the expression 3 + 5 * 2 isn't actually an expression. It's two binary messages being sent to two SmallIntegers. That means that the only reasonable way to be consistent about it is to treat arithmetic strictly from the left; so that the expression above will actually evaluate to 16 rather than the expected 13 if you try it out.

Mouse-Centric

This may actually be a pro for some people, but it's not for me. The environment expects you to do most things with the mouse[8]. There's a greater than usual amount of time spent dealing with objects and widgets, so I guess that might be fair, but look. If your window system doesn't let me move between windows without reaching for the rat, you're doing something wrong. Being already used to a tiling WM just makes it that much more annoying[9]. A lot of things have keyboard shortcuts, but not everything, and those things that don't are quite annoying. Not exactly annoying enough to jump over to GNU Smalltalk, but still.

Odd Choice of String Delimiters

In Smalltalk "foo" is not the string foo. It's actually the comment foo. The string foo looks like 'foo'. How do you put an apostrophe in a string? You don''t. You either escape it with a second quote, or you use typographers’ quotes. Now you know. I'm still not entirely sure why this decision was made though. It seems like pretty much any other comment delimiters would have made more sense.

Wonky Keyboard Shortcuts

I'm putting this one at the bottom of the list because I'm convinced that there must be a way to change them that I just haven't discovered yet. By "wonky", I don't mean "it uses the wrong letter", I mean "who the fuck thought this was the correct behavior?". Things like not having Ctrl+backspace backward-word-kill (giving that honor to Shift+backspace for comical effect), or having Ctrl+x kill a line, but move forward doing it and keep the \n in place, or having Ctrl+Right move forward a word, but skip newlines so that moving your point to the last symbol of a line is just that little bit more annoying. Also in this category, things like having (, ' and " auto-close themselves, but only about half the time and with a noticeable delay. Like I said, this isn't that huge a deal because I'm convinced that

  • There must be options I'm missing that will let me fix this
  • Even if there is no explicit config option, there's a way to fix this through the object model, and it won't be complicated enough to drive me to drink

So there. That's first impressions after about half a week of poking at Pharo. Hopefully it came off as more positive than negative, because I really do like the language so far, but my internal censor goes a bit wonky at about 11:00, and I won't get a chance to proof this until tomorrow morning.


Footnotes

1 - [back] - And now I'm shocked because I could have sworn it wasn't more than a year ago, but here we are.

2 - [back] - Though the official Seaside tutorial has some issues with registering new handlers, in Pharo at least. They tell you to go the web-interface route, which consistently ignored my brilliant HelloWorld handler. Check the sidebar for links to working tutorials ("Seaside 3.0 in Pharo" actually gives you a working tour as of this writing).

3 - [back] - Which differ from Preliminary Impressions in that they're founded on something more than gut feeling.

4 - [back] - Though I will admit that squeak is at the top of top by using a whopping 6%-8% of my CPU and 10%-16% of my memory.

5 - [back] - I'm looking at you, Eclipse. Though, to be fair, it's been a while, I guess you may have lost weight since then.

6 - [back] - It does tell you to fuck off if you try to add a halo to a halo component. I'm assuming I have to edit that directly through the class editor rather than at the front end.

7 - [back] - It's not cumbersome, though, every command I've typed so far has been extremely elegant, if alien (though that's just because I'm not used to it).

8 - [back] - In fact, it advises you to go get a three-button mouse if you don't have one already.

9 - [back] - Though I'm sure it must be possible to build one for the VM in such a way that you can easily strip it from your final product; I may look into this once I get my bearings. Edit: Nevermind. Though it might still be a good learning exercise.

Saturday, January 28, 2012

The Lisp Shell Followup

So, I may have to backtrack on what I was saying earlier. Specifically, I called clisp a toy shell, and I called the machine I'm currently typing this on a toy machine. I did this because, having just installed it and spent a grand total of five minutes poking around, I assumed

  • it wouldn't run some programs properly
  • my scripts would now be useless
  • cd wouldn't work
  • I'd lose tab completion on files
  • there would be no gains to offset all the losses
  • it would be a pain in the ass to use a regular shell when I hit the limits of clisp

It turns out that most of those don't apply. I did actually lose tab-completion when working with files, but that's it. Pretty much every program that I want to run typically[1] works just as well from clisp as it does in bash, scripts run exactly the same as under a standard shell when you use run-shell-command, cd is actually a function defined in clisps' cl-user, and when I need to run a regular bash for whatever reason eshell can pickup the slack.

There's also a few non-obvious things I gain to offset losing filename tab completion.

First off, I get to define helper functions at my command line. One situation I've already found this useful in is copying files off my previous computer. It's a fairly specific situation, because I didn't want to sync a complete directory, but rather surgically copy over some 12 or 13 irregularly named files. That would have taken 12 or 13 separate scp calls. In regular shell, I'd have to do something like write a script for it. Having an actual language available let me pull out my first trick

> (defun cp-file (file-name) 
    (run-shell-command (format nil "scp inaimathi@other-machine:.emacs.d/~a .emacs.d/")))

CP-FILE

> (cp-file "example.el")

This isn't specific to clisp, obviously. I assume that any language shell you use could pull the same trick. Still, having the ability to define helpers on the fly is something I occasionally wish I had[2].

Another thing that I imagine would work in any language shell, is an easier way of defining shell scripts. I wrote a little set of ui utilities a while ago, one of which is pack, a translator for various archive formats so that I can write pack foo rather than tar -xyzomgwtfbbq foo.tar.gz foo

#!/usr/bin/ruby

require 'optparse'
require 'pp'
require 'fileutils'

archive_types = {
  "tar" => ["tar", "-cvf"],
  "tar.gz" => ["tar", "-zcvf"],
  "tgz" => ["tar", "-zcvf"],
  "tar.bz2" => ["tar", "-jcvf"],
  "zip" => ["zip"]
}

########## parsing inputs
options = { :type => "tar", :excluded => [".git", ".gitignore", "*~"] }
optparse = OptionParser.new do|opts|
  opts.on("-e", "--exclude a,b,c", Array,
          "Specify things to ignore. Defaults to [#{options[:excluded].join ", "}]") do |e|
    options[:excluded] = e
  end
  opts.on("-t", "--type FILE-TYPE",
          "Specify archive type to make. Defaults to '#{options[:type]}'. Supported types: #{archive_types.keys.join ", "}") do |t|
    options[:type] = t
  end
end
optparse.parse!
##########

ARGV.each do |target|
  if not archive_types[options[:type]]
    puts "Supported types are #{archive_types.keys.join ", "}"
    exit
  elsif options[:type] == "zip"
    exclude = options[:excluded].map{|d| ["-x", d]}.flatten
  else
    exclude = options[:excluded].map{|d| ["--exclude", d]}.flatten
  end
  fname = target.sub(/\/$/, "")
  args = archive_types[options[:type]] +
    [fname + "." + options[:type], fname] +
    exclude
  system(*args)
end

So that was necessary in bash, and because shell scripts can't easily share data, the companion script, unpack, had to define almost the exact same set of file-extension-to-command/option mappings[3]. If I'm using clisp, I could instead write

(defun pack (file-name &key (type tar) (exclude '(".git" ".gitignore" "*~"))) 
  (pack-file (make-instance type :file-name file-name :excluded exclude)))

(defmethod pack-file ((f tar.gz))
  (run-shell-command (format nil "tar -zcvf ~@[~{--exclude ~a~^~}~]~a" 
                             (excluded f) (file-name f))))

and be done with it[4]. This is a similar, but more extreme version of the previous point. Instead of writing shell-scripts, I can now write functions, macros or methods. These are smaller conceptual units and deal with inputs more easily, letting me focus on expressing what I want the script to do. In fact looking at language shells this way makes it obvious that things like optparse are just hacks to get around the way that scripts accept arguments.

The last cool thing is to do with the package management. I could be wrong about this, but I don't think the Lisp notion of in-package exists elsewhere. So I can define a package like

(defpackage :apt-get (:use :cl))

(defun install (&rest packages)
  (su-cmd "apt-get install ~{~(~a~)~^ ~}" packages))

(defun update () 
  (su-cmd "apt-get update"))

(defun search (search-string) 
  (cmd "apt-cache search '~a'" search-string))

where the cmds are defined as something like

(defmacro cmd (command &rest args)
  `(run-shell-command 
    (if args `(format nil ,command ,@args) `command)))

(defmacro su-cmd (command &rest args)
  `(run-shell-command 
    (format nil "su -c \"~a\""
            (if args `(format nil ,command ,@args) `command))))

The issue I'd have with defining these in, for example a Python shell, is that I'd then have a choice. I could either import the file and put up with typing out the name of the module at every invocation, or I could import install, update, search from and then hope that I don't have to define conflicting functions[5]. In a Lisp shell, I can define it and load it and then do (in-package :apt-get) when I need to do a series of commands relating to installing new modules.

Now all of these, clisp-exclusive or not, are small syntactic fixes that work around basic shell annoyances. To the point that you're probably asking yourself what the big deal is. It's basically the same reason that macros are awesome; they get rid of inconsistencies at the most basic levels of your code, and the increased simplicity you get that way has noticeable impacts further up the abstraction ladder. The sorts of things that look like minor annoyances can add up to some pretty hairy code, and cutting it off at the root often saves you more trouble than you'd think.

I'll admit that tab completion on file names is a pretty big thing to lose[6], but the things I outline above are mighty tempting productivity boosts to my shell. To the point that I'm fairly seriously debating switching over on my main machine. Between Emacs, StumpWM/Xmonad and Conkeror, it's not really as if someone else can productively use my laptop anyway. Adding an esoteric shell really doesn't seem like it would be a big negative at this point.


Footnotes

1 - [back] - Including fairly complex CLI stuff like wicd-curses, mplayer and rsync --progress

2 - [back] - And now, I do

3 - [back] - Except for compression rather than expansion

4 - [back] - Defining methods for each archive type, and the appropriate class, obviously

5 - [back] - Or import another module that defines new ones with the same names

6 - [back] - And I'm going to put a bit of research into not losing it

Wednesday, January 25, 2012

WebMote and "Open" software

web-mote

I've been working on a tiny little utility to marginally improve my life in an almost insignificant way.

You've already heard the casual references to my "media PC", and you may have come to the conclusion that it's just a standard NAS setup, but no. It's actually just a regular computer hooked up to my TV through a VGA and audio wire[1]. About a week ago, the wireless keyboard I was using started chugging and finally gave out. Well, being that I mostly listen to music and watch ridiculous low-res videos in mplayer, why not just get a remote working? As it happens, I also had an old iPod touch lying around literally collecting dust since I stopped carrying a mobile music device with me. So I put the two together and hacked up a little remote control server for the computer. It basically just starts mplayer with -slave -idle, runs Hunchentoot out front and then passes along commands when I click on the various video links. I'm using it to watch some downloaded videos from the science network as we speak. The only gap in the interface is that I can't control the actual TV the same way yet (so I still need to get up to change channels or up the output volume)

The code is up at my github, as usual. I just noticed that there's no license file, though.

Just a second.

Ok. Compiling and inserting that took almost as long as writing the actual code. There you have it, in any case. I don't seriously recommend you use this program until I've ironed out one or two things, but feel free to if you like. I definitely enjoy being able to control my media center from whatever HTML client I happen to have at hand.

Next step: figuring out how to control the TV through wifi (though early research is not encouraging).

fingerquotes open

This was just sort of depressing.

I dunno, maybe it's not that big a deal to most people, but I'm depressed.

A good third to a half of my last weekend was spent researching ways of getting Open Genera up and running, only to find out that "Open" doesn't quite mean what I thought it did in this context. Granted, the system was built back in the 80s, so I guess the word may not have had the same connotation, but I still got confused.

It's bizarre, because I honestly don't get the point of a closed-source Lisp system. The whole point is that the entire machine is there, open to pokes and prods at its various sources and definitions. Saying that it's not being released openly or freely is just ... I dunno, off. The message is so fundamentally incongruous with the medium that it seemed to come at me entirely out of left field[2]. I guess that'll teach me to read the license first next time.

I still have my ersatz lisp machine, I guess. And I could do a bit of poking and hacking on Movitz if I really wanted to. That'll have to hold me.


Footnotes

1 - [back] - Incidentally, the mediaphiles among you should refrain from telling me that I should be using HDMI instead. Enough of my friends tell me that already, and I could give a shit. I mostly listen to music and watch almost ridiculously low-res videos; there's a separate DVD player for the occasional high-def media I watch.

2 - [back] - Enough to delete the relevant article from my archives (sorry, to the people who linked to it already) and never speak of it again[3].

3 - [back] - Except for here, obviously.

Friday, January 20, 2012

How Close can you get to a Lisp Machine?

Aside from the obvious, I mean.

Here's what I've been playing with for the past little while; using clisp instead of bash.

Those instructions still work surprisingly well, given that they were published all of 11 years ago. Here's what I did to replicate them

apt-get install clisp x-window-system
wget http://beta.quicklisp.org/quicklisp.lisp
echo "/usr/bin/clisp" >> /etc/shells

I then installed quicklisp and ran (ql:add-to-init-file), then manually added the following to .clisprc:

(ql:quickload (list :cl-fad :cl-ppcre :trivial-shell))
(defun startx () (execute "/usr/bin/X11/xinit"))
and the following to my .xinitrc clisp -x "(progn (ql:quickload (list :clx :cl-ppcre :stumpwm)) (funcall (intern \"STUMPWM\" :stumpwm))"

After poking around for a little while and making sure everything worked approximately correctly, I ran chsh and set my shell to /usr/bin/clisp.

Performance-wise, it's surprisingly snappy given

a - what it's running on and

b - that there are at least 3 instances of clisp at work at any given time. It's a toy, but quite a quick and fun toy, actually.

Now, granted, the title is supposed to be taken with a grain of salt[1], but this still feels like it's approaching the target. What I've got running is a fully open system[2] that implements most of its components in Lisp (the shell is Clisp, the WM is Stump and the editor is Emacs). I suppose I could also throw in Closure[3] and Climacs as well, but I'm done playing for today.


Footnotes

1 - [back] - since I've never used an actual LISP Machine or even the Open Genera System. Incidentally, these links are here to remind me to look into it when I have a spare moment, so I'm not sure how much longer I'll be able to say "never used 'em".

2 - [back] - Except that it uses b43-fwcutter for the wireless card.

3 - [back] - As an aside, that meme-space is getting pretty crowded. To the point that I have to disambiguate in conversation. There's Clojure (the language), Clozure (the Common Lisp implementation) and Closure (the common-lisp based browser/html-parser)

Monday, January 2, 2012

Passing Notes

So here's how you can pass messages to each other without your parents[1] reading them.

Step 1: Get Some Friends and Computers together

I can't help you with this. You need some computers[2] and you need some friends to talk to, otherwise why would you be passing messages?

Step 2: Get GnuPG

This should be pretty simple. If you're on Debian/Ubuntu type

apt-get install gnupg

If you're on Windows, you'll need to install Cygwin with the gnupg package or gpg4win, if you're on OS X, you'll need to get GPGtools.

Step 3: Make some keys

Each of you should create a set of keys. Do that by typing

gpg --gen-key

GnuPG will then give you a menu that looks like this:

gpg (GnuPG) 1.4.10; Copyright (C) 2008 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)

Accept the default by hitting enter[3]. It will then ask you

RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)

Don't accept the default here, take the longest possible by typing 4096 and hitting enter. Next, gpg will ask when your key should expire. You can either specify days, weeks, months, years or accept the default (never expire). It's a balancing act; the more often you get new keys the more secure they will be against attacks. On the other hand, each time you change your key, you need to send the new one to each of your friends and they have to remember to use the new one for passing you notes. For now, just take the default (gpg will ask for confirmation, so type y and hit enter). Next, fill in your name, email and comment and confirm them by typing o and then enter.

Next, enter a pass phrase. It can have spaces and it can be fairly long. Pick something easy to remember; a favorite quote or maybe a few lines from a song you like.

This next part may seem a bit weird, but gpg actually needs you to do some other stuff on the computer. Anything you like, but actually do things. Compose an email, check your favorite news aggregator, do some drawing, hit your head on the keyboard, kick the mouse around for a while, whatever you need to do. It may occasionally ask you to keep at it with messages like

Not enough random bytes available.  Please do some other work to give
the OS a chance to collect more entropy! (Need 148 more bytes)
..............+++++

Eventually, it'll give you back control of your terminal, and at that point your key has been created. The hard part is done.

Step 4: Share Your Keys

Ok, get those friends that I told you to have ready.

Each of you should run the command

gpg --armor --output your-name.txt --export your-name

(your-name should be the name you typed in as part of your information in Step 3). That will create your-name.txt; a text file that should look something like

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.10 (GNU/Linux)

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
PzbGgzGART8EBfSEbML6gOgvyXC3
=n+lE
-----END PGP PUBLIC KEY BLOCK-----

It won't look exactly the same, yours will be different, but it will be about that wide and surrounded by the --- PUBLIC KEY BLOCK --- tags. That's your public key. Give it to your friends and get theirs. It doesn't matter how; either bust out the USB sticks or hop on a network and use scp or just paste that text to your Deviant-Linked-Book-Space page or personal site. Each of you should get the key text files and run

gpg --import your-friends-name.txt

for every key you have. Once that's done, check that you have all your friends' keys imported by typing gpg --list-keys and hitting enter.

Step 5: Send A Message

Type up your message in a text file (we'll call it message.txt, but it can be any type of file) and then run

gpg --output message.gpg --encrypt --armor --recipient your-friend --recipient your-other-friend --recipient and-so-on message.txt

Here's what a text file containing "something something dark side" looks like after it has been encrypted.

-----BEGIN PGP MESSAGE-----
Version: GnuPG v1.4.10 (GNU/Linux)

hQIMAw1KmAJVwvKhAQ//ZZf/xMd9jSW0XDX1s5uVQB39XUQq/OGMBFZN1Dk1WMbA
zZFShccV44X8NSAJu0h5S1PCdPkgQAzjecoScya9IVnSQVeyGgDP5dv7ZU4KqQQS
Pacd5J0u0BbLrHaIxSfCwchLofwGxlt52m9lAMAzlTnC4hEGxkwqo1QlM3PZZrKY
5P8abtVIv2VZXDTaDBgm1Y9KuFRrGhJtvHG8GHsg5zLuO6+gTZG6TC3F/BM5BUZZ
m0KBc8ZbztVmYbI6w6qm24M2MHExQd5tHFCx1VS51kOJ6gRg25XOFHHi8E/+F92A
bFUSh+/ZAvuPTBkjIATNI5rri7xHb+0BU1GHVSDAV0DUkI0GDMy05Hddrt/G7ORi
dsT+7BK/WipFAWpERevW65ZjaiV+sQHfj/ErjcnYV1hjRWqKJdOp4jdHROQO8ZUD
HuB6iw93i0Zo4UUb71GzptEigUriBCoRbb0q5T2cXsen8YKVXdaDr9+MTiQd8lqr
vaO4Wc/BiEkVecI9X0N63aGEoVRLLNsmBlnJGxTmOD1+WfwI1e4xlA6F5dR80nyr
T2ZuFuvhOxtSUIvWw3nb++YT0Z6hzTgD6JzffDySdBXTSozDYoFtRV34+VhD3CS0
LCVCF3ZVU+4c/Yiv72BVB1V+bJXN9BR6rmAFDTNtjnlWdubdwf8eFTMXlbvc6t7S
XAExmTYEcONMmjYG2rreIA97/6tbIrSMeWD2d3n66mLkpI8lvJMaTsBh0tS21NQ2
/Z0ixQK6OH7Vz++qmFC3sCtLKT6U8zVFtEFtLRlGV1/StKF8tC5ygMPEfewC
=MvOO
-----END PGP MESSAGE-----

You can now send this to everyone. Email, Facebook, reddit, your blog, a random comment section, anything goes. Only people with the keys you specified as --recipients will be able to decrypt it.

Step 6: Read A Message

If you get a block like the above from one of your friends, save it to a text file called encrypted.gpg[4] and run

gpg --output decrypted.txt --decrypt encrypted.gpg

And decrypted.txt will now contain the note your friend passed you in plain text that you can read. Of course, anyone can read it now, so if it discusses anything you really want to keep secret, you should run shred on it once you've read it.

Boss Fight

Use the comment section of Joe Armstrong's Blog to send a message to a friend that only the two of you can read. If you're feeling adventurous, send it to all of the friends you got together in Step 1

Bonus Stage

Sending your friends messages that your parents can't read is nice, but if they catch you, they can still ground you until you decrypt it for them[5]. Ideally, you'd send your friends messages that your parents or teachers wouldn't even know are messages. To do that, you need a second program called steghide. On Debian GNU/Linux, just type apt-get install steghide as root. Cygwin supports this package too, but I have no idea how to get it on OS X so you Mac users are on your own here.

Get an image, like this one

encrypt your message, and then run

steghide embed -ef message.gpg -cf not-sure-if-secret.jpg

You will be asked for a pass phrase, leave it blank for now, but you should really agree to one with your friends and use it to protect these. You can now send your friends that image via email or imgur without raising suspicions (unless your parents are reading this blog). When your friends get it, they can run

steghide extract -sf not-sure-if-secret.jpg

(and enter the pass phrase if you set one) to get your encrypted message and then decrypt that to read what you sent them. You can use steghide to hide files in images or music that you can then send without raising suspicions.

Secret Boss Fight

Send your mailing list one of those stupid Fw: Fw: Fw: Fw: joke mails, but embed a secret message to one of your friends in the first picture. Ideally, that friend should be one of the people you send the email, otherwise you're needlessly spamming.

Secret Boss Fight -- Stage 2

Find a forum/reddit thread somewhere and carry on a steganographic, encrypted conversation about hipster ninjas with two or three of your friends.

Secret Boss Fight -- Final Stage

Sneak onto your friends' computer while they're in the washroom and change their desktop background to a steganographic message. Chuckle about it constantly. When asked "What's so funny", laugh maniacally and run out of the room with your stuff.


Footnotes

1 - [back] - Or teachers, or political enemies, or competitors, what-have-you

2 - [back] - If you don't have access to your own computer, you can use a usb stick to run Ubuntu live, or (better yet) TAILS and merely plug it into whatever computer you do actually have access to.

3 - [back] - If all of your friends can be trusted to keep secrets, you might opt to go with a symmetric cypher instead. The process of sending and receiving messages is more or less the same, but there's only one key that the entire group shares rather than there being two keys per person (a public and a private). The advantage is that there's less to keep track of. The downside is that if anyone finds out your key, all your notes can be cracked rather than just those sent to the person who let their key get compromised. I won't go through this method, but if you follow that link to the GnuPG manual, you should be able to figure it out.

4 - [back] - It doesn't actually need to be called that, just an example.

5 - [back] - They probably won't just ask for your key because they'd have no idea what to do with it.

Friday, December 30, 2011

XMonad Keybindings and an aside on Piracy

So I've gotten my x220 all set up the way I like. I ended up using XFCE as my desktop environment again, because if nothing else, I sometimes like an external monitor. Really, I expect to spend most of my time in Screen now that I've set up wicd. By the way, in case you're trying to get it to work and failing, you actually need to specify what wireless device it should use to connect to networks. This device is almost always wlan0, but a default isn't set so you need to do Shift+p and type it in manually in the preferences screen.

I'm still debating window managers, and currently have both StumpWM and XMonad installed. I've been using Stump pretty consistently lately, but I have noticed some inconveniences with it related to how window management works. The main things keeping me on it are the home-row mod "key" and the ability to override that "key" in case the program I'm using needs the keystroke. Taking a look at what XMonad config can do these days, here's how I would do the same

import XMonad
import XMonad.Config.Xfce

import qualified XMonad.StackSet as S
import XMonad.Actions.CycleWS
import XMonad.Actions.CycleWindows
import XMonad.Actions.WindowGo
import XMonad.Actions.GridSelect

import XMonad.Util.EZConfig
import XMonad.Util.CustomKeys

import qualified Data.Map as M

main = xmonad $ xfceConfig { modMask = mod4Mask }
       `additionalKeysP`
       [ ("M-p", dmenu_launch)
       , ("M-<Return>", spawn_term)
       , ("<Print>", spawn "xfce4-screenshooter")
       , ("C-t p", dmenu_launch)
       , ("C-t C-p", dmenu_launch)
       , ("C-t <Return>", spawn_term)
       , ("C-t w", nextScreen)
       , ("C-t e", prevScreen)
         
       , ("C-t j", windows S.focusDown)
       , ("C-t S-j", windows S.swapDown)
       , ("C-t k", windows S.focusUp)
       , ("C-t S-k", windows S.swapUp)
       , ("C-t g", goToSelected defaultGSConfig)
         
       , ("C-t <Space>", sendMessage NextLayout)
       , ("C-t h", sendMessage Shrink)
       , ("C-t l", sendMessage Expand)
       , ("C-t t", withFocused $ windows . S.sink)
         
       , ("C-t C-t", spawn "xdotool key ctrl+t") -- this is a lie
       ]
       
dmenu_launch = spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\""
spawn_term = spawn "xterm"

A couple of those are blatantly ridiculous (specifically the resizing options are less than helpful if you have to do C-t h C-t h C-t h rather than S h h h), and the xdotool command is entirely fictitious, as the comment claims. I'm actually not even sure why that is; the command works fine when I run it from terminal directly, and it also works fine on any keystroke other than C-t, in both Stump and XMonad. I'm beginning to think it may be a bug in xdotool. Incidentally, I realize that in this one specific situation, it may have been simpler to specify my own submap, but I like the emacs-style keys enough to keep them despite a bit of repetition.

Ok, massive gear shift incoming. Don't say I didn't warn you.

There's been a lot of discussion about piracy across the various reddits I frequent lately. Mostly related to the imminent SOPA and PROTECT IP bills set to pass in the US. The thing that's bothered me the entire way through most of the discussions is that there isn't a standard definition of "piracy". Personally, I'd prefer to take the Stallman approach, just outright rejecting the legitimacy of the term, but that ship has sailed[1]. There are political parties[2] using the term as a rallying cry for people in favor of digital freedom and privacy, and most people joining the conversation haven't been exposed to other terms.

You can't help but be biased if that's all the exposure you've had, recent hollywood romanticization of pirates notwithstanding. So, here are some things to keep in mind. I'm not claiming that this is the complete conversation, but this is the 101-level thought that needs to go into a framework for deciding what your opinion of the situation is.

First off, "piracy" in the software sense translates to "unauthorized copying" if you want to be a bit more pedantic about it. If we look at what DMCA, SOPA et al attempt to regulate, piracy includes, but isn't limited to distributing copyrighted works for profit. To start with, here are some examples of piracy in no particular order:

I'm not making a value judgement on which of the above is ok and which isn't, by the way, and you shouldn't either[3] if you hope to build a working understanding of piracy as it applies to copyrighted works. First off, with so many examples, lets try to extract some principles so that we can figure out how the concept is structured. Based on the above, the different examples, all of which are technically piracy, vary along at least 5 axes: commercial/non-commercial, public/private, incidental/central, singular/numerous, direct/transformitive. Note that none of these are binary either/or situations; they're all on a continuum.

Commercial vs. Non-commercial seems fairly straightforward; is the intent behind the copy to make money or not? It's not that cut and dried though. Copying something onto physical media takes money, and so does hosting a web server. So does recouping that cost count as a commercial venture? This is actually a stumbling block that Linux development hit early on; some people wanted to distribute the operating system, but the initial license forbade accepting money in return. Those would-be distributors contacted Linus about this and pointed out that even though they don't want to charge for the OS itself, the CDs they needed to use cost money, and it took time to produce them. Linus switched to the GPL2 as a result, and this sort of semi-commercial sharing (along with fully commercial distribution) is now perfectly legal.

On one end is "no money or goods change hands in compensation for the shared copy" (think exchanging mix-tapes or posting content on Reddit without actually selling anything), on the other is "I'm making more money the more copyrights are infringed" (think the DVD copying organization, or an advertising-supported model of filesharing)

Public vs. Private is more obviously on a continuum based on that example. On one end is something like "copying a CD or DVD you own onto your iPod", on the other is "Put it on the Pirate Bay", and between the two of them are networks of varying privacy ranging from your own family LAN, to a University-only network to a small-esque online community.

Incidental vs. Central; is the entire point of the copy the copyrighted material (as in the example of fan art), or is the point something else entirely and the copyrighted material happens to be there (as in the example of the first steps video)? A decent test for this is: if you strip out any questionably violating material, would the media still be interesting to its target audience?

Singular vs. Numerous is perhaps the most obviously measurable; how many infringements were there? At one end is making one copy of a music CD to put onto your own music device. At the other is making a few hundred thousand copies. Of course, this is made more ambiguous when you consider how digital technology actually works. In the technical sense, a new copy of a YouTube video is produced every time someone views it (even though the original poster only actually produced one copy; the upload from their computer to the YouTube server). Who takes the heat for the additional copies created when the video is viewed? Is a viewer more guilty when they use a service like keepvid to download the video rather than streming it even though the same number of copies are created either way? The technical aspects of the implementation really begin to matter if the law assumes that all copies are equally infringing.

Direct vs. Transformitive is obviously a continuum, but not very easy to measure. If you make a copy of a DVD, that's direct, but how much of a change can actually transform that media into a new work? I'm biased here, having gone through design school and come out the other end, so I'll only point out that merely obstructing pieces strategically changes the audience reaction. By the time you get to fan art/fic, entirely new works merely using existing settings and character names, it seems fair to claim that we should at least reflect on whether Fair Use might apply to the act of piracy in question.

So those are the axes on which piracy is currently defined. I should note that, according to related laws[4], all of these things qualify as piracy (and therefore constitute content that a website might get taken down over), even though only some of them are things which most humans would agree is immoral. There isn't an idea of degree or fair use inherent in the process of a DMCA takedown, or the upcoming SOPA takedowns, and in general the onus is on the accused to fight the accusation rather than on the accuser to prove harm. Is this piracy? [ ] yes [ ] no, and if [x] yes, it's criminal so take it down. Which should at least illustrate why I think it's important for people to understand the terms.

Many of the things we call "piracy" today are things that were considered either free or fair uses as recently as 20 years ago, many didn't exist as recently as 10 years ago, some were illegal then but probably shouldn't be anymore as a result of technological advancements, and finally some were and continue to be both unethical and illegal.

Now I hope you've been paying attention, because here comes the curveball.

Do you support piracy?

Richard Stallman: Attacking ships and killing the entire crew to take the cargo is very bad. Now, if you mean sharing, of course you should share. Sharing is good.

Sony/RIAA/MPAA/et al: No, all pirates should be executed (or at least fined their life savings and prevented from going near a computer ever again.)

You:[your answer here]

I hope you can see how both of the extreme stances are a bit ridiculous, and that a nuanced view needs to prevail if we're going to continue this decade with any semblance of freedom. And if we refer to the thought framework I proposed earlier, it's fairly obvious why each of the extreme views are ridiculous.

It is not ok to take the output of others' creative work without their permission and distribute it for your own profit without sharing any of the proceeds[5]. That sort of activity is not sharing, and probably should be a criminal act. The people who do this are spiritual descendants of the original book pirates; the guys who would set up a press to run off a few thousand Dickens or Twains and sell them to line their own pockets without supporting the original writers. That's one of the few forms of copyright infringements that you can call "theft" without me getting pissed off at you about the implications. There is clear harm being done to the author and legitimate publishers, there is money being made, the work is not transformitive, there are multitudes of copies, those copies are public, and the copyrighted work is central to the exercise. Lock those fuckers up.

It is ok to take copyrighted works that you've purchased and copy them as many times as you like for personal use. You should be able to copy your CDs onto your DVDs and onto your iPods and onto your computers, and you shouldn't have to fear getting arrested and being forced to prove the legitimacy of your copies over that. You should be able to make a backup of your entire hard drive for later restoration, including all copyrighted works present on that hard drive.

You also should be able to post snippets of copyrighted works for educational and critical purposes, and that cuts to the point I touched on earlier about how this page can itself arguably be considered piracy. In order to semi-intelligently discuss pirate sites like The Pirate Bay, YouTube and DeviantArt, I have to link to them. The discussion wouldn't be nearly as enlightening if you had to imagine how these sites worked based on my vague descriptions, but the act of linking connects you to these tools that you can potentially use to pirate media. The linking question is particularly relevant to sites like The Pirate Bay, which indirectly make money off of copyright infringement, but host none of the files. Conceptually, what's happening is that TPB is keeping a list of files on peoples' private computers, and handing you directions to them when you ask. If you torrent something, they're not involved at all after the introductions; the data is moving directly between you and some individuals at the other end.

It's probably this last kind of piracy that's prompting bills like SOPA; peer to peer sharing via torrents can really only be regulated by monitoring all communications on the internet ever, checking whether they contain copyrighted content and stopping them if they do. That sounds like a bad thing to me, but ultimately it'll fall on the public and public officials to decide whether an end to piracy is worth setting up the perfect world-wide surveillance state, criminalizing cryptography again, and giving up the fair use protections that let them discuss piracy intelligently. Hopefully, that'll be a no, but even if it isn't (or you don't think it should be) you should now be equipped to tell me why without resorting to "why do you support stealing?" like a trained parrot.


Footnotes

1 - [back] - No pun intended there. Seriously.

2 - [back] - In Canada too, by the way.

3 - [back] - Yet

4 - [back] - Both ones that are in effect, and ones that are still being discussed.

5 - [back] - This is a matter completely separate from whether it's wise for a creative worker to grant that permission. I'm not getting into that argument today, but most of my code is available under MIT/GPL licenses and all my illustration work is CC-BY-SA, so you can probably infer what side of that argument I'm on.

Monday, December 26, 2011

x220 and (unrelatedly) Portable Keyboards

So I finally got a new machine. A lenovo x220 laptop. It's kind of a lightweight, but even with an i3 and only 4gb of ram, it easily doubles the specs of my netbook (RIP, battery). Speaking of which, I also got a 9-cell battery for the new box. The cost of this unit was much lower than usual because of a promotional code I stumbled upon. I haven't actually had time to take it for a spin, but the battery came about 20% charged and told me that this would be enough for 2:30 hours of operation. That. Is. Awesome.

The drive in it still has Windows on it so I didn't poke around much like I said. My plan is to toss an SSD in there and throw in Debian as usual[1]. That hard drive is going to be a pain in the ass though. It is a standard 2.5 inch SATA drive, but it's very slightly shorter than the OCZ units I have lying around. That leaves me three options

  1. Go buy a new SSD for the laptop (plausible in a little while, but I'm sort of shopped out at the moment)
  2. Mod the laptop to accept slightly taller drives (this kills the warranty, and the laptop is still covered so I don't want to fuck with it)
  3. Mod one of my drives to fit a smaller space (this also kills the warranty, but my drives are long out of their warranty period, so I give precisely zero fucks)

Three is the obvious winner for the short term. I may still go get a new hard drive later; this unit supports SATA3, so I can get an extra little speed boost by shelling out. Sadly, it looks like SATA3 drives don't come smaller than 120GB, which means ~$200 or so. Incidentally, a SATA2 32GB drive is down to the $60 range these days, so if you're a Linux user that still has a 7200RPM, or even a 10k, you may want to pick an SSD up at these prices. The difference is noticeable.

Time Passes...

So that's done

Except that "mod" turned out to be a bit of an overstatement[2]. I literally just removed the case[3], braced it in the laptop drive bracket using some rubber stoppers, and secured the whole thing with some electrical tape. I'm not about to get fancy for an internal component; it just needs to work, and this does.

That's the old install of AMD64 Squeeze I already had on the drive, and it works mostly fine but I'll still need to re-install. The ethernet drivers in Squeeze don't support Intel's 82579LM; it's already been patched in Wheezy. There obviously aren't any plans to push new changes to the stable release, so I guess I'm moving up to testing. I don't anticipate any problems; unetbootin is running as we speak[4].

I have done a little bit of typing on it by this point; my intention was to finish this article with it, but the lack of network connectivity of any kind put a stop to that. I did notice that the keyboard was improperly set; the right side was ok, but the left side was actually vibrating with each keystroke. Undoing the keyboard screws on the back and re-setting it fixed that easily.

Installing Debian is pretty close to a non-event around here in terms of the attention it requires. Really, I've been thinking about prototyping some things. Firstly, the password-logger is proceeding apace, though I still don't have enough done to consider publishing. I'm at the stage where I can have it remember multiple passwords, cycle through them, and output them on a single button-press. Right now I'm playing around with a little 1x11 numeric LED display I picked up so that the unit will be able to display a label for each password, and I still want to put it together in such a way that I can add/change/delete passwords without having to recompile the thing.

In other news, you already know that my plans involved building a prototype of the chordite, but I've also been looking at other layouts and ideas for the same purposes. I'm only about half-way through building a chordite, but here are my preliminary thoughts about all the designs I've taken a look at[5].

Chordite

Pros:

  • one-handed (that lets you use the other hand for balance, for example, in a public transit situation)
  • data entry doesn't use fingertips (makes it easy to chord)
  • standard configuration covers 84 keys (the default key mapping reference is in that zip file)
  • simple construction (only 8 switches to wire up, and the rest can be built with a bit of cardboard and some wire coathanger)

Cons:

  • chords entered in serial (so it doesn't seem like you could easily use Emacs keystrokes of the style C-x C-h, and it seems like this would kneecap your speed since you're doing two or three effective key-presses per keystroke)
  • strapless (which may or may not be comfortable, but it does mean that you thumb needs to support the board rather than being available for its own keystrokes)

joestutes Wearable Keyboard

Pros:

  • one-handed
  • non-chording (each switch is one key; I'm assuming that this would be more accurate and faster than chording; it also seems like it would make it easier to memorize)
  • provides "mouse" through thumb-operated joystick

Cons:

  • emulates 40 keys total, so you need a reduced character set; this is not a coding keyboard
  • requires high level of precision control from each finger (I'm actually not even sure if this is a human thing, or just my mutant hands, but I can only reliably do fine-grained independent control with my index and middle finger; the ring and pinky fingers, even on my main hand, can't really move on their own very well and almost certainly wouldn't be able to do the sort of multi-directional movement that this thing seems to need)
  • construction involves wiring 40 separate switches and a joystick. That's not exactly simple.

Ergo Electronics Keyglove

Pros:

  • tons of keys
  • glove-based (you're not actually holding anything, so you can easily switch between typing and doing something else)
  • one-handed

Cons:

  • requires high-precision movements with all fingers (it works with some fairly densely packed contacts, and assumes you can hit them, with various levels of hand-contorting movements, with each finger).
  • single keystrokes, so doing Emacs chords seems difficult if not impossible

Carsten Mehring Keyglove (first prototype)

Pros:

  • tons of keys
  • glove-based
  • Emacs-style chord capability

Cons:

  • two-handed (even though you can switch between things easily, you need both hands dedicated to typing with this one; not sure if it would work that way in practice)
  • demo video translation is typeset set in Comic Sans (I'm still a designer, dammit)

Carsten Mehring Keyglove (second prototype)

Pros:

  • glove-based
  • one-handed

Cons:

  • not entirely sure how the keystrokes are generated, and therefore how many can actually be generated (it looks like it doesn't do Emacs chords either)

Twiddler

Pros:

  • one-handed
  • strapped (so your thumb is used for modifier keys)
  • provides "mouse" (mini joystick operated by thumb)
  • thumb operates modifier keys (so Emacs-style chords are easy to hit)

Cons

  • none that I can see, actually

Like I said, these are very preliminary thoughts. It's possible that actually using some is easier/more comfortable than I imagined based on the demo videos. A priori, it looks like the ideal is a strap-using, portable-joystick-style keyboard, or a two-handed glove model. Granted I've got different problems than the average user. Emacs use means I need to be able to chord multiple keys, and Tiling WM/keyboard reliance means I don't really want a pointing device most of the time.


Footnotes

1 - [back] - The documentation tells me that I'll actually need to set up one piece of non-free software to run my wireless card, which is sad, but I don't really have the time or patience to go hunt down an atheros-based card this week.

2 - [back] - Which is why you don't see pictures.

3 - [back] - Which is not nearly as dangerous for the drive on an SSD as it is on a traditional platter drive.

4 - [back] - Those of you who already have a Debian machine set up can actually just apt-get install unetbootin

5 - [back] - Ideally, I'll actually put together one of each and take them for a test-drive, but I have no idea how long that will actually take me.

Wednesday, December 14, 2011

Teensy Mode

Ok, this isn't one of those bullshit times where I just say "I'm going to bed" and then end up spending three hours explaining various shit at the expense of wakefulness the following day. Seriously, just one thing, I added a very quick, simple teensy-mode module to my emacs-utils project over at github. It turns out that I can't focus to save my life, but it has been damn enjoyable jumping into code so thoroughly after the set of weeks I've just been through.

The relevant bits are really just

(defun teensy-compile-current-file ()
  (interactive)
  (shell-command (format "%s %s" teensy-compile-file-command buffer-file-name)))

(defun teensy-compile-project ()
  (interactive)
  (shell-command teensy-compile-project-command))

(defun teensy-load-hex-file ()
  (interactive)
  (let ((hex-files (directory-files (file-name-directory buffer-file-name) nil "hex$"))
        (command (format "%s -mmcu=%s -wv " teensy-loader-program teensy-processor-type)))
    (cond ((not hex-files) (message "No hex file found"))
          ((= 1 (length hex-files)) (shell-command (concat command (car hex-files))))
          (t (shell-command (concat command (completing-read "Hex File: " hex-files)))))))

(defun teensy-compile-project-load-file ()
  (interactive)
  (progn (teensy-compile-project)
         (teensy-load-hex-file)))

the rest of it is various Emacs boilerplate like key and customization declarations. The one interesting thing I've learned from this experience is that the :options keyword does pretty much jack shit. In a hook customization, you can presumably at least see the list, but it still doesn't constrain your choices to what's there. It might be nice to have the option, since the customization I had in mind was the chip-type of your Teensy board (a required option that needs to be correct or it'll hang your chip, so it's sort of critical to get it correct).

I'm also flaking out on actual project compilation, opting to keep the make command involved, which has some seriaah fuck this, I'm going to sleep.

Tuesday, December 13, 2011

Teensy Fucking Passwords

I've been trying to get to as many Toronto Lisp Users' Group meetings as I can since missing one a couple months ago, though I'm not entirely sure it's good for me. The minutes of conversation/interesting thing ratio is approaching zero, to the point that it might be more accurate to start tracking interesting things/minute, which is excellent except that I have this day job thing which keeps me from spending every waking moment on interesting fiction, crazy languages(closed source warning) and crazier architectures. That's without even accounting for the fact that my wife and I have been beating down the Thalmor on a regular basis.

The last link is actually what got me off my ass and seriously interested in coding again in my spare time[1]. The thing is, as soon as I started looking at it, I had to admit that I have no fucking idea how to go about serious embedded programming, let alone how to do it with forth[2].

I also mentioned a little while ago that I tried poking around at the Teensy and found it to actually work on my machine[3]. The other thing I like is that Teensy lets me use Emacs for development, though there isn't a nicely integrated mode for compiling and loading projects/files onto the chip without jumping into an eshell buffer. I might want to sink some time into that...

No! Focus! No new shit right now!

Anyway, poking around is all very well, but I find that I really need a project of some sort to work on otherwise I don't internalize the concepts effectively. I do still eventually want to build myself a chordite (or something similar; relevant bit at the 2:10 mark), but that looks like it'll take a lot more industrial design and wiring than I'm in the mood for. At about the same time I was thinking about all of this, I stumbled upon a writeup about a hardware keylogger and an article entitled fuck passwords.

The second one is all the correct complaints about passwords and authentication that OpenID was supposed to almost solve, and that SSH keys do solve for those of us lucky enough to be using SSH. The problems remaining are

  • Not every system uses OpenID (and even if they did, you still need to authenticate to your OpenID provider, most of whom use passwords :p)
  • The ones that don't use OpenID are the ones you want to be most secure (banks and such), and their password constraints make them the hardest to remember
  • The most secure thing to do is to come up with a separate password for every service and rotate them regularly, but this is hard to memorize

On top of that, there are a few things[4] that can't be solved by OpenID or RSA keys. Just keep that problem in mind.

The other link is called "PHUKD". And a quick glance at that spec page will tell you exactly why. It's a pranking/surveillance tool whose accompanying paper is entitled Plug and Prey: Malicious USB Devices. The way that it works ostensibly this. First, you surreptitiously connect the device to your targets' computer. Then, depending on what you want, you either set the thing to record keyboard/mouse input for later collection, or have it emulate a mouse/keyboard to run random malicious programs[5]. Like I said, the phukd library makes it a fairly explicit goal to screw with people's computers in this way with a separate CommandAtRunBar for each of Windows, GNOME and OS X. That's just ... mean.

Putting it all together, and as usual, I'm almost 100% sure that I'm not even remotely the first person to think this up, but why not just put your passwords onto a hardware key?

It'll be something like the actual teensy keyboard implementation, but bind each button to a sequence of N characters that get typed out whenever it's pushed. Here's what I came up with after a bit of fiddling (pardon the giant code block, C isn't quite as easy for me to express ideas in).

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <string.h>
#include "usb_keyboard.h"

#define LED_CONFIG      (DDRD |= (1<<6))
#define LED_ON          (PORTD &= ~(1<<6))
#define LED_OFF         (PORTD |= (1<<6))
#define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))

uint16_t idle_count=0;

int key_from_char(char c){
  if (c >= 'a' && c <= 'z') return c - 93;
  if (c >= 'A' && c <= 'Z') return c - 61;
  if (c >= '1' && c <= '9') return c - 19;
  if (c == '0') return 39; // 0 is low in ASCII, high in the USB key definition
 
  // there's no easy mapping between the other ASCII characters and 
  // USB keyboard keys, so these are all 
  switch (c) {
  case 32: return KEY_SPACE; break;
  case 33: return KEY_1; break;
  case 34: return KEY_QUOTE; break;
  case 35: return KEY_NUMBER; break;
  case 36: return KEY_4; break;
  case 37: return KEY_5; break;
  case 38: return KEY_7; break;
  case 39: return KEY_QUOTE; break;
  case 40: return KEY_9; break;
  case 41: return KEY_0; break;
  case 42: return KEYPAD_ASTERIX; break;
  case 43: return KEYPAD_PLUS; break;
  case 44: return KEY_COMMA; break;
  case 45: return KEYPAD_MINUS; break;
  case 46: return KEY_PERIOD; break;
  case 47: return KEYPAD_SLASH; break;

  case 58: return KEY_SEMICOLON; break;
  case 59: return KEY_SEMICOLON; break;
  case 60: return KEY_COMMA; break;
  case 61: return KEY_EQUAL; break;
  case 62: return KEY_PERIOD; break;
  case 63: return KEY_SLASH; break;
  case 64: return KEY_2; break;

  case 91: return KEY_LEFT_BRACE; break;
  case 92: return KEY_BACKSLASH; break;
  case 93: return KEY_RIGHT_BRACE; break;
  case 94: return KEY_6; break;
  case 95: return KEY_MINUS; break;
  case 96: return KEY_TILDE; break;

  case 123: return KEY_LEFT_BRACE; break;
  case 124: return KEY_BACKSLASH; break;
  case 125: return KEY_RIGHT_BRACE; break;
  case 126: return KEY_TILDE; break;

  default: return 0;
  }
}

int modifier_from_char(char c){
  if ((c >= 'A' && c <= 'Z') ||
      c == 33 || c == 34 ||
      (c >= 36 && c <= 38) ||
      c == 40 || c == 41 ||
      c == 58 || c == 60 ||
      (c >= 62 && c <= 64) ||
      c == 94 || c == 95 ||
      (c >= 123 && c <= 126)) return KEY_SHIFT;
  
  return 0;
}

int8_t usb_keyboard_print(char *s){
  int s_len = strlen(s);
  int i;

  for(i = 0; i < s_len; i++){
    usb_keyboard_press(key_from_char(s[i]), modifier_from_char(s[i]));
  }
}

int main(void) {
  uint8_t b, mask, i;
  uint8_t b_previous=0xFF;
  CPU_PRESCALE(0); // set for 16 MHz clock

  DDRB = 0x00;
  PORTB = 0xFF;
 
  usb_init();
  while (!usb_configured()) /* wait */ ;
  _delay_ms(1000);

  while (1) {
    b = PINB;
    mask = 1;

    for (i=0; i<8; i++) {
      if (((b & mask) == 0) && (b_previous & mask) != 0) {
        usb_keyboard_print("abcdABCD1234!@#$%^&*()_+|~{}:\">?<-=\\`[];',./");
      }
      mask = mask << 1;
    }

    b_previous = b;
    _delay_ms(2);
  }
}

It's actually very close to the example code that runs the keyboard, except that I've modified it to take ASCII strings as input and pretend to type them out. I didn't complicate things (yet) by trying to figure out how to hook up an LED or micro SD card. It just keeps passwords in as part of the program source, and outputs one password per button.

I'm going to develop this over the next little while, at least while my interest is focused enough on it, into a unit that stores a bunch of passwords, lets me select which one I want (indexed by what the password belongs to), and output it. Ideally, I'd also get this down to a package small enough that I could just put on a physical keychain. That's a pretty good learning project for the short term.

Anyway, full circle with the fucking of passwords, when I'm done, this will be a little machine that I can plug into any USB-capable computer, push a button or two, and have it output (with much better recall and more accuracy than I could) a password that can be as massive and unique as I damn well please. It's not computer-specific (so I don't need to worry about syncing it, except for backup purposes), and it works anywhere I'd want to plug in an actual keyboard. That should kill the security problems dead. Of course, I'll need to keep some sort of safeguard in case it stops working or falls into the wrong hands, but at least I won't be forced to pick between convenience and security anymore.

It seems like a pretty obvious, elegant solution to the problem, conceptually. I can see why it hasn't caught on yet though; if you want to use something like this, you basically need to build it yourself. If I had to buy one of these dongles rather than build one, there's no chance in hell that I'd do it. I'd have to either really, truly, trust the provider enough to believe that they'd never snoop on my passwords or log unrelated information, OR I'd need to get a blank key and the source code and then put them together myself. There are just too many fragile points to do it any other way.

And that's about it. Oh, actually, I did toss this code up to codereview, in case someone out there is actually a C coder and wants to tear me a new one about something. I'm still chewing through clomments issues, and I've actually done some real work on cl-chan which I'll hopefully be kicking up soon; just as soon as I decide how to treat the image manipulation pieces.


Footnotes

1 - [back] - If you're also interested, but don't want to drop the $500 on a full, two-chip eval board, you can also get a breadboard kit for about $60 total, though you have to buy the individual chip, power supply and breakout board.

2 - [back] - My only experience with it was writing a few toy programs in gforth, which I get the feeling won't be very useful.

3 - [back] - In marked contrast to the more popular Arduino.

4 - [back] - Like the actual, physical computer I use everyday.

5 - [back] - Or just commandeer the browser to take your target to goatse

Friday, December 2, 2011

Clomment Moderation

It's idea-peeling time, so if you're here for insights, go away. I'm just trying to reason things out to myself.

I'm still trying to tackle the moderation system for clomments in some reasonably simple, reasonably performant way.

The questions I've got around my head are all related to the fact that each person won't be hosting their own server. As much as I'd like that to be the case because of how simple it would make things, it probably won't work out that way. This means that there'll be a need for someone other than the clomments server operator to have moderation powers on the server.

The authentication can all be handled using the OpenID protocol[1], but moderation in a system like this adds some complexity.

Primarily, those complications come from the fact that the model here is a large number of servers each handling the comments sections of a small number of sites. That's not necessarily even a design point, just something I'll have to make peace with if I want to release this under AGPL. So it would be a good idea to avoid having to sign up at each clomments server in order to moderate the comments it stores. Establishing ownership of the domain could be handled by the OpenID protocol, but it won't necessarily be doing so; I don't want it to be a requirement that you host your own identity.

So what I'd really want is a way to moderate comments that doesn't involve setting up an account. What I've got in mind is something like

  • allow anyone to "moderate" clomments on a given page
  • moderating doesn't actually delete anything; you can hide or approve a comment
  • as part of calling the clomments server, the user specifies
    1. the moderator account name(s) for this page
    2. how the page treats new comments (hide-until-approved or show-unless-flagged)
  • the clomments server responds with a subset of comments for the given page that fit the specified moderator/behavior

So, what does this get me?

First, a way of moderating clomments without keeping track of anything user-related. If I use OpenID and this kind of soft-moderation as the first step, I don't have to care who owns what domain or who moderates what page. An anonymous 4chan-style comments system means that I don't have to care who posts what comment. The only person has "account information" on the server is ... the person hosting it (their DB user name and password), and that's sort of unavoidable.

Second, likely a bunch of performance headaches. If I'm keeping moderation sets separate from the comment tree, I need a way of merging them for display purposes. I can probably store them in a way that makes it relatively simple to figure out what comments should be shown based on whose approvals/flags need to be taken into consideration.

Third, a bit of added robustness against intrusions. If someone finds out a site owners credentials and "deletes" all comments on a given page, for instance, no real damage has been done. As soon as control of the account is restored, all removed items can be reinstated.

Finally, some potential support for future features. For instance, if I'm letting anyone "moderate", then it would be pretty cool to write a client at some point in the future that lets a viewer apply their own preferences on top of what's been decided by the moderators.

Ok, so I guess I'm definitely doing it this way. The difficult part is really going to be figuring out how to store mod changes in a way that can be queried efficiently along with the comments and then acted upon in the display layer. The simplest way of the top of my head would be something like

(def-view-class moderation ()
  ((id :type integer :accessor id :initarg :id :db-constraints (:not-null :auto-increment) :db-kind :key)
   (user-id :type integer :reader user-id :initarg :user-id)
   (comment-id :type integer :reader comment-id :initarg :comment-id)
   (page-id :type integer :reader page-id :initarg :page-id)

   (stat :type keyword :accessor stat :initarg :stat)))

At which point we'd get the comments, plus mods, out by calling a method that looks something like

(defmethod comments ((page page) &rest moderators)
  (select 'comment 'moderation
          :where [and [null [slot-value 'comment 'parent]]
                      [= [slot-value 'comment 'page-id] (id page)]
                      [= user-id [or moderators]]]))

That wouldn't run, incidentally, it's just pseudo code of what we'd want. Take a page and a list of moderators (in descending order of authority) and get the comment plus the first moderation on the list. And tadaah, what you have is a list of ([comment] [moderation]) pairs that tell you what should be shown. In fact, it may actually be better to have separate methods for "hide-until-approved" and "show-unless-flagged" sites so that the query itself would only return comments that need to be shown.

It's too late for me to get into thinking about that now though. Hopefully, morning me will have some good ideas on how to translate this bullshit into some vaguely runnable code.


Footnotes

1 - [back] - Which, just as a side-note, seems to be much simpler and more flexible than I sort of imagined it being. Check out the wiki page for a breakdown.