Metadata-Version: 2.4
Name: fernetfile
Version: 0.0.6
Summary: A python xxxFile like ( ie GzipFile, BZ2File, ...) for manipulating Fernet encrypted files.
Project-URL: HomePage, https://github.com/bibi21000/FernetFile
Project-URL: Issues, https://github.com/bibi21000/FernetFile/issues
Project-URL: Changelog, https://github.com/bibi21000/FernetFile/blob/master/CHANGELOG.md
Author-email: bibi21000 <bibi21000@gmail.com>
Maintainer-email: bibi21000 <bibi21000@gmail.com>
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Requires-Dist: cryptography>=43.0.0
Provides-Extra: benchmark
Requires-Dist: pytest-ordering; extra == 'benchmark'
Provides-Extra: build
Requires-Dist: build; extra == 'build'
Requires-Dist: twine; extra == 'build'
Provides-Extra: test
Requires-Dist: bandit; extra == 'test'
Requires-Dist: coverage[toml]; extra == 'test'
Requires-Dist: pytest; extra == 'test'
Requires-Dist: pytest-cov; extra == 'test'
Requires-Dist: ruff; extra == 'test'
Provides-Extra: zstd
Requires-Dist: pyzstd; extra == 'zstd'
Description-Content-Type: text/markdown

[![CircleCI](https://dl.circleci.com/status-badge/img/gh/bibi21000/FernetFile/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/bibi21000/FernetFile/tree/main)
[![codecov](https://codecov.io/gh/bibi21000/FernetFile/graph/badge.svg?token=4124GIOJAK)](https://codecov.io/gh/bibi21000/FernetFile)

# FernetFile

A python xxxFile like (ie GzipFile, BZ2File, ...) for encrypting files with Fernet.

 - encrypting / decrypting data using chunks to reduce memory footprint
 - chainable with other python xxxFile interfaces (stream mode)
 - look at BENCHMARK.md ... and chain :)
 - look at tests for examples


## Install

```
    pip install fernetfile
```

## Create your encryption key

```
    from cryptography.fernet import Fernet

    key = Fernet.generate_key()
```

and store it in a safe place (disk, database, ...).

This key is essential to encrypt and decrypt data.
Losing this key means losing the data.

## "open" your encrypted files like normal files

Text files :

```
    import fernetfile

    with fernetfile.open('test.txc', mode='wt', fernet_key=key, encoding="utf-8") as ff:
        ff.write(data)

    with fernetfile.open('test.txc', "rt", fernet_key=key, encoding="utf-8") as ff:
        data = ff.read()

    with fernetfile.open('test.txc', mode='wt', fernet_key=key, encoding="utf-8") as ff:
        ff.writelines(data)

    with fernetfile.open('test.txc', "rt", fernet_key=key, encoding="utf-8") as ff:
        data = ff.readlines()
```

Binary files :

```
    import fernetfile

    with fernetfile.open('test.dac', mode='wb', fernet_key=key) as ff:
        ff.write(data)

    with fernetfile.open('test.dac', "rb", fernet_key=key) as ff:
        data = ff.read()
```

## Use the FernetFile interface

```
    import fernetfile

    with fernetfile.FernetFile('test.dac', mode='wb', fernet_key=key) as ff:
        ff.write(data)

    with fernetfile.FernetFile('test.dac', mode='rb', fernet_key=key) as ff:
        data = ff.read()
```

## Chain it to bz2

```
    import fernetfile
    import bz2

    class Bz2FernetFile(bz2.BZ2File):

        def __init__(self, name, mode='r', fernet_key=None, chunk_size=fernetfile.CHUNK_SIZE, **kwargs):
            compresslevel = kwargs.pop('compresslevel', 9)
            self.fernet_file = fernetfile.FernetFile(name, mode,
                fernet_key=fernet_key, chunk_size=chunk_size, **kwargs)
            try:
                super().__init__(self.fernet_file, mode=mode,
                    compresslevel=compresslevel, **kwargs)
            except Exception:
                self.fernet_file.close()
                raise

        def close(self):
            try:
                super().close()
            finally:
                if self.fernet_file is not None:
                    self.fernet_file.close()


    with Bz2FernetFile('test.bzc', mode='wb', fernet_key=key) as ff:
        ff.write(data)

    with Bz2FernetFile('test.bzc', mode='rb', fernet_key=key) as ff:
        data = ff.read()
```

## Chain it to tar and pyzstd

```
    import fernetfile
    import pyzstd
    import tarfile

    class TarZstdFernetFile(tarfile.TarFile):

        def __init__(self, name, mode='r', fernet_key=None, chunk_size=fernetfile.CHUNK_SIZE, **kwargs):
            level_or_option = kwargs.pop('level_or_option', None)
            zstd_dict = kwargs.pop('zstd_dict', None)
            self.fernet_file = fernetfile.FernetFile(name, mode,
                fernet_key=fernet_key, chunk_size=chunk_size, **kwargs)
            try:
                self.zstd_file = pyzstd.ZstdFile(self.fernet_file, mode=mode,
                    level_or_option=level_or_option, zstd_dict=zstd_dict, **kwargs)
                try:
                    super().__init__(fileobj=self.zstd_file, mode=mode, **kwargs)

                except Exception:
                    self.zstd_file.close()
                    raise

            except Exception:
                self.fernet_file.close()
                raise

        def close(self):
            try:
                super().close()
            finally:
                try:
                    if self.zstd_file is not None:
                        self.zstd_file.close()
                finally:
                    if self.fernet_file is not None:
                        self.fernet_file.close()


    with TarZstdFernetFile('test.zsc', mode='wb', fernet_key=key) as ff:
        ff.add(dataf1, 'file1.out')
        ff.add(dataf2, 'file2.out')

    with TarZstdFernetFile('test.zsc', mode='rb', fernet_key=key) as ff:
        fdata1 = ff.extractfile('file1.out')
        fdata2 = ff.extractfile('file2.out')
```
