createSaltedVerificationKeyFromBytes static method

Future<SaltedVerificationKey> createSaltedVerificationKeyFromBytes({
  1. required Uint8List passwordBytes,
  2. Uint8List? userIdBytes,
  3. BigInt? generator,
  4. BigInt? safePrime,
  5. KdfChoice? kdf,
  6. Kdf? customKdf,
  7. Uint8List? salt,
})

Creates a salted verification key from byte arrays.

Pass this key to server as part of user registration request.

WARNING: If safePrime is not provided, the default safe prime provided by dsrp is used. This should NOT be done in production. You are encouraged to generate your own safe prime instead to reduce the chance of a pre-computed attack on common safe primes impacting your users.

If kdf is not provided, Argon2id is used since it is slow and hence relatively secure. Alternatively, provide customKdf to use a custom KDF implementation (cannot provide both kdf and customKdf).

If salt is not provided then a 32-byte random salt is generated.

Only provide userIdBytes if you want derivation of the user private key to include it, as is done in the RFC5054 standard. Not including the user ID means if the ID changes, the user private key will need to be regenerated and the user registration process repeated. When key derivation excludes the user ID, re-registration is only needed if the password changes.

Another option is to provide a unique, fixed userIdBytes (e.g., a UUID, user database index, etc.) that is different from the user-chosen ID. That allows the user to change their login ID while their internal user ID remains constant.

For improved security, use createSaltedVerificationKeyFromBytes to pass credentials as Uint8List instead of String.

This method is preferred over createSaltedVerificationKey for security reasons, as it avoids storing passwords as Strings in memory.

passwordBytes and userIdBytes should be UTF-8 encoded credentials.

Implementation

static Future<SaltedVerificationKey> createSaltedVerificationKeyFromBytes({
    required Uint8List passwordBytes,
    Uint8List? userIdBytes,
    BigInt? generator, BigInt? safePrime,
    KdfChoice? kdf,
    Kdf? customKdf,
    Uint8List? salt
}) async {
  if (kdf != null && customKdf != null) {
    throw InvalidParameterException(
      'Cannot provide both kdf and customKdf. Please provide only one.'
    );
  }
  if (safePrime == null) {
    _log.warning('Using default safe prime. For production use, generate a custom safe prime using scripts/generate_safe_primes to reduce risk of pre-computed attacks.');
  }
  final generatorBigInt = generator ?? defaultGenerator;
  final safePrimeBigInt = safePrime ?? defaultSafePrime;

  final resolvedKdf = customKdf ?? getKdf(kdf ?? defaultKdfChoice);
  salt ??= generateRandomBytes(defaultSaltByteLengthForSaltedVerificationKey);
  final privateKey = await _derivePrivateKey(
    userIdBytes: userIdBytes,
    passwordBytes: passwordBytes,
    salt: salt,
    kdf: resolvedKdf
  );

  final verifierKey = _deriveVerificationKey(privateKey: privateKey,
    generator: generatorBigInt, safePrime: safePrimeBigInt);
  final verifierKeyBytes = verifierKey.toByteList();
  return SaltedVerificationKey(
    key: verifierKeyBytes,
    salt: salt,
  );
}