-
-
Notifications
You must be signed in to change notification settings - Fork 292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Builtin SHA256 hashing #6977
base: main
Are you sure you want to change the base?
Builtin SHA256 hashing #6977
Changes from 25 commits
e19e816
32e1e3d
5c6348d
6e0f94d
385e0ec
88acae8
f7ed577
8ecf644
ee8640e
6845d47
053d3b5
7a1dc3c
1e20be7
4e10dbc
907a8ff
1cc2751
4248bac
01cda95
e1e2a5e
e29ad5b
87c44e7
de025dd
d61a7ee
2bfad6f
aada47b
6717da6
154ec64
208fc98
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
const std = @import("std"); | ||
const crypto = std.crypto; | ||
const sha2 = crypto.hash.sha2; | ||
const list = @import("list.zig"); | ||
const utils = @import("utils.zig"); | ||
|
||
const Sha256 = extern struct { | ||
location: [*]u8, | ||
fn pointer(self: Sha256) *sha2.Sha256 { | ||
return @alignCast(@ptrCast(self.location)); | ||
} | ||
}; | ||
|
||
pub fn emptySha256() callconv(.C) Sha256 { | ||
const allocation = utils.allocateWithRefcount(@sizeOf(sha2.Sha256), @alignOf(sha2.Sha256), false); | ||
const ptr: *sha2.Sha256 = @alignCast(@ptrCast(allocation)); | ||
ptr.* = sha2.Sha256.init(.{}); | ||
return Sha256{ | ||
.location = @alignCast(@ptrCast(ptr)), | ||
}; | ||
} | ||
|
||
pub fn sha256AddBytes(sha: Sha256, data: list.RocList) callconv(.C) Sha256 { | ||
var out = emptySha256(); | ||
out.pointer().* = sha.pointer().*; | ||
if (data.bytes) |bytes| { | ||
const byteSlice: []u8 = bytes[0..data.length]; | ||
out.pointer().*.update(byteSlice); | ||
} | ||
return out; | ||
} | ||
|
||
pub const Digest256 = extern struct { | ||
firstHalf: u128, | ||
secondHalf: u128, | ||
}; | ||
|
||
pub fn sha256Digest(sha: Sha256) callconv(.C) Digest256 { | ||
return @bitCast(sha.pointer().*.peek()); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
module [ | ||
emptySha256, | ||
sha256AddBytes, | ||
sha256Digest, | ||
hashSha256, | ||
digest256ToBytes, | ||
Sha256, | ||
Digest256, | ||
] | ||
|
||
import Bool exposing [Eq] | ||
import List | ||
import Num exposing [U8, U64, U128] | ||
import Result | ||
import Str | ||
|
||
## Represents, as an opaque type, the state of a SHA256 cryptographic hashing function, after some (or no) data have been added to the hash. | ||
Sha256 := { location : U64 } | ||
|
||
## Represents the digest of some data produced by the SHA256 cryptographic hashing function as an opaque type. | ||
## `Digest256`implements the `Eq` ability. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nitpick: also, consider adding an empty doc comment between these two doc comments, that'll follow the "one-line summary on top, context beneath" structure for docs. |
||
Digest256 := { firstHalf : U128, secondHalf : U128 } implements [Eq] | ||
|
||
## Returns a `Sha256` to which no data have been added. | ||
emptySha256 : {} -> Sha256 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd recommend "An empty SHA-256 digest." And it's "to which no data has been added." https://www.thesaurus.com/e/grammar/data-is-or-data-are/ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The word shouldn't be "digest", I think. We should save that for the I agree that data (EDIT)is in this context; I've had too much time in settings where I get corrected the other way. |
||
|
||
## Adds bytes of data to be hashed in the `Sha256`. | ||
sha256AddBytes : Sha256, List U8 -> Sha256 | ||
|
||
## Returns the digest of the cryptographic hashing function represted by a`Sha256`. | ||
sha256Digest : Sha256 -> Digest256 | ||
|
||
## Applies the SHA256 crytographic hashing function to some bytes. | ||
hashSha256 : List U8 -> Digest256 | ||
hashSha256 = \bytes -> emptySha256 {} |> sha256AddBytes bytes |> sha256Digest | ||
|
||
u128Bytes : U128 -> List U8 | ||
u128Bytes = \number -> | ||
loop = \n, bytes, place -> | ||
if place == 16 then | ||
bytes | ||
else | ||
newByte = n |> Num.bitwiseAnd 255 |> Num.toU8 | ||
loop (Num.shiftRightBy n 8) (List.prepend bytes newByte) (place + 1) | ||
loop number [] 0 | ||
|
||
## Returns the bytes of a `Digest256`as a list. | ||
digest256ToBytes : Digest256 -> List U8 | ||
digest256ToBytes = \@Digest256 { firstHalf, secondHalf } -> | ||
List.concat (u128Bytes firstHalf) (u128Bytes secondHalf) | ||
|
||
# test data taken from https://ziglang.org/documentation/0.11.0/std/src/std/crypto/sha2.zig.html#L434 | ||
digestBytesOfEmpty : List U8 | ||
digestBytesOfEmpty = fromHexString "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" | ||
|
||
digestBytesOfAbc : List U8 | ||
digestBytesOfAbc = fromHexString "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" | ||
|
||
digestBytesOfLong : List U8 | ||
digestBytesOfLong = fromHexString "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1" | ||
|
||
expect | ||
data : List U8 | ||
data = [] | ||
want = digestBytesOfEmpty | ||
got = data |> hashSha256 |> digest256ToBytes | ||
want == got | ||
|
||
expect | ||
data = ['a', 'b', 'c'] | ||
want = digestBytesOfAbc | ||
got = data |> hashSha256 |> digest256ToBytes | ||
want == got | ||
|
||
expect | ||
data = Str.toUtf8 "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" | ||
want = digestBytesOfLong | ||
got = data |> hashSha256 |> digest256ToBytes | ||
want == got | ||
|
||
expect | ||
want = digestBytesOfEmpty | ||
got = emptySha256 {} |> sha256Digest |> digest256ToBytes | ||
want == got | ||
|
||
expect | ||
data = Str.toUtf8 "abc" | ||
want = digestBytesOfAbc | ||
got = | ||
emptySha256 {} | ||
|> sha256AddBytes data | ||
|> sha256Digest | ||
|> digest256ToBytes | ||
want == got | ||
|
||
expect | ||
want = digestBytesOfAbc | ||
got = | ||
emptySha256 {} | ||
|> sha256AddBytes ['a'] | ||
|> sha256AddBytes ['b'] | ||
|> sha256AddBytes ['c'] | ||
|> sha256Digest | ||
|> digest256ToBytes | ||
want == got | ||
|
||
fromHexString : Str -> List U8 | ||
fromHexString = \hex -> | ||
fromHexDigit = \smallNumber -> | ||
if smallNumber <= '9' then | ||
smallNumber - '0' | ||
else | ||
smallNumber - 'a' | ||
|
||
fromHexDigits = \pair -> | ||
first = pair |> List.first |> Result.withDefault 0 | ||
second = pair |> List.get 1 |> Result.withDefault 0 | ||
16 * (fromHexDigit first) + (fromHexDigit second) | ||
|
||
hex | ||
|> Str.toUtf8 | ||
|> List.chunksOf 2 | ||
|> List.map fromHexDigits |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,4 +12,5 @@ package [ | |
Box, | ||
Inspect, | ||
Task, | ||
Crypt, | ||
] {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -94,6 +94,7 @@ impl Default for ModuleCache<'_> { | |
HASH, | ||
INSPECT, | ||
TASK, | ||
CRYPT, | ||
} | ||
|
||
Self { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: it being an opaque type is implied, I don't think we need to explicitly mention that here.