Free the mouse Replay ReplayEncryption
Home | Changes | Index | Search | Go
The ReplayTV 4000 and 4500 systems use a simple encryption system in a few distinct places.

At its heart, it's a streaming cypher:

static u32 cryptblock(u32 k, char * buf, u32 size)
{
    u32 i;

    for (i = 0; i < size; i++) {
         k = k * 0xb8f7 + 0x15bb9;
         buf[i] ^= k;
    }
    return k;
}
The same cryptblock function is used for both encyphering and decyphering.

Each encrypted item is preceeded by a 32-byte header that includes the key, 4 bytes of obfuscatory material, an md5 checksum of the cyphertext and some constant extra material, an encyphered form of the number 0x42ffdfa9, used as a sanity check, and an encyphered form of the current time, in unix time_t style.

The key is xored with 0xcb0baf47 and stored at offsets 2, 4, 1, and 7. The unknown material is in bytes 0, 3, 5, and 6. The md5sum is bytes 8 through 23, followed by the sanity check, the time value, and the text itself.

The ReplayTV4000? chooses the key and obfuscatory material using the Park-Miller pseudo-random-number generator, implemented with Schrage's factorization method:

static u32 r(void)
{
    static u32 s;

    s = 16807 * (s % 127773) - 2836 * (s / 127773);
    return s;
}
The division is actually done with a MagicDivision I still don't understand, and the actual values used are taken mod (unsigned)-1, which has no effect that I can see:
static u32 r(void)
{
    static u32 s;
    u32 A, B, C;

    A = ((u64)(s) * 0x69c16bd) >> 32;
    B = (A + ((s - A) >> 1)) >> 16;  /* B = s/127773 */
    C = s - B * 127773;

    s = 16807 * C - 2836 * B;

    return s % 0xffffffff;
}

The details of how the keys are chosen are interesting, but unimportant for interoperability; simply using rand() works just fine.

There are two variants of this encryption system; they differ only in the extradata used in the checksum step. The original version, which I'll refer to as 'checksum #0', is used in the RDDNS protocol; in GaeaSoftware version 4.3, the second version ('checksum #1') is used in HTTP query strings.

static void checksum(unsigned char * dest, unsigned const char * src, u32 len, int checksum_num)
{
    MD5_CTX c;

    static unsigned char extradata[][64] = {{
        0x41,0x47,0xc8,0x09, 0xba,0x3c,0x99,0x6a,
        0xda,0x09,0x9a,0x0f, 0xc0,0xd3,0x47,0xca,
        0xd1,0x95,0x81,0x19, 0xab,0x17,0xc6,0x5f,
        0xad,0xea,0xe5,0x75, 0x9c,0x49,0x18,0xa5,
        0xdf,0x35,0x46,0x5b, 0x78,0x0e,0xcb,0xc7,
        0x8c,0x3e,0xf4,0x90, 0xa2,0xb7,0x8e,0x00,
        0x53,0x8d,0x4c,0xab, 0x13,0xa5,0x16,0x00,
        0xff,0xb8,0x4b,0x20, 0x29,0x22,0x9d,0xee,
    }, {
        0xda,0x76,0x5c,0xd4, 0x34,0xc3,0xd7,0x2c,
        0xac,0x40,0xb8,0xd8, 0x59,0xbc,0x59,0x34, 
        0xaa,0xbf,0x89,0xbd, 0x85,0xe8,0x40,0x27,
        0x78,0x2b,0x18,0x6e, 0xa6,0x6e,0x5a,0xc6, 
        0xda,0xe3,0x86,0x84, 0x40,0x14,0x2a,0x23,
        0x4f,0x5d,0x38,0x5e, 0x7f,0xd9,0x73,0x7d, 
        0xe4,0x80,0x3d,0x21, 0x28,0x41,0xf1,0xb2,
        0x96,0x43,0x2b,0xcc, 0x0c,0x9d,0x26,0xb9,
    }};

    MD5_Init(&c);
    MD5_Update(&c, src, len);
    MD5_Update(&c, extradata[checksum_num], sizeof extradata[checksum_num]);
    MD5_Final(dest, &c);
}

