Skip to content

Commit

Permalink
Add sha256 and sha224.
Browse files Browse the repository at this point in the history
  • Loading branch information
q-uint committed Nov 9, 2021
1 parent d20bb29 commit 6098f56
Show file tree
Hide file tree
Showing 12 changed files with 346 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.vessel/
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,19 @@
# Crypto Package for Motoko

## Packages

| Package Name | Description | Path |
|--------------|-------------|------|
| SHA | SHA224 and SHA256 hash algorithms as defined in FIPS 180-4 | crypto/SHA |

## Usage

```motoko
SHA256.sum(Blob.toArray(Text.encodeUtf8("hello world\n"))
// "A948904F2F0F479B8F8197694B30184B0D2ED1C1CD2A1EC0FB85D299A192A447"
let h = SHA256.New();
h.write(Blob.toArray(Text.encodeUtf8("hello world\n")));
h.sum([]);
// "A948904F2F0F479B8F8197694B30184B0D2ED1C1CD2A1EC0FB85D299A192A447"
```
2 changes: 2 additions & 0 deletions package-set.dhall
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
let upstream = https://github.com/aviate-labs/package-set/releases/download/v0.1.3/package-set.dhall sha256:ca68dad1e4a68319d44c587f505176963615d533b8ac98bdb534f37d1d6a5b47
in upstream
17 changes: 17 additions & 0 deletions src/Hash.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Hash {
public type Hash = {
// Returns the block size of the hash.
blockSize() : Nat;
// Returns the checksum data.
checkSum() : [Nat8];
// Resets the hash to its initial state.
reset() : ();
// Returns the number of bytes that sum will return.
size() : Nat;
// Adds the current hash to the resulting slice.
// The underlying hash is not modified.
sum(bs : [Nat8]) : [Nat8];
// Adds data to the running hash.
write(bs : [Nat8]) : ();
};
};
138 changes: 138 additions & 0 deletions src/SHA/SHA2.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import Array "mo:base/Array";
import Binary "mo:encoding/Binary";
import Iter "mo:base/Iter";
import Nat32 "mo:base/Nat32";
import Nat64 "mo:base/Nat64";

import Hash "../Hash";
import Util "../Utilities";

/// For internal use only.
module {
// First thirty-two bits of the fractional parts of the cube roots of the
// first sixty-four prime numbers.
private let K : [Nat32] = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
];

// Initial hash value, H(0).
private let H256 : [Nat32] = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,
0x1f83d9ab, 0x5be0cd19,
];

public class SHA2(
initialState : [Nat32],
hashSize : Nat,
) : Hash.Hash = {
private var h : [var Nat32] = Array.thaw<Nat32>(initialState);
private var x : [var Nat8] = Array.init<Nat8>(64, 0);
private var nx : Nat = 0;
private var len : Nat64 = 0;

public func blockSize() : Nat { 64; };

public func reset() : () {
h := Array.thaw<Nat32>(initialState);
x := Array.init<Nat8>(64, 0);
nx := 0;
len := 0;
};

public func size() : Nat { hashSize; };

public func sum(bs : [Nat8]) : [Nat8] {
Array.append(bs, checkSum());
};

public func checkSum() : [Nat8] {
let n = len;
var tmp = Array.init<Nat8>(64, 0);
tmp[0] := 0x80;
if (Nat64.toNat(len) % 64 < 56) {
write(Util.takeN<Nat8>(
56 - Nat64.toNat(len) % 64,
Array.freeze(tmp),
));
} else {
write(Util.takeN<Nat8>(
64 + 56 - Nat64.toNat(len) % 64,
Array.freeze(tmp),
));
};
write(Binary.BigEndian.fromNat64(n << 3));
var digest : [Nat8] = [];
label l for (i in h.keys()) {
if (i == 7 and hashSize == 28) { break l; };
digest := Array.append(digest, Binary.BigEndian.fromNat32(h[i]));
};
digest;
};

public func write(bs : [Nat8]) : () {
var p = bs;
len +%= Nat64.fromNat(bs.size());
if (0 < nx) {
let n = Util.copy<Nat8>(nx, x, p);
nx += n;
if (nx == 64) {
block(Array.freeze(x));
nx := 0;
};
p := Util.removeN(n, p);
};
if (64 <= p.size()) {
let n = Nat64.toNat(Nat64.fromNat(p.size()) & (^63));
block(Util.takeN(n, p));
p := Util.removeN(n, p);
};
if (0 < p.size()) {
nx := Util.copy<Nat8>(0, x, p);
};
};

private func block(bs : [Nat8]) {
var p = bs;
var w : [var Nat32] = Array.init<Nat32>(64, 0);
var h0 = h[0]; var h1 = h[1]; var h2 = h[2]; var h3 = h[3];
var h4 = h[4]; var h5 = h[5]; var h6 = h[6]; var h7 = h[7];
while (64 <= p.size()) {
for (i in Iter.range(0, 15)) {
let j = i * 4;
w[i] := Util.nat8to32(p[j]) << 24 | Util.nat8to32(p[j+1]) << 16
| Util.nat8to32(p[j+2]) << 8 | Util.nat8to32(p[j+3]);
};
for (i in Iter.range(16, 63)) {
let v1 = w[i-2];
let t1 = (Nat32.bitrotRight(v1, 17) ^ Nat32.bitrotRight(v1, 19)) ^ (v1 >> 10);
let v2 = w[i-15];
let t2 = (Nat32.bitrotRight(v2, 7) ^ Nat32.bitrotRight(v2, 18)) ^ (v2 >> 3);
w[i] := t1 +% w[i-7] +% t2 +% w[i - 16];
};
var a = h0; var b = h1; var c = h2; var d = h3;
var e = h4; var f = h5; var g = h6; var h = h7;
for (i in Iter.range(0, 63)) {
let t1 = h +% (Nat32.bitrotRight(e, 6) ^ Nat32.bitrotRight(e, 11) ^ Nat32.bitrotRight(e, 25)) +% ((e & f) ^ (^e & g)) +% K[i] +% w[i];
let t2 = (Nat32.bitrotRight(a, 2) ^ Nat32.bitrotRight(a, 13) ^ Nat32.bitrotRight(a, 22)) +% ((a & b) ^ (a & c) ^ (b & c));
h := g; g := f; f := e; e := d +% t1;
d := c; c := b; b := a; a := t1 +% t2;
};
h0 +%= a; h1 +%= b; h2 +%= c; h3 +%= d;
h4 +%= e; h5 +%= f; h6 +%= g; h7 +%= h;
p := Util.removeN(64, p);
};
h[0] := h0; h[1] := h1; h[2] := h2; h[3] := h3;
h[4] := h4; h[5] := h5; h[6] := h6; h[7] := h7;
};
};
};
20 changes: 20 additions & 0 deletions src/SHA/SHA224.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import SHA2 "SHA2";

import Hash "../Hash";

module SHA224 {
// Initial hash value, H(0).
private let H224 : [Nat32] = [
0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511,
0x64f98fa7, 0xbefa4fa4,
];

/// Returns the SHA224 checkum of the data.
public func sum(bs : [Nat8]) : [Nat8] {
let h = New();
h.write(bs);
h.checkSum()
};

public func New() : Hash.Hash { SHA2.SHA2(H224, 28); };
};
20 changes: 20 additions & 0 deletions src/SHA/SHA256.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import SHA2 "SHA2";

import Hash "../Hash";

module SHA224 {
// Initial hash value, H(0).
private let H256 : [Nat32] = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,
0x1f83d9ab, 0x5be0cd19,
];

/// Returns the SHA256 checksum of the data.
public func sum(bs : [Nat8]) : [Nat8] {
let h = New();
h.write(bs);
h.checkSum();
};

public func New() : Hash.Hash { SHA2.SHA2(H256, 32); };
};
46 changes: 46 additions & 0 deletions src/Utilities.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Array "mo:base/Array";
import Nat8 "mo:base/Nat8";
import Nat32 "mo:base/Nat32";

module Utilities {
public func copy<T>(
n : Nat, // Position to start writing.
dst : [var T],
src : [T],
) : Nat {
let l = dst.size();
for (i in src.keys()) {
if (l <= i) return l;
dst[n + i] := src[i];
};
src.size();
};

public func removeN<T>(
n : Nat, // Number to remove.
xs : [T],
) : [T] {
Array.tabulate<T>(
xs.size() - n,
func (i : Nat) : T {
xs[i + n];
},
);
};

public func takeN<T>(
n : Nat, // Number to take.
xs : [T],
) : [T] {
Array.tabulate<T>(
n,
func (i : Nat) : T {
xs[i];
},
);
};

public func nat8to32(n : Nat8) : Nat32 {
Nat32.fromNat(Nat8.toNat(n));
};
};
24 changes: 24 additions & 0 deletions test/SHA224.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Blob "mo:base/Blob";
import Hex "mo:encoding/Hex";
import Text "mo:base/Text";

import SHA224 "../src/SHA/SHA224";

let sum224 = SHA224.sum(Blob.toArray(Text.encodeUtf8("hello world\n")));
assert(Hex.encode(sum224) == "95041DD60AB08C0BF5636D50BE85FE9790300F39EB84602858A9B430");

for (v in [
("", "D14A028C2A3A2BC9476102BB288234C415A2B01F828EA62AC5B3E42F"),
("a", "ABD37534C7D9A2EFB9465DE931CD7055FFDB8879563AE98078D6D6D5"),
("ab", "DB3CDA86D4429A1D39C148989566B38F7BDA0156296BD364BA2F878B"),
("abc", "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7"),
("abcd", "A76654D8E3550E9A2D67A0EEB6C67B220E5885EDDD3FDE135806E601"),
("abcde", "BDD03D560993E675516BA5A50638B6531AC2AC3D5847C61916CFCED6"),
("abcdef", "7043631CB415556A275A4EBECB802C74EE9F6153908E1792A90B6A98"),
("abcdefg", "D1884E711701AD81ABE0C77A3B0EA12E19BA9AF64077286C72FC602D"),
("abcdefgh", "17EB7D40F0356F8598E89EAFAD5F6C759B1F822975D9C9B737C8A517"),
("abcdefghi", "AEB35915346C584DB820D2DE7AF3929FFAFEF9222A9BCB26516C7334"),
("abcdefghij", "D35E1E5AF29DDB0D7E154357DF4AD9842AFEE527C689EE547F753188"),
].vals()) {
assert(Hex.encode(SHA224.sum(Blob.toArray(Text.encodeUtf8(v.0)))) == v.1);
};
38 changes: 38 additions & 0 deletions test/SHA256.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Blob "mo:base/Blob";
import Hex "mo:encoding/Hex";
import Text "mo:base/Text";

import Debug "mo:base/Debug";

import SHA256 "../src/SHA/SHA256";

let sum256 = SHA256.sum(Blob.toArray(Text.encodeUtf8("hello world\n")));
assert(Hex.encode(sum256) == "A948904F2F0F479B8F8197694B30184B0D2ED1C1CD2A1EC0FB85D299A192A447");

let h = SHA256.New();
h.write(Blob.toArray(Text.encodeUtf8("hello world\n")));
assert(Hex.encode(sum256) == Hex.encode(h.sum([])));

for (v in [
("", "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855"),
("a", "CA978112CA1BBDCAFAC231B39A23DC4DA786EFF8147C4E72B9807785AFEE48BB"),
("ab", "FB8E20FC2E4C3F248C60C39BD652F3C1347298BB977B8B4D5903B85055620603"),
("abc", "BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD"),
("abcd", "88D4266FD4E6338D13B845FCF289579D209C897823B9217DA3E161936F031589"),
("abcde", "36BBE50ED96841D10443BCB670D6554F0A34B761BE67EC9C4A8AD2C0C44CA42C"),
("abcdef", "BEF57EC7F53A6D40BEB640A780A639C83BC29AC8A9816F1FC6C5C6DCD93C4721"),
("abcdefg", "7D1A54127B222502F5B79B5FB0803061152A44F92B37E23C6527BAF665D4DA9A"),
("abcdefgh", "9C56CC51B374C3BA189210D5B6D4BF57790D351C96C47C02190ECF1E430635AB"),
("abcdefghi", "19CC02F26DF43CC571BC9ED7B0C4D29224A3EC229529221725EF76D021C8326F"),
("abcdefghij", "72399361DA6A7754FEC986DCA5B7CBAF1C810A28DED4ABAF56B2106D06CB78B0"),
].vals()) {
assert(Hex.encode(SHA256.sum256(Blob.toArray(Text.encodeUtf8(v.0)))) == v.1);
};

do {
let h = SHA256.New();
h.write(Blob.toArray(Text.encodeUtf8("hello")));
h.write(Blob.toArray(Text.encodeUtf8(" ")));
h.write(Blob.toArray(Text.encodeUtf8("world")));
assert(Hex.encode(h.sum([])) == "B94D27B9934D3E08A52E52D7DA7DABFAC484EFE37A5380EE9088F7ACE2EFCDE9");
};
18 changes: 18 additions & 0 deletions test/Utilities.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Array "mo:base/Array";
import Iter "mo:base/Iter";

import Util "../src/Utilities";

let fifty = Iter.toArray(Iter.range(0, 49));
let hundred = Iter.toArray(Iter.range(0, 99));

// Remove the first 50.
assert(Util.removeN(50, hundred) == Iter.toArray(Iter.range(50, 99)));

// Take the first 50.
assert(Util.takeN(50, hundred) == fifty);

// Copy the first 50 starting at position 50.
let copyTo = Array.thaw<Nat>(hundred);
assert(Util.copy(50, copyTo, Util.takeN(50, hundred)) == 50);
assert(Array.freeze(copyTo) == Array.append(fifty, fifty));
4 changes: 4 additions & 0 deletions vessel.dhall
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
dependencies = [ "base", "encoding" ],
compiler = Some "0.6.10"
}

0 comments on commit 6098f56

Please sign in to comment.