Thanks for the clarification. On Sat, Jan 4, 2025 at 2:59 PM Alex Gaynor <alex.gay...@gmail.com> wrote:
> 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