Calculate SH256 for a Text

Hi,
I just start with Motoko but struggle with some very basic stuff.
I need to calculate a hash for a text.
I found SHA-256 in Pure Motoko
But didn’t manage to get it working for a text.
Am I right that I need to Text -> [Char] -> [Word32] -> [NAT] -> [Word8]?
Any hints are welcome
Ulrich

Efficient conversion between primitive types requires compiler support, and, last I checked, conversion from Text to [Word8] was not yet implemented. I believe this is a known issue. Perhaps @claudio can give a timeline here. The workaround you mention here is a good temporary solution.

2 Likes

Your solution should work, eg:

import Array "mo:base/Array";
import Prim "mo:prim";
import Sha256 "./SHA256";

actor {
    public func main() : async [Word8] {
        let s = "hello";

        var a: [Word8] = [];
        for (c in s.chars()) {
            let word: Word8 = Prim.natToWord8(Prim.word32ToNat(Prim.charToWord32(c)));
            a := Array.append(a, [word]);
        };

        return Sha256.sha256(a);
    };
};

Returns:

(
  blob "\2c\f2\4d\ba\5f\b0\a3\0e\26\e8\3b\2a\c5\b9\e2\9e\1b\16\1e\5c\1f\a7\42\5e\73\04\33\62\93\8b\98\24",
)

As per:

$ echo -n hello | sha256sum
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
4 Likes

Thanks both!
I finally ended up with the following implementation which returns a string and the hash hex encoded.
The hex.mo example at https://sdk.dfinity.org/docs/developers-guide/sample-apps.html doesn’t work any longer due to syntax changes. I just fixed the encodeWord8 function here…

And sorry - I get 403 a response when using the preformated option for the code, so just plain text now

import Array “mo:base/Array”;
import Word8 “mo:base/Word8”;
import Char “mo:base/Char”;
import Text “mo:base/Text”;
import Iter “mo:base/Iter”;
import Prelude “mo:base/Prelude”;
import Prim “mo:prim”;
import Sha256 “./SHA256”;

actor {

/**
* Encode an unsigned 8-bit integer in hexadecimal format.
* main source from https://sdk.dfinity.org/docs/developers-guide/sample-apps.html 
* but needed updates to current Motoko syntax
*/
func encodeWord8(w8 : Word8) : Text {

    func encodeWord4(w4 : Word8) : Char {
        if (w4 > (0x0F : Word8)) {
        Prelude.unreachable()
        };
        let arr : [Char] =
        [ '0', '1', '2', '3', '4', '5', '6', '7'
        , '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
        ];
        var out : Char = arr[Word8.toNat(w4)];
        return out;
    };

let n : Word8 = 0x10;
let c1 : Char = encodeWord4(w8 / n);
let c2 : Char = encodeWord4(w8 % n);

var out : Text = Char.toText(c1) # Char.toText(c2);

return out

};

public func sha256TextToHexText(s : Text) : async Text {

    var a: [Word8] = [];
    for (c in s.chars()) {
        let word: Word8 = Prim.natToWord8(Prim.word32ToNat(Prim.charToWord32(c)));
        a := Array.append(a, [word]);
    };

    var bh: [Word8] = [];
    bh :=  Sha256.sha256(a);

    var out: Text = "";
    for (w in Iter.fromArray(bh)) {
        out := Text.concat(out, encodeWord8(w));
    };

    return out;
};

};

Returns now for hello:
(“2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824”)

2 Likes

Thanks for pointing this out. The Hex.mo modules comes from the motoko-hex package found here:

I’ve just upgraded this package to the latest 0.6.16 release. @lsgunn perhaps we should update the website as well?

So you could probably reduce that code down to:

import Array "mo:base/Array";
import Hex "../vendor/motoko-hex/src/Hex";
import Prim "mo:prim";
import SHA256 "../vendor/motoko-sha/src/SHA256";

actor {
  public func sha256TextToHexText(text : Text) : async Text {
    let data = Array.init<Word8>(text.size(), 0);
    var i = 0;
    for (char in text.chars()) {
      data[i] := Prim.natToWord8(Prim.word32ToNat(Prim.charToWord32(char)));
      i += 1;
    };
    let hash = SHA256.sha256(Array.freeze(data));
    return Hex.encode(hash);
  };
};
4 Likes

Yep, sounds like the right plan–or change the website example to point to your repo ;-D

2 Likes