This application is a cross-platform tool to encrypt and decrypt data using the AES algorithm.
You can find information about this alogithm on the wikipedia page at https://en.wikipedia.org/wiki/Advanced_Encryption_Standard.
You have to download and install .NET Core 1.1+.
You can find all the necessary instructions on the .NET Core page at https://www.microsoft.com/net/core.
Once this is done, you first need to restore the packages. To do so, open a console in the root folder and type the following command:
dotnet restore
Then go to the Aes
directory, and build, typing the following commands:
cd Aes
dotnet build
Everything should be OK. If you encounter a problem, feel free to file an issue on this repository and I will answer as soon as I can.
Once this is done, go to the output directory in order to be able to execute the application with arguments:
cd bin/<configuration>/<runtime>
dotnet Aes.dll <parameters>
Where:
<configurartion>
is eitherDebug
orRelease
(or else), depending on what you chose to build.
It isDebug
by default, but you can choose to build a release version typing thedotnet build -c release
command.<runtime>
is the platform you randotnet build
on.<paramters>
are the options you provide to the application, and they are described in the next section.
Note: The casing you use for the words debug
and release
when building with th -c
option does not matter.
However, it determines the name (and thus casing) of the configuration directory when created the first time, so becareful on OS with case sensitive file system.
dotnet Aes.dll <options> <input-argument> [<output-argument>]
This option is mandatory, -e
is short for --encrypt
and -d
is short for --decrypt
.
Both encrypt and decrypt options are mutually exclusive.
To encrypt, either -e
or --encrypt
is necessary, but not both. Same with -d
or --decrypt
to decrypt data.
This option is optional and can take two values, file
or console
. It defaults to file
if not provided.
It indicates whether the input argument is a file path, with --input-type file
, or if it is a raw value, with --input-type console
.
This option is optional and can take three values, hex
, base64
(b64
for short) or raw
. It defaults to raw
if not provided.
It indicates whether the input data is either hexadecimal encoded, base64 encode, or not encoded at all, respectively.
If hex
is provided, the input data is considered as ASCII characters containing only characters in the range A
to F
, a
to f
and 0
to 9
.
In this format, two characters result in one decoded byte.
For exemple, the hexadecimal value 48656C6C6F20576F726C6421
results in the value Hello World!
.
If base64
(or b64
) is provided, the input data is considered as base64 to be decoded before being processed.
For exemple, the base64 value SGVsbG8gV29ybGQh
results in the value Hello World!
.
See the wikipedia page for more information about this encoding: https://en.wikipedia.org/wiki/Base64
If raw
is provided, the input data is not decoded and processed as is, using the console input encoding to transform it to raw bytes data.
This option is optional and can take two values, file
or console
. It defaults to file
if not provided.
It indicates whether the output argument is a file path, with --output-type file
, or if output data should be printed to the console, with --output-type console
.
When the output type is set to console
, the output argument is ignored.
This option is optional and can take three values, hex
, base64
(b64
for short) or raw
. It defaults to raw
if not provided.
It indicates whether the output data is written as hexadecimal encoded, base64 encoded, or not encoded at all, respectively.
If hex
is provided, the raw output data is encodced to ASCII characters containing only characters in the range a
to f
and 0
to 9
.
In this format, one byte is encoded to two ASCII characters.
For exemple, the value Hello World!
results in the hexadecimal value 48656C6C6F20576F726C6421
.
If base64
(or b64
) is provided, the raw output data is encoded to base64 before being written.
For exemple, the value Hello World!
results in the base64 value SGVsbG8gV29ybGQh
.
If raw
is provided, the output data is not encoded at all and written as is, using the console output encoding to transform bytes to a printable string.
This option is optional, and is used to provide encryption or decryption password.
If not provided, the application prompts for password to be typed directly in the console. When entered this way, the characters are hidden.
It is recommended to not use the --password <password>
option, or at least to not use it in scripts stored on disk.
This option is optional, it takes an integer value between 8 and 65'535. It defaults to 32
if not provided.
This is used only when encrypting, to generate a random salt of the provided amount of bytes.
When decrypting, the salt is read from the input data.
See this wikipedia page https://en.wikipedia.org/wiki/PBKDF2 or the RFC 2898 https://tools.ietf.org/html/rfc2898 for more informaton.
This option is optional, it takes an integer value greater than 0. It defaults to 2000
if not provided.
The minimum recommended value is 1000
.
See this wikipedia page https://en.wikipedia.org/wiki/PBKDF2 or the RFC 2898 https://tools.ietf.org/html/rfc2898 for more informaton.
This option is optional and can take three values, 128
, 192
or 256
. It defaults to 256
if not provided.
This sets the size of key blocks used by the AES algorithm.
See the wikipedia page of the AES algorithm for more information: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
This option is optional, it takes an integer value greater than or equal to 0. It defaults to 0
if not provided.
This is used to skip the first bytes of input data, in case there would be a header to discard before the payload.
This option is used only when decrypting and is ignored when encrypting.
This argument is mandatory.
It is either the path of the file containing the input data to encrypt or decrypt, or the input data provided through the command line.
(see --input-type
option for more information)
If file path is provided, it can be either absolute or not. If it is not absolute, it is relative to the current working directory.
The input file must exist or the process will fail.
This argument is mandatory when output type is set to file
, and should be ommited when output type is set to console
.
If a value is provided when in console
output type, the value is simply ignored.
(see --output-type
for more information)
When output is a file, this is the path of the file where to store the output data.
The path can be either absolute or not. If it is not absolute, it is relative to the current working directory.
The output file does not have to exist for the process to run. If the output file already exists, it is overwritten.
The padding used in tool is hard-coded to PKCS7
.
This sets the way data is padded when there is not enough to fulfill a cipher block.
For more informarion, see the wikipedia page of the AES algorithm https://en.wikipedia.org/wiki/Advanced_Encryption_Standard or the page about padding https://en.wikipedia.org/wiki/Padding_(cryptography) for more information.
The cipher mode used in this application is CBC
.
This sets the way an encrypted block impacts the next one, and so on.
See the wikipedia page of the cipher block modes for more information: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+---------+-----------------------+------------------+-----------...------------+-------------...---------------+
| 2 bytes | 16 bytes | 2 bytes | <salt-bytes-count> bytes | <remaining-bytes-count> bytes |
+---------+-----------------------+------------------+-----------...------------+-------------...---------------+
| version | initialization vector | salt bytes count | salt | encrypted data |
+---------+-----------------------+------------------+-----------...------------+-------------...---------------+
\ ^
---------------------/
The width of the cells on the table above are not at scale.
To summarize the format in a more verbose and textual manner, the file starts with 2 bytes of version, then 16 bytes of initialization vector (IV), then 2 bytes indicating the size of the next block of data, which is the amount of bytes of salt.
The version and the salt bytes count are always unsigned and written in network order. Then comes the salt itself, of length given by the 2 previous bytes, then comes the encrypted data, which runs until the end of the whole data.
The initialization vector (IV) is always 16 bytes long (128 bits) because it has to match the cipher block size, which is always 128 bits with the AES algorithm.
The salt is of variable length because it is up to the encrypter to set it.
Note: The AES alogithm allows 128, 192 or 256 bits (respectively 16, 24 or 32 bytes) of key block size, but has a fixed cipher block size of 128 bits (16 bytes).
There are only 4 unit tests that run the most basic scenarios.
Additionally, there is a testing application that tries all the possible combination of options, resulting in 194'400 tests.
Some situations are skipped because they do not make sense, such as displaying raw encrypted data to the console.
Test passed: 194400 / 194400 (100.00 %)
Effective tests: 135000 / 194400 (69.44 %)
Skipped tests: 59400 (30.56 %)
Note that the 135'000 tests reading and writing to disk may not be what you want to do, so in order to avoid stressing the disk, an in-memory fallback is implemented.
It is activated in debug build configurarion only, not in release. It is necessary to disable it to run the unit tests, so you have to manually get rid of the IN_MEMORY
conditional compilation symbol.