jihyocrypt 2.5.0.5
A simple implementation of Salsa20 along with a nonce+password based key and hash determination function.     Multiple exposed functions to ease encryption and decryption to a single key based program.

1. [Introduction](#intro)
1. [Installation](#install) 
2. [Options](#options) 
3. [All functions](#funcs) 
4. [Optional Arguments](#optargs)
5. [Passphrase](#passphrase) 
6. [Injecting Key Generation Parameters](#keyparams) 
7. [Structure of the Output](#output) 
8. [Speed Tests](#stests)
9. [Credits](#credits) 
10. [License](#license)
11. [Bugs](#bugs) 

## <a name='intro'>Jihyocrypt</a> 

A simple, standalone library-based implementation of [Salsa20 stream cipher](https://en.wikipedia.org/wiki/Salsa20) developed by [Daniel J Bernstein](https://en.wikipedia.org/wiki/Daniel_J._Bernstein)  to encrypt and decrypt data along with a password determined key function. 

Includes a multi-round nonce+password based key and hash function. The program is determined to be fairly cryptographically secure and is sufficiently fast for its implementation needs. 

It implements a memory buffer for file encryptions and decryptions to make sure the memory usage does not blow up. 

From the speed tests it can be asserted that it requires ~6s to encrypt 1GB of data. Which is fairly fast for most encryption/decryption operations required in Microservices and standalone applications(the main target for this). 

**A rust based library with equivalent API is soon underway.**


## <a name="install"> Installation</a> 

Installing from [PyPI](https://pypi.org) 

```python3 -m pip install jihyocrypt``` 

Or install from source 
```
git clone https://gitlab.com/loggerheads-with-binary/jihyocrypt
cd jihyocrypt 
python3 -m pip install -r requirements.txt 
python3 -m setup.py install 
```

## <a name="options"> Options</a> 


The following functions exposed for different purposes. The following mnemonic has been used for the encryption and decryption. 

Syntax for functions:: 
- syntax: enc/dec_x2x         
- where x: s -> Pickleable object; f-> File ; b -> bytestring
- enc/dec : _enc_ -> Encryption , dec -> Decryption
- example: enc_s2f means encrypt from a standard object to a file

Ex: 

```python 
import jihyocrypt as jihyo 
jihyo.enc_f2f( 'plaintext.txt' , output_file = 'ciphertext.bin' , password = passphrase)  
#Returns None, ciphertext.bin consists of encrypted data  
```


### <a name='optargs'>Optional Arguments</a> 

The exposed encryption and decryption functions consist of two flags:

| **Flag**       | **Default** | **If True**                                                                                                                                                | **If False**                                                                                                                                          |
|----------------|-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| `progress_bar` | False       | Prints a progress bar to stderr which can be used to monitor the  progress of encryption/decryption                                                        | No such bar  is produced                                                                                                                              |
| `redundant`    | True        | Encodes the hash of the nonce and  passphrase inside the output <br><br>  **Advisable to use since incorrect  decryption will be flagged by  the program** | The hash is not outputted. <br><br>  During decryption with incorrect passphrase, **the program will  generate incorrect output flagging  no error.** |

It also consists of an optional `buffer_size` argument in functions involving a file intermediary. The buffer size can be changed and is by default 3MB. A sweet spot for larger files with [respect to time](#b2bbufferstest) is ~100MB. The choice of time vs memory footprint is left to the developer


### <a name="passphrase">Passphrase</a> 

Passphrase can be a string/bytestring of any length. The phrase is put through a key generation function along with a nonce which generates a 256 bit key for the Salsa20 cipher and a 512 bit hash for the password verification. 

If the redundant flag is set to False, the hash is not generated at all while if it is True, the hash is encoded in the output. 

**Note: The `redundant` flag should be the same in both encryption and decryption. Otherwise it can lead to erroneous output/DecryptionError** 

## <a name='keyparams'>Injecting Key Generation Parameters </a>

The program uses two different integers, `DEFAULT_OBFUSCATION_ROUNDS` and `DEFAULT_JPIC_ROUNDS` (JPIC is the 512 bit hash and is short for Jihyocrypt Password Integrity Check)


| Flag                         | Default | Recommended | Purpose                                                                                   |
|------------------------------|---------|-------------|-------------------------------------------------------------------------------------------|
| `DEFAULT_OBFUSCATION_ROUNDS` | 6       | >4          | To create a suitable 256 bit key for Salsa20 cipher from provided  password and nonce     |
| `DEFAULT_JPIC_ROUNDS`        | 6       | >2          | To create a suitable 512 bit hash to verify validity of input password  during decryption |

The following parameters can be modified for modifying the key generation process. A higher value of the `DEFAULT_OBFUSCATION_ROUNDS` will increase security of the key generated from a simple passphrase, but [also increase the time needed](#roundsvstime). Similarly, a higher value of `DEFAULT_JPIC_ROUNDS` will reduce the chances of backtracking an encryption password but [require more time](#roundsvstimejpic). 

Injection can be done as follows:

```python
import jihyocrypt as jihyo 
jihyo.DEFAULT_OBFUSCATION_ROUNDS = N	#N>0, N : int 
jihyo.DEFAULT_JPIC_ROUNDS = M			#M>0, M : int

##Encryption/Decryption values must be same
```


**Note that if injection is used, injection values should be same for both encryption and decryption** 
 



## <a name='output'>Structure of The output</a> 

Let's assume the plaintext input is a bytestring of N bytes, the following is the output structure 

- If `redundant == True` <br><br>

Ciphertext Size = N + 72 <br><br>
<pre>
<span style="color:red">-------------------</span>|<span style="color:blue">---------------------------------</span>|<span style="color:green">---------------------------------</span><br>   
      <span style="color:red">8 (nonce)</span>                <span style="color:blue">64 (JPIC)</span>                 <span style="color:green">N (encrypted ciphertext)</span>  
</pre>
    
- If `redundant == False` <br><br>

Ciphertext Size = N + 8 <br><br>
<pre>
<span style="color:red">-------------------</span>|<span style="color:green">---------------------------------------------------</span><br>

<span style="color:red">8 (nonce)</span>                       <span style="color:green">N (encrypted ciphertext)</span>  
</pre>


## <a name='stests'>Speed Tests</a>: 

The following speed tests were performed using a [Google Colab Script](https://colab.research.google.com/drive/1VZvQOOouSZVCaLg3rpfb8zPeVb_BQm_d?usp=sharing) and Google Colab Resources. Your results may vary significantly. 

#### The following tests were performed 
- [Key Generation vs No of Rounds](#roundsvstime) 
- [JPIC Generation vs No of Rounds](#roundsvstimejpic) 
- [Plaintext Encryption and Decryption (b2b) vs size](#b2bstest) 
- [File to File Encryption and Decryption (f2f( vs size](#f2fstest) 
- [Plaintext Encryption (f2f) based on buffer size](#b2bbufferstest) 



#### <a name='roundsvstime'>Key Generation vs No of Rounds</a>:
<p align="center">
  <img src="https://gitlab.com/loggerheads-with-binary/jihyocrypt/-/raw/main/speedtests/keygen.png" alt="Keygen.png"  />
</p>

As expected, the behavior is fairly linear with O(N) complexity. 




#### <a name='roundsvstimejpic'>JPIC Generation vs No of Rounds</a>: 

<p align="center">
  <img src="https://gitlab.com/loggerheads-with-binary/jihyocrypt/-/raw/main/speedtests/jpic.png" alt="BufferTest.png"  />
</p>

This behavior is fairly linear as well, and is ~(O(N)<sub>```redundant=True```</sub> - O(N)<sub>```redundant=False```</sub>)

#### <a name='b2bstest'>Plaintext Encryption (b2b) vs size</a>

<p align="center">
  <img src="https://gitlab.com/loggerheads-with-binary/jihyocrypt/-/raw/main/speedtests/b2b.png" alt="BufferTest.png"  />
</p>

Standard linear behavior as expected. 

#### <a name='f2fstest'>File to File Encryption vs size</a>

<p align="center">
  <img src="https://gitlab.com/loggerheads-with-binary/jihyocrypt/-/raw/main/speedtests/f2f.png" alt="BufferTest.png"  />
</p>

Similar behavior with added overhead of File I/O. Takes about 12.8 secs to encrypt a 1 GB, this can vary with disk I/O speeds. 

#### <a name='b2bbufferstest'>Plaintext Encryption (f2f) based on buffer size </a>

<p align="center">
  <img src="https://gitlab.com/loggerheads-with-binary/jihyocrypt/-/raw/main/speedtests/buffers.png" alt="BufferTest.png"  />
</p>

A weird trend I noticed is the drop and then rise in time needed for encryption alongwith increase in buffer size. In my experience, a buffer of about 100MB can significantly reduce time. While above, it starts becoming slower. I don't have a possible explanation for this. 

Then why is the JihyoCrypt buffer defaulting to 3MB one could ask. It's because when I first developed it, memory was a larger concern than Speed. The program is regularly used to encrypt and decrypt large files(20 GB and above), and the time difference is not significant. However, having only ~6 MB of the memory being occupied vs ~100 MB of the memory being occupied for an hour or more is a choice left to the programmer. As mentioned before, one can easily change the buffer size since it is just a parameter of the encryption/decryption. 


## <a name='credits'> Credits: </a>

Credits for the following libraries used: [pycryptodome](https://github.com/Legrandin/pycryptodome/) 

And credits to  [Daniel J Bernstein](https://en.wikipedia.org/wiki/Daniel_J._Bernstein) for the [Salsa20 stream cipher](https://en.wikipedia.org/wiki/Salsa20) 
## <a name='license'>License</a> 

The library is provided with [MIT License](./LICENSE) 

## <a name='bugs'>Bugs</a> 

Any bugs or cryptographic improvements can be suggested to dev@aniruddh.ml