the_endian
the_endian

Reputation: 2527

How can I fix scrypt's invalid parameter combination?

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.

Upvotes: 3

Views: 799

Answers (1)

Daniel Trugman
Daniel Trugman

Reputation: 8491

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.

Upvotes: 1

Related Questions