Thursday, April 30, 2015

It's not always what it seems

Recently I was moving Python CGI code to an Apache server and received the following error messages.

          suexec failure: could not open log file
          fopen: Permission denied
          Premature end of script headers: mc.cgi

I checked and double checked permissions, links, names for embedded spaces and more.  Googling suggested a few more things like inappropriate EOL constructs.  No Joy.

In the end I discovered a fix that had nothing to do with permissions or a log file.  ...nor was there any such suggestion from the search engines.  My problem was an incorrect shebang (#!) for the CGI script.  After fixing that all was well. 

Wednesday, July 06, 2011

Low cost GPS on a Mac

I recently acquired a copy of Shai Vaingast's book, "Beginning Python Visualization" and his examples are based on GPS data. Not having a source of GPS data and not wanting to spend a lot of money, for $19 I bought a USB GPS dongle, a UniTraQ UD-731. Its package contained the GPS receiver, a short USB extension cable, and a miniCD with user-level Windows apps. Because I cannot easily read miniCDs, I prefer Mac, and I wanted to read and parse the data myself, I was on my own. But the price was right.

Having worked some with USB I started there, but soon discovered the protocol was overly complex for what I wanted to do. Poking around I learned the UD-731 is really a serial device behind a USB interface and that a serial-over-USB driver might be all I needed. Indeed, SiLabs ( has such drivers for the Mac, Linux, and several flavors of Windows. The Mac driver came with it's own installer (reboot required) and installing PySerial was a snap. A quick look in /dev revealed the device name, but you might try:
    ls /dev | fgrep tty.   [include the trailing dot]
Alternatively, you could use   ls /dev/tty.*

The final step was to write a short Python program and here are the essential elements of that program. Note that the UD-731 talks at 4800 baud.
from serial   import Serial
from binascii import unhexlify

def cksum(msg):
    """ the check sum is a simple XOR over all 
        characters after the leading '$' up to 
        but not including the '*'.
    cksum = 0
    for byt in msg[1:]:
        if byt == '*': break
        cksum ^= ord(byt)
    return cksum

ser = Serial('/dev/tty.SLAB_USBtoUART', 4800)
if ser.isOpen():
    print '\nUse cntl-C to quit \n'
    # consume the first one as it may be partial
    while True:
        gps_stmt  = ser.readline()
        msg_cksum = gps_stmt.split('*')[1].strip()
        old_cksum = ord(unhexlify(msg_cksum))
        new_cksum = cksum(gps_stmt)
        if new_cksum != old_cksum:
            print '  ** checksum error >>>',
        print gps_stmt, 
If all you want is to see the output stream, from Terminal you can enter:
    screen /dev/tty.SLAB_USBtoUART 4800

UD-731 Data Sheet: Data sheet V1.3-20091217.pdf
UD-731 User Manual: User Manual V1.3-20100730.pdf

Labels: ,

Monday, May 16, 2011

Porting to Python3

I put off converting my Python code to Python3 for the usual reasons, including lack of 3rd-party library support and perceived issues with Unicode. But not all my work are applications; a significant portion are crypto libraries. I should at least convert them to Python3 so I do not become part of the problem.

When hashing data for digital signatures the data must always have a consistent representation, be of consistent length, and have zero endian issues. Change anything, even a single bit, and the hashes will not agree. While hashing Unicode is technically possible, the community is not sufficiently well versed on how to [consistently] do this correctly. (If in doubt, serious expertise should be consulted.) Likewise, software tools are not standardized, can produce varying results, and interoperability is a major requirement.

In acknowledgement of this state of affairs, today's conventional wisdom says we should be hashing text in the lowest common denominator, ASCII text or perhaps Latin-1 single-byte encodings. KISS. Since hashes are seldom used in isolation (cryptographically), all my other crypto routines need to have consistent data passing protocols, the simpler the better. Unicode does not [easily] meet this requirement.

After a couple of half-hearted false starts I decided it best to start over, beginning with some serious homework. I chose for my first "for-real" conversion a relatively simple Blowfish crypto library,, and its test procedures. Here are a few lessons learned from that exercise.

Unicode may be a boring topic but do read these first.
And as usual, there's more via Google.

Converting took a while. Some of that was the learning curve, but in truth, getting it to work didn't take that much time. Defining simple, clean, efficient idioms, patterns, and guidelines did. Time spent here should make subsequent conversions much easier.

Perhaps not new to the community, but here is a short summary in terms that work for me.
  • If textual data (perhaps reproduced from printed materials) appears in source code, is likely to be displayed or printed, or is otherwise associated with human consumption, be sure it is str (Unicode).

  • If data is associated with processing, is to be passed as an argument or as a return value, or is to be communicated to other programs or stored in files for later processing, be sure they are bytes (bytestring).

  • If test values are defined in hex, leave them as str for ease of importing into the code and for general readability, but convert them to bytes with .encode('latin-1') and unhexlify() before processing.

  • If bytes need to be converted to str, use .decode().

  • If bytes need to be converted to hex, hexlify() and convert to str with .decode().

  • If doing crypto and 8-bit byte bytestrings are important, consider the 'latin-1' encoding (AKA iso-8559-1). The high-order Latin characters may not always print the same across all operating systems, but latin-1 will always provide an 8-bit byte representation for all values 0..255. (Both 'ascii' and 'utf-8' are a single byte for 0..127 but the values 128..255 get converted into a two-byte representation. Not good. Ignore 'utf-16' and 'utf-32'.)

  • Be on the lookout for Unicode as the result of some default action somewhere. Databases are one common source. I worked with one DB that, unbeknownst to me, accepted byte strings (seen as ASCII characters), converted and stored them as Unicode, and returned UTF-16 when selected. The before and after hash values were very different. Consider using raw hex dumps/views when things don't make sense as ASCII text and Unicode will often print the same. The DB I was using had parameters I could set, but use extreme care if your DB is already populated with data.
Additional code changes will be necessary, some supported by 2to3 and some are very manual. Thus far the most awkward one was zip(). More on this later. After you get things working under Python3, go back and try to get backward compatibility with Python 2.6 and 2.7 by adding
    from __future__ import print_function
Mod and morph as appropriate. There will be a few exceptions but do try to get the same code to work under Python2 and Python3. (Don't forget to re-test again when finished.)
Later, I ran with the time command. I was somewhat surprised with the results. (Subsequent re-runs provided very similar times.)   
          python 2.5     4.276s     (Python2 code, 32-bit w/o psyco)
          python 2.5     1.800s     (Python2 code, 32-bit w/psyco)
          python 2.6     2.655s     (64-bit)
          python 2.7     2.705s     (64-bit)
          pypy (2.7)     2.192s     (64-bit)
          python 3.2     1.783s     (64-bit)
I expect the results to be skewed even further when I encrypt larger data.

Labels: ,

Thursday, March 10, 2011

Python versions posted

I have posted pure Python versions of Salsa20, ChaCha, BLAKE, and SHA-512 (which also supports SHA-384 and the new NIST algorithms SHA-512/256, and SHA-512/224).

Update (May   9): added Blowfish
Update (May 17): started a folder for Python3 conversions
Update (May 30): added a Python wrapper for C version of BLAKE

Labels: ,

Saturday, December 18, 2010

Erase keys and credit card numbers in Python

Not to start a long discussion on system security and the advisability of doing crypto in potentially compromised environments, suffice it to say it is still good practice to erase keys, credit card numbers, and other sensitive information when no longer needed. Overwriting the sensitive content with garbage will not prevent leakage but it can reduce the likelihood.

Unfortunately Python's assignment statement
key = "qwerty" 
key = "Kilroy was here!" 
does not overwrite previous string values. True, key now points to "Kilroy was here!" but this is a new string. The old one is not overwritten and still resides in memory flagged for garbage collection. When it will be collected and reused is indeterminate, and even then, there is no assurance it will be overwritten. This problem is common to many scripting languages, not just Python.

Python's ctypes' c_buffer() has an interesting property. Operations on the buffer contents occur in a fixed memory location making it attractive for the later clearing of sensitive content. Consider this:
from ctypes import c_buffer, addressof

TEMPLATE = '  %s: key location: 0x%X, value: %s'

# instantiate key
key = c_buffer(16)
print '  key: %s' % key

# set the key value
key.value = 'qwerty'
print TEMPLATE % ('set', addressof(key), key.value)

# use the key in some way

# overwrite the key
key.value = 'Kilroy was here!'
print TEMPLATE % ('clr', addressof(key), key.value)
If you are using ctypes to access an AES routine written in C, simply pass the variable key which, as it turns out, is essentially a pointer to the character array. The above approach also works for other data types such as int and ulong.


Labels: , ,

Monday, October 11, 2010


You may be aware, or perhaps not... I've been salivating for months now. In late November the wait will be over.

Adam is an Android-based touchscreen tablet running the NVIDIA Tegra2 platform. ...with a 10" PixelQi screen (1024×600 and viewable in bright sunlight), 3G, Wifi 802.11n, GPS, 3Mpx camera, microSD card, etc, etc, and 16 hours of battery life (up to 160(?) hours with backlight off).

The Tegra2 is a system-on-a-chip (SoC) with a 1GHz dual-core ARM Cortex-A9 (FPU each core), an NVIDIA ultra low power GPU, an embedded ARM7TDMI and other specialized processing cores for audio and video processing.

One of Adam's strongest features is its software which should take user-oriented multitasking and the touch screen interface to the next level. ...and it is supposedly an open device. If not with Android, know that there is a version of Ubuntu being readied for Adam.

...all for less than $500.

More at:

Update (9 Feb 2011):  A limited quantity of Adams were made available during a "pre-sale," and again now to those unable to get one in the 1st round. I would have ordered but sadly they are only accepting AmEx and Visa. (Reportedly MC is [extremely?] slow in approving; don't know about PayPal.) Nevertheless, the Adam is one to watch.

Update (2 Jun 2011):  The Adam may indeed turn out to be THE tablet, but waiting has been agonizing. (I did sign up [again] after Notion started accepting MCs but never received a notification.) The iPad 2 is compelling and friends recommend it highly, but it is expensive and it is a closed system. So, as a stop-gap measure I purchased a refurbished Nook Color from BN for $225 and am seriously considering rooting it into a full-fledged Android tablet. It will be WiFi and Bluetooth only, but that is what I want. More on this later. ...and for now, Adam can wait.

Update (Jan 2012):  In December I finally received an invitation from Notion Ink allowing me to order an Adam. I debated for a few minutes and decided against.  Not perfect but I'm finding my Nook Color has been filling most of my tablet needs/wants and another $500-$600 would provide only small marginal benefits.  (I'm running Android 2.3.7 thanks to CyanogenMod and CM7.)


Labels: , ,

Sunday, September 27, 2009

A Stick Figure Guide to AES

Jeff Moser wrote/tooned an excellent, gentle introduction to AES.

Labels: ,

Sunday, August 09, 2009

Using Python ctypes to access C lib global data

I want to access global data in a C library from within a Python program. The Python ctypes documentation tells you how to call functions, set up arguments and other things. But if you want to access global data, well, the doc is lacking a good example. This may help. (I'm assuming you are generally familiar with ctypes.)

Suppose you have a C library with several functions and a single global data item named "myGlobal" of type unsigned long. Load the library as "lib" and you can access myGlobal with this snippet.
    myGlobal = c_ulong.in_dll(lib, "myGlobal").value
print(" myGlobal = %d" % myGlobal)
If you have several global variables and have organized them as a C struct named "G"....
    struct {
// reference with a "G." prefix
unsigned long value1;
unsigned long value2;
unsigned long value3;
} G;
...your Python code might include a snippet like this.
    class AllMyGlobals(Structure):
_fields_ = [("value1", c_ulong),
("value2", c_ulong),
("value3", c_ulong)]

G = AllMyGlobals.in_dll(lib, "G")
print(" G.value1 = %d" % G.value1)
print(" G.value2 = %d" % G.value2)
print(" G.value3 = %d" % G.value3)
Changing one of those values in the C library is easy too.
    G.value2 = 1234

Labels: ,

Sunday, September 21, 2008

Jython classes callable from Java

Folks keep asking so perhaps I should post this with wider distribution. It is a page, with examples, that talks about how to install Jython, call Java classes, make Jython classes callable from Java, and more.

Labels: , ,

Monday, May 26, 2008


I've been off the grid for the last couple of months, heads-down on a project. ...a fun and challenging project. Now that I've come up for air, I notice Salsa20/12 (Salsa20 with 12 rounds) has been selected one of the eSTREAM finalists. Congratulations to Daniel Bernstein.

I conclude from my re-read of the final report and other materials that Salasa20/12 is indeed the preferred software algorithm for Profile 1. Depending on where you look, Rabbit and Sosemanuk also received high marks, but it was Salsa20/12 that was consistently on top. ...and because Rabbit has commercial-use IP restrictions, Sosemanuk easily takes second place. (eSTREAM was careful not to get involved in the politics of being so explicit.)

While I've played some with Sosemanuk, I've actually been using Salsa20 for a while now. I'm pleased. One question though, ought not it be named "Salsa12" to help avoid confusion?

More on Salsa20 and other eSTREAM ciphers....
    Fast stream ciphers from eSTREAM
    Python access to CryptMT, Dragon, HC, LEX, NLS, Rabbit

Updated: 6Jun08

Labels: ,

Wednesday, March 05, 2008

Make OpenSSL CA, SSL Server and Client Certs

Even at this late date there is still confusion on how to get OpenSSL to generate a CA and SSL certificates. So, here is a script that I hope will answer some questions. ...there will undoubtedly be more.

I also have an ECC version I will upload in a few days. It was written some time ago and needs to be reviewed.

Update 3/6: Here is an ECC script. It points out a couple of likely bugs with OpenSSL. First, an OpenSSL ECC CA always signs its certs with SHA1 regardless of curve, what you specify in the command, or what you define as the default_md in openssl.cnf. This is not the case if the CA uses an RSA key. Second, if you use openssl ecparam -genkey to create a key pair, you cannot secure the PEM file output. You have to follow with a second command openssl ec to encrypt the private key with AES. ...but you have already written the key to disk. Oops!

Update 3/10: OpenSSL 0.9.9 indeed has a fix for the SHA1-only self-signed certs. The catch is 0.9.9 is still in development (the making of .dylib files fails and make test fails on one of the new TSA tests), but prognosis is good.

Update 3/20: The OpenSSL 0.9.9-dev daily snapshot is meeting my needs very nicely now. openssl (the executable) can now sign certs using ECC and the SHA2 family. I can create the OSX .dylib files if I disable the x86 asm accelerations using the -no-asm switch, and Python with M2Crypto has so far not shown any problems. The linker problem necessitating no x86 asm acceleration is my only outstanding issue. Sweet!

Update: Using config's -shared switch seems to also cure the asm problems.


Friday, January 11, 2008

Chumby - first impressions

I just adopted a Chumby.

Open the bag, plug it in, run the short built-in tutorial, go to the chumby web site to name and authenticate, select some widgets, and away you go.  ...approx 15 minutes.

Now playing...

Poking around in the control panel, you can enable ssh and login.  It is Linux, very familiar.  It has Perl but not Python.  The flash memory is pretty full so if you need more than 1.9MB you need to add a memory stick which you will see at /mnt/usb.  ...and if you put Python on a stick and set PATH, away you go.  Remember this is an ARM processor so your executables need to be compiled accordingly.  You can also access the Chumby via browser at http://<IP addr> but until you load some pages it is kinda lame.  ...but it does work.

It has a touch screen, wi-fi access, 2 USB ports, Flash for GUIs, add Python and I'm ready to write some of my own code.


Sunday, November 04, 2007

Python access to CryptMT, Dragon, HC, LEX, NLS, Rabbit (eSTREAM ciphers)

The last couple of months have been heads down and very long hours with my day job. I'm back now to personal projects and put together another Python wrapper for these ciphers using the eSTREAM APIs. See

Labels: , , ,

Monday, August 13, 2007

SOAP and SSL using ZSI and SOAPpy

I've been attempting to get web services (SOAP) to work using SSL with mutual authentication via X.509 certs. a Python, M2Crypto and OpenSSL environment. The two most obvious choices are ZSI and SOAPpy.  See Python Web Services.

Short version:  I'm disappointed.

More testing is needed but I did get M2Crypto's SSL to do a full handshake complete with arbitration and mutual authentication.  Fine, but I cannot find a way to inject *that* connection into either SOAPpy or ZSI.  They only accept a URL and want to do their own SSL thingy, and if you look at their libs, they do not seem to be equipped to do much more than establish a basic tunnel on their own.

Standing back, may I suggest ZSI leaves a lot to be desired?  It doesn't seem intuitive and is not well documented.  ...perhaps incomplete?  The doc seems extensive, but stops short of a full explanation in a number of places, and sadly, only covers 2/3 of the questions I have.  The code's comments didn't help.  Google only turned up a few articles that are several years old and not much help.  Some of their examples needed fixing and one was missing critical material.  ...and none explained SOAP with SSL, let alone with mutual authentication, which at some level is imperative.

SOAPpy has a *much* cleaner interface but is incomplete. For example, it has client-side SSL but only supports GET, not POST.  ...and while SOAPpy is cleaner, it is slated to be folded into ZSI and discontinued.  A mistake in the making?

One's choices of SOAP with SSL and Python are small. After ZSI and SOAPpy there is OSE.  OSE is written in C++ and has a Python wrapper.  For certain functions, OSE requires ZSI be installed, and some environment variables must be properly set before running.  ...and the install dependencies are growing (fpconst, SOAPpy, ZSI, OSE).  Nevertheless, it seems worth a look.

Another option is cSOAP.  cSOAP is written in C and needs a wrapper.  If OSE fails I will consider writing a wrapper.  Heck, I just might do that anyway.

Frustrated?  Yes.  Am I an experienced SOAP developer?  No, but doing simple things should not be this hard. 

Update:  With some help I was able to get a simple example working, but I'm still not happy with the architecture.  It seems a lot more complex than what should be necessary for simple situations.  ...cSOAP beckons. ;-)

Update2:  I have a ctypes wrapper for cSOAP that implements some simple client functionality.  Time (lack thereof) is my enemy so progress is slow. To be continued.

Update3:  I've taken another look at just what it is I'm trying to accomplish.  XML provides a lot of standards-based flexibility and interoperability, but admittedly, mine is a closed system of perhaps a dozen primary systems.  With the issues surrounding SSL XML, I've decided to use RPyC and feel a little embarrassed for having taken the time consuming side trip.  I've also implemented, in parallel, a pure socket protocol and can switch from one to the other as appropriate.  (This is somewhat an experimental project so I can afford such luxuries.) ...and the socket "protocol" is dirt simple so I don't believe I've compromised much on interoperability.


Labels: , ,

Monday, July 09, 2007

Optional typing: best of both worlds?

There continues to be an ongoing debate over which is better, static typing or dynamic typing.  I argue there are advantages to both, but if I have to choose, I favor dynamic. 

I'll make no bones about it, I'm one of those that loves Python, personal productivity topping my list of reasons.  Python is an excellent language for many/most uses, but I run into places where pure dynamic typing is counter productive.  I say keep the dynamic typing, but give me the ability to optionally specify a variable's type. 

Optional typing has several advantages.  First, it helps make code self documenting, especially with arguments and return values.  Second, type declarations can result in cleaner code than assert statements.  And finally, the interpreter can provide performance advantages when the variables' types have been declared and checked.

Optional typing would be the best of both worlds.

Labels: ,

Wednesday, July 04, 2007

Want more?

In case you haven't noticed, processor speeds in the form of clock rates have not increased much in the last few years, yet higher performance CPUs are being delivered.  Moore's law is still in effect but increases in chip density are no longer being translated to increases in clock speed.  Instead, we are seeing more CPUs on a chip. 

Unfortunately, most of today's software doesn't know of these other CPUs or how to take advantage of them.  To harness this increased power, be they additional cores or distributed platforms, we need to look deeply at how software is written and compiled.  We need new algorithms, languages, compilers, and operating systems. 

Work has been done in this space but even today special, custom programming is needed to take advantage of multiple cores, vector processors, clusters and distributed grids.  The process needs to be more automatic, and we need to move it to the mainstream if we expect enjoy our faster machines and avoid this costly special attention. 

This is not a new problem; what's new is that we can no longer expect faster processing simply by putting in a CPU with a higher clock rate.  If tomorrow morning we want more, we will need to be thinking different.

Labels: ,

Tuesday, July 03, 2007

Making Simple DLLs Simply

I often want to compile some simple C code and have it accessible from Python, but making DLLs and Python extensions for Windows has always been, for me, problematic.  Keeping this note brief and on a positive note....

Python extensions compiled using Microsoft tools need to be compiled using the same version as that used to compile Python itself.  If you don't have that version of Visual Studio or you want to also compile the extension for other versions of Python, having access to all the versions can be problematic.  I was successful in getting the mingw gcc to work for extensions, but when compiling what should be a small DLL (4K-8K for OSX and Linux), mingw created a bloated one, 742K.  For my purposes, that was a problem.  Bottom line: I went shopping for another compiler/linker.

After visiting The Free Country, I settled on the Digital Mars C (DMC) compiler.  It is a small, clean, fast ANSI C compiler, and generates code more like what I expected.  Creating DLLs was never fun, but DMC makes it really easy.  The license is short and easily understandable.  NB: The license expressly says the compiler is not tested, but I am assuming that is more for legal liability reasons.  Nevertheless, I will test my program; I advise you test yours.  That said....

To create a DLL, create a [sourcename].def file so it looks something like this.
    LIBRARY "[sourcename].dll"
    DESCRIPTION '[a short description]'

Then execute this to create the DLL.
    dmc -WD [sourcename].c

Install the .dll file in a directory on your PATH and it should now be available to Python via ctypes.

Some themes and variations are possible so you might want to look at...
    Compiling Code
    Win32 Programming Guidelines
        (scroll to "Building and Using Windows DLLs", about 1/2 way down)
    DMC: Compiler

Oh, I almost forgot.  Digital Mars is Walter Bright, the creator of the D programming language. 

Labels: , ,

Tuesday, June 12, 2007

Papyros: platform independent parallel processing

Papyros, written by George Sakkis, appears to be a fairly simple way to use surplus cycles on other machines. Written in Python, it is platform independent using a master-slave model. Remote communication is handled by Pyro. To parallelize your program, some code changes are necessary, but they appear to be minimal. Each slave platform needs to have a Python app running to receive the processing requests, but other than that, all "wierdness" happens on the master.

I don't see any special security provisions so it's fair to assume all players must be mutually trusted. YMMV.

Labels: ,

Monday, June 11, 2007

Fast stream ciphers from eSTREAM

Several years ago the EU (European Union), through a project called NESSIE, analyzed and recommended a number of cryptographic primitives. You can learn more about NESSIE from the links below. One category in which they were not satisfied and made no recommendation is stream ciphers, so they launched a new project, eSTREAM, expressly for the evaluation of stream ciphers. eSTREAM is now in Phase 3, the final evaluation phase, and there are a number of promising candidates.

Personally, I like Salsa20. I benchmarked Salsa20 as encrypting over 100 MB per second on a lowly 1.5 GHz G4 PPC mini Mac. ...128-bit encryption strength, memory resident data (no I/O), and before Python's GC kicked in. This is more than 3x faster than AES-128 on the same machine. Others I like are Sosemanuk and Phelix, but Phelix didn't advance to Phase 3. Ugh. Well, two out of three isn't bad.

The core routines are written in C so I wrote ctypes wrappers to access Salsa20, Sosemanuk, Phelix and others. I'm making that code available, free for any use.


Labels: , , ,

Sunday, June 10, 2007

A Python ctypes wrapper for LibTomCrypt

Recently I wrote a Python ctypes wrapper for LibTomCrypt. I'm making that code available, free for any use.

pyTomCrypt v0.20 implements most of Tom's crypto library:
    - public key algorithms: RSA, DSA, ECDSA, ECDH
    - hash algorithms:
      md2, md4, md5, rmd128, rmd160, rmd256, rmd320,
      sha1, sha224, sha256, sha384, sha512, tiger, whirlpool
    - symmetric ciphers:
      aes, rijndael, twofish, blowfish, des, des3, cast5,
      kasumi, anubis, kseed, khazad, noekeon, rc2, rc5, rc6,
      xtea, skipjack
    - modes: ecb, cbc, ctr, cfb, ofb
    - MACs: HMAC, OMAC, PMAC, Pelican, XCBC, F9
    - PRNGs: fortuna, rc4, sprng, yarrow, sober128
and is based on:
    - libtomcrypt 1.17
    - libtommath 0.41 (default)
    - tomsfastmath 0.12 (optional)



Labels: , ,

For me, an experiment.

I am not a blogger, or at least I don't think I am one, but I've noticed I have a few things to say.  ...or at least there have been a few emails that could be considered of general interest.  Rather than pushing email, I really should try a pull mechanism and give Blogging a go.  If all this works and I indeed have something to say, I'll continue.

So, what will you likely find here?  Today I tinker with Python, crypto, simulation, agents, machine learning, mini Macs, wirewrapping, and hopefully soon, robotics.  I've been at this since the early '60s (my days with a Burroughs E-103) so I suppose some history might creep in.