Marco Ricci commited on 2025-01-27 19:39:03
Zeige 4 geänderte Dateien mit 137 Einfügungen und 0 Löschungen.
| ... | ... |
@@ -0,0 +1,131 @@ |
| 1 |
+--- |
|
| 2 |
+title: What are "interchangable passphrases" in `vault`, and what does that mean in practice? |
|
| 3 |
+--- |
|
| 4 |
+ |
|
| 5 |
+# What are "interchangable passphrases" in `vault`, and what does that mean in practice? |
|
| 6 |
+ |
|
| 7 |
+## What are "interchangable passphrases"? |
|
| 8 |
+ |
|
| 9 |
+The "vault" derivation scheme internally uses PBKDF2-HMAC-SHA1[^1] to turn |
|
| 10 |
+the master passphrase[^2] into a pseudo-random bit sequence, which then |
|
| 11 |
+drives the actual passphrase derivation. |
|
| 12 |
+In this context, the master passphrase is passed directly as a key to |
|
| 13 |
+HMAC-SHA1, and because HMAC-SHA1 requires keys of exactly 64 bytes size, the |
|
| 14 |
+key is thus subject to the HMAC key mapping procedure. |
|
| 15 |
+Because the mapping of infinitely many arbitrarily sized keys to 64-byte |
|
| 16 |
+sized keys cannot be one-to-one, there exist pairs of keys that behave |
|
| 17 |
+identically when passed into (PBKDF2-)HMAC-SHA1, i.e., the keys (master |
|
| 18 |
+passphrases) are "interchangable" from the vault scheme's perspective. |
|
| 19 |
+ |
|
| 20 |
+Fundamentally, this is an issue of *encoding*: the master passphrase is |
|
| 21 |
+interpreted as an encoding of the HMAC-SHA1 key, and this encoding is not |
|
| 22 |
+unique, so the effective space of HMAC-SHA1 keys is reduced through the |
|
| 23 |
+presence of "non-canonical" encodings of keys. |
|
| 24 |
+ |
|
| 25 |
+ [^1]: PBKDF2 is a key derivation function, published in [RFC 2898][]. |
|
| 26 |
+ It uses a pseudo-random function such as HMAC-SHA1 (hashed message |
|
| 27 |
+ authentication code, specified in [RFC 2104][] and using SHA1 as the |
|
| 28 |
+ underlying hash function) when processing its input. PBKDF2 passes the |
|
| 29 |
+ key on to its pseudo-random function, and otherwise only depends on the |
|
| 30 |
+ output of the pseudo-random function, not on the key. |
|
| 31 |
+ |
|
| 32 |
+ [^2]: If you use a master SSH key, it is first converted to an "equivalent |
|
| 33 |
+ master passphrase". |
|
| 34 |
+ |
|
| 35 |
+ [RFC 2104]: https://datatracker.ietf.org/doc/html/rfc2104 |
|
| 36 |
+ [RFC 2898]: https://datatracker.ietf.org/doc/html/rfc2898 |
|
| 37 |
+ |
|
| 38 |
+## What is the HMAC key mapping procedure? |
|
| 39 |
+ |
|
| 40 |
+???+ abstract "HMAC key mapping procedure" |
|
| 41 |
+ |
|
| 42 |
+ Let <var>MP</var> denote the master passphrase, and let <var>K</var> |
|
| 43 |
+ denote the HMAC key candidate. Let <var>B</var> denote the block size |
|
| 44 |
+ of HMAC-SHA1 in bytes, i.e., `64`. At the beginning, |
|
| 45 |
+ <var>K</var> = <var>MP</var>. |
|
| 46 |
+ |
|
| 47 |
+ 1. If <var>K</var> (= <var>MP</var>) is larger than <var>B</var>, set |
|
| 48 |
+ <var>K</var> to `SHA1(K)`. This updates <var>K</var> for all |
|
| 49 |
+ further steps below. |
|
| 50 |
+ 2. If <var>K</var> is smaller than <var>B</var>, append as many NUL |
|
| 51 |
+ bytes as necessary to extend <var>K</var> to size <var>B</var>. |
|
| 52 |
+ 3. Use <var>K</var> as the HMAC key. |
|
| 53 |
+ |
|
| 54 |
+## What effect does the HMAC key mapping procedure have on key security? |
|
| 55 |
+ |
|
| 56 |
+The key space shrinks to 99.6% of its original size. |
|
| 57 |
+But since it started out as astronomically large (2^512^), it *still* is |
|
| 58 |
+astronomically large. |
|
| 59 |
+ |
|
| 60 |
+??? example "Mathematical details: key space" |
|
| 61 |
+ |
|
| 62 |
+ | variant | key space size | fraction of total size | |
|
| 63 |
+ |:-----------------------------|:---------------|:-----------------------| |
|
| 64 |
+ | 64-byte keys only | 256^64^ = 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096 | 99.609375% | |
|
| 65 |
+ | full key size up to 64 bytes | (256^65^ – 1) / (256 – 1) = 13460387568883548460748825096238025916214579019888834136067575410167731732152266768867764001296969715641757473316629133406121545097483615318772604492382465 | 100% | |
|
| 66 |
+ |
|
| 67 |
+ The key space sizes can be calculated using the following formulas. Let |
|
| 68 |
+ <var>q</var> = `256` denote the alphabet size of binary strings, and let |
|
| 69 |
+ <var>n</var> denote the string length. The total count of all strings |
|
| 70 |
+ of size <var>n</var> is <var>q</var>^<var>n</var>^, and the total count |
|
| 71 |
+ of all strings up to (and including) size <var>n</var> is |
|
| 72 |
+ (<var>q</var><sup><var>n</var> + 1</sup> – 1) / (<var>q</var> – 1) per |
|
| 73 |
+ the formula for geometric series. |
|
| 74 |
+ |
|
| 75 |
+ Verification: |
|
| 76 |
+ |
|
| 77 |
+ ~~~~ shell-session |
|
| 78 |
+ $ # using GNU bc 1.07.1 |
|
| 79 |
+ $ |
|
| 80 |
+ $ BC_LINE_LENGTH=0 bc <<'HERE' |
|
| 81 |
+ > # The total count of size 64 byte strings. |
|
| 82 |
+ > 256^64 |
|
| 83 |
+ > # The total count of byte strings of size 64 or less. |
|
| 84 |
+ > (256^65 - 1) / (256 - 1) |
|
| 85 |
+ > # The fraction of the former within the latter. |
|
| 86 |
+ > scale = 8 |
|
| 87 |
+ > (256^64) / ((256^65 - 1) / (256 - 1)) |
|
| 88 |
+ > HERE |
|
| 89 |
+ 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096 |
|
| 90 |
+ 13460387568883548460748825096238025916214579019888834136067575410167731732152266768867764001296969715641757473316629133406121545097483615318772604492382465 |
|
| 91 |
+ .99609375 |
|
| 92 |
+ $ |
|
| 93 |
+ $ BC_LINE_LENGTH=0 bc <<'HERE' |
|
| 94 |
+ > # The fraction of unusable keys. |
|
| 95 |
+ > scale = 160 |
|
| 96 |
+ > (256^64 - 1) / (256^65 - 1) |
|
| 97 |
+ > 1 / 256 |
|
| 98 |
+ > HERE |
|
| 99 |
+ .0039062499999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999997097 |
|
| 100 |
+ .0039062500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 |
|
| 101 |
+ ~~~~ |
|
| 102 |
+ |
|
| 103 |
+In particular, assuming a sufficiently secure master passphrase, this |
|
| 104 |
+mapping procedure is still cryptographically secure against attackers |
|
| 105 |
+without possession of the master passphrase if the hashing function (here: |
|
| 106 |
+SHA1) is secure against preimage attacks: |
|
| 107 |
+ |
|
| 108 |
+ * The attacker can attempt to guess a NUL-extended version of the |
|
| 109 |
+ passphrase if it is shorter than or equal in length to 64 bytes. This |
|
| 110 |
+ has the same computational cost as guessing the master passphrase |
|
| 111 |
+ directly, which is cryptographically secure by assumption. |
|
| 112 |
+ |
|
| 113 |
+ * The attacker can attempt to guess a hashed and NUL-extended version of |
|
| 114 |
+ the passphrase if it is larger than 64 bytes. This amounts to carrying |
|
| 115 |
+ out a preimage attack against the SHA1 digest of the master passphrase, |
|
| 116 |
+ which is also cryptographically secure by assumption. |
|
| 117 |
+ |
|
| 118 |
+## What effect does the HMAC key mapping procedure have on `derivepassphrase`? |
|
| 119 |
+ |
|
| 120 |
+`derivepassphrase vault` does not check for interchangable passphrases, and |
|
| 121 |
+will happily accept any (non-empty) passphrase it is given. |
|
| 122 |
+The [`derivepassphrase.vault.Vault`][] class does not check for |
|
| 123 |
+interchangable passphrases either, and will happily accept any passphrase it |
|
| 124 |
+is given, even empty ones. |
|
| 125 |
+ |
|
| 126 |
+Most interchangable variations of a master passphrase contain binary |
|
| 127 |
+characters such as NUL, or even arbitrary byte sequences, which may be hard |
|
| 128 |
+to type in or impossible to express in certain storage formats. As such, it |
|
| 129 |
+is unlikely---but otherwise supported---that the user would want to enter |
|
| 130 |
+or store a different, interchangable version of their master passphrase in |
|
| 131 |
+the first place. |
| ... | ... |
@@ -4,5 +4,8 @@ title: Explanation overview |
| 4 | 4 |
|
| 5 | 5 |
* [How to comply with the "altered versions" clause of the |
| 6 | 6 |
license][FAQ_ALTERED_VERSIONS] |
| 7 |
+* [What are "interchangable passphrases" in `vault`, and what does that mean |
|
| 8 |
+ in practice?][FAQ_INTERCHANGABLE_PASSPHRASES] |
|
| 7 | 9 |
|
| 8 | 10 |
[FAQ_ALTERED_VERSIONS]: faq-altered-versions.md |
| 11 |
+[FAQ_INTERCHANGABLE_PASSPHRASES]: faq-vault-interchangable-passphrases.md |
| ... | ... |
@@ -117,6 +117,7 @@ nav: |
| 117 | 117 |
- Design & Background: |
| 118 | 118 |
- explanation/index.md |
| 119 | 119 |
- '"altered versions" license requirement': explanation/faq-altered-versions.md |
| 120 |
+ - '"interchangable passphrases" in vault': explanation/faq-vault-interchangable-passphrases.md |
|
| 120 | 121 |
- Changelog: |
| 121 | 122 |
- Changelog: changelog.md |
| 122 | 123 |
- Upgrade notes: upgrade-notes.md |
| ... | ... |
@@ -136,6 +137,7 @@ markdown_extensions: |
| 136 | 137 |
- smarty |
| 137 | 138 |
- toc: |
| 138 | 139 |
permalink: true |
| 140 |
+ - pymdownx.caret |
|
| 139 | 141 |
- pymdownx.details |
| 140 | 142 |
- pymdownx.snippets: |
| 141 | 143 |
base_path: |
| ... | ... |
@@ -44,6 +44,7 @@ nav: |
| 44 | 44 |
- Design & Background: |
| 45 | 45 |
- explanation/index.md |
| 46 | 46 |
- '"altered versions" license requirement': explanation/faq-altered-versions.md |
| 47 |
+ - '"interchangable passphrases" in vault': explanation/faq-vault-interchangable-passphrases.md |
|
| 47 | 48 |
- Changelog: |
| 48 | 49 |
- Changelog: changelog.md |
| 49 | 50 |
- Upgrade notes: upgrade-notes.md |
| 50 | 51 |