int decrypt(const char * cyphertext, u32 cyphertext_len,
            char * plainbuf, u32 plainbuf_len,
            u32 * p_time, u32 * p_plain_len, int checksum_num)
{
    unsigned char key_buf[4];
    unsigned char sanity_buf[4];
    unsigned char time_buf[4];
    unsigned char csum_buf[16];
    unsigned char * p;
    u32 key;
    u32 sanity;

    if (plainbuf_len < cyphertext_len - 32)
        return -1;

    /* unshuffle the key and unxor it */
    key_buf[0] = cyphertext[2];
    key_buf[1] = cyphertext[4];
    key_buf[2] = cyphertext[1];
    key_buf[3] = cyphertext[7];
    p = key_buf;
    key = rtv_to_u32(&p) ^ 0xcb0baf47;

    /* check the sanity field */
    memcpy(sanity_buf, cyphertext + 24, 4);
    key = cryptblock(key, sanity_buf, 4);
    p = sanity_buf;
    sanity = rtv_to_u32(&p);
    if (sanity != 0x42ffdfa9)
        return -1;

    /* decrypt the time field */
    memcpy(time_buf, cyphertext + 28, 4);
    key = cryptblock(key, time_buf, 4);

    /* decrypt the actual text */
    memcpy(plainbuf, cyphertext + 32, cyphertext_len - 32);
    cryptblock(key, plainbuf, cyphertext_len - 32);

    /* check the checksum */
    checksum(csum_buf, cyphertext + 24, cyphertext_len - 24, checksum_num);
    if (memcmp(csum_buf, cyphertext + 8, 16) != 0)
        return -2;

    if (p_plain_len) {
        *p_plain_len = cyphertext_len - 32;
    }
    if (p_time) {
        p = time_buf;
        *p_time = rtv_to_u32(&p);
    }
    return 0;
}

int encrypt(const char * plaintext, u32 plaintext_len,
            char * cyphertext, u32 buffer_len, u32 * cyphertext_len,
            int checksum_num)
{
    u32 key;
    u32 t;
    u32 obfusc;
    unsigned char key_buf[4];
    unsigned char obfusc_buf[4];
    unsigned char * p;

    if (buffer_len < plaintext_len + 32)
        return -1;

    /* make up a key and obfuscatory material; get the time */
    key    = rand();
    obfusc = rand();
    t      = time(NULL);

    /* encrypt the key */
    p = key_buf;
    rtv_from_u32(&p, key ^ 0xcb0baf47);

    p = obfusc_buf;
    rtv_from_u32(&p, obfusc);

    /* scramble the key and obfusc material */
    cyphertext[0]  = obfusc_buf[3];
    cyphertext[1]  = key_buf[2];
    cyphertext[2]  = key_buf[0];
    cyphertext[3]  = obfusc_buf[2];
    cyphertext[4]  = key_buf[1];
    cyphertext[5]  = obfusc_buf[1];
    cyphertext[6]  = obfusc_buf[0];
    cyphertext[7]  = key_buf[3];

    /* store the sanity check & time */
    p = cyphertext + 24;
    rtv_from_u32(&p, 0x42ffdfa9);
    rtv_from_u32(&p, t);

    /* copy the plaintext */
    memcpy(p, plaintext, plaintext_len);

    /* encrypt the whole thing */
    cryptblock(key, cyphertext+24, plaintext_len+8);

    /* fill in the checksum */
    checksum(cyphertext + 8, cyphertext + 24, plaintext_len + 8, checksum_num);

    /* and we're done */
    *cyphertext_len = plaintext_len + 32;
    return 0;
}
-- ToddLarason - 10 Jun 2002


Your post will appear before this form in chronological order (newest at bottom)

Topic ReplayEncryption . { Edit | Attach | Ref-By | Printable | Diffs | r1.3 | > | r1.2 | > | r1.1 | More }
Revision r1.3 - 01 Apr 2003 - 04:27 GMT - TWikiGuest

Copyright © 2001 by the contributing authors. All material on this collaboration tool is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback.