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