Skip to content
This repository has been archived by the owner on Jul 30, 2023. It is now read-only.

Commit

Permalink
feat: implement generichash (#21)
Browse files Browse the repository at this point in the history
* feat: init

* feat: add functions

* feat: add complete implementation
  • Loading branch information
Tomio committed Apr 2, 2022
1 parent e362b74 commit c24d717
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 21 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ crate-type = ["cdylib"]
[dependencies]
napi = { version = "2.2.0", features = ["napi6"] }
napi-derive = "2.2.0"
sodiumoxide = { git = "https://github.com/devtomio/sodiumoxide", branch = "master" }
sodiumoxide = { git = "https://github.com/devtomio/sodiumoxide" }
dryoc = { git = "https://github.com/brndnmtthws/dryoc" }
libc = "0.2.121"

Expand Down
4 changes: 2 additions & 2 deletions lib/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import { loadBinding } from '@node-rs/helper';
import { join } from 'node:path';

const bindings: { [key: string]: any } = loadBinding(join(__dirname, '..'), 'sodium', '@devtomio/sodium');
const { Box, randombytes_buf, SecretBox, Sign } = bindings;
const { Box, randombytes_buf, SecretBox, Sign, GenericHash } = bindings;

export { Box, randombytes_buf, SecretBox, Sign };
export { Box, randombytes_buf, SecretBox, Sign, GenericHash };
34 changes: 34 additions & 0 deletions lib/crypto/generichash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { GenericHash } from '../bindings';
import type { Nullish } from '../types';

const generichash = new GenericHash();

export interface GenericHashState {
opaque: Uint8Array;
}

export const crypto_generichash_BYTES = generichash.cryptoGenerichashBytes;
export const crypto_generichash_BYTES_MAX = generichash.cryptoGenerichashBytesMax;
export const crypto_generichash_BYTES_MIN = generichash.cryptoGenerichashBytesMin;
export const crypto_generichash_KEYBYTES = generichash.cryptoGenerichashKeybytes;
export const crypto_generichash_KEYBYTES_MAX = generichash.cryptoGenerichashKeybytesMax;
export const crypto_generichash_KEYBYTES_MIN = generichash.cryptoGenerichashKeybytesMin;

/** @see https://docs.rs/sodiumoxide/latest/sodiumoxide/crypto/generichash/fn.hash.html */
export const crypto_generichash = (data: Uint8Array, out_len: Nullish<number>, key: Nullish<Uint8Array>): Uint8Array =>
generichash.crypto_generichash(data, out_len, key);

/** @see https://docs.rs/libsodium-sys/latest/libsodium_sys/fn.crypto_generichash_final.html */
export const crypto_generichash_final = (state: GenericHashState, out_len: number): Uint8Array =>
generichash.crypto_generichash_final(state, out_len);

/** @see https://docs.rs/sodiumoxide/latest/sodiumoxide/crypto/generichash/struct.State.html#method.new */
export const crypto_generichash_init = (out_len: Nullish<number>, key: Nullish<Uint8Array>): GenericHashState =>
generichash.crypto_generichash_init(out_len, key);

/** @see https://docs.rs/libsodium-sys/latest/libsodium_sys/fn.crypto_generichash_keygen.html */
export const crypto_generichash_keygen = (): Uint8Array => generichash.crypto_generichash_keygen();

/** @see https://docs.rs/libsodium-sys/latest/libsodium_sys/fn.crypto_generichash_update.html */
export const crypto_generichash_update = (state: GenericHashState, data: Uint8Array): GenericHashState =>
generichash.crypto_generichash_update(state, data);
14 changes: 7 additions & 7 deletions lib/crypto/sign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import type { KeyPair } from '../types';

const sign = new Sign();

export interface Hash {
export interface SignHash {
state: BigUint64Array;
count: BigUint64Array;
buf: Uint8Array;
}

export interface State {
hs: Hash;
export interface SignState {
hs: SignHash;
}

export const crypto_sign_BYTES: number = sign.cryptoSignBytes;
Expand All @@ -32,13 +32,13 @@ export const crypto_sign_ed25519_pk_to_curve25519 = (ed25519_pk: Uint8Array): Ui
export const crypto_sign_ed25519_sk_to_curve25519 = (ed25519_sk: Uint8Array): Uint8Array => sign.crypto_sign_ed25519_sk_to_curve25519(ed25519_sk);

/** @see https://docs.rs/libsodium-sys/latest/libsodium_sys/fn.crypto_sign_final_create.html */
export const crypto_sign_final_create = (state: State, sk: Uint8Array): Uint8Array => sign.crypto_sign_final_create(state, sk);
export const crypto_sign_final_create = (state: SignState, sk: Uint8Array): Uint8Array => sign.crypto_sign_final_create(state, sk);

/** @see https://docs.rs/libsodium-sys/latest/libsodium_sys/fn.crypto_sign_final_verify.html */
export const crypto_sign_final_verify = (state: State, sig: Uint8Array, pk: Uint8Array): boolean => sign.crypto_sign_final_verify(state, sig, pk);
export const crypto_sign_final_verify = (state: SignState, sig: Uint8Array, pk: Uint8Array): boolean => sign.crypto_sign_final_verify(state, sig, pk);

/** @see https://docs.rs/libsodium-sys/latest/libsodium_sys/fn.crypto_sign_init.html */
export const crypto_sign_init = (): State => sign.crypto_sign_init();
export const crypto_sign_init = (): SignState => sign.crypto_sign_init();

/** @see https://docs.rs/sodiumoxide/latest/sodiumoxide/crypto/sign/ed25519/fn.gen_keypair.html */
export const crypto_sign_keypair = (): KeyPair => sign.crypto_sign_keypair();
Expand All @@ -50,7 +50,7 @@ export const crypto_sign_open = (sm: Uint8Array, pk: Uint8Array): Uint8Array =>
export const crypto_sign_seed_keypair = (seed: Uint8Array): KeyPair => sign.crypto_sign_seed_keypair(seed);

/** @see https://docs.rs/libsodium-sys/latest/libsodium_sys/fn.crypto_sign_update.html */
export const crypto_sign_update = (state: State, m: Uint8Array): State => sign.crypto_sign_update(state, m);
export const crypto_sign_update = (state: SignState, m: Uint8Array): SignState => sign.crypto_sign_update(state, m);

/** @see https://docs.rs/sodiumoxide/latest/sodiumoxide/crypto/sign/ed25519/fn.verify_detached.html */
export const crypto_sign_verify_detached = (sig: Uint8Array, m: Uint8Array, pk: Uint8Array): boolean => sign.crypto_sign_verify_detached(sig, m, pk);
1 change: 1 addition & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export * from './crypto/box';
export * from './crypto/rng';
export * from './crypto/secretbox';
export * from './crypto/sign';
export * from './crypto/generichash';
export * from './types';
export * as raw from './bindings';
2 changes: 2 additions & 0 deletions lib/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export type Nullish<T> = T | null | undefined;

export interface KeyPair {
public_key: Uint8Array;
secret_key: Uint8Array;
Expand Down
153 changes: 153 additions & 0 deletions src/generichash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
use crate::vec_arr_func;
use dryoc::constants::*;
use libc::c_ulonglong;
use napi::bindgen_prelude::*;
use sodiumoxide::{crypto::generichash, ffi, init};

vec_arr_func!(to_opaque, u8, 384);

#[napi(object)]
pub struct GenericHashState {
pub opaque: Uint8Array,
}

#[napi]
pub struct GenericHash {}

#[napi]
impl GenericHash {
#[napi(constructor)]
pub fn new() -> Self {
init().unwrap();
GenericHash {}
}

#[napi(js_name = "crypto_generichash")]
pub fn crypto_generichash(
&self,
data: Uint8Array,
out_len: Option<u32>,
key: Option<Uint8Array>,
) -> Uint8Array {
let ol = match out_len {
Some(l) => Some(l as usize),
None => None,
};

let k = match key {
Some(ky) => Some(ky.to_vec()),
None => None,
};

let d = generichash::hash(&data, ol, k).unwrap();

Uint8Array::new(d.as_ref().to_vec())
}

#[napi(js_name = "crypto_generichash_final")]
pub fn crypto_generichash_final(&self, state: GenericHashState, out_len: u32) -> Result<Uint8Array> {
let mut result = generichash::Digest::new(out_len as usize);
let mut st = ffi::crypto_generichash_state {
opaque: to_opaque(&state.opaque),
};
let rc =
unsafe { ffi::crypto_generichash_final(&mut st, result.data.as_mut_ptr(), result.len) };

match rc {
0 => Ok(Uint8Array::new(result.data.to_vec())),
_ => Err(Error::new(
Status::GenericFailure,
"Failed to execute".to_string(),
)),
}
}

#[napi(js_name = "crypto_generichash_init")]
pub fn crypto_generichash_init(
&self,
out_len: Option<u32>,
key: Option<Uint8Array>,
) -> GenericHashState {
let ol = match out_len {
Some(l) => Some(l as usize),
None => None,
};

let k = match key {
Some(ky) => Some(ky.to_vec()),
None => None,
};

let s = generichash::State::new(ol, k).unwrap();

GenericHashState {
opaque: Uint8Array::new(s.state.opaque.to_vec()),
}
}

#[napi(js_name = "crypto_generichash_keygen")]
pub fn crypto_generichash_keygen(&self) -> Uint8Array {
let mut k = [0u8; CRYPTO_GENERICHASH_KEYBYTES];

unsafe {
ffi::crypto_generichash_keygen(k.as_mut_ptr());
};

Uint8Array::new(k.to_vec())
}

#[napi(js_name = "crypto_generichash_update")]
pub fn crypto_generichash_update(
&self,
state: GenericHashState,
data: Uint8Array,
) -> Result<GenericHashState> {
let mut st = ffi::crypto_generichash_state {
opaque: to_opaque(&state.opaque),
};

let rc = unsafe {
ffi::crypto_generichash_update(&mut st, data.as_ptr(), data.len() as c_ulonglong)
};

match rc {
0 => Ok(GenericHashState {
opaque: Uint8Array::new(st.opaque.to_vec()),
}),
_ => Err(Error::new(
Status::GenericFailure,
"Failed to execute".to_string(),
)),
}
}

#[napi(getter)]
pub fn crypto_generichash_bytes(&self) -> u32 {
CRYPTO_GENERICHASH_BYTES as u32
}

#[napi(getter)]
pub fn crypto_generichash_bytes_max(&self) -> u32 {
CRYPTO_GENERICHASH_BYTES_MAX as u32
}

#[napi(getter)]
pub fn crypto_generichash_bytes_min(&self) -> u32 {
CRYPTO_GENERICHASH_BYTES_MIN as u32
}

#[napi(getter)]
pub fn crypto_generichash_keybytes(&self) -> u32 {
CRYPTO_GENERICHASH_KEYBYTES as u32
}

#[napi(getter)]
pub fn crypto_generichash_keybytes_max(&self) -> u32 {
CRYPTO_GENERICHASH_KEYBYTES_MAX as u32
}

#[napi(getter)]
pub fn crypto_generichash_keybytes_min(&self) -> u32 {
CRYPTO_GENERICHASH_KEYBYTES_MIN as u32
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
extern crate napi_derive;

pub mod r#box;
pub mod generichash;
pub mod r#macro;
pub mod rng;
pub mod secretbox;
Expand Down
22 changes: 11 additions & 11 deletions src/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ vec_arr_func!(to_count, u64, 2);
vec_arr_func!(to_buf, u8, 128);

#[napi(object)]
pub struct Hash {
pub struct SignHash {
pub state: BigUint64Array,
pub count: BigUint64Array,
pub buf: Uint8Array,
}

#[napi(object)]
pub struct State {
pub hs: Hash,
pub struct SignState {
pub hs: SignHash,
}

#[napi]
Expand Down Expand Up @@ -63,7 +63,7 @@ impl Sign {
}

#[napi(js_name = "crypto_sign_final_create")]
pub fn crypto_sign_final_create(&mut self, state: State, sk: Uint8Array) -> Uint8Array {
pub fn crypto_sign_final_create(&mut self, state: SignState, sk: Uint8Array) -> Uint8Array {
let mut sig = [0u8; CRYPTO_SIGN_BYTES];
let mut siglen: c_ulonglong = 0;
let mut st = ffi::crypto_sign_state {
Expand All @@ -84,7 +84,7 @@ impl Sign {
#[napi(js_name = "crypto_sign_final_verify")]
pub fn crypto_sign_final_verify(
&mut self,
state: State,
state: SignState,
mut sig: Uint8Array,
pk: Uint8Array,
) -> bool {
Expand All @@ -102,15 +102,15 @@ impl Sign {
}

#[napi(js_name = "crypto_sign_init")]
pub fn crypto_sign_init(&mut self) -> State {
pub fn crypto_sign_init(&mut self) -> SignState {
let mut s = MaybeUninit::uninit();
let state = unsafe {
ffi::crypto_sign_init(s.as_mut_ptr());
s.assume_init()
};

State {
hs: Hash {
SignState {
hs: SignHash {
state: BigUint64Array::new(state.hs.state.to_vec()),
count: BigUint64Array::new(state.hs.count.to_vec()),
buf: Uint8Array::new(state.hs.buf.to_vec()),
Expand Down Expand Up @@ -147,7 +147,7 @@ impl Sign {
}

#[napi(js_name = "crypto_sign_update")]
pub fn crypto_sign_update(&self, state: State, m: Uint8Array) -> State {
pub fn crypto_sign_update(&self, state: SignState, m: Uint8Array) -> SignState {
let mut st = ffi::crypto_sign_state {
hs: ffi::crypto_hash_sha512_state {
state: to_state(&state.hs.state),
Expand All @@ -160,8 +160,8 @@ impl Sign {
ffi::crypto_sign_update(&mut st, m.as_ptr(), m.len() as c_ulonglong);
}

State {
hs: Hash {
SignState {
hs: SignHash {
state: BigUint64Array::new(st.hs.state.to_vec()),
count: BigUint64Array::new(st.hs.count.to_vec()),
buf: Uint8Array::new(st.hs.buf.to_vec()),
Expand Down
25 changes: 25 additions & 0 deletions tests/generichash.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as sodium from '../dist';

test('generichash', () => {
const message = 'Science, math, technology, engineering, and compassion for others.';
const piece1 = message.slice(0, 16);
const piece2 = message.slice(16);

let hash1 = Buffer.from(sodium.crypto_generichash(Buffer.from(message), 32, null));
expect(hash1.toString('hex')).toBe('47c1fdbde32b30b9c54dd47cf88ba92d2d05df1265e342c9563ed56aee84ab02');

let state = sodium.crypto_generichash_init(32, null);
let state1 = sodium.crypto_generichash_update(state, Buffer.from(piece1));
let state2 = sodium.crypto_generichash_update(state1, Buffer.from(piece2));
let hash2 = Buffer.from(sodium.crypto_generichash_final(state2, 32));
expect(hash2.toString('hex', 0, 32)).toBe(hash1.toString('hex', 0, 32));

const key = Buffer.from(sodium.crypto_generichash_keygen());
hash1 = Buffer.from(sodium.crypto_generichash(Buffer.from(message), 32, key));
state = sodium.crypto_generichash_init(32, key);
state1 = sodium.crypto_generichash_update(state, Buffer.from(piece1));
state2 = sodium.crypto_generichash_update(state1, Buffer.from(piece2));
hash2 = Buffer.from(sodium.crypto_generichash_final(state2, 32));

expect(hash1.toString('hex', 0, 32)).toBe(hash2.toString('hex', 0, 32));
});

0 comments on commit c24d717

Please sign in to comment.