0

I'm trying to use the SHA256 hash function provided by sodiumoxide to write a hash_string function. This function should accept a string and return the hash of the string represented as a string.

Here's what I have so far:

extern crate sodiumoxide;

use std::string::String;
use sodiumoxide::crypto::hash::sha256;

pub fn hash_string(s: String) -> String {
    let digest = sha256::hash(&s.into_bytes());
    String::from_utf8_unchecked(digest).to_owned()
}

Clearly this isn't correct but I don't know how to fix it.

I was able to implement this with the rust-crypto crate.

pub fn hash_string(input: String) -> String {
    let mut sha = Sha256::new();
    sha.input_str(&input);
    sha.result_str()
}

I want to do exactly the above but using the sodiumoxide crate instead.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Wilfred
  • 799
  • 2
  • 16
  • 26
  • 2
    Please include why you think that isn't correct. Have you tested the function? What inputs (possibly with known expected outputs) have you used? – E_net4 May 14 '17 at 20:59
  • It doesn't compile so I can't test the function. I don't know how to convert a [digest](https://dnaq.github.io/sodiumoxide/sodiumoxide/crypto/hash/sha256/struct.Digest.html) into a string. – Wilfred May 14 '17 at 21:04
  • 1
    Assuming that you understand that the hash is simply a vector of bytes, it is fairly clear that interpreting them as a UTF-8 string is wrong. You might want to convert it to a textual representation (hex, Base64, etc.). Again, can you provide an input and expected output? – E_net4 May 14 '17 at 21:06
  • Could you provide the compiler error? – Shepmaster May 14 '17 at 21:07
  • I edited the code so that it uses `String` instead of `str`. I understand that a hash is a vector of bytes and I would like to convert it to a String which is why I'm using the [from_utf8](https://doc.rust-lang.org/std/string/struct.String.html#method.from_utf8) method. Example input: "hello world", output: "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" – Wilfred May 14 '17 at 21:13
  • Compiler error is : expected struct `std::vec::Vec`, found struct `sodiumoxide::crypto::hash::sha256::Digest`. This makes sense to me but I don't know how to get the underlying byte array out of the Digest struct. – Wilfred May 14 '17 at 21:16
  • 1
    And how did you arrive at that sequence of characters from a set of bytes? Did you maybe use some algorithm like "(hex, Base64, etc.)"? Perhaps you would be so kind as to *share that algorithm with us*? – Shepmaster May 14 '17 at 21:19
  • I did that with with the code I just added to the question. – Wilfred May 14 '17 at 21:35
  • That function outputs the hash in hexadecimal format. https://docs.rs/rust-crypto/0.2.36/crypto/digest/trait.Digest.html#method.result_str – E_net4 May 14 '17 at 21:39

1 Answers1

2

From the documentation you linked, Digest is defined as:

pub struct Digest(pub [u8; 32]);

You can get access to those bytes by using the tuple index notation: foo.0. It also implements AsRef<[u8]>.

Then you can just use one of the existing answers to convert a slice to hex, such as those in Show u8 slice in hex representation.

extern crate sodiumoxide;

use sodiumoxide::crypto::hash::sha256;
use std::fmt;

pub fn hash_string(s: &str) -> String {
    let digest = sha256::hash(s.as_bytes());
    format!("{:X}", HexSlice::new(&digest))
}

struct HexSlice<'a>(&'a [u8]);

impl<'a> HexSlice<'a> {
    fn new<T>(data: &'a T) -> HexSlice<'a>
        where T: ?Sized + AsRef<[u8]> + 'a
    {
        HexSlice(data.as_ref())
    }
}

impl<'a> fmt::UpperHex for HexSlice<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for byte in self.0 {
            // Decide if you want upper- or lowercase results,
            // padding the values to two characters, spaces
            // between bytes, etc.
            write!(f, "{:X}", byte)?;
        }
        Ok(())
    }
}

fn main() {
    let h = hash_string("hello world");
    println!("{}", h);
}

Note that there's no benefit to taking an owned Stringas we don't make use of the allocation.

Community
  • 1
  • 1
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    Note that `write!(f, "{:X}", byte)` only writes one digit if the byte's value is less than 16. When printing hashes as hex, you usually want to print each byte with 2 digits. That's what the [`hex` crate](https://crates.io/crates/hex) is doing. – Lukas Kalbertodt May 14 '17 at 21:52
  • @LukasKalbertodt yep! The linked question/answer discusses such nuances (also upper/lower, padding between bytes, etc.) – Shepmaster May 14 '17 at 22:09