[![Cocoon](https://github.com/fadeevab/cocoon/workflows/Cocoon/badge.svg)](https://github.com/fadeevab/cocoon) [![crates.io](https://img.shields.io/crates/v/cocoon.svg)](https://crates.io/crates/cocoon) [![docs.rs](https://docs.rs/cocoon/badge.svg)](https://docs.rs/cocoon/) [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/fadeevab/cocoon/LICENSE) [![coverage](https://coveralls.io/repos/github/fadeevab/cocoon/badge.svg?branch=main)](https://coveralls.io/github/fadeevab/cocoon?branch=main) # Cocoon Cocoon format `MiniCocoon` and `Cocoon` are protected containers to wrap sensitive data with strong [encryption](#cryptography) and format validation. A format of `MiniCocoon` and `Cocoon` is developed for the following practical cases: 1. As an _encrypted file format_ to organize simple secure storage: 1. Key store. 2. Password store. 3. Sensitive data store. 2. For _encrypted data transfer_: * As a secure in-memory container. `Cocoon` is developed with security in mind. It aims to do only one thing and do it flawlessly. It has a minimal set of dependencies and a minimalist design to simplify control over security aspects. It's a pure Rust implementation, and all dependencies are pure Rust packages with disabled default features. # Use Case Whenever you need to transmit and store data securely you reinvent the wheel: you have to take care of how to encrypt data properly, how to handle randomly generated buffers, then how to get data back, parse, and decrypt. Instead, you can use `MiniCocoon` and `Cocoon`. # Basic Usage ## 📌 Wrap/Unwrap One party wraps private data into a container using `MiniCocoon::wrap`. Another party (or the same one, or whoever knows the key) unwraps data out of the container using `MiniCocoon::unwrap`. `MiniCocoon` is preferred against `Cocoon` in a case of simple data encryption because it generates a container with a smaller header without version control, and also it allows to wrap data sequentially (wrap, wrap, wrap!) without performance drop because of KDF calculation. ```rust let mut cocoon = MiniCocoon::from_key(b"0123456789abcdef0123456789abcdef", &[0; 32]); let wrapped = cocoon.wrap(b"my secret data")?; assert_ne!(&wrapped, b"my secret data"); let unwrapped = cocoon.unwrap(&wrapped)?; assert_eq!(unwrapped, b"my secret data"); ``` ## 📌 Dump/Parse You can store data to file. Put data into `Vec` container, the data is going to be encrypted _in place_ and stored in a file using the "cocoon" [format](#cocoon). `Cocoon` is preferred as a long-time data storage, it has an extended header with a magic number, options, and version control. ```rust let mut data = b"my secret data".to_vec(); let mut cocoon = Cocoon::new(b"password"); cocoon.dump(data, &mut file)?; let data = cocoon.parse(&mut file)?; assert_eq!(&data, b"my secret data"); ``` ## 📌 Encrypt/Decrypt You can encrypt data in place and avoid re-allocations. The method operates with a detached meta-data (a container format prefix) in the array on the stack. It is suitable for "`no_std`" build and whenever you want to evade re-allocations of a huge amount of data. You have to care about how to store and transfer a data length and a container prefix though. Both `MiniCocoon` and `Cocoon` have the same API, but prefixes are of different sizes. `MiniCocoon` doesn't have the overhead of generating KDF on each encryption call, therefore it's recommended for simple sequential encryption/decryption operations. ```rust let mut data = "my secret data".to_owned().into_bytes(); let mut cocoon = MiniCocoon::from_key(b"0123456789abcdef0123456789abcdef", &[0; 32]); let detached_prefix = cocoon.encrypt(&mut data)?; assert_ne!(data, b"my secret data"); cocoon.decrypt(&mut data, &detached_prefix)?; assert_eq!(data, b"my secret data"); ``` # Study Case You implement a database of secrets that must be stored in an encrypted file using a user password. There are a lot of ways how your database can be represented in memory and how it could be serialized. You handle these aspects on your own, e.g. you can use `HashMap` to manage data and use `borsh`, or `bincode`, to serialize the data. You can even compress a serialized buffer before encryption. In the end, you use `Cocoon` to put the final image into an encrypted container. ```rust use borsh::{BorshDeserialize, BorshSerialize}; use cocoon::{Cocoon, Error}; use std::collections::HashMap; use std::fs::File; // Your data can be represented in any way. #[derive(BorshDeserialize, BorshSerialize)] struct Database { inner: HashMap, } fn main() -> Result<(), Error> { let mut file = File::create("target/test.db")?; let mut db = Database { inner: HashMap::new() }; // Over time you collect some kind of data. db.inner.insert("my.email@example.com".to_string(), "eKPV$PM8TV5A2".to_string()); // You can choose how to serialize data. Also, you can compress it. let encoded = db.try_to_vec().unwrap(); // Finally, you want to store your data secretly. // Supply some password to Cocoon: it can be any byte array, basically. // Don't use a hard-coded password in real life! // It could be a user-supplied password. let mut cocoon = Cocoon::new(b"secret password"); // Dump the serialized database into a file as an encrypted container. let container = cocoon.dump(encoded, &mut file)?; // Let's look at how to decrypt the container and parse it back. let mut file = File::open("target/test.db").unwrap(); let encoded = cocoon.parse(&mut file).unwrap(); let decoded = Database::try_from_slice(&encoded).unwrap(); Ok(()) } ``` # Cryptography 256-bit cryptography is chosen as a `Cocoon` baseline. | Cipher (AEAD) | Key Derivation Function (KDF) | |-------------------|----------------------------------| | Chacha20-Poly1305 | PBKDF2-SHA256: 100000 iterations | | AES256-GCM | | * Key: 256-bit. * Salt for KDF: random 128-bit + predefined part. * Nonce for encryption: random 96-bit. Key derivation parameters comply with NIST SP 800-132 recommendations (salt, iterations), and cipher parameters (key, nonce, length) fit requirements of a particular cipher. AEAD is chosen in order to authenticate encrypted data together with an unencrypted header. # Zeroization Encryption key is wrapped into zeroizing container (provided by `zeroize` crate), which means that the key is erased automatically once it is dropped. # How It Works See more implementation details on [![docs.rs](https://docs.rs/cocoon/badge.svg)](https://docs.rs/cocoon/), e.g. 1. the process of [container creation](https://docs.rs/cocoon/#container-creation), 2. customizable [crate features](https://docs.rs/cocoon/#crate-features), 3. and of course [API](https://docs.rs/cocoon/#cocoon).