├── .github └── workflows │ └── basic.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── examples ├── read.rs ├── readme.rs └── write.rs └── src ├── bufread.rs ├── lib.rs ├── read.rs ├── tests ├── issue_9.rs ├── mod.rs └── random_read.rs └── write.rs /.github/workflows/basic.yml: -------------------------------------------------------------------------------- 1 | name: Linux Build & Test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2-beta 12 | - name: Build 13 | run: cargo build --verbose 14 | - name: Run tests 15 | run: cargo test --verbose 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cryptostream" 3 | version = "0.3.2" 4 | authors = ["Mahmoud Al-Qudsi ", 5 | "NeoSmart Technologies "] 6 | description = "Transparent encryption and decryption for Read and Write streams" 7 | homepage = "https://github.com/neosmart/cryptostream" 8 | repository = "https://github.com/neosmart/cryptostream" 9 | readme = "README.md" 10 | keywords = ["encryption", "decryption", "cryptography", "cryptostream", "aes"] 11 | categories = ["cryptography"] 12 | license = "MIT" 13 | edition = "2018" 14 | 15 | [dependencies] 16 | openssl = "0.10.41" 17 | 18 | [features] 19 | # default = [ "openssl-vendored" ] 20 | openssl-vendored = [ "openssl/vendored" ] 21 | 22 | [dev-dependencies] 23 | base64 = "0.13.0" 24 | rand = { version = "0.8.5", features = [ "min_const_gen" ] } 25 | size = "0.4.0" 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Developed and maintained by Mahmoud Al-Qudsi 4 | Copyright (c) 2018-2022 NeoSmart Technologies 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # CargoMake by NeoSmart Technologies 2 | # Written and maintained by Mahmoud Al-Qudsi 3 | # Released under the MIT public license 4 | # Obtain updates from https://github.com/neosmart/CargoMake 5 | 6 | COLOR ?= always # Valid COLOR options: {always, auto, never} 7 | CARGO = cargo --color $(COLOR) 8 | 9 | .PHONY: all bench build check clean doc install publish run test update 10 | 11 | all: build 12 | 13 | bench: 14 | @$(CARGO) bench 15 | 16 | build: 17 | @$(CARGO) build 18 | 19 | check: build test 20 | 21 | clean: 22 | @$(CARGO) clean 23 | 24 | doc: 25 | @$(CARGO) doc 26 | 27 | install: build 28 | @$(CARGO) install 29 | 30 | publish: 31 | @$(CARGO) publish 32 | 33 | run: build 34 | @$(CARGO) run 35 | 36 | test: build 37 | @$(CARGO) test 38 | 39 | update: 40 | @$(CARGO) update 41 | 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust Cryptostream Crate 2 | 3 | [![crates.io](https://img.shields.io/crates/v/cryptostream.svg)](https://crates.io/crates/cryptostream) [![docs.rs](https://docs.rs/cryptostream/badge.svg)](https://docs.rs/crate/cryptostream) 4 | 5 | `cryptostream` provides a rust equivalent to the [.NET 6 | Cryptostream](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptostream) 7 | class, providing an efficient and easy solution to on-the-fly encryption or decryption of existing 8 | `Read` or `Write` resources. Cryptography is provided via [rust-openssl](https://github.com/sfackler/rust-openssl) 9 | and is fully configurable. 10 | 11 | ## What is a Cryptostream? 12 | 13 | In brief, a 14 | [Cryptostream](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptostream) 15 | is a wrapper around a stream (in rust parlance, a `Read` or `Write` type) that transparently 16 | encrypts or decrypts the underlying contents. After creating an instance of a `Cryptostream` with 17 | the cipher, key, and IV specified, bytes written to or read from the Cryptostream are the same as 18 | the `Read` or `Write` stream it is wrapping, only additionally encrypted or decrypted. It makes 19 | handling encrypted sources or destinations a breeze, and requires virtually no changes to your 20 | existing pipeline - it's just a `Read` or `Write`, like any other. 21 | 22 | ## Crate Design 23 | 24 | As rust (for better or for worse) lacks a `Stream` type, `cryptostream` has been implemented in both 25 | encryption and decryption modes twice, once as a `Read` impl and once as a `Write` impl (design cues 26 | taken from the `flate2` crate), with a bonus `BufRead` impl thrown in for good measure. This means 27 | that for any combination of available [ciphertext|plaintext] and desired [read|write] application, 28 | one of the `cryptostream` impls should match your usecase. A `cryptostream` should be created 29 | matching the type of resource you wish to consume (in case source data is a `Read` impl) or the type 30 | of resource you wish to create (in case destination is a `Write` impl). 31 | 32 | Implementations have been grouped by trait into namespace and have names conveying their 33 | applications: 34 | 35 | * `cryptostream::read::Encryptor` 36 | * `cryptostream::read::Decryptor` 37 | * `cryptostream::write::Encryptor` 38 | * `cryptostream::write::Decryptor` 39 | 40 | ## `Read` vs `Write` Cryptostreams 41 | 42 | The difference between the `Read` and `Write` variants of `cryptostream` are perhaps best 43 | illustrated by example. In both of the following examples, we will be decrypting ciphertext, however 44 | in one case we need to use `read::Decryptor` and in the other `write::Decryptor`. 45 | 46 | In the first case, we have a `Read` source which contains the bytes we need to decrypt, and we wish 47 | to obtain the equivalent plaintext in memory to later perform some operation with in its decoded 48 | state: 49 | 50 | ```rust 51 | 52 | // This is the cipher text, base64-encoded to avoid any whitespace munging. In this 53 | // contrived example, we are using a binary `Vec` as the `Read` source containing 54 | // the encrypted data; in practice it could be a binary file, a network stream, or 55 | // anything else. 56 | let src: Vec = decode(concat!( 57 | "vuU+0SXFWQLu8vl/o1WzmPCmf7x/O6ToGQ162Aq2CHxcnc/ax/Q8nTbRlNn0OSPrFuE3yDdO", 58 | "VC35RmwtUIlxKIkWbnxJpRF5yRJvVByQgWX1qLW8DfMjRp7gVaFNv4qr7G65M6hbSx6hGJXv", 59 | "Q6s1GiFwi91q0V17DI79yVrINHCXdBnUOqeLGfJ05Edu+39EQNYn4dky7VdgTP2VYZE7Vw==", 60 | )) 61 | .unwrap(); 62 | let key: Vec<_> = decode("kjtbxCPw3XPFThb3mKmzfg==").unwrap(); 63 | let iv: Vec<_> = decode("dB0Ej+7zWZWTS5JUCldWMg==").unwrap(); 64 | 65 | // The source can be anything implementing `Read`. In this case, a simple &[u8] slice. 66 | let mut decryptor = 67 | read::Decryptor::new(src.as_slice(), Cipher::aes_128_cbc(), &key, &iv).unwrap(); 68 | 69 | let mut decrypted = [0u8; 1024]; // a buffer to decrypt into 70 | let mut bytes_decrypted = 0; 71 | 72 | loop { 73 | // Just read from the `Decryptor` as if it were any other `Read` impl, 74 | // the decryption takes place automatically. 75 | let read_count = decryptor.read(&mut decrypted[bytes_decrypted..]).unwrap(); 76 | bytes_decrypted += read_count; 77 | if read_count == 0 { 78 | break; 79 | } 80 | } 81 | 82 | println!("{}", String::from_utf8_lossy(&decrypted)); 83 | 84 | ``` 85 | 86 | Now what about if you want to _write out_ the decrypted contents instead of _read_ them, but still 87 | wish to perform decryption all the same? 88 | 89 | ```rust 90 | 91 | // Starting again with the same encrypted bytestream, encoded as base64: 92 | let src: Vec = decode(concat!( 93 | "vuU+0SXFWQLu8vl/o1WzmPCmf7x/O6ToGQ162Aq2CHxcnc/ax/Q8nTbRlNn0OSPrFuE3yDdO", 94 | "VC35RmwtUIlxKIkWbnxJpRF5yRJvVByQgWX1qLW8DfMjRp7gVaFNv4qr7G65M6hbSx6hGJXv", 95 | "Q6s1GiFwi91q0V17DI79yVrINHCXdBnUOqeLGfJ05Edu+39EQNYn4dky7VdgTP2VYZE7Vw==" 96 | )) 97 | .unwrap(); 98 | let key: Vec<_> = decode("kjtbxCPw3XPFThb3mKmzfg==").unwrap(); 99 | let iv: Vec<_> = decode("dB0Ej+7zWZWTS5JUCldWMg==").unwrap(); 100 | 101 | // The destination can be any object implementing `Write`: in this case, a `Vec`. 102 | let mut decrypted = Vec::new(); 103 | 104 | // When a `cryptostream` is dropped, all buffers are flushed and it is automatically 105 | // finalized. We can either call `drop()` on the cryptostream or put its usage in a 106 | // separate scope. 107 | { 108 | let mut decryptor = 109 | write::Decryptor::new(&mut decrypted, Cipher::aes_128_cbc(), &key, &iv).unwrap(); 110 | 111 | let mut bytes_decrypted = 0; 112 | 113 | while bytes_decrypted != src.len() { 114 | // Just write encrypted ciphertext to the `Decryptor` instance as if it were any 115 | // other `Write` impl. Decryption takes place automatically. 116 | let write_count = decryptor.write(&src[bytes_decrypted..]).unwrap(); 117 | bytes_decrypted += write_count; 118 | } 119 | } 120 | 121 | // The underlying `Write` instance is only guaranteed to contain the complete and 122 | // finalized contents after the cryptostream is either explicitly finalized with a 123 | // call to `Cryptostream::finish()` or when it's dropped (either at the end of a scope 124 | // or via an explicit call to `drop()`, whichever you prefer). 125 | println!("{}", String::from_utf8_lossy(&decrypted)); 126 | 127 | ``` 128 | -------------------------------------------------------------------------------- /examples/read.rs: -------------------------------------------------------------------------------- 1 | use base64::decode; 2 | use cryptostream::read; 3 | use openssl::symm::Cipher; 4 | use std::io::Read; 5 | 6 | fn main() { 7 | let src: Vec = 8 | decode("vuU+0SXFWQLu8vl/o1WzmPCmf7x/O6ToGQ162Aq2CHxcnc/ax/Q8nTbRlNn0OSPrFuE3yDdOVC35RmwtUIlxKIkWbnxJpRF5yRJvVByQgWX1qLW8DfMjRp7gVaFNv4qr7G65M6hbSx6hGJXvQ6s1GiFwi91q0V17DI79yVrINHCXdBnUOqeLGfJ05Edu+39EQNYn4dky7VdgTP2VYZE7Vw==").unwrap(); 9 | let key: Vec<_> = decode("kjtbxCPw3XPFThb3mKmzfg==").unwrap(); 10 | let iv: Vec<_> = decode("dB0Ej+7zWZWTS5JUCldWMg==").unwrap(); 11 | 12 | // the source can be any object implementing `Read`. In this case, a simple &[u8] slice. 13 | let mut decryptor = 14 | read::Decryptor::new(src.as_slice(), Cipher::aes_128_cbc(), &key, &iv).unwrap(); 15 | 16 | let mut decrypted = [0u8; 1024]; // a buffer to decrypt into 17 | let mut bytes_decrypted = 0; 18 | 19 | loop { 20 | // Just read from `Decryptor` as if it were any other `Read` impl. Decryption 21 | // is automatic. 22 | let read_count = decryptor.read(&mut decrypted[bytes_decrypted..]).unwrap(); 23 | bytes_decrypted += read_count; 24 | if read_count == 0 { 25 | break; 26 | } 27 | } 28 | 29 | println!("{}", String::from_utf8_lossy(&decrypted)); 30 | } 31 | -------------------------------------------------------------------------------- /examples/readme.rs: -------------------------------------------------------------------------------- 1 | use base64::decode; 2 | use cryptostream::{read, write}; 3 | use openssl::symm::Cipher; 4 | use std::io::prelude::*; 5 | 6 | fn main() { 7 | f1(); 8 | f2(); 9 | } 10 | 11 | fn f1() { 12 | // This is the cipher text, base64-encoded to avoid any whitespace munging. In this 13 | // contrived example, we are using a binary `Vec` as the `Read` source containing 14 | // the encrypted data; in practice it could be a binary file, a network stream, or 15 | // anything else. 16 | let src: Vec = decode(concat!( 17 | "vuU+0SXFWQLu8vl/o1WzmPCmf7x/O6ToGQ162Aq2CHxcnc/ax/Q8nTbRlNn0OSPrFuE3yDdO", 18 | "VC35RmwtUIlxKIkWbnxJpRF5yRJvVByQgWX1qLW8DfMjRp7gVaFNv4qr7G65M6hbSx6hGJXv", 19 | "Q6s1GiFwi91q0V17DI79yVrINHCXdBnUOqeLGfJ05Edu+39EQNYn4dky7VdgTP2VYZE7Vw==", 20 | )) 21 | .unwrap(); 22 | let key: Vec<_> = decode("kjtbxCPw3XPFThb3mKmzfg==").unwrap(); 23 | let iv: Vec<_> = decode("dB0Ej+7zWZWTS5JUCldWMg==").unwrap(); 24 | 25 | // The source can be anything implementing `Read`. In this case, a simple &[u8] slice. 26 | let mut decryptor = 27 | read::Decryptor::new(src.as_slice(), Cipher::aes_128_cbc(), &key, &iv).unwrap(); 28 | 29 | let mut decrypted = [0u8; 1024]; // a buffer to decrypt into 30 | let mut bytes_decrypted = 0; 31 | 32 | loop { 33 | // Just read from the `Decryptor` as if it were any other `Read` impl, 34 | // the decryption takes place automatically. 35 | let read_count = decryptor.read(&mut decrypted[bytes_decrypted..]).unwrap(); 36 | bytes_decrypted += read_count; 37 | if read_count == 0 { 38 | break; 39 | } 40 | } 41 | 42 | println!("{}", String::from_utf8_lossy(&decrypted)); 43 | } 44 | 45 | fn f2() { 46 | // Starting again with the same encrypted bytestream, encoded as base64: 47 | let src: Vec = decode(concat!( 48 | "vuU+0SXFWQLu8vl/o1WzmPCmf7x/O6ToGQ162Aq2CHxcnc/ax/Q8nTbRlNn0OSPrFuE3yDdO", 49 | "VC35RmwtUIlxKIkWbnxJpRF5yRJvVByQgWX1qLW8DfMjRp7gVaFNv4qr7G65M6hbSx6hGJXv", 50 | "Q6s1GiFwi91q0V17DI79yVrINHCXdBnUOqeLGfJ05Edu+39EQNYn4dky7VdgTP2VYZE7Vw==" 51 | )) 52 | .unwrap(); 53 | let key: Vec<_> = decode("kjtbxCPw3XPFThb3mKmzfg==").unwrap(); 54 | let iv: Vec<_> = decode("dB0Ej+7zWZWTS5JUCldWMg==").unwrap(); 55 | 56 | // The destination can be any object implementing `Write`: in this case, a `Vec`. 57 | let mut decrypted = Vec::new(); 58 | 59 | // When a `cryptostream` is dropped, all buffers are flushed and it is automatically 60 | // finalized. We can either call `drop()` on the cryptostream or put its usage in a 61 | // separate scope. 62 | { 63 | let mut decryptor = 64 | write::Decryptor::new(&mut decrypted, Cipher::aes_128_cbc(), &key, &iv).unwrap(); 65 | 66 | let mut bytes_decrypted = 0; 67 | 68 | while bytes_decrypted != src.len() { 69 | // Just write encrypted ciphertext to the `Decryptor` instance as if it were any 70 | // other `Write` impl. Decryption takes place automatically. 71 | let write_count = decryptor.write(&src[bytes_decrypted..]).unwrap(); 72 | bytes_decrypted += write_count; 73 | } 74 | } 75 | 76 | // The underlying `Write` instance is only guaranteed to contain the complete and 77 | // finalized contents after the cryptostream is either explicitly finalized with a 78 | // call to `Cryptostream::finish()` or when it's dropped (either at the end of a scope 79 | // or via an explicit call to `drop()`, whichever you prefer). 80 | println!("{}", String::from_utf8_lossy(&decrypted)); 81 | } 82 | -------------------------------------------------------------------------------- /examples/write.rs: -------------------------------------------------------------------------------- 1 | use base64::decode; 2 | use cryptostream::write; 3 | use openssl::symm::Cipher; 4 | use std::io::Write; 5 | 6 | fn main() { 7 | let src: Vec = 8 | decode("vuU+0SXFWQLu8vl/o1WzmPCmf7x/O6ToGQ162Aq2CHxcnc/ax/Q8nTbRlNn0OSPrFuE3yDdOVC35RmwtUIlxKIkWbnxJpRF5yRJvVByQgWX1qLW8DfMjRp7gVaFNv4qr7G65M6hbSx6hGJXvQ6s1GiFwi91q0V17DI79yVrINHCXdBnUOqeLGfJ05Edu+39EQNYn4dky7VdgTP2VYZE7Vw==").unwrap(); 9 | let key: Vec<_> = decode("kjtbxCPw3XPFThb3mKmzfg==").unwrap(); 10 | let iv: Vec<_> = decode("dB0Ej+7zWZWTS5JUCldWMg==").unwrap(); 11 | 12 | // the destination can be any object implementing `Write`. In this case, a Vec. 13 | let mut decrypted = Vec::new(); 14 | { 15 | let mut decryptor = 16 | write::Decryptor::new(&mut decrypted, Cipher::aes_128_cbc(), &key, &iv).unwrap(); 17 | 18 | let mut bytes_decrypted = 0; 19 | 20 | while bytes_decrypted != src.len() { 21 | // Just write encrypted ciphertext to the `Decryptor` instance as if it were any 22 | // other `Write` impl. Decryption is automatic. 23 | let write_count = decryptor.write(&src[bytes_decrypted..]).unwrap(); 24 | bytes_decrypted += write_count; 25 | } 26 | } 27 | 28 | println!("{}", String::from_utf8_lossy(&decrypted)); 29 | } 30 | -------------------------------------------------------------------------------- /src/bufread.rs: -------------------------------------------------------------------------------- 1 | //! Cryptostream types which operate over [`BufRead`](std::io::BufRead) streams, providing both 2 | //! encryption and decryption facilities. 3 | //! 4 | //! Note that unlike the situation with `Read` and `Write`, there's no need for a `bufwrite` 5 | //! counterpart to this flavor of `Cryptostream` as there is no issue with simply wrapping a 6 | //! [`write::Encryptor`]/[`write::Decryptor`] instance (implementing [`Write`](std::io::Write)) in 7 | //! a `BufWriter` the way you would any other `Write` destination. However when reading *out* of a 8 | //! `BufRead` source (whether to encrypt or decrypt its contents) you will run into an ugly 9 | //! situation when you read partial blocks as only a complete block in the middle of a stream can 10 | //! be correctly encrypted or decrypted. If your block is `n` bytes and your call to 11 | //! `source.read()` returns `n + x` bytes, you need to buffer them somewhere then hand them back to 12 | //! the `Cryptostream` encryptor/decryptor to prepend to the results of the next read so that they 13 | //! can be processed correctly. 14 | //! 15 | //! The `bufread::Cryptostream` variants in this module handle the buffering for you, and ensure 16 | //! that reads always return (when and where possible) nice, round buffers divisible by the 17 | //! enryption algorithm's block size. 18 | 19 | use openssl::error::ErrorStack; 20 | use openssl::symm::{Cipher, Crypter, Mode}; 21 | use std::io::{BufRead, Error, ErrorKind, Read}; 22 | 23 | /// EVP_MAX_BLOCK_LENGTH in OpenSSL is 32 bytes, and we require at least 2*n-1 for the worst case 24 | /// where we start off with just a byte shy of a block and then read an entire block. 25 | const EVP_MAX_BLOCK_LENGTH: usize = 32; 26 | const BUFFER_SIZE: usize = EVP_MAX_BLOCK_LENGTH * 2; 27 | 28 | struct Buffer { 29 | buffer: [u8; BUFFER_SIZE], 30 | write_index: usize, 31 | read_index: usize, 32 | } 33 | 34 | // Explicitly use a simple stack-allocated struct rather than a heap-allocated vector. 35 | impl Default for Buffer { 36 | fn default() -> Self { 37 | Self { 38 | buffer: [0u8; BUFFER_SIZE], 39 | write_index: 0, 40 | read_index: 0, 41 | } 42 | } 43 | } 44 | 45 | impl<'a> Buffer { 46 | fn len(&self) -> usize { 47 | self.write_index - self.read_index 48 | } 49 | 50 | fn is_empty(&self) -> bool { 51 | self.write_index == self.read_index 52 | } 53 | 54 | fn fill(&mut self, mut read: F) -> Result 55 | where 56 | F: FnMut(&mut [u8]) -> Result, 57 | { 58 | let written = read(&mut self.buffer[self.write_index..])?; 59 | self.write_index += written; 60 | Ok(written) 61 | } 62 | 63 | fn reset(&mut self) { 64 | self.read_index = 0; 65 | self.write_index = 0; 66 | } 67 | } 68 | 69 | impl Read for Buffer { 70 | fn read(&mut self, dst: &mut [u8]) -> std::io::Result { 71 | let len = std::cmp::min(dst.len(), self.len()); 72 | dst[..len].copy_from_slice(&self.buffer[self.read_index..][..len]); 73 | self.read_index += len; 74 | Ok(len) 75 | } 76 | } 77 | 78 | #[test] 79 | fn zero_len_buffer_read() { 80 | let mut b = Buffer::default(); 81 | let mut temp = Vec::new(); 82 | match b.read(&mut temp) { 83 | Ok(0) => {} 84 | _ => panic!("Zero-length read failure!"), 85 | } 86 | } 87 | 88 | struct Cryptostream { 89 | reader: R, 90 | read_buffer: [u8; BUFFER_SIZE], 91 | write_buffer: Buffer, 92 | never_used: bool, 93 | cipher: Cipher, 94 | crypter: Crypter, 95 | finalized: bool, 96 | } 97 | 98 | impl Cryptostream { 99 | pub fn new( 100 | mode: Mode, 101 | reader: R, 102 | cipher: Cipher, 103 | key: &[u8], 104 | iv: &[u8], 105 | ) -> Result { 106 | let mut crypter = Crypter::new(cipher, mode, key, Some(iv))?; 107 | crypter.pad(true); 108 | 109 | Ok(Self { 110 | reader: reader, 111 | read_buffer: [0; BUFFER_SIZE], 112 | write_buffer: Default::default(), 113 | never_used: true, 114 | cipher: cipher.clone(), 115 | crypter: crypter, 116 | finalized: false, 117 | }) 118 | } 119 | 120 | pub fn finish(self) -> R { 121 | self.reader 122 | } 123 | } 124 | 125 | impl Read for Cryptostream { 126 | fn read(&mut self, mut buf: &mut [u8]) -> Result { 127 | let block_size = self.cipher.block_size(); 128 | debug_assert!( 129 | block_size.count_ones() == 1, 130 | "Only algorithms with power-of-two block sizes are supported!" 131 | ); 132 | 133 | if !self.write_buffer.is_empty() { 134 | // Resume from previously transformed content 135 | let drained = self.write_buffer.read(&mut buf)?; 136 | return Ok(drained); 137 | } 138 | if self.finalized { 139 | return Ok(0); 140 | } 141 | 142 | // Read::read() is required to return zero bytes only if the EOF is reached, so we must 143 | // loop over the input source until at least one block has been read and transformed. 144 | let mut bytes_read = 0; 145 | loop { 146 | let max_read = self.read_buffer.len() - bytes_read - block_size; 147 | let mut read_buffer = &mut self.read_buffer[bytes_read..][..max_read]; 148 | match self.reader.read(&mut read_buffer) { 149 | Ok(0) => { 150 | // We have reached the end of the wrapped/underlying stream 151 | self.finalized = true; 152 | 153 | // [openssl::symm::Crypter::finalize(..)] will panic if zero bytes have been 154 | // written to the instance before `finalize()` is called. We have to call 155 | // Crypter::finalize(..) if we ever wrote to the instance. 156 | return if self.never_used { 157 | Ok(0) 158 | } else if !self.write_buffer.is_empty() || buf.len() < bytes_read + block_size { 159 | // The destination buffer is not sufficient for a zero-copy operation 160 | // without scatter-gather. 161 | let write_buffer = &mut self.write_buffer; 162 | let crypter = &mut self.crypter; 163 | write_buffer.fill(|b| crypter.finalize(b)) 164 | .map_err(|e| Error::new(ErrorKind::Other, e))?; 165 | 166 | self.write_buffer.read(buf) 167 | } else { 168 | // We can skip the copy and use the provided buffer directly. 169 | self.crypter 170 | .finalize(&mut buf) 171 | .map_err(|e| Error::new(ErrorKind::Other, e)) 172 | } 173 | } 174 | Ok(n) => { 175 | self.never_used = false; 176 | let old_bytes_read = bytes_read; 177 | bytes_read += n; 178 | 179 | debug_assert!(self.write_buffer.is_empty()); 180 | 181 | // OpenSSL will panic if we try to read into too small a buffer, so we may need 182 | // to buffer the result locally. 183 | if buf.len() < n + block_size { 184 | let write_buffer = &mut self.write_buffer; 185 | let crypter = &mut self.crypter; 186 | // The write buffer reset here is not to clear any content (which must have 187 | // necessarily been read by this point) but rather only to move the write 188 | // cursor to the start of the buffer. 189 | write_buffer.reset(); 190 | let bytes_written = write_buffer.fill(|b| crypter.update(&read_buffer[old_bytes_read..bytes_read], b)) 191 | .map_err(|e| Error::new(ErrorKind::Other, e))?; 192 | 193 | match bytes_written { 194 | 0 => continue, 195 | _ => return self.write_buffer.read(&mut buf) 196 | }; 197 | } else { 198 | // Skip the double-buffering and write directly to the source. 199 | match self.crypter.update(&self.read_buffer[old_bytes_read..bytes_read], &mut buf)? { 200 | 0 => continue, 201 | written => return Ok(written), 202 | }; 203 | }; 204 | } 205 | // It is safe to just bubble up ErrorKind::Interrupted as our state is persisted/updated each loop. 206 | Err(e) => return Err(e), 207 | }; 208 | } 209 | } 210 | } 211 | 212 | /// An encrypting stream adapter that encrypts what it reads 213 | /// 214 | /// `bufread::Encryptor` is a stream adapter that sits atop a plaintext (non-encrypted) [`BufRead`] 215 | /// source, exposing a second [`BufRead`] interface. Bytes read out of `bufread::Encryptor` are the 216 | /// encrypted contents of the underlying stream. 217 | pub struct Encryptor { 218 | inner: Cryptostream, 219 | } 220 | 221 | impl Encryptor { 222 | pub fn new(reader: R, cipher: Cipher, key: &[u8], iv: &[u8]) -> Result { 223 | Ok(Self { 224 | inner: Cryptostream::new(Mode::Encrypt, reader, cipher, key, iv)?, 225 | }) 226 | } 227 | 228 | pub fn finish(self) -> R { 229 | self.inner.reader 230 | } 231 | } 232 | 233 | impl Read for Encryptor { 234 | /// Reads encrypted data out of the underlying plaintext 235 | fn read(&mut self, mut buf: &mut [u8]) -> Result { 236 | self.inner.read(&mut buf) 237 | } 238 | } 239 | 240 | /// A decrypting stream adapter that decrypts what it reads 241 | /// 242 | /// `bufread::Decryptor` is a stream adapter that sits atop a ciphertext (encrypted) `BufRead` source, 243 | /// exposing a second `BufRead` interface. Bytes read out of `bufread::Decrytor` are the decrypted 244 | /// contents of the underlying `Read` stream. 245 | pub struct Decryptor { 246 | inner: Cryptostream, 247 | } 248 | 249 | impl Decryptor { 250 | pub fn new(reader: R, cipher: Cipher, key: &[u8], iv: &[u8]) -> Result { 251 | Ok(Self { 252 | inner: Cryptostream::new(Mode::Decrypt, reader, cipher, key, iv)?, 253 | }) 254 | } 255 | 256 | pub fn finish(self) -> R { 257 | self.inner.finish() 258 | } 259 | } 260 | 261 | impl Read for Decryptor { 262 | /// Reads decrypted data out of the underlying encrypted ciphertext 263 | /// 264 | /// `buf` must be at least the size of one block or else `read` will return 0 prematurely. This 265 | /// routine will read in multiples of block size to avoid needless buffering of data, and so it 266 | /// is normal for it to read less than the buffer size if the buffer is not a multiple of the 267 | /// block size. 268 | fn read(&mut self, mut buf: &mut [u8]) -> Result { 269 | self.inner.read(&mut buf) 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The `cryptostream` crate provides a number of stream adapters [that provide "transparent" 2 | //! encryption or 3 | //! decryption](https://neosmart.net/blog/2018/transparent-encryption-and-decryption-in-rust-with-cryptostreams/) 4 | //! of the wrapped [`Read`](std::io::Read) or [`Write`](std::io::Write) source. Since rust doesn't 5 | //! have a generic "stream" type that implements both reads and writes and in order to enforce 6 | //! correct semantics (and boost security!), you need to pick the cryptostream variant that 7 | //! correctly matches your needs. 8 | //! 9 | //! The most common reasons for using this library: 10 | //! 11 | //! * [`read::Decryptor`]: You have an encrypted 12 | //! `Read` source and you want to transparently decrypt its contents while reading from it (e.g. 13 | //! you have encrypted data at rest and want to decrypt it into memory). 14 | //! * [`write::Encryptor`]: 15 | //! You have a `Write` instance you want to write the encrypted ciphertext equivalent of some 16 | //! plaintext you have in memory (e.g. you have plaintext data in memory you want to store it 17 | //! encrypted). 18 | //! 19 | //! Considerably less common use cases: 20 | //! 21 | //! * [`read::Encryptor`]: You have a `Read` source containing 22 | //! plaintext but you want to pull encrypted contents out of it (e.g. you want to encrypt data 23 | //! stored as plaintext). 24 | //! * [`write::Decryptor`]: You want to write cyphertext to a `Write` 25 | //! instance and have it pass through the decrypted plaintext to the underlying stream (e.g. you 26 | //! have cryptotext in memory and want to store it decrypted). 27 | //! 28 | //! Additionally, the [`bufread`] module provides the [`bufread::Encryptor`] and 29 | //! [`bufread::Decryptor`] types for encrypting/decrypting plaintext/ciphertext on-the-fly from a 30 | //! [`BufRead`](std::io::BufRead) source. (There is no need for a `bufwrite` variant.) 31 | 32 | pub mod bufread; 33 | pub mod read; 34 | pub mod write; 35 | 36 | #[cfg(test)] 37 | mod tests; 38 | -------------------------------------------------------------------------------- /src/read.rs: -------------------------------------------------------------------------------- 1 | //! Cryptostream types which operate over [`Read`](std::io::Read) streams, providing both 2 | //! encryption and decryption facilities. 3 | //! 4 | //! Create a [`read::Decryptor`] over/from an encrypted `Read` source to decrypt its contents 5 | //! on-the-fly when reading out of the `Decryptor`, or use [`read::Encryptor`] to read plaintext 6 | //! from the wrapped/underlying `Read` instance and return the encrypted equivalent (a block at a 7 | //! time) via `.read(..)` calls. 8 | 9 | use crate::bufread; 10 | use openssl::error::ErrorStack; 11 | use openssl::symm::Cipher; 12 | use std::io::{BufReader, Error, Read}; 13 | 14 | /// An encrypting stream adapter that encrypts what it reads 15 | /// 16 | /// `read::Encryptor` is a stream adapter that sits atop a plaintext (non-encrypted) `Read` source, 17 | /// exposing a second `Read` interface. Bytes read out of `read::Encryptor` are the encrypted 18 | /// contents of the underlying `Read` stream. 19 | pub struct Encryptor { 20 | reader: bufread::Encryptor>, 21 | } 22 | 23 | impl Encryptor { 24 | pub fn new(reader: R, cipher: Cipher, key: &[u8], iv: &[u8]) -> Result { 25 | Ok(Self { 26 | reader: bufread::Encryptor::new(BufReader::new(reader), cipher, key, iv)?, 27 | }) 28 | } 29 | 30 | pub fn finish(self) -> R { 31 | self.reader.finish().into_inner() 32 | } 33 | } 34 | 35 | impl Read for Encryptor { 36 | /// Reading from the cryptostream returns an encrypted view of bytes pulled from the underlying 37 | /// `Read` stream. 38 | fn read(&mut self, mut buf: &mut [u8]) -> Result { 39 | self.reader.read(&mut buf) 40 | } 41 | } 42 | 43 | /// A decrypting stream adapter that decrypts what it reads 44 | /// 45 | /// `read::Decryptor` is a stream adapter that sits atop a ciphertext (encrypted) `Read` source, 46 | /// exposing a second `Read` interface. Bytes read out of `read::Decrytor` are the decrypted 47 | /// contents of the underlying `Read` stream. 48 | pub struct Decryptor { 49 | reader: bufread::Decryptor>, 50 | } 51 | 52 | impl Decryptor { 53 | pub fn new(reader: R, cipher: Cipher, key: &[u8], iv: &[u8]) -> Result { 54 | Ok(Self { 55 | reader: bufread::Decryptor::new(BufReader::new(reader), cipher, key, iv)?, 56 | }) 57 | } 58 | 59 | pub fn finish(self) -> R { 60 | self.reader.finish().into_inner() 61 | } 62 | } 63 | 64 | impl Read for Decryptor { 65 | /// Reading from the cryptostream returns returns a decrypted view of bytes pulled from the 66 | /// underlying `Read` stream. 67 | fn read(&mut self, mut buf: &mut [u8]) -> Result { 68 | self.reader.read(&mut buf) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/tests/issue_9.rs: -------------------------------------------------------------------------------- 1 | //! This is a test for https://github.com/neosmart/cryptostream/issues/9 2 | //! which reported a mismatch after an encrypt/decrypt cycle with a large 3 | //! (4096-byte) buffer size. It turned out to be a user error, caused by 4 | //! not using `write_all()`. 5 | 6 | #![allow(unused)] 7 | 8 | use crate::bufread; 9 | use crate::read::Decryptor as ReadDecryptor; 10 | use crate::read::Encryptor as ReadEncryptor; 11 | use crate::write::Encryptor as WriteEncryptor; 12 | use openssl::symm::{Cipher, Crypter, Mode}; 13 | use rand::Fill; 14 | use size::Size; 15 | use std::cmp::Ordering; 16 | use std::fs::File; 17 | use std::io; 18 | use std::io::prelude::*; 19 | use std::io::BufWriter; 20 | use std::io::SeekFrom; 21 | use std::io::{BufReader, Read}; 22 | use std::path::Path; 23 | use std::time::Instant; 24 | 25 | /// Creates a file at the indicated path `path` and fills it with `size` bytes of random data. 26 | fn create_rand_file(path: &Path, size: usize) { 27 | let mut buffer = [0u8; 1024]; 28 | let mut remainder = size as usize; 29 | let mut rng = rand::thread_rng(); 30 | let mut dst = File::options() 31 | .create(true) 32 | .write(true) 33 | .truncate(true) 34 | .open(&path) 35 | .unwrap(); 36 | 37 | while remainder > 0 { 38 | buffer.try_fill(&mut rng).unwrap(); 39 | let write_count = remainder.min(buffer.len()); 40 | dst.write_all(&buffer[..write_count]).unwrap(); 41 | remainder = remainder.checked_sub(write_count).unwrap(); 42 | } 43 | } 44 | 45 | struct OnDrop 46 | where 47 | F: Fn(), 48 | { 49 | callback: F, 50 | } 51 | 52 | impl OnDrop 53 | where 54 | F: Fn(), 55 | { 56 | pub fn new(f: F) -> Self { 57 | Self { callback: f } 58 | } 59 | } 60 | 61 | impl Drop for OnDrop 62 | where 63 | F: Fn(), 64 | { 65 | fn drop(&mut self) { 66 | (self.callback)() 67 | } 68 | } 69 | 70 | #[test] 71 | fn large_buffer_encrypt_decrypt() { 72 | let cleanup = OnDrop::new(|| { 73 | for f in ["issue_9.src", "issue_9.enc", "issue_9.dec"] { 74 | let _ = std::fs::remove_file(Path::new(f)); 75 | } 76 | }); 77 | 78 | // Create a large source file 79 | let start = Instant::now(); 80 | let src_path = Path::new("issue_9.src"); 81 | let size = Size::from_mib(10); 82 | create_rand_file(&src_path, size.bytes() as usize); 83 | eprintln!("{} source file generated in {:?}", size, start.elapsed()); 84 | 85 | let mut rng = rand::thread_rng(); 86 | let mut key = [0u8; 16]; 87 | key.try_fill(&mut rng); 88 | let mut iv = [0u8; 16]; 89 | iv.try_fill(&mut rng); 90 | 91 | // Encrypt directly to the destination by reading from source and writing to a write::Encryptor 92 | let start = Instant::now(); 93 | let mut src = File::open(&src_path).unwrap(); 94 | let enc_path = Path::new("issue_9.enc"); 95 | let mut enc = File::options() 96 | .write(true) 97 | .truncate(true) 98 | .create(true) 99 | .open(&enc_path) 100 | .unwrap(); 101 | let mut encryptor = WriteEncryptor::new(enc, Cipher::aes_128_cbc(), &key, &iv).unwrap(); 102 | 103 | // Bug report alleges corruption happens with large read buffer size? 104 | let mut buffer = [0u8; 4096]; 105 | 106 | loop { 107 | let read = src.read(&mut buffer).unwrap(); 108 | encryptor.write_all(&buffer[..read]).unwrap(); 109 | if read == 0 { 110 | break; 111 | } 112 | } 113 | 114 | // Finalize the encrypted stream 115 | encryptor.finish().unwrap(); 116 | eprintln!("File encrypted in {:?}", start.elapsed()); 117 | 118 | // Now decrypt the encrypted contents to a third file 119 | let start = Instant::now(); 120 | let dec_path = Path::new("issue_9.dec"); 121 | let mut dec = File::options() 122 | .write(true) 123 | .truncate(true) 124 | .create(true) 125 | .open(&dec_path) 126 | .unwrap(); 127 | let mut dec = BufWriter::new(dec); 128 | let mut enc = File::open(&enc_path).unwrap(); 129 | let mut enc = BufReader::new(enc); 130 | // let mut decryptor = ReadDecryptor::new(enc, Cipher::aes_128_cbc(), &key, &iv).unwrap(); 131 | let mut decryptor = 132 | crate::bufread::Decryptor::new(enc, Cipher::aes_128_cbc(), &key, &iv).unwrap(); 133 | 134 | loop { 135 | let read = decryptor.read(&mut buffer).unwrap(); 136 | dec.write_all(&buffer[..read]).unwrap(); 137 | 138 | if read == 0 { 139 | break; 140 | } 141 | } 142 | 143 | dec.flush().unwrap(); 144 | eprintln!("File decrypted in {:?}", start.elapsed()); 145 | 146 | assert_files_are_equal(&src_path, &dec_path); 147 | } 148 | 149 | fn assert_files_are_equal(lhs: &Path, rhs: &Path) { 150 | let lhs_len = std::fs::metadata(&lhs).unwrap().len(); 151 | let rhs_len = std::fs::metadata(&rhs).unwrap().len(); 152 | assert_eq!(lhs_len, rhs_len, "Mismatch in file lengths!"); 153 | 154 | let mut file1 = File::open(&lhs).unwrap(); 155 | let mut file2 = File::open(&rhs).unwrap(); 156 | 157 | let mut buff1 = [0u8; 4096]; 158 | let mut buff2 = [0u8; 4096]; 159 | let mut chunk_start = 0; 160 | loop { 161 | let read = file1.read(&mut buff1).unwrap(); 162 | if read == 0 { 163 | break; 164 | } 165 | 166 | file2.read_exact(&mut buff2[..read]).unwrap(); 167 | 168 | if buff1[..read].cmp(&buff2[..read]) != Ordering::Equal { 169 | for i in 0..read { 170 | if buff1[i] != buff2[i] { 171 | panic!( 172 | "Mismatch in file contents starting at {:x}", 173 | chunk_start + i 174 | ); 175 | } 176 | } 177 | } 178 | 179 | chunk_start += read; 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod random_read; 2 | mod issue_9; 3 | 4 | use openssl::symm::{Cipher, Crypter, Mode}; 5 | use std::io::prelude::*; 6 | use crate::read; 7 | use crate::write; 8 | 9 | pub const TEST: &[u8] = b"It was the best of times, it was the worst of times."; 10 | 11 | #[test] 12 | fn basic_read_encrypt() { 13 | let source: &[u8] = TEST; 14 | let key: [u8; 128 / 8] = rand::random(); 15 | let iv: [u8; 128 / 8] = rand::random(); 16 | let cipher = Cipher::aes_128_cbc(); 17 | 18 | let mut encrypted = [0u8; 1024]; 19 | let mut encryptor = read::Encryptor::new(source, cipher, &key, &iv).unwrap(); 20 | let mut total_bytes_read = 0; 21 | loop { 22 | let bytes_read = encryptor 23 | .read(&mut encrypted[total_bytes_read..]) 24 | .expect("Encryptor read failure!"); 25 | if bytes_read == 0 { 26 | break; 27 | } 28 | 29 | eprintln!("Read {} bytes out of encrypted stream", bytes_read); 30 | eprintln!( 31 | "Bytes: {:?}", 32 | &encrypted[total_bytes_read..total_bytes_read + bytes_read] 33 | ); 34 | total_bytes_read += bytes_read; 35 | } 36 | 37 | assert!( 38 | total_bytes_read > source.len(), 39 | "Encrypted payload less than original input!" 40 | ); 41 | assert!( 42 | total_bytes_read < source.len() + cipher.block_size(), 43 | "Encrypted payload exceeds padded original input!" 44 | ); 45 | 46 | let mut crypter = Crypter::new(cipher, Mode::Decrypt, &key, Some(&iv)).unwrap(); 47 | let mut decrypted = [0u8; 1024]; 48 | let mut bytes_decrypted = crypter 49 | .update(&encrypted[0..total_bytes_read], &mut decrypted) 50 | .unwrap(); 51 | bytes_decrypted += crypter.finalize(&mut decrypted[bytes_decrypted..]).unwrap(); 52 | 53 | eprintln!("Decrypted {} bytes", bytes_decrypted); 54 | let decrypted_msg = String::from_utf8(decrypted[0..bytes_decrypted].to_vec()).unwrap(); 55 | eprintln!("Decrypted message: {}", decrypted_msg); 56 | assert_eq!(String::from_utf8(source.to_vec()).unwrap(), decrypted_msg); 57 | } 58 | 59 | #[test] 60 | fn basic_write_encrypt() { 61 | let source: &[u8] = TEST; 62 | let key: [u8; 128 / 8] = rand::random(); 63 | let iv: [u8; 128 / 8] = rand::random(); 64 | let cipher = Cipher::aes_128_cbc(); 65 | 66 | let mut encrypted = Vec::new(); 67 | let mut bytes_written = 0; 68 | 69 | { 70 | let mut encryptor = write::Encryptor::new(&mut encrypted, cipher, &key, &iv).unwrap(); 71 | 72 | while bytes_written < source.len() { 73 | let write_bytes = encryptor.write(&source[bytes_written..]).unwrap(); 74 | eprintln!("Wrote {} bytes to cryptostream", write_bytes); 75 | bytes_written += write_bytes; 76 | } 77 | } 78 | 79 | eprintln!("Encrypted bytes: {:?}", &encrypted[0..bytes_written]); 80 | 81 | let mut crypter = Crypter::new(cipher, Mode::Decrypt, &key, Some(&iv)).unwrap(); 82 | let mut decrypted = [0u8; 1024]; 83 | let mut bytes_decrypted = crypter.update(&encrypted, &mut decrypted).unwrap(); 84 | bytes_decrypted += crypter.finalize(&mut decrypted[bytes_decrypted..]).unwrap(); 85 | 86 | eprintln!("Decrypted {} bytes", bytes_decrypted); 87 | let decrypted_msg = String::from_utf8(decrypted[0..bytes_decrypted].to_vec()).unwrap(); 88 | eprintln!("Decrypted message: {}", decrypted_msg); 89 | assert_eq!(String::from_utf8(source.to_vec()).unwrap(), decrypted_msg); 90 | } 91 | 92 | #[test] 93 | fn basic_read_decrypt() { 94 | let source = TEST; 95 | let key: [u8; 128 / 8] = rand::random(); 96 | let iv: [u8; 128 / 8] = rand::random(); 97 | let cipher = Cipher::aes_128_cbc(); 98 | let mut crypter = Crypter::new(cipher, Mode::Encrypt, &key, Some(&iv)).unwrap(); 99 | 100 | let mut encrypted = [0u8; 1024]; 101 | let mut bytes_written = crypter.update(TEST, &mut encrypted).unwrap(); 102 | bytes_written += crypter.finalize(&mut encrypted[bytes_written..]).unwrap(); 103 | 104 | let encrypted = &encrypted[0..bytes_written]; // reframe 105 | let mut decrypted = [0u8; 1024]; 106 | let mut bytes_decrypted = 0; 107 | 108 | { 109 | let mut decryptor = read::Decryptor::new(encrypted, cipher, &key, &iv).unwrap(); 110 | 111 | while bytes_decrypted < source.len() { 112 | let decrypt_bytes = decryptor.read(&mut decrypted[bytes_decrypted..]).unwrap(); 113 | bytes_decrypted += decrypt_bytes; 114 | } 115 | eprintln!("Decrypted a total of {} bytes", bytes_decrypted); 116 | } 117 | 118 | let decrypted = &decrypted[0..bytes_decrypted]; // reframe 119 | assert_eq!(&decrypted, &TEST); 120 | } 121 | 122 | #[test] 123 | fn basic_write_decrypt() { 124 | let source = TEST; 125 | let key: [u8; 128 / 8] = rand::random(); 126 | let iv: [u8; 128 / 8] = rand::random(); 127 | let cipher = Cipher::aes_128_cbc(); 128 | let mut cryptor = Crypter::new(cipher, Mode::Encrypt, &key, Some(&iv)).unwrap(); 129 | 130 | let mut encrypted = [0u8; 1024]; 131 | let mut bytes_written = cryptor.update(TEST, &mut encrypted).unwrap(); 132 | bytes_written += cryptor.finalize(&mut encrypted[bytes_written..]).unwrap(); 133 | 134 | let encrypted = &encrypted[0..bytes_written]; // reframe 135 | let mut decrypted = Vec::new(); 136 | let mut bytes_decrypted = 0; 137 | 138 | { 139 | let mut decryptor = write::Decryptor::new(&mut decrypted, cipher, &key, &iv).unwrap(); 140 | 141 | while bytes_decrypted < source.len() { 142 | let decrypt_bytes = decryptor.write(&encrypted[bytes_decrypted..]).unwrap(); 143 | bytes_decrypted += decrypt_bytes; 144 | } 145 | eprintln!("Decrypted a total of {} bytes", bytes_decrypted); 146 | } 147 | 148 | assert_eq!(&decrypted, &TEST); 149 | } 150 | 151 | #[test] 152 | fn empty_read_decrypt() { 153 | let key: [u8; 128 / 8] = rand::random(); 154 | let iv: [u8; 128 / 8] = rand::random(); 155 | let cipher = Cipher::aes_128_cbc(); 156 | 157 | let encrypted: &[u8] = b""; 158 | let mut decrypted = [0u8; 1024]; 159 | 160 | let mut decryptor = read::Decryptor::new(encrypted, cipher, &key, &iv).unwrap(); 161 | let decrypted_bytes = decryptor.read(&mut decrypted[0..]).unwrap(); 162 | assert_eq!(decrypted_bytes, 0); 163 | } 164 | 165 | #[test] 166 | fn empty_write_decrypt() { 167 | let key: [u8; 128 / 8] = rand::random(); 168 | let iv: [u8; 128 / 8] = rand::random(); 169 | let cipher = Cipher::aes_128_cbc(); 170 | 171 | let encrypted = Vec::new(); 172 | let encryptor = write::Decryptor::new(encrypted, cipher, &key, &iv).unwrap(); 173 | encryptor.finish().unwrap(); 174 | } 175 | 176 | #[test] 177 | fn finish_empty_write_encrypt() { 178 | let key: [u8; 128 / 8] = rand::random(); 179 | let iv: [u8; 128 / 8] = rand::random(); 180 | let cipher = Cipher::aes_128_cbc(); 181 | 182 | let encrypted = Vec::new(); 183 | let encryptor = write::Encryptor::new(encrypted, cipher, &key, &iv).unwrap(); 184 | encryptor.finish().unwrap(); 185 | } 186 | 187 | fn init_secrets() -> (Cipher, [u8; 128/8], [u8; 128/8]) { 188 | let cipher = Cipher::aes_128_cbc(); 189 | let key: [u8; 128 / 8] = rand::random(); 190 | let iv: [u8; 128 / 8] = rand::random(); 191 | 192 | (cipher, key, iv) 193 | } 194 | 195 | #[test] 196 | fn finish_empty_read_encrypt() { 197 | let (cipher, key, iv) = init_secrets(); 198 | 199 | let plaintext: &[u8] = b""; 200 | let encryptor = read::Encryptor::new(plaintext, cipher, &key, &iv).unwrap(); 201 | encryptor.finish(); 202 | } 203 | 204 | fn encrypt(plaintext: &[u8], cipher: Cipher, key: &[u8], iv: &[u8]) -> Vec { 205 | let mut cryptor = openssl::symm::Crypter::new(cipher, Mode::Encrypt, key, Some(iv)) 206 | .expect("Failed to create OpenSSL encryptor!"); 207 | let mut encrypted = Vec::new(); 208 | encrypted.resize(plaintext.len() + cipher.block_size(), 0); 209 | let mut bytes_written = cryptor.update(plaintext, &mut encrypted) 210 | .expect("OpenSSL update for encryption failed!"); 211 | bytes_written += cryptor.finalize(&mut encrypted[bytes_written..]) 212 | .expect("OpenSSL finalization for encryption failed!"); 213 | encrypted.truncate(bytes_written); 214 | encrypted 215 | } 216 | 217 | fn verify_transform(plaintext: &[u8], ciphertext: &[u8], cipher: Cipher, key: &[u8], iv: &[u8]) { 218 | let encrypted = encrypt(plaintext, cipher, &key, &iv); 219 | assert_eq!(ciphertext, encrypted.as_slice()); 220 | } 221 | 222 | #[test] 223 | fn read_encrypt_less_than_block() { 224 | let (cipher, key, iv) = init_secrets(); 225 | 226 | let plaintext: &[u8] = b"one"; 227 | let mut encryptor = read::Encryptor::new(plaintext, cipher, &key, &iv).unwrap(); 228 | let mut encrypted = Vec::new(); 229 | 230 | match encryptor.read_to_end(&mut encrypted) { 231 | Ok(16) => {}, 232 | _ => panic!("Failed to read encrypted bytes!"), 233 | }; 234 | drop(encryptor); 235 | 236 | // It's OK not to drop the encryptor before verifying since we necessarily read until the 237 | // source is exhausted. 238 | verify_transform(plaintext, &encrypted, cipher, &key, &iv); 239 | } 240 | 241 | 242 | #[test] 243 | fn read_decrypt_less_than_block() { 244 | let (cipher, key, iv) = init_secrets(); 245 | 246 | let plaintext: &[u8] = b"one"; 247 | let encrypted = encrypt(plaintext, cipher, &key, &iv); 248 | let mut decryptor = read::Encryptor::new(plaintext, cipher, &key, &iv).unwrap(); 249 | let mut decrypted = Vec::new(); 250 | 251 | match decryptor.read_to_end(&mut decrypted) { 252 | Ok(16) => {}, 253 | _ => panic!("Failed to read encrypted bytes!"), 254 | }; 255 | 256 | // It's OK not to drop the decryptor before verifying since we necessarily read until the 257 | // source is exhausted. 258 | verify_transform(plaintext, &encrypted, cipher, &key, &iv); 259 | } 260 | 261 | #[test] 262 | fn write_encrypt_less_than_block() { 263 | let (cipher, key, iv) = init_secrets(); 264 | 265 | let plaintext: &[u8] = b"hello"; 266 | let ciphertext = Vec::new(); 267 | let mut encryptor = write::Encryptor::new(ciphertext, cipher, &key, &iv).unwrap(); 268 | 269 | encryptor.write_all(plaintext) 270 | .expect("Failed to write all bytes to encryptor!"); 271 | 272 | // Here we must ensure the encryptor is flushed/dropped before comparing the results. 273 | // Merely dropping the encryptor leaves us unable to access the results, so call 274 | // `.finish()` instead. 275 | let ciphertext = encryptor.finish() 276 | .expect("Failed to finish encryptor!"); 277 | verify_transform(&plaintext, &ciphertext, cipher, &key, &iv); 278 | } 279 | 280 | #[test] 281 | fn write_decrypt_less_than_block() { 282 | let (cipher, key, iv) = init_secrets(); 283 | 284 | let plaintext: &[u8] = b"hello"; 285 | let encrypted = encrypt(plaintext, cipher, &key, &iv); 286 | let decrypted = Vec::new(); 287 | let mut decryptor = write::Decryptor::new(decrypted, cipher, &key, &iv).unwrap(); 288 | 289 | decryptor.write_all(&encrypted) 290 | .expect("Failed to write all bytes to decryptor!"); 291 | 292 | // Here we must ensure the encryptor is flushed/dropped before comparing the results. 293 | // Merely dropping the encryptor leaves us unable to access the results, so call 294 | // `.finish()` instead. 295 | let decrypted = decryptor.finish() 296 | .expect("Failed to finish decryptor!"); 297 | assert_eq!(plaintext, decrypted.as_slice(), "Mismatch of original and decrypted contents!"); 298 | } 299 | -------------------------------------------------------------------------------- /src/tests/random_read.rs: -------------------------------------------------------------------------------- 1 | //! This test is taken from the bug report in neosmart/cryptostream#7 and verifies that the issue 2 | //! described in f64cf3a25496f880f73b5b75dbe79d04e57cb328 does not occur. The bug was caused by too 3 | //! small a read buffer combined with a sufficiently large underlying source and a sufficiently 4 | //! large destination buffer. 5 | 6 | use crate::bufread; 7 | use openssl::symm::{Cipher, Crypter, Mode}; 8 | use std::io::{BufReader, Read}; 9 | use std::io; 10 | 11 | struct RandomRead 12 | where 13 | R: Read, 14 | { 15 | inner: R, 16 | rng: rand::rngs::ThreadRng, 17 | } 18 | 19 | impl RandomRead { 20 | pub fn new(inner: R) -> Self { 21 | RandomRead { 22 | inner, 23 | rng: rand::thread_rng(), 24 | } 25 | } 26 | } 27 | 28 | impl Read for RandomRead { 29 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 30 | use rand::prelude::*; 31 | 32 | let bufsize: usize = if buf.len() <= 5 { 33 | buf.len() 34 | } else { 35 | std::cmp::max(self.rng.gen::() % buf.len(), 5) 36 | }; 37 | 38 | self.inner.read(&mut buf[..bufsize]) 39 | } 40 | } 41 | 42 | #[test] 43 | fn random_read() -> io::Result<()> { 44 | use openssl::rand::rand_bytes; 45 | 46 | let mut plaintext = vec![0u8; (0.4 * 1024. * 1024. + 5.) as usize]; 47 | rand_bytes(&mut plaintext)?; 48 | let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; 49 | let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07"; 50 | 51 | let mut encrypted = vec![0u8; (0.4 * 1024. * 1024. + 128.) as usize]; 52 | 53 | let cipher = Cipher::aes_128_cbc(); 54 | let mut crypter = Crypter::new(cipher, Mode::Encrypt, key, Some(iv))?; 55 | 56 | let resultsize = crypter.update(&plaintext, &mut encrypted)?; 57 | let resultsize2 = crypter.finalize(&mut encrypted[resultsize..])?; 58 | encrypted.truncate(resultsize + resultsize2); 59 | 60 | let bufreader = BufReader::new(RandomRead::new(&encrypted[..])); 61 | let mut decryptor = bufread::Decryptor::new(bufreader, cipher, key, iv)?; 62 | let mut test: Vec = Vec::new(); 63 | decryptor.read_to_end(&mut test)?; 64 | drop(decryptor); 65 | 66 | assert_eq!(plaintext.len(), test.len()); 67 | // Don't use assert_eq!(plaintext, test) here, as it will dump the entire contents 68 | // of the buffer to stdout on test failure! 69 | assert!(plaintext == test); 70 | 71 | Ok(()) 72 | } 73 | -------------------------------------------------------------------------------- /src/write.rs: -------------------------------------------------------------------------------- 1 | //! Cryptostream types which operate over [`Write`](std::io::Write) streams, providing both 2 | //! encryption and decryption facilities. 3 | //! 4 | //! Use [`write::Encryptor`] to pass in plaintext and have it write the encrypted equivalent to the 5 | //! underlying `Write` stream, or use [`write::Decryptor`] to do the opposite and have decrypted 6 | //! plaintext written to the wrapped `Write` output each time encrypted bytes are written to the 7 | //! instance. 8 | 9 | use openssl::error::ErrorStack; 10 | use openssl::symm::{Cipher, Crypter, Mode}; 11 | use std::io::{Error, ErrorKind, Write}; 12 | 13 | const BUFFER_SIZE: usize = 4096; 14 | 15 | struct Cryptostream { 16 | buffer: [u8; BUFFER_SIZE], 17 | /// This `Option` is guaranteed to always be `Some` up until the point 18 | /// [`to_inner()`](self::to_inner) is called, which is the only place `None` is swapped in. As 19 | /// that call consumes the `Cryptostream` object, we can safely assume that `writer.unwrap()` 20 | /// is always safe to call. 21 | writer: Option, 22 | cipher: Cipher, 23 | crypter: Crypter, 24 | finalized: bool, 25 | never_used: bool, 26 | } 27 | 28 | impl Cryptostream { 29 | pub fn new( 30 | mode: Mode, 31 | writer: W, 32 | cipher: Cipher, 33 | key: &[u8], 34 | iv: &[u8], 35 | ) -> Result { 36 | let mut crypter = Crypter::new(cipher, mode, key, Some(iv))?; 37 | crypter.pad(true); 38 | 39 | Ok(Self { 40 | buffer: [0u8; BUFFER_SIZE], 41 | writer: Some(writer), 42 | never_used: true, 43 | cipher: cipher.clone(), 44 | crypter: crypter, 45 | finalized: false, 46 | }) 47 | } 48 | 49 | /// Function shared by Drop and finish() 50 | fn inner_finish(&mut self) -> Result<(), Error> { 51 | if !self.finalized { 52 | self.finalized = true; 53 | 54 | // Crypter::finalize() will panic if Crypter::update() was never previously called. 55 | if !self.never_used { 56 | let mut buffer = [0u8; 16]; 57 | let bytes_written = self 58 | .crypter 59 | .finalize(&mut buffer) 60 | .map_err(|e| Error::new(ErrorKind::Other, e))?; 61 | 62 | self.writer 63 | .as_mut() 64 | .unwrap() 65 | .write(&buffer[0..bytes_written])?; 66 | } 67 | } 68 | 69 | self.flush() 70 | } 71 | 72 | /// Finishes writing to the underlying cryptostream, padding the final block as needed, 73 | /// flushing all output. Returns the wrapped `Write` instance. 74 | pub fn finish(mut self) -> Result { 75 | self.inner_finish()?; 76 | 77 | // Return the original `W` instance. Since we implement `Drop`, we have to put something in 78 | // its place, as we cannot simply destructure ourselves. (This is why `inner` is an 79 | // `Option` rather than `W`). 80 | let mut inner = None; 81 | std::mem::swap::>(&mut self.writer, &mut inner); 82 | Ok(inner.unwrap()) 83 | } 84 | } 85 | 86 | impl Write for Cryptostream { 87 | fn write(&mut self, buf: &[u8]) -> Result { 88 | if self.finalized { 89 | return Ok(0); 90 | } 91 | 92 | // Crypter::update() requires `output.len() >= input.len() + block_size` 93 | let block_size = self.cipher.block_size(); 94 | let max_read = std::cmp::min(BUFFER_SIZE - block_size, buf.len()); 95 | 96 | if max_read > 0 { 97 | let bytes_encrypted = self 98 | .crypter 99 | .update(&buf[0..max_read], &mut self.buffer) 100 | .map_err(|e| Error::new(ErrorKind::Other, e))?; 101 | 102 | // Flag the crypter as having been used and needing finalizing 103 | self.never_used = false; 104 | 105 | self.writer 106 | .as_mut() 107 | .unwrap() 108 | .write_all(&self.buffer[0..bytes_encrypted])?; 109 | } 110 | 111 | // Regardless of how many bytes of encrypted ciphertext we wrote to the underlying stream 112 | // (taking padding into consideration) we return how many bytes of *input* were processed, 113 | // which can never be larger than the number of bytes passed in to us originally. 114 | Ok(max_read) 115 | } 116 | 117 | /// Flushes the underlying stream but does not clear all internal buffers or explicitly pad the 118 | /// output blocks, as that would prevent us from appeding anything in the future if we are not 119 | /// at a block boundary. 120 | fn flush(&mut self) -> Result<(), Error> { 121 | // If `Cryptostream::finish()` is manually called, writer can be `None` 122 | match self.writer.as_mut() { 123 | Some(x) => x.flush(), 124 | _ => Ok(()), 125 | } 126 | } 127 | } 128 | 129 | impl Drop for Cryptostream { 130 | /// Write all buffered output to the underlying stream, pad the final block if needed, and 131 | /// flush everything. 132 | fn drop(&mut self) { 133 | // We should never panic on Drop 134 | let _r = self.inner_finish(); 135 | } 136 | } 137 | 138 | /// An encrypting stream adapter that encrypts what is written to it. 139 | /// 140 | /// `write::Encryptor` is a stream adapter that sits atop a `Write` stream. Plaintext written to 141 | /// the `Encryptor` is encrypted and written to the underlying stream. 142 | pub struct Encryptor { 143 | inner: Cryptostream, 144 | } 145 | 146 | impl Encryptor { 147 | pub fn new(writer: W, cipher: Cipher, key: &[u8], iv: &[u8]) -> Result { 148 | Ok(Self { 149 | inner: Cryptostream::new(Mode::Encrypt, writer, cipher, key, iv)?, 150 | }) 151 | } 152 | 153 | /// Finishes writing to the underlying cryptostream, padding the final block as needed, 154 | /// flushing all output. Returns the wrapped `Write` instance. 155 | pub fn finish(self) -> Result { 156 | self.inner.finish() 157 | } 158 | } 159 | 160 | impl Write for Encryptor { 161 | /// Writes decrypted bytes to the cryptostream, causing their encrypted contents to be written 162 | /// to the underlying `Write` object. 163 | fn write(&mut self, mut buf: &[u8]) -> Result { 164 | self.inner.write(&mut buf) 165 | } 166 | 167 | /// Flushes the underlying stream but does not clear all internal buffers or explicitly pad the 168 | /// output blocks as that would prevent us from appeding anything in the future if we are not a 169 | /// block boundary. 170 | fn flush(&mut self) -> Result<(), Error> { 171 | self.inner.flush() 172 | } 173 | } 174 | 175 | /// A decrypting stream adapter that decrypts what is written to it 176 | /// 177 | /// `write::Decryptor` is a stream adapter that sits atop a `Write` stream. Ciphertext written to 178 | /// the `Decryptor` is decrypted and written to the underlying stream. 179 | pub struct Decryptor { 180 | inner: Cryptostream, 181 | } 182 | 183 | impl Decryptor { 184 | pub fn new(writer: W, cipher: Cipher, key: &[u8], iv: &[u8]) -> Result { 185 | Ok(Self { 186 | inner: Cryptostream::new(Mode::Decrypt, writer, cipher, key, iv)?, 187 | }) 188 | } 189 | 190 | /// Finishes writing to the underlying cryptostream, padding the final block as needed, 191 | /// flushing all output. Returns the wrapped `Write` instance. 192 | pub fn finish(self) -> Result { 193 | self.inner.finish() 194 | } 195 | } 196 | 197 | impl Write for Decryptor { 198 | /// Writes encrypted bytes to the cryptostream, causing their decrypted contents to be written 199 | /// to the underlying `Write` object. 200 | fn write(&mut self, mut buf: &[u8]) -> Result { 201 | self.inner.write(&mut buf) 202 | } 203 | 204 | /// Flushes the underlying stream but does not clear all internal buffers or explicitly pad the 205 | /// output blocks as that would prevent us from reading any further in the future if we are not 206 | /// a block boundary. 207 | fn flush(&mut self) -> Result<(), Error> { 208 | self.inner.flush() 209 | } 210 | } 211 | --------------------------------------------------------------------------------