├── .clippy.toml ├── .gitignore ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── LICENSE-MIT ├── README.md └── src ├── aes.rs ├── aes_cbc.rs ├── common.rs ├── hmac.rs ├── lib.rs ├── pbkdf2.rs ├── pkcs7.rs ├── salsa20.rs ├── scrypt.rs └── sha256.rs /.clippy.toml: -------------------------------------------------------------------------------- 1 | single-char-binding-names-threshold = 10 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | rust: 4 | - stable 5 | - beta 6 | before_script: 7 | - rustup component add clippy rustfmt 8 | script: 9 | - cargo clippy --all-targets --all-features -- -D warnings 10 | - cargo fmt --all -- --check 11 | - cargo build 12 | - cargo test 13 | matrix: 14 | allow_failures: 15 | - rust: nightly 16 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "dumb-crypto" 3 | version = "3.1.0" 4 | 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dumb-crypto" 3 | description = "Dumb, but easily verifiable implementations of crypto algorithms" 4 | version = "3.1.0" 5 | authors = ["Fedor Indutny "] 6 | repository = "https://github.com/indutny/dumb-crypto" 7 | license = "MIT" 8 | categories = ["cryptography"] 9 | keywords = ["crypto","sha256","hmac","pbkdf2","scrypt"] 10 | readme = "README.md" 11 | exclude = [".travis.yml"] 12 | edition = "2018" 13 | 14 | [dependencies] 15 | 16 | [badges] 17 | travis-ci = { repository = "indutny/dumb-crypto" } 18 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | This software is licensed under the MIT License. 2 | 3 | Copyright Fedor Indutny, 2019. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a 6 | copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to permit 10 | persons to whom the Software is furnished to do so, subject to the 11 | following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 19 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 21 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 22 | USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dumb-crypto 2 | [![Build Status](https://secure.travis-ci.org/indutny/dumb-crypto.svg)](http://travis-ci.org/indutny/dumb-crypto) 3 | [![Latest version](https://img.shields.io/crates/v/dumb-crypto.svg)](https://crates.io/crates/dumb-crypto) 4 | [![Documentation](https://docs.rs/dumb-crypto/badge.svg)][docs] 5 | ![License](https://img.shields.io/crates/l/dumb-crypto.svg) 6 | 7 | This library implements following cryptographic routines in the dumbest and 8 | the most obvious way: 9 | 10 | - sha256 11 | - hmac-sha256 12 | - pbkdf2-sha256 13 | - salsa20 14 | - scrypt 15 | 16 | ## Why? 17 | 18 | Normally, one would find a highly optimized code implementing those. 19 | However, verifying such code is a non-trivial task. All routines (except for 20 | scrypt itself) are pre-requisites for scrypt, and a provided just for 21 | convenience. 22 | 23 | ## Quick example 24 | 25 | ```rust 26 | extern crate dumb_crypto; 27 | 28 | use::dumb_crypto::scrypt::Scrypt; 29 | 30 | let scrypt = Scrypt::new(1, 128, 1); 31 | 32 | let mut out: [u8; 8] = [0; 8]; 33 | 34 | scrypt.derive(b"passphrase", b"salt", &mut out); 35 | 36 | assert_eq!(out.to_vec(), vec![ 37 | 79, 35, 225, 99, 145, 145, 172, 245, 38 | ]); 39 | ``` 40 | 41 | ## Using dumb-crypto 42 | 43 | See [documentation][docs] for details. 44 | 45 | [docs]: https://docs.rs/dumb-crypto 46 | -------------------------------------------------------------------------------- /src/aes.rs: -------------------------------------------------------------------------------- 1 | //! # AES 2 | //! 3 | //! Implementation of AES encryption/decryption algorithm according to 4 | //! [FIPS 197][fips]. 5 | //! 6 | //! [fips]: https://csrc.nist.gov/csrc/media/publications/fips/197/final/documents/fips-197.pdf 7 | //! 8 | 9 | use std::error::Error; 10 | use std::fmt; 11 | use std::fmt::Display; 12 | 13 | pub const BLOCK_SIZE: usize = 16; 14 | const NB: usize = 4; 15 | 16 | // Note: block[column][row] 17 | type Block = [[u8; 4]; 4]; 18 | 19 | const SBOX: [u8; 256] = [ 20 | 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 21 | 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 22 | 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 23 | 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 24 | 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 25 | 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 26 | 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 27 | 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 28 | 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 29 | 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 30 | 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 31 | 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 32 | 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 33 | 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 34 | 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 35 | 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, 36 | ]; 37 | 38 | const INV_SBOX: [u8; 256] = [ 39 | 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 40 | 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 41 | 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 42 | 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 43 | 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 44 | 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 45 | 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 46 | 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 47 | 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 48 | 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 49 | 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 50 | 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 51 | 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 52 | 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 53 | 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 54 | 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d, 55 | ]; 56 | 57 | // Rcon[i], contains the values given by [ x^(i - 1), 0, 0, 0 ], with x^(i - 1) 58 | // being powers of x (x denoted as {02}) in the field GF(2^8). (note that i 59 | // starts at 1, not 0) 60 | const RCON: [u32; 10] = [ 61 | 0x0100_0000, 62 | 0x0200_0000, 63 | 0x0400_0000, 64 | 0x0800_0000, 65 | 0x1000_0000, 66 | 0x2000_0000, 67 | 0x4000_0000, 68 | 0x8000_0000, 69 | 0x1b00_0000, 70 | 0x3600_0000, 71 | ]; 72 | 73 | // Double the number in the binary field. 74 | fn double(b: u8) -> u8 { 75 | if (b & 0x80) == 0 { 76 | b << 1 77 | } else { 78 | (b << 1) ^ 0x1b 79 | } 80 | } 81 | 82 | // Multiply the number by another number in the binary field. 83 | fn mul(b: u8, mut by: usize) -> u8 { 84 | let mut res: u8 = 0; 85 | let mut power = b; 86 | while by != 0 { 87 | if (by & 1) != 0 { 88 | res ^= power; 89 | } 90 | power = double(power); 91 | by >>= 1; 92 | } 93 | res 94 | } 95 | 96 | fn sub_word(b: u32) -> u32 { 97 | let b0 = (b >> 24) as usize; 98 | let b1 = ((b >> 16) & 0xff) as usize; 99 | let b2 = ((b >> 8) & 0xff) as usize; 100 | let b3 = (b & 0xff) as usize; 101 | 102 | (u32::from(SBOX[b0]) << 24) 103 | | (u32::from(SBOX[b1]) << 16) 104 | | (u32::from(SBOX[b2]) << 8) 105 | | u32::from(SBOX[b3]) 106 | } 107 | 108 | fn rot_word(b: u32) -> u32 { 109 | (b << 8) | (b >> 24) 110 | } 111 | 112 | fn expand_key(key: &[u8], round_count: usize) -> Vec { 113 | // byte key[4*Nk] 114 | let nk = key.len() / 4; 115 | 116 | // word w[Nb*(Nr+1)] 117 | let mut w = Vec::with_capacity(NB * (round_count + 1)); 118 | 119 | // word temp 120 | // i = 0 121 | // while (i < Nk) 122 | // w[i] = word(key[4*i], key[4*i+1], key[4*i+2], key[4*i+3]) 123 | // i = i+1 124 | // end while 125 | 126 | let mut i: usize = 0; 127 | while i < nk { 128 | w.push( 129 | (u32::from(key[4 * i]) << 24) 130 | | (u32::from(key[4 * i + 1]) << 16) 131 | | (u32::from(key[4 * i + 2]) << 8) 132 | | u32::from(key[4 * i + 3]), 133 | ); 134 | i += 1; 135 | } 136 | 137 | // i = Nk 138 | // while (i < Nb * (Nr+1)] 139 | // temp = w[i-1] 140 | // if (i mod Nk = 0) 141 | // temp = SubWord(RotWord(temp)) xor Rcon[i/Nk] 142 | // else if (Nk > 6 and i mod Nk = 4) 143 | // temp = SubWord(temp) 144 | // end if 145 | // w[i] = w[i-Nk] xor temp 146 | // i = i + 1 147 | // end while 148 | while i < NB * (round_count + 1) { 149 | let mut temp = w[i - 1]; 150 | if i % nk == 0 { 151 | temp = sub_word(rot_word(temp)) ^ RCON[i / nk - 1]; 152 | } else if nk > 6 && i % nk == 4 { 153 | temp = sub_word(temp); 154 | } 155 | w.push(w[i - nk] ^ temp); 156 | i += 1; 157 | } 158 | 159 | w 160 | } 161 | 162 | trait AESSide { 163 | fn lookup_sbox(b: u8) -> u8; 164 | fn mix_column(col: [u8; 4]) -> [u8; 4]; 165 | fn shift_rows(s: &Block) -> Block; 166 | 167 | // Common methods 168 | 169 | fn add_round_key(s: &mut Block, round_key: &[u32]) { 170 | for (col, &key) in s.iter_mut().zip(round_key) { 171 | col[0] ^= (key >> 24) as u8; 172 | col[1] ^= ((key >> 16) & 0xff) as u8; 173 | col[2] ^= ((key >> 8) & 0xff) as u8; 174 | col[3] ^= (key & 0xff) as u8; 175 | } 176 | } 177 | 178 | fn sub_bytes(s: &mut Block) { 179 | for col in s.iter_mut() { 180 | for cell in col.iter_mut() { 181 | *cell = Self::lookup_sbox(*cell); 182 | } 183 | } 184 | } 185 | 186 | fn mix_columns(s: &mut Block) { 187 | for column in s.iter_mut() { 188 | *column = Self::mix_column(*column); 189 | } 190 | } 191 | } 192 | 193 | struct AESEncrypt {} 194 | struct AESDecrypt {} 195 | 196 | impl AESSide for AESEncrypt { 197 | fn lookup_sbox(b: u8) -> u8 { 198 | SBOX[usize::from(b)] 199 | } 200 | 201 | fn mix_column(col: [u8; 4]) -> [u8; 4] { 202 | [ 203 | mul(col[0], 2) ^ mul(col[1], 3) ^ col[2] ^ col[3], 204 | mul(col[1], 2) ^ mul(col[2], 3) ^ col[3] ^ col[0], 205 | mul(col[2], 2) ^ mul(col[3], 3) ^ col[0] ^ col[1], 206 | mul(col[3], 2) ^ mul(col[0], 3) ^ col[1] ^ col[2], 207 | ] 208 | } 209 | 210 | fn shift_rows(s: &Block) -> Block { 211 | [ 212 | [s[0][0], s[1][1], s[2][2], s[3][3]], 213 | [s[1][0], s[2][1], s[3][2], s[0][3]], 214 | [s[2][0], s[3][1], s[0][2], s[1][3]], 215 | [s[3][0], s[0][1], s[1][2], s[2][3]], 216 | ] 217 | } 218 | } 219 | 220 | impl AESSide for AESDecrypt { 221 | fn lookup_sbox(b: u8) -> u8 { 222 | INV_SBOX[usize::from(b)] 223 | } 224 | 225 | fn mix_column(col: [u8; 4]) -> [u8; 4] { 226 | [ 227 | mul(col[0], 0x0e) ^ mul(col[1], 0x0b) ^ mul(col[2], 0x0d) ^ mul(col[3], 0x09), 228 | mul(col[1], 0x0e) ^ mul(col[2], 0x0b) ^ mul(col[3], 0x0d) ^ mul(col[0], 0x09), 229 | mul(col[2], 0x0e) ^ mul(col[3], 0x0b) ^ mul(col[0], 0x0d) ^ mul(col[1], 0x09), 230 | mul(col[3], 0x0e) ^ mul(col[0], 0x0b) ^ mul(col[1], 0x0d) ^ mul(col[2], 0x09), 231 | ] 232 | } 233 | 234 | fn shift_rows(s: &Block) -> Block { 235 | [ 236 | [s[0][0], s[3][1], s[2][2], s[1][3]], 237 | [s[1][0], s[0][1], s[3][2], s[2][3]], 238 | [s[2][0], s[1][1], s[0][2], s[3][3]], 239 | [s[3][0], s[2][1], s[1][2], s[0][3]], 240 | ] 241 | } 242 | } 243 | 244 | fn to_block(b: &[u8; BLOCK_SIZE]) -> Block { 245 | [ 246 | [b[0], b[1], b[2], b[3]], 247 | [b[4], b[5], b[6], b[7]], 248 | [b[8], b[9], b[10], b[11]], 249 | [b[12], b[13], b[14], b[15]], 250 | ] 251 | } 252 | 253 | fn from_block(s: &Block) -> [u8; BLOCK_SIZE] { 254 | [ 255 | s[0][0], s[0][1], s[0][2], s[0][3], s[1][0], s[1][1], s[1][2], s[1][3], s[2][0], s[2][1], 256 | s[2][2], s[2][3], s[3][0], s[3][1], s[3][2], s[3][3], 257 | ] 258 | } 259 | 260 | /// Possible initialization errors 261 | #[derive(Debug, PartialEq)] 262 | pub enum AESError { 263 | /// Returned when key size is neither of: 128, 192, or 256 bits. 264 | InvalidKeySize, 265 | 266 | /// Returned when attempting encryption/decryption on non-initialized AES 267 | /// instance 268 | NotInitialized, 269 | } 270 | 271 | impl Display for AESError { 272 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 273 | write!(f, "AESError: {}", self.description()) 274 | } 275 | } 276 | 277 | impl Error for AESError { 278 | fn description(&self) -> &str { 279 | match self { 280 | AESError::InvalidKeySize => "key size must be either of: 128, 192, or 256 bits", 281 | AESError::NotInitialized => "AES instance must be initialized prior to use", 282 | } 283 | } 284 | } 285 | 286 | /// 287 | /// Main AES structure. 288 | /// 289 | /// Usage: 290 | /// ```rust 291 | /// extern crate dumb_crypto; 292 | /// 293 | /// use dumb_crypto::aes::{AES, BLOCK_SIZE}; 294 | /// 295 | /// let mut aes = AES::new(); 296 | /// let key: [u8; 16] = [ 297 | /// 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 298 | /// 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c, 299 | /// ]; 300 | /// aes.init(&key).unwrap(); 301 | /// 302 | /// let cleartext: [u8; BLOCK_SIZE] = [ 303 | /// 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 304 | /// 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34, 305 | /// ]; 306 | /// 307 | /// let ciphertext = aes.encrypt(&cleartext).unwrap(); 308 | /// 309 | /// assert_eq!(aes.decrypt(&ciphertext).unwrap(), cleartext); 310 | /// ``` 311 | /// 312 | pub struct AES { 313 | round_count: usize, 314 | round_keys: Option>, 315 | } 316 | 317 | impl AES { 318 | /// Create new uninitialized instance of AES. 319 | pub fn new() -> Self { 320 | AES { 321 | round_count: 0, 322 | round_keys: None, 323 | } 324 | } 325 | 326 | /// Initialize an instance with a encryption/decryption key. 327 | pub fn init(&mut self, key: &[u8]) -> Result<(), AESError> { 328 | self.round_count = match key.len() { 329 | // 128 bits 330 | 16 => 10, 331 | 332 | // 192 bits 333 | 24 => 12, 334 | 335 | // 256 bits 336 | 32 => 14, 337 | 338 | // Invalid key size 339 | _ => { 340 | return Err(AESError::InvalidKeySize); 341 | } 342 | }; 343 | self.round_keys = Some(expand_key(key, self.round_count)); 344 | 345 | Ok(()) 346 | } 347 | 348 | /// Encrypt block of data. 349 | pub fn encrypt(&self, b: &[u8; BLOCK_SIZE]) -> Result<[u8; BLOCK_SIZE], AESError> { 350 | let nr = self.round_count; 351 | 352 | let mut state = to_block(b); 353 | let round_keys = match &self.round_keys { 354 | Some(keys) => keys, 355 | None => { 356 | return Err(AESError::NotInitialized); 357 | } 358 | }; 359 | 360 | AESEncrypt::add_round_key(&mut state, &round_keys[0..NB]); 361 | 362 | for round in 1..nr { 363 | AESEncrypt::sub_bytes(&mut state); 364 | state = AESEncrypt::shift_rows(&state); 365 | AESEncrypt::mix_columns(&mut state); 366 | AESEncrypt::add_round_key(&mut state, &round_keys[(round * NB)..((round + 1) * NB)]); 367 | } 368 | 369 | AESEncrypt::sub_bytes(&mut state); 370 | state = AESEncrypt::shift_rows(&state); 371 | AESEncrypt::add_round_key(&mut state, &round_keys[(nr * NB)..]); 372 | 373 | Ok(from_block(&state)) 374 | } 375 | 376 | /// Decrypt block of data. 377 | pub fn decrypt(&self, b: &[u8; BLOCK_SIZE]) -> Result<[u8; BLOCK_SIZE], AESError> { 378 | let nr = self.round_count; 379 | 380 | let mut state = to_block(b); 381 | let round_keys = match &self.round_keys { 382 | Some(keys) => keys, 383 | None => { 384 | return Err(AESError::NotInitialized); 385 | } 386 | }; 387 | 388 | AESDecrypt::add_round_key(&mut state, &round_keys[(nr * NB)..]); 389 | 390 | for round in (1..nr).rev() { 391 | state = AESDecrypt::shift_rows(&state); 392 | AESDecrypt::sub_bytes(&mut state); 393 | AESDecrypt::add_round_key(&mut state, &round_keys[(round * NB)..((round + 1) * NB)]); 394 | AESDecrypt::mix_columns(&mut state); 395 | } 396 | 397 | state = AESDecrypt::shift_rows(&state); 398 | AESDecrypt::sub_bytes(&mut state); 399 | AESDecrypt::add_round_key(&mut state, &round_keys[0..NB]); 400 | 401 | Ok(from_block(&state)) 402 | } 403 | } 404 | 405 | impl Default for AES { 406 | fn default() -> Self { 407 | Self::new() 408 | } 409 | } 410 | 411 | #[cfg(test)] 412 | mod tests { 413 | use super::*; 414 | use crate::common::hex_to_vec; 415 | 416 | const TEST_BLOCK: Block = [ 417 | [0x37, 0xd7, 0xa0, 0x2d], 418 | [0x8a, 0x65, 0xc1, 0x96], 419 | [0xda, 0xee, 0x01, 0x99], 420 | [0xb9, 0x9e, 0x55, 0x65], 421 | ]; 422 | 423 | #[test] 424 | fn it_should_multiply_in_field() { 425 | assert_eq!(mul(1, 2), 2); 426 | assert_eq!(mul(1, 3), 3); 427 | 428 | assert_eq!(mul(0x57, 0x02), 0xae); 429 | assert_eq!(mul(0x57, 0x04), 0x47); 430 | assert_eq!(mul(0x57, 0x08), 0x8e); 431 | assert_eq!(mul(0x57, 0x10), 0x07); 432 | assert_eq!(mul(0x57, 0x13), 0xfe); 433 | } 434 | 435 | #[test] 436 | fn it_should_inverse_sub_bytes() { 437 | let mut s: Block = TEST_BLOCK; 438 | AESEncrypt::sub_bytes(&mut s); 439 | AESDecrypt::sub_bytes(&mut s); 440 | assert_eq!(s, TEST_BLOCK); 441 | } 442 | 443 | #[test] 444 | fn it_should_inverse_mix_columns() { 445 | let mut s: Block = TEST_BLOCK; 446 | AESEncrypt::mix_columns(&mut s); 447 | AESDecrypt::mix_columns(&mut s); 448 | assert_eq!(s, TEST_BLOCK); 449 | } 450 | 451 | #[test] 452 | fn it_should_inverse_shift_rows() { 453 | let t = AESEncrypt::shift_rows(&TEST_BLOCK); 454 | assert_eq!(AESDecrypt::shift_rows(&t), TEST_BLOCK); 455 | } 456 | 457 | #[test] 458 | fn it_should_expand_128bit_key() { 459 | assert_eq!( 460 | expand_key( 461 | &[ 462 | 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 463 | 0xcf, 0x4f, 0x3c 464 | ], 465 | 10 466 | ), 467 | vec![ 468 | 0x2b7e_1516, 469 | 0x28ae_d2a6, 470 | 0xabf7_1588, 471 | 0x09cf_4f3c, 472 | 0xa0fa_fe17, 473 | 0x8854_2cb1, 474 | 0x23a3_3939, 475 | 0x2a6c_7605, 476 | 0xf2c2_95f2, 477 | 0x7a96_b943, 478 | 0x5935_807a, 479 | 0x7359_f67f, 480 | 0x3d80_477d, 481 | 0x4716_fe3e, 482 | 0x1e23_7e44, 483 | 0x6d7a_883b, 484 | 0xef44_a541, 485 | 0xa852_5b7f, 486 | 0xb671_253b, 487 | 0xdb0b_ad00, 488 | 0xd4d1_c6f8, 489 | 0x7c83_9d87, 490 | 0xcaf2_b8bc, 491 | 0x11f9_15bc, 492 | 0x6d88_a37a, 493 | 0x110b_3efd, 494 | 0xdbf9_8641, 495 | 0xca00_93fd, 496 | 0x4e54_f70e, 497 | 0x5f5f_c9f3, 498 | 0x84a6_4fb2, 499 | 0x4ea6_dc4f, 500 | 0xead2_7321, 501 | 0xb58d_bad2, 502 | 0x312b_f560, 503 | 0x7f8d_292f, 504 | 0xac77_66f3, 505 | 0x19fa_dc21, 506 | 0x28d1_2941, 507 | 0x575c_006e, 508 | 0xd014_f9a8, 509 | 0xc9ee_2589, 510 | 0xe13f_0cc8, 511 | 0xb663_0ca6, 512 | ], 513 | ); 514 | } 515 | 516 | #[test] 517 | fn it_should_expand_192bit_key() { 518 | assert_eq!( 519 | expand_key( 520 | &[ 521 | 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 522 | 0x90, 0x79, 0xe5, 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b, 523 | ], 524 | 12 525 | ), 526 | vec![ 527 | 0x8e73_b0f7, 528 | 0xda0e_6452, 529 | 0xc810_f32b, 530 | 0x8090_79e5, 531 | 0x62f8_ead2, 532 | 0x522c_6b7b, 533 | 0xfe0c_91f7, 534 | 0x2402_f5a5, 535 | 0xec12_068e, 536 | 0x6c82_7f6b, 537 | 0x0e7a_95b9, 538 | 0x5c56_fec2, 539 | 0x4db7_b4bd, 540 | 0x69b5_4118, 541 | 0x85a7_4796, 542 | 0xe925_38fd, 543 | 0xe75f_ad44, 544 | 0xbb09_5386, 545 | 0x485a_f057, 546 | 0x21ef_b14f, 547 | 0xa448_f6d9, 548 | 0x4d6d_ce24, 549 | 0xaa32_6360, 550 | 0x113b_30e6, 551 | 0xa25e_7ed5, 552 | 0x83b1_cf9a, 553 | 0x27f9_3943, 554 | 0x6a94_f767, 555 | 0xc0a6_9407, 556 | 0xd19d_a4e1, 557 | 0xec17_86eb, 558 | 0x6fa6_4971, 559 | 0x485f_7032, 560 | 0x22cb_8755, 561 | 0xe26d_1352, 562 | 0x33f0_b7b3, 563 | 0x40be_eb28, 564 | 0x2f18_a259, 565 | 0x6747_d26b, 566 | 0x458c_553e, 567 | 0xa7e1_466c, 568 | 0x9411_f1df, 569 | 0x821f_750a, 570 | 0xad07_d753, 571 | 0xca40_0538, 572 | 0x8fcc_5006, 573 | 0x282d_166a, 574 | 0xbc3c_e7b5, 575 | 0xe98b_a06f, 576 | 0x448c_773c, 577 | 0x8ecc_7204, 578 | 0x0100_2202, 579 | ], 580 | ); 581 | } 582 | 583 | #[test] 584 | fn it_should_expand_256bit_key() { 585 | assert_eq!( 586 | expand_key( 587 | &[ 588 | 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 589 | 0x7d, 0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 590 | 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4, 591 | ], 592 | 14 593 | ), 594 | vec![ 595 | 0x603d_eb10, 596 | 0x15ca_71be, 597 | 0x2b73_aef0, 598 | 0x857d_7781, 599 | 0x1f35_2c07, 600 | 0x3b61_08d7, 601 | 0x2d98_10a3, 602 | 0x0914_dff4, 603 | 0x9ba3_5411, 604 | 0x8e69_25af, 605 | 0xa51a_8b5f, 606 | 0x2067_fcde, 607 | 0xa8b0_9c1a, 608 | 0x93d1_94cd, 609 | 0xbe49_846e, 610 | 0xb75d_5b9a, 611 | 0xd59a_ecb8, 612 | 0x5bf3_c917, 613 | 0xfee9_4248, 614 | 0xde8e_be96, 615 | 0xb5a9_328a, 616 | 0x2678_a647, 617 | 0x9831_2229, 618 | 0x2f6c_79b3, 619 | 0x812c_81ad, 620 | 0xdadf_48ba, 621 | 0x2436_0af2, 622 | 0xfab8_b464, 623 | 0x98c5_bfc9, 624 | 0xbebd_198e, 625 | 0x268c_3ba7, 626 | 0x09e0_4214, 627 | 0x6800_7bac, 628 | 0xb2df_3316, 629 | 0x96e9_39e4, 630 | 0x6c51_8d80, 631 | 0xc814_e204, 632 | 0x76a9_fb8a, 633 | 0x5025_c02d, 634 | 0x59c5_8239, 635 | 0xde13_6967, 636 | 0x6ccc_5a71, 637 | 0xfa25_6395, 638 | 0x9674_ee15, 639 | 0x5886_ca5d, 640 | 0x2e2f_31d7, 641 | 0x7e0a_f1fa, 642 | 0x27cf_73c3, 643 | 0x749c_47ab, 644 | 0x1850_1dda, 645 | 0xe275_7e4f, 646 | 0x7401_905a, 647 | 0xcafa_aae3, 648 | 0xe4d5_9b34, 649 | 0x9adf_6ace, 650 | 0xbd10_190d, 651 | 0xfe48_90d1, 652 | 0xe618_8d0b, 653 | 0x046d_f344, 654 | 0x706c_631e, 655 | ], 656 | ); 657 | } 658 | 659 | #[test] 660 | fn it_should_encrypt_first_test_from_the_spec() { 661 | let mut aes = AES::new(); 662 | let key: [u8; 16] = [ 663 | 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 664 | 0x4f, 0x3c, 665 | ]; 666 | aes.init(&key).expect("init to not fail"); 667 | 668 | let cleartext: [u8; BLOCK_SIZE] = [ 669 | 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 670 | 0x07, 0x34, 671 | ]; 672 | 673 | let ciphertext = aes.encrypt(&cleartext).expect("encrypt to not fail"); 674 | 675 | assert_eq!( 676 | ciphertext, 677 | [ 678 | 0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 679 | 0x0b, 0x32, 680 | ] 681 | ); 682 | } 683 | 684 | fn hex_to_block(hex: &str) -> [u8; 16] { 685 | let vec = hex_to_vec(hex); 686 | let mut b = [0; 16]; 687 | 688 | for (i, elem) in vec.into_iter().enumerate() { 689 | b[i] = elem; 690 | } 691 | 692 | b 693 | } 694 | 695 | fn check_cipher_vector(key: &str, input: &str, output: &str) { 696 | let mut aes = AES::new(); 697 | aes.init(&hex_to_vec(key)).expect("init to not fail"); 698 | 699 | assert_eq!( 700 | aes.encrypt(&hex_to_block(input)) 701 | .expect("encrypt to not fail"), 702 | hex_to_block(output) 703 | ); 704 | assert_eq!( 705 | aes.decrypt(&hex_to_block(output)) 706 | .expect("decrypt to not fail"), 707 | hex_to_block(input) 708 | ); 709 | } 710 | 711 | #[test] 712 | fn it_should_encrypt_test_vector_0() { 713 | check_cipher_vector( 714 | "000102030405060708090a0b0c0d0e0f", 715 | "00112233445566778899aabbccddeeff", 716 | "69c4e0d86a7b0430d8cdb78070b4c55a", 717 | ); 718 | } 719 | 720 | #[test] 721 | fn it_should_encrypt_test_vector_1() { 722 | check_cipher_vector( 723 | "000102030405060708090a0b0c0d0e0f1011121314151617", 724 | "00112233445566778899aabbccddeeff", 725 | "dda97ca4864cdfe06eaf70a0ec0d7191", 726 | ); 727 | } 728 | 729 | #[test] 730 | fn it_should_encrypt_test_vector_2() { 731 | check_cipher_vector( 732 | "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 733 | "00112233445566778899aabbccddeeff", 734 | "8ea2b7ca516745bfeafc49904b496089", 735 | ); 736 | } 737 | } 738 | -------------------------------------------------------------------------------- /src/aes_cbc.rs: -------------------------------------------------------------------------------- 1 | //! # AES-CBC 2 | //! 3 | //! Implementation of CBC-mode AES cipher/decipher. 4 | //! 5 | 6 | use crate::aes::{AESError, AES, BLOCK_SIZE}; 7 | use crate::pkcs7; 8 | 9 | use std::error::Error; 10 | use std::fmt; 11 | use std::fmt::Display; 12 | 13 | /// 14 | /// Main AES-CBC Cipher structure. 15 | /// 16 | /// Usage: 17 | /// ```rust 18 | /// extern crate dumb_crypto; 19 | /// 20 | /// use dumb_crypto::aes::BLOCK_SIZE; 21 | /// use dumb_crypto::aes_cbc::Cipher; 22 | /// 23 | /// let iv = [ 24 | /// 0x4c, 0xb4, 0x52, 0xd6, 0x78, 0xca, 0x94, 0x61, 25 | /// 0x92, 0xcd, 0xc6, 0x91, 0xb7, 0xab, 0x61, 0x76, 26 | /// ]; 27 | /// let key: [u8; 16] = [ 28 | /// 0x69, 0xc2, 0xa9, 0xe6, 0x2b, 0x61, 0x30, 0x60, 29 | /// 0xc9, 0x79, 0x7b, 0xdc, 0xe4, 0xf6, 0x40, 0x8e, 30 | /// ]; 31 | /// let mut cipher = Cipher::new(iv); 32 | /// cipher.init(&key).unwrap(); 33 | /// 34 | /// let mut ciphertext = cipher.write("aes-cbc with iv".as_bytes()).unwrap(); 35 | /// ciphertext.append(&mut cipher.flush().unwrap()); 36 | /// ``` 37 | /// 38 | pub struct Cipher { 39 | aes: AES, 40 | pad: pkcs7::Pad, 41 | iv: [u8; BLOCK_SIZE], 42 | } 43 | 44 | impl Cipher { 45 | /// Create new instance of AES-CBC Cipher 46 | /// 47 | /// `iv` - Initialization Vector (this better be a very random value) 48 | pub fn new(iv: [u8; BLOCK_SIZE]) -> Self { 49 | Cipher { 50 | aes: AES::new(), 51 | pad: pkcs7::Pad::new(BLOCK_SIZE), 52 | iv, 53 | } 54 | } 55 | 56 | /// Initialize instance of AES-CBC Cipher 57 | /// 58 | /// `key` - AES Key. Must be either 128, 192, or 256 bits. 59 | pub fn init(&mut self, key: &[u8]) -> Result<(), AESError> { 60 | self.aes.init(key)?; 61 | Ok(()) 62 | } 63 | 64 | /// Encrypt data 65 | pub fn write(&mut self, b: &[u8]) -> Result, AESError> { 66 | let blocks = match self.pad.write(b) { 67 | None => { 68 | return Ok(vec![]); 69 | } 70 | Some(block) => block, 71 | }; 72 | 73 | let mut result: Vec = Vec::with_capacity(blocks.len()); 74 | for block in blocks.chunks(BLOCK_SIZE) { 75 | let mut ciphertext = self.encrypt_block(block)?; 76 | result.append(&mut ciphertext); 77 | } 78 | Ok(result) 79 | } 80 | 81 | /// Encrypt and return final cipher block 82 | pub fn flush(&mut self) -> Result, AESError> { 83 | let block = self.pad.flush(); 84 | Ok(self.encrypt_block(&block)?) 85 | } 86 | 87 | fn encrypt_block(&mut self, b: &[u8]) -> Result, AESError> { 88 | let mut block = [0; BLOCK_SIZE]; 89 | block.copy_from_slice(&b); 90 | 91 | for (elem, iv_elem) in block.iter_mut().zip(self.iv.iter()) { 92 | *elem ^= iv_elem; 93 | } 94 | 95 | match self.aes.encrypt(&block) { 96 | Ok(new_iv) => { 97 | self.iv = new_iv; 98 | Ok(new_iv.to_vec()) 99 | } 100 | Err(err) => Err(err), 101 | } 102 | } 103 | } 104 | 105 | /// 106 | /// Main AES-CBC Decipher structure. 107 | /// 108 | /// Usage: 109 | /// ```rust 110 | /// extern crate dumb_crypto; 111 | /// 112 | /// use::dumb_crypto::aes::BLOCK_SIZE; 113 | /// use::dumb_crypto::aes_cbc::Decipher; 114 | /// 115 | /// let iv = [ 116 | /// 0x4c, 0xb4, 0x52, 0xd6, 0x78, 0xca, 0x94, 0x61, 117 | /// 0x92, 0xcd, 0xc6, 0x91, 0xb7, 0xab, 0x61, 0x76, 118 | /// ]; 119 | /// let key: [u8; 16] = [ 120 | /// 0x69, 0xc2, 0xa9, 0xe6, 0x2b, 0x61, 0x30, 0x60, 121 | /// 0xc9, 0x79, 0x7b, 0xdc, 0xe4, 0xf6, 0x40, 0x8e, 122 | /// ]; 123 | /// let mut decipher = Decipher::new(iv); 124 | /// decipher.init(&key).unwrap(); 125 | /// 126 | /// let mut cleartext = decipher.write(&[ 127 | /// 0x98, 0xba, 0x8e, 0x07, 0x5b, 0xcf, 0xa7, 0xb9, 128 | /// 0x3a, 0xbe, 0x45, 0x3a, 0xb1, 0x84, 0xdc, 0x68, 129 | /// ]).unwrap(); 130 | /// cleartext.append(&mut decipher.flush().unwrap()); 131 | /// ``` 132 | /// 133 | pub struct Decipher { 134 | aes: AES, 135 | unpad: pkcs7::Unpad, 136 | iv: [u8; BLOCK_SIZE], 137 | buffer: pkcs7::Pad, 138 | } 139 | 140 | /// Possible decipher errors 141 | #[derive(Debug, PartialEq)] 142 | pub enum DecipherError { 143 | /// Returned on AES-specific errors 144 | AES(AESError), 145 | /// Returned on PKCS7-specific padding errors 146 | PKCS7(pkcs7::UnpadError), 147 | } 148 | 149 | impl Display for DecipherError { 150 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 151 | write!(f, "DecipherError: {}", self.description()) 152 | } 153 | } 154 | 155 | impl Error for DecipherError { 156 | fn description(&self) -> &str { 157 | // TODO(indutny): prefix description 158 | match self { 159 | DecipherError::AES(err) => err.description(), 160 | DecipherError::PKCS7(err) => err.description(), 161 | } 162 | } 163 | } 164 | 165 | impl Decipher { 166 | /// Create new instance of AES-CBC Decipher 167 | /// 168 | /// `iv` - Initialization Vector (this better be a very random value) 169 | pub fn new(iv: [u8; BLOCK_SIZE]) -> Self { 170 | Decipher { 171 | aes: AES::new(), 172 | unpad: pkcs7::Unpad::new(BLOCK_SIZE), 173 | iv, 174 | buffer: pkcs7::Pad::new(BLOCK_SIZE), 175 | } 176 | } 177 | 178 | /// Initialize instance of AES-CBC Decipher 179 | /// 180 | /// `key` - AES Key. Must be either 128, 192, or 256 bits. 181 | pub fn init(&mut self, key: &[u8]) -> Result<(), AESError> { 182 | self.aes.init(key)?; 183 | Ok(()) 184 | } 185 | 186 | /// Decrypt data 187 | pub fn write(&mut self, b: &[u8]) -> Result, DecipherError> { 188 | let blocks = match self.buffer.write(b) { 189 | Some(blocks) => blocks, 190 | None => { 191 | return Ok(vec![]); 192 | } 193 | }; 194 | 195 | let mut padded_result = Vec::with_capacity(blocks.len()); 196 | for ciphertext in blocks.chunks(BLOCK_SIZE) { 197 | let mut block = [0; BLOCK_SIZE]; 198 | block.copy_from_slice(&ciphertext); 199 | 200 | let mut cleartext = match self.aes.decrypt(&block) { 201 | Err(err) => return Err(DecipherError::AES(err)), 202 | Ok(out) => out, 203 | }; 204 | 205 | for (elem, iv_elem) in cleartext.iter_mut().zip(self.iv.iter()) { 206 | *elem ^= iv_elem; 207 | } 208 | 209 | self.iv = block; 210 | padded_result.append(&mut cleartext.to_vec()); 211 | } 212 | 213 | Ok(self.unpad.write(&padded_result).unwrap_or_default()) 214 | } 215 | 216 | /// Decrypt and return final cleartext data 217 | pub fn flush(&mut self) -> Result, DecipherError> { 218 | match self.unpad.flush() { 219 | Err(err) => Err(DecipherError::PKCS7(err)), 220 | Ok(block) => Ok(block), 221 | } 222 | } 223 | } 224 | 225 | #[cfg(test)] 226 | mod tests { 227 | use super::*; 228 | use crate::common::hex_to_vec; 229 | 230 | fn check(key: &str, iv: &str, cleartext: &str, ciphertext: &str) { 231 | let key_vec = hex_to_vec(key); 232 | let mut iv_arr = [0; BLOCK_SIZE]; 233 | iv_arr.copy_from_slice(&hex_to_vec(iv)); 234 | 235 | let mut cipher = Cipher::new(iv_arr); 236 | cipher.init(&key_vec).expect("cipher init to not fail"); 237 | 238 | let mut actual = cipher 239 | .write(cleartext.as_bytes()) 240 | .expect("cipher write to not fail"); 241 | actual.append(&mut cipher.flush().expect("cipher flush to not fail")); 242 | 243 | assert_eq!(actual, hex_to_vec(ciphertext)); 244 | 245 | let mut decipher = Decipher::new(iv_arr); 246 | decipher.init(&key_vec).expect("decipher init to not fail"); 247 | let mut back = decipher 248 | .write(&hex_to_vec(ciphertext)) 249 | .expect("decipher write to not fail"); 250 | back.append(&mut decipher.flush().expect("decipher flush to not fail")); 251 | 252 | assert_eq!(back, cleartext.as_bytes()); 253 | } 254 | 255 | #[test] 256 | fn it_should_not_fail_on_vec_0() { 257 | check( 258 | "00000000000000000000000000000000", 259 | "00000000000000000000000000000000", 260 | "hello", 261 | "9834ed518cbc8fbe9af3c6ecb75eb8c0", 262 | ); 263 | } 264 | 265 | #[test] 266 | fn it_should_not_fail_on_vec_1() { 267 | check( 268 | "69c2a9e62b613060c9797bdce4f6408e", 269 | "4cb452d678ca946192cdc691b7ab6176", 270 | "aes-cbc with iv", 271 | "98ba8e075bcfa7b93abe453ab184dc68", 272 | ); 273 | } 274 | 275 | #[test] 276 | fn it_should_not_fail_on_vec_2() { 277 | check( 278 | "57e79712f7b7813490b63446e1bec39f", 279 | "a32fd274f2d5392f1a217aaa37a5a44d", 280 | "several AES blocks means more xoring", 281 | "d1aa1d92d6a93c84032ae322102aba62692b2548e92abc9f8d19bb42dd172e84c4102ff8d45889011b87de27f6d91ae4", 282 | ); 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/common.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! wrapping_sum { 3 | ( $a:expr, $( $x:expr ),* ) => { 4 | { 5 | let mut acc = $a; 6 | $( 7 | acc = acc.wrapping_add($x); 8 | )* 9 | acc 10 | } 11 | }; 12 | } 13 | 14 | #[cfg(test)] 15 | pub fn hex_to_vec(hex: &str) -> Vec { 16 | let mut iter = hex.as_bytes().iter().map(|letter| match letter { 17 | b'0'...b'9' => letter - b'0', 18 | b'a'...b'f' => letter - b'a' + 10, 19 | b'A'...b'F' => letter - b'A' + 10, 20 | _ => panic!(), 21 | }); 22 | 23 | let mut result: Vec = Vec::with_capacity(hex.len() / 2); 24 | while let (Some(h), Some(l)) = (iter.next(), iter.next()) { 25 | result.push((h << 4) | l); 26 | } 27 | result 28 | } 29 | -------------------------------------------------------------------------------- /src/hmac.rs: -------------------------------------------------------------------------------- 1 | //! # Hmac 2 | //! 3 | //! Implementation of HMac SHA256 message authentication algorithm according to 4 | //! [RFC 2104][rfc]. 5 | //! 6 | //! [rfc]: https://tools.ietf.org/html/rfc2104#section-2 7 | //! 8 | 9 | use crate::sha256::{BLOCK_SIZE, DIGEST_SIZE, SHA256}; 10 | 11 | /// 12 | /// Main hmac-sha256 structure. 13 | /// 14 | /// Usage: 15 | /// ```rust 16 | /// extern crate dumb_crypto; 17 | /// 18 | /// use dumb_crypto::hmac::HMac; 19 | /// 20 | /// let mut hmac = HMac::new(b"secret key"); 21 | /// 22 | /// hmac.update(b"hello world"); 23 | /// assert_eq!(hmac.digest().to_vec(), vec![ 24 | /// 0xc6, 0x1b, 0x51, 0x98, 0xdf, 0x58, 0x63, 0x9e, 25 | /// 0xdb, 0x98, 0x92, 0x51, 0x47, 0x56, 0xb8, 0x9a, 26 | /// 0x36, 0x85, 0x6d, 0x82, 0x6e, 0x5d, 0x85, 0x02, 27 | /// 0x3a, 0xb1, 0x81, 0xb4, 0x8e, 0xa5, 0xd0, 0x18, 28 | /// ]); 29 | /// ``` 30 | /// 31 | pub struct HMac { 32 | inner: SHA256, 33 | outer: SHA256, 34 | } 35 | 36 | // See https://tools.ietf.org/html/rfc2104#section-2 37 | 38 | impl HMac { 39 | /// 40 | /// Create new instance of Hmac-sha256 message authentication. 41 | /// 42 | /// Arguments: 43 | /// 44 | /// - `key` - secret key 45 | /// 46 | pub fn new(key: &[u8]) -> HMac { 47 | // 48 | // ipad = the byte 0x36 repeated B times 49 | // opad = the byte 0x5C repeated B times. 50 | // 51 | // To compute HMAC over the data `text' we perform 52 | // 53 | // H(K XOR opad, H(K XOR ipad, text)) 54 | // 55 | // Namely, 56 | // 57 | // (1) append zeros to the end of K to create a B byte string 58 | // (e.g., if K is of length 20 bytes and B=64, then K will be 59 | // appended with 44 zero bytes 0x00) 60 | // (2) XOR (bitwise exclusive-OR) the B byte string computed in step 61 | // (1) with ipad 62 | // (3) append the stream of data 'text' to the B byte string resulting 63 | // from step (2) 64 | // (4) apply H to the stream generated in step (3) 65 | // (5) XOR (bitwise exclusive-OR) the B byte string computed in 66 | // step (1) with opad 67 | // (6) append the H result from step (4) to the B byte string 68 | // resulting from step (5) 69 | // (7) apply H to the stream generated in step (6) and output 70 | // the result 71 | // 72 | 73 | // Hash the key to fit it into the block 74 | if key.len() > BLOCK_SIZE { 75 | let mut tmp = SHA256::new(); 76 | tmp.update(key); 77 | return HMac::new(&tmp.digest()); 78 | } 79 | 80 | let mut block: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE]; 81 | 82 | let mut inner = SHA256::new(); 83 | let mut outer = SHA256::new(); 84 | 85 | block[..key.len()].copy_from_slice(key); 86 | 87 | for elem in block.iter_mut() { 88 | *elem ^= 0x36; 89 | } 90 | inner.update(&block); 91 | 92 | /* NOTE: a ^ b ^ b ^ c = a ^ c */ 93 | for elem in block.iter_mut() { 94 | *elem ^= 0x36 ^ 0x5c; 95 | } 96 | outer.update(&block); 97 | 98 | HMac { inner, outer } 99 | } 100 | 101 | /// 102 | /// Add input `data` to the digest. 103 | /// 104 | pub fn update(self: &mut HMac, data: &[u8]) { 105 | self.inner.update(data); 106 | } 107 | 108 | /// 109 | /// Generate digest array. 110 | /// 111 | pub fn digest(self: &mut HMac) -> [u8; DIGEST_SIZE] { 112 | self.outer.update(&self.inner.digest()); 113 | self.outer.digest() 114 | } 115 | } 116 | 117 | #[cfg(test)] 118 | mod tests { 119 | use super::*; 120 | 121 | fn check(key: &[u8], inputs: &[&[u8]], expected: [u8; DIGEST_SIZE]) { 122 | let mut hmac = HMac::new(key); 123 | 124 | for chunk in inputs { 125 | hmac.update(chunk); 126 | } 127 | assert_eq!(hmac.digest(), expected); 128 | } 129 | 130 | // 131 | // See https://tools.ietf.org/html/rfc4231 132 | // 133 | 134 | #[test] 135 | fn it_should_compute_digest_for_vec0() { 136 | check( 137 | b"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", 138 | &[b"Hi There"], 139 | [ 140 | 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 141 | 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, 142 | 0x2e, 0x32, 0xcf, 0xf7, 143 | ], 144 | ); 145 | } 146 | 147 | #[test] 148 | fn it_should_compute_digest_for_vec1() { 149 | check( 150 | b"Jefe", 151 | &[b"what do ya want ", b"for nothing?"], 152 | [ 153 | 0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 154 | 0x75, 0xc7, 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, 0x9d, 0xec, 0x58, 0xb9, 155 | 0x64, 0xec, 0x38, 0x43, 156 | ], 157 | ); 158 | } 159 | 160 | #[test] 161 | fn it_should_compute_digest_for_vec2() { 162 | check( 163 | &[ 164 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 165 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 166 | ], 167 | &[ 168 | &[ 169 | 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 170 | 0xdd, 0xdd, 0xdd, 171 | ], 172 | &[ 173 | 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 174 | 0xdd, 0xdd, 0xdd, 175 | ], 176 | &[ 177 | 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 178 | 0xdd, 0xdd, 0xdd, 179 | ], 180 | &[0xdd, 0xdd], 181 | ], 182 | [ 183 | 0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, 0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 184 | 0x81, 0xa7, 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22, 0xd9, 0x63, 0x55, 0x14, 185 | 0xce, 0xd5, 0x65, 0xfe, 186 | ], 187 | ); 188 | } 189 | 190 | #[test] 191 | fn it_should_compute_digest_for_vec3() { 192 | check( 193 | &[ 194 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 195 | 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 196 | ], 197 | &[ 198 | &[ 199 | 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 200 | 0xcd, 0xcd, 0xcd, 201 | ], 202 | &[ 203 | 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 204 | 0xcd, 0xcd, 0xcd, 205 | ], 206 | &[ 207 | 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 208 | 0xcd, 0xcd, 0xcd, 209 | ], 210 | &[0xcd, 0xcd], 211 | ], 212 | [ 213 | 0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e, 0xa4, 0xcc, 0x81, 0x98, 0x99, 0xf2, 214 | 0x08, 0x3a, 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07, 0x7a, 0x2e, 0x3f, 0xf4, 215 | 0x67, 0x29, 0x66, 0x5b, 216 | ], 217 | ); 218 | } 219 | 220 | #[test] 221 | fn it_should_compute_digest_for_vec4() { 222 | check( 223 | &[ 224 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 225 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 226 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 227 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 228 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 229 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 230 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 231 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 232 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 233 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 234 | ], 235 | &[ 236 | b"Test Using Large", 237 | b"r Than Block-Siz", 238 | b"e Key - Hash Key", 239 | b" First", 240 | ], 241 | [ 242 | 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 243 | 0xb7, 0x7f, 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14, 0x05, 0x46, 0x04, 0x0f, 244 | 0x0e, 0xe3, 0x7f, 0x54, 245 | ], 246 | ); 247 | } 248 | 249 | #[test] 250 | fn it_should_compute_digest_for_vec5() { 251 | check( 252 | &[ 253 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 254 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 255 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 256 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 257 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 258 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 259 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 260 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 261 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 262 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 263 | ], 264 | &[ 265 | b"This is a test u", 266 | b"sing a larger th", 267 | b"an block-size ke", 268 | b"y and a larger t", 269 | b"han block-size d", 270 | b"ata. The key nee", 271 | b"ds to be hashed ", 272 | b"before being use", 273 | b"d by the HMAC al", 274 | b"gorithm.", 275 | ], 276 | [ 277 | 0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, 0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0, 278 | 0xe9, 0x44, 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93, 0x8a, 0x7f, 0x51, 0x53, 279 | 0x5c, 0x3a, 0x35, 0xe2, 280 | ], 281 | ); 282 | } 283 | 284 | // 285 | // Just random tests 286 | // 287 | 288 | #[test] 289 | fn it_should_compute_pbkdf2_stage_properly() { 290 | check( 291 | &[0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64], 292 | &[&[ 293 | 0xc6, 0x00, 0x40, 0x4e, 0x39, 0xc9, 0xe9, 0x7a, 0x7d, 0x7a, 0x74, 0x5b, 0x32, 0xc3, 294 | 0xe7, 0x42, 0x63, 0x87, 0xb3, 0x65, 0x69, 0x3c, 0x7f, 0x59, 0x30, 0x0f, 0xd8, 0xa0, 295 | 0x3a, 0xab, 0x4c, 0x6e, 296 | ]], 297 | [ 298 | 0xbe, 0x97, 0xc8, 0x11, 0x49, 0x75, 0x0f, 0x47, 0x65, 0x9a, 0x37, 0xf6, 0x23, 0x00, 299 | 0x43, 0xf5, 0x79, 0xb5, 0xd8, 0x35, 0xac, 0xdd, 0xfc, 0x8e, 0x70, 0xd7, 0x4a, 0xef, 300 | 0x66, 0x95, 0xe1, 0x28, 301 | ], 302 | ); 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # dumb-crypto 2 | //! 3 | //! This library implements following cryptographic routines in the dumbest and 4 | //! the most obvious way: 5 | //! 6 | //! - sha256 7 | //! - hmac-sha256 8 | //! - pbkdf2-sha256 9 | //! - salsa20 10 | //! - scrypt 11 | //! 12 | //! Normally, one would find a highly optimized code implementing those. 13 | //! However, verifying such code is a non-trivial task. 14 | //! 15 | //! All routines (except for scrypt itself) are pre-requisites for scrypt. They 16 | //! are provided here just for convenience. 17 | //! 18 | //! Documentation is available for each separate module. 19 | //! 20 | 21 | #[macro_use] 22 | mod common; 23 | mod pkcs7; 24 | 25 | pub mod aes; 26 | pub mod aes_cbc; 27 | pub mod hmac; 28 | pub mod pbkdf2; 29 | pub mod salsa20; 30 | pub mod scrypt; 31 | pub mod sha256; 32 | -------------------------------------------------------------------------------- /src/pbkdf2.rs: -------------------------------------------------------------------------------- 1 | //! # PBKDF2 2 | //! 3 | //! Implementation of PBKDF2 key derivation algorithm according to 4 | //! [RFC 2898][rfc]. 5 | //! 6 | //! [rfc]: https://tools.ietf.org/html/rfc2898#section-5.2 7 | //! 8 | 9 | use crate::hmac::HMac; 10 | use crate::sha256::DIGEST_SIZE; 11 | 12 | // See: https://tools.ietf.org/html/rfc2898#section-5.2 13 | 14 | /// 15 | /// Derive key using `password`, `salt`, and `c` rounds. 16 | /// 17 | /// Usage: 18 | /// ```rust 19 | /// extern crate dumb_crypto; 20 | /// 21 | /// use dumb_crypto::pbkdf2::pbkdf2_sha256; 22 | /// 23 | /// let mut out: [u8; 8] = [0; 8]; 24 | /// 25 | /// pbkdf2_sha256(b"password", b"salt", 100, &mut out); 26 | /// 27 | /// assert_eq!(out.to_vec(), vec![ 28 | /// 0x07, 0xe6, 0x99, 0x71, 0x80, 0xcf, 0x7f, 0x12, 29 | /// ]); 30 | /// ``` 31 | /// 32 | pub fn pbkdf2_sha256(password: &[u8], salt: &[u8], c: usize, out: &mut [u8]) { 33 | // 34 | // Terminology: 35 | // P = password 36 | // S = salt 37 | // DK = out 38 | // dkLen = out_len 39 | // 40 | 41 | // 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and stop. 42 | // (skip) 43 | 44 | // 2. Let l be the number of hLen-octet blocks in the derived key, 45 | // rounding up, and let r be the number of octets in the last 46 | // block: 47 | // 48 | // l = CEIL (dkLen / hLen) , 49 | // r = dkLen - (l - 1) * hLen . 50 | // 51 | // Here, CEIL (x) is the "ceiling" function, i.e. the smallest 52 | // integer greater than, or equal to, x. 53 | // 54 | 55 | let l: usize = (out.len() + DIGEST_SIZE - 1) / DIGEST_SIZE; 56 | 57 | // NOTE: unused 58 | // let r = out.len() - (l - 1) * DIGEST_SIZE; 59 | 60 | // 3. For each block of the derived key apply the function F defined 61 | // below to the password P, the salt S, the iteration count c, and 62 | // the block index to compute the block: 63 | // 64 | // T_1 = F (P, S, c, 1) , 65 | // T_2 = F (P, S, c, 2) , 66 | // ... 67 | // T_l = F (P, S, c, l) , 68 | // 69 | // where the function F is defined as the exclusive-or sum of the 70 | // first c iterates of the underlying pseudorandom function PRF 71 | // applied to the password P and the concatenation of the salt S 72 | // and the block index i: 73 | // 74 | // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c 75 | // 76 | // where 77 | // 78 | // U_1 = PRF (P, S || INT (i)) , 79 | // U_2 = PRF (P, U_1) , 80 | // ... 81 | // U_c = PRF (P, U_{c-1}) . 82 | // 83 | // Here, INT (i) is a four-octet encoding of the integer i, most 84 | // significant octet first. 85 | // 86 | for i in 1..=l { 87 | let ctr: [u8; 4] = [(i >> 24) as u8, (i >> 16) as u8, (i >> 8) as u8, i as u8]; 88 | 89 | // U_1 = PRF (P, S || INT (i)) 90 | let mut hmac = HMac::new(password); 91 | hmac.update(salt); 92 | hmac.update(&ctr); 93 | let mut u = hmac.digest(); 94 | let mut t = u; 95 | 96 | for _j in 2..=c { 97 | // U_c = PRF (P, U_{c-1}) 98 | let mut hmac = HMac::new(password); 99 | hmac.update(&u); 100 | u = hmac.digest(); 101 | 102 | // Xor Us 103 | for k in 0..u.len() { 104 | t[k] ^= u[k]; 105 | } 106 | } 107 | 108 | // Copy out the results 109 | let start = (i - 1) * DIGEST_SIZE; 110 | let end = std::cmp::min(i * DIGEST_SIZE, out.len()); 111 | out[start..end].copy_from_slice(&t[..(end - start)]); 112 | } 113 | } 114 | 115 | #[cfg(test)] 116 | mod tests { 117 | use super::*; 118 | 119 | // 120 | // See: https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-03#page-10 121 | // 122 | 123 | #[test] 124 | fn it_should_compute_digest_for_vec0() { 125 | let mut out: [u8; 64] = [0; 64]; 126 | 127 | pbkdf2_sha256(b"passwd", b"salt", 1, &mut out); 128 | assert_eq!( 129 | out.to_vec(), 130 | vec![ 131 | 0x55, 0xac, 0x04, 0x6e, 0x56, 0xe3, 0x08, 0x9f, 0xec, 0x16, 0x91, 0xc2, 0x25, 0x44, 132 | 0xb6, 0x05, 0xf9, 0x41, 0x85, 0x21, 0x6d, 0xde, 0x04, 0x65, 0xe6, 0x8b, 0x9d, 0x57, 133 | 0xc2, 0x0d, 0xac, 0xbc, 0x49, 0xca, 0x9c, 0xcc, 0xf1, 0x79, 0xb6, 0x45, 0x99, 0x16, 134 | 0x64, 0xb3, 0x9d, 0x77, 0xef, 0x31, 0x7c, 0x71, 0xb8, 0x45, 0xb1, 0xe3, 0x0b, 0xd5, 135 | 0x09, 0x11, 0x20, 0x41, 0xd3, 0xa1, 0x97, 0x83, 136 | ] 137 | ); 138 | } 139 | 140 | #[test] 141 | fn it_should_compute_digest_for_vec1() { 142 | let mut out: [u8; 64] = [0; 64]; 143 | 144 | pbkdf2_sha256(b"Password", b"NaCl", 80000, &mut out); 145 | assert_eq!( 146 | out.to_vec(), 147 | vec![ 148 | 0x4d, 0xdc, 0xd8, 0xf6, 0x0b, 0x98, 0xbe, 0x21, 0x83, 0x0c, 0xee, 0x5e, 0xf2, 0x27, 149 | 0x01, 0xf9, 0x64, 0x1a, 0x44, 0x18, 0xd0, 0x4c, 0x04, 0x14, 0xae, 0xff, 0x08, 0x87, 150 | 0x6b, 0x34, 0xab, 0x56, 0xa1, 0xd4, 0x25, 0xa1, 0x22, 0x58, 0x33, 0x54, 0x9a, 0xdb, 151 | 0x84, 0x1b, 0x51, 0xc9, 0xb3, 0x17, 0x6a, 0x27, 0x2b, 0xde, 0xbb, 0xa1, 0xd0, 0x78, 152 | 0x47, 0x8f, 0x62, 0xb3, 0x97, 0xf3, 0x3c, 0x8d, 153 | ] 154 | ); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/pkcs7.rs: -------------------------------------------------------------------------------- 1 | //! # PKCS7 2 | //! 3 | //! Implementation of PKCS7 padding. 4 | //! 5 | 6 | use std::error::Error; 7 | use std::fmt; 8 | use std::fmt::Display; 9 | 10 | pub struct Pad { 11 | block_size: usize, 12 | storage: Vec, 13 | } 14 | 15 | impl Pad { 16 | pub fn new(block_size: usize) -> Self { 17 | Pad { 18 | block_size, 19 | storage: Vec::with_capacity(2 * block_size), 20 | } 21 | } 22 | 23 | pub fn write(&mut self, buf: &[u8]) -> Option> { 24 | for &elem in buf { 25 | self.storage.push(elem); 26 | } 27 | 28 | if self.storage.len() < self.block_size { 29 | return None; 30 | } 31 | 32 | let tail_len = self.storage.len() % self.block_size; 33 | let tail_pos = self.storage.len() - tail_len; 34 | 35 | let mut tail = self.storage.split_off(tail_pos); 36 | let head = self.storage.clone(); 37 | self.storage.clear(); 38 | self.storage.append(&mut tail); 39 | 40 | Some(head) 41 | } 42 | 43 | pub fn flush(&mut self) -> Vec { 44 | let len = self.block_size - self.storage.len(); 45 | while self.storage.len() < self.block_size { 46 | self.storage.push(len as u8); 47 | } 48 | 49 | let result = self.storage.clone(); 50 | self.storage.clear(); 51 | result 52 | } 53 | } 54 | 55 | /// Possible unpad errors 56 | #[derive(Debug, PartialEq)] 57 | pub enum UnpadError { 58 | /// Returned when the data length is not divisible by the block size. 59 | UnfinishedBlock, 60 | 61 | /// Returned when the padding is invalid in the last block. 62 | InvalidPadding, 63 | } 64 | 65 | impl Display for UnpadError { 66 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 67 | write!(f, "UnpadError: {}", self.description()) 68 | } 69 | } 70 | 71 | impl Error for UnpadError { 72 | fn description(&self) -> &str { 73 | match self { 74 | UnpadError::UnfinishedBlock => "data length must be a multiple of block size", 75 | UnpadError::InvalidPadding => "final block contains invalid padding", 76 | } 77 | } 78 | } 79 | 80 | pub struct Unpad { 81 | block_size: usize, 82 | storage: Vec, 83 | } 84 | 85 | impl Unpad { 86 | pub fn new(block_size: usize) -> Self { 87 | Unpad { 88 | block_size, 89 | storage: Vec::with_capacity(2 * block_size), 90 | } 91 | } 92 | 93 | pub fn write(&mut self, buf: &[u8]) -> Option> { 94 | for &elem in buf { 95 | self.storage.push(elem); 96 | } 97 | 98 | // Note the `<=` here. We don't know where the block is the last, so we 99 | // can't yet emit the block until we have at least one byte of the next 100 | // block. 101 | if self.storage.len() <= self.block_size { 102 | return None; 103 | } 104 | 105 | let mut tail_len = self.storage.len() % self.block_size; 106 | 107 | // NOTE: this is different from the Pad::write 108 | if tail_len == 0 { 109 | tail_len = self.block_size; 110 | } 111 | 112 | let tail_pos = self.storage.len() - tail_len; 113 | 114 | let mut tail = self.storage.split_off(tail_pos); 115 | let head = self.storage.clone(); 116 | self.storage.clear(); 117 | self.storage.append(&mut tail); 118 | 119 | Some(head) 120 | } 121 | 122 | pub fn flush(&mut self) -> Result, UnpadError> { 123 | if self.storage.len() != self.block_size { 124 | return Err(UnpadError::UnfinishedBlock); 125 | } 126 | 127 | let len = self.storage[self.storage.len() - 1]; 128 | if len > (self.block_size as u8) { 129 | return Err(UnpadError::InvalidPadding); 130 | } 131 | let pad_off: usize = self.block_size - usize::from(len); 132 | 133 | // XXX(indutny): hopefully this won't be optimized away 134 | let mut is_same: u8 = 0; 135 | for b in self.storage[pad_off..].iter() { 136 | is_same |= len ^ b; 137 | } 138 | 139 | if is_same != 0 { 140 | return Err(UnpadError::InvalidPadding); 141 | } 142 | 143 | let result = self.storage[..pad_off].to_vec(); 144 | self.storage.clear(); 145 | Ok(result) 146 | } 147 | } 148 | 149 | #[cfg(test)] 150 | mod tests { 151 | use super::*; 152 | 153 | #[test] 154 | fn it_should_apply_padding_to_incomplete_block() { 155 | let mut pad = Pad::new(4); 156 | 157 | assert_eq!(pad.write(b"123"), None); 158 | assert_eq!(pad.flush(), b"123\x01"); 159 | } 160 | 161 | #[test] 162 | fn it_should_apply_padding_to_second_incomplete_block() { 163 | let mut pad = Pad::new(4); 164 | 165 | assert_eq!(pad.write(b"123456"), Some(b"1234".to_vec())); 166 | assert_eq!(pad.flush(), b"56\x02\x02"); 167 | } 168 | 169 | #[test] 170 | fn it_should_return_padding_block() { 171 | let mut pad = Pad::new(4); 172 | 173 | assert_eq!(pad.write(b"1234"), Some(b"1234".to_vec())); 174 | assert_eq!(pad.flush(), b"\x04\x04\x04\x04"); 175 | } 176 | 177 | #[test] 178 | fn it_should_unpad_single_block() { 179 | let mut unpad = Unpad::new(4); 180 | 181 | assert_eq!(unpad.write(b"123\x01"), None); 182 | assert_eq!(unpad.flush().expect("flush to succeed"), b"123".to_vec()); 183 | } 184 | 185 | #[test] 186 | fn it_should_unpad_two_blocks() { 187 | let mut unpad = Unpad::new(4); 188 | 189 | assert_eq!(unpad.write(b"1234"), None); 190 | assert_eq!(unpad.write(b"5\x03\x03\x03"), Some(b"1234".to_vec())); 191 | assert_eq!(unpad.flush().expect("flush to succeed"), b"5".to_vec()); 192 | } 193 | 194 | #[test] 195 | fn it_should_unpad_full_block() { 196 | let mut unpad = Unpad::new(4); 197 | 198 | assert_eq!(unpad.write(b"1234"), None); 199 | assert_eq!(unpad.write(b"\x04\x04\x04\x04"), Some(b"1234".to_vec())); 200 | assert_eq!(unpad.flush().expect("flush to succeed"), b"".to_vec()); 201 | } 202 | 203 | #[test] 204 | fn it_should_error_on_unfinished_data() { 205 | let mut unpad = Unpad::new(4); 206 | 207 | assert_eq!(unpad.write(b"123"), None); 208 | assert_eq!( 209 | unpad.flush().expect_err("flush to fail"), 210 | UnpadError::UnfinishedBlock 211 | ); 212 | } 213 | 214 | #[test] 215 | fn it_should_error_on_invalid_padding() { 216 | let mut unpad = Unpad::new(4); 217 | 218 | assert_eq!(unpad.write(b"12\x03\x02"), None); 219 | assert_eq!( 220 | unpad.flush().expect_err("flush to fail"), 221 | UnpadError::InvalidPadding 222 | ); 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /src/salsa20.rs: -------------------------------------------------------------------------------- 1 | //! # Salsa20 2 | //! 3 | //! Implementation of salsa20 algorithm according to [the specification][spec]. 4 | //! 5 | //! [spec]: http://cr.yp.to/snuffle/spec.pdf 6 | //! 7 | 8 | /// Algorithm block size 9 | pub const BLOCK_SIZE: usize = 64; 10 | 11 | // http://cr.yp.to/snuffle/spec.pdf 12 | fn rotl(n: u32, s: u8) -> u32 { 13 | (n << s) | (n >> (32 - s)) 14 | } 15 | 16 | fn quarterround(y: &[u32], z: &mut [u32]) { 17 | // 18 | // If y = (y0, y1, y2, y3) then quarterround(y) = (z0, z1, z2, z3) where 19 | // 20 | // z1 = y1 ⊕ ((y0 + y3) <<< 7), 21 | // z2 = y2 ⊕ ((z1 + y0) <<< 9), 22 | // z3 = y3 ⊕ ((z2 + z1) <<< 13), 23 | // z0 = y0 ⊕ ((z3 + z2) <<< 18). 24 | // 25 | z[1] = y[1] ^ rotl(wrapping_sum!(y[0], y[3]), 7); 26 | z[2] = y[2] ^ rotl(wrapping_sum!(z[1], y[0]), 9); 27 | z[3] = y[3] ^ rotl(wrapping_sum!(z[2], z[1]), 13); 28 | z[0] = y[0] ^ rotl(wrapping_sum!(z[3], z[2]), 18); 29 | } 30 | 31 | fn rowround(y: &[u32], z: &mut [u32]) { 32 | // 33 | // If y = (y0, y1, y2, y3, . . . , y15) then 34 | // rowround(y) = (z0, z1, z2, z3, . . . , z15) where 35 | // 36 | // (z0, z1, z2, z3) = quarterround(y0, y1, y2, y3), 37 | // (z5, z6, z7, z4) = quarterround(y5, y6, y7, y4), 38 | // (z10, z11, z8, z9) = quarterround(y10, y11, y8, y9), 39 | // (z15, z12, z13, z14) = quarterround(y15, y12, y13, y14). 40 | // 41 | 42 | let s0: [u32; 4] = [y[0], y[1], y[2], y[3]]; 43 | let s1: [u32; 4] = [y[5], y[6], y[7], y[4]]; 44 | let s2: [u32; 4] = [y[10], y[11], y[8], y[9]]; 45 | let s3: [u32; 4] = [y[15], y[12], y[13], y[14]]; 46 | let mut t: [u32; 4] = [0; 4]; 47 | 48 | quarterround(&s0, &mut t); 49 | z[0] = t[0]; 50 | z[1] = t[1]; 51 | z[2] = t[2]; 52 | z[3] = t[3]; 53 | 54 | quarterround(&s1, &mut t); 55 | z[5] = t[0]; 56 | z[6] = t[1]; 57 | z[7] = t[2]; 58 | z[4] = t[3]; 59 | 60 | quarterround(&s2, &mut t); 61 | z[10] = t[0]; 62 | z[11] = t[1]; 63 | z[8] = t[2]; 64 | z[9] = t[3]; 65 | 66 | quarterround(&s3, &mut t); 67 | z[15] = t[0]; 68 | z[12] = t[1]; 69 | z[13] = t[2]; 70 | z[14] = t[3]; 71 | } 72 | 73 | fn columnround(x: &[u32], y: &mut [u32]) { 74 | // 75 | // If x = (x0, x1, x2, x3, . . . , x15) then 76 | // columnround(x) = (y0, y1, y2, y3, . . . , y15) where 77 | // 78 | // (y0, y4, y8, y12) = quarterround(x0, x4, x8, x12), 79 | // (y5, y9, y13, y1) = quarterround(x5, x9, x13, x1), 80 | // (y10, y14, y2, y6) = quarterround(x10, x14, x2, x6), 81 | // (y15, y3, y7, y11) = quarterround(x15, x3, x7, x11). 82 | // 83 | 84 | let s0: [u32; 4] = [x[0], x[4], x[8], x[12]]; 85 | let s1: [u32; 4] = [x[5], x[9], x[13], x[1]]; 86 | let s2: [u32; 4] = [x[10], x[14], x[2], x[6]]; 87 | let s3: [u32; 4] = [x[15], x[3], x[7], x[11]]; 88 | let mut t: [u32; 4] = [0; 4]; 89 | 90 | quarterround(&s0, &mut t); 91 | y[0] = t[0]; 92 | y[4] = t[1]; 93 | y[8] = t[2]; 94 | y[12] = t[3]; 95 | 96 | quarterround(&s1, &mut t); 97 | y[5] = t[0]; 98 | y[9] = t[1]; 99 | y[13] = t[2]; 100 | y[1] = t[3]; 101 | 102 | quarterround(&s2, &mut t); 103 | y[10] = t[0]; 104 | y[14] = t[1]; 105 | y[2] = t[2]; 106 | y[6] = t[3]; 107 | 108 | quarterround(&s3, &mut t); 109 | y[15] = t[0]; 110 | y[3] = t[1]; 111 | y[7] = t[2]; 112 | y[11] = t[3]; 113 | } 114 | 115 | fn doubleround(x: &[u32], y: &mut [u32]) { 116 | // 117 | // A double round is a column round followed by a row round: 118 | // doubleround(x) = rowround(columnround(x)). 119 | // 120 | let mut t: [u32; 16] = [0; 16]; 121 | columnround(x, &mut t); 122 | rowround(&t, y); 123 | } 124 | 125 | fn littleendian(b: &[u8]) -> u32 { 126 | // 127 | // If b = (b0, b1, b2, b3) then littleendian(b) = 128 | // b0 + 2^8 * b1 + 2^16 * b2 + 2^24 * b3. 129 | // 130 | u32::from(b[0]) | (u32::from(b[1]) << 8) | (u32::from(b[2]) << 16) | (u32::from(b[3]) << 24) 131 | } 132 | 133 | fn littleendian_inv(x: u32, y: &mut [u8]) { 134 | y[0] = x as u8; 135 | y[1] = (x >> 8) as u8; 136 | y[2] = (x >> 16) as u8; 137 | y[3] = (x >> 24) as u8; 138 | } 139 | 140 | /// 141 | /// Mix `input` using `rounds` of internal transformations. 142 | /// 143 | /// NOTE: `input` and `output` lengths must be multiples of `BLOCK_SIZE` 144 | /// NOTE: `output` MUST have the same size as `input`. 145 | /// 146 | /// Usage: 147 | /// ```rust 148 | /// extern crate dumb_crypto; 149 | /// 150 | /// use dumb_crypto::salsa20::salsa20; 151 | /// 152 | /// let mut out: [u8; 64] = [0; 64]; 153 | /// 154 | /// salsa20(&[7; 64], 100, &mut out); 155 | /// 156 | /// assert_eq!(out.to_vec(), vec![ 157 | /// 121, 110, 7, 195, 60, 132, 20, 193, 62, 42, 49, 114, 249, 93, 87, 33, 158 | /// 249, 93, 87, 33, 121, 110, 7, 195, 60, 132, 20, 193, 62, 42, 49, 114, 159 | /// 62, 42, 49, 114, 249, 93, 87, 33, 121, 110, 7, 195, 60, 132, 20, 193, 160 | /// 60, 132, 20, 193, 62, 42, 49, 114, 249, 93, 87, 33, 121, 110, 7, 195, 161 | /// ]); 162 | /// ``` 163 | /// 164 | pub fn salsa20(input: &[u8], rounds: usize, output: &mut [u8]) { 165 | // 166 | // In short: Salsa20(x) = x + doubleround^10(x), where each 4-byte sequence is 167 | // viewed as a word in little-endian form. 168 | // 169 | // In detail: Starting from x = (x[0], x[1], . . . , x[63]), define 170 | // x0 = littleendian(x[0], x[1], x[2], x[3]), 171 | // x1 = littleendian(x[4], x[5], x[6], x[7]), 172 | // x2 = littleendian(x[8], x[9], x[10], x[11]), 173 | // ... 174 | // x15 = littleendian(x[60], x[61], x[62], x[63]). 175 | // 176 | // Define (z0, z1, . . . , z15) = doubleround10(x0, x1, . . . , x15). 177 | // 178 | // Then Salsa20(x) is the concatenation of 179 | // littleendian^−1(z0 + x0), 180 | // littleendian^−1(z1 + x1), 181 | // littleendian^−1(z2 + x2), 182 | // ... 183 | // littleendian^−1(z15 + x15). 184 | // 185 | 186 | let mut x: [u32; 16] = [0; 16]; 187 | 188 | for (i, elem) in x.iter_mut().enumerate() { 189 | let from = i * 4; 190 | let to = from + 4; 191 | *elem = littleendian(&input[from..to]); 192 | } 193 | 194 | let mut z = x; 195 | 196 | for _ in 0..rounds { 197 | let mut t: [u32; 16] = [0; 16]; 198 | doubleround(&z, &mut t); 199 | z = t; 200 | } 201 | 202 | for ((i, &x_elem), &z_elem) in x.iter().enumerate().zip(z.iter()) { 203 | let from = i * 4; 204 | let to = from + 4; 205 | littleendian_inv(wrapping_sum!(x_elem, z_elem), &mut output[from..to]); 206 | } 207 | } 208 | 209 | #[cfg(test)] 210 | mod tests { 211 | use super::*; 212 | 213 | // 214 | // http://cr.yp.to/snuffle/spec.pdf 215 | // 216 | 217 | fn quarter_check(input: [u32; 4], expected: [u32; 4]) { 218 | let mut out: [u32; 4] = [0; 4]; 219 | quarterround(&input, &mut out); 220 | assert_eq!(out, expected); 221 | } 222 | 223 | #[test] 224 | fn it_should_compute_quarterround_for_vec0() { 225 | quarter_check( 226 | [0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000], 227 | [0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000], 228 | ); 229 | } 230 | 231 | #[test] 232 | fn it_should_compute_quarterround_for_vec1() { 233 | quarter_check( 234 | [0x0000_0001, 0x0000_0000, 0x0000_0000, 0x0000_0000], 235 | [0x0800_8145, 0x0000_0080, 0x0001_0200, 0x2050_0000], 236 | ); 237 | } 238 | 239 | #[test] 240 | fn it_should_compute_quarterround_for_vec2() { 241 | quarter_check( 242 | [0x0000_0000, 0x0000_0001, 0x0000_0000, 0x0000_0000], 243 | [0x8800_0100, 0x0000_0001, 0x0000_0200, 0x0040_2000], 244 | ); 245 | } 246 | 247 | #[test] 248 | fn it_should_compute_quarterround_for_vec3() { 249 | quarter_check( 250 | [0x0000_0000, 0x0000_0000, 0x0000_0001, 0x0000_0000], 251 | [0x8004_0000, 0x0000_0000, 0x0000_0001, 0x0000_2000], 252 | ); 253 | } 254 | 255 | #[test] 256 | fn it_should_compute_quarterround_for_vec4() { 257 | quarter_check( 258 | [0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0001], 259 | [0x0004_8044, 0x0000_0080, 0x0001_0000, 0x2010_0001], 260 | ); 261 | } 262 | 263 | #[test] 264 | fn it_should_compute_quarterround_for_vec5() { 265 | quarter_check( 266 | [0xe7e8_c006, 0xc4f9_417d, 0x6479_b4b2, 0x68c6_7137], 267 | [0xe876_d72b, 0x9361_dfd5, 0xf146_0244, 0x9485_41a3], 268 | ); 269 | } 270 | 271 | #[test] 272 | fn it_should_compute_quarterround_for_vec6() { 273 | quarter_check( 274 | [0xd391_7c5b, 0x55f1_c407, 0x52a5_8a7a, 0x8f88_7a3b], 275 | [0x3e2f_308c, 0xd90a_8f36, 0x6ab2_a923, 0x2883_524c], 276 | ); 277 | } 278 | 279 | fn column_check(input: [u32; 16], expected: [u32; 16]) { 280 | let mut out: [u32; 16] = [0; 16]; 281 | columnround(&input, &mut out); 282 | assert_eq!(out, expected); 283 | } 284 | 285 | #[test] 286 | fn it_should_compute_columnround_for_vec0() { 287 | column_check( 288 | [ 289 | 0x0000_0001, 290 | 0x0000_0000, 291 | 0x0000_0000, 292 | 0x0000_0000, 293 | 0x0000_0001, 294 | 0x0000_0000, 295 | 0x0000_0000, 296 | 0x0000_0000, 297 | 0x0000_0001, 298 | 0x0000_0000, 299 | 0x0000_0000, 300 | 0x0000_0000, 301 | 0x0000_0001, 302 | 0x0000_0000, 303 | 0x0000_0000, 304 | 0x0000_0000, 305 | ], 306 | [ 307 | 0x1009_0288, 308 | 0x0000_0000, 309 | 0x0000_0000, 310 | 0x0000_0000, 311 | 0x0000_0101, 312 | 0x0000_0000, 313 | 0x0000_0000, 314 | 0x0000_0000, 315 | 0x0002_0401, 316 | 0x0000_0000, 317 | 0x0000_0000, 318 | 0x0000_0000, 319 | 0x40a0_4001, 320 | 0x0000_0000, 321 | 0x0000_0000, 322 | 0x0000_0000, 323 | ], 324 | ); 325 | } 326 | 327 | #[test] 328 | fn it_should_compute_columnround_for_vec1() { 329 | column_check( 330 | [ 331 | 0x0852_1bd6, 332 | 0x1fe8_8837, 333 | 0xbb2a_a576, 334 | 0x3aa2_6365, 335 | 0xc54c_6a5b, 336 | 0x2fc7_4c2f, 337 | 0x6dd3_9cc3, 338 | 0xda0a_64f6, 339 | 0x90a2_f23d, 340 | 0x067f_95a6, 341 | 0x06b3_5f61, 342 | 0x41e4_732e, 343 | 0xe859_c100, 344 | 0xea4d_84b7, 345 | 0x0f61_9bff, 346 | 0xbc6e_965a, 347 | ], 348 | [ 349 | 0x8c9d_190a, 350 | 0xce8e_4c90, 351 | 0x1ef8_e9d3, 352 | 0x1326_a71a, 353 | 0x90a2_0123, 354 | 0xead3_c4f3, 355 | 0x63a0_91a0, 356 | 0xf070_8d69, 357 | 0x789b_010c, 358 | 0xd195_a681, 359 | 0xeb7d_5504, 360 | 0xa774_135c, 361 | 0x481c_2027, 362 | 0x53a8_e4b5, 363 | 0x4c1f_89c5, 364 | 0x3f78_c9c8, 365 | ], 366 | ); 367 | } 368 | 369 | fn double_check(input: [u32; 16], expected: [u32; 16]) { 370 | let mut out: [u32; 16] = [0; 16]; 371 | doubleround(&input, &mut out); 372 | assert_eq!(out, expected); 373 | } 374 | 375 | #[test] 376 | fn it_should_compute_doubleround_for_vec0() { 377 | double_check( 378 | [ 379 | 0x0000_0001, 380 | 0x0000_0000, 381 | 0x0000_0000, 382 | 0x0000_0000, 383 | 0x0000_0000, 384 | 0x0000_0000, 385 | 0x0000_0000, 386 | 0x0000_0000, 387 | 0x0000_0000, 388 | 0x0000_0000, 389 | 0x0000_0000, 390 | 0x0000_0000, 391 | 0x0000_0000, 392 | 0x0000_0000, 393 | 0x0000_0000, 394 | 0x0000_0000, 395 | ], 396 | [ 397 | 0x8186_a22d, 398 | 0x0040_a284, 399 | 0x8247_9210, 400 | 0x0692_9051, 401 | 0x0800_0090, 402 | 0x0240_2200, 403 | 0x0000_4000, 404 | 0x0080_0000, 405 | 0x0001_0200, 406 | 0x2040_0000, 407 | 0x0800_8104, 408 | 0x0000_0000, 409 | 0x2050_0000, 410 | 0xa000_0040, 411 | 0x0008_180a, 412 | 0x612a_8020, 413 | ], 414 | ); 415 | } 416 | 417 | #[test] 418 | fn it_should_compute_doubleround_for_vec1() { 419 | double_check( 420 | [ 421 | 0xde50_1066, 422 | 0x6f9e_b8f7, 423 | 0xe4fb_bd9b, 424 | 0x454e_3f57, 425 | 0xb755_40d3, 426 | 0x43e9_3a4c, 427 | 0x3a6f_2aa0, 428 | 0x726d_6b36, 429 | 0x9243_f484, 430 | 0x9145_d1e8, 431 | 0x4fa9_d247, 432 | 0xdc8d_ee11, 433 | 0x054b_f545, 434 | 0x254d_d653, 435 | 0xd942_1b6d, 436 | 0x67b2_76c1, 437 | ], 438 | [ 439 | 0xccaa_f672, 440 | 0x23d9_60f7, 441 | 0x9153_e63a, 442 | 0xcd9a_60d0, 443 | 0x5044_0492, 444 | 0xf07c_ad19, 445 | 0xae34_4aa0, 446 | 0xdf4c_fdfc, 447 | 0xca53_1c29, 448 | 0x8e79_43db, 449 | 0xac16_80cd, 450 | 0xd503_ca00, 451 | 0xa74b_2ad6, 452 | 0xbc33_1c5c, 453 | 0x1dda_24c7, 454 | 0xee92_8277, 455 | ], 456 | ); 457 | } 458 | 459 | fn row_check(input: [u32; 16], expected: [u32; 16]) { 460 | let mut out: [u32; 16] = [0; 16]; 461 | rowround(&input, &mut out); 462 | assert_eq!(out, expected); 463 | } 464 | 465 | #[test] 466 | fn it_should_compute_rowround_for_vec0() { 467 | row_check( 468 | [ 469 | 0x0000_0001, 470 | 0x0000_0000, 471 | 0x0000_0000, 472 | 0x0000_0000, 473 | 0x0000_0001, 474 | 0x0000_0000, 475 | 0x0000_0000, 476 | 0x0000_0000, 477 | 0x0000_0001, 478 | 0x0000_0000, 479 | 0x0000_0000, 480 | 0x0000_0000, 481 | 0x0000_0001, 482 | 0x0000_0000, 483 | 0x0000_0000, 484 | 0x0000_0000, 485 | ], 486 | [ 487 | 0x0800_8145, 488 | 0x0000_0080, 489 | 0x0001_0200, 490 | 0x2050_0000, 491 | 0x2010_0001, 492 | 0x0004_8044, 493 | 0x0000_0080, 494 | 0x0001_0000, 495 | 0x0000_0001, 496 | 0x0000_2000, 497 | 0x8004_0000, 498 | 0x0000_0000, 499 | 0x0000_0001, 500 | 0x0000_0200, 501 | 0x0040_2000, 502 | 0x8800_0100, 503 | ], 504 | ); 505 | } 506 | 507 | #[test] 508 | fn it_should_compute_rowround_for_vec1() { 509 | row_check( 510 | [ 511 | 0x0852_1bd6, 512 | 0x1fe8_8837, 513 | 0xbb2a_a576, 514 | 0x3aa2_6365, 515 | 0xc54c_6a5b, 516 | 0x2fc7_4c2f, 517 | 0x6dd3_9cc3, 518 | 0xda0a_64f6, 519 | 0x90a2_f23d, 520 | 0x067f_95a6, 521 | 0x06b3_5f61, 522 | 0x41e4_732e, 523 | 0xe859_c100, 524 | 0xea4d_84b7, 525 | 0x0f61_9bff, 526 | 0xbc6e_965a, 527 | ], 528 | [ 529 | 0xa890_d39d, 530 | 0x65d7_1596, 531 | 0xe948_7daa, 532 | 0xc8ca_6a86, 533 | 0x949d_2192, 534 | 0x764b_7754, 535 | 0xe408_d9b9, 536 | 0x7a41_b4d1, 537 | 0x3402_e183, 538 | 0x3c3a_f432, 539 | 0x5066_9f96, 540 | 0xd89e_f0a8, 541 | 0x0040_ede5, 542 | 0xb545_fbce, 543 | 0xd257_ed4f, 544 | 0x1818_882d, 545 | ], 546 | ); 547 | } 548 | 549 | fn check(input: [u8; 64], rounds: usize, expected: [u8; 64]) { 550 | let mut out: [u8; 64] = [0; 64]; 551 | salsa20(&input, rounds, &mut out); 552 | assert_eq!(out.to_vec(), expected.to_vec()); 553 | } 554 | 555 | #[test] 556 | fn it_should_compute_salsa20_for_vec0() { 557 | check( 558 | [ 559 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 560 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 561 | 0, 0, 0, 0, 0, 0, 0, 0, 562 | ], 563 | 10, 564 | [ 565 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 566 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 567 | 0, 0, 0, 0, 0, 0, 0, 0, 568 | ], 569 | ); 570 | } 571 | 572 | #[test] 573 | fn it_should_compute_salsa20_for_vec1() { 574 | check( 575 | [ 576 | 211, 159, 13, 115, 76, 55, 82, 183, 3, 117, 222, 37, 191, 187, 234, 136, 49, 237, 577 | 179, 48, 1, 106, 178, 219, 175, 199, 166, 48, 86, 16, 179, 207, 31, 240, 32, 63, 578 | 15, 83, 93, 161, 116, 147, 48, 113, 238, 55, 204, 36, 79, 201, 235, 79, 3, 81, 156, 579 | 47, 203, 26, 244, 243, 88, 118, 104, 54, 580 | ], 581 | 10, 582 | [ 583 | 109, 42, 178, 168, 156, 240, 248, 238, 168, 196, 190, 203, 26, 110, 170, 154, 29, 584 | 29, 150, 26, 150, 30, 235, 249, 190, 163, 251, 48, 69, 144, 51, 57, 118, 40, 152, 585 | 157, 180, 57, 27, 94, 107, 42, 236, 35, 27, 111, 114, 114, 219, 236, 232, 135, 111, 586 | 155, 110, 18, 24, 232, 95, 158, 179, 19, 48, 202, 587 | ], 588 | ); 589 | } 590 | 591 | #[test] 592 | fn it_should_compute_salsa20_for_vec2() { 593 | check( 594 | [ 595 | 88, 118, 104, 54, 79, 201, 235, 79, 3, 81, 156, 47, 203, 26, 244, 243, 191, 187, 596 | 234, 136, 211, 159, 13, 115, 76, 55, 82, 183, 3, 117, 222, 37, 86, 16, 179, 207, 597 | 49, 237, 179, 48, 1, 106, 178, 219, 175, 199, 166, 48, 238, 55, 204, 36, 31, 240, 598 | 32, 63, 15, 83, 93, 161, 116, 147, 48, 113, 599 | ], 600 | 10, 601 | [ 602 | 179, 19, 48, 202, 219, 236, 232, 135, 111, 155, 110, 18, 24, 232, 95, 158, 26, 110, 603 | 170, 154, 109, 42, 178, 168, 156, 240, 248, 238, 168, 196, 190, 203, 69, 144, 51, 604 | 57, 29, 29, 150, 26, 150, 30, 235, 249, 190, 163, 251, 48, 27, 111, 114, 114, 118, 605 | 40, 152, 157, 180, 57, 27, 94, 107, 42, 236, 35, 606 | ], 607 | ); 608 | } 609 | 610 | // https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-03#page-8 611 | 612 | #[test] 613 | fn it_should_compute_salsa20_for_vec3() { 614 | check( 615 | [ 616 | 0x7e, 0x87, 0x9a, 0x21, 0x4f, 0x3e, 0xc9, 0x86, 0x7c, 0xa9, 0x40, 0xe6, 0x41, 0x71, 617 | 0x8f, 0x26, 0xba, 0xee, 0x55, 0x5b, 0x8c, 0x61, 0xc1, 0xb5, 0x0d, 0xf8, 0x46, 0x11, 618 | 0x6d, 0xcd, 0x3b, 0x1d, 0xee, 0x24, 0xf3, 0x19, 0xdf, 0x9b, 0x3d, 0x85, 0x14, 0x12, 619 | 0x1e, 0x4b, 0x5a, 0xc5, 0xaa, 0x32, 0x76, 0x02, 0x1d, 0x29, 0x09, 0xc7, 0x48, 0x29, 620 | 0xed, 0xeb, 0xc6, 0x8d, 0xb8, 0xb8, 0xc2, 0x5e, 621 | ], 622 | 4, 623 | [ 624 | 0xa4, 0x1f, 0x85, 0x9c, 0x66, 0x08, 0xcc, 0x99, 0x3b, 0x81, 0xca, 0xcb, 0x02, 0x0c, 625 | 0xef, 0x05, 0x04, 0x4b, 0x21, 0x81, 0xa2, 0xfd, 0x33, 0x7d, 0xfd, 0x7b, 0x1c, 0x63, 626 | 0x96, 0x68, 0x2f, 0x29, 0xb4, 0x39, 0x31, 0x68, 0xe3, 0xc9, 0xe6, 0xbc, 0xfe, 0x6b, 627 | 0xc5, 0xb7, 0xa0, 0x6d, 0x96, 0xba, 0xe4, 0x24, 0xcc, 0x10, 0x2c, 0x91, 0x74, 0x5c, 628 | 0x24, 0xad, 0x67, 0x3d, 0xc7, 0x61, 0x8f, 0x81, 629 | ], 630 | ); 631 | } 632 | } 633 | -------------------------------------------------------------------------------- /src/scrypt.rs: -------------------------------------------------------------------------------- 1 | //! # Scrypt 2 | //! 3 | //! Implementation of scrypt key derivation algorithm according to 4 | //! [RFC][rfc]. 5 | //! 6 | //! [rfc]: https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-03 7 | //! 8 | 9 | use crate::pbkdf2::pbkdf2_sha256; 10 | use crate::salsa20::{salsa20, BLOCK_SIZE as SALSA_BLOCK_SIZE}; 11 | 12 | const SALSA_ROUNDS: usize = 4; 13 | const PBKDF2_ROUNDS: usize = 1; 14 | const BLOCK_SIZE: usize = 64; 15 | 16 | use std::error::Error; 17 | use std::fmt; 18 | use std::fmt::Display; 19 | 20 | type Block = Vec; 21 | 22 | #[derive(Debug, PartialEq)] 23 | pub enum ScryptError { 24 | RIsTooSmall, 25 | NIsTooSmall, 26 | NIsNotAPowerOfTwo, 27 | PIsTooSmall, 28 | } 29 | 30 | impl Display for ScryptError { 31 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 32 | write!(f, "ScryptError: {}", self.description()) 33 | } 34 | } 35 | 36 | impl Error for ScryptError { 37 | fn description(&self) -> &str { 38 | match self { 39 | ScryptError::RIsTooSmall => "`r` must be larger than 1", 40 | ScryptError::NIsTooSmall => "`n` must be larger than 1", 41 | ScryptError::NIsNotAPowerOfTwo => "`n` must be a power of two", 42 | ScryptError::PIsTooSmall => "`p` must be larger than 1", 43 | } 44 | } 45 | } 46 | 47 | /// 48 | /// Main scrypt structure. 49 | /// 50 | /// Usage: 51 | /// ```rust 52 | /// extern crate dumb_crypto; 53 | /// 54 | /// use dumb_crypto::scrypt::Scrypt; 55 | /// 56 | /// let scrypt = Scrypt::new(1, 128, 1); 57 | /// 58 | /// let mut out: [u8; 8] = [0; 8]; 59 | /// 60 | /// scrypt.derive(b"passphrase", b"salt", &mut out).unwrap(); 61 | /// 62 | /// assert_eq!(out.to_vec(), vec![ 63 | /// 79, 35, 225, 99, 145, 145, 172, 245, 64 | /// ]); 65 | /// ``` 66 | /// 67 | pub struct Scrypt { 68 | r: usize, 69 | n: usize, 70 | p: usize, 71 | } 72 | 73 | fn block_xor(a: &[u8], b: &[u8]) -> Block { 74 | a.iter().zip(b.iter()).map(|(a, b)| a ^ b).collect() 75 | } 76 | 77 | fn integerify(x: &[Block]) -> u64 { 78 | let last = &x[x.len() - 1]; 79 | let tail = &last[(last.len() - SALSA_BLOCK_SIZE)..]; 80 | 81 | u64::from(tail[0]) 82 | | (u64::from(tail[1]) << 8) 83 | | (u64::from(tail[2]) << 16) 84 | | (u64::from(tail[3]) << 24) 85 | | (u64::from(tail[4]) << 32) 86 | | (u64::from(tail[5]) << 40) 87 | | (u64::from(tail[6]) << 48) 88 | | (u64::from(tail[7]) << 56) 89 | } 90 | 91 | impl Scrypt { 92 | /// 93 | /// Create new instance of Scrypt. 94 | /// 95 | /// Arguments: 96 | /// - `r` Block size parameter, must be larger than 1 97 | /// - `n` CPU/Memory cost parameter, must be larger than 1, 98 | /// a power of 2 and less than 2 ^ (16 * r) 99 | /// - `p` Parallelization parameter, a positive integer 100 | /// less than or equal to (2^32-1) / (4 * r) 101 | /// where hLen is 32 and MFlen is 128 * r. 102 | /// 103 | pub fn new(r: usize, n: usize, p: usize) -> Self { 104 | Self { r, n, p } 105 | } 106 | 107 | fn block_mix(self: &Scrypt, b: &[Block]) -> Vec { 108 | // 109 | // Algorithm scryptBlockMix 110 | // 111 | // Parameters: 112 | // r Block size parameter. 113 | // 114 | // Input: 115 | // B[0], ..., B[2 * r - 1] 116 | // Input vector of 2 * r 64-octet blocks. 117 | // 118 | // Output: 119 | // B'[0], ..., B'[2 * r - 1] 120 | // Output vector of 2 * r 64-octet blocks. 121 | // 122 | // Steps: 123 | // 124 | // 1. X = B[2 * r - 1] 125 | // 126 | // 2. for i = 0 to 2 * r - 1 do 127 | // T = X xor B[i] 128 | // X = Salsa (T) 129 | // Y[i] = X 130 | // end for 131 | // 132 | // 3. B' = (Y[0], Y[2], ..., Y[2 * r - 2], 133 | // Y[1], Y[3], ..., Y[2 * r - 1]) 134 | // 135 | 136 | // Step 1 137 | let mut x = b[2 * self.r - 1].clone(); 138 | 139 | // Step 2 140 | let mut y: Vec = Vec::with_capacity(2 * self.r); 141 | 142 | for b_elem in b.iter() { 143 | let t = block_xor(&x, b_elem); 144 | salsa20(&t, SALSA_ROUNDS, &mut x); 145 | y.push(x.clone()); 146 | } 147 | 148 | // Step 3 149 | let mut bs_head: Vec = Vec::with_capacity(2 * self.r); 150 | let mut bs_tail: Vec = Vec::with_capacity(self.r); 151 | for (i, y_elem) in y.into_iter().enumerate() { 152 | if i % 2 == 0 { 153 | bs_head.push(y_elem); 154 | } else { 155 | bs_tail.push(y_elem); 156 | } 157 | } 158 | bs_head.append(&mut bs_tail); 159 | bs_head 160 | } 161 | 162 | fn ro_mix(self: &Scrypt, b: Vec) -> Vec { 163 | // 164 | // Algorithm scryptROMix 165 | // 166 | // Input: 167 | // r Block size parameter. 168 | // B Input octet vector of length 128 * r octets. 169 | // N CPU/Memory cost parameter, must be larger than 1, 170 | // a power of 2 and less than 2^(128 * r / 8). 171 | // 172 | // Output: 173 | // B' Output octet vector of length 128 * r octets. 174 | // 175 | // Steps: 176 | // 177 | // 1. X = B 178 | // 179 | // 2. for i = 0 to N - 1 do 180 | // V[i] = X 181 | // X = scryptBlockMix (X) 182 | // end for 183 | // 184 | // 3. for i = 0 to N - 1 do 185 | // j = Integerify (X) mod N 186 | // where Integerify (B[0] ... B[2 * r - 1]) is defined 187 | // as the result of interpreting B[2 * r - 1] as a 188 | // little-endian integer. 189 | // T = X xor V[j] 190 | // X = scryptBlockMix (T) 191 | // end for 192 | // 193 | // 4. B' = X 194 | // 195 | 196 | // Step 1 197 | let mut x = b; 198 | 199 | // Step 2 200 | let mut v: Vec> = Vec::with_capacity(self.n); 201 | for _i in 0..self.n { 202 | let t = self.block_mix(&x); 203 | v.push(x); 204 | x = t; 205 | } 206 | 207 | // Step 3 208 | for _i in 0..self.n { 209 | let j = (integerify(&x) as usize) % self.n; 210 | let t: Vec = x 211 | .iter() 212 | .zip(v[j].iter()) 213 | .map(|(x_block, v_block)| block_xor(x_block, v_block)) 214 | .collect(); 215 | x = self.block_mix(&t); 216 | } 217 | 218 | x 219 | } 220 | 221 | /// 222 | /// Derive secret string using `passphrase` and `salt`. 223 | /// 224 | pub fn derive( 225 | self: &Scrypt, 226 | passphrase: &[u8], 227 | salt: &[u8], 228 | out: &mut [u8], 229 | ) -> Result<(), ScryptError> { 230 | if self.r < 1 { 231 | return Err(ScryptError::RIsTooSmall); 232 | } 233 | 234 | if self.n < 1 { 235 | return Err(ScryptError::NIsTooSmall); 236 | } 237 | 238 | if ((self.n - 1) & self.n) != 0 { 239 | return Err(ScryptError::NIsNotAPowerOfTwo); 240 | } 241 | 242 | if self.p < 1 { 243 | return Err(ScryptError::PIsTooSmall); 244 | } 245 | 246 | // 247 | // Algorithm scrypt 248 | // 249 | // Input: 250 | // P Passphrase, an octet string. 251 | // S Salt, an octet string. 252 | // N CPU/Memory cost parameter, must be larger than 1, 253 | // a power of 2 and less than 2^(128 * r / 8). 254 | // r Block size parameter. 255 | // p Parallelization parameter, a positive integer 256 | // less than or equal to ((2^32-1) * hLen) / MFLen 257 | // where hLen is 32 and MFlen is 128 * r. 258 | // dkLen Intended output length in octets of the derived 259 | // key; a positive integer less than or equal to 260 | // (2^32 - 1) * hLen where hLen is 32. 261 | // 262 | // Output: 263 | // DK Derived key, of length dkLen octets. 264 | // 265 | // Steps: 266 | // 267 | // 1. B[0] || B[1] || ... || B[p - 1] = 268 | // PBKDF2-HMAC-SHA256 (P, S, 1, p * 128 * r) 269 | // 270 | // 2. for i = 0 to p - 1 do 271 | // B[i] = scryptROMix (r, B[i], N) 272 | // end for 273 | // 274 | // 3. DK = PBKDF2-HMAC-SHA256 (P, B[0] || B[1] || ... || B[p - 1], 275 | // 1, dkLen) 276 | // 277 | 278 | // Step 1 279 | let mut raw_b: Vec = vec![0; self.p * 2 * BLOCK_SIZE * self.r]; 280 | pbkdf2_sha256(passphrase, salt, PBKDF2_ROUNDS, &mut raw_b); 281 | 282 | let mut b: Vec> = raw_b 283 | .chunks_exact(2 * BLOCK_SIZE * self.r) 284 | .map(|chunk| { 285 | chunk 286 | .chunks_exact(BLOCK_SIZE) 287 | .map(|sub_chunk| sub_chunk.to_vec()) 288 | .collect() 289 | }) 290 | .collect(); 291 | 292 | // Step 2 293 | b = b.into_iter().map(|elem| self.ro_mix(elem)).collect(); 294 | 295 | // Step 3 296 | let b_salt: Vec = b.into_iter().flatten().flatten().collect(); 297 | pbkdf2_sha256(passphrase, &b_salt, PBKDF2_ROUNDS, out); 298 | 299 | Ok(()) 300 | } 301 | } 302 | 303 | #[cfg(test)] 304 | mod tests { 305 | use super::*; 306 | 307 | // https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-03#page-8 308 | 309 | fn check_mix(r: usize, input: &[Block], expected: &[Block]) { 310 | let s = Scrypt::new(r, 1, 1); 311 | assert_eq!(s.block_mix(input), expected); 312 | } 313 | 314 | #[test] 315 | fn it_should_compute_block_mix_for_vec0() { 316 | check_mix( 317 | 1, 318 | &[ 319 | vec![ 320 | 0xf7, 0xce, 0x0b, 0x65, 0x3d, 0x2d, 0x72, 0xa4, 0x10, 0x8c, 0xf5, 0xab, 0xe9, 321 | 0x12, 0xff, 0xdd, 0x77, 0x76, 0x16, 0xdb, 0xbb, 0x27, 0xa7, 0x0e, 0x82, 0x04, 322 | 0xf3, 0xae, 0x2d, 0x0f, 0x6f, 0xad, 0x89, 0xf6, 0x8f, 0x48, 0x11, 0xd1, 0xe8, 323 | 0x7b, 0xcc, 0x3b, 0xd7, 0x40, 0x0a, 0x9f, 0xfd, 0x29, 0x09, 0x4f, 0x01, 0x84, 324 | 0x63, 0x95, 0x74, 0xf3, 0x9a, 0xe5, 0xa1, 0x31, 0x52, 0x17, 0xbc, 0xd7, 325 | ], 326 | vec![ 327 | 0x89, 0x49, 0x91, 0x44, 0x72, 0x13, 0xbb, 0x22, 0x6c, 0x25, 0xb5, 0x4d, 0xa8, 328 | 0x63, 0x70, 0xfb, 0xcd, 0x98, 0x43, 0x80, 0x37, 0x46, 0x66, 0xbb, 0x8f, 0xfc, 329 | 0xb5, 0xbf, 0x40, 0xc2, 0x54, 0xb0, 0x67, 0xd2, 0x7c, 0x51, 0xce, 0x4a, 0xd5, 330 | 0xfe, 0xd8, 0x29, 0xc9, 0x0b, 0x50, 0x5a, 0x57, 0x1b, 0x7f, 0x4d, 0x1c, 0xad, 331 | 0x6a, 0x52, 0x3c, 0xda, 0x77, 0x0e, 0x67, 0xbc, 0xea, 0xaf, 0x7e, 0x89, 332 | ], 333 | ], 334 | &[ 335 | vec![ 336 | 0xa4, 0x1f, 0x85, 0x9c, 0x66, 0x08, 0xcc, 0x99, 0x3b, 0x81, 0xca, 0xcb, 0x02, 337 | 0x0c, 0xef, 0x05, 0x04, 0x4b, 0x21, 0x81, 0xa2, 0xfd, 0x33, 0x7d, 0xfd, 0x7b, 338 | 0x1c, 0x63, 0x96, 0x68, 0x2f, 0x29, 0xb4, 0x39, 0x31, 0x68, 0xe3, 0xc9, 0xe6, 339 | 0xbc, 0xfe, 0x6b, 0xc5, 0xb7, 0xa0, 0x6d, 0x96, 0xba, 0xe4, 0x24, 0xcc, 0x10, 340 | 0x2c, 0x91, 0x74, 0x5c, 0x24, 0xad, 0x67, 0x3d, 0xc7, 0x61, 0x8f, 0x81, 341 | ], 342 | vec![ 343 | 0x20, 0xed, 0xc9, 0x75, 0x32, 0x38, 0x81, 0xa8, 0x05, 0x40, 0xf6, 0x4c, 0x16, 344 | 0x2d, 0xcd, 0x3c, 0x21, 0x07, 0x7c, 0xfe, 0x5f, 0x8d, 0x5f, 0xe2, 0xb1, 0xa4, 345 | 0x16, 0x8f, 0x95, 0x36, 0x78, 0xb7, 0x7d, 0x3b, 0x3d, 0x80, 0x3b, 0x60, 0xe4, 346 | 0xab, 0x92, 0x09, 0x96, 0xe5, 0x9b, 0x4d, 0x53, 0xb6, 0x5d, 0x2a, 0x22, 0x58, 347 | 0x77, 0xd5, 0xed, 0xf5, 0x84, 0x2c, 0xb9, 0xf1, 0x4e, 0xef, 0xe4, 0x25, 348 | ], 349 | ], 350 | ); 351 | } 352 | 353 | fn check_ro_mix(r: usize, n: usize, input: &[Block], expected: &[Block]) { 354 | let s = Scrypt::new(r, n, 1); 355 | assert_eq!(s.ro_mix(input.to_vec()), expected); 356 | } 357 | 358 | #[test] 359 | fn it_should_compute_ro_mix_for_vec0() { 360 | check_ro_mix( 361 | 1, 362 | 16, 363 | &[ 364 | vec![ 365 | 0xf7, 0xce, 0x0b, 0x65, 0x3d, 0x2d, 0x72, 0xa4, 0x10, 0x8c, 0xf5, 0xab, 0xe9, 366 | 0x12, 0xff, 0xdd, 0x77, 0x76, 0x16, 0xdb, 0xbb, 0x27, 0xa7, 0x0e, 0x82, 0x04, 367 | 0xf3, 0xae, 0x2d, 0x0f, 0x6f, 0xad, 0x89, 0xf6, 0x8f, 0x48, 0x11, 0xd1, 0xe8, 368 | 0x7b, 0xcc, 0x3b, 0xd7, 0x40, 0x0a, 0x9f, 0xfd, 0x29, 0x09, 0x4f, 0x01, 0x84, 369 | 0x63, 0x95, 0x74, 0xf3, 0x9a, 0xe5, 0xa1, 0x31, 0x52, 0x17, 0xbc, 0xd7, 370 | ], 371 | vec![ 372 | 0x89, 0x49, 0x91, 0x44, 0x72, 0x13, 0xbb, 0x22, 0x6c, 0x25, 0xb5, 0x4d, 0xa8, 373 | 0x63, 0x70, 0xfb, 0xcd, 0x98, 0x43, 0x80, 0x37, 0x46, 0x66, 0xbb, 0x8f, 0xfc, 374 | 0xb5, 0xbf, 0x40, 0xc2, 0x54, 0xb0, 0x67, 0xd2, 0x7c, 0x51, 0xce, 0x4a, 0xd5, 375 | 0xfe, 0xd8, 0x29, 0xc9, 0x0b, 0x50, 0x5a, 0x57, 0x1b, 0x7f, 0x4d, 0x1c, 0xad, 376 | 0x6a, 0x52, 0x3c, 0xda, 0x77, 0x0e, 0x67, 0xbc, 0xea, 0xaf, 0x7e, 0x89, 377 | ], 378 | ], 379 | &[ 380 | vec![ 381 | 0x79, 0xcc, 0xc1, 0x93, 0x62, 0x9d, 0xeb, 0xca, 0x04, 0x7f, 0x0b, 0x70, 0x60, 382 | 0x4b, 0xf6, 0xb6, 0x2c, 0xe3, 0xdd, 0x4a, 0x96, 0x26, 0xe3, 0x55, 0xfa, 0xfc, 383 | 0x61, 0x98, 0xe6, 0xea, 0x2b, 0x46, 0xd5, 0x84, 0x13, 0x67, 0x3b, 0x99, 0xb0, 384 | 0x29, 0xd6, 0x65, 0xc3, 0x57, 0x60, 0x1f, 0xb4, 0x26, 0xa0, 0xb2, 0xf4, 0xbb, 385 | 0xa2, 0x00, 0xee, 0x9f, 0x0a, 0x43, 0xd1, 0x9b, 0x57, 0x1a, 0x9c, 0x71, 386 | ], 387 | vec![ 388 | 0xef, 0x11, 0x42, 0xe6, 0x5d, 0x5a, 0x26, 0x6f, 0xdd, 0xca, 0x83, 0x2c, 0xe5, 389 | 0x9f, 0xaa, 0x7c, 0xac, 0x0b, 0x9c, 0xf1, 0xbe, 0x2b, 0xff, 0xca, 0x30, 0x0d, 390 | 0x01, 0xee, 0x38, 0x76, 0x19, 0xc4, 0xae, 0x12, 0xfd, 0x44, 0x38, 0xf2, 0x03, 391 | 0xa0, 0xe4, 0xe1, 0xc4, 0x7e, 0xc3, 0x14, 0x86, 0x1f, 0x4e, 0x90, 0x87, 0xcb, 392 | 0x33, 0x39, 0x6a, 0x68, 0x73, 0xe8, 0xf9, 0xd2, 0x53, 0x9a, 0x4b, 0x8e, 393 | ], 394 | ], 395 | ); 396 | } 397 | 398 | // https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-03#page-10 399 | 400 | #[test] 401 | fn it_should_compute_scrypt_for_vec0() { 402 | let s = Scrypt::new(1, 16, 1); 403 | 404 | let mut out: [u8; 64] = [0; 64]; 405 | s.derive(b"", b"", &mut out) 406 | .expect("derivation to not fail"); 407 | assert_eq!( 408 | out.to_vec(), 409 | vec![ 410 | 0x77, 0xd6, 0x57, 0x62, 0x38, 0x65, 0x7b, 0x20, 0x3b, 0x19, 0xca, 0x42, 0xc1, 0x8a, 411 | 0x04, 0x97, 0xf1, 0x6b, 0x48, 0x44, 0xe3, 0x07, 0x4a, 0xe8, 0xdf, 0xdf, 0xfa, 0x3f, 412 | 0xed, 0xe2, 0x14, 0x42, 0xfc, 0xd0, 0x06, 0x9d, 0xed, 0x09, 0x48, 0xf8, 0x32, 0x6a, 413 | 0x75, 0x3a, 0x0f, 0xc8, 0x1f, 0x17, 0xe8, 0xd3, 0xe0, 0xfb, 0x2e, 0x0d, 0x36, 0x28, 414 | 0xcf, 0x35, 0xe2, 0x0c, 0x38, 0xd1, 0x89, 0x06 415 | ] 416 | ); 417 | } 418 | 419 | #[test] 420 | fn it_should_compute_scrypt_for_vec1() { 421 | let s = Scrypt::new(8, 1024, 16); 422 | 423 | let mut out: [u8; 64] = [0; 64]; 424 | s.derive(b"password", b"NaCl", &mut out) 425 | .expect("derivation to not fail"); 426 | assert_eq!( 427 | out.to_vec(), 428 | vec![ 429 | 0xfd, 0xba, 0xbe, 0x1c, 0x9d, 0x34, 0x72, 0x00, 0x78, 0x56, 0xe7, 0x19, 0x0d, 0x01, 430 | 0xe9, 0xfe, 0x7c, 0x6a, 0xd7, 0xcb, 0xc8, 0x23, 0x78, 0x30, 0xe7, 0x73, 0x76, 0x63, 431 | 0x4b, 0x37, 0x31, 0x62, 0x2e, 0xaf, 0x30, 0xd9, 0x2e, 0x22, 0xa3, 0x88, 0x6f, 0xf1, 432 | 0x09, 0x27, 0x9d, 0x98, 0x30, 0xda, 0xc7, 0x27, 0xaf, 0xb9, 0x4a, 0x83, 0xee, 0x6d, 433 | 0x83, 0x60, 0xcb, 0xdf, 0xa2, 0xcc, 0x06, 0x40 434 | ] 435 | ); 436 | } 437 | 438 | #[test] 439 | fn it_should_compute_scrypt_for_vec2() { 440 | let s = Scrypt::new(8, 16384, 1); 441 | 442 | let mut out: [u8; 64] = [0; 64]; 443 | s.derive(b"pleaseletmein", b"SodiumChloride", &mut out) 444 | .expect("derivation to not fail"); 445 | assert_eq!( 446 | out.to_vec(), 447 | vec![ 448 | 0x70, 0x23, 0xbd, 0xcb, 0x3a, 0xfd, 0x73, 0x48, 0x46, 0x1c, 0x06, 0xcd, 0x81, 0xfd, 449 | 0x38, 0xeb, 0xfd, 0xa8, 0xfb, 0xba, 0x90, 0x4f, 0x8e, 0x3e, 0xa9, 0xb5, 0x43, 0xf6, 450 | 0x54, 0x5d, 0xa1, 0xf2, 0xd5, 0x43, 0x29, 0x55, 0x61, 0x3f, 0x0f, 0xcf, 0x62, 0xd4, 451 | 0x97, 0x05, 0x24, 0x2a, 0x9a, 0xf9, 0xe6, 0x1e, 0x85, 0xdc, 0x0d, 0x65, 0x1e, 0x40, 452 | 0xdf, 0xcf, 0x01, 0x7b, 0x45, 0x57, 0x58, 0x87 453 | ] 454 | ); 455 | } 456 | 457 | #[test] 458 | fn it_should_compute_scrypt_for_vec3() { 459 | let s = Scrypt::new(8, 1_048_576, 1); 460 | 461 | let mut out: [u8; 64] = [0; 64]; 462 | s.derive(b"pleaseletmein", b"SodiumChloride", &mut out) 463 | .expect("derivation to not fail"); 464 | assert_eq!( 465 | out.to_vec(), 466 | vec![ 467 | 0x21, 0x01, 0xcb, 0x9b, 0x6a, 0x51, 0x1a, 0xae, 0xad, 0xdb, 0xbe, 0x09, 0xcf, 0x70, 468 | 0xf8, 0x81, 0xec, 0x56, 0x8d, 0x57, 0x4a, 0x2f, 0xfd, 0x4d, 0xab, 0xe5, 0xee, 0x98, 469 | 0x20, 0xad, 0xaa, 0x47, 0x8e, 0x56, 0xfd, 0x8f, 0x4b, 0xa5, 0xd0, 0x9f, 0xfa, 0x1c, 470 | 0x6d, 0x92, 0x7c, 0x40, 0xf4, 0xc3, 0x37, 0x30, 0x40, 0x49, 0xe8, 0xa9, 0x52, 0xfb, 471 | 0xcb, 0xf4, 0x5c, 0x6f, 0xa7, 0x7a, 0x41, 0xa4 472 | ] 473 | ); 474 | } 475 | } 476 | -------------------------------------------------------------------------------- /src/sha256.rs: -------------------------------------------------------------------------------- 1 | //! # SHA256 2 | //! 3 | //! Implementation of SHA256 digest according to [RFC 6234][rfc]. 4 | //! 5 | //! [rfc]: https://tools.ietf.org/html/rfc6234#section-5.1 6 | //! 7 | 8 | /// Length of digest array 9 | pub const DIGEST_SIZE: usize = 32; 10 | 11 | /// Internal block size 12 | pub const BLOCK_SIZE: usize = 64; 13 | 14 | // See: https://tools.ietf.org/html/rfc6234#section-5.1 15 | const K: [u32; 64] = [ 16 | 0x428a_2f98, 17 | 0x7137_4491, 18 | 0xb5c0_fbcf, 19 | 0xe9b5_dba5, 20 | 0x3956_c25b, 21 | 0x59f1_11f1, 22 | 0x923f_82a4, 23 | 0xab1c_5ed5, 24 | 0xd807_aa98, 25 | 0x1283_5b01, 26 | 0x2431_85be, 27 | 0x550c_7dc3, 28 | 0x72be_5d74, 29 | 0x80de_b1fe, 30 | 0x9bdc_06a7, 31 | 0xc19b_f174, 32 | 0xe49b_69c1, 33 | 0xefbe_4786, 34 | 0x0fc1_9dc6, 35 | 0x240c_a1cc, 36 | 0x2de9_2c6f, 37 | 0x4a74_84aa, 38 | 0x5cb0_a9dc, 39 | 0x76f9_88da, 40 | 0x983e_5152, 41 | 0xa831_c66d, 42 | 0xb003_27c8, 43 | 0xbf59_7fc7, 44 | 0xc6e0_0bf3, 45 | 0xd5a7_9147, 46 | 0x06ca_6351, 47 | 0x1429_2967, 48 | 0x27b7_0a85, 49 | 0x2e1b_2138, 50 | 0x4d2c_6dfc, 51 | 0x5338_0d13, 52 | 0x650a_7354, 53 | 0x766a_0abb, 54 | 0x81c2_c92e, 55 | 0x9272_2c85, 56 | 0xa2bf_e8a1, 57 | 0xa81a_664b, 58 | 0xc24b_8b70, 59 | 0xc76c_51a3, 60 | 0xd192_e819, 61 | 0xd699_0624, 62 | 0xf40e_3585, 63 | 0x106a_a070, 64 | 0x19a4_c116, 65 | 0x1e37_6c08, 66 | 0x2748_774c, 67 | 0x34b0_bcb5, 68 | 0x391c_0cb3, 69 | 0x4ed8_aa4a, 70 | 0x5b9c_ca4f, 71 | 0x682e_6ff3, 72 | 0x748f_82ee, 73 | 0x78a5_636f, 74 | 0x84c8_7814, 75 | 0x8cc7_0208, 76 | 0x90be_fffa, 77 | 0xa450_6ceb, 78 | 0xbef9_a3f7, 79 | 0xc671_78f2, 80 | ]; 81 | 82 | const H: [u32; 8] = [ 83 | 0x6a09_e667, 84 | 0xbb67_ae85, 85 | 0x3c6e_f372, 86 | 0xa54f_f53a, 87 | 0x510e_527f, 88 | 0x9b05_688c, 89 | 0x1f83_d9ab, 90 | 0x5be0_cd19, 91 | ]; 92 | 93 | // CH( x, y, z) = (x AND y) XOR ( (NOT x) AND z) 94 | fn ch(x: u32, y: u32, z: u32) -> u32 { 95 | (x & y) ^ ((!x) & z) 96 | } 97 | 98 | // MAJ( x, y, z) = (x AND y) XOR (x AND z) XOR (y AND z) 99 | fn maj(x: u32, y: u32, z: u32) -> u32 { 100 | (x & y) ^ (x & z) ^ (y & z) 101 | } 102 | 103 | fn rotr(n: u32, s: u32) -> u32 { 104 | (n << (32 - s)) | (n >> s) 105 | } 106 | 107 | // BSIG0(x) = ROTR^2(x) XOR ROTR^13(x) XOR ROTR^22(x) 108 | fn bsig0(x: u32) -> u32 { 109 | rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22) 110 | } 111 | 112 | // BSIG1(x) = ROTR^6(x) XOR ROTR^11(x) XOR ROTR^25(x) 113 | fn bsig1(x: u32) -> u32 { 114 | rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25) 115 | } 116 | 117 | // SSIG0(x) = ROTR^7(x) XOR ROTR^18(x) XOR SHR^3(x) 118 | fn ssig0(x: u32) -> u32 { 119 | rotr(x, 7) ^ rotr(x, 18) ^ (x >> 3) 120 | } 121 | 122 | // SSIG1(x) = ROTR^17(x) XOR ROTR^19(x) XOR SHR^10(x) 123 | fn ssig1(x: u32) -> u32 { 124 | rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10) 125 | } 126 | 127 | fn fill_block(input: &[u8; BLOCK_SIZE], output: &mut [u32; BLOCK_SIZE / 4]) { 128 | for i in 0..output.len() { 129 | let i0 = u32::from(input[i * 4]); 130 | let i1 = u32::from(input[i * 4 + 1]); 131 | let i2 = u32::from(input[i * 4 + 2]); 132 | let i3 = u32::from(input[i * 4 + 3]); 133 | 134 | output[i] = (i0 << 24) | (i1 << 16) | (i2 << 8) | i3; 135 | } 136 | } 137 | 138 | fn write_u64_be(data: &mut [u8], value: u64) { 139 | data[0] = (value >> 56) as u8; 140 | data[1] = (value >> 48) as u8; 141 | data[2] = (value >> 40) as u8; 142 | data[3] = (value >> 32) as u8; 143 | data[4] = (value >> 24) as u8; 144 | data[5] = (value >> 16) as u8; 145 | data[6] = (value >> 8) as u8; 146 | data[7] = (value) as u8; 147 | } 148 | 149 | /// 150 | /// Main digest structure. 151 | /// 152 | /// Usage: 153 | /// ```rust 154 | /// extern crate dumb_crypto; 155 | /// 156 | /// use dumb_crypto::sha256::SHA256; 157 | /// 158 | /// let mut sha256 = SHA256::new(); 159 | /// 160 | /// sha256.update(b"hello world"); 161 | /// assert_eq!(sha256.digest().to_vec(), vec![ 162 | /// 0xb9, 0x4d, 0x27, 0xb9, 0x93, 0x4d, 0x3e, 0x08, 163 | /// 0xa5, 0x2e, 0x52, 0xd7, 0xda, 0x7d, 0xab, 0xfa, 164 | /// 0xc4, 0x84, 0xef, 0xe3, 0x7a, 0x53, 0x80, 0xee, 165 | /// 0x90, 0x88, 0xf7, 0xac, 0xe2, 0xef, 0xcd, 0xe9, 166 | /// ]); 167 | /// ``` 168 | /// 169 | pub struct SHA256 { 170 | h: [u32; 8], 171 | buffer: [u8; BLOCK_SIZE], 172 | length: usize, 173 | } 174 | 175 | impl SHA256 { 176 | /// 177 | /// Create new instance of SHA256 digest. 178 | /// 179 | pub fn new() -> SHA256 { 180 | SHA256 { 181 | h: H, 182 | buffer: [0; BLOCK_SIZE], 183 | length: 0, 184 | } 185 | } 186 | 187 | fn process_block(self: &mut SHA256, block: &[u32; 16]) { 188 | // 189 | // 1. Prepare the message schedule W: 190 | // For t = 0 to 15 191 | // Wt = M(i)t 192 | // For t = 16 to 63 193 | // Wt = SSIG1(W(t-2)) + W(t-7) + SSIG0(w(t-15)) + W(t-16) 194 | // 195 | let mut w: [u32; 64] = [0; 64]; 196 | let mut a: u32; 197 | let mut b: u32; 198 | let mut c: u32; 199 | let mut d: u32; 200 | let mut e: u32; 201 | let mut f: u32; 202 | let mut g: u32; 203 | let mut h: u32; 204 | 205 | w[..16].clone_from_slice(&block[..16]); 206 | 207 | for t in 16..64 { 208 | w[t] = wrapping_sum!(ssig1(w[t - 2]), w[t - 7], ssig0(w[t - 15]), w[t - 16]); 209 | } 210 | 211 | // 212 | // 2. Initialize the working variables: 213 | // a = H(i-1)0 214 | // b = H(i-1)1 215 | // c = H(i-1)2 216 | // d = H(i-1)3 217 | // e = H(i-1)4 218 | // f = H(i-1)5 219 | // g = H(i-1)6 220 | // h = H(i-1)7 221 | // 222 | a = self.h[0]; 223 | b = self.h[1]; 224 | c = self.h[2]; 225 | d = self.h[3]; 226 | e = self.h[4]; 227 | f = self.h[5]; 228 | g = self.h[6]; 229 | h = self.h[7]; 230 | 231 | // 232 | // 3. Perform the main hash computation: 233 | // For t = 0 to 63 234 | // T1 = h + BSIG1(e) + CH(e,f,g) + Kt + Wt 235 | // T2 = BSIG0(a) + MAJ(a,b,c) 236 | // h = g 237 | // g = f 238 | // f = e 239 | // e = d + T1 240 | // d = c 241 | // c = b 242 | // b = a 243 | // a = T1 + T2 244 | // 245 | for t in 0..64 { 246 | let t1 = wrapping_sum!(h, bsig1(e), ch(e, f, g), K[t], w[t]); 247 | let t2 = wrapping_sum!(bsig0(a), maj(a, b, c)); 248 | h = g; 249 | g = f; 250 | f = e; 251 | e = wrapping_sum!(d, t1); 252 | d = c; 253 | c = b; 254 | b = a; 255 | a = wrapping_sum!(t1, t2); 256 | } 257 | 258 | // 259 | // 4. Compute the intermediate hash value H(i) 260 | // H(i)0 = a + H(i-1)0 261 | // H(i)1 = b + H(i-1)1 262 | // H(i)2 = c + H(i-1)2 263 | // H(i)3 = d + H(i-1)3 264 | // H(i)4 = e + H(i-1)4 265 | // H(i)5 = f + H(i-1)5 266 | // H(i)6 = g + H(i-1)6 267 | // H(i)7 = h + H(i-1)7 268 | // 269 | self.h[0] = wrapping_sum!(self.h[0], a); 270 | self.h[1] = wrapping_sum!(self.h[1], b); 271 | self.h[2] = wrapping_sum!(self.h[2], c); 272 | self.h[3] = wrapping_sum!(self.h[3], d); 273 | self.h[4] = wrapping_sum!(self.h[4], e); 274 | self.h[5] = wrapping_sum!(self.h[5], f); 275 | self.h[6] = wrapping_sum!(self.h[6], g); 276 | self.h[7] = wrapping_sum!(self.h[7], h); 277 | } 278 | 279 | /// 280 | /// Add input `data` to the digest. 281 | /// 282 | pub fn update(self: &mut SHA256, data: &[u8]) { 283 | let mut block: [u32; BLOCK_SIZE / 4] = [0; BLOCK_SIZE / 4]; 284 | 285 | for &b in data { 286 | let off = self.length % BLOCK_SIZE; 287 | 288 | // Fill the buffer 289 | self.buffer[off] = b; 290 | self.length += 1; 291 | 292 | if self.length % BLOCK_SIZE != 0 { 293 | continue; 294 | } 295 | 296 | fill_block(&self.buffer, &mut block); 297 | self.process_block(&block); 298 | } 299 | } 300 | 301 | /// 302 | /// Generate digest array. 303 | /// 304 | pub fn digest(self: &mut SHA256) -> [u8; DIGEST_SIZE] { 305 | // https://tools.ietf.org/html/rfc6234#section-4.1 306 | 307 | // 308 | // Suppose a message has length L < 2^64. Before it is input to the 309 | // hash function, the message is padded on the right as follows: 310 | // 311 | // a. "1" is appended. Example: if the original message is "01010000", 312 | // this is padded to "010100001". 313 | // 314 | // b. K "0"s are appended where K is the smallest, non-negative solution 315 | // to the equation 316 | // 317 | // ( L + 1 + K ) mod 512 = 448 318 | // 319 | // c. Then append the 64-bit block that is L in binary representation. 320 | // After appending this block, the length of the message will be a 321 | // multiple of 512 bits. 322 | // 323 | 324 | let mut block: [u32; BLOCK_SIZE / 4] = [0; BLOCK_SIZE / 4]; 325 | 326 | // NOTE: It is simple to calculate `k`, but having it is a bit useless, 327 | // since we know that this algorithm just wants to zero the rest of the 328 | // block and to put the 64bit length to the end 329 | let mut off = self.length % BLOCK_SIZE; 330 | 331 | // (b) 332 | for i in off..self.buffer.len() { 333 | self.buffer[i] = 0; 334 | } 335 | 336 | // If the padding does not fit in a single block - append 0x80 and zeroes 337 | // to the current block and flush it. 338 | // Then fill the block with zeroes and append size to it, and flush it 339 | // again. 340 | let has_overflow = off + 9 > BLOCK_SIZE; 341 | if has_overflow { 342 | // (a) 343 | self.buffer[off] |= 0x80; 344 | 345 | fill_block(&self.buffer, &mut block); 346 | self.process_block(&block); 347 | 348 | off = 0; 349 | self.buffer = [0; BLOCK_SIZE]; 350 | } 351 | 352 | // (c) 353 | let len_off = self.buffer.len() - 8; 354 | write_u64_be(&mut self.buffer[len_off..], (self.length * 8) as u64); 355 | 356 | // (a) 357 | if !has_overflow { 358 | self.buffer[off] |= 0x80; 359 | } 360 | 361 | fill_block(&self.buffer, &mut block); 362 | self.process_block(&block); 363 | 364 | let mut out: [u8; DIGEST_SIZE] = [0; DIGEST_SIZE]; 365 | for i in 0..self.h.len() { 366 | out[i * 4] = (self.h[i] >> 24) as u8; 367 | out[i * 4 + 1] = (self.h[i] >> 16) as u8; 368 | out[i * 4 + 2] = (self.h[i] >> 8) as u8; 369 | out[i * 4 + 3] = (self.h[i]) as u8; 370 | } 371 | out 372 | } 373 | } 374 | 375 | impl Default for SHA256 { 376 | fn default() -> SHA256 { 377 | SHA256::new() 378 | } 379 | } 380 | 381 | #[cfg(test)] 382 | mod tests { 383 | use super::*; 384 | 385 | fn check(inputs: &[&str], expected: [u8; DIGEST_SIZE]) { 386 | let mut sha256 = SHA256::new(); 387 | 388 | for chunk in inputs { 389 | sha256.update(chunk.as_bytes()); 390 | } 391 | assert_eq!(sha256.digest(), expected); 392 | } 393 | 394 | #[test] 395 | fn it_should_compute_digest_for_abc() { 396 | check( 397 | &["abc"], 398 | [ 399 | 0xBA, 0x78, 0x16, 0xBF, 0x8F, 0x01, 0xCF, 0xEA, 0x41, 0x41, 0x40, 0xDE, 0x5D, 0xAE, 400 | 0x22, 0x23, 0xB0, 0x03, 0x61, 0xA3, 0x96, 0x17, 0x7A, 0x9C, 0xB4, 0x10, 0xFF, 0x61, 401 | 0xF2, 0x00, 0x15, 0xAD, 402 | ], 403 | ); 404 | } 405 | 406 | #[test] 407 | fn it_should_compute_digest_for_a_b_c() { 408 | check( 409 | &["a", "b", "c"], 410 | [ 411 | 0xBA, 0x78, 0x16, 0xBF, 0x8F, 0x01, 0xCF, 0xEA, 0x41, 0x41, 0x40, 0xDE, 0x5D, 0xAE, 412 | 0x22, 0x23, 0xB0, 0x03, 0x61, 0xA3, 0x96, 0x17, 0x7A, 0x9C, 0xB4, 0x10, 0xFF, 0x61, 413 | 0xF2, 0x00, 0x15, 0xAD, 414 | ], 415 | ); 416 | } 417 | 418 | #[test] 419 | fn it_should_compute_digest_for_long_str() { 420 | check( 421 | &["abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"], 422 | [ 423 | 0x24, 0x8D, 0x6A, 0x61, 0xD2, 0x06, 0x38, 0xB8, 0xE5, 0xC0, 0x26, 0x93, 0x0C, 0x3E, 424 | 0x60, 0x39, 0xA3, 0x3C, 0xE4, 0x59, 0x64, 0xFF, 0x21, 0x67, 0xF6, 0xEC, 0xED, 0xD4, 425 | 0x19, 0xDB, 0x06, 0xC1, 426 | ], 427 | ); 428 | } 429 | 430 | #[test] 431 | fn it_should_compute_digest_for_long_chunked_str() { 432 | check( 433 | &[ 434 | "abcdbcdec", 435 | "defdefgefg", 436 | "hfghighijhi", 437 | "jkijkljklmkl", 438 | "mnlmnomnopnopq", 439 | ], 440 | [ 441 | 0x24, 0x8D, 0x6A, 0x61, 0xD2, 0x06, 0x38, 0xB8, 0xE5, 0xC0, 0x26, 0x93, 0x0C, 0x3E, 442 | 0x60, 0x39, 0xA3, 0x3C, 0xE4, 0x59, 0x64, 0xFF, 0x21, 0x67, 0xF6, 0xEC, 0xED, 0xD4, 443 | 0x19, 0xDB, 0x06, 0xC1, 444 | ], 445 | ); 446 | } 447 | 448 | #[test] 449 | fn it_should_compute_digest_for_doubled_chunked_long_str() { 450 | check( 451 | &[ 452 | "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 453 | "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 454 | ], 455 | [ 456 | 0x59, 0xf1, 0x09, 0xd9, 0x53, 0x3b, 0x2b, 0x70, 0xe7, 0xc3, 0xb8, 0x14, 0xa2, 0xbd, 457 | 0x21, 0x8f, 0x78, 0xea, 0x5d, 0x37, 0x14, 0x45, 0x5b, 0xc6, 0x79, 0x87, 0xcf, 0x0d, 458 | 0x66, 0x43, 0x99, 0xcf, 459 | ], 460 | ); 461 | } 462 | 463 | #[test] 464 | fn it_should_compute_digest_for_doubled_long_str() { 465 | check( 466 | &[ 467 | "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopqabc\ 468 | dbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 469 | ], 470 | [ 471 | 0x59, 0xf1, 0x09, 0xd9, 0x53, 0x3b, 0x2b, 0x70, 0xe7, 0xc3, 0xb8, 0x14, 0xa2, 0xbd, 472 | 0x21, 0x8f, 0x78, 0xea, 0x5d, 0x37, 0x14, 0x45, 0x5b, 0xc6, 0x79, 0x87, 0xcf, 0x0d, 473 | 0x66, 0x43, 0x99, 0xcf, 474 | ], 475 | ); 476 | } 477 | } 478 | --------------------------------------------------------------------------------