3

I have the following function call to scrypt() from hashlib using Python 3.7.9:

def aes_encrypt(msg, passwordStr):
    kdfSalt = urandom(16) # 16 bytes == 128 bits
    hashedKey = scrypt(passwordStr.encode(),salt=kdfSalt,n=16384,r=16,p=1, dklen=64) # 64 octets = 512 bits

When this code runs, I get the error:

  File "aes_scrypt_hmac.py", line 69, in <module>
    main()
  File "aes_scrypt_hmac.py", line 38, in main
    print(aes_encrypt(sampleData,testPassword))
  File "aes_scrypt_hmac.py", line 18, in aes_encrypt
    hashedKey = scrypt(passwordStr.encode(),salt=kdfSalt,n=16384,r=16,p=1, dklen=64) 
ValueError: Invalid parameter combination for n, r, p, maxmem.

I have read the documentation for scrypt, and it does not specify the expectations for the parameters; though it does link to the RFC and these params seem valid. maxmem's specific requirement is not mentioned in the documentation (e.g. what does 0 mean? And what the unit of measurement is) or in the RFC.

the_endian
  • 2,259
  • 1
  • 24
  • 49

1 Answers1

1

I have to admit I'm not sure which API you plan on using. According to scrypt's python package, the APIs are encrypt, decrypt and hash, and you are using something I can't find.

Your method is named encrypt, but the variable is called hashedKey, so I'm not sure if you are hashing or encrypting, and those are obviously different.

However, these references might help.

scrypt's python package implementation:

def hash(password, salt, N=1 << 14, r=8, p=1, buflen=64):
    """
    Compute scrypt(password, salt, N, r, p, buflen).
    The parameters r, p, and buflen must satisfy r * p < 2^30 and
    buflen <= (2^32 - 1) * 32. The parameter N must be a power of 2
    greater than 1. N, r and p must all be positive.
    Notes for Python 2:
      - `password` and `salt` must be str instances
      - The result will be a str instance
    Notes for Python 3:
      - `password` and `salt` can be both str and bytes. If they are str
        instances, they wil be encoded with utf-8.
      - The result will be a bytes instance
    Exceptions raised:
      - TypeError on invalid input
      - scrypt.error if scrypt failed
    """

When I run the following script using Python3 and the PyPi scrypt package, everything works for me:

import scrypt
import os

def aes_encrypt(pwd):
    kdfSalt = os.urandom(16) # 16 bytes == 128 bits
    return scrypt.hash(pwd.encode(), salt=kdfSalt, N=16384, r=16, p=1, buflen=64)

Go's scrypt package manual:

Key derives a key from the password, salt, and cost parameters, returning a byte slice of length keyLen that can be used as cryptographic key.

N is a CPU/memory cost parameter, which must be a power of two greater than 1. r and p must satisfy r * p < 2³⁰. If the parameters do not satisfy the limits, the function returns a nil byte slice and an error.

For example, you can get a derived key for e.g. AES-256 (which needs a 32-byte key) by doing:

dk, err := scrypt.Key([]byte("some password"), salt, 32768, 8, 1, 32)

The recommended parameters for interactive logins as of 2017 are N=32768, r=8 and p=1. The parameters N, r, and p should be increased as memory latency and CPU parallelism increases; consider setting N to the highest power of 2 you can derive within 100 milliseconds. Remember to get a good random salt.

Daniel Trugman
  • 8,186
  • 20
  • 41
  • I have to investigate this a bit more; I'm seeing something weird here where probably the API was updated because you are importing `scrypt`, but I am importing `from hashlib import scrypt`. As per the documentation that I linked, my function signature seems to check out as valid (but diff from yours), and my IDE confirms this as well. Also note that I am wondering specifically why I am getting an error about invalid parameters when these do appear to be valid, not necessarily about the recommended parameters (the project calls for these specific ones in this case, but thats beside the point). – the_endian Jun 14 '22 at 21:55
  • @the_endian, yes, that's probably a good place to start your discovery. Anyway, I hope that will help you solve your issue. – Daniel Trugman Jun 14 '22 at 22:04
  • 1
    So I still don't know what the *original* issue is, but I did a `python3 -m pip install scrypt` and I simply changed my `dklen` parameter to `buflen=` with the same value, and also used the `scrypt.hash()` method as you did, and that worked fine with the same given parameters... So this must be some issue with the `scrypt()` in `hashlib`. I guess this is a case where reading the docs made things *harder* for me, because searching the docs is how I even discovered the `hashlib` variant of `scrypt`. – the_endian Jun 15 '22 at 01:30
  • 1
    BTW, I like your idea of checking docs from *other languages* as a strategy for issues like this. – the_endian Jun 15 '22 at 01:35