# How to combine pepper in random string generation

I am writing a key generator program.

Indeed it generates a key in the form of a random text string (not binary), with its characters chosen randomly from a set of defined characters, with a specified length.

You can see an example of my program's config.ini file for clarification:

[general]
key_chars = abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789
key_len = 22
pepper = G89ne_^DFIM"]12@\z2's#GBA


Ignoring the pepper part, we can generate the desired key with a code like this (code is in Python language):

Warning: The following code is not secure! *

def generate(self, keyLen, keyChars):
key=''
for i in range(keyLen):
randIndex=randint(0, len(keyChars))
key+=keyChars[randIndex]
return key


But I don't want my keys' security rely solely on the OS/Language (presumably) secure random generator (In this case, pycrypto module's CSPRNG that probably itself uses OS' CSPRNG under the hood), so I want to engage the pepper in the procedure too, but I am not sure about the proper way to do that securely. Please tell me how to combine the pepper in the above code (Or maybe you suggest a different method for doing that which is better suited to such purpose).

*: I used randint in my example code because I thought it is part of pycrypto module, then I realized that is not the case!

• Randint doesn't give you CSPRNG. Pepper is pointless in CSPRNG's, it's used in PKDF (for some rare cases). Apr 11 '17 at 10:29
• @axapaxa I use randint from pycrypto. Isn't it cryptographically strong? Apr 11 '17 at 12:18

First of all, you should just trust the random source that the OS provides and call it via os.urandom(n). Not only is it generally safe, but if your OS is unsafe you may have bigger problems on your hands.

If you really do want to use your pepper as an input to the random bytes that are generated, the obvious way would be to encrypt the OS's stream using a block cipher. You then use the ciphertext as your new random source.

• finally I used HKDF for that purpose, giving OS' stream as IKM (Input key material) and my pepper as Salt to it. Is that ok? Apr 11 '17 at 19:08
• Yes. This method is also ok. Apr 11 '17 at 19:27
• But again, there's not really a point. Just use urandom and encode it according to your mapping algorithm. If you can't trust urandom, you can't trust the entire computer you're running on. Apr 12 '17 at 0:17
• @stephen-touset I do not think it is a bad idea. E.g. in the case of a bug in the library/OS or lack of enough randomness in the environment for some (possibly unexpected/hidden) reason, our custom source of randomness (pepper), although fixed/ad-hoc, can save us from disaster. I think implementing it is not so hard and costly; So why not? Apr 12 '17 at 6:47
• This also depends on the platform, of course. Especially on more obscure or embedded platforms I agree that this is probably a good idea. Apr 13 '17 at 8:46

I would use the XOR-NRBG construct from NIST SP800-90c Second Draft with a DRBG based on your pepper and the system Random source as the NRBG entropy source.

The method is described in section 9.3 however it is a good idea to read the whole series.

Finally I devised this procedure to create custom key strings from random bytes generated by pycrypto CSPRNG:

Variables:

keyChars: a string of all possible characters in the output key
numChars: number of characters that key characters can be chosen from (Simply keyChars length!)
keyStrength: key strength requested (e.g. 128 bit)

We compute the required output key length with this:

numRequiredKeyChars=ceil((keyStrength*log(2))/log(numChars))


We intend to generate a bunch of bytes and then map each byte to an index into keyChars string/array. We need to discard possible unfair bytes. so I wrote this function for determining a byte fairness:

def isFair(rByte):
fullSetsOfValues=floor(255/numChars)
return rByte < (numChars * fullSetsOfValues)


Then generate enough fair random bytes with this:

def generateFairBytes():
fairBytes=b''
loop=True
while loop:
bytes=myGetRandomBytes(16)
for i in range(len(bytes)):
if isFair(bytes[i]):
fairBytes+=bytes[i:i+1]
if len(fairBytes)==numRequiredKeyChars:
loop=False
break
return fairBytes


Then generate the final custom string key with this:

key=''
for i in generateFairBytes(): key+=keyChars[i%numChars]


And about myGetRandomBytes: it is a function that combines my Pepper with OS/Python CSPRNG generated bytes:

def myGetRandomBytes(n):