add crypto_pwhash api

This commit is contained in:
Christophe Diederichs 2023-06-02 15:35:52 +01:00
parent 64089d1fca
commit 4465a532c4
3 changed files with 267 additions and 1 deletions

264
crypto_pwhash.js Normal file
View File

@ -0,0 +1,264 @@
/* eslint-disable camelcase */
const b4a = require('b4a')
const argon2 = require('../../wasm/argon2-wasm')
const { randombytes_buf } = require('./randombytes')
const crypto_pwhash_argon2i_ALG_ARGON2I13 = 1
const crypto_pwhash_argon2i_BYTES_MIN = 16
const crypto_pwhash_argon2i_BYTES_MAX = 4294967295
const crypto_pwhash_argon2i_PASSWD_MIN = 0
const crypto_pwhash_argon2i_PASSWD_MAX = 4294967295
const crypto_pwhash_argon2i_SALTBYTES = 16
const crypto_pwhash_argon2i_STRBYTES = 128
const crypto_pwhash_argon2i_STRPREFIX = '$argon2i$'
const crypto_pwhash_argon2i_OPSLIMIT_MIN = 3
const crypto_pwhash_argon2i_OPSLIMIT_MAX = 4294967295
const crypto_pwhash_argon2i_MEMLIMIT_MIN = 8192
const crypto_pwhash_argon2i_MEMLIMIT_MAX = 2147483648
const crypto_pwhash_argon2i_OPSLIMIT_INTERACTIVE = 4
const crypto_pwhash_argon2i_MEMLIMIT_INTERACTIVE = 33554432
const crypto_pwhash_argon2i_OPSLIMIT_MODERATE = 6
const crypto_pwhash_argon2i_MEMLIMIT_MODERATE = 134217728
const crypto_pwhash_argon2i_OPSLIMIT_SENSITIVE = 8
const crypto_pwhash_argon2i_MEMLIMIT_SENSITIVE = 536870912
const crypto_pwhash_argon2id_ALG_ARGON2ID13 = 2
const crypto_pwhash_argon2id_BYTES_MIN = 16
const crypto_pwhash_argon2id_BYTES_MAX = 4294967295
const crypto_pwhash_argon2id_PASSWD_MIN = 0
const crypto_pwhash_argon2id_PASSWD_MAX = 4294967295
const crypto_pwhash_argon2id_SALTBYTES = 16
const crypto_pwhash_argon2id_STRBYTES = 128
const crypto_pwhash_argon2id_STRPREFIX = '$argon2id$'
const crypto_pwhash_argon2id_OPSLIMIT_MIN = 1
const crypto_pwhash_argon2id_OPSLIMIT_MAX = 4294967295
const crypto_pwhash_argon2id_MEMLIMIT_MIN = 8192
const crypto_pwhash_argon2id_MEMLIMIT_MAX = 2147483648
const crypto_pwhash_argon2id_OPSLIMIT_INTERACTIVE = 2
const crypto_pwhash_argon2id_MEMLIMIT_INTERACTIVE = 67108864
const crypto_pwhash_argon2id_OPSLIMIT_MODERATE = 3
const crypto_pwhash_argon2id_MEMLIMIT_MODERATE = 268435456
const crypto_pwhash_argon2id_OPSLIMIT_SENSITIVE = 4
const crypto_pwhash_argon2id_MEMLIMIT_SENSITIVE = 1073741824
const crypto_pwhash_ALG_ARGON2I13 = crypto_pwhash_argon2i_ALG_ARGON2I13
const crypto_pwhash_ALG_ARGON2ID13 = crypto_pwhash_argon2id_ALG_ARGON2ID13
const crypto_pwhash_ALG_DEFAULT = crypto_pwhash_ALG_ARGON2ID13
const crypto_pwhash_BYTES_MIN = crypto_pwhash_argon2id_BYTES_MIN
const crypto_pwhash_BYTES_MAX = crypto_pwhash_argon2id_BYTES_MAX
const crypto_pwhash_PASSWD_MIN = crypto_pwhash_argon2id_PASSWD_MIN
const crypto_pwhash_PASSWD_MAX = crypto_pwhash_argon2id_PASSWD_MAX
const crypto_pwhash_SALTBYTES = crypto_pwhash_argon2id_SALTBYTES
const crypto_pwhash_STRBYTES = crypto_pwhash_argon2id_STRBYTES
const crypto_pwhash_STRPREFIX = crypto_pwhash_argon2id_STRPREFIX
const crypto_pwhash_OPSLIMIT_MIN = crypto_pwhash_argon2id_OPSLIMIT_MIN
const crypto_pwhash_OPSLIMIT_MAX = crypto_pwhash_argon2id_OPSLIMIT_MAX
const crypto_pwhash_MEMLIMIT_MIN = crypto_pwhash_argon2id_MEMLIMIT_MIN
const crypto_pwhash_MEMLIMIT_MAX = crypto_pwhash_argon2id_MEMLIMIT_MAX
const crypto_pwhash_OPSLIMIT_INTERACTIVE = crypto_pwhash_argon2id_OPSLIMIT_INTERACTIVE
const crypto_pwhash_MEMLIMIT_INTERACTIVE = crypto_pwhash_argon2id_MEMLIMIT_INTERACTIVE
const crypto_pwhash_OPSLIMIT_MODERATE = crypto_pwhash_argon2id_OPSLIMIT_MODERATE
const crypto_pwhash_MEMLIMIT_MODERATE = crypto_pwhash_argon2id_MEMLIMIT_MODERATE
const crypto_pwhash_OPSLIMIT_SENSITIVE = crypto_pwhash_argon2id_OPSLIMIT_SENSITIVE
const crypto_pwhash_MEMLIMIT_SENSITIVE = crypto_pwhash_argon2id_MEMLIMIT_SENSITIVE
module.exports = {
crypto_pwhash_ALG_ARGON2I13,
crypto_pwhash_ALG_ARGON2ID13,
crypto_pwhash_ALG_DEFAULT,
crypto_pwhash_BYTES_MIN,
crypto_pwhash_BYTES_MAX,
crypto_pwhash_PASSWD_MIN,
crypto_pwhash_PASSWD_MAX,
crypto_pwhash_SALTBYTES,
crypto_pwhash_STRBYTES,
crypto_pwhash_STRPREFIX,
crypto_pwhash_OPSLIMIT_MIN,
crypto_pwhash_OPSLIMIT_MAX,
crypto_pwhash_MEMLIMIT_MIN,
crypto_pwhash_MEMLIMIT_MAX,
crypto_pwhash_OPSLIMIT_INTERACTIVE,
crypto_pwhash_MEMLIMIT_INTERACTIVE,
crypto_pwhash_OPSLIMIT_MODERATE,
crypto_pwhash_MEMLIMIT_MODERATE,
crypto_pwhash_OPSLIMIT_SENSITIVE,
crypto_pwhash_MEMLIMIT_SENSITIVE,
crypto_pwhash_argon2i,
crypto_pwhash_argon2id,
crypto_pwhash_argon2i_str,
crypto_pwhash_argon2id_str,
crypto_pwhash,
crypto_pwhash_str,
crypto_pwhash_str_verify,
crypto_pwhash_str_needs_rehash
}
function crypto_pwhash_argon2i (out, passwd, salt, passes, memory, alg) {
const outlen = out.byteLength
const passwdlen = passwd.byteLength
out.fill(0)
if (outlen > crypto_pwhash_argon2i_BYTES_MAX) {
throw new Error('Too large')
}
if (outlen < crypto_pwhash_argon2i_BYTES_MIN) {
throw new Error('Invalid opts')
}
if (passwdlen > crypto_pwhash_argon2i_PASSWD_MAX ||
passes > crypto_pwhash_argon2i_OPSLIMIT_MAX ||
memory > crypto_pwhash_argon2i_MEMLIMIT_MAX) {
throw new Error('Too large')
}
if (passwdlen < crypto_pwhash_argon2i_PASSWD_MIN ||
(passes && passes < crypto_pwhash_argon2i_OPSLIMIT_MIN) ||
(memory && memory < crypto_pwhash_argon2i_MEMLIMIT_MIN)) {
throw new Error('Invalid opts')
}
switch (alg) {
case crypto_pwhash_argon2i_ALG_ARGON2I13: {
const buf = argon2(passwd, salt, null, null, 'binary', { memory: memory >> 10, passes, outlen, type: argon2.ARGON2I })
out.set(buf)
return 0
}
default:
throw new Error('Invalid input')
}
}
function crypto_pwhash_argon2id (out, passwd, salt, passes, memory, alg) {
const outlen = out.byteLength
const passwdlen = passwd.byteLength
out.fill(0)
if (outlen > crypto_pwhash_argon2id_BYTES_MAX) {
throw new Error('Too large')
}
if (outlen < crypto_pwhash_argon2id_BYTES_MIN) {
throw new Error('Invalid opts')
}
if (passwdlen > crypto_pwhash_argon2id_PASSWD_MAX ||
passes > crypto_pwhash_argon2id_OPSLIMIT_MAX ||
memory > crypto_pwhash_argon2id_MEMLIMIT_MAX) {
throw new Error('Too large')
}
if (passwdlen < crypto_pwhash_argon2id_PASSWD_MIN ||
(passes && passes < crypto_pwhash_argon2id_OPSLIMIT_MIN) ||
(memory && memory < crypto_pwhash_argon2id_MEMLIMIT_MIN)) {
throw new Error('Invalid opts')
}
switch (alg) {
case crypto_pwhash_argon2id_ALG_ARGON2ID13: {
const buf = argon2(passwd, salt, null, null, 'binary', { memory: memory >> 10, passes, outlen, type: argon2.ARGON2ID })
out.set(buf)
return 0
}
default:
throw new Error('Invalid input')
}
}
function crypto_pwhash_argon2i_str (out, passwd, salt, passes, memory, alg) {
const outlen = 32
const passwdlen = passwd.byteLength
if (passwdlen > crypto_pwhash_argon2i_PASSWD_MAX ||
passes > crypto_pwhash_argon2i_OPSLIMIT_MAX ||
memory > crypto_pwhash_argon2i_MEMLIMIT_MAX) {
throw new Error('Too large')
}
if (passwdlen < crypto_pwhash_argon2i_PASSWD_MIN ||
(passes && passes < crypto_pwhash_argon2i_OPSLIMIT_MIN) ||
(memory && memory < crypto_pwhash_argon2i_MEMLIMIT_MIN)) {
throw new Error('Invalid opts')
}
switch (alg) {
case crypto_pwhash_argon2i_ALG_ARGON2I13:
return argon2(passwd, salt, null, null, { memory: memory >> 10, passes, outlen, type: argon2.ARGON2I })
default:
throw new Error('Invalid input')
}
}
function crypto_pwhash_argon2id_str (out, passwd, salt, passes, memory, alg) {
const outlen = 32
const passwdlen = passwd.byteLength
if (passwdlen > crypto_pwhash_argon2id_PASSWD_MAX ||
passes > crypto_pwhash_argon2id_OPSLIMIT_MAX ||
memory > crypto_pwhash_argon2id_MEMLIMIT_MAX) {
throw new Error('Too large')
}
if (passwdlen < crypto_pwhash_argon2id_PASSWD_MIN ||
(passes && passes < crypto_pwhash_argon2id_OPSLIMIT_MIN) ||
(memory && memory < crypto_pwhash_argon2id_MEMLIMIT_MIN)) {
throw new Error('Invalid opts')
}
switch (alg) {
case crypto_pwhash_argon2id_ALG_ARGON2ID13:
out.write(argon2(passwd, salt, null, null, { memory: memory >> 10, passes, outlen, type: argon2.ARGON2ID }))
return
default:
throw new Error('Invalid input')
}
}
function crypto_pwhash (out, passwd, salt, opslimit, memlimit, alg) {
switch (alg) {
case crypto_pwhash_argon2i_ALG_ARGON2I13:
return crypto_pwhash_argon2i(out, passwd, salt, opslimit, memlimit, alg)
case crypto_pwhash_argon2id_ALG_ARGON2ID13:
return crypto_pwhash_argon2id(out, passwd, salt, opslimit, memlimit, alg)
}
}
function crypto_pwhash_str (out, passwd, opslimit, memlimit) {
const salt = b4a.alloc(crypto_pwhash_SALTBYTES)
randombytes_buf(salt)
return crypto_pwhash_argon2id_str(out, passwd, salt, opslimit, memlimit, crypto_pwhash_ALG_DEFAULT)
}
function crypto_pwhash_str_verify (str, passwd) {
if (b4a.isBuffer(str)) return crypto_pwhash_str_verify(str.toString(), passwd)
if (str.slice(0, crypto_pwhash_argon2id_STRPREFIX.length) === crypto_pwhash_argon2id_STRPREFIX) {
return argon2.verify(str, passwd, null)
}
if (str.slice(0, crypto_pwhash_argon2i_STRPREFIX.length) === crypto_pwhash_argon2i_STRPREFIX) {
return argon2.verify(str, passwd, null)
}
return false
}
function crypto_pwhash_str_needs_rehash (str, opslimit, memlimit, type) {
memlimit >>= 10
if ((opslimit | memlimit) > 0xffffffff || (str.byteLength > crypto_pwhash_STRBYTES)) {
throw new Error('Invalid opts.')
}
try {
return argon2.needsRehash(str, opslimit, memlimit, type)
} catch {
return true
}
}

View File

@ -12,6 +12,7 @@ forward(require('./randombytes'))
forward(require('./memory'))
forward(require('./helpers'))
forward(require('./utils'))
forward(require('./crypto_aead'))
forward(require('./crypto_auth'))
forward(require('./crypto_box'))
forward(require('./crypto_core'))
@ -21,7 +22,7 @@ forward(require('./crypto_hash'))
forward(require('./crypto_hash_sha256'))
forward(require('./crypto_kdf'))
forward(require('./crypto_kx'))
forward(require('./crypto_aead'))
forward(require('./crypto_pwhash'))
forward(require('./crypto_onetimeauth'))
forward(require('./crypto_scalarmult_ed25519'))
// forward(require('./crypto_scalarmult'))

View File

@ -4,6 +4,7 @@
"description": "WIP - a pure javascript version of sodium-native",
"main": "index.js",
"dependencies": {
"argon2-wasm": "^0.9.0",
"blake2b": "^2.1.1",
"chacha20-universal": "^1.0.4",
"nanoassert": "^2.0.0",