Add custom argon2 Wasm implementation

This commit is contained in:
Simon Warta 2021-10-20 00:14:41 +02:00
parent 79b147b644
commit d3c8a276e1
12 changed files with 497 additions and 14 deletions

View File

@ -36,7 +36,7 @@
"test-safari": "yarn pack-web && karma start --single-run --browsers Safari",
"test": "yarn build-or-skip && yarn test-node",
"coverage": "nyc --reporter=text --reporter=lcov yarn test --quiet",
"build": "rm -rf ./build && tsc",
"build": "rm -rf ./build && tsc && cp -R src/pkg2 build",
"build-or-skip": "[ -n \"$SKIP_BUILD\" ] || yarn build",
"pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.js"
},

View File

@ -129,15 +129,15 @@ describe("Libsodium", () => {
// 8 bytes
await Argon2id.execute(password, fromHex("aabbccddeeff0011"), options)
.then(() => fail("Argon2id with invalid salt length must not resolve"))
.catch((e) => expect(e).toMatch(/invalid salt length/));
.catch((e) => expect(e).toMatch(/invalid salt length/i));
// 15 bytes
await Argon2id.execute(password, fromHex("aabbccddeeff001122334455667788"), options)
.then(() => fail("Argon2id with invalid salt length must not resolve"))
.catch((e) => expect(e).toMatch(/invalid salt length/));
.catch((e) => expect(e).toMatch(/invalid salt length/i));
// 17 bytes
await Argon2id.execute(password, fromHex("aabbccddeeff00112233445566778899aa"), options)
.then(() => fail("Argon2id with invalid salt length must not resolve"))
.catch((e) => expect(e).toMatch(/invalid salt length/));
.catch((e) => expect(e).toMatch(/invalid salt length/i));
// 32 bytes
await Argon2id.execute(
password,
@ -145,7 +145,7 @@ describe("Libsodium", () => {
options,
)
.then(() => fail("Argon2id with invalid salt length must not resolve"))
.catch((e) => expect(e).toMatch(/invalid salt length/));
.catch((e) => expect(e).toMatch(/invalid salt length/i));
});
});

View File

@ -6,6 +6,8 @@
import { isNonNullObject } from "@cosmjs/utils";
import sodium from "libsodium-wrappers";
import argon2id from "./pkg2";
export interface Argon2idOptions {
/** Output length in bytes */
readonly outputLength: number;
@ -39,15 +41,21 @@ export class Argon2id {
salt: Uint8Array,
options: Argon2idOptions,
): Promise<Uint8Array> {
await sodium.ready;
return sodium.crypto_pwhash(
options.outputLength,
password,
salt, // libsodium only supports 16 byte salts and will throw when you don't respect that
options.opsLimit,
options.memLimitKib * 1024,
sodium.crypto_pwhash_ALG_ARGON2ID13,
);
// await sodium.ready;
// return sodium.crypto_pwhash(
// options.outputLength,
// password,
// salt, // libsodium only supports 16 byte salts and will throw when you don't respect that
// options.opsLimit,
// options.memLimitKib * 1024,
// sodium.crypto_pwhash_ALG_ARGON2ID13,
// );
if (salt.length !== 16) {
throw new Error(
"Invalid salt length. Only 16 bytes is supported for compatibility with previous libsodium-based implementation. Feel free to raise an issue if you need something else.",
);
}
return argon2id.hash(password, salt, options.outputLength, options.memLimitKib, options.opsLimit);
}
}

17
packages/crypto/src/pkg2/index.d.ts vendored Normal file
View File

@ -0,0 +1,17 @@
/* tslint:disable */
/* eslint-disable */
/**
* @param {string} password
* @param {Uint8Array} salt
* @param {number} output_length
* @param {number} memory_cost
* @param {number} time_cost
* @returns {Uint8Array}
*/
export function hash(
password: string,
salt: Uint8Array,
output_length: number,
memory_cost: number,
time_cost: number,
): Uint8Array;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,4 @@
{
"main": "index.js",
"types": "index.d.ts"
}

View File

@ -0,0 +1,3 @@
[alias]
wasm = "build --release --target wasm32-unknown-unknown"
wasm-debug = "build --target wasm32-unknown-unknown"

3
wasm/argon2id/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
pkg/
pkg2/
target/

210
wasm/argon2id/Cargo.lock generated Normal file
View File

@ -0,0 +1,210 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "argon2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0a21e93ce9e91fcd0e01744e4f205fb9192f82915646a7e880dfef0ab4a38d8"
dependencies = [
"base64ct",
"blake2",
]
[[package]]
name = "argon2id"
version = "0.1.0"
dependencies = [
"argon2",
"wasm-bindgen",
]
[[package]]
name = "base64ct"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b"
[[package]]
name = "blake2"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b94ba84325db59637ffc528bbe8c7f86c02c57cff5c0e2b9b00f9a851f42f309"
dependencies = [
"digest",
]
[[package]]
name = "block-buffer"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03588e54c62ae6d763e2a80090d50353b785795361b4ff5b3bf0a5097fc31c0b"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crypto-common"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d6b536309245c849479fba3da410962a43ed8e51c26b729208ec0ac2798d0"
dependencies = [
"generic-array",
]
[[package]]
name = "digest"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b"
dependencies = [
"block-buffer",
"crypto-common",
"generic-array",
"subtle",
]
[[package]]
name = "generic-array"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "proc-macro2"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
"proc-macro2",
]
[[package]]
name = "subtle"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "typenum"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "wasm-bindgen"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"

12
wasm/argon2id/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "argon2id"
version = "0.1.0"
edition = "2018"
publish = false
[lib]
crate-type = ["cdylib"]
[dependencies]
argon2 = { version = "0.3.2", default-features = false, features = ["alloc"] }
wasm-bindgen = { version = "0.2.78" }

24
wasm/argon2id/src/lib.rs Normal file
View File

@ -0,0 +1,24 @@
use wasm_bindgen::prelude::*;
use argon2::{Algorithm, Argon2, Params, Version};
#[wasm_bindgen]
pub fn hash(
password: &str,
salt: &[u8],
output_length: usize,
memory_cost: u32,
time_cost: u32,
) -> Result<Vec<u8>, JsValue> {
let threads = 1;
let params =
Params::new(memory_cost, time_cost, threads, Some(output_length)).map_err(|e| e.to_string())?;
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
let mut out = vec![0; output_length];
argon2
.hash_password_into(password.as_bytes(), salt, &mut out[..])
.map_err(|e| e.to_string())?;
Ok(out)
}

33
wasm/build_argon2id.sh Executable file
View File

@ -0,0 +1,33 @@
#!/bin/bash
set -o errexit -o nounset -o pipefail
command -v shellcheck >/dev/null && shellcheck "$0"
gnused="$(command -v gsed || echo sed)"
(
cd argon2id
wasm-pack build --target nodejs
# Prettify before inlining the big Wasm blob to keep this fast and bytes list compact
yarn prettier --write 'pkg/*.{js,ts}'
echo "Wasm blob built:"
ls -l pkg/argon2id_bg.wasm
BYTES=$(python3 <<HEREDOC
with open("pkg/argon2id_bg.wasm", "rb") as f:
bytes = [str(byte[0]) for byte in iter(lambda: f.read(1), b'')]
print("const bytes = new Uint8Array([" + ",".join(bytes) + "]);")
HEREDOC
)
"$gnused" -i \
-e "s/^const { TextDecoder, TextEncoder } =.*$//" \
-e "s/^const path =.*$//" \
-e "s/^const bytes =.*$/$BYTES/" \
pkg/argon2id.js
mkdir -p pkg2
cp pkg/argon2id.js pkg2/index.js
cp pkg/argon2id.d.ts pkg2/index.d.ts
echo '{ "main": "index.js", "types": "index.d.ts" }' | jq > pkg2/package.json
cp -R pkg2 ../../packages/crypto/src/
)