# Introduction

Kryptor is a simple, modern, and secure file encryption and signing tool for Windows, Linux, and macOS.

It aims to be a better version of [age](https://github.com/FiloSottile/age) and [Minisign](https://jedisct1.github.io/minisign/) to provide a leaner, user friendly alternative to [GPG](https://gnupg.org/).

<figure><img src="/files/2SZOrr4o2Q0nuLR14iqK" alt=""><figcaption></figcaption></figure>

## Download Kryptor

### Pre-built binaries

* [Windows (x64)](https://github.com/samuel-lucas6/Kryptor/releases/latest/download/kryptor-windows-x64.zip) ([Kryptor signature](https://github.com/samuel-lucas6/Kryptor/releases/latest/download/kryptor-windows-x64.zip.signature)) ([SHA-256](https://github.com/samuel-lucas6/Kryptor/releases/latest/download/kryptor-windows-x64.zip.digest))
* [Linux (x64)](https://github.com/samuel-lucas6/Kryptor/releases/latest/download/kryptor-linux-x64.zip) ([Kryptor signature](https://github.com/samuel-lucas6/Kryptor/releases/latest/download/kryptor-linux-x64.zip.signature)) ([SHA-256](https://github.com/samuel-lucas6/Kryptor/releases/latest/download/kryptor-linux-x64.zip.digest))
* [macOS (x64)](https://github.com/samuel-lucas6/Kryptor/releases/latest/download/kryptor-macos-x64.zip) ([Kryptor signature](https://github.com/samuel-lucas6/Kryptor/releases/latest/download/kryptor-macos-x64.zip.signature)) ([SHA-256](https://github.com/samuel-lucas6/Kryptor/releases/latest/download/kryptor-macos-x64.zip.digest))
* [Linux (ARM64)](https://github.com/samuel-lucas6/Kryptor/releases/latest/download/kryptor-linux-arm64.zip) ([Kryptor signature](https://github.com/samuel-lucas6/Kryptor/releases/latest/download/kryptor-linux-arm64.zip.signature)) ([SHA-256](https://github.com/samuel-lucas6/Kryptor/releases/latest/download/kryptor-linux-arm64.zip.digest))
* [macOS (ARM64)](https://github.com/samuel-lucas6/Kryptor/releases/latest/download/kryptor-macos-arm64.zip) ([Kryptor signature](https://github.com/samuel-lucas6/Kryptor/releases/latest/download/kryptor-macos-arm64.zip.signature)) ([SHA-256](https://github.com/samuel-lucas6/Kryptor/releases/latest/download/kryptor-macos-arm64.zip.digest))

For instructions on verifying the signatures and digests, please see the [Installation](/installation#verifying-signatures) page.

{% hint style="success" %}
Please follow the project on [GitHub](https://github.com/samuel-lucas6/Kryptor) to stay up to date.
{% endhint %}

{% hint style="warning" %}
**For macOS (x64/ARM64)**, there is currently an [upstream issue](https://github.com/dotnet/sdk/issues/45903) in the .NET SDK that prevents the libsodium library from being bundled correctly into the Kryptor executable. The .NET team has not investigated this for over a year, so this is unlikely to be fixed.

Until this is resolved, you must keep the `libsodium.dylib` file (within the download ZIP) in the same directory as the `kryptor` file.
{% endhint %}

### Package managers

<table data-header-hidden><thead><tr><th>Software</th><th>Command</th><th data-hidden></th></tr></thead><tbody><tr><td><a href="https://community.chocolatey.org/packages/kryptor">Chocolatey</a> (Windows)</td><td><code>choco install kryptor</code></td><td></td></tr><tr><td><a href="https://scoop.sh/">Scoop</a> (Windows)</td><td><code>scoop bucket add extras; scoop install kryptor</code></td><td></td></tr><tr><td><a href="https://brew.sh/">Homebrew</a> (macOS)</td><td>Help wanted!</td><td></td></tr><tr><td><a href="https://aur.archlinux.org/packages?O=0&#x26;SeB=nd&#x26;K=kryptor&#x26;outdated=&#x26;SB=p&#x26;SO=d&#x26;PP=50&#x26;submit=Go">AUR</a> (Arch Linux)</td><td><code>yay -S kryptor</code> (from source) or <code>yay -S kryptor-bin</code> (published binary)</td><td></td></tr><tr><td><a href="https://search.nixos.org/packages?channel=unstable&#x26;show=kryptor&#x26;from=0&#x26;size=50&#x26;sort=relevance&#x26;type=packages&#x26;query=kryptor">NixOS</a></td><td><code>nix-shell -p kryptor</code></td><td></td></tr></tbody></table>

{% hint style="success" %}
Up for maintaining a package? [Let's talk](#contact)!
{% endhint %}

## Source code

You can find the source code on [GitHub](https://github.com/samuel-lucas6/Kryptor).

## License

Kryptor is licensed under [GPLv3](https://github.com/samuel-lucas6/Kryptor/blob/master/LICENSE).

## Contact

If you'd like to report a bug, provide feedback, ask a question, or need technical support, [GitHub](https://github.com/samuel-lucas6/Kryptor/issues/new/choose) is the place to go.

To report a vulnerability, please see the [SECURITY.md](https://github.com/samuel-lucas6/Kryptor/blob/master/SECURITY.md) file.

For other enquiries, please email me at `samuel at samuellucas dot com`.

## Donate

If you've found the software useful or just approve of the design and goals, please consider donating. As a student, every little helps, and I will be eternally grateful.

* PayPal: [samuellucas6](https://www.paypal.com/paypalme/samuellucas6)
* Monero: `46hQy5JebdE5L3XsCAwh9tQ6zVA4631JQQb5f9mFJWQ99XaH1SUs7CDPq5QPnKq74rbzGZPxFQD9K45UYxq211V8C2F5iVD`

## Goals

Kryptor aims to be better than a combination of [age](https://github.com/FiloSottile/age) and [Minisign](https://jedisct1.github.io/minisign/) in terms of security and usability. The use case is to protect files for backups and sharing, with a focus on limiting metadata.

It's not attempting to be a complete replacement of GPG. That would lead to similar problems, such as considerable complexity and too much code to single-handedly audit.

### Simple

* No [cryptographic agility](https://www.imperialviolet.org/2016/05/16/agility.html)/config options.
* A [limited](/usage) number of command-line options.
* Allow some options to be skipped to shorten commands.
* Encryption and signing support to avoid having to use different tools.
* Support for passphrases, symmetric keys, and asymmetric keys.
* Encryption of multiple files and directories to avoid having to use different tools.
* Optional file name encryption and overwriting of input files.
* Short public keys that can be copied and pasted or shared as files.
* Generate key pairs without having to use a separate keygen program.

### Secure

* The [latest and greatest](/features#security) cryptographic algorithms.
* Rely on a [widely used](https://doc.libsodium.org/libsodium_users), [fast](https://monocypher.org/speed), and [audited](https://www.privateinternetaccess.com/blog/libsodium-v1-0-12-and-v1-0-13-security-assessment/) cryptographic library.
* [Post-quantum secure](https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/Brochure/quantum-safe-cryptography.html?nn=433196) symmetric cryptography.
* [Pre-shared key](https://www.wireguard.com/protocol/) support (or [keep your public key secret](https://crypto.stackexchange.com/q/119226/102678)) for [post-quantum secure](https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/Brochure/quantum-safe-cryptography.html?nn=433196) key exchange, unlike older versions of [age](https://github.com/FiloSottile/age/discussions/231) ([v1.3.0](https://github.com/FiloSottile/age/releases/tag/v1.3.0) onwards has hybrid ML-KEM-768).
* AEAD [stream encryption](https://www.imperialviolet.org/2014/06/27/streamingencryption.html) with [key commitment](https://eprint.iacr.org/2022/268), unlike [age](https://github.com/FiloSottile/age/commit/2194f6962c8bb3bca8a55f313d5b9302596b593b).
* [Sender authenticated](https://neilmadden.blog/2018/11/26/public-key-authenticated-encryption-and-why-you-want-it-part-ii/) key exchange for hybrid file encryption (with some [caveats](/security-limitations#multi-recipient-sender-authentication)), unlike [age](https://github.com/FiloSottile/age/discussions/229) (without [secret public keys](https://words.filippo.io/age-authentication/)).
* Private key encryption for protection at rest, unlike [age](https://github.com/FiloSottile/age/discussions/252) [by default](https://github.com/FiloSottile/age?tab=readme-ov-file#passphrase-protected-key-files).
* Sign the whole signature file and support [non-prehashed](https://cryptologie.net/article/497/eddsa-ed25519-ed25519-ietf-ed25519ph-ed25519ctx-hasheddsa-pureeddsa-wtf/) signatures, unlike [Minisign](https://github.com/jedisct1/minisign/issues/104).
* Make encrypted files completely [indistinguishable from random](https://petsymposium.org/2019/files/papers/issue4/popets-2019-0056.pdf) to limit metadata, unlike [age](https://github.com/C2SP/C2SP/blob/main/age.md).

### Documented

* The [protocol](/specification) is explained in sufficient detail to not have to read much code.
* Readable [code](https://github.com/samuel-lucas6/Kryptor/tree/master/src).
* It's [clear](https://github.com/samuel-lucas6/Kryptor/blob/master/SECURITY.md) how to report security vulnerabilities.
* A thorough [tutorial](/tutorial) to help newcomers.
* There's a [roadmap](/roadmap) and [changelog](/changelog).
* [Commits](https://github.com/samuel-lucas6/Kryptor/commits/master) have decent naming to track individual changes.

### Out of scope

* Backwards compatibility (for [major versions](https://semver.org/)): Kryptor uses an indistinguishable from random file format for encryption, meaning there's no way to tell if a file was encrypted by Kryptor or which version was used from looking at the contents. Furthermore, almost every protocol has flaws/limitations that cannot be corrected without breaking changes. A good example is adding support for post-quantum algorithms, which are still being researched/standardised.
* Compatibility with other tools and protocols: you cannot be better by being the same, and compatibility on top of something new just increases complexity. There are limitations with other programs that need to be addressed, which requires breaking changes, such as the lack of [authenticated key exchange](https://github.com/FiloSottile/age/discussions/229) and [key commitment](https://github.com/FiloSottile/age/commit/2194f6962c8bb3bca8a55f313d5b9302596b593b) in [age](https://github.com/FiloSottile/age).
* Key distribution: just share your public keys on a personal website or social media account. If you only use them to communicate with friends or family members, you can share them via a messaging app like [Signal](https://www.signal.org/).
* Disk encryption: this is completely different to file encryption. It's best left to [another](https://gitlab.com/cryptsetup/cryptsetup/) tool.
* Anything related to email: although you can, of course, attach encrypted files and signature files to emails.
* A GUI and mobile apps: a lot easier said than done. I simply don't have the time.
* A [password store](https://www.passwordstore.org/): there are [numerous problems](https://rot256.dev/post/pass/) with this approach. Just use a GUI password manager like [Bitwarden](https://bitwarden.com/) or [KeePassXC](https://keepassxc.org/).
* Things few people *currently* use: for example, [YubiKeys](https://en.wikipedia.org/wiki/YubiKey). It might be a '*cool*' feature to have, but it's useless for the majority of people.

## Acknowledgements

Huge thanks to everyone in this section!

### Contributors

* Everyone who has reported bugs and provided feedback on the design, code quality, UX, and documentation.
* [replydev](https://github.com/replydev) for making and maintaining [AUR](https://aur.archlinux.org/packages?O=0\&SeB=nd\&K=kryptor\&outdated=\&SB=p\&SO=d\&PP=50\&submit=Go) packages.
* [arthsmn](https://github.com/arthsmn) for making and maintaining a [NixOS](https://search.nixos.org/packages?channel=unstable\&show=kryptor\&from=0\&size=50\&sort=relevance\&type=packages\&query=kryptor) package.

### Dependencies

* Frank Denis for writing the [libsodium](https://doc.libsodium.org/) library.
* Loup Vaillant for writing the [Monocypher](https://monocypher.org/) library.
* Nate McMaster for working on the [CommandLineUtils](https://github.com/natemcmaster/CommandLineUtils) library.
* Alexandre Mutel for creating the [Monocypher.NET](https://github.com/xoofx/Monocypher.NET) library.
* ​Klaus Hartke, Trond Arne Bråthen, Adam Caudill, and everyone who contributed to the libsodium-net and [libsodium-core](https://github.com/tabrath/libsodium-core/) libraries.

### Cryptographers

* Daniel J. Bernstein for designing [ChaCha20](https://cr.yp.to/chacha.html), [Poly1305](https://cr.yp.to/mac.html), and [Curve25519](https://cr.yp.to/ecdh.html).
* Daniel J. Bernstein, Niels Duif, Tanja Lange, Peter Schwabe, and Bo-Yin Yang for designing [Ed25519](https://ed25519.cr.yp.to/).
* Daniel J. Bernstein, Mike Hamburg, Anna Krasnova, and Tanja Lange for designing [Elligator](https://elligator.cr.yp.to/).
* Alex Biryukov, Daniel Dinu, and Dmitry Khovratovich for designing [Argon2](https://github.com/P-H-C/phc-winner-argon2).
* Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn, and Christian Winnerlein for designing [BLAKE2](https://www.blake2.net/).

### Organisations

* [GitBook](https://www.gitbook.com/) for their free open source plan.
* [Tuta](https://tuta.com/) for donating their private email service.
* The [EFF](https://www.eff.org/) for making their [wordlists for random passphrases](https://www.eff.org/deeplinks/2016/07/new-wordlists-random-passphrases).


# Features

## Security

* [ChaCha20-Poly1305](https://www.rfc-editor.org/rfc/rfc8439) for authenticated encryption, with the [padding fix](https://github.com/samuel-lucas6/kcChaCha20-Poly1305) for key commitment.
* [Argon2id](https://www.rfc-editor.org/rfc/rfc9106.html) for password-based key derivation.
* [BLAKE2b](https://www.rfc-editor.org/rfc/rfc7693) for hashing, keyed hashing, and key derivation.
* [X25519](https://www.rfc-editor.org/rfc/rfc7748) for key exchange.
* [Elligator 2](https://elligator.cr.yp.to/) for hiding ephemeral public keys.
* [Ed25519](https://www.rfc-editor.org/rfc/rfc8032) for digital signatures.

The [libsodium](https://doc.libsodium.org/) library is used for everything but Elligator2, which is from [Monocypher](https://monocypher.org/).

## Functionality

* Encrypt multiple files/directories with a passphrase, symmetric key, or asymmetric keys.
* Encrypt to multiple recipients for sender authenticated, one-way file sharing.
* Encrypted files are indistinguishable from random. File names can also be encrypted.
* Create and verify digital signatures, with support for an authenticated comment and prehashing.
* Small public keys. Private keys are encrypted for protection at rest.
* UNIX style passphrase entry and random passphrase generation.
* Pre-shared keys can be used for post-quantum secure key exchange.

## Privacy

* Kryptor runs locally on your computer. No personal data is ever collected or sent to anyone.​
* Kryptor runs offline unless you run the `-u|--update` command, which downloads [this](https://github.com/samuel-lucas6/Kryptor/blob/master/version.txt) text file and [this](https://github.com/samuel-lucas6/Kryptor/blob/master/version.txt.signature) signature file from GitHub. If you choose to let Kryptor install an update for you when prompted, the [latest release](https://github.com/samuel-lucas6/Kryptor/releases/latest) for your operating system and the associated signature file will also be downloaded from GitHub. You can read GitHub's Privacy Statement [here](https://docs.github.com/en/github/site-policy/github-privacy-statement).

{% hint style="info" %}
As of [v4.1.0](https://www.kryptor.co.uk/pages/-MUD6G-JBYqklHgrh5kv#v4.1.0), the`-u|--update` option has been removed to be consistent with other command line tools. A package manager should be used instead if this is a concern.
{% endhint %}


# FAQ

## General

<details>

<summary>Is Kryptor compatible with age and Minisign?</summary>

No, it uses newer cryptographic primitives and different file formats to address [problems](/#goals) with both tools. However, Kryptor isn't perfect either.

</details>

<details>

<summary>Why should I use Kryptor over the alternatives?</summary>

Three words: security, privacy, and usability.

1. Kryptor uses [strong, fast, and modern](/features#security) cryptographic algorithms, offering [post-quantum security](https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/Brochure/quantum-safe-cryptography.html?nn=433196). It also addresses security [limitations](/#goals) of tools like [age](https://github.com/FiloSottile/age) and [Minisign](https://jedisct1.github.io/minisign/).
2. Unlike most tools, Kryptor limits metadata by using an indistinguishable from random encrypted file format. Encrypted files have no identifiable headers and are randomly padded. File names can also be encrypted.
3. Encrypt/sign multiple files and directories at once. No need to specify an output file path. No config options. Short public keys. Fewer command-line options than [Minisign](https://jedisct1.github.io/minisign/) whilst also supporting encryption.

</details>

<details>

<summary>When should I not use Kryptor?</summary>

* If you want to frequently access lots of your encrypted files (e.g. multiple large directories), you should use [Cryptomator](https://cryptomator.org/) or [VeraCrypt](https://www.veracrypt.fr/).​​
* If you want to encrypt a disk, you should use [VeraCrypt](https://www.veracrypt.fr/) on Windows, [LUKS](https://gitlab.com/cryptsetup/cryptsetup/) on Linux, and [FileVault](https://support.apple.com/en-gb/guide/mac-help/mh11785/mac) on macOS.
* If you want to use SSH keys, you should use [age](https://github.com/FiloSottile/age). However, SSH keys aren't recommended by the author.
* If you want something based on an industry standard, you're [stuck](https://latacora.micro.blog/2019/07/16/the-pgp-problem.html) with [GPG](https://gnupg.org/).

</details>

<details>

<summary>Has Kryptor been audited?</summary>

No, audits are extremely expensive (e.g. **$5,000+**). A very generous donation would be required to cover the cost.

However, some cryptographers were contacted for design feedback and advice, [libsodium](https://doc.libsodium.org/) and [Monocypher](https://monocypher.org/) are used for the cryptography, and it should be possible to single-handedly audit the code.

</details>

<details>

<summary>I forgot my passphrase/lost a key, can I recover encrypted files?</summary>

No, it would take an impractical amount of time to bruteforce the encryption keys unless you used a very weak passphrase/low entropy key.

</details>

<details>

<summary>Is it safe to use Kryptor for long-term backups?</summary>

Sort of. Backwards compatibility for major versions is not a goal for Kryptor, as explained [here](/#out-of-scope).  However, Kryptor doesn't get updated that frequently. Breaking changes will get grouped together and require significant planning and sometimes waiting for dependencies to get updated.

To ensure you can decrypt files in the future, you can either:

1. Keep a copy of the executable and source code of the version used to encrypt files. The .NET version will eventually go out of support, but you can edit the version in the source code and rebuild. The main risk is that further changes may be needed to get things working due to .NET/dependency changes, which would require some familiarity with C#.
2. Re-encrypt files when a new major version is released.

The second approach is recommended if possible.

</details>

<details>

<summary>Does Kryptor connect to the internet?</summary>

No, unless you use the `-u|--update` option to check for updates via [GitHub](https://github.com/samuel-lucas6/Kryptor/blob/master/version.txt), as explained [here](/features#privacy).

</details>

## Best practices

<details>

<summary>Which method of encryption should I use?</summary>

* For yourself: passphrases or random pre-shared keys. Keep them in a password manager.
* For others: your private key and their public key. Keep your private key backed up on external storage (e.g. memory sticks).

</details>

<details>

<summary>How do I choose a strong passphrase?</summary>

Just let Kryptor [generate a passphrase](/tutorial/entering-a-passphrase) for you and store it in a password manager like [KeePassXC](https://keepassxc.org/) or [Bitwarden](https://bitwarden.com/).

</details>

<details>

<summary>What's a keyfile?</summary>

A keyfile is a file that's combined with or used instead of a passphrase. If combined, it acts like 2FA. Used alone, it's weaker than a passphrase in that it's stored on disk and can't be memorised. In both cases, it needs to be kept secret.

A [random keyfile](/tutorial#using-a-keyfile) is equivalent to a random symmetric key stored on disk. Using a regular file as a keyfile is equivalent to hashing the file, so the file should be high in entropy (e.g. compressed).

</details>

<details>

<summary>How do I choose a keyfile?</summary>

Just let Kryptor [generate a keyfile](/tutorial/encrypting-files-for-yourself#using-a-keyfile) for you and back it up on external storage (e.g. memory sticks).

However, if you insist on choosing a regular file, make sure it's a compressed file type (e.g. `.zip`).

</details>

<details>

<summary>Should I store keyfiles in the cloud?</summary>

No, they're not encrypted. It would be like uploading a text file containing your passphrase.

</details>

<details>

<summary>How do I share my public key?</summary>

Send people your public key string or `.public` file. Either could also be put on a personal website.

</details>

<details>

<summary>How do I back up my private key?</summary>

Navigate to the `~/.kryptor` directory and copy any `.private` files to external storage (e.g. memory sticks). Keeping private keys offline is good practice even though they're encrypted.

</details>

<details>

<summary>How do I share a pre-shared key?</summary>

Using an [end-to-end encrypted messenger](https://signal.org/) with [disappearing messages](https://support.signal.org/hc/en-us/articles/360007320771-Set-and-manage-disappearing-messages) enabled.

</details>

<details>

<summary>When should I rotate my key pair?</summary>

When you think the private key has been compromised, which shouldn't happen if you keep it offline and use a strong passphrase.

</details>

## Contributing

<details>

<summary>How do I report bugs?</summary>

Please use the `Bug report` issue template on [GitHub](https://github.com/samuel-lucas6/Kryptor/issues/new/choose).

</details>

<details>

<summary>How do I report vulnerabilities?</summary>

Please read the [SECURITY.md](https://github.com/samuel-lucas6/Kryptor/blob/master/SECURITY.md) file on GitHub.

</details>

<details>

<summary>Can I help package Kryptor?</summary>

Yes, please [get in touch](/#contact).

</details>

## Crypto

<details>

<summary>Why ChaCha20-Poly1305?</summary>

It's fast, [standardised](https://www.rfc-editor.org/rfc/rfc8439), doesn't require hardware support, and ChaCha20 has a [higher security margin](https://eprint.iacr.org/2019/1492) than AES.

</details>

<details>

<summary>How does the key-committing ChaCha20-Poly1305 padding fix work?</summary>

The latter 32 bytes of block 0 (after the Poly1305 key) are prepended to the ciphertext as a commitment. For decryption, this commitment is checked in constant time alongside the tag, eliminating a timing difference. [Here's](https://github.com/samuel-lucas6/kcChaCha20-Poly1305) some code.

This is similar to the padding fix discussed in [*How to Abuse and Fix Authenticated Encryption Without Key Commitment*](https://eprint.iacr.org/2020/1456). Credit goes to Loup Vaillant for the block 0 [idea](https://old.reddit.com/r/crypto/comments/opm10n/do_i_need_a_key_committing_aead_to_be_random_key/).

</details>

<details>

<summary>Why BLAKE2b?</summary>

It's [faster](https://www.blake2.net/) than SHA-2 and SHA-3 whilst having a larger security margin than [BLAKE3](https://github.com/BLAKE3-team/BLAKE3) and being as real-world [secure](https://eprint.iacr.org/2019/1492) as SHA-3.

</details>

<details>

<summary>Why Argon2id?</summary>

Argon2 won the [Password Hashing Competition](https://www.password-hashing.net/) in 2015 and is now recommended over other password-based key derivation algorithms, such as PBKDF2 and scrypt.

Argon2id is used because it's the recommended mode in the [RFC](https://www.rfc-editor.org/rfc/rfc9106.html#name-recommendations) and Argon2i is vulnerable to [attacks](https://eprint.iacr.org/2016/759) plus weaker in terms of GPU/ASIC resistance.

</details>

<details>

<summary>Why randomised padding?</summary>

[PADMÉ](https://petsymposium.org/2019/files/papers/issue4/popets-2019-0056.pdf) padding, which is deterministic, was originally implemented. However, [Covert Encryption](https://github.com/covert-encryption/covert) convinced me that it [seems wrong](https://github.com/covert-encryption/covert/blob/main/docs/Rationale.md#random-padding) to have deterministic file sizes when trying to make files indistinguishable from random.

</details>

## Coding

<details>

<summary>How do I build from source?</summary>

Instructions can be found [here](/installation#building-kryptor-from-source).

</details>

<details>

<summary>Why are Kryptor executables ~30 MiB?</summary>

Because the .NET runtime is [included](https://learn.microsoft.com/en-us/dotnet/core/deploying/#publish-self-contained) so users don't have to install .NET themselves, and [ReadyToRun](https://learn.microsoft.com/en-us/dotnet/core/deploying/ready-to-run) compilation is enabled to improve startup performance.

</details>

<details>

<summary>What programming language is Kryptor written in?</summary>

[C#](https://learn.microsoft.com/en-us/dotnet/csharp/) using [.NET 8](https://dotnet.microsoft.com/en-us/download/dotnet/8.0).

</details>

<details>

<summary>What versioning do you use?</summary>

[Semantic versioning](https://semver.org/).

</details>


# Installation

## Downloading Kryptor

Pre-built binaries for Windows (x64), Linux (x64 and ARM64), and macOS (x64 and ARM64) are available on the [Introduction](/#download-kryptor) page.

Kryptor is also available as a package from [several package managers](/#download-kryptor), although **help is wanted** to make it available elsewhere.

## Building Kryptor from source

### Downloading the source code

1. Go to the [**latest release**](https://github.com/samuel-lucas6/Kryptor/releases/latest) on GitHub and download the `Source code (zip)` asset.
2. Navigate to the download location and extract the ZIP file.

{% hint style="warning" %}
Do **NOT** download the source code from the *master* branch.
{% endhint %}

### Using the .NET CLI

1. Install the latest [.NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0). This may be newer than the SDK and runtime version used to build the release files.
2. Navigate to the `src` folder in the terminal (e.g. `cd Downloads\Kryptor-4.0.0\src`).
3. Copy and paste the following command for the operating system and architecture you'd like to build for:

#### Windows (x64)

```bash
dotnet publish -c Release -r win-x64
```

{% hint style="info" %}
The [libsodium](https://doc.libsodium.org/) library requires the [Visual C++ Redistributable for Visual Studio 2015-2022](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170) to work on Windows. However, the appropriate `vcruntime140.dll` is automatically written to disk by Kryptor.
{% endhint %}

#### Linux (x64)

```bash
dotnet publish -c Release -r linux-x64
```

#### macOS (x64)

```bash
dotnet publish -c Release -r osx-x64
```

{% hint style="warning" %}
**For macOS (x64/ARM64)**, there is currently an [upstream issue](https://github.com/dotnet/sdk/issues/45903) in the .NET SDK that prevents the libsodium library from being bundled correctly into the Kryptor executable. The .NET team has not investigated this for over a year, so this is unlikely to be fixed.

Until this is resolved, you must keep the `libsodium.dylib` file (from the `publish` folder) in the same directory as the `kryptor` file.
{% endhint %}

#### Linux (ARM64)

```bash
dotnet publish -c Release -r linux-arm64
```

#### macOS (ARM64)

```bash
dotnet publish -c Release -r osx-arm64
```

{% hint style="warning" %}
Publishing for `osx-arm64` [requires](https://github.com/dotnet/runtime/discussions/66574) using macOS because Apple Silicon Macs require code signing.
{% endhint %}

{% hint style="warning" %}
**For macOS (x64/ARM64)**, there is currently an [upstream issue](https://github.com/dotnet/sdk/issues/45903) in the .NET SDK that prevents the libsodium library from being bundled correctly into the Kryptor executable. The .NET team has not investigated this for over a year, so this is unlikely to be fixed.

Until this is resolved, you must keep the `libsodium.dylib` file (from the `publish` folder) in the same directory as the `kryptor` file.
{% endhint %}

#### Linux (ARM)

```bash
dotnet publish -c Release -r linux-arm
```

{% hint style="warning" %}
`win-arm64` isn't supported yet. Currently, `linux-arm`, `linux-arm64`, `linux-musl-arm64`, and `linux-musl-x64` are not tested. Better support and testing are on the [roadmap](/roadmap).
{% endhint %}

{% hint style="info" %}
A full list of .NET CLI publish options can be found [here](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish). The settings Kryptor uses can be found in the [Kryptor.csproj](https://github.com/samuel-lucas6/Kryptor/blob/master/src/Kryptor/Kryptor.csproj) file.
{% endhint %}

## Verifying signatures

If you already have Kryptor installed, you can use the previous version to verify the `.signature` file for your new download.

{% hint style="info" %}
If you don't have a previous version of Kryptor installed, you should follow the [Verifying digests](#verifying-digests) section below instead.
{% endhint %}

1. Download the signature file for your version of Kryptor [here](/#download-kryptor).
2. Make sure the `.signature` file is in the same directory as the downloaded Kryptor `.zip` file.
3. Open the `Command Prompt` on Windows (e.g. search for `cmd` in the taskbar) or the `terminal` on Linux and macOS (e.g. search your programs for `terminal`).
4. For v4 and above, copy and paste the following command, replacing `[file]` with the file path of the Kryptor `.zip` file:

```bash
kryptor -v -y Ed//bnY+xqUXVMaaI0poBwTqDRlKA9+x9FjlDAHE9MmXKG4= "[file]"
```

For v3, use the following command instead:

```bash
kryptor -v -y RWRudj7GpRdUxpojSmgHBOoNGUoD37H0WOUMAcT0yZcobg== "[file]"
```

Kryptor should display the message '`Good signature`', followed by the authenticated comment '`Kryptor v[version]`'.

{% hint style="danger" %}
If you see '`Bad signature`', your download is either corrupted or not legitimate and may be unsafe.​
{% endhint %}

## Verifying digests

### Windows

1. Download the latest SHA-256 `.digest` file [here](/#download-kryptor).
2. Open a `PowerShell` window (e.g. search `PowerShell` in the taskbar).
3. Navigate to the directory of the downloaded Kryptor `.zip` file (e.g. `cd Downloads`).
4. Copy and paste the following command into PowerShell:

```powershell
(Get-FileHash .\kryptor-windows-x64.zip).Hash -eq (Get-Content .\kryptor-windows-x64.zip.digest).split(" ")[0].ToUpper()
```

The word `True` should appear in the `PowerShell` window.

{% hint style="danger" %}
If you see `False`, your download is either corrupted or not legitimate and may be unsafe.​
{% endhint %}

### Linux and macOS

1. Download the latest SHA-256 `.digest` file [here](/#download-kryptor).
2. Open a `terminal` window (e.g. search your programs for `terminal`).
3. Navigate to the directory of the downloaded Kryptor `.zip` file (e.g. `cd Downloads`).
4. Copy and paste the following command, replacing `[file]` with the file name of the `.zip` file:

```bash
shasum -a 256 -c [file].digest
```

The word `OK` should appear.

{% hint style="danger" %}
If you see another message, your download is either corrupted or not legitimate and may be unsafe.​
{% endhint %}


# Usage

If you're just getting started, check out the [tutorial](/tutorial) instead.​

```
Usage: kryptor [options] <file>

Arguments:
  file             specify a file/directory path

Options:
  -e|--encrypt     encrypt files/directories
  -d|--decrypt     decrypt files/directories
  -p|--passphrase  specify a passphrase (empty for interactive entry)
  -k|--key         specify or randomly generate a symmetric key or keyfile
  -x|--private     specify your private key (unused or empty for default key)
  -y|--public      specify a public key
  -n|--names       encrypt file/directory names
  -o|--overwrite   overwrite files
  -g|--generate    generate a new key pair
  -r|--recover     recover your public key from your private key
  -m|--modify      change your private key passphrase
  -s|--sign        create a signature
  -c|--comment     add a comment to a signature or new key pair
  -l|--prehash     sign large files by prehashing
  -v|--verify      verify a signature
  -t|--signature   specify a signature file (unused for default name)
  --version        view the program version
  -h|--help        show help information

Examples:
  --encrypt [file]
  --encrypt -p [file]
  --encrypt [-y recipient's public key] [file]
  --decrypt [-y sender's public key] [file]
  --sign [-c comment] [file]
  --verify [-y public key] [file]
```

## Specifying files

When referencing file names/paths that contain spaces, you must surround them with "speech marks":

```bash
$ kryptor -e "GitHub Logo.png"
$ kryptor -e "C:\Users\samuel-lucas6\Downloads\GitHub Logo.png"
```

Files in the same directory as the `kryptor` executable can be specified using a file name:

```bash
$ kryptor -e file.txt
```

However, files that aren't in the same directory must be specified using a file path:

```bash
$ kryptor -e C:\Users\samuel-lucas6\Documents\file.txt
```

Multiple files and/or directories can be specified at once:

```bash
$ kryptor -e file1.txt file2.jpg file3.mp4 Photos Videos
```

## Specifying your private key

You can perform encryption, decryption, and signing with your default private key as follows:

```bash
$ kryptor -e file.txt
$ kryptor -d file.txt.bin
$ kryptor -s file.txt
```

This is the recommended approach, but it means your private keys must be kept in the default folder, which varies depending on your operating system:

* Windows: `%USERPROFILE%/.kryptor`
* Linux: `/home/.kryptor`
* macOS: `/Users/USERNAME/.kryptor`

To specify a private key for `-r|--recover`, `-m|--modify`, or a private key not stored in the default folder, you must use the `-x|--private` option followed by `:[file]` like so:

```bash
$ kryptor -r -x:"C:\Users\samuel-lucas6\Documents\encryption.private"
```


# Tutorial

{% hint style="info" %}
Some common example commands are listed below. For step-by-step instructions, advice/warnings, and additional functionality, please see the different [sections](/tutorial/entering-a-passphrase).
{% endhint %}

## tldr

* Encrypt a file/directory with a passphrase:

```bash
kryptor --encrypt --passphrase "path/to/unencrypted_file"
```

* Decrypt an encrypted file with a passphrase:

```bash
kryptor --decrypt --passphrase "path/to/encrypted_file"
```

* Generate a new key pair (stored in `~/.kryptor`) for encryption or signing:

```bash
kryptor --generate
```

* Encrypt a file/directory to one or more recipient public keys:

{% code overflow="wrap" fullWidth="false" %}

```bash
kryptor --encrypt --public Cu//0bQcNYsWeSMwh+sZlJjDIRajfxn65x0qY0MHanPAjlk= --public Cu//Rwvoo8l1x0/bn6yg3p2+SMWSzKhDdPqvlYRFLNaaFSU= "path/to/unencrypted_file"
```

{% endcode %}

* Decrypt a file sent to your public key using your default private key and the sender's public key:

{% code overflow="wrap" %}

```bash
kryptor --decrypt --public Cu//2M5FqAnxmW7PiKxIqLmKEPNxT1Kdkm4U3K/Gr++6OAQ= "path/to/encrypted_file"
```

{% endcode %}

* Sign a file with your default private key:

```bash
kryptor --sign "path/to/unsigned_file"
```

* Verify a signature using someone's public key:

{% code overflow="wrap" %}

```bash
kryptor --verify --public Ed//bnY+xqUXVMaaI0poBwTqDRlKA9+x9FjlDAHE9MmXKG4= "path/to/signed_file"
```

{% endcode %}


# Running Kryptor

## Windows

1. Navigate to the location of the downloaded `.zip` file (e.g. the Downloads folder).
2. Right click the `.zip` file and select `Extract All`.
3. Cut and paste (`Ctrl` + `X`) the `kryptor.exe` file into `%USERPROFILE%`, which can be accessed by searching `%USERPROFILE%` in the taskbar.
4. Open the `Command Prompt` (e.g. search for `cmd` in the taskbar).
5. Type `kryptor -h` for a list of commands and some examples.

{% hint style="info" %}
Don't move the executable into a directory that requires administrator privileges.
{% endhint %}

## Linux

1. Open the `terminal` (e.g. search your programs for `terminal`) and navigate to the path of the downloaded `.zip` file (e.g. `cd Downloads`).
2. Type `unzip [file].zip`.
3. Navigate to the extracted folder (e.g. `cd kryptor-linux-x64`).
4. Mark the `kryptor` file as executable using `chmod +x kryptor`.
5. Move the `kryptor` executable to the `/home` directory using `mv kryptor /home`.
6. Type `./kryptor -h` for a list of commands and some examples.

{% hint style="info" %}
Don't move the executable into a directory that requires root privileges.
{% endhint %}

## macOS

1. Open `Finder` and navigate to the location of the downloaded `.zip` file (e.g. `Downloads/kryptor-macos-x64.zip`).
2. Double click the `.zip` file to extract it.
3. Open the `Terminal` (e.g. search your programs for `Terminal`) and navigate to the path of the extracted folder (e.g. `cd Downloads/kryptor-macos-x64`).
4. Mark the `kryptor` file as executable using `chmod +x kryptor`.
5. Move the `kryptor` executable to the `/Users/USERNAME` directory, replacing `USERNAME` with your user account name, which is shown before the `@` symbol on the far left of the terminal (e.g. `mv kryptor /Users/samuellucas`).
6. Type `cd` to return to your user directory.
7. Then type `./kryptor -h` for a list of commands and some examples.
8. macOS will give a warning that Apple couldn't verify the file since it hasn't been signed. Click `Done`, open `System Settings`, go to `Privacy & Security`, scroll down, and click the `Allow Anyway` button.
9. Run `./kryptor -h` again and click `Open Anyway` when another popup appears. This will ask you to authenticate (e.g. with Touch ID). Then the application will be saved as an exception to your security settings and can be run without any further popups.

{% hint style="info" %}
See [this](https://support.apple.com/en-us/102445) article for more information on Gatekeeper in macOS, which blocks unidentified software from running to protect against malware.
{% endhint %}

{% hint style="info" %}
Don't move the executable into a directory that requires root privileges.
{% endhint %}


# Specifying files

When referencing file names/paths that contain spaces, you must surround them with "speech marks":

```bash
$ kryptor -e "GitHub Logo.png"
$ kryptor -e "C:\Users\samuel-lucas6\Downloads\GitHub Logo.png"​
```

Files in the same directory as the `kryptor` executable can be specified using a file name:

```bash
$ kryptor -e file.txt​
```

However, files that are in a different directory (e.g. in your `Documents` folder) must be specified using a file path:

```bash
$ kryptor -e "C:\Users\samuel-lucas6\Documents\file.txt​"
```

Multiple files and/or directories can be specified at once:

```bash
$ kryptor -e file1.txt file2.jpg file3.mp4 Photos Videos
```

{% hint style="info" %}
If you've installed Kryptor as a package, these rules apply to the working directory. The `cd` command on Windows and `pwd` on Linux/macOS can be used to print the working directory.
{% endhint %}


# Entering a passphrase

{% hint style="danger" %}
To ensure that files/private keys can always be decrypted, **only use** [**ASCII**](https://www.rfc-editor.org/rfc/rfc20) **characters in passphrases**. For example, use a [password manager](https://bitwarden.com/) to randomly generate passwords/passphrases.
{% endhint %}

## Interactively

There are three ways of entering a passphrase interactively:

1. Press `Enter` on your keyboard (instead of typing anything) to randomly generate a secure passphrase, which will be displayed in the terminal. **Make sure you copy this passphrase into a** [**password manager**](https://keepassxc.org/).
2. Type in your passphrase, press `Enter` on your keyboard, and retype the passphrase.
3. Paste your passphrase by right-clicking, press `Enter` on your keyboard, and paste the passphrase again.

{% hint style="info" %}
Kryptor uses UNIX style passphrase entry, meaning **nothing is displayed in the terminal when you type**. This prevents someone from seeing the passphrase characters and the length of the passphrase.
{% endhint %}

Here's what randomly generating a passphrase looks like:

```bash
$ kryptor -e -p file.txt

Enter a passphrase (leave empty for a random one):

Randomly generated passphrase: lethargic-shivering-confirm-doable-unlearned-fragility-aim-glitch
```

Here's what typing a passphrase looks like:

```bash
$ kryptor -e -p file.txt

Enter a passphrase (leave empty for a random one):

Retype passphrase:

```

## Non-interactively

Instead of the above, you can specify `-p:"[passphrase]"` like so:

```bash
$ kryptor -e -p:"YeV$Ra7{_IJ]nN2v1m,ei9d+T" file.txt
```

To randomly generate a passphrase this way, you can specify `-p:" "` like so:

```bash
$ kryptor -e -p:" " file.txt

Randomly generated passphrase: dumpster-roundup-unviable-sanitizer-unsecured-budget-sinner-shindig
```

This method of passphrase entry can also be used for encrypting and decrypting a private key (e.g. for [generating a key pair](#generating-a-new-key-pair), [encrypting files](#using-a-private-key) with a private key, or [signing files](#sign-files)). However, you must specify `-x|--private` alongside non-interactive passphrase entry for file encryption/decryption.

Here are some examples:

```bash
$ kryptor -g -p:"w*%Ul=j.F>1e{6H,+#ZWhXYUl"
$ kryptor -e -x -p:"1yF+k5l9zezPB}R]Pe+|*;sra" file.txt
$ kryptor -d -x -p:"1yF+k5l9zezPB}R]Pe+|*;sra" file.txt.bin
$ kryptor -s -p:"w*%Ul=j.F>1e{6H,+#ZWhXYUl" file.txt
```

{% hint style="warning" %}
This shouldn't be used if someone else may see your screen.
{% endhint %}


# Encrypting files for yourself

Encrypting a file ensures that the contents cannot be read or modified without the secret key. This is useful for protecting your files on external storage (e.g. memory sticks) or in the cloud.

## Using a passphrase

Kryptor will convert your passphrase into a unique encryption key per file/directory. For security reasons, this involves a slight delay.

{% hint style="warning" %}

* To prevent files from being decrypted by someone else, **always use a strong passphrase**!
* To ensure that files can always be decrypted, **only use** [**ASCII**](https://www.rfc-editor.org/rfc/rfc20) **characters in passphrases**. For example, use a [password manager](https://bitwarden.com/) to randomly generate passwords/passphrases.
* If you forget your passphrase, any files encrypted using that passphrase will be **unrecoverable**. Therefore, using a [password manager](https://keepassxc.org/) is strongly recommended.
  {% endhint %}

You can either type your passphrase:

1. Interactively (**recommended**): you'll be asked to enter a new passphrase and then to retype the passphrase for confirmation. You can type nothing initially to randomly generate a passphrase. The characters you type are hidden to prevent someone from seeing your passphrase and the length of the passphrase.
2. Non-interactively (**less secure**): this involves specifying the passphrase on the command line. You can type `" "` to randomly generate a passphrase.

Here's an example using interactive passphrase entry:

```bash
$ kryptor -e -p file.txt

Enter a passphrase (leave empty for a random one):

Retype passphrase:

```

Here's an example using non-interactive passphrase entry:

```bash
$ kryptor -e -p:"CED<7q*uxx@4S=szftln3@co(" file.txt
```

## Using a symmetric key

Kryptor will convert the specified or randomly generated symmetric key into a unique encryption key per file/directory. Unlike using a passphrase, this involves no delay.

{% hint style="warning" %}
If you lose the symmetric key, any files encrypted using that symmetric key will be **unrecoverable**. Therefore, using a [password manager](https://keepassxc.org/) is strongly recommended.
{% endhint %}

Here's an example of randomly generating a symmetric key string:

```bash
$ kryptor -e -k " " file.txt

Randomly generated key: PSK/I1SkTbgoylRxiW3w0wcYmovWTWgC54bPmpgsiQrgO3c=
```

Here's an example of using the same symmetric key string again:

```
$ kryptor -e -k PSK/I1SkTbgoylRxiW3w0wcYmovWTWgC54bPmpgsiQrgO3c= file2.txt
```

## Using a keyfile

Kryptor will convert the hash of the keyfile into a unique encryption key per file/directory. Keyfiles can be used alongside (**recommended**) or instead of a passphrase (**less secure**).

When used with a passphrase, the keyfile is like [2FA](https://en.wikipedia.org/wiki/Multi-factor_authentication). Used alone, it's weaker than a passphrase in that it's stored on disk and cannot be memorised.

{% hint style="warning" %}

* If the keyfile is lost or modified, files encrypted using that keyfile will become **unrecoverable**. Therefore, you should **back up** keyfiles to external storage (e.g. memory sticks).
* **Never** share a keyfile! Keep them **secret** and **offline**!
  {% endhint %}

You can either:

1. Randomly generate keyfiles (**recommended**): you can specify a non-existent file to give the keyfile a name, or you can specify a directory that exists to generate a keyfile with a random name.
2. Select ordinary files (**less secure**): you can specify any file that's at least 32 bytes long. Compressed files (e.g. `.zip`) are strongly recommended.

Here's an example of randomly generating a keyfile alongside using a passphrase:

```bash
kryptor -e -p -k "C:\Users\samuel-lucas6\Documents\keyfile.key" file.txt

Randomly generated keyfile: keyfile.key
```

Here's an example of using an ordinary file as a keyfile alongside a passphrase:

```bash
$ kryptor -e -p -k "C:\Users\samuel-lucas6\Pictures\wallpaper.jpg" file.txt
```

## Using a private key

Kryptor will convert your encryption private key into a unique encryption key per file/directory.

This requires an encryption key pair, which can be generated as follows:

```bash
$ kryptor -g

Please select a key pair type (type 1 or 2):
1) Encryption
2) Signing
1

Enter a passphrase (leave empty for a random one):

Retype passphrase:

Deriving encryption key from passphrase...

Public key: Cu//2M5FqAnxmW7PiKxIqLmKEPNxT1Kdkm4U3K/Gr++6OAQ=
Public key file: "C:\Users\samuel-lucas6\.kryptor\encryption.public"

Private key file: "C:\Users\samuel-lucas6\.kryptor\encryption.private" - Keep this secret!

IMPORTANT: Please back up these files to external storage (e.g. memory sticks).
```

Or non-interactively like so:

```bash
$ kryptor -g -e -p:"G,GfDJ0(RcX8eq}_NH5/BhIoo"
```

{% hint style="warning" %}

* If you lose the private key file, files encrypted using that private key will be **unrecoverable**. Therefore, you should **back up** the private key file to external storage (e.g. memory sticks).
* **Never** share your **private key** file! Keep it **secret** and **offline**!
  {% endhint %}

When performing encryption, you'll be asked to decrypt your private key using your passphrase. For security reasons, this involves a slight delay.

Here's an example of how to use your default encryption private key:

```bash
$ kryptor -e file.txt

Enter your private key passphrase:

```

Here's an example of how to specify an encryption private key not stored in the [default folder](/tutorial/generating-a-new-key-pair):

```bash
$ kryptor -e -x:"C:\Users\samuel-lucas6\Documents\encryption.private" file.txt

Enter your private key passphrase:

```


# Encrypting files for others

Encrypting a file ensures that the contents cannot be read or modified without the secret key. This allows you to securely share a file with other people via an insecure channel (e.g. email, an unencrypted messenger like Discord, or the cloud).

## Using public keys

Kryptor will convert the shared secret for each (sender private key, recipient public key) pair into a unique encryption key per file/directory.

**Only specified recipients can decrypt the files/directories you send**, using their private key.

Both **you (the sender) and any recipients** need to generate an encryption key pair **once**, which can be generated as follows:

```bash
$ kryptor -g

Please select a key pair type (type 1 or 2):
1) Encryption
2) Signing
1

Enter a passphrase (leave empty for a random one):

Retype passphrase:

Deriving encryption key from passphrase...

Public key: Cu//2M5FqAnxmW7PiKxIqLmKEPNxT1Kdkm4U3K/Gr++6OAQ=
Public key file: "C:\Users\samuel-lucas6\.kryptor\encryption.public"

Private key file: "C:\Users\samuel-lucas6\.kryptor\encryption.private" - Keep this secret!

IMPORTANT: Please back up these files to external storage (e.g. memory sticks).
```

Or non-interactively like so:

```bash
$ kryptor -g -s -p:"TkU-?IV_V|@UcTvCHf#FpU-yf"
```

{% hint style="warning" %}

* **Never** share your **private key** file! Keep it **secret** and **offline**!
* **Back up** the private key file to external storage (e.g. memory sticks).
* Do **NOT** overwrite unencrypted files (please see the [Encryption options](/tutorial/encryption-options#overwriting-unencrypted-files) section) unless you (the sender) want to lose access to them.
  {% endhint %}

Next, you need to exchange **encryption public keys** (e.g. via a messaging app). **This only needs to be done once** unless someone generates a new key pair. You can either share your public key:

1. As a string (**easiest**): `Cu//2M5FqAnxmW7PiKxIqLmKEPNxT1Kdkm4U3K/Gr++6OAQ=`
2. As a `.public` file (**more long-term**): `~/.kryptor/encryption.public`

**You must specify your private key and each recipient's public key**. You'll be asked to decrypt your private key using your passphrase. For security reasons, this involves a slight delay.

Here's an example of how to use your default encryption private key with one recipient:

```bash
$ kryptor -e -y Cu//0bQcNYsWeSMwh+sZlJjDIRajfxn65x0qY0MHanPAjlk= file.txt

Enter your private key passphrase:

```

Here's an example with multiple recipients:

```bash
$ kryptor -e -y Cu//0bQcNYsWeSMwh+sZlJjDIRajfxn65x0qY0MHanPAjlk= -y Cu//Rwvoo8l1x0/bn6yg3p2+SMWSzKhDdPqvlYRFLNaaFSU= file.txt

Enter your private key passphrase:

```

Here's an example of how to specify an encryption private key not stored in the [default folder](/tutorial/generating-a-new-key-pair):

```bash
$ kryptor -e -x:"C:\Users\samuel-lucas6\Documents\encryption.private" -y Cu//0bQcNYsWeSMwh+sZlJjDIRajfxn65x0qY0MHanPAjlk= file.txt 
```

After encryption has finished, you can share the encrypted file(s) with the recipients (e.g. via email, a messaging app, or a cloud storage service).

## Using a symmetric key

Kryptor will convert the randomly generated symmetric key into a unique encryption key per file/directory.

Unlike using public keys, **anybody** with access to the symmetric key can decrypt the file.

{% hint style="warning" %}
**Never** send someone the key via an insecure channel (e.g. email, SMS, an unencrypted messenger like Discord, or the cloud)!
{% endhint %}

Here's an example of randomly generating a symmetric key string:

```bash
$ kryptor -e -k " " file.txt

Randomly generated key: PSK/I1SkTbgoylRxiW3w0wcYmovWTWgC54bPmpgsiQrgO3c=
```

Then share the key with the recipient using an **end-to-end encrypted** messaging app (e.g. [Signal](https://www.signal.org/)) with [disappearing messages](https://support.signal.org/hc/en-us/articles/360007320771-Set-and-manage-disappearing-messages) if possible.


# Encryption options

## Overwriting unencrypted files

For each specified file, Kryptor will copy the encrypted file to the location of the unencrypted file before deleting the overwritten file, leaving just the encrypted file.

Here's an example:

```bash
$ kryptor -e -o file.txt
```

{% hint style="warning" %}
Disk encryption should be used if you want to prevent unencrypted files being recovered.
{% endhint %}

## Encrypting file names

Kryptor will randomise the names of encrypted files, and the original name for each file will be restored automatically during decryption.

Here's an example:

```bash
$ kryptor -e -n file.txt
```

{% hint style="warning" %}
Using this feature is not recommended if file names contain non-[ASCII](https://www.rfc-editor.org/rfc/rfc20) characters (e.g. non-English/accented characters).
{% endhint %}


# Decrypting your files

## Using your passphrase

Kryptor will convert your passphrase into the unique encryption key per file/directory used for encryption. For security reasons, this involves a slight delay.

You can either type your passphrase:

1. Interactively (**recommended**): you'll be asked to enter your passphrase and then to retype the passphrase for confirmation. The characters you type are hidden to prevent someone from seeing your passphrase and the length of the passphrase.
2. Non-interactively (**less secure**): this involves specifying your passphrase on the command line.

Here's an example using interactive passphrase entry:

```bash
$ kryptor -d -p file.txt.bin

Enter your passphrase:

Retype passphrase:

```

Here's an example using non-interactive passphrase entry:

```bash
$ kryptor -d -p:"CED<7q*uxx@4S=szftln3@co(" file.txt.bin
```

## Using your symmetric key

Kryptor will convert your symmetric key into the unique encryption key per file/directory used for encryption. Unlike using a passphrase, this involves no delay.

Here's an example:

```bash
$ kryptor -d -k PSK/I1SkTbgoylRxiW3w0wcYmovWTWgC54bPmpgsiQrgO3c= file.txt.bin
```

## Using your keyfile

Kryptor will convert the hash of the keyfile into the unique encryption key per file/directory used for encryption.

Here's an example of specifying a keyfile used alongside a passphrase:

```bash
$ kryptor -d -p -k "C:\Users\samuel-lucas6\Documents\keyfile.key" file.txt.bin
```

## Using your private key

Kryptor will convert your encryption private key into the unique encryption key per file/directory used for encryption.

You'll be asked to decrypt your private key using your passphrase. For security reasons, this involves a slight delay.

Here's an example of how to use your default encryption private key:

```bash
$ kryptor -d file.txt.bin

Enter your private key passphrase:

```

Here's an example of how to specify an encryption private key not stored in the [default folder](/tutorial/generating-a-new-key-pair):

```bash
$ kryptor -d -x:"C:\Users\samuel-lucas6\Documents\encryption.private" file.txt.bin

Enter your private key passphrase:

```


# Decrypting received files

## Using a public key

Kryptor will convert the shared secret for the (recipient private, sender public) key pair into the unique encryption key per file/directory used for encryption.

The sender cannot decrypt the files/directories they sent; only you (the recipient) and any other recipients can.

**You must specify your private key and the sender's public key**. You'll be asked to decrypt your private key using your passphrase. For security reasons, this involves a slight delay.

Here's an example of how to use your default encryption private key:

```bash
$ kryptor -d -y Cu//2M5FqAnxmW7PiKxIqLmKEPNxT1Kdkm4U3K/Gr++6OAQ= file.txt.bin

Enter your private key passphrase:

```

Here's an example using a private key not stored in the [default folder](/tutorial/generating-a-new-key-pair):

```bash
$ kryptor -d -x:"C:\Users\samuel-lucas6\Documents\encryption.private" -y Cu//2M5FqAnxmW7PiKxIqLmKEPNxT1Kdkm4U3K/Gr++6OAQ= file.txt.bin

Enter your private key passphrase:

```

## Using a symmetric key

Kryptor will convert the symmetric key into the unique encryption key per file/directory used for encryption. Unlike using a passphrase, this involves no delay.

Here's an example:

```bash
$ kryptor -d -k PSK/I1SkTbgoylRxiW3w0wcYmovWTWgC54bPmpgsiQrgO3c= file.txt.bin
```


# Decryption options

## Deleting encrypted files

Kryptor will delete the specified encrypted files after decryption, leaving just the decrypted files.

Here's an example:

```bash
$ kryptor -d -o file.txt.bin
```


# Signing files

Signing a file can show that it came from you and wasn't modified.

Kryptor will use your signing private key to create a signature file for each file/file in a directory you specify. Sharing the file, signature file, and your **public key** allows other people to verify the signature for that file.

This requires a signing key pair, which can be generated as follows:

```bash
$ kryptor -g

Please select a key pair type (type 1 or 2):
1) Encryption
2) Signing
2

Enter a passphrase (leave empty for a random one):

Retype passphrase:

Deriving encryption key from passphrase...

Public key: Ed//L2CzmImpu9UA6HFjllemS4TVaE84PdTTrTNc5i6gxFU=
Public key file: "C:\Users\samuel-lucas6\.kryptor\signing.public"
​
Private key file: "C:\Users\samuel-lucas6\.kryptor\signing.private" - Keep this secret!

IMPORTANT: Please back up these files to external storage (e.g. memory sticks).
```

Or non-interactively like so:

```bash
$ kryptor -g -s -p:"dy#p}hIJBKSZ*M3b6I)!6iAK@"
```

{% hint style="warning" %}

* You should **back up** the private key file to external storage (e.g. memory sticks).
* **Never** share your **private key** file! Keep it **secret** and **offline**!
  {% endhint %}

You can either share your public key:

* As a string (**easiest**): `Ed//L2CzmImpu9UA6HFjllemS4TVaE84PdTTrTNc5i6gxFU=`
* As a `.public` file (**more long-term**): `~/.kryptor/signing.public`

Before signing, you will be asked to decrypt your private key using your passphrase. For security reasons, this involves a slight delay.

Here's an example of how to use your default signing private key:

```bash
$ kryptor -s file.zip

Enter your private key passphrase:

```

Here's an example of how to use a private key not stored in the [default folder](/tutorial/generating-a-new-key-pair):

```bash
$ kryptor -s -x:"C:\Users\samuel-lucas6\Documents\signing.private" file.zip

Enter your private key passphrase:

```


# Signing options

## Specifying a custom signature file

If you want to give the signature file a custom name or store it in a different location to the file being signed, you must specify a custom file name/path.

Here's an example:

```bash
$ kryptor -s -t "C:\Users\samuel-lucas6\Documents\file.signature" file.zip
```

## Specifying a comment

This is useful for adding instructions or metadata like an intended file name, timestamps, or version numbers.

Kryptor will verify the comment as part of the signature and display it to the user verifying the signature if verification succeeds. If you don't specify a comment, the default comment will be used.

Remember to type "speech marks" around the comment like so:

```bash
$ kryptor -s -c "Signed by Bob on the 4th of February 2022." file.zip
```

If you don't want the default comment, you can type a space as the comment:

```bash
$ kryptor -s -c " " file.zip
```

## Signing large files

If you have limited RAM, you may not be able to sign large files without prehashing. However, Kryptor automatically uses prehashing when you select a file that's equal to or greater than 1 GiB in size, so this shouldn't be a problem on most machines.

Here's an example:

```bash
$ kryptor -s -l photos.zip
```


# Verifying signatures

If you have a signature, the associated file to verify, and the **signer's public key**, you can verify that the signature is valid for that file and public key.

You can either specify their public key:

* As a string (**easiest**): `Ed//L2CzmImpu9UA6HFjllemS4TVaE84PdTTrTNc5i6gxFU=`
* As a `.public` file (**more long-term**): `signing.public`

{% hint style="warning" %}
If you see the message 'Bad signature', the signature is not valid for that public key and the comment will not be displayed. This means either:

* The public key is wrong for that signature file.
* The file has been corrupted or tampered with.
  {% endhint %}

If the signature file has the same file name (minus the `.signature` extension) and is in the same directory as the file to verify, you don't need to specify the signature file:

```bash
$ kryptor -v -y Ed//L2CzmImpu9UA6HFjllemS4TVaE84PdTTrTNc5i6gxFU= file.zip

Good signature.
Authenticated comment: Signed by Bob on the 4th of February 2022.
```

Here's an example where the signature file name is different to that of the file to verify:

```bash
$ kryptor -v -y Ed//L2CzmImpu9UA6HFjllemS4TVaE84PdTTrTNc5i6gxFU= -t file.signature file.zip
```

{% hint style="success" %}
If the signature is valid, you'll see the message 'Good signature', followed by the authenticated comment if there is one.
{% endhint %}


# Generating a new key pair

Kryptor will randomly generate an asymmetric key pair and use your passphrase to encrypt the private key. For security reasons, this involves a slight delay.

The asymmetric keys will be exported to files (`.public` and `.private`), and the file paths will be displayed in the terminal. The public key will also be displayed in the terminal as a Base64 string, which can be copied and pasted. **Only the public key should be shared**.

{% hint style="warning" %}

* To protect the private key, **always use a strong passphrase**!
* To ensure that the private key can always be decrypted, **only use** [**ASCII**](https://www.rfc-editor.org/rfc/rfc20) **characters in passphrases**. For example, use a [password manager](https://bitwarden.com/) to randomly generate passwords/passphrases.
* **Always** back up the **private key** file to external storage (e.g. memory sticks).
* **Never** share your **private key** file! Keep it **secret** and **offline**!
  {% endhint %}

The default key directory depends on your operating system:

* Windows: `%USERPROFILE%/.kryptor`
* Linux: `/home/.kryptor`
* macOS: `/Users/USERNAME/.kryptor`

Using the default key directory saves you typing in the path of your private key each time you want to use it.

Here's an example of using the default key directory:

```bash
$ kryptor -g

Please select a key pair type (type 1 or 2):
1) Encryption
2) Signing
2

Enter a passphrase (leave empty for a random one):

Retype passphrase:

Deriving encryption key from passphrase...

Public key: Ed//L2CzmImpu9UA6HFjllemS4TVaE84PdTTrTNc5i6gxFU=
Public key file: "C:\Users\samuel-lucas6\.kryptor\signing.public"

Private key file: "C:\Users\samuel-lucas6\.kryptor\signing.private" - Keep this secret!

IMPORTANT: Please back up these files to external storage (e.g. memory sticks).
```

This can also be done non-interactively like so, specifying `-e` for encryption or `-s` for signing:

```bash
$ kryptor -g -s -p:"NA2nxxj.Ip^EH96hq1Y:Lv;Of"
```

Here's an example of specifying a custom key directory:

```bash
$ kryptor -g "C:\Users\samuel-lucas6\Documents\Keys"
```

You can share your encryption public key string/file with other people so they can send you an encrypted file that only you and any other specified recipients will be able to decrypt.

You can share your signing public key string/file to allow other people to verify signatures you created so people can verify the authenticity of files.

## Specifying a comment

When generating a new key pair, you can include a comment in the public/private key files as an identifier. Note that this is not authenticated in any way, so it can be changed without detection.

Here's an example:

```bash
$ kryptor -g -c "username@domain.com"
```

## Overwriting key pairs

If you've already generated a key pair for the default/custom directory, you must specify that you want to overwrite the existing key pair when generating a new key pair of the same type (encryption or signing).

{% hint style="danger" %}
This will **replace** the existing key pair.​ Make sure you **back up the old key pair** if you don't want to lose access to it.
{% endhint %}

Here's an example:

```bash
$ kryptor -g -o
```


# Key pair options

## Recovering your public key

If you've lost/forgotten your public key, Kryptor can regenerate it for you from your private key. The public key string will be displayed, and a `.public` key file will be created if one doesn't exist in the same directory as the specified `.private` key file.

Here's an example:

```bash
$ kryptor -r -x:"C:\Users\samuel-lucas6\.kryptor\signing.private"

Enter your private key passphrase:

Decrypting private key...

Public key: Ed//L2CzmImpu9UA6HFjllemS4TVaE84PdTTrTNc5i6gxFU=
Public key file: "C:\Users\samuel-lucas6\.kryptor\signing.public"
```

## Changing your private key passphrase

Kryptor will re-encrypt your private key using a new passphrase, overwriting the previous encrypted private key file.

Here's an example:

```bash
$ kryptor -m -x:"C:\Users\samuel-lucas6\.kryptor\encryption.private"

Enter your private key passphrase:

Decrypting private key...

Enter a passphrase (leave empty for a random one):

Retype passphrase:

Deriving encryption key from passphrase...
Passphrase changed successfully.
```


# Specification

**Author**: Samuel Lucas

**Revision**: 5

**Date**: 25/03/2023

**Status**: Official/Unstable

## Introduction

This page describes the file and key formats used by Kryptor v4, providing a high-level overview of the innerworkings and implementation guidance.

Unlike [age](https://github.com/C2SP/C2SP/blob/main/age.md), backwards compatibility is not a goal. This is because it prevents making certain improvements. Instead, the focus is on developing the most sensible, long-lasting protocol. Unfortunately, due to various constraints, v4 is not that protocol. Therefore, breaking changes can be expected in the distant future, although the aim is to eventually have a stable format. This is unlikely to occur anytime soon due to issues such as post-quantum security and AEAD committing security.

{% hint style="success" %}
If anything is unclear, please share your feedback [here](https://github.com/samuel-lucas6/Kryptor/discussions).
{% endhint %}

## Conventions and definitions

The key words "**MUST**", "**MUST NOT**", "**REQUIRED**", "**SHALL**", "**SHALL NOT**", "**SHOULD**", "**SHOULD NOT**", "**RECOMMENDED**", "**NOT RECOMMENDED**", "**MAY**", and "**OPTIONAL**" in this document are to be interpreted as described in [BCP 14](https://www.rfc-editor.org/info/bcp14) \[[RFC 2119](https://www.rfc-editor.org/rfc/rfc2119)] \[[RFC 8174](https://www.rfc-editor.org/rfc/rfc8174)] when, and only when, they appear in all capitals, as shown here.

`||` denotes concatenation. `=` denotes assignment. `,` denotes separate parameters. `0x` followed by two hexadecimal characters denotes a byte value in the 0-255 range. Finally, `++` denotes incremented by one in little-endian.

Variable/parameter names may contain spaces for readability. Additionally, the parameter order follows the [Geralt](https://www.geralt.xyz/) cryptographic library API. This means the key usually does not come first. See the bullet points below the pseudocode and Geralt documentation for clarification.

Base64 refers to the original Base64 encoding with padding, as specified in [RFC 4648](https://www.rfc-editor.org/rfc/rfc4648.html). Encoders **MUST** generate canonical Base64 according to [Section 3.5](https://www.rfc-editor.org/rfc/rfc4648.html#section-3.5), and decoders **MUST** reject non-canonical encodings and other Base64 variants, such as Base64URL and Base64 without padding.

ChaCha20-Poly1305 is the AEAD from [RFC 8439](https://www.rfc-editor.org/rfc/rfc8439). kcChaCha20-Poly1305 is ChaCha20-Poly1305 with a [variant](https://github.com/samuel-lucas6/kcChaCha20-Poly1305) of the [key commitment padding fix](https://eprint.iacr.org/2020/1456), which involves prepending the latter 32 bytes of block 0 (after the Poly1305 key) to the ciphertext as a commitment. Verification of the commitment **MUST** be done in constant time at the same time as verification of the authentication tag.

ChaCha20 is the unauthenticated stream cipher from [RFC 8439](https://www.rfc-editor.org/rfc/rfc8439), with the block counter set to 0.

BLAKE2b is the cryptographic hash function and message authentication code from [RFC 7693](https://www.rfc-editor.org/rfc/rfc7693). However, for key derivation, BLAKE2b uses the personalisation and salt parameters, which are instead specified in the [BLAKE2](https://www.blake2.net/blake2.pdf) paper. BLAKE2b-256/BLAKE2b-512 refers to the output length in bits.

Argon2id is the password-based key derivation function from [RFC 9106](https://www.rfc-editor.org/rfc/rfc9106.html), with a parallelism of 1, a memory size of 256 MiB, and 3 passes.

X25519 is outlined in [RFC 7748](https://www.rfc-editor.org/rfc/rfc7748). Implementations **MUST** reject all-zero outputs as outlined in [Section 6](https://www.rfc-editor.org/rfc/rfc7748#section-6).

Ed25519 is from [RFC 8032](https://www.rfc-editor.org/rfc/rfc8032). For prehashing, BLAKE2b-512 is used to hash the message before regular Ed25519 is used on the hash. Ed25519 keys **MUST NOT** be converted to X25519 keys.

Elligator 2 is described in [*Elligator: Elliptic-curve points indistinguishable from uniform random strings*](https://elligator.cr.yp.to/elligator-20130828.pdf). The [Monocypher](https://monocypher.org/) cryptographic library **MUST** be used to [generate](https://monocypher.org/manual/elligator) 'dirty' ephemeral public keys and [decode](https://monocypher.org/manual/elligator) said ephemeral public keys. Other [known implementations](https://elligator.org/implementations) are incompatible.

ISO/IEC 7816-4 padding is from [ISO/IEC 7816-4:2005](https://www.iso.org/standard/36134.html).

A CSPRNG **MUST** be used whenever the word 'random' is used.

Sensitive bytes **SHOULD** be zeroed from memory once they are no longer required.

Zero-length passphrases **MUST** be rejected.

It is **RECOMMENDED** that all cryptography except Elligator 2 is implemented using the [libsodium](https://doc.libsodium.org/) cryptographic library.

## Encrypted file format

A Kryptor file is treated as binary and consists of four parts: an unencrypted header, an encrypted key wrap header, an encrypted file metadata header, and an encrypted payload.

When file names are not being encrypted, Kryptor files **SHOULD** use the extension `.bin`. In contrast, with file name encryption, Kryptor files **SHOULD NOT** use an extension. In both cases, the extension `.kryptor` **MUST NOT** be used as this would defeat the purpose of the encrypted file being indistinguishable from random data. Similarly, encrypted files **SHOULD NOT** be marked as read-only.

### Unencrypted header

```
salt || hidden ephemeral public key
```

* `salt`: a random salt per file (16 bytes).
* `hidden ephemeral public key`: random bytes per file if only a passphrase and/or symmetric key is used; otherwise, a random X25519 ephemeral public key per file made indistinguishable from random using Elligator 2 (32 bytes).

### Key wrap header

```
wrapped file key [1] || ... || wrapped file key [20]
```

This header contains space to wrap the same 256-bit symmetric key for up to 20 different recipients, making it 640 bytes long. It **MUST** be fixed in length. This was done to keep parsing simple and to avoid leaking the number of recipients, which padding may not protect sufficiently.

If a symmetric encryption method is used (e.g. a passphrase) or less than 20 recipients are specified, random bytes **MUST** be generated to fill the rest of the header.

```
wrapped file key = ChaCha20(file key, nonce, header key)
```

* `file key`: a random symmetric key per file (32 bytes).
* `nonce`: an all-zero nonce (12 bytes).
* `header key`: the output keying material from a KDF per recipient (32 bytes).

### Header key derivation

The way the header key is derived depends on the encryption method used. The header key **MUST** be unique per recipient.

#### Passphrase

```
hashed passphrase = Argon2id(passphrase, salt)
header key = BLAKE2b-256(hashed passphrase, personalisation, empty salt, info)
```

* `passphrase`: the UTF8 encoding of the user's passphrase (1+ bytes).
* `salt`: the random salt from the unencrypted header (16 bytes).
* `hashed passphrase`: the key for BLAKE2b (32 bytes).
* `personalisation`: the UTF8 encoding of `"Kryptor.Personal"` (16 bytes).
* `empty salt`: an all-zero salt (16 bytes).
* `info`: the hidden ephemeral public key from the unencrypted header, which is actually just random bytes when a passphrase is used (32 bytes).

#### Symmetric key

```
header key = BLAKE2b-256(symmetric key, personalisation, salt, info) 
```

* `symmetric key`: the key for BLAKE2b (32 bytes).
* `personalisation`: the UTF8 encoding of `"Kryptor.Personal"` (16 bytes).
* `salt`: the random salt from the unencrypted header (16 bytes).
* `info`: the hidden ephemeral public key from the unencrypted header, which is actually just random bytes when a symmetric key is used (32 bytes).

#### Passphrase and symmetric key

```
hashed passphrase = Argon2id(passphrase, salt)
header key = BLAKE2b-256(hashed passphrase || symmetric key, personalisation, empty salt, info)
```

* `passphrase`: the UTF8 encoding of the user's passphrase (1+ bytes).
* `salt`: the random salt from the unencrypted header (16 bytes).
* `hashed passphrase`: the first half of the key for BLAKE2b (32 bytes).
* `symmetric key`: the second half of the key for BLAKE2b (32 bytes).
* `personalisation`: the UTF8 encoding of `"Kryptor.Personal"` (16 bytes).
* `empty salt`: an all-zero salt (16 bytes).
* `info`: the hidden ephemeral public key from the unencrypted header, which is actually just random bytes when a passphrase and symmetric key is used (32 bytes).

#### Private key

```
shared secret = X25519(private key, unhidden ephemeral public key)
hashed shared secret = BLAKE2b-256(shared secret || public key || unhidden ephemeral public key, pre-shared key)
header key = BLAKE2b-256(hashed shared secret, personalisation, salt, info)
```

* `private key`: the user's private key (32 bytes).
* `unhidden ephemeral public key`: the hidden ephemeral public key from the unencrypted header decoded to a curve point on Curve25519 (32 bytes).
* `shared secret`: the result of a key exchange between the above parameters (32 bytes).
* `public key`: the user's public key computed from their private key (32 bytes).
* `pre-shared key`: an optional symmetric key in the BLAKE2b key slot for post-quantum security (0 or 32 bytes).
* `hashed shared secret`: the key for BLAKE2b (32 bytes).
* `personalisation`: the UTF8 encoding of `"Kryptor.Personal"` (16 bytes).
* `salt`: the random salt from the unencrypted header (16 bytes).
* `info`: the hidden ephemeral public key from the unencrypted header (32 bytes).

#### Private and public key(s)

```
ephemeral shared secret = X25519(ephemeral private key, recipient public key)
hashed ephemeral shared secret = BLAKE2b-256(ephemeral shared secret || unhidden ephemeral public key || recipient public key, pre-shared key)

shared secret = X25519(sender private key, recipient public key)
hashed shared secret = BLAKE2b-256(shared secret || sender public key || recipient public key, pre-shared key)

input keying material = hashed ephemeral shared secret || hashed shared secret
header key = BLAKE2b-256(input keying material, personalisation, salt, info)
```

* `ephemeral private key`: a random ephemeral private key used for all recipients (32 bytes).
* `recipient public key`: the public key for a specified recipient (32 bytes).
* `ephemeral shared secret`: the result of a key exchange between the above parameters (32 bytes).
* `unhidden ephemeral public key`: the hidden ephemeral public key from the unencrypted header decoded to a curve point on Curve25519 (32 bytes).
* `pre-shared key`: an optional symmetric key in the BLAKE2b key slot for post-quantum security (0 or 32 bytes).
* `sender private key`: the user's private key (32 bytes).
* `shared secret`: the result of a key exchange between the user's static private key and the recipient's static public key (32 bytes).
* `sender public key`: the user's public key computed from their private key (32 bytes).
* `input keying material`: the concatenation of the hashed ephemeral and static shared secrets used as the key for BLAKE2b, following the [K one-way handshake pattern](https://neilmadden.blog/2018/11/26/public-key-authenticated-encryption-and-why-you-want-it-part-ii/) from the [Noise Protocol Framework](https://noiseprotocol.org/) (64 bytes).
* `personalisation`: the UTF8 encoding of `"Kryptor.Personal"` (16 bytes).
* `salt`: the random salt from the unencrypted header (16 bytes).
* `info`: the hidden ephemeral public key from the unencrypted header (32 bytes).

### File metadata header

```
file length || padded file name || free space || directory flag
```

* `file length`: the plaintext file length as a signed 64-bit integer converted to bytes in little-endian (8 bytes).
* `padded file name`: the UTF8 encoding of the file name padded using ISO/IEC 7816-4 padding (256 bytes). If file name encryption is not used, these bytes are filled with padding.
* `free space`: zeros that can be replaced by something in the future (27 bytes).
* `directory flag`: whether a directory is being encrypted as a Boolean converted to a single `0x01` or `0x00` byte, with `0x01` representing `true` (1 byte).

Encryption of the above is done using a key-committing version of ChaCha20-Poly1305. This commits to the key and nonce, preventing [partitioning oracle attacks](https://eprint.iacr.org/2020/1491).

```
commitment || ciphertext || tag = kcChaCha20-Poly1305(metadata header, nonce, file key, associated data)
```

* `metadata header`: the header above as the plaintext (292 bytes).
* `nonce`: a counter with all bytes starting at `0x00`, except the last byte is fixed at `0x00` until the final chunk when it becomes `0x01` (12 bytes).
* `file key`: the random file key (32 bytes).
* `associated data`: the key wrap header (640 bytes).

For decryption, if authentication fails, the file key **SHOULD** be zeroed and an error **MUST** be thrown, causing decryption of the file to stop.

### Payload

If a directory is specified, the contents **MUST** be converted to a ZIP file with no compression and treated like any other file. The ZIP file name **MUST** be the directory name plus the `.zip` extension.

If file name encryption is specified, the encrypted file **MUST** be given a randomly generated name consisting of lower/uppercase letters and numbers, and an extension **SHOULD NOT** be used. A file name length of 16 characters is **RECOMMENDED**. Otherwise, the encrypted file name **SHOULD** be the original file name plus `.bin`.

First, the [randomised padding scheme](https://github.com/samuel-lucas6/CovertPadding) from [Covert Encryption](https://github.com/covert-encryption/covert) is used to determine the amount of padding to encrypt given the plaintext file length. The proportion **SHOULD** be set to 10%, which ensures very small messages are padded to at least 50 bytes.

The plaintext file is then read and encrypted in 16 KiB chunks. Once the end of the file has been reached, whatever was previously read into the plaintext buffer is encrypted in chunks as padding until the padded length is reached. The final chunk can be less than 16 KiB.

Each chunk is encrypted using regular ChaCha20-Poly1305 with the file key, the counter nonce from the metadata header incremented in little-endian by one, and no associated data. For the final chunk, the last reserved byte of the nonce **MUST** be set to `0x01`, following the [STREAM](https://eprint.iacr.org/2015/189) construction.

```
ciphertext || tag = ChaCha20-Poly1305(chunk, nonce++, file key)
```

After encryption, if overwriting is specified, the encrypted file **SHOULD** be copied to the location of the plaintext file in an attempt to overwrite its contents.

For decryption, if authentication fails at any point, decryption **MUST** stop, the file key **SHOULD** be zeroed, and the plaintext output file **SHOULD** be deleted. Otherwise, if decryption succeeds and overwriting is specified, the encrypted file **SHOULD** be deleted.

## Signature file format

A signature file is treated as binary and **MUST** use the extension `.signature`. Signature files **SHOULD** be marked as read-only.

```
magic bytes || version || prehashed flag || file signature || comment || global signature
```

* `magic bytes`: the UTF8 encoding of `"SIGNATURE"` (9 bytes).
* `version`: a signed 16-bit integer converted to bytes in little-endian, which currently equals `{ 0x01, 0x00 }` (2 bytes).
* `prehashed flag`: whether the file is being prehashed as a Boolean converted to a single `0x01` or `0x00` byte, with `0x01` representing `true` (1 byte).
* `file signature`: the Ed25519 signature calculated over the file bytes or the BLAKE2b-512 hash of the file bytes (64 bytes).
* `comment`: the UTF8 encoding of a message to accompany the signature. By default, the comment is `"This file has not been tampered with."` (37 bytes).
* `global signature`: the Ed25519 signature calculated over all of the above concatenated together in the above order (64 bytes).

If a file is equal to or greater than 1 GiB in size, prehashing **SHOULD** be used automatically. Otherwise, the default **MUST** be no prehashing.

Before verifying a signature, incorrect magic bytes or an incorrect version **MUST** be rejected. Next, the global signature **MUST** be verified first. If invalid, `"Bad signature"` **MUST** be displayed and verification **MUST** stop.

If the global signature is valid, the file to verify should be read into memory or prehashed incrementally, and the file signature should be verified. If invalid, `"Bad signature"` **MUST** be displayed and no comment should be shown. Otherwise, `"Good signature"` **MUST** be shown, followed by the authenticated comment unless it only consists of whitespace.

## Asymmetric key format

### Public key

```
public key string = Base64(key algorithm || public key)
```

* `key algorithm`: either `{ 10, 239, 255 }` for Curve25519 or `{ 17, 223, 255 }` for Ed25519 so the Base64 string starts with `"Cu//"` or `"Ed//"` (3 bytes).
* `public key`: the public key for the random private key (32 bytes).

Strings that are not 48 characters in length or missing the correct key algorithm for the specified command-line option **MUST** be rejected.

Public key files **MUST** use the `.public` extension and contain the Base64 public key string on the first line. However, whitespace before/after or a space and comment after the public key string **MAY** be present and ignored.

### Private key

```
private key string = Base64(key algorithm || version || salt || encrypted private key)
```

* `key algorithm`: either `{ 10, 239, 255 }` for Curve25519 or `{ 17, 223, 255 }` for Ed25519 so the Base64 string starts with `"Cu//"` or `"Ed//"` (3 bytes).
* `version`: a signed 16-bit integer converted to bytes in little-endian, which currently equals `{ 0x02, 0x00 }` (2 bytes).
* `salt`: a random salt per private key (16 bytes).
* `encrypted private key`: a random private key (and the associated public key with Ed25519) encrypted using the key-committing version of ChaCha20-Poly1305 (80 or 112 bytes):

```
key = Argon2id(passphrase, salt)
commitment || ciphertext || tag = kcChaCha20-Poly1305(private key, nonce, key, associated data)
```

* `passphrase`: the UTF8 encoding of the user's passphrase (1+ bytes).
* `salt`: the random salt from the unencrypted header (16 bytes).
* `private key`: the random Curve25519 private key or the random Ed25519 seed concatenated with the associated public key (32 or 64 bytes).
* `nonce`: an all-zero nonce (12 bytes).
* `key`: the output from Argon2id (32 bytes).
* `associated data`: the key algorithm concatenated with the version (5 bytes).

Strings that are not 136 or 180 characters in length or missing the correct key algorithm for the specified command-line option **MUST** be rejected.

Private key files **MUST** use the `.private` extension and contain the Base64 private key string on the first line. However, whitespace before/after or a space and comment after the private key string **MAY** be present and ignored.

To be detected as a default private key, the file **MUST** be named either `encryption.private` or `signing.private` and stored in the `~/.kryptor` directory.

## Symmetric key format

### Pre-shared key

```
pre-shared key string = Base64(header || key)
```

* `header`: the bytes `{ 61, 34, 191 }` so the Base64 string starts with `"PSK/"` (3 bytes).
* `key`: a random symmetric key (32 bytes).

Strings that are not 48 characters in length or missing the correct header **MUST** be rejected.

### Keyfile

```
symmetric key = BLAKE2b-256(keyfile)
```

* `keyfile`: the entire file as bytes.

Keyfiles **MUST** be at least 32 bytes long. A random keyfile is just a random symmetric key, which is 32 bytes long, written to a file as bytes. Random keyfiles **MUST** be marked as read-only.


# Known limitations

## Unicode passwords

Kryptor uses [UTF-8](https://www.rfc-editor.org/rfc/rfc3629) to convert password strings/characters to bytes. Unfortunately, [Unicode normalization](https://www.unicode.org/reports/tr15/) is not applied, meaning the same Unicode character can get encoded in different ways depending on the operating system, keyboard, etc. This can cause issues decrypting files when non-ASCII characters are used in a password.

Sadly, there's no pretty solution to this problem. Even if you use Unicode normalization, you can [still](https://1passwordstatic.com/files/security/1password-white-paper.pdf) encounter this issue, so it's only a partial fix that annoyingly duplicates the password in memory. Confusingly, there are also multiple forms of normalization, with different organisations [not](https://pages.nist.gov/800-63-3/sp800-63b.html) [agreeing](https://datatracker.ietf.org/doc/html/rfc8265) on which one developers should use.

To eliminate the problem, you have to enforce [ASCII](https://www.rfc-editor.org/rfc/rfc20) characters, which some people are against since it's restrictive and reduces password entropy for the same number of characters. Furthermore, organisations like [NIST](https://pages.nist.gov/800-63-3/sp800-63b.html), the [IETF](https://datatracker.ietf.org/doc/html/rfc8265), and [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html) recommend supporting Unicode characters. However, people should be randomly generating passwords/passphrases using a [password manager](https://bitwarden.com/password-generator/) or [Diceware](https://en.wikipedia.org/wiki/Diceware) (even if you're going to memorise them), which results in ASCII passwords unless you go out of your way to use Unicode characters (e.g. a [non-English wordlist](https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md)). Additionally, some websites don't support Unicode passwords, and websites that do support Unicode passwords have password reset functionality in case there's an issue, which isn't possible with an offline application.

## Multi-recipient sender authentication

Kryptor currently only has sender authentication with a single recipient or if all recipients are honest. When sending a file to multiple recipients, if any are malicious or have had their private key compromised, they can create a new file that looks like it came from the original sender.

This is possible because all recipients have access to the same file key (to avoid encrypting the file repeatedly, which is slow and would require sending multiple files) and the wrapped keys aren't tied to that specific encrypted file payload (they can be decrypted independently of the payload).

Protecting against this requires processing the entire file in one go rather than or as well as in chunks, either using a signature scheme (outsider non-repudiation) or a MAC (insider non-repudiation). This is something that was actively avoided due to the benefits of chunking.

## 20 recipients

An indistinguishable file format means [more complicated parsing](https://en.wikipedia.org/wiki/PURB_\(cryptography\)#Encoding_and_decoding_PURBs), and you don't want to leak the number of recipients. As such, I decided to use a fixed header format like before. This is simple but less efficient, adds constant storage overhead regardless of how many recipients you have, and limits the number of recipients to 20. However, this was the [original](https://github.com/FiloSottile/age/issues/139) limit with age and should rarely be a problem.

## File metadata

Whilst encrypted files are intended to be indistinguishable from random data, if an attacker knows that the same file has been encrypted many times, they may be able to determine the unpadded file length.

The headers are also fixed in size, so there's a range of small file sizes that Kryptor doesn't produce. Thus, files of the minimum length could be seen as an indicator that Kryptor was used. However, dummy random files could be stored to address this type of problem.

Finally, the timestamps on encrypted files are currently untouched. This may change in the future, perhaps only when file name encryption is specified, but [proper](https://www.inversecos.com/2022/04/defence-evasion-technique-timestomping.html) [timestomping](https://attack.mitre.org/techniques/T1070/006/) is more complicated than simply [changing](https://docs.microsoft.com/en-us/dotnet/api/system.io.file.setcreationtime?view=net-6.0) the [standard timestamps](https://az4n6.blogspot.com/2014/10/timestomp-mft-shenanigans.html).

## Post-quantum security

The **asymmetric** algorithms in Kryptor aren't [post-quantum secure](https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/Brochure/quantum-safe-cryptography.html?nn=433196). However, a pre-shared key can be specified when encrypting with your private key to add post-quantum security. Alternatively, you can [keep your public key secret](https://crypto.stackexchange.com/q/119226/102678) (e.g., when encrypting files to yourself or only sharing it with people you trust via an [end-to-end encrypted messenger](https://signal.org/) with [disappearing messages](https://support.signal.org/hc/en-us/articles/360007320771-Set-and-manage-disappearing-messages) enabled).

Why a pre-shared key instead of post-quantum algorithms?

* Pre-shared keys are known to be post-quantum secure (when managed correctly). In contrast, various post-quantum schemes have been [proven](https://eprint.iacr.org/2022/214) [insecure](https://eprint.iacr.org/2022/975), with post-quantum cryptography having generally received less study.
* Supporting pre-shared keys is simple, whereas post-quantum or hybrid KEMs mean a protocol redesign.
* Standardisation is ongoing for both [post-quantum](https://csrc.nist.gov/projects/post-quantum-cryptography) and [hybrid](https://datatracker.ietf.org/doc/html/draft-connolly-cfrg-xwing-kem) crypto. In Kryptor, there's also KEM [obfuscation](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-kemeleon) and [signatures](https://csrc.nist.gov/projects/pqc-dig-sig) to consider.
* The cryptographic libraries used by Kryptor don't support post-quantum crypto yet, and it's preferable to have as few dependencies as possible. Therefore, waiting is sensible.
* Post-quantum crypto is far from the norm in practice today. For example, the [NSA](https://media.defense.gov/2022/Sep/07/2003071836/-1/-1/1/CSI_CNSA_2.0_FAQ_.PDF) and [UK NCSC](https://www.ncsc.gov.uk/guidance/pqc-migration-timelines) mention a deadline of 2035 for migration. As of December 2025, [BSI](https://www.bsi.bund.de/dok/study_status_quantum_computer) estimate cryptographically relevant quantum computers (CRQCs) will be available within 15 years (by 2040).

## Hardware support

[ChaCha20-Poly1305](https://www.rfc-editor.org/rfc/rfc8439) is not as fast as algorithms like [AEGIS](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-aegis-aead) and [Rocca-S](https://datatracker.ietf.org/doc/html/draft-nakano-rocca-s) with hardware support. However, it's still fast, doesn't require hardware support, is widely used, and the cryptography is unlikely to be a performance bottleneck compared to disk IO. With that said, ChaCha20-Poly1305 will likely be replaced in the future.

## Compromised machine

If an attacker has physical or remote access to your machine, they could retrieve sensitive data (e.g. encryption keys) whilst Kryptor is running. This is quite literally impossible to prevent.

However, Kryptor does attempt to zero out sensitive data as soon as possible from memory. With pinning, this should be guaranteed, but sometimes pinning can't be used. For example, non-interactive string inputs (e.g. pre-shared keys) can't be erased from memory and will unfortunately get [leaked](https://www.netmeister.org/blog/passing-passwords.html) into the process table/shell history.

[Ed25519](https://ed25519.cr.yp.to/) for digital signatures can also be susceptible to [fault attacks](https://eprint.iacr.org/2017/1014.pdf) when an attacker has physical or remote access to the machine. As this is generally only a concern for [embedded devices](https://cybermashup.files.wordpress.com/2017/10/practical-fault-attack-against-eddsa_fdtc-2017.pdf) and most mitigations are [slow and ineffective](https://eprint.iacr.org/2017/1014.pdf), this type of attack is typically not protected against.


# Changelog

This page attempts to follow the [Keep a Changelog](https://keepachangelog.com/) guidelines to make it easy to see what's changed in each version of Kryptor. Here are the types of changes:

* `Added` for new features.
* `Changed` for changes in existing functionality.
* `Deprecated` for soon-to-be removed features.
* `Removed` for now removed features.
* `Fixed` for any bug fixes.
* `Security` for vulnerabilities.

## v4.1.1

{% hint style="info" %}
For this release, the macOS binaries are not single file due to an [upstream issue](https://github.com/dotnet/sdk/issues/45903) with either the libsodium NuGet package or the .NET SDK. Therefore, you must keep the `libsodium.dylib` file in the same directory as the `kryptor` file. This will be fixed in the next release.
{% endhint %}

#### Added

* A warning when the user enters a passphrase containing Unicode characters because certain Unicode characters can be encoded in multiple ways depending on things like the OS/keyboard. This could affect reliable decryption of files/private keys (see [#78](https://github.com/samuel-lucas6/Kryptor/discussions/78)). It's recommended to use a password manager and randomly generate ASCII passwords/passphrases, which avoids this problem entirely.

#### Changed

* Updated to .NET 8 as .NET 6 is [no longer supported](https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core). I'm sorry for the delay; it's due to working on other projects, IRL events, and the upstream issue above.
* Updated the dependencies.
* The publish options are now in the [Kryptor.csproj](https://github.com/samuel-lucas6/Kryptor/blob/master/src/Kryptor/Kryptor.csproj) file, which simplifies [building from source](https://www.kryptor.co.uk/installation#building-kryptor-from-source).
* The `vcruntime140.dll` file required for libsodium to work on Windows is now hidden from the user.
* A workaround is in place to fix trimming [issues](https://github.com/natemcmaster/CommandLineUtils/issues/529) with CommandLineUtils after switching to .NET 8.

#### Fixed

* The delete key is now supported for deleting characters during interactive passphrase entry.

## v4.1.0

#### Added

* Support for changing a private key passphrase via `-m|--modify`.
* Support for a comment after a public/private key string in `.public`/`.private` files, either manually by opening such files in a text editor or by specifying `-c|--comment` during key pair generation. Note that these comments are not authenticated in any way.

#### Changed

* The `-a|--about` option has been replaced with `--version` to align with other tools.
* There's no longer a new line before output to the terminal.
* `Bad signature` now has an exit code of `-1`.
* Errors should be written to stderr.
* Updated dependencies/.NET.

#### Removed

* The `-u|--update` option to align with other tools. Just use a package manager.

## v4.0.1

#### Removed

* Support for v1 private and public keys. Please see the [v4.0.0 release](#v4.0.0) for more information.
* The dependencies for v1 key pairs.

#### Changed

* 'Password' has been renamed to 'passphrase'. The option is now `-p|--passphrase`.
* Random passphrases are now lowercase to allow easy typing.
* The public/private key file length checks have been moved.
* Various error messages have been shortened/rephrased.
* Updated dependencies and the .NET SDK/runtime.

#### Fixed

* `InsufficientMemoryException` if you don't have enough memory to use Argon2.

## v4.0.0

{% hint style="warning" %}
This release contains many **breaking changes**. If you're updating from a previous version of Kryptor, **please decrypt all of your files using your current version**.
{% endhint %}

#### Deprecated

* Support for the previous private key format will be removed in a future release. The format is automatically updated when you use your private key for the first time.
* Similarly, support for the previous public key format will be removed in a future release. Again, the format is automatically updated when your private key is. Alternatively, you can use `-r|--recover`.

{% hint style="info" %}
You should republish/share your new **public** key string/file in place of the old one and back up your new private key file.

However, your key pair doesn't actually change. The private key gets re-encrypted, and the public key has a different header, so the string looks different.
{% endhint %}

#### Added

* The new encryption format is intended to be indistinguishable from random to limit metadata. This means no identifiable headers and randomised padding. Few tools do this (e.g. age doesn't care about metadata). A huge thanks to [Monocypher](https://monocypher.org/) for Elligator2 and [Covert Encryption](https://github.com/covert-encryption/covert) for inspiration and their randomised padding scheme.
* Directories are now converted into ZIP files (with no compression for speed) before being encrypted. This means an encrypted directory is indistinguishable from an encrypted file.
* You can now specify up to 20 public key recipients for a single file. It used to be limited to 1 recipient.
* Support for pre-shared keys has been added to provide optional post-quantum security when encrypting a file to someone's public key.
* Pre-shared keys can also be used for file encryption alone like keyfiles. This is a faster alternative to password-based encryption.
* Some of the encrypted metadata header is currently empty, which will eventually be used for storing the file timestamps and cross-platform attributes. The timestamps of the encrypted file can then be altered to further limit metadata. This all needs more thought and some study of digital forensics though.
* You can now sign each file in a directory. This is handy for signing software releases quickly.
* Multiple signatures can now be verified at once.
* Multiple custom signature paths can be specified at once for signing.
* Key pairs can be generated non-interactively.
* macOS ARM64 and Linux ARM64 are now officially supported and can be updated using `-u|--update`.
* A `-1` exit code is returned when an error occurs.

#### Changed

* Switched from XChaCha20-BLAKE2b to ChaCha20-Poly1305 for encryption. It's faster and standardised. The padding fix is applied when encrypting the metadata header to add key commitment.
* Now using a little-endian counter nonce and the [STREAM](https://eprint.iacr.org/2015/189) construction. A random nonce is unnecessary in this use case, especially since it was being incremented for each chunk before anyway. Then the STREAM construction has become unofficially standardised and is more flexible.
* The Argon2 parameters have been reduced as they were excessive and much slower than I thought on other machines. This speeds up password-based key derivation whilst retaining a good security margin.
* Passwords are no longer prehashed. This was done previously for consistency with how peppering was done.
* The pepper is now used as input keying material for key derivation after password hashing instead of being used as a key prior to password hashing. This was done to save an extra call to BLAKE2b.
* Using a keyfile alone no longer uses Argon2 as random keyfiles are high in entropy.
* Keyfile hashing has been made compatible with the pre-shared key format, and random keyfiles are now 32 bytes in size instead of 64 bytes.
* The previous authentication tag is no longer used as associated data. This was unnecessary and came with a performance cost.
* Both public keys are included in the key derivation for shared secrets.
* The long-term and ephemeral shared secrets are concatenated the other way around to comply with the [Noise Protocol Framework](https://noiseprotocol.org/).
* The ephemeral public key is used as info in the wrap key derivation instead of being used as associated data. With passwords/pre-shared keys, this is actually an additional 256-bit random salt since no public key is used.
* The encrypted metadata header has been rearranged. The file length is now stored instead of the amount of padding. The file name is also stored there and padded to 256 bytes.
* Private keys are now encrypted using ChaCha20-Poly1305 with the padding fix for key commitment and an all zero nonce since the key is unique.
* The public/private key headers have been changed to make the algorithm readable at the beginning of the string (`Cu//` for Curve25519 and `Ed//` for Ed25519).
* [Geralt](https://www.geralt.xyz/), my libsodium binding, is now used instead of [libsodium-core](https://github.com/ektrah/libsodium-core).
* Thanks to Geralt, spans are used instead of byte arrays when possible for improved performance and fewer allocations.
* Sensitive bytes are pinned when possible/sensible so they can be zeroed properly.
* FileStream performance should be improved as the buffer size is now adjusted based on the size of the file and output files are preallocated on disk.
* Random file name generation has been improved due to Geralt.
* File names are checked for invalid characters to prevent problems storing the file name/decrypting cross-platform.
* [libsodium](https://doc.libsodium.org/) is used for Base64 encoding, which is done in constant time.
* Lots of wrapper classes have been removed due to Geralt.
* It's now clearer when you're being asked for a private key password compared to a regular password.
* 'Directory' is used instead of 'folder' in messages.
* Some error messages have been made more consistent.
* Blue is no longer used for any messages; orange (technically 'dark yellow') is used instead.
* The initial validation has been improved.
* Code has been spaced out for readability, Geralt constants are used, and the if/loop braces style has been changed.

#### Fixed

* Early returns when an exception was thrown during decryption, potentially preventing some files from being processed.
* An empty file name in error messages when the path ends in a directory/volume separator character.
* An unhandled directory `UnauthorizedAccessException` during empty directory validation.
* 'name (2)' getting restored to 'name (3)' instead of 'name (2) (2)' if 'name (2)' exists decrypting a file that had its name encrypted. I don't like this numbering, but it's the safest approach I can come up with.
* Removing double digit file name numbers (e.g. 'name (10)').
* The resources are now embedded when building as well as publishing.
* The publish profiles for ARM64.
* Probably some other stuff I've neglected to mention.

## v3.1.1

#### Fixed

* Files with a certain length would accidentally have the last 16,384 byte chunk removed during decryption due to a mathematical mistake that wasn't detected during any of my testing because of the file sizes of my test files. **I'm extremely sorry for any trouble this causes**. It's rather crushing as a maintainer when something like this happens, but I would like to again thank the person who reported this issue.
* Chocolatey installs hopefully won't have a `vcruntime140.dll` extraction error anymore. I'm also working on adding the package equivalent of this file as a dependency.

#### Changed

* Encrypted files are no longer deleted by default when decrypting. The `-o|--overwrite` option now needs to be specified to do this.
* Illegal characters from file/directory names are now rejected with an error rather than being silently removed. As this was taken from a v4 commit, with v4 encrypting directories differently, this change won't work with subdirectories.
* 'Decrypting private key...' is displayed instead of 'Deriving encryption key from password...' for private key decryption to avoid confusion.
* The dependencies and `vcruntime140.dll` files have been updated.

#### Added

* A message saying to back up the private key file when generating a new key pair.
* A message saying to back up the keyfile when generating a random keyfile.
* Publish profiles for Linux ARM64 and macOS ARM64 to build from source more easily. Builds for these platforms may be included in releases for v4 onwards.

## v3.1.0

#### Security

* Patched a *potential* [directory traversal attack](https://en.wikipedia.org/wiki/Directory_traversal_attack) **vulnerability** when decrypting a file/folder someone sent you that contained a malicious file name. I say *potential* because I have not attempted to exploit the attack, and this type of vulnerability primarily affects web applications. However, it has also affected [ZIP libraries](https://snyk.io/research/zip-slip-vulnerability) and [encryption libraries](https://cure53.de/pentest-report_streamcryptor.pdf).

#### Added

* Non-interactive password support, meaning you can now do `-p:"[password]"` instead of entering the password interactively. To randomly generate a password, you can type a space: `-p:" "`. However, entering a password interactively is still more secure as it hides your password and avoids using a string variable.
* Exporting the recovered public key from `-r|--recover` to a `.public` file if one does not exist in the same directory as the `.private` key file.
* Automatic `vcruntime140.dll` extraction on Windows to always ensure that the [libsodium](https://doc.libsodium.org/) cryptographic library is portable.
* `-u|--update` can now install updates for you. This checks the download signatures automatically, ensuring [authenticity and integrity](https://en.wikipedia.org/wiki/Digital_signature), and replaces the `kryptor` executable in place.
* Coloured error messages (red) and successful messages (green). Blue and orange are also used but rarely.
* A note in `-h|--help` about having to surround file names/paths with "speech marks".

#### Changed

* Switched to [.NET 6](https://devblogs.microsoft.com/dotnet/announcing-net-6/).
* The `-f|--obfuscate` option has been renamed to `-n|--names`. I will now be calling it file name encryption rather than file name obfuscation.
* [Path.GetRandomFileName()](https://docs.microsoft.com/en-us/dotnet/api/system.io.path.getrandomfilename?view=net-6.0) is no longer being used because the documentation was updated to remove the claim that it is cryptographically secure.
* It is now possible to sign `.signature` files.
* The spacing in the output text has been changed to try and make things more readable.
* File names in the output text are now surrounded by "speech marks" to help distinguish them from other text.
* Lots of code improvements to reduce the line count.
* Various error messages have been improved.

#### Fixed

* The authenticated comment is no longer shown if it is empty when verifying a signature.
* Folders containing only empty subdirectories are now detected as containing no files, leading to an error.
* [String.Replace()](https://docs.microsoft.com/en-us/dotnet/api/system.string.replace?view=net-6.0) is no longer used for file paths since it may cause problems by removing multiple parts of a string.
* Illegal file name characters are now removed from the file name before it is stored during file name encryption because this could cause issues decrypting the file on another operating system. This may be switched to an error in the future.
* The total count should now be correct when decrypting a directory with an incorrect salt length.

## v3.0.4

#### Added

* A '`Deriving encryption key from the password...`' message to explain the key derivation delay.
* '`Encrypting [file] => [file.kryptor]...`' and '`Decrypting [file.kryptor] => [file]...`' messages to indicate that Kryptor is doing something rather than frozen when encrypting/decrypting large files.
* '`Commencing encryption of [directory] directory...`' and `'Commencing decryption of [directory] directory...`' messages to help separate the folder encryption output from the file encryption output.

#### Changed

* Now displaying the name of randomly generated keyfiles.
* Various code improvements, although there is still lots to go through. More code improvements will be coming in the next release.

#### Fixed

* A bug when encrypting/decrypting 0-byte files (please see [#27](https://github.com/samuel-lucas6/Kryptor/issues/27)).
* A bug related to renaming duplicated files (please see [#28](https://github.com/samuel-lucas6/Kryptor/issues/28)).
* The `-s|--sign` and `-v|--verify` validation has been improved.
* An `UnauthorizedAccessException` when trying to randomly generate a keyfile to a path where a keyfile by that name already exists.

## v3.0.3

#### Added

* Empty directory validation.
* A validation check for a salt file when encrypting directories.
* A validation check for a salt file when decrypting directories using a private key.
* Added a private key encryption example to `-h|--help` to clarify that you do not need to specify your public key when encrypting files for yourself.

#### Removed

* Error logging. It is not needed, and the log file never got shared in bug reports.

#### Changed

* Validation for file paths now happens before being asked to enter a password.
* Simplified the examples in `-h|--help`.
* Reworded several error messages.

#### Fixed

* The total number of files count is no longer reduced when a file is invalid (e.g. already encrypted).​

## v3.0.2-beta

#### Changed

* Now displaying the copied/backup directory name when encrypting a directory without the `-o|--overwrite` option.
* Now renaming the copied/backup directory to the original directory name if possible (if `-f|--obfuscate` is specified and `-o|--overwrite` is not). The directory cannot be renamed otherwise because two directories cannot have the same path.

#### Fixed

* The `UnauthorizedAccessException` when overwriting read-only files.
* The `UnauthorizedAccessException` when storing the file name if the file is read-only.
* Now restoring the `-o|--overwrite` setting if an exception occurs during directory encryption.

## v3.0.1-beta

#### Changed

* Improved `-a|--about`.​

#### Fixed

* Visual C++ runtime issues on Windows by including a `vcruntime140.dll` file in the ZIP file.

## v3.0.0-beta

#### Added

* Authenticated hybrid file encryption.
* Masked password entry with support for random passphrase generation.
* File signing functionality.
* Implemented separate encryption and signing asymmetric keys.
* Implemented export functionality for asymmetric key pairs.
* Private keys are encrypted for protection at rest.

#### Changed

* Switched to chunked AEAD encryption.
* New KEK/DEK design.
* Now using fixed Argon2 parameters.
* Faster directory encryption.
* No longer working on a GUI version - not enough time to work on two different versions, tricky to implement in a GUI format, not cross-platform, and more confusing for the user downloading the software.


# Roadmap

## Suggesting a new feature

If you'd like to suggest a new feature, feel free to open a feature request on [GitHub](https://github.com/samuel-lucas6/Kryptor/issues/new/choose). However, please read the [Goals](/#goals) section first.

## v4.1.2

* Fix the macOS binaries not embedding the libsodium library. This is an [upstream issue](https://github.com/dotnet/sdk/issues/45903) with either the libsodium NuGet package or the .NET SDK.
* Create a [WinGet](https://github.com/microsoft/winget-cli) package.
* Improve the [Chocolatey package](https://github.com/samuel-lucas6/Kryptor-Chocolatey).

## v4.2.0

* Try to implement some automated testing.
* Investigate guarded heap allocations for byte array secrets.
* Investigate better erasure of secrets (e.g. pinning and wiping strings/char arrays).
* Support YubiKeys via the [.NET YubiKey SDK](https://docs.yubico.com/yesdk/). A decision needs to be made whether to use PIV, which requires the 5.7 firmware for X25519/Ed25519, or challenge-response, which is symmetric, allows backups, and works with older YubiKeys. Using PIV requires waiting for updates to the SDK and me buying a new YubiKey. Supporting both is probably an overcomplicated UX.
* Address build warnings.
* Investigate support for `win-arm64`.

## Long run

### Breaking

* Address Unicode normalization.
* Add multi-recipient sender authentication if possible.
* Switch to [AEGIS-256](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-aegis-aead) for encryption. Much faster, well analysed, and fully committing (assuming the associated data is empty or hashed).
* Change the KDF to either HKDF (needlessly inefficient and won't be using BLAKE2), BLAKE3 (another dependency), or one of [NIST's KDFs](https://github.com/samuel-lucas6/NIST-KBKDFs) using BLAKE2b (not widely used).
* Use Ed25519ph for prehashing in v2 of the signature format. It wasn't available in the [previous](https://github.com/ektrah/libsodium-core) libsodium binding.
* Reconsider [hedged signatures](https://soatok.blog/2020/05/03/hedged-signatures-with-libsodium-using-dhole/).
* Reconsider random nonces (e.g. for private key encryption).
* Could switch to libsodium's [secretstream](https://www.geralt.xyz/authenticated-encryption/stream-and-file-encryption) API for file encryption. This wasn't available in the [previous](https://github.com/ektrah/libsodium-core) libsodium binding. One [may](https://doc.libsodium.org/roadmap) be coming for AEGIS. Or just write a [STREAM](https://eprint.iacr.org/2015/189) library, which I should do anyway.
* Support more recipients/change the key wrap header approach.
* Remove free space from the file metadata header.
* Consider ASCII armour/Minisign style detached signature files.
* Will need to eventually switch to post-quantum asymmetric primitives - KEM and signing. This requires waiting for [further analysis](https://csrc.nist.gov/Projects/post-quantum-cryptography/round-4-submissions) and [library support](https://github.com/jedisct1/libsodium/discussions/1275) (likely years). The UX will be terrible.
* ~~Consider supporting unencrypted private keys for non-interactive use cases.~~

### Non-breaking

* Support non-detached signatures?
* Confirm `y/n` before `-o|--overwrite`?
* Do a progress bar like Docker?
* Add a `-b`|`--batch` option for automated use cases that rejects interactive input and terminates the program when an error/exception occurs?
* Consider `-q|--quiet` to hide output.
* Investigate [globbing](https://github.com/samuel-lucas6/Kryptor/discussions/45).
* Investigate [stdin](https://clig.dev/) support.
* Display `-h|--help` with no user input if possible.
* Have a `trusted` folder for public keys, with separate files or folders for encryption/signing? No idea what the UX would be.
* ~~Add support for generating~~ [~~vanity address~~](https://github.com/warner/wireguard-vanity-address) ~~public keys?~~


