<commit_before>'use strict'
const { randomBytes } = require('crypto')
const { promisify } = require('util')
const bindings = require('bindings')('argon2')
const phc = require('@phc/format')

const limits = Object.freeze(bindings.limits)
const types = Object.freeze(bindings.types)

const defaults = Object.freeze({
  hashLength: 32,
  saltLength: 16,
  timeCost: 3,
  memoryCost: 1 << 12,
  parallelism: 1,
  type: types.argon2i,
  version: bindings.version
})

const bindingsHash = promisify(bindings.hash)
const generateSalt = promisify(randomBytes)

const hash = async (plain, { raw, salt, ...options } = {}) => {
  options = { ...defaults, ...options }

  for (const [key, { max, min }] of Object.entries(limits)) {
    const value = options[key]
    if (value > max || value < min) {
      throw new Error(`Invalid ${key}, must be between ${min} and ${max}.`)
    }
  }

  salt = salt || await generateSalt(options.saltLength)

  const output = await bindingsHash(Buffer.from(plain), salt, options)
  if (raw) {
    return output.hash
  }

  return phc.serialize(output)
}

const needsRehash = (digest, options) => {
  options = { ...defaults, ...options }

  const {
    version, params: { m: memoryCost, t: timeCost }
  } = phc.deserialize(digest)
  return +version !== +options.version ||
    +memoryCost !== +options.memoryCost ||
    +timeCost !== +options.timeCost
}

const verify = async (digest, plain) => {
  const {
    id: type, version = 0x10, params: {
      m: memoryCost, t: timeCost, p: parallelism
    }, salt, hash
  } = phc.deserialize(digest)

  const { hash: expected } = await bindingsHash(Buffer.from(plain), salt, {
    type: module.exports[type],
    version: +version,
    hashLength: hash.length,
    memoryCost: +memoryCost,
    timeCost: +timeCost,
    parallelism: +parallelism
  })
  return expected.equals(hash)
}

module.exports = { defaults, limits, hash, needsRehash, verify, ...types }<commit_msg>Use timing safe comparison on verify<commit_after>'use strict'
const { randomBytes, timingSafeEqual } = require('crypto')
const { promisify } = require('util')
const bindings = require('bindings')('argon2')
const phc = require('@phc/format')

const limits = Object.freeze(bindings.limits)
const types = Object.freeze(bindings.types)

const defaults = Object.freeze({
  hashLength: 32,
  saltLength: 16,
  timeCost: 3,
  memoryCost: 1 << 12,
  parallelism: 1,
  type: types.argon2i,
  version: bindings.version
})

const bindingsHash = promisify(bindings.hash)
const generateSalt = promisify(randomBytes)

const hash = async (plain, { raw, salt, ...options } = {}) => {
  options = { ...defaults, ...options }

  for (const [key, { max, min }] of Object.entries(limits)) {
    const value = options[key]
    if (value > max || value < min) {
      throw new Error(`Invalid ${key}, must be between ${min} and ${max}.`)
    }
  }

  salt = salt || await generateSalt(options.saltLength)

  const output = await bindingsHash(Buffer.from(plain), salt, options)
  if (raw) {
    return output.hash
  }

  return phc.serialize(output)
}

const needsRehash = (digest, options) => {
  options = { ...defaults, ...options }

  const {
    version, params: { m: memoryCost, t: timeCost }
  } = phc.deserialize(digest)
  return +version !== +options.version ||
    +memoryCost !== +options.memoryCost ||
    +timeCost !== +options.timeCost
}

const verify = async (digest, plain) => {
  const {
    id: type, version = 0x10, params: {
      m: memoryCost, t: timeCost, p: parallelism
    }, salt, hash
  } = phc.deserialize(digest)

  const { hash: expected } = await bindingsHash(Buffer.from(plain), salt, {
    type: module.exports[type],
    version: +version,
    hashLength: hash.length,
    memoryCost: +memoryCost,
    timeCost: +timeCost,
    parallelism: +parallelism
  })
  return timingSafeEqual(expected, hash)
}

module.exports = { defaults, limits, hash, needsRehash, verify, ...types }
{'fraction_non_alphanumeric': 0.1490066225165563, 'fraction_numerical': 0.007131940906775344, 'mean_word_length': 3.6917562724014337, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2693226', 'n_tokens_mistral': 1304, 'n_tokens_neox': 1251, 'n_words': 335}