Skip to content
This repository was archived by the owner on Mar 18, 2022. It is now read-only.

Rework interface to mimic bcrypto #33

Merged
merged 9 commits into from
Feb 19, 2020
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
<a name="0.1.4"></a>
## 0.2.0 (unreleased)

### BREAKING CHANGES

* new TS and AS exported interface

<a name="0.1.4"></a>
## 0.1.4 (2020-01-30)

Expand Down
50 changes: 11 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,23 @@
# as-sha256
Assembly Script implementation of SHA256.

This repository contains two npm distributions; a pure AssemblyScript version, and a Typescript wrapper for the WASM build.

## Benchmarks

```
Sha256-Rust#hash x 328,611 ops/sec ±5.99% (75 runs sampled)
Sha256-Js#hash x 340,641 ops/sec ±4.64% (77 runs sampled)
Sha256-Asm#hash x 5,217 ops/sec ±8.83% (74 runs sampled)
Sha256-BCrypto#hash x 411,191 ops/sec ±9.87% (59 runs sampled)
AS-Sha256#hash x 537,124 ops/sec ±8.91% (76 runs sampled)
Fastest is AS-Sha256#hash
```


## AssemblyScript

`yarn add @chainsafe/as-sha256`

## TypeScript Wrapper

`yarn add @chainsafe/sha256`
AssemblyScript implementation of SHA256.

## Usage

```JS
import sha256 from "@chainsafe/sha256";

const hash = sha256(Buffer.from("Hello world"));
```
`yarn add @chainsafe/as-sha256`

We also expose the lower level WASM exports for those that may wish to use it. It can be accessed as follows:
```JS
import { wasm } from "@chainsafe/sha256"
```typescript
import SHA256 from "@chainsafe/as-sha256";

const buffer = Buffer.from("Hello world");
const input = wasm.__retain(wasm.__allocArray(wasm.UINT8ARRAY_ID, buffer));
const output = wasm.hash(input);
const result = wasm.__getUint8Array(output);
let hash: Uint8Array;

// use result before releases. Otherwise use `.slice()` for `result` for prevent modification attached buffer
console.log(toHexString(result));
// create a new sha256 context
const sha256 = new SHA256();
// with init(), update(data), and final()
hash = sha256.init().update(Buffer.from("Hello world")).final();

