cryptography icon indicating copy to clipboard operation
cryptography copied to clipboard

Hkdf function requires a salt/nonce

Open rohintoncollins opened this issue 1 year ago • 2 comments

According to the HMAC-based Extract-and-Expand Key Derivation Function (HKDF) standard RFC 5869 (https://www.rfc-editor.org/rfc/rfc5869)

salt     optional salt value (a non-secret random value);
         if not provided, it is set to a string of HashLen zeros.

With cryptography version 2.7.0, the salt or nonce is required, even though the parameter is defined as optional with a default value of an empty list.

This code:

    final hkdf = Hkdf(hmac: Hmac.sha256(), outputLength: 32);
    final keyData = await hkdf.deriveKey(secretKey: secretKey);

will emit the following error:

Invalid argument (secretKey): Secret key must be non-empty: Instance of 'SecretKeyData'

This is because on line 44 of HKDF.dart, the nonce is used as the secretKey in a call to the Mac.calculateMac function:

   final nonceAsSecretKey = SecretKey(nonce);
   final prkMac = await Mac.calculateMac(
      secretKeyBytes,
      secretKey: nonceAsSecretKey,
      nonce: nonce
   );

This eventually causes an exception due to the following code in Mac.dart, line 123:

   if (secretKey.bytes.empty) {
      throw ArgumentError.noValue(
         secretKey,
         'secretKey',
         'secretKey must be non-empty',
      )
   }

A workaround is to manually do what the spec suggests: "if not provided, it is set to a string of HashLen zeros".

nonce: List<int>.filled(32, 0)

My testing indicates the latter successfully cross-tests with Botan and CryptoKit.

rohintoncollins avatar Feb 29 '24 21:02 rohintoncollins

@terrier989 Please add me as a contributor and I'll push a PR to fix this issue.

Tested fix which does not require workaround in client code would be, in hkdf.dart:

 @override
  Future<SecretKeyData> deriveKey({
    required SecretKey secretKey,
    List<int> nonce = const <int>[],
    List<int> info = const <int>[],
  }) async {
    /// https://github.com/dint-dev/cryptography/issues/176
    if (nonce.length == 0) {
      nonce = List<int>.filled(hmac.hashAlgorithm.hashLengthInBytes, 0);
    }
...

Although you may want to advise whether this fix would be required in other implementations of Hkdf.

rohintoncollins avatar Mar 01 '24 09:03 rohintoncollins

@rohintoncollins Unless this repository has some unusual permissions settings, you do not need to be a collaborator or member to open a pull request.

elliotwutingfeng avatar Mar 14 '24 21:03 elliotwutingfeng