2
\$\begingroup\$

I've been spending some time learning Rust and I came across the Cryptopals Challenges. This is my implementation of challenge 1

Convert hex to base64 The string:

49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d

Should produce:

SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t

So go ahead and make that happen. You'll need to use this code for the rest of the exercises.

Cryptopals Rule: Always operate on raw bytes, never on encoded strings. Only use hex and base64 for pretty-printing.

I did go ahead and pull in a crate for handling base64 encoding. It didn't seem important to implement that myself.

mod basics {
    extern crate base64;
    use self::base64::encode;

    pub fn hex_to_base64(hex: &str) -> String {
        encode(&hex_to_bytes(hex))
    }

    pub fn hex_to_bytes(hex: &str) -> Vec<u8> {
        hex.chars()
            .collect::<Vec<_>>() //convert to slice-able
            .chunks(2)           //each char is one nibble
            .map(|byte| byte.iter().collect::<String>())
            .map(|byte| u8::from_str_radix(&byte[..], 16).unwrap())
            .collect()
    }

    #[cfg(test)]
    mod tests {
        use super::*;

        #[test]
        fn can_convert_hex_to_base64() {
            let hex_as_string =
                "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d";
            let base64_as_string =
                "SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t";

            assert_eq!(hex_to_base64(hex_as_string), base64_as_string);
        }

        #[test]
        fn ff_byte_hex_string_to_byte_vector() {
            let hex = "FF";
            assert_eq!(vec![0xFF], hex_to_bytes(hex));
        }

        #[test]
        fn single_byte_hex_string_to_byte_vector() {
            let hex = "2A";
            assert_eq!(vec![0x2A], hex_to_bytes(hex));
        }

        #[test]
        fn multibyte_hex_string_to_byte_vector() {
            let hex = "2ABE";
            assert_eq!(vec![0x2A, 0xBE], hex_to_bytes(hex));
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ Related: Hex string to Base64 \$\endgroup\$ – Shepmaster Sep 15 '18 at 23:53
  • \$\begingroup\$ Yeah. I’ve seen that @Shepmaster. Makes me think I might go back and implement base64 at some point in the future. \$\endgroup\$ – RubberDuck Sep 15 '18 at 23:58
2
\$\begingroup\$
pub fn hex_to_base64(hex: &str) -> String {
    encode(&hex_to_bytes(hex))

I would not use self::base64::encode and instead refer to base64::encode here so that it is clearer what is being encoded.

}

pub fn hex_to_bytes(hex: &str) -> Vec<u8> {
    hex.chars()
        .collect::<Vec<_>>() //convert to slice-able

Given that you are doing hex, which is all in the ASCII range, you may want to use as_bytes() which will give you a slice of bytes without copying the whole string into a new Vec.

        .chunks(2)           //each char is one nibble

What happens if the string length isn't a multiple of two?

        .map(|byte| byte.iter().collect::<String>())

byte isn't correct here as it is a slice of chars. Further, if you don't use chars() as I suggest as above, you can use std::str::from_utf8 to get a &str without allocating a new String.

        .map(|byte| u8::from_str_radix(&byte[..], 16).unwrap())

byte really isn't correct here as you have a String. Additionally, there is no need to take a slice.

        .collect()
}
\$\endgroup\$
  • \$\begingroup\$ It took some doing, but I did end up with something I feel is a little bit better. Thanks! hex.as_bytes().chunks(2).map(|nibbles| str::from_utf8(&nibbles).unwrap()).map(|s| u8::from_str_radix(&s, 16)).collect::<Vec<u8>>() \$\endgroup\$ – RubberDuck Sep 25 '18 at 0:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.