// To prevent memory leaks
wasm.__release(input);
wasm.__release(output);
// or use a one-pass interface
hash = SHA256.digest(Buffer.from("Hello world"));
```

### License
Expand Down
26 changes: 25 additions & 1 deletion assembly/__tests__/example.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,28 @@
import { hash, toHexString } from '..';
import { init, update, final } from '..';

export function toHexString(bin: Uint8Array): string {
let bin_len = bin.length;
let hex = "";
for (let i = 0; i < bin_len; i++) {
let bin_i = bin[i] as u32;
let c = bin_i & 0xf;
let b = bin_i >> 4;
let x: u32 = ((87 + c + (((c - 10) >> 8) & ~38)) << 8) |
(87 + b + (((b - 10) >> 8) & ~38));
hex += String.fromCharCode(x as u8);
x >>= 8;
hex += String.fromCharCode(x as u8);
}
return hex;
}

function hash(data: Uint8Array): Uint8Array {
const output = new Uint8Array(32);
init();
update(changetype<usize>(data.buffer), data.length);
final(changetype<usize>(output.buffer));
return output;
}

describe("example", () => {
it("Hash: empty array", () => {
Expand Down
66 changes: 24 additions & 42 deletions assembly/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@

//https://github.com/dchest/fast-sha256-js/blob/master/src/sha256.ts
export const UINT8ARRAY_ID = idof<Uint8Array>();
const DIGEST_LENGTH = 32;
const INPUT_LENGTH = 512;
export const INPUT_LENGTH = 512;

// constants used in the SHA256 compression function
const K: u32[] = [
Expand All @@ -26,6 +25,7 @@ const kPtr = K.dataStart;
var H0: u32, H1: u32, H2: u32, H3: u32, H4: u32, H5: u32, H6: u32, H7: u32;

// hash registers

var a: u32, b: u32, c: u32, d: u32, e: u32, f: u32, g: u32, h: u32, i: u32, t1: u32, t2: u32;

// 16 32bit message blocks
Expand All @@ -36,8 +36,12 @@ const mPtr = changetype<usize>(M);
const W = new ArrayBuffer(256);
const wPtr = changetype<usize>(W);

// input buffer
export const input = new ArrayBuffer(INPUT_LENGTH);
const inputPtr = changetype<usize>(input);

// output buffer
const output = new ArrayBuffer(DIGEST_LENGTH);
export const output = new ArrayBuffer(DIGEST_LENGTH);
const outputPtr = changetype<usize>(output);

// number of bytes in M buffer
Expand Down Expand Up @@ -77,6 +81,14 @@ function load8(ptr: usize, offset: usize): u8 {
return load<u8>(ptr + offset);
}

@inline
function fill(ptr: usize, value: u8, length: u32): void {
const finalPtr = ptr + length;
while(ptr < finalPtr) {
store<u8>(ptr, value);
ptr++;
}
}

@inline
function CH(x: u32, y: u32, z: u32): u32 {
Expand Down Expand Up @@ -167,7 +179,7 @@ function hashBlocks(wPtr: usize, mPtr: usize): void {
H7 += h;
}

function reset(): void {
export function init(): void {
H0 = 0x6a09e667;
H1 = 0xbb67ae85;
H2 = 0x3c6ef372;
Expand All @@ -181,8 +193,7 @@ function reset(): void {
bytesHashed = 0;
}

export function update(data: Uint8Array, dataLength: i32): void {
let dataPtr = data.dataStart;
export function update(dataPtr: usize, dataLength: i32): void {
let dataPos = 0;
bytesHashed += dataLength;
// If message blocks buffer has data, fill to 64
Expand Down Expand Up @@ -215,32 +226,30 @@ export function update(data: Uint8Array, dataLength: i32): void {
}
}

export function finish(output: ArrayBuffer): void {
export function final(outPtr: usize): void {
// one additional round of hashes required
// because padding will not fit
if ((bytesHashed & 63) < 63) {
store8(mPtr, mLength, 0x80);
mLength++;
}
if ((bytesHashed & 63) >= 56) {
memory.fill(mPtr + mLength, 0, 64 - mLength);
fill(mPtr + mLength, 0, 64 - mLength);
hashBlocks(wPtr, mPtr);
mLength = 0;
}
if ((bytesHashed & 63) >= 63) {
store8(mPtr, mLength, 0x80);
mLength++;
}
memory.fill(mPtr + mLength, 0, 64 - mLength - 8);
fill(mPtr + mLength, 0, 64 - mLength - 8);

store<u32>(mPtr + 64 - 8, bswap(bytesHashed / 0x20000000)); // length -- high bits
store<u32>(mPtr + 64 - 4, bswap(bytesHashed << 3)); // length -- low bits

// hash round for padding
hashBlocks(wPtr, mPtr);

let outPtr = changetype<usize>(output);

store32(outPtr, 0, bswap(H0));
store32(outPtr, 1, bswap(H1));
store32(outPtr, 2, bswap(H2));
Expand All @@ -251,35 +260,8 @@ export function finish(output: ArrayBuffer): void {
store32(outPtr, 7, bswap(H7));
}

export function digest(): Uint8Array {
finish(output);
let ret = new Uint8Array(DIGEST_LENGTH);
memory.copy(ret.dataStart, outputPtr, DIGEST_LENGTH);
return ret;
}

export function hash(data: Uint8Array): Uint8Array {
reset();
update(data, data.length);
finish(output);

let ret = new Uint8Array(DIGEST_LENGTH);
memory.copy(ret.dataStart, changetype<usize>(output), DIGEST_LENGTH);
return ret;
}

export function toHexString(bin: Uint8Array): string {
let bin_len = bin.length;
let hex = "";
for (let i = 0; i < bin_len; i++) {
let bin_i = bin[i] as u32;
let c = bin_i & 0xf;
let b = bin_i >> 4;
let x: u32 = ((87 + c + (((c - 10) >> 8) & ~38)) << 8) |
(87 + b + (((b - 10) >> 8) & ~38));
hex += String.fromCharCode(x as u8);
x >>= 8;
hex += String.fromCharCode(x as u8);
}
return hex;
export function digest(length: i32): void {
init();
update(inputPtr, length);
final(outputPtr)
}
Binary file modified build/optimized.wasm
Binary file not shown.
Loading