├── src
├── glossary.md
├── signing
│ └── README.md
├── hashing-data
│ └── README.md
├── encryption-asymmetric
│ └── README.md
├── encryption-symmetric
│ └── README.md
├── message-authentication
│ └── README.md
├── README.md
├── SUMMARY.md
└── key-derivation
│ ├── README.md
│ └── hashing-password.md
├── test-book
├── src
│ └── main.rs
├── tests
│ ├── skeptic.rs
│ └── check_glossary_sort.rs
├── build.rs
├── Cargo.toml
└── Cargo.lock
├── pre-commit
├── book.toml
├── .gitignore
├── README.md
├── .github
└── workflows
│ └── ci.yaml
└── LICENSE
/src/glossary.md:
--------------------------------------------------------------------------------
1 | # Glossary
2 |
--------------------------------------------------------------------------------
/src/signing/README.md:
--------------------------------------------------------------------------------
1 | # Digital signing
2 |
--------------------------------------------------------------------------------
/src/hashing-data/README.md:
--------------------------------------------------------------------------------
1 | # Data Hashing
2 |
--------------------------------------------------------------------------------
/src/encryption-asymmetric/README.md:
--------------------------------------------------------------------------------
1 | # Asymmetric Encryption
2 |
--------------------------------------------------------------------------------
/src/encryption-symmetric/README.md:
--------------------------------------------------------------------------------
1 | # Symmetric Encryption
2 |
--------------------------------------------------------------------------------
/test-book/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | println!("Hello, world!");
3 | }
4 |
--------------------------------------------------------------------------------
/src/message-authentication/README.md:
--------------------------------------------------------------------------------
1 | # Hash-Based Message Authentication (HMAC)
2 |
--------------------------------------------------------------------------------
/test-book/tests/skeptic.rs:
--------------------------------------------------------------------------------
1 | include!(concat!(env!("OUT_DIR"), "/skeptic-tests.rs"));
2 |
--------------------------------------------------------------------------------
/src/README.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | The [RustCrypto] organization provides pure Rust implementations of various cryptographic
4 | algorithms.
5 |
6 | This book is a work in progress.
7 |
8 | [rustcrypto]: https://github.com/RustCrypto/
9 |
--------------------------------------------------------------------------------
/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # check mdbook
4 | mdbook build
5 |
6 | # apply formatting
7 | mdformat -h > /dev/null || echo 'You may need to install mdformat: `pip install mdformat-gfm`'
8 | mdformat --wrap 100 .
9 |
10 | cargo fmt --manifest-path=test-book/Cargo.toml
11 |
--------------------------------------------------------------------------------
/book.toml:
--------------------------------------------------------------------------------
1 | [book]
2 | authors = ["RustCrypto Organization"]
3 | language = "en"
4 | multilingual = false
5 | title = "The RustCrypto Book"
6 | description = "Reference manual for the RustCrypto project"
7 |
8 | [rust]
9 | edition = "2021"
10 |
11 | # [output.html.playground]
12 | # editable = true
13 |
--------------------------------------------------------------------------------
/test-book/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | // Generate tests for all markdown files
3 | let mdbook_path = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/");
4 | let readme_path = concat!(env!("CARGO_MANIFEST_DIR"), "/../README.md");
5 | let mut mdbook_files = skeptic::markdown_files_of_directory(mdbook_path);
6 | mdbook_files.push(readme_path.into());
7 | skeptic::generate_doc_tests(&mdbook_files);
8 | }
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Node rules:
2 | ## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
3 | .grunt
4 |
5 | ## Dependency directory
6 | ## Commenting this out is preferred by some people, see
7 | ## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git
8 | node_modules
9 |
10 | # Book build output
11 | _book
12 |
13 | # eBook build output
14 | *.epub
15 | *.mobi
16 | *.pdf
17 |
18 | # Book build output
19 | book/
20 |
21 | # Cargo output
22 | **/target/
23 |
--------------------------------------------------------------------------------
/test-book/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "test-book"
3 | version = "0.1.0"
4 | edition = "2021"
5 | description = "Test runner for mdbook"
6 |
7 | [dev-dependencies]
8 | skeptic = "0.13.7"
9 |
10 | [build-dependencies]
11 | skeptic = "0.13.7"
12 |
13 | [dependencies]
14 | argon2 = "0.5.2"
15 | data-encoding = "2.4.0"
16 | hmac = "0.12.1"
17 | password-hash = "0.5.0"
18 | pbkdf2 = { version = "0.12.2", features = ["simple"] }
19 | scrypt = { version = "0.11.0", features = ["simple"] }
20 | sha2 = "0.10.8"
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # The RustCrypto Book
2 |
3 | Reference manual for the RustCrypto project, implemented as a MDBook \[WIP\].
4 |
5 | You can read the book at .
6 |
7 | ## Contributing
8 |
9 | Contributions are welcome! You will need [`mdbook`] to build the book and \[`mdformat-gfn`\] to
10 | check formatting.
11 |
12 | If you would like to use the pre-commit hook, just simlink it:
13 | `ln -s "$(pwd)/pre-commit" .git/hooks/`.
14 |
15 | [`mdbook`]: https://rust-lang.github.io/mdBook/guide/installation.html
16 |
--------------------------------------------------------------------------------
/src/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | [Introduction](README.md)
4 |
5 | - [Data Hashing](hashing-data/README.md)
6 | - [Key Derivation](key-derivation/README.md)
7 | - [Password Hashing](key-derivation/hashing-password.md)
8 | - [Message Authentication](message-authentication/README.md)
9 | - [Digital Signing](signing/README.md)
10 | - [Asymmetric Encryption](encryption-asymmetric/README.md)
11 | - [Symmetric Encryption](encryption-symmetric/README.md)
12 |
13 | ______________________________________________________________________
14 |
15 | - [Glossary](glossary.md)
16 |
--------------------------------------------------------------------------------
/src/key-derivation/README.md:
--------------------------------------------------------------------------------
1 | # Key Derivation Functions
2 |
3 | [Key derivation functions] (KDFs) provide a way to take an input such as a password, and create an
4 | output of specified length. This output can then be used, for example, as the key for an encryption
5 | algorithm.
6 |
7 | Password hashing functions are a related concept to key derivation functions; see
8 | [Password Hashing](hashing-password.md) for more information on those.
9 |
10 |
11 |
12 | [key derivation functions]: https://en.wikipedia.org/wiki/Key_derivation_function
13 |
--------------------------------------------------------------------------------
/test-book/tests/check_glossary_sort.rs:
--------------------------------------------------------------------------------
1 | use std::fmt::Write;
2 |
3 | #[test]
4 | fn check_glossary_sort() {
5 | // Make sure glossary headings are in order
6 | let glossary_path = concat!(env!("CARGO_MANIFEST_DIR"), "/../src/glossary.md");
7 | let text = std::fs::read_to_string(glossary_path).unwrap();
8 |
9 | let mut linenums = vec![];
10 | let mut headings = vec![];
11 | text.lines()
12 | .enumerate()
13 | .filter(|(_, line)| line.starts_with("## "))
14 | .for_each(|(idx, line)| {
15 | linenums.push(idx);
16 | headings.push(line.strip_prefix("## ").unwrap());
17 | });
18 |
19 | let unsorted = headings.clone();
20 | headings.sort_unstable();
21 | let longest = headings.iter().max_by_key(|h| h.len()).unwrap_or(&"").len();
22 |
23 | if !(unsorted == headings) {
24 | let mut msg = format!(
25 | "glossary headings are not currently sorted\n\n\
26 | line {:17, block size of 8, and 1 degree of
22 | parallelism
23 | 1. Bcrypt with a minimum work factor of 10 and a password limit of 72 bytes
24 | 1. If FIPS-140 compliance is required, PBKDF2 with a minimum work factor of 600,000 and a SHA-256
25 | internal hash function
26 |
27 | You don't need to worry about setting these parameters; the default implementations match the
28 | recommendations.
29 |
30 | ```rust,noplaypen
31 | # fn main() {
32 | use argon2::{Argon2, PasswordHasher, PasswordVerifier, password_hash::Salt};
33 |
34 | let password = "password";
35 |
36 | // This is the b64 hash of "bad salt!" for demo only: don't do this! Instead use:
37 | // let salt = SaltString::generate(&mut OsRng);
38 | let salt_str = "YmFkIHNhbHQh";
39 | let salt: Salt = salt_str.try_into().unwrap();
40 |
41 | let argon2 = Argon2::default();
42 | let hash = argon2.hash_password(password.as_bytes(), salt).unwrap();
43 |
44 | // This is the hash we will store. Notice our salt string is included, as well as parameters:
45 | // version 0x13 (19), memory 19456KiB (19 MiB), 2 iterations (time), parallelism 1
46 | let expected =
47 | "$argon2id$v=19$m=19456,t=2,p=1$YmFkIHNhbHQh$DqHGwv6NQV0VcaJi7jeF1E8IpfMXmXcpq4r2kKyqpXk";
48 | // ^ hash ^ parameters ^ salt ^ combined hash
49 |
50 | assert_eq!(expected, hash.to_string());
51 |
52 | // The verifier reads the salt and the parameters from the hash and verifies the result is equal
53 | Argon2::default().verify_password(password.as_bytes(), &hash).expect("invalid password");
54 | # }
55 | ```
56 |
57 | Sometimes it may be necessary to support the verification of passwords that were hashed with
58 | different algorithms or parameters. This can be done using the traits in `password_hash`:
59 |
60 | ```rust,noplaypen
61 | # fn main() {
62 | use password_hash::{PasswordHash, PasswordVerifier};
63 |
64 | use argon2::Argon2;
65 | use pbkdf2::Pbkdf2;
66 | use scrypt::Scrypt;
67 |
68 | // Can be: `$argon2`, `$pbkdf2`, or `$scrypt`
69 | let hash_string =
70 | "$argon2id$v=19$m=19456,t=2,p=1$YmFkIHNhbHQh$DqHGwv6NQV0VcaJi7jeF1E8IpfMXmXcpq4r2kKyqpXk";
71 | let input_password = "password";
72 |
73 | let password_hash = PasswordHash::new(&hash_string).expect("invalid password hash");
74 |
75 | // Trait objects for algorithms to support
76 | let algs: &[&dyn PasswordVerifier] = &[&Argon2::default(), &Pbkdf2, &Scrypt];
77 |
78 | password_hash.verify_password(algs, input_password).expect("invalid password");
79 | # }
80 | ```
81 |
82 | ## Differences with Standard Hash Functions
83 |
84 | Compared with [standard hash functions](hashing-data/README.md), password hashes have a few features
85 | that make them well suited to this purpose:
86 |
87 | ### Salt
88 |
89 | "Salt" is random data that gets concatenated with the password before going through the hash
90 | function. This data needs to be different for each password hashed, but gets stored with the output
91 | hash and reused during verification.
92 |
93 | This roughly looks like the following:
94 |
95 | ```
96 | storage:
97 |
98 | per-entry random salt -\
99 | \
100 | plaintext password -> argon2 -> database entry
101 |
102 |
103 | verification:
104 |
105 | /-- hash ---------------------------\
106 | database entry -| argon2 -> test hash -> compare
107 | \ / /
108 | \-- salt --/ /
109 | /
110 | test password ----------------/
111 |
112 | ```
113 |
114 | See [Why Salt?](#appendix-why-salt) in the appendix for an illustration of why this is necessary.
115 |
116 | ### Slowness
117 |
118 | While hashes like SHA-256 are meant to be fast, password hashes are meant to be slow. This is to
119 | help slow down brute forcing, where an attacker tries hashing a wide variety of common passwords
120 | with a known salt to try to compute a known hash.
121 |
122 | As mentioned in [What should I use?], password hashing algorithms have parameters that can control
123 | how slow or fast they are to solve. The defaults recommended by OWASP provide a good balance of high
124 | security without being nonperformant.
125 |
126 | ### A Note on Pepper
127 |
128 | Peppering is the process of encrypting hashed passwords with a secret key before storing in the
129 | database. For example:
130 |
131 | ```
132 | storage:
133 |
134 | per-entry random salt -\
135 | \
136 | plaintext password -> argon2 -> aes256-gcm encrypt -> database entry
137 | /
138 | per-database secret key --------/
139 |
140 |
141 | verification:
142 |
143 | per-database secret key ----\
144 | \ /-- hash ---------------------------\
145 | database entry -> aes256-gem decrypt -| argon2 -> test hash -> compare
146 | \ / /
147 | \-- salt --/ /
148 | /
149 | test password --------------------------------------/
150 |
151 | ```
152 |
153 | OWSAP now suggests this process since it means that even if an attacker has access to the database
154 | table, they cannot do anything with it unless they also have the encryption key (stored separately).
155 |
156 | Peppering is not considered part of password hashing functions.
157 |
158 | ## Appendix: Why Salt?
159 |
160 | What purpose does salting passwords solve? To start, imagine that we use SHA-256 to hash our
161 | password:
162 |
163 | > **WARNING**: DO NOT use SHA-256 as a password hashing algorithm. We use it in this example only
164 | > because it is easy to understand. See the [What should I use?] section at the top of this page for
165 | > better options.
166 |
167 | ```rust,editable
168 | # fn main() {
169 | # use sha2::{Sha256, Digest};
170 | # use data_encoding::HEXLOWER;
171 | let input = "password";
172 |
173 | let mut hash = Sha256::new();
174 | hash.update(input.as_bytes());
175 | let result = hash.finalize();
176 |
177 | println!("Hash: {}", HEXLOWER.encode(&result));
178 | # }
179 | ```
180 |
181 | This prints `5e884898da...`, which we would store in our database.
182 |
183 | Now some security event happens, and our database with all password hashes gets leaked. Now, the
184 | public has this information:
185 |
186 | ```
187 | +----------+------------+
188 | | username | pw_sha256 |
189 | +----------+------------+
190 | | ferris | 5e884898da | <- note this hash
191 | | curie | 437dd76609 |
192 | | turing | fc3a03a63b |
193 | +----------+------------+
194 | ```
195 |
196 | An attacker is prepared, and has precalculated a table of common password hashes:
197 |
198 | ```
199 | +----------+------------+
200 | | input | sha_256 |
201 | +----------+------------+
202 | | 1234 | 03ac674216 |
203 | | abcd | 84e73dc50f |
204 | | password | 5e884898da | <- note this hash
205 | +----------+------------+
206 | ```
207 |
208 | Bingo! user `ferris` has password hash `5e884898da`, which means the password `password` will work
209 | to access ferris's account. The user is now compromised!
210 |
211 | This is a simple version of a rainbow attack. To prevent this, we do what is called "salting". This
212 | means that we pick a random salt that we store with the password in the database, and hash that salt
213 | with the input password when we want to check it:
214 |
215 | ```
216 | +----------+------------------------+
217 | | username | pw |
218 | +----------+------------------------+
219 | | ferris | $sha2$salty$d878e396b9 | # user's password is 'password'
220 | | curie | $sha2$spice$036ecfef69 |
221 | | turing | $sha2$spice$701f72c284 |
222 | | lovelace | $sha2$sugar$a92ec3b280 | # user's password is also 'password'
223 | +----------+------------------------+
224 | ```
225 |
226 | Notice how no hashes from the attacker's list appear in our new password table, and that even though
227 | two users have the same password, their hashes are different. This means that an attacker can no
228 | longer look up a hash directly, and instead has to waste time calculating each possible input!
229 |
230 | A salt function with SHA-256 would roughly look like:
231 |
232 | ```rust,editable
233 | # fn main() {
234 | # use sha2::{Sha256, Digest};
235 | # use data_encoding::HEXLOWER;
236 | // Input from the user
237 | let input = "password";
238 |
239 | // That user's salt, stored in the database
240 | let salt = "salty";
241 | let to_hash = format!("{salt}{input}");
242 |
243 | // Compute salt+input hash
244 | let mut hash = Sha256::new();
245 | hash.update(to_hash.as_bytes());
246 | let result = hash.finalize();
247 | let digest = HEXLOWER.encode(&result);
248 |
249 | println!("Hash: {digest}");
250 |
251 | // Hash of salt+password in the database
252 | let stored_value = "d878e396b9";
253 | assert_eq!(stored_value, &digest[..10]);
254 | # }
255 | ```
256 |
257 | The [`PasswordHasher`] trait makes sure that salt is always used when hashing passwords.
258 |
259 | [password hash functions]: https://en.wikipedia.org/wiki/Key_derivation_function#Password_hashing
260 | [password storage cheat sheet]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
261 | [what should i use?]: #what-should-i-use
262 | [`password-hash`]: https://docs.rs/password-hash/latest/password_hash/
263 | [`passwordhasher`]: https://docs.rs/password-hash/latest/password_hash/trait.PasswordHasher.html
264 | [`passwordverifier`]: https://docs.rs/password-hash/latest/password_hash/trait.PasswordVerifier.html
265 |
--------------------------------------------------------------------------------
/test-book/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "argon2"
7 | version = "0.5.2"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "17ba4cac0a46bc1d2912652a751c47f2a9f3a7fe89bcae2275d418f5270402f9"
10 | dependencies = [
11 | "base64ct",
12 | "blake2",
13 | "cpufeatures",
14 | "password-hash",
15 | ]
16 |
17 | [[package]]
18 | name = "base64ct"
19 | version = "1.6.0"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 | checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
22 |
23 | [[package]]
24 | name = "bitflags"
25 | version = "1.3.2"
26 | source = "registry+https://github.com/rust-lang/crates.io-index"
27 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
28 |
29 | [[package]]
30 | name = "bitflags"
31 | version = "2.4.0"
32 | source = "registry+https://github.com/rust-lang/crates.io-index"
33 | checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
34 |
35 | [[package]]
36 | name = "blake2"
37 | version = "0.10.6"
38 | source = "registry+https://github.com/rust-lang/crates.io-index"
39 | checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
40 | dependencies = [
41 | "digest",
42 | ]
43 |
44 | [[package]]
45 | name = "block-buffer"
46 | version = "0.10.4"
47 | source = "registry+https://github.com/rust-lang/crates.io-index"
48 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
49 | dependencies = [
50 | "generic-array",
51 | ]
52 |
53 | [[package]]
54 | name = "bytecount"
55 | version = "0.6.4"
56 | source = "registry+https://github.com/rust-lang/crates.io-index"
57 | checksum = "ad152d03a2c813c80bb94fedbf3a3f02b28f793e39e7c214c8a0bcc196343de7"
58 |
59 | [[package]]
60 | name = "camino"
61 | version = "1.1.6"
62 | source = "registry+https://github.com/rust-lang/crates.io-index"
63 | checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c"
64 | dependencies = [
65 | "serde",
66 | ]
67 |
68 | [[package]]
69 | name = "cargo-platform"
70 | version = "0.1.3"
71 | source = "registry+https://github.com/rust-lang/crates.io-index"
72 | checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479"
73 | dependencies = [
74 | "serde",
75 | ]
76 |
77 | [[package]]
78 | name = "cargo_metadata"
79 | version = "0.14.2"
80 | source = "registry+https://github.com/rust-lang/crates.io-index"
81 | checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
82 | dependencies = [
83 | "camino",
84 | "cargo-platform",
85 | "semver",
86 | "serde",
87 | "serde_json",
88 | ]
89 |
90 | [[package]]
91 | name = "cc"
92 | version = "1.0.83"
93 | source = "registry+https://github.com/rust-lang/crates.io-index"
94 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
95 | dependencies = [
96 | "libc",
97 | ]
98 |
99 | [[package]]
100 | name = "cfg-if"
101 | version = "1.0.0"
102 | source = "registry+https://github.com/rust-lang/crates.io-index"
103 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
104 |
105 | [[package]]
106 | name = "cipher"
107 | version = "0.4.4"
108 | source = "registry+https://github.com/rust-lang/crates.io-index"
109 | checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
110 | dependencies = [
111 | "crypto-common",
112 | "inout",
113 | ]
114 |
115 | [[package]]
116 | name = "cpufeatures"
117 | version = "0.2.9"
118 | source = "registry+https://github.com/rust-lang/crates.io-index"
119 | checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
120 | dependencies = [
121 | "libc",
122 | ]
123 |
124 | [[package]]
125 | name = "crypto-common"
126 | version = "0.1.6"
127 | source = "registry+https://github.com/rust-lang/crates.io-index"
128 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
129 | dependencies = [
130 | "generic-array",
131 | "typenum",
132 | ]
133 |
134 | [[package]]
135 | name = "data-encoding"
136 | version = "2.4.0"
137 | source = "registry+https://github.com/rust-lang/crates.io-index"
138 | checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
139 |
140 | [[package]]
141 | name = "digest"
142 | version = "0.10.7"
143 | source = "registry+https://github.com/rust-lang/crates.io-index"
144 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
145 | dependencies = [
146 | "block-buffer",
147 | "crypto-common",
148 | "subtle",
149 | ]
150 |
151 | [[package]]
152 | name = "errno"
153 | version = "0.3.4"
154 | source = "registry+https://github.com/rust-lang/crates.io-index"
155 | checksum = "add4f07d43996f76ef320709726a556a9d4f965d9410d8d0271132d2f8293480"
156 | dependencies = [
157 | "errno-dragonfly",
158 | "libc",
159 | "windows-sys",
160 | ]
161 |
162 | [[package]]
163 | name = "errno-dragonfly"
164 | version = "0.1.2"
165 | source = "registry+https://github.com/rust-lang/crates.io-index"
166 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
167 | dependencies = [
168 | "cc",
169 | "libc",
170 | ]
171 |
172 | [[package]]
173 | name = "error-chain"
174 | version = "0.12.4"
175 | source = "registry+https://github.com/rust-lang/crates.io-index"
176 | checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
177 | dependencies = [
178 | "version_check",
179 | ]
180 |
181 | [[package]]
182 | name = "fastrand"
183 | version = "2.0.1"
184 | source = "registry+https://github.com/rust-lang/crates.io-index"
185 | checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
186 |
187 | [[package]]
188 | name = "generic-array"
189 | version = "0.14.7"
190 | source = "registry+https://github.com/rust-lang/crates.io-index"
191 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
192 | dependencies = [
193 | "typenum",
194 | "version_check",
195 | ]
196 |
197 | [[package]]
198 | name = "getrandom"
199 | version = "0.2.10"
200 | source = "registry+https://github.com/rust-lang/crates.io-index"
201 | checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
202 | dependencies = [
203 | "cfg-if",
204 | "libc",
205 | "wasi",
206 | ]
207 |
208 | [[package]]
209 | name = "glob"
210 | version = "0.3.1"
211 | source = "registry+https://github.com/rust-lang/crates.io-index"
212 | checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
213 |
214 | [[package]]
215 | name = "hmac"
216 | version = "0.12.1"
217 | source = "registry+https://github.com/rust-lang/crates.io-index"
218 | checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
219 | dependencies = [
220 | "digest",
221 | ]
222 |
223 | [[package]]
224 | name = "inout"
225 | version = "0.1.3"
226 | source = "registry+https://github.com/rust-lang/crates.io-index"
227 | checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
228 | dependencies = [
229 | "generic-array",
230 | ]
231 |
232 | [[package]]
233 | name = "itoa"
234 | version = "1.0.9"
235 | source = "registry+https://github.com/rust-lang/crates.io-index"
236 | checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
237 |
238 | [[package]]
239 | name = "libc"
240 | version = "0.2.148"
241 | source = "registry+https://github.com/rust-lang/crates.io-index"
242 | checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
243 |
244 | [[package]]
245 | name = "linux-raw-sys"
246 | version = "0.4.8"
247 | source = "registry+https://github.com/rust-lang/crates.io-index"
248 | checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db"
249 |
250 | [[package]]
251 | name = "memchr"
252 | version = "2.6.4"
253 | source = "registry+https://github.com/rust-lang/crates.io-index"
254 | checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
255 |
256 | [[package]]
257 | name = "password-hash"
258 | version = "0.5.0"
259 | source = "registry+https://github.com/rust-lang/crates.io-index"
260 | checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
261 | dependencies = [
262 | "base64ct",
263 | "rand_core",
264 | "subtle",
265 | ]
266 |
267 | [[package]]
268 | name = "pbkdf2"
269 | version = "0.12.2"
270 | source = "registry+https://github.com/rust-lang/crates.io-index"
271 | checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
272 | dependencies = [
273 | "digest",
274 | "hmac",
275 | "password-hash",
276 | "sha2",
277 | ]
278 |
279 | [[package]]
280 | name = "proc-macro2"
281 | version = "1.0.67"
282 | source = "registry+https://github.com/rust-lang/crates.io-index"
283 | checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
284 | dependencies = [
285 | "unicode-ident",
286 | ]
287 |
288 | [[package]]
289 | name = "pulldown-cmark"
290 | version = "0.9.3"
291 | source = "registry+https://github.com/rust-lang/crates.io-index"
292 | checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998"
293 | dependencies = [
294 | "bitflags 1.3.2",
295 | "memchr",
296 | "unicase",
297 | ]
298 |
299 | [[package]]
300 | name = "quote"
301 | version = "1.0.33"
302 | source = "registry+https://github.com/rust-lang/crates.io-index"
303 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
304 | dependencies = [
305 | "proc-macro2",
306 | ]
307 |
308 | [[package]]
309 | name = "rand_core"
310 | version = "0.6.4"
311 | source = "registry+https://github.com/rust-lang/crates.io-index"
312 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
313 | dependencies = [
314 | "getrandom",
315 | ]
316 |
317 | [[package]]
318 | name = "redox_syscall"
319 | version = "0.3.5"
320 | source = "registry+https://github.com/rust-lang/crates.io-index"
321 | checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
322 | dependencies = [
323 | "bitflags 1.3.2",
324 | ]
325 |
326 | [[package]]
327 | name = "rustix"
328 | version = "0.38.15"
329 | source = "registry+https://github.com/rust-lang/crates.io-index"
330 | checksum = "d2f9da0cbd88f9f09e7814e388301c8414c51c62aa6ce1e4b5c551d49d96e531"
331 | dependencies = [
332 | "bitflags 2.4.0",
333 | "errno",
334 | "libc",
335 | "linux-raw-sys",
336 | "windows-sys",
337 | ]
338 |
339 | [[package]]
340 | name = "ryu"
341 | version = "1.0.15"
342 | source = "registry+https://github.com/rust-lang/crates.io-index"
343 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
344 |
345 | [[package]]
346 | name = "salsa20"
347 | version = "0.10.2"
348 | source = "registry+https://github.com/rust-lang/crates.io-index"
349 | checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213"
350 | dependencies = [
351 | "cipher",
352 | ]
353 |
354 | [[package]]
355 | name = "same-file"
356 | version = "1.0.6"
357 | source = "registry+https://github.com/rust-lang/crates.io-index"
358 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
359 | dependencies = [
360 | "winapi-util",
361 | ]
362 |
363 | [[package]]
364 | name = "scrypt"
365 | version = "0.11.0"
366 | source = "registry+https://github.com/rust-lang/crates.io-index"
367 | checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f"
368 | dependencies = [
369 | "password-hash",
370 | "pbkdf2",
371 | "salsa20",
372 | "sha2",
373 | ]
374 |
375 | [[package]]
376 | name = "semver"
377 | version = "1.0.19"
378 | source = "registry+https://github.com/rust-lang/crates.io-index"
379 | checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0"
380 | dependencies = [
381 | "serde",
382 | ]
383 |
384 | [[package]]
385 | name = "serde"
386 | version = "1.0.188"
387 | source = "registry+https://github.com/rust-lang/crates.io-index"
388 | checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
389 | dependencies = [
390 | "serde_derive",
391 | ]
392 |
393 | [[package]]
394 | name = "serde_derive"
395 | version = "1.0.188"
396 | source = "registry+https://github.com/rust-lang/crates.io-index"
397 | checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
398 | dependencies = [
399 | "proc-macro2",
400 | "quote",
401 | "syn",
402 | ]
403 |
404 | [[package]]
405 | name = "serde_json"
406 | version = "1.0.107"
407 | source = "registry+https://github.com/rust-lang/crates.io-index"
408 | checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
409 | dependencies = [
410 | "itoa",
411 | "ryu",
412 | "serde",
413 | ]
414 |
415 | [[package]]
416 | name = "sha2"
417 | version = "0.10.8"
418 | source = "registry+https://github.com/rust-lang/crates.io-index"
419 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
420 | dependencies = [
421 | "cfg-if",
422 | "cpufeatures",
423 | "digest",
424 | ]
425 |
426 | [[package]]
427 | name = "skeptic"
428 | version = "0.13.7"
429 | source = "registry+https://github.com/rust-lang/crates.io-index"
430 | checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8"
431 | dependencies = [
432 | "bytecount",
433 | "cargo_metadata",
434 | "error-chain",
435 | "glob",
436 | "pulldown-cmark",
437 | "tempfile",
438 | "walkdir",
439 | ]
440 |
441 | [[package]]
442 | name = "subtle"
443 | version = "2.5.0"
444 | source = "registry+https://github.com/rust-lang/crates.io-index"
445 | checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
446 |
447 | [[package]]
448 | name = "syn"
449 | version = "2.0.37"
450 | source = "registry+https://github.com/rust-lang/crates.io-index"
451 | checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8"
452 | dependencies = [
453 | "proc-macro2",
454 | "quote",
455 | "unicode-ident",
456 | ]
457 |
458 | [[package]]
459 | name = "tempfile"
460 | version = "3.8.0"
461 | source = "registry+https://github.com/rust-lang/crates.io-index"
462 | checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
463 | dependencies = [
464 | "cfg-if",
465 | "fastrand",
466 | "redox_syscall",
467 | "rustix",
468 | "windows-sys",
469 | ]
470 |
471 | [[package]]
472 | name = "test-book"
473 | version = "0.1.0"
474 | dependencies = [
475 | "argon2",
476 | "data-encoding",
477 | "hmac",
478 | "password-hash",
479 | "pbkdf2",
480 | "scrypt",
481 | "sha2",
482 | "skeptic",
483 | ]
484 |
485 | [[package]]
486 | name = "typenum"
487 | version = "1.17.0"
488 | source = "registry+https://github.com/rust-lang/crates.io-index"
489 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
490 |
491 | [[package]]
492 | name = "unicase"
493 | version = "2.7.0"
494 | source = "registry+https://github.com/rust-lang/crates.io-index"
495 | checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
496 | dependencies = [
497 | "version_check",
498 | ]
499 |
500 | [[package]]
501 | name = "unicode-ident"
502 | version = "1.0.12"
503 | source = "registry+https://github.com/rust-lang/crates.io-index"
504 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
505 |
506 | [[package]]
507 | name = "version_check"
508 | version = "0.9.4"
509 | source = "registry+https://github.com/rust-lang/crates.io-index"
510 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
511 |
512 | [[package]]
513 | name = "walkdir"
514 | version = "2.4.0"
515 | source = "registry+https://github.com/rust-lang/crates.io-index"
516 | checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
517 | dependencies = [
518 | "same-file",
519 | "winapi-util",
520 | ]
521 |
522 | [[package]]
523 | name = "wasi"
524 | version = "0.11.0+wasi-snapshot-preview1"
525 | source = "registry+https://github.com/rust-lang/crates.io-index"
526 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
527 |
528 | [[package]]
529 | name = "winapi"
530 | version = "0.3.9"
531 | source = "registry+https://github.com/rust-lang/crates.io-index"
532 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
533 | dependencies = [
534 | "winapi-i686-pc-windows-gnu",
535 | "winapi-x86_64-pc-windows-gnu",
536 | ]
537 |
538 | [[package]]
539 | name = "winapi-i686-pc-windows-gnu"
540 | version = "0.4.0"
541 | source = "registry+https://github.com/rust-lang/crates.io-index"
542 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
543 |
544 | [[package]]
545 | name = "winapi-util"
546 | version = "0.1.6"
547 | source = "registry+https://github.com/rust-lang/crates.io-index"
548 | checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
549 | dependencies = [
550 | "winapi",
551 | ]
552 |
553 | [[package]]
554 | name = "winapi-x86_64-pc-windows-gnu"
555 | version = "0.4.0"
556 | source = "registry+https://github.com/rust-lang/crates.io-index"
557 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
558 |
559 | [[package]]
560 | name = "windows-sys"
561 | version = "0.48.0"
562 | source = "registry+https://github.com/rust-lang/crates.io-index"
563 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
564 | dependencies = [
565 | "windows-targets",
566 | ]
567 |
568 | [[package]]
569 | name = "windows-targets"
570 | version = "0.48.5"
571 | source = "registry+https://github.com/rust-lang/crates.io-index"
572 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
573 | dependencies = [
574 | "windows_aarch64_gnullvm",
575 | "windows_aarch64_msvc",
576 | "windows_i686_gnu",
577 | "windows_i686_msvc",
578 | "windows_x86_64_gnu",
579 | "windows_x86_64_gnullvm",
580 | "windows_x86_64_msvc",
581 | ]
582 |
583 | [[package]]
584 | name = "windows_aarch64_gnullvm"
585 | version = "0.48.5"
586 | source = "registry+https://github.com/rust-lang/crates.io-index"
587 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
588 |
589 | [[package]]
590 | name = "windows_aarch64_msvc"
591 | version = "0.48.5"
592 | source = "registry+https://github.com/rust-lang/crates.io-index"
593 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
594 |
595 | [[package]]
596 | name = "windows_i686_gnu"
597 | version = "0.48.5"
598 | source = "registry+https://github.com/rust-lang/crates.io-index"
599 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
600 |
601 | [[package]]
602 | name = "windows_i686_msvc"
603 | version = "0.48.5"
604 | source = "registry+https://github.com/rust-lang/crates.io-index"
605 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
606 |
607 | [[package]]
608 | name = "windows_x86_64_gnu"
609 | version = "0.48.5"
610 | source = "registry+https://github.com/rust-lang/crates.io-index"
611 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
612 |
613 | [[package]]
614 | name = "windows_x86_64_gnullvm"
615 | version = "0.48.5"
616 | source = "registry+https://github.com/rust-lang/crates.io-index"
617 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
618 |
619 | [[package]]
620 | name = "windows_x86_64_msvc"
621 | version = "0.48.5"
622 | source = "registry+https://github.com/rust-lang/crates.io-index"
623 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
624 |
--------------------------------------------------------------------------------