Conversation

Jarkko Sakkinen

After trial and error, i.e. brute force going through crc crate algorithms, I can say that CRC32_32_ISO_HDLC is the correct variant for ZMODEM 32-bit transfers, i.e. cipher can be acquired by:

const CRC32: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);

It is better to declare like this so that it gets compiled into .rodata and not initialized at run-time.

1
0
0

Jarkko Sakkinen

Edited 1 year ago

As a bug fix: https://github.com/jarkkojs/zmodem2/commit/ba5c5bbb7d521fc6df6177d1dec2825ea137da14

This abandoned looking zmodem crate is fully working and has als lrzsz based test suite, so kudos to the author for making this initial version, even if it since has been forgotten. Working code is always better than clean code…

So my crate is called zmodem2 and I’m purely focusing right now to make it work best possible way for my serial tool but I might want to go “beyond zmodem” later on. Since there’s not over-crowded supply of zmodem tools, I’ll add subcommands send and receive directly to the command-line so that terminal session is not necessity to do a file transfer.

1
0
1

Jarkko Sakkinen

Edited 1 year ago

For 16-bit it was heck a lot of easier to pick the right one. There was CRC_16_XMODEM, and yes it did work.

0
0
0

Jarkko Sakkinen

Edited 1 year ago

Interestingly enough for CRC16 byte array is construct reverse, i.e. big-endian so the final refurnished functions that pass the tests are:

use crc32::{Crc, CRC_16_XMODEM, CRC_32_ISO_HDLC};

const CRC16: Crc<u16> = Crc::<u16>::new(&CRC_16_XMODEM);
const CRC32: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);

pub fn get_crc16(buf: &[u8], maybe_zcrc: Option<u8>) -> [u8; 2] {
    let mut digest = CRC16.digest();

    digest.update(buf);

    if let Some(zcrc) = maybe_zcrc {
        digest.update(&[zcrc]);
    }

    digest.finalize().to_be_bytes()
    // [(crc >> 8) as u8, (crc & 0xff) as u8]
}

pub fn get_crc32(buf: &[u8], maybe_zcrc: Option<u8>) -> [u8; 4] {
    let mut digest = CRC32.digest();

    digest.update(buf);

    if let Some(zcrc) = maybe_zcrc {
        digest.update(&[zcrc]);
    }

    // Assuming little-endian byte order, given that ZMODEM used to work on
    // VAX, which was a little-endian computer architecture:
    digest.finalize().to_le_bytes()
}

The manual conversion matches this so I guess this is right. No idea how it ended up like this in the history. Hooray, now I an finally open-code these convoluting functions by exporting CRC16 and CRC32 and using them directly in the call sites, mostly by just calling .checksum() (there was only one call site where you really need to use .update() and .finalize().

0
0
0