2011-09-20

PyMeta2 Example

I found it difficult to find examples on the use of PyMeta2, an implememtation of the OMeta language in Python. I mamaged to put together a small example myself on how to parse a PGP public key block and extract the DSA key.

pgp.py:

from pymeta.grammar import OMeta

# Base 64 decoder, see RFC 2045.
grammar = """
code = letter:x ?(x <= 'Z') -> (ord(x) - ord('A'))
| letter:x ?(x <= 'z') -> (ord(x) - ord('a') + 26)
| digit:y -> (ord(y) - ord('0') + 52)
| '+' -> (62)
| '/' -> (63)
sixbits = spaces code:z -> (z)
| code:z -> (z)
pad = spaces '='
| '='
group = sixbits:a sixbits:b sixbits:c sixbits:d
-> ([(a<<2)+(b>>4), ((b&0xf)<<4)+(c>>2), ((c&0x3)<<6)+d])
| sixbits:a sixbits:b sixbits:c pad
-> ([(a<<2)+(b>>4), ((b&0xf)<<4)+(c>>2)])
| sixbits:a sixbits:b pad pad
-> ([(a<<2)+(b>>4)])
data = data:s group:z -> (s + z)
| group:z -> (z)
packet = data:d spaces '=' data:c spaces end -> ({'data': d, 'checksum': c})
"""

parser = OMeta.makeGrammar(grammar, {})

# Data from PGP PUBLIC KEY BLOCK
code = """
mQGiBE5cxWkRBACgm0DoNQj5HTTcQLL/eooKYMJTmYiINk0bSfQ46+R1iQliL/67
K0MTUZltFv3z70ATY9HulMgmlutDsFRGG4yUWceQX0qNBbJv2Q7FzB2rAFd/XfHT
tB8EOYc1fZk2RCDk0m5Ef33kwVXMm7/+MyvJ4GWxd8rvEsvrLdVZGufZ1wCg+K9p
Qgm542nvRI8LZN91yZGEiMcD/Rv6+oYykhOfU4ozxpOv2CLjzF3qk+D8TvVFEDrE
yjfoZZBGBHWY8bWyMsWONjk3o2DFSwQ5qwhz/bgcUtTPj8qU9K8KyrLB1mmiRTj1
+6svwUORoOCqKmTXkna7bmzzdlsqRQR2g9AdMHuIDkynyE7PBEYO6r7roUZZVtHU
TIRiBACRiV9ezsDsKRNfFBvoI5Lip7aA81E3+aesDGvGLqCeje3NIJfZ5q0q02iE
WTkNduAwxjcP+Q6QnakCoTSK7tlyiFWTw+bc0fq4V72hey+PqBclk8gRFDoCSuCF
vJOty98GfATei8fBJIpL4+hLNSGbN6QIKWtakS3IorWVXfeqHrQHVGFjdGl2b4hg
BBMRAgAgBQJOXMVpAhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQ9t723H2z
gCa0hgCcCH6NcL6fBrf3uHckMAFvPLub0SYAn2HeLhGECfI8pJ06S7aT/oPX0tyU
uQENBE5cxWkQBACGpC50KVpoy+4QmyIUHtSVQ4gbfeGOMmHOZlRRs41t93iqdf8F
y0aDUiK1G4AbnpHgJL/IFfH2HUPn1v7A4zRP9kd14k7+V5hjtiFj6m+USz/9M2Cd
CCWC0b7JAwK0egCDoGfdDPZBXF73ec5ObntinBg+YqBy7GDMCsFgZ8fjKwADBgP9
HUCsOp+BikAKHErqi5p73vRkzGq4kLs32ixwWP/6F2wcR2mJlgR7ET0gopvlkx9A
fDptYO4TG0nNVtBF/GqEbUIBcEfvpDVtVgstW25Aj367x06ySOWD1A/7No3j2YY8
bXPRxTDmUpyJNhcFDauaSClffI75UbYTMreuG9JbiYuISQQYEQIACQUCTlzFaQIb
DAAKCRD23vbcfbOAJqBvAKDocpghK+6fgmDiz6NDnWsnjKHOLgCfVuO99xoUFd+b
IWBs+tbQvkoRK8U=
=9tpq
"""

packet = parser(code).apply("packet")


# Checksum calculator
def crc24(octets):
INIT = 0xB704CE
POLY = 0x1864CFB
crc = INIT
for octet in octets:
crc ^= (octet << 16)
for i in xrange(8):
crc <<= 1
if crc & 0x1000000: crc ^= POLY
return crc & 0xFFFFFF

# Compare values in checksum manually
checksum = list()

# Checksum in code
checksum.append(packet[0]['checksum'][0]*256*256 + packet[0]['checksum'][1]*256 + packet[0]['checksum'][2])

# Calculated checksum
checksum.append(crc24(packet[0]['data']))


# DSA public-key packet parser, see RFC 4880
blockgrammar = """
number 0 = -> (0)
number :m = number(m-1):msb anything:lsb -> (msb*256+lsb)

length 0 = number(1):l -> (l)
length 1 = number(2):l -> (l)
length 2 = number(4):l -> (l)

mpi = number(2):bits number((bits+7)/8):i -> ([bits, i])

block = anything:tag length(tag&0x03):l anything:ver number(4):t anything:algo mpi:p mpi:q mpi:g mpi:y
-> ({'tag': (tag&0x7f)>>2, 'length': l, 'version': ver, 'time': t, 'algorithm': algo, 'p': p, 'q': q, 'g': g, 'y': y})
"""

blockparser = OMeta.makeGrammar(blockgrammar, {})

block = blockparser(packet[0]['data']).apply("block")


A session using this code would look something like this:

Python 2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import pgp
>>> pgp.packet
({'checksum': [246, 218, 106], 'data': [153, 1, 162, 4, 78, 92, 197, 105, 17, 4,
0, 160, 155, 64, 232, 53, 8, 249, 29, 52, 220, 64, 178, 255, 122, 138, 10, 96,
194, 83, 153, 136, 136, 54, 77, 27, 73, 244, 56, 235, 228, 117, 137, 9, 98, 47,
254, 187, 43, 67, 19, 81, 153, 109, 22, 253, 243, 239, 64, 19, 99, 209, 238, 148
, 200, 38, 150, 235, 67, 176, 84, 70, 27, 140, 148, 89, 199, 144, 95, 74, 141, 5
, 178, 111, 217, 14, 197, 204, 29, 171, 0, 87, 127, 93, 241, 211, 180, 31, 4, 57
, 135, 53, 125, 153, 54, 68, 32, 228, 210, 110, 68, 127, 125, 228, 193, 85, 204,
155, 191, 254, 51, 43, 201, 224, 101, 177, 119, 202, 239, 18, 203, 235, 45, 213
, 89, 26, 231, 217, 215, 0, 160, 248, 175, 105, 66, 9, 185, 227, 105, 239, 68, 1
43, 11, 100, 223, 117, 201, 145, 132, 136, 199, 3, 253, 27, 250, 250, 134, 50, 1
46, 19, 159, 83, 138, 51, 198, 147, 175, 216, 34, 227, 204, 93, 234, 147, 224, 2
52, 78, 245, 69, 16, 58, 196, 202, 55, 232, 101, 144, 70, 4, 117, 152, 241, 181,
178, 50, 197, 142, 54, 57, 55, 163, 96, 197, 75, 4, 57, 171, 8, 115, 253, 184,
28, 82, 212, 207, 143, 202, 148, 244, 175, 10, 202, 178, 193, 214, 105, 162, 69,
56, 245, 251, 171, 47, 193, 67, 145, 160, 224, 170, 42, 100, 215, 146, 118, 187
, 110, 108, 243, 118, 91, 42, 69, 4, 118, 131, 208, 29, 48, 123, 136, 14, 76, 16
7, 200, 78, 207, 4, 70, 14, 234, 190, 235, 161, 70, 89, 86, 209, 212, 76, 132, 9
8, 4, 0, 145, 137, 95, 94, 206, 192, 236, 41, 19, 95, 20, 27, 232, 35, 146, 226,
167, 182, 128, 243, 81, 55, 249, 167, 172, 12, 107, 198, 46, 160, 158, 141, 237
, 205, 32, 151, 217, 230, 173, 42, 211, 104, 132, 89, 57, 13, 118, 224, 48, 198,
55, 15, 249, 14, 144, 157, 169, 2, 161, 52, 138, 238, 217, 114, 136, 85, 147, 1
95, 230, 220, 209, 250, 184, 87, 189, 161, 123, 47, 143, 168, 23, 37, 147, 200,
17, 20, 58, 2, 74, 224, 133, 188, 147, 173, 203, 223, 6, 124, 4, 222, 139, 199,
193, 36, 138, 75, 227, 232, 75, 53, 33, 155, 55, 164, 8, 41, 107, 90, 145, 45, 2
00, 162, 181, 149, 93, 247, 170, 30, 180, 7, 84, 97, 99, 116, 105, 118, 111, 136
, 96, 4, 19, 17, 2, 0, 32, 5, 2, 78, 92, 197, 105, 2, 27, 3, 6, 11, 9, 8, 7, 3,
2, 4, 21, 2, 8, 3, 4, 22, 2, 3, 1, 2, 30, 1, 2, 23, 128, 0, 10, 9, 16, 246, 222,
246, 220, 125, 179, 128, 38, 180, 134, 0, 156, 8, 126, 141, 112, 190, 159, 6, 1
83, 247, 184, 119, 36, 48, 1, 111, 60, 187, 155, 209, 38, 0, 159, 97, 222, 46, 1
7, 132, 9, 242, 60, 164, 157, 58, 75, 182, 147, 254, 131, 215, 210, 220, 148, 18
5, 1, 13, 4, 78, 92, 197, 105, 16, 4, 0, 134, 164, 46, 116, 41, 90, 104, 203, 23
8, 16, 155, 34, 20, 30, 212, 149, 67, 136, 27, 125, 225, 142, 50, 97, 206, 102,
84, 81, 179, 141, 109, 247, 120, 170, 117, 255, 5, 203, 70, 131, 82, 34, 181, 27
, 128, 27, 158, 145, 224, 36, 191, 200, 21, 241, 246, 29, 67, 231, 214, 254, 192
, 227, 52, 79, 246, 71, 117, 226, 78, 254, 87, 152, 99, 182, 33, 99, 234, 111, 1
48, 75, 63, 253, 51, 96, 157, 8, 37, 130, 209, 190, 201, 3, 2, 180, 122, 0, 131,
160, 103, 221, 12, 246, 65, 92, 94, 247, 121, 206, 78, 110, 123, 98, 156, 24, 6
2, 98, 160, 114, 236, 96, 204, 10, 193, 96, 103, 199, 227, 43, 0, 3, 6, 3, 253,
29, 64, 172, 58, 159, 129, 138, 64, 10, 28, 74, 234, 139, 154, 123, 222, 244, 10
0, 204, 106, 184, 144, 187, 55, 218, 44, 112, 88, 255, 250, 23, 108, 28, 71, 105
, 137, 150, 4, 123, 17, 61, 32, 162, 155, 229, 147, 31, 64, 124, 58, 109, 96, 23
8, 19, 27, 73, 205, 86, 208, 69, 252, 106, 132, 109, 66, 1, 112, 71, 239, 164, 5
3, 109, 86, 11, 45, 91, 110, 64, 143, 126, 187, 199, 78, 178, 72, 229, 131, 212,
15, 251, 54, 141, 227, 217, 134, 60, 109, 115, 209, 197, 48, 230, 82, 156, 137,
54, 23, 5, 13, 171, 154, 72, 41, 95, 124, 142, 249, 81, 182, 19, 50, 183, 174,
27, 210, 91, 137, 139, 136, 73, 4, 24, 17, 2, 0, 9, 5, 2, 78, 92, 197, 105, 2, 2
7, 12, 0, 10, 9, 16, 246, 222, 246, 220, 125, 179, 128, 38, 160, 111, 0, 160, 23
2, 114, 152, 33, 43, 238, 159, 130, 96, 226, 207, 163, 67, 157, 107, 39, 140, 16
1, 206, 46, 0, 159, 86, 227, 189, 247, 26, 20, 21, 223, 155, 33, 96, 108, 250, 2
14, 208, 190, 74, 17, 43, 197]}, _MaybeParseError(1194, [('message', 'end of inp
ut')]))
>>> pgp.checksum
[16177770, 16177770]
>>> pgp.block
({'q': [160, 1419741510835642352048304995189763742904405493959L], 'p': [1024, 11
27816910289527974061075044173534361876859840537664278626449706640702688418029301
77627320915504464134384919932726006749358191240819384430019080661316770630666273
73305307310915789743932633603785654698308793554266637396727988530315573294163969
5505278895830548483021403317423418546610032441185543867874125797847L], 'length':
418, 'tag': 6, 'g': [1021, 1964849467881428710466800502308496822275241438855066
35238515330556083042068220950919284412399840170713771945402998891574208390341039
32135373724979642618657215923305914952852917206440714185965098155864193815365920
66817031665349355835965700904192728740918402151294857530330803724346461299359845
6219939378070626L], 'version': 4, 'algorithm': 17, 'time': 1314702697, 'y': [102
4, 10219928411694978026422720720306614004007482190710640189044175475269784538174
10827189912669049493898343302013241201652486175229135262490562087262759959706358
79842784809961205983751216640065755418899948380797160593911267435665817196921472
667773602697266619313590993808832086874091786796761538144131865973860894L]}, _Ma
ybeParseError(293, [('expected', None, 0)]))
>>>

2011-09-16

Compass Belt

So, I made my own compass belt, like many others. I was inspired by the Wired article published a couple of years ago.



The core parts are a HMC6352 compass module connected to a 3.3V Arduino Pro Mini. A ULN2803 with eight Darlington drivers supply power to the vibrators positioned at even intervals around the belt. Everything is powered by a 9V battery.



I found some sample code to read the bearing from the compass, and then added the vibrator control myself.


#include <Wire.h>

int compassAddress = 0x42 >> 1; // From datasheet compass address is 0x42
// shift the address 1 bit right, the Wire library only needs the 7
// most significant bits for the address
int reading = 0;

int bearing = 0;
int offset = 10;

void setup()
{
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); // start serial communication at 9600bps
pinMode(13, OUTPUT);
digitalWrite(13, HIGH);

pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
}

void loop()
{
// step 1: instruct sensor to read echoes
Wire.beginTransmission(compassAddress); // transmit to device
// the address specified in the datasheet is 66 (0x42)
// but i2c adressing uses the high 7 bits so it's 33
Wire.send('A'); // command sensor to measure angle
Wire.endTransmission(); // stop transmitting

// step 2: wait for readings to happen
delay(10); // datasheet suggests at least 6000 microseconds

// step 3: request reading from sensor
Wire.requestFrom(compassAddress, 2); // request 2 bytes from slave device #33

// step 4: receive reading from sensor
if (2 <= Wire.available()) // if two bytes were received
{
reading = Wire.receive(); // receive high byte (overwrites previous reading)
reading = reading << 8; // shift high byte to be high 8 bits
reading += Wire.receive(); // receive low byte as lower 8 bits
reading /= 10;
Serial.println(reading); // print the reading
}

// Activate vibrators
offset = -offset;
bearing = reading + 180 + offset;
if (bearing > 359) bearing -= 360;
if (bearing < 0) bearing += 360;

digitalWrite(9, bearing >= 0 && bearing < 45 ? HIGH : LOW);
digitalWrite(8, bearing >= 45 && bearing < 90 ? HIGH : LOW);
digitalWrite(7, bearing >= 90 && bearing < 135 ? HIGH : LOW);
digitalWrite(6, bearing >= 135 && bearing < 180 ? HIGH : LOW);
digitalWrite(5, bearing >= 180 && bearing < 225 ? HIGH : LOW);
digitalWrite(4, bearing >= 225 && bearing < 270 ? HIGH : LOW);
digitalWrite(3, bearing >= 270 && bearing < 315 ? HIGH : LOW);
digitalWrite(2, bearing >= 315 && bearing < 360 ? HIGH : LOW);

delay(10);

digitalWrite(9, LOW);
digitalWrite(8, LOW);
digitalWrite(7, LOW);
digitalWrite(6, LOW);
digitalWrite(5, LOW);
digitalWrite(4, LOW);
digitalWrite(3, LOW);
digitalWrite(2, LOW);
}


The vibrators are glued on velcro, to be easily adjustable for various waist sizes. The cables are nicely embedded in velcro as well.

One of my friends advised me not to go on the subway with this thing, unless I wanted to get arrested. May you live in interesting times.

2011-05-19

Albumplayer - A MPD Web Client

I have written a simple web client for the Music Player Daemon (MPD). It is suitable to use on mobile devices, like my Android phone. It allows me to select artists from my music collection and play their music on my headless media frontend.

To do anything useful, Albumplayer requires an MPD instance. Install that first. Then, extract albumplayer.tgz where you want it located on your web server. Edit mpd.expect to connect to your MPD instance. (Change the IP address.) Albumplayer is written in bash, expect, grep, sed, awk, Python and PHP. Make sure you have them installed on your system before use.

I tried a couple of lightweight web clients before I wrote Albumplayer, but all seemed to require that I first defined playlists in MPD, which then could be played in the client. That is not how I want it to work. I want to select artists directly from the database and play their music in a straightforward manner.

Albumplayer is completely insecure. Don't run it on a public web server.

Update DB will not work in the general case, when it fails to finish before Expect terminates the connection. An opportunity for improvement.

Enjoy your music.