I helped a friend in reversing the firmware of some reciva internet radio module. Here are the results, explained in a few words he wrote down for interested readers.
Downloading from the Reciva Servers
Reciva has extended the open-source curl program for doing firmware downloads
(and possibly more) from its servers. They do not offer the source code which
is probably ok because curl is covered by an MIT/X derivate license.
If you log into the Linux system which is running on the Barracuda radios,
you can use curl to do downloads from the Reciva servers. e.g.:
curl reciva://copper.reciva.com:6000/service-pack/sp255-c-106.bin > sp255-c-106.bin
When looking at the communication, it appears to be encrypted. So your radio
talks home and you don’t know what it’s talking which isn’t exactly very
polite for hardware you own. When looking at the exchanged data more exactly,
the first 8 bytes transmitted to the Reciva server is the serial number, so
Reciva knows which radio has downloaded which software. The first answer
sent by the Reciva server are also 8 bytes which change every time that
something has been successfully downloaded. These 8 bytes seem to be random,
but they are not the encryption key. Instead, the encryption key is calculated
from the random bytes. This is where the sernum daemon comes into play.
The sernum daemon
If you look at the process list, you’ll notice a program named “sernum”. It
is responsible for reading the serial number and for handling encryption
keys (see the next chapter on how this is done). Both curl and the main
radio application (named “ir”) are talking to this daemon via a unix socket
(/tmp/sernum).
The protocol is simple: sernum clients issue requests on the socket, and the
sernum server responds. The following requests are known:
(“>” means client to server, “<” means server to client)
Read radio serial number.
> \x03
< \x00\x01\x23\x45
Give encryption keys:
> \x01\x8c\xe3\xbe\xb8\x6e\x2c\x26\x7f
< \x1e\xc9\x50\xc1\x66\xfa\xae\x84 // Download authorization token
> \x00\x1e\xc9\x50\xc1\x66\xfa\xae\x84
< \xab\x81\x64\x5e\x21\xaf\xaa\xf1 // DES keys
> \x00\xab\x81\x64\x5e\x21\xaf\xaa\xf1
< \xb2\x47\x30\x32\x87\x13\x98\x3a // 3DES key, 1st part
> \x00\xb2\x47\x30\x32\x87\x13\x98\x3a
< \x45\x31\x39\xd9\xcd\x9f\x8e\x11 // 3DES key, 2nd part
> \x00\x45\x31\x39\xd9\xcd\x9f\x8e\x11
< \x61\x38\xb8\xc1\xde\x1e\x33\x4a // 3DES key, 3rd part
Read version (probably the sernum protocol version):
> \x04
< \x02
The sernum daemon doesn’t contain the serial number, nor does it perform the
actual encryption. This is done by the Atmel MCU instead.
The Atmel MCU
The sernum daemon talks to the Atmel CPU via a three wire serial protocol
(data, clock, acknowledge) by directly accessing the S3C2410 GPIO registers
(via mmap()). The protocol used is similar to the /tmp/sernum one, but
different command bytes are used. The algorithm used is probably XTEA, but
the encryption key is unknown and probably depends on the serial number.
The reciva protocol
Now, back to the download process. As already mentioned above, the client
sends its 8 byte serial number (ASCII encoded) to the server, which responds
with an 8 byte challenge. From this challenge, the client calculates 3 byte
sequences (download authorization token, DES key, 3DES key) with the help of
the sernum daemon (which in turn uses the Atmel MCU).
The rest of the protocol is based on fixed size blocks with 3 types of blocks:
Type 1: Download request block (256 bytes, DES-CBC encryption)
Type 2: Data block (256 bytes, DES-CBC encryption)
Type 3: Check block (192 bytes, 3DES-ECB encryption)
After the two initial PDUs (serial number, challenge), the client requests
a file by sending a download request block. This block contains:
8 byte authorization token + filename (‘\0’ terminated) + pad + checksum
The whole 256 byte block is then DES encrypted.
The checksum is the last byte of the block and contains the modulo 256 sum
of the first 255 bytes. The pad seems to be random data, but does never
contain an 0x00 byte.
Now, the server sends the file contents in data blocks. After every 20th data
block, a check block is sent.
The first data block contains an 8 byte header which contains the file size
(4 bytes) and an unknown (maybe fixed) value (also 4 bytes). The header
is followed by 246 bytes of payload, a single 0x00 byte and a checksum
(again, a simple modulo 256 sum).
All further (i.e. non-first) data blocks contain 254 bytes of payload,
a single 0x00 byte and a checksum.
All data blocks are DES encrypted, but note that the data block DES stream is
encrypted independently of the download request block.
The check blocks do not contain any actual data. Instead, they repeat the
last 192 bytes of the encrypted (!) preceding data block, with a 3DES-ECB
encryption applied. In other words, the last 192 bytes of the already DES
encrypted preceding data block is encrypted again (with 3DES-ECB this time)
and then sent as some sort of check.
The DES and 3DES ciphers use fixed initialization vectors which are as
follows:
For DES: \xbd\xe7\x32\x66\xb9\x46\xf3\xab
For 3DES: \xed\x9e\xa8\x97\x7c\xee\xc8\xac