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.
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
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”)
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);
};
};
Yep, sounds like the right plan–or change the website example to point to your repo ;-D