It's correct that nonces do not need to be secret.

Alex

On Sat, Jan 4, 2025 at 5:56 PM Sriram R <sramac...@gmail.com> wrote:
>
> Alex,
>
> Found the flaw in the code logic, it had to do with the location of the nonce 
> file. I was storing the nonce file in the same directory that was getting 
> encrypted/decrypted.
> Once I changed the nonce file location to be outside the target directory, 
> the encryption and decryption works flawlessly now.
>
> One last question, I read that the nonce file contents are not sensitive and 
> it doesn't need to be encrypted. Is that your understanding also?
>
> Best,
> PE
>
> On Sat, Jan 4, 2025 at 2:37 PM Sriram R <sramac...@gmail.com> wrote:
>>
>> Fair point, here's the entire code:
>>
>> import cryptography
>> from cryptography.fernet import Fernet
>> from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
>> import secrets, base64, getpass, os, os.path, argparse
>>
>> from cryptography.hazmat.primitives.ciphers.aead import AESGCMSIV
>>
>> from pathlib import Path
>>
>> from collections import defaultdict, OrderedDict
>>
>> aad = b"super secret authenticated but unencrypted data"
>>
>> rootpath = "/tmp/cstore/"
>> #rootpath = "/tmp/certstore/"
>> #dirs = ['certs/', 'csrs/', 'keys/']
>> dirs = ['csrs/']
>> files = []
>>
>> saltfile = Path.home().joinpath("salt.salt")
>> keyfile = Path.home().joinpath(".key")
>> saltsize = int(32)
>>
>> def lsdir(path):
>>   for nodes in Path(path).iterdir():
>>      if nodes.is_dir():
>>          #print("Dir is: ", nodes)
>>          lsdir(nodes)
>>      else:
>>          #print("File is: ", nodes)
>>          files.append(nodes)
>>
>> def gen_salt(size=32):
>>   return secrets.token_bytes(size)
>>
>> def der_key(salt, password):
>>   kdf = Scrypt(salt=salt, length=32, n=2**20, r=8, p=1)
>>   return kdf.derive(password.encode())
>>
>> def gen_symmkey(salt, password):
>>   symmkey = der_key(salt, password)
>>   return symmkey
>>   #return base64.urlsafe_b64encode(symmkey)
>>
>> def encrypt(nonce, fname, aesgcmsiv):
>>   with open(fname, "rb") as infile:
>>     file_data = infile.read()
>>
>>   # encrypt data
>>   edata = aesgcmsiv.encrypt(nonce, file_data, aad)
>>   with open(fname, "wb") as outfile:
>>      outfile.write(edata)
>>
>>
>> def decrypt(nonce, fname, aesgcmsiv):
>>   with open(fname, "rb") as infile:
>>     edata = infile.read()
>>
>>   try:
>>     data = aesgcmsiv.decrypt(nonce, edata, aad)
>>   except cryptography.exceptions.InvalidTag:
>>     print("something went wrong")
>>     return False
>>
>>   with open(fname, "wb") as outfile:
>>      outfile.write(data)
>>
>> def call_crypto(saltsize, password):
>>    if os.path.isfile(saltfile):
>>      salt = open(saltfile, 'rb').read()
>>    else:
>>      salt = gen_salt(saltsize)
>>      with open(saltfile, 'wb') as sfile:
>>         sfile.write(salt)
>>
>>    key = gen_symmkey(salt, password)
>>    aesgcmsiv = AESGCMSIV(key)
>>
>>     # dir walk and store files to process in a list
>>    lsdir(rootpath)
>>
>>    # encrypt/decrypt all elements in the 'files' list
>>    if ENC:
>>      for file in files:
>>        nonce = os.urandom(12)
>>        encrypt(nonce, file, aesgcmsiv)
>>        #nfile = Path.joinpath(file, ".nonce")
>>        nfile = "/tmp/cstore/csrs/eud1.csr.nonce"
>>        with open(nfile, 'wb') as f:
>>          f.write(nonce)
>>    else:
>>        for file in files:
>>          #nfile = Path.joinpath(file, ".nonce")
>>          nfile = "/tmp/cstore/csrs/eud1.csr.nonce"
>>          with open(nfile, 'rb') as f:
>>            nonce = f.read()
>>            decrypt(nonce, file, aesgcmsiv)
>>
>> if __name__ == "__main__":
>>
>>    password = some_password
>>
>>    ENC = False
>>    call_crypto(saltsize, password)
>>
>> Best,
>> PE
>>
>> On Sat, Jan 4, 2025 at 2:32 PM Sriram R <sramac...@gmail.com> wrote:
>>>
>>> Fair point, here's the entire code:
>>>
>>> [certadmin@r9kc01-silver ~]$ !source
>>> source ./testvenv/bin/activate
>>> (testvenv) [certadmin@r9kc01-silver ~]$ vi ./gcm.py
>>> (testvenv) [certadmin@r9kc01-silver ~]$ cat !$
>>> cat ./gcm.py
>>> import cryptography
>>> from cryptography.fernet import Fernet
>>> from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
>>> import secrets, base64, getpass, os, os.path, argparse
>>>
>>> from cryptography.hazmat.primitives.ciphers.aead import AESGCMSIV
>>>
>>> from pathlib import Path
>>>
>>> from collections import defaultdict, OrderedDict
>>>
>>> aad = b"super secret authenticated but unencrypted data"
>>>
>>> rootpath = "/tmp/cstore/"
>>> #rootpath = "/tmp/certstore/"
>>> #dirs = ['certs/', 'csrs/', 'keys/']
>>> dirs = ['csrs/']
>>> files = []
>>>
>>> saltfile = Path.home().joinpath("salt.salt")
>>> keyfile = Path.home().joinpath(".key")
>>> saltsize = int(32)
>>>
>>> def lsdir(path):
>>>   for nodes in Path(path).iterdir():
>>>      if nodes.is_dir():
>>>          #print("Dir is: ", nodes)
>>>          lsdir(nodes)
>>>      else:
>>>          #print("File is: ", nodes)
>>>          files.append(nodes)
>>>
>>> def gen_salt(size=32):
>>>   return secrets.token_bytes(size)
>>>
>>> def der_key(salt, password):
>>>   kdf = Scrypt(salt=salt, length=32, n=2**20, r=8, p=1)
>>>   return kdf.derive(password.encode())
>>>
>>> def gen_symmkey(salt, password):
>>>   symmkey = der_key(salt, password)
>>>   return symmkey
>>>   #return base64.urlsafe_b64encode(symmkey)
>>>
>>> def encrypt(nonce, fname, aesgcmsiv):
>>>   with open(fname, "rb") as infile:
>>>     file_data = infile.read()
>>>
>>>   # encrypt data
>>>   edata = aesgcmsiv.encrypt(nonce, file_data, aad)
>>>   with open(fname, "wb") as outfile:
>>>      outfile.write(edata)
>>>
>>>
>>> def decrypt(nonce, fname, aesgcmsiv):
>>>   with open(fname, "rb") as infile:
>>>     edata = infile.read()
>>>
>>>   try:
>>>     data = aesgcmsiv.decrypt(nonce, edata, aad)
>>>   except cryptography.exceptions.InvalidTag:
>>>     print("something went wrong")
>>>     return False
>>>
>>>   with open(fname, "wb") as outfile:
>>>      outfile.write(data)
>>>
>>> def call_crypto(saltsize, password):
>>>    if os.path.isfile(saltfile):
>>>      salt = open(saltfile, 'rb').read()
>>>    else:
>>>      salt = gen_salt(saltsize)
>>>      with open(saltfile, 'wb') as sfile:
>>>         sfile.write(salt)
>>>
>>>    key = gen_symmkey(salt, password)
>>>    aesgcmsiv = AESGCMSIV(key)
>>>
>>>    #print('key is: ', key)
>>>
>>>    # dir walk and store files to process in a list
>>>    lsdir(rootpath)
>>>
>>>    # encrypt/decrypt all elements in the 'files' list
>>>    if ENC:
>>>      for file in files:
>>>        nonce = os.urandom(12)
>>>        encrypt(nonce, file, aesgcmsiv)
>>>        nfile = "/tmp/cstore/csrs/eud1.red.ftf.net.csr.nonce"
>>>        with open(nfile, 'wb') as f:
>>>          f.write(nonce)
>>>    else:
>>>        for file in files:
>>>          #nfile = Path.joinpath(file, ".nonce")
>>>          nfile = "/tmp/cstore/csrs/eud1.red.ftf.net.csr.nonce"
>>>          with open(nfile, 'rb') as f:
>>>            nonce = f.read()
>>>            decrypt(nonce, file, aesgcmsiv)
>>>
>>> if __name__ == "__main__":
>>>
>>>    password = some_password
>>>
>>>    ENC = False
>>>    call_crypto(saltsize, password)
>>>
>>>
>>> Best,
>>> PE
>>>
>>> On Sat, Jan 4, 2025 at 1:25 PM Alex Gaynor <alex.gay...@gmail.com> wrote:
>>>>
>>>> It's impossible to say without seeing your code. If
>>>> encrypt()/decrypt() isn't working, then some of your parameters don't
>>>> match between them.
>>>>
>>>> Alex
>>>>
>>>> On Sat, Jan 4, 2025 at 4:11 PM Sriram R <sramac...@gmail.com> wrote:
>>>> >
>>>> > Also, I'm saving and reading the ct and the nonce values in binary mode 
>>>> > during encryption and decryption. Not sure if that needs to be done 
>>>> > using a codec if the read/write operation is damaging the values.
>>>> >
>>>> > Appreciate taking the time to respond to this issue.
>>>> >
>>>> >
>>>> > On Sat, Jan 4, 2025, 12:52 PM Sriram R <sramac...@gmail.com> wrote:
>>>> >>
>>>> >> Yes, all the other kdf parameters are the same. Like the salt size, n, 
>>>> >> p, r values etc.
>>>> >> Any ideas as to why the decryption fails?
>>>> >> I'm passing the same nonce to the .decrypt() method.
>>>> >>
>>>> >>
>>>> >> On Sat, Jan 4, 2025, 12:46 PM Alex Gaynor <alex.gay...@gmail.com> wrote:
>>>> >>>
>>>> >>> Assuming the salt, and all other parameters passed to the KDF are the
>>>> >>> same, then yes.
>>>> >>>
>>>> >>> On Sat, Jan 4, 2025 at 3:34 PM Sriram R <sramac...@gmail.com> wrote:
>>>> >>> >
>>>> >>> > Appreciate the symmetric key explanation. If the key is derived 
>>>> >>> > using a password based kdf during encryption, isn't it reasonable to 
>>>> >>> > expect the same key would be generated by the kdf during decryption 
>>>> >>> > phase if the password
>>>> >>> > is also the same?
>>>> >>> >
>>>> >>> > PE
>>>> >>> >
>>>> >>> >
>>>> >>> > On Sat, Jan 4, 2025, 9:30 AM Alex Gaynor <alex.gay...@gmail.com> 
>>>> >>> > wrote:
>>>> >>> >>
>>>> >>> >> Based on "it creates a new key from the password and is used for
>>>> >>> >> decryption" it sounds like you're using different keys for 
>>>> >>> >> encryption
>>>> >>> >> and decryption.
>>>> >>> >>
>>>> >>> >> You need to use the same key to decrypt a value as you used to 
>>>> >>> >> encrypt
>>>> >>> >> it -- this is the point of symmetric encryption! It wouldn't make 
>>>> >>> >> much
>>>> >>> >> sense if you could decrypt a value without possessing the key.
>>>> >>> >>
>>>> >>> >> How exactly you manage your keys depends a lot on your application,
>>>> >>> >> deployment environment, and threat model, so I can't really provide
>>>> >>> >> any general purpose advice. You generally should not store the key
>>>> >>> >> with the ciphretext, as there's basically no threat model that makes
>>>> >>> >> sense under.
>>>> >>> >>
>>>> >>> >> Alex
>>>> >>> >>
>>>> >>> >> On Sat, Jan 4, 2025 at 12:28 PM Sriram R via Cryptography-dev
>>>> >>> >> <cryptography-dev@python.org> wrote:
>>>> >>> >> >
>>>> >>> >> > Hello,
>>>> >>> >> >
>>>> >>> >> > I'm reaching out to the cryptography experts for this issue.
>>>> >>> >> >
>>>> >>> >> > Using Python 3.9 on a RHEL 9.4 platform with the cryptography 
>>>> >>> >> > module installed on it.
>>>> >>> >> > The requirements are to encrypt some data using 
>>>> >>> >> > classcryptography.hazmat.primitives.ciphers.aead.AESGCM(key)
>>>> >>> >> >
>>>> >>> >> > I've written the encryption code to use the above cipher and it 
>>>> >>> >> > works without any issues.
>>>> >>> >> >
>>>> >>> >> > The issue I'm running into is in the decryption step. The same 
>>>> >>> >> > Python code is called with the flag set to decryption but it 
>>>> >>> >> > raises the InvalidToken exception.
>>>> >>> >> > Reading the documentation it says this could occur if the 
>>>> >>> >> > ciphertext is changed, which it is not. Or if the nonce is 
>>>> >>> >> > different, I adjusted the code to save each nonce during the 
>>>> >>> >> > encryption process and I supply this same nonce during decryption.
>>>> >>> >> > The documentation also says this exception can occur if the key 
>>>> >>> >> > is different.
>>>> >>> >> >
>>>> >>> >> > This is where my confusion is, the .decrypt() only takes in the 
>>>> >>> >> > ct, nonce and aad parameters.
>>>> >>> >> > Obviously when the Python module is run for decryption, it 
>>>> >>> >> > creates a new key from the password and is used for decryption. 
>>>> >>> >> > Which will be different than what was used during encryption.
>>>> >>> >> > I don't think the solution is to also store the key along with 
>>>> >>> >> > the nonce, since it's a very bad security practice.
>>>> >>> >> >
>>>> >>> >> > What am I missing?
>>>> >>> >> > Do I need to parse the ciphertext and extract the tag or 
>>>> >>> >> > something? But then .decrypt() method doesn't use a tag parameter.
>>>> >>> >> >
>>>> >>> >> > FYI, I'm saving the ciphertext and the nonce with the 'wb' flag 
>>>> >>> >> > in the open() statement and the decryption step uses the 'rb' 
>>>> >>> >> > flag to read in the ciphertext and the nonce.
>>>> >>> >> >
>>>> >>> >> > The example code snippet in the cryptography website shows the 
>>>> >>> >> > encrypt and decrypt operation in sequence.
>>>> >>> >> > In my situation, encryption happens at one time and the 
>>>> >>> >> > decryption has to happen some time later.
>>>> >>> >> >
>>>> >>> >> > Several websites/users are using the Pycryptodome module for this.
>>>> >>> >> > I'd rather not because the cryptography module works great for my 
>>>> >>> >> > other requirements.
>>>> >>> >> > Also I'd prefer not to use cryptography module and the 
>>>> >>> >> > Pycryptodome module as well. I think that would be overkill.
>>>> >>> >> >
>>>> >>> >> > Best
>>>> >>> >> > PE
>>>> >>> >> > _______________________________________________
>>>> >>> >> > Cryptography-dev mailing list
>>>> >>> >> > Cryptography-dev@python.org
>>>> >>> >> > https://mail.python.org/mailman/listinfo/cryptography-dev
>>>> >>> >>
>>>> >>> >>
>>>> >>> >>
>>>> >>> >> --
>>>> >>> >> All that is necessary for evil to succeed is for good people to do 
>>>> >>> >> nothing.
>>>> >>>
>>>> >>>
>>>> >>>
>>>> >>> --
>>>> >>> All that is necessary for evil to succeed is for good people to do 
>>>> >>> nothing.
>>>>
>>>>
>>>>
>>>> --
>>>> All that is necessary for evil to succeed is for good people to do nothing.



-- 
All that is necessary for evil to succeed is for good people to do nothing.
_______________________________________________
Cryptography-dev mailing list
Cryptography-dev@python.org
https://mail.python.org/mailman/listinfo/cryptography-dev

Reply via email to