PTO_SCRYPT_INVALID_PARAMETER();
      r = options.blockSize;
      validateUint32(r, 'blockSize');
    }
    const has_p = options.p !== undefined;
    if (has_p) {
      p = options.p;
      validateUint32(p, 'p');
    }
    if (options.parallelization !== undefined) {
      if (has_p) throw new ERR_CRYPTO_SCRYPT_INVALID_PARAMETER();
      p = options.parallelization;
      validateUint32(p, 'parallelization');
    }
    if (options.maxmem !== undefined) {
      maxmem = options.maxmem;
      validateInteger(maxmem, 'maxmem', 0);
    }
    if (N === 0) N = defaults.N;
    if (r === 0) r = defaults.r;
    if (p === 0) p = defaults.p;
    if (maxmem === 0) maxmem = defaults.maxmem;
  }

  return { password, salt, keylen, N, r, p, maxmem };
}

async function scryptDeriveBits(algorithm, baseKey, length) {
  validateUint32(length, 'length');
  const {
    N = 16384,
    r = 8,
    p = 1,
    maxmem = 32 * 1024 * 1024,
    encoding,
  } = algorithm;
  validateUint32(N, 'algorithm.N');
  validateUint32(r, 'algorithm.r');
  validateUint32(p, 'algorithm.p');
  validateUint32(maxmem, 'algorithm.maxmem');
  const salt = getArrayBufferOrView(algorithm.salt, 'algorithm.salt', encoding);

  const raw = baseKey[kKeyObject].export();

  let byteLength = 64;  // the default
  if (length !== undefined) {
    if (length === 0)
      throw lazyDOMException('length cannot be zero', 'OperationError');
    if (length % 8) {
      throw lazyDOMException(
        'length must be a multiple of 8',
        'OperationError');
    }
    byteLength = length / 8;
  }

  return new Promise((resolve, reject) => {
    scrypt(raw, salt, byteLength, { N, r, p, maxmem }, (err, result) => {
      if (err) return reject(err);
      resolve(result.buffer);
    });
  });
}

module.exports = {
  scrypt,
  scryptSync,
  scryptDeriveBits,
};
