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)]))
>>>

No comments: