-
-
Notifications
You must be signed in to change notification settings - Fork 750
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Argon2 the second part: implement key encryption / decryption #6469
Argon2 the second part: implement key encryption / decryption #6469
Conversation
Codecov Report
@@ Coverage Diff @@
## master #6469 +/- ##
==========================================
+ Coverage 82.76% 83.09% +0.32%
==========================================
Files 39 39
Lines 10655 10573 -82
Branches 2089 2069 -20
==========================================
- Hits 8819 8786 -33
+ Misses 1327 1286 -41
+ Partials 509 501 -8
Continue to review full report at Codecov.
|
After refreshing my memory of Encrypt-then-MAC vs Encrypt-and-MAC vs MAC-then-Encrypt I feel like I would prefer to use a high level API that just does the right thing. Do you have one? If not I'll try to Encrypt-then-MAC. How hard can it be to remember to MAC all the bits in the message that affect the decryption? |
280eb43
to
b8a1369
Compare
Done. I will mark this as ready for review after #6468 is merged. |
Note: a malicious key file might be able to perform denial of service e.g. by causing us to allocate too much RAM |
About DoS via expensive argon2 parameters in key file: guess if someone wants to annoy you and has write access to your key files, they can do worse than that, like e.g. just delete the key files. |
c5f7181
to
3922314
Compare
This should be ready now. |
maybe also do the "encrypt key file" here? |
back to draft then |
1. Note: I have rebased this on top of 78f0414 and fixed the code to work with the new Passphrase.argon2 interface 2. Refactor: s/enc_key/encrypted_key/ - I intend to use the name enc_key for something else 3. Dispatch on algorithm instead of version borgbackup#747 (comment) New: rebased on 28731c5 (current master)
040e6be
to
46243e1
Compare
Things left to do before this PR is ready:
|
looks good so far. TBD yet whether we should rather use the recommended or 2nd recommended profile for argon2. problem is that we have quite widespread use cases, some people use borg on their old raspi or on their mobile phone, other use it on server / workstation class hardware with lots of memory. one way to solve that would be that memory limited users keep using pbkdf2 while we use the high-memory profile for argon2 for everybody else. another way would be to offer both argon2 profiles, so people with low memory do not need to use the old crap. :-) |
Should we run testsuite/benchmark.py at full KDF strength? I feel that weakening KDF may defeat the point of a benchmark. |
Not sure about the benchmarks, but I guess:
|
I am starting one more experiment to see the difference between skipping KDF entirely and weakening its parameters https://github.com/hexagonrecursion/borg/actions/runs/2087617735 |
Skip and weaken are often rather close. What are the differences between benchmark 0..3 configuration? |
My data tells me that TESTONLY_MOCK_KDF=weaken and TESTONLY_MOCK_KDF=skip performed essentially identically. Which approach would you prefer?
I will happily implement any combination |
|
0e4d072
to
24010a6
Compare
None. I wanted to be confident in my methodology so I repeat the same test 4 times. I got consistent results as expected. I have submitted one final benchmark - to verify that the performance improvement matches our expectation https://github.com/hexagonrecursion/borg/actions/runs/2094039179 My plan is to start working on |
The benchmark run you linked above did not work, rc == 1. |
Thanks. I have started a run with --show-output to see what failed https://github.com/hexagonrecursion/borg/actions/runs/2096410352 |
Are we ready merge this now or are we going to expand the scope of this PR again? |
src/borg/helpers/passphrase.py
Outdated
if os.environ.get("BORG_TESTONLY_WEAKEN_KDF") is not None: | ||
iterations = 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could the code from yes
module be used here?
as it is, there is some danger that people set this to 0 or 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if not:
os.environ.get("BORG_TESTONLY_WEAKEN_KDF", "0") == "1"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if not:
os.environ.get("BORG_TESTONLY_WEAKEN_KDF", "0") == "1"
Good idea! This have not occurred to me
src/borg/helpers/passphrase.py
Outdated
if os.environ.get("BORG_TESTONLY_WEAKEN_KDF") is not None: | ||
time_cost = 1 | ||
parallelism = 1 | ||
# 8 is the smallest value that aviods the "Memory cost is too small" exception |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did a full review again and found some minor stuff.
src/borg/testsuite/archiver.py
Outdated
|
||
def test_init_with_explicit_key_algorithm(self): | ||
"""https://github.com/borgbackup/borg/issues/747#issuecomment-1076160401""" | ||
self.cmd('init', '--encryption=repokey', '--key-algorithm', 'pbkdf2', self.repository_location) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just for consistency: can you please give --key-algorithm
in the same way as --encryption
?
please fix all places with this.
src/borg/testsuite/archiver.py
Outdated
def test_change_passphrase_does_not_change_algorithm(self): | ||
self.cmd('init', '--encryption=repokey', '--key-algorithm', 'argon2', self.repository_location) | ||
os.environ['BORG_NEW_PASSPHRASE'] = 'newpassphrase' | ||
|
||
self.cmd('key', 'change-passphrase', self.repository_location) | ||
|
||
with Repository(self.repository_path) as repository: | ||
key = msgpack.unpackb(a2b_base64(repository.load_key())) | ||
assert key[b'algorithm'] == b'argon2 aes256-ctr hmac-sha256' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, guess this test should start from pbkdf2 key, because:
- this is what people usually have now
- we would change algorithm pbkdf2 -> argon2 (but not argon2 -> pbkdf2) if we would do automated upgrades on change passphrase. so if you want to make sure here that we do not upgrade, you need to use the constellation that would actually get upgraded.
src/borg/testsuite/archiver.py
Outdated
self.cmd('init', '--encryption=keyfile', '--key-algorithm', 'argon2', self.repository_location) | ||
|
||
self.cmd('key', 'change-location', self.repository_location, 'repokey') | ||
|
||
with Repository(self.repository_path) as repository: | ||
key = msgpack.unpackb(a2b_base64(repository.load_key())) | ||
assert key[b'algorithm'] == b'argon2 aes256-ctr hmac-sha256' | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here, needs to start from pbkdf2.
src/borg/testsuite/crypto.py
Outdated
'salt': b'salt'*4, | ||
'argon2_time_cost': 3, | ||
'argon2_memory_cost': 2**16, | ||
'argon2_parallelism': 4, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this does not seem to match params seen in 265.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
guess this can succeed because you are force-weakening the params in the tests even if the stored params disagree.
encrypted = msgpack.packb({ | ||
'version': 1, | ||
'algorithm': 'sha256', | ||
'iterations': 1, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but here the weakened params are stored in the key also, so this is not consistent procedure with the above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!
src/borg/testsuite/crypto.py
Outdated
@unittest.mock.patch('getpass.getpass') | ||
def test_repo_key_detect_does_not_raise_integrity_error(getpass, monkeypatch): | ||
"""https://github.com/borgbackup/borg/pull/6469#discussion_r832670411""" | ||
repository = MagicMock(id=b'repository_id') | ||
getpass.return_value = "hello, pass phrase" | ||
monkeypatch.setenv('BORG_DISPLAY_PASSPHRASE', 'no') | ||
RepoKey.create(repository, args=MagicMock(key_algorithm='argon2')) | ||
repository.load_key.return_value = repository.save_key.call_args.args[0] | ||
|
||
# 1. detect() tries an empty passphrase first before prompting the user | ||
# 2. load() was throwing integrity errors instead of returning None due to a bug |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you check this test again? it somehow unclear what it is doing / what it is checking. Also the comment is not very clear as it refers to functions you are not calling (directly).
@ThomasWaldmann I have addressed your comments. |
Thanks, merged! |
Note: this PR is not yet complete. I am submitting it to try running CIThis is the second part of #747
~~ - [x] Add code in decrypt_key_file necessary to be able to decrypt v2 keys ~~
~~ - [x] Implement encryption of keys in the new format ~~
See new TODO
This PR includes #6468