Skip to content
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

Balloon implementation #232

Merged
merged 40 commits into from
Jan 5, 2022
Merged

Balloon implementation #232

merged 40 commits into from
Jan 5, 2022

Conversation

daxpedda
Copy link
Contributor

@daxpedda daxpedda commented Sep 2, 2021

See #1.

There are a lot of small open questions, but the really important ones that need to be answered are:

For reference:

Comment on lines 229 to 236
// block_t idx_block = ints_to_block(t, m, i)
// int other = to_int(hash(cnt++, salt, idx_block)) mod s_cost
digest.update(&cnt.to_le_bytes());
cnt += 1;
digest.update(salt);
digest.update(&u32::try_from(t).unwrap().to_le_bytes());
digest.update(&u32::try_from(m).unwrap().to_le_bytes());
digest.update(&i.to_le_bytes());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what ints_to_block is supposed to mean, the reference implementation does something with AES (I think) that I didn't understand: libballoon/hash_state.c#L171 -> libballoon/bitstream.c#L152 -> libballoon/bitstream.c#L134 -> libballoon/bitstream.c#L118 -> EVP_EncryptUpdate

I took some inspiration from a Python implementation instead.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also not an expert, but ints_to_block seems to just be for type safety than an actual cryptographic operation. Feeding directly into the hash seems fine

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright then.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to revisit this for compatibility reasons.
My thinking was that ints_to_block is supposed to be some functions that can take these integers and turn them into a "block" of bytes, which is done in the prototype implementations by encrypting them. As an alternative we could simply use the hash already in use to do that.

We could also possibly use SHA-1 here instead of the possibly more expensive hash used in D.

balloon/src/lib.rs Outdated Show resolved Hide resolved
/// - Default set of [`Params`] to be used
/// - (Optional) Secret key a.k.a. "pepper" to be used
#[derive(Clone, Default)]
pub struct Balloon<'key, D: Digest> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to store the hash algorithm to use in Balloon because I can't pass it into PasswordHasher otherwise.

I could also move it to Params, but I was unsure if I should store the hash algorithm in the PHC string and I don't know how to store it in the PHC string anyway, where do I get an ID or name for every hash algorithm from?

Another idea would be to make PasswordHasher always use SHA-256, or some other algorithm.

Copy link

@rozbb rozbb Sep 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A thought: the key size is fixed to the size of a block_t, which is the hash digest size. So instead of making Balloon depend on a 'key lifetime, you can just make the secret a GenericArray<u8, D::OutputSize> or something

Sorry that made no sense. I mistook the password for the key. What precisely would be the purpose of a "pepper" in this scheme? Honest question because I've never seen it used.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re the hasher: parameterizing by D and using a PhantomData is a pretty standard pattern

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What precisely would be the purpose of a "pepper" in this scheme? Honest question because I've never seen it used.

I thought the reason it was used in Argon2 was because it is considered good practice. See OWASP for example.

re the hasher: parameterizing by D and using a PhantomData is a pretty standard pattern

Yes, I'm familiar with it, I'm just uncertain because which hash algorithm is used is quiet important, considering Params isn't storing it, it's being left out of the PHC string too. In any case, as noted in Zulip, we might wanna remove the whole PasswordHasher anyway.

balloon/src/lib.rs Outdated Show resolved Hide resolved
balloon/src/lib.rs Outdated Show resolved Hide resolved
balloon/src/lib.rs Outdated Show resolved Hide resolved
balloon/src/lib.rs Outdated Show resolved Hide resolved
Comment on lines 280 to 371
algorithm: Ident::new("balloon"),
version: Some(1),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have a standard or spec, it's "just" a research paper, it never mentions PHC strings. I just took those from the reference implementation.

balloon/src/lib.rs Outdated Show resolved Hide resolved
Comment on lines +27 to +33
Error::MemoryTooLittle => "memory cost is too small",
Error::ThreadsTooFew => "not enough threads",
Error::ThreadsTooMany => "too many threads",
Error::TimeTooSmall => "time cost is too small",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could add more descriptive messages here. MemoryTooLittle could mention how much memory is actually needed. ThreadsTooMany could mention that you need the parallel crate feature to use threads. I don't know how to translate any of this to password_hash::Error though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added how much memory is needed in the documentation.

@daxpedda daxpedda marked this pull request as ready for review September 2, 2021 10:29
balloon/src/params.rs Outdated Show resolved Hide resolved
@rozbb
Copy link

rozbb commented Sep 2, 2021

Maybe some baseline questions to guide the discussion of whether to put this in this repo:

  1. Does there exist any attempt to standardize this algorithm
  2. Who uses this algorithm?
  3. What are the alternatives to this algorithm? What are their pros and cons?

@tarcieri
Copy link
Member

tarcieri commented Sep 2, 2021

So far we've been fairly inclusive of algorithms which have some standardization effort behind them. We don't exactly have a policy regarding who's allowed to describe standards.

This algorithm does seem a bit nonstandard in that regard.

@daxpedda
Copy link
Contributor Author

daxpedda commented Sep 2, 2021

  1. Does there exist any attempt to standardize this algorithm

Not as far as I know, and the research paper really leaves a lot to desire that is usually expected in a standard, which is to be expected.

  1. Who uses this algorithm?

I couldn't find anybody who does. The biggest motivation I found is that it is the only alternative mentioned by NIST to PBKDF2.

  1. What are the alternatives to this algorithm? What are their pros and cons?

Almost half of the research paper compares it to Argon2. I'm no cryptographer, but from what I could make out the biggest advantages are:

  • Simplicity
  • Formal verification (of it's memory-hardness capabilities?)
  • Speed

To me the biggest disadvantages are:

  • No standard
  • Nobody seems to use it, ergo no scrutiny and not battle-tested (hopefully we can change this)

@tarcieri
Copy link
Member

tarcieri commented Sep 2, 2021

If we can generate test vectors and demonstrate some sort of interoperability between implementations in multiple languages, I think we could potentially consider including it.

Curious what @newpavlov thinks.

@daxpedda
Copy link
Contributor Author

daxpedda commented Sep 4, 2021

If we can generate test vectors and demonstrate some sort of interoperability between implementations in multiple languages, I think we could potentially consider including it.

I tested a couple, none of them are compatible with each other as far as I could determine:

From what I could gather there are some minor differences in the implementation that keep things from being interoperable.

I currently started working on getting the prototype implementation into a Rust crate to test against: https://github.com/daxpedda/balloon-proto.
When working through the implementation, trying to figure out what the differences are exactly, I noticed that the prototype implementation significantly differs from the research paper. For example it hashes some things in a different order or adds completely new things to the hash. The mixing part of the the algorithm is the most confusing one, it really doesn't follow the research paper whatsoever, as far as I can tell.

Other implementations really followed the paper closely, I'm unsure what to do now, should I just try to reach compatibility with the prototype implementation, or should I try to follow the paper?

@tarcieri
Copy link
Member

tarcieri commented Sep 5, 2021

Wow, that's not a great situation. I'm not sure what to do if literally no two implementations interoperate.

Perhaps it would be good to open some issues on the respective repos and try to coordinate some form of interoperability.

@daxpedda
Copy link
Contributor Author

daxpedda commented Sep 5, 2021

I don't mind following up on that, but all of these implementations are years old by now, the prototype implementation is officially unsupported now: henrycg/balloon#5 (comment). The only recently active implementation is the Rust implementation: https://github.com/stichtingorganism/balloon.

@newpavlov
Copy link
Member

Lack of some kind of standardization, at the very least in the form of test vectors is certainly not great... I agree that it may be worth to try to coordinate some form of interoperability first.

I wonder if @henrycg can chime in and recommend whether we should treat the prototype code as a reference implementation. Also I wonder if @goldenMetteyya is open to sharing with us access to the balloon crate name on crates.io.

@henrycg
Copy link

henrycg commented Sep 6, 2021

Yes, I am happy to add some context here, even if it is a bit unsatisfying.

For background: The Balloon algorithm never ended up being standardized in an RFC or similar standards document. The goal in the research paper was just to give a high-level description of the algorithm, without concern for the low-level details of encoding, endianness, ordering of arguments, etc.

Our hope at the time of writing the paper was that someone would be interested enough in the algorithm to formalize it in an RFC or similar standard at the level of precision that one would need to write an interoperable implementation. Unfortunately, that never happened. While the prototype code gives one way to map the high-level algorithm into code, there are lots of other ways that are equally good, or perhaps even better.

People who have used the algorithm in practice tend to make the low-level implementation decisions (about endianness, encoding, ordering of arguments, etc.) themselves. Since there is no standard implementation and since the algorithm is not widely used anyways, interoperability is not a concern for most users.

I'm not sure if this answers your question exactly, but I hope that the background is helpful.

@daxpedda
Copy link
Contributor Author

daxpedda commented Sep 7, 2021

So I see three options:

  1. We follow the paper closely, which would allow us to potentially be compatible with other libraries out there
  2. We follow the prototype implementation, which would allow us to have some test vectors, but not really, because we will not use EVP_EncryptUpdate outside of testing
  3. We close the PR, considering there is no standard or test vectors, it might have been a bad idea to begin this

I'm leaning towards option 1.

@tarcieri
Copy link
Member

tarcieri commented Sep 8, 2021

Option 1 seems the best to me, but ideally producing a set of test vectors and ensuring interop with at least one other implementation, preferably implemented in something other than Rust

@daxpedda
Copy link
Contributor Author

I believe I addressed all remaining issues except the compatibility one. I will open a PR or issue at https://github.com/nachonavarro/balloon-hashing, which is the implementation with the most recent activity. If everything goes well I can add it to the tests.

balloon/Cargo.toml Outdated Show resolved Hide resolved
balloon/src/lib.rs Outdated Show resolved Hide resolved
@daxpedda
Copy link
Contributor Author

I'm now using https://github.com/daxpedda/nachonavarro-balloon, which uses https://github.com/nachonavarro/balloon-hashing, to test against a Python implementation. Compatibility was provided here: nachonavarro/balloon-hashing#2.

I added some test vectors that I generated. I believe I addressed all outstanding issues.

@daxpedda
Copy link
Contributor Author

daxpedda commented Dec 8, 2021

Re-based and updated to digest and sha2 0.10.

Copy link
Member

@newpavlov newpavlov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have several minor nits. I haven't looked deeply into implementation of the algorithm itself and I probably would've done some things a bit differently (e.g. I prefer free-standing functions, on top of which abstractions are built), but I think we can merge the crate after the nits will be fixed. Unless, of course, @tarcieri has something to add.

balloon/src/params.rs Outdated Show resolved Hide resolved
];

#[test]
fn test() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have it's worth to have a more descriptive test name, e.g. test_balloon_m.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I named it test_vectors, it already is in a module called balloon_m, what do you think?

balloon/tests/balloon.rs Outdated Show resolved Hide resolved
balloon/src/lib.rs Outdated Show resolved Hide resolved
balloon/tests/balloon_m.rs Outdated Show resolved Hide resolved
@daxpedda
Copy link
Contributor Author

daxpedda commented Dec 8, 2021

I prefer free-standing functions, on top of which abstractions are built

I could still do that, it sounds quiet attractive to me too.

@daxpedda
Copy link
Contributor Author

daxpedda commented Dec 8, 2021

I hope I addressed all outstanding issues. Please feel free to ask for any kind of improvement, even if it's just a preference.

@tarcieri tarcieri merged commit a4488e1 into RustCrypto:master Jan 5, 2022
@tarcieri
Copy link
Member

tarcieri commented Jan 5, 2022

Thank you!

@ecki
Copy link

ecki commented Jan 31, 2023

I am currently toying with a java impl, and i will try to make it compatible to this, thanks for the test vectors.

there are some open issues I tried to raise upstream, but maybe we can resolve it here?

  • mod sampling bias of the bitstream
  • Relative small size in sha256, go,sha512 or Blake?
  • Pepper? Usage label
  • Final (HMAC hashing).
  • PBKDF2 preseeding (low itewtion instead of init seed?
  • Salt guessing (underspecify salt so defender can half the tries
  • delta configurable
  • Xor mixing (more memory bw)
  • Algorithm Id in modular password hash syntax

Still interested to look into this or has the argon fraction won? ,)

@daxpedda
Copy link
Contributor Author

IANAC but as I am generally interested in this algorithm here are my 2 cents:

  • Relative small size in sha256, go,sha512 or Blake?

The balloon-hash crate here already supports any hash with the Digest trait.

  • Pepper? Usage label

Also supported. Never heard of a usage label in a password hash.

  • Final (HMAC hashing).

Does this belong in a password hash?

  • delta configurable

Does that achieve anything?

  • Xor mixing (more memory bw)

I thought we are doing that already?

  • mod sampling bias of the bitstream
  • PBKDF2 preseeding (low itewtion instead of init seed?
  • Salt guessing (underspecify salt so defender can half the tries
  • Algorithm Id in modular password hash syntax

No clue what these are.

Still interested to look into this or has the argon fraction won? ,)

Honestly if Balloon is as good as the research says it is I think it's sad that it was forgotten like this.
The only way I see Balloon can be salvaged is if somebody makes a serious standardization effort, at the IETF for example.

@ecki
Copy link

ecki commented Feb 1, 2023

  • Relative small size in sha256, go,sha512 or Blake?

The balloon-hash crate here already supports any hash with the Digest trait.

yes, But there is no reference impl and no (official?) test vectors, so it would be good if „community“ can agree on one alternate hash which is supported by all and does not have the blocksize problem of the AB16b paper (bw is worse than argon2).

  • Pepper? Usage label

Also supported. Never heard of a usage label in a password hash.

if you use it for KDF it might be relevant. Not sure how safe the idea of using it as a KDF would be though. For password hashes a „site distinguisher“ would be a Pepper with lower secrecy requirements. It would prevent reusing „found hashes“ (known from the md5 correlation attacks (shucking)). Not sure how important that is for modern/salted hashes, but still it does not cost much. Anyway, I will implement a pepper interface I balloon compatible to your rust api - if I can find it ,)

  • Final (HMAC hashing).

Does this belong in a password hash?

the balloon paper suggests a hash or HMAC as a final step. It is just a small extension after the xor step (or to replace the xor step). If you use a real HMAC in this step it would introduce a good place for a „NIST compliant“ peppering step.

  • delta configurable

Does that achieve anything?

it would add confusion, but more seriously it looks like it was a moving target in the beginning and the xx paper identified a much larger delta as a possibility to gain some protection back (in their proposed xor alternative).

  • Xor mixing (more memory bw)

I thought we are doing that already?

Yes the parallel phase uses it already, but I meant in the mixing phase as discussed by AB16b. (But i know it comes with its own problems as the xor-block states could be used as a optimized way of storage. So that would require some more advanced crypto knowledge to design it it, I am curious if there is some interest for follow up research)

  • mod sampling bias of the bitstream

currently the mix function retrieves a 64Bit pseudorandom integer and reduces it to the neighbor blockindex with integer module, this introduces some bias as the block numbers are not power of two. This hohle be fixed but would require a new algorithm version. I also wonder if 32bit for Block Index would be enough.

  • PBKDF2 preseeding (with lower iterations) instead of init seed?

when hashing password and seed into the initial state with the HMAC iterations of PBKDF2 you have nearly no increase in complexity but an „approved“ function which would „firewall“ password from the relative uncommon and under-analysed balloon function.

  • Salt guessing (underspecify salt so defender can half the tries

It is a general trick to „forget“ some parts of the salt or randomize the iteration numbers. This forces the validator to try all combinations (for example 4 different 2 bit combinations of the salt). As a defender you normally can validate a correct password after half the attempts by chance, as a attacker you would have to try all 4 wrong attempts - effectively halving the attackers cracking advantage (with the cost of making the verification times a bit more unpredictable if not run in parallel)

  • Algorithm Id in modular password hash syntax

we should see which hash, which algorithm modifications, maybe which bitstream method, pre/posthashinng and random-module is used. For example we can define $balloon$v=1$a=0.. parameter (add it to the meta dat hashing if >0). 0 would be sha256 with aes-ctr-sha256, a=1 could be sha512 etc. (the bitstream fix could also be v=2).

another parameter would be good to name the pepper key for rotation, but that one most likely does not need standardization.

Honestly if Balloon is as good as the research says it is ..

i also like its construction simplicity, but regarding „as good as research“, there is not much (and it’s really weird NIST recommended it even without any production ready implementwtion) did you check the alwen,blocki 16 paper?

AB16b: https://eprint.iacr.org/2016/759

BTW if you or any reader/researcher also are on the balloon-hashing mailinglist, should we continue the discussion there?

@tarcieri
Copy link
Member

tarcieri commented Feb 1, 2023

BTW if you or any reader/researcher also are on the balloon-hashing mailinglist

TIL there's a mailing list. I guess it's this one? https://mailman.stanford.edu/mailman/listinfo/balloon-hashing

@daxpedda
Copy link
Contributor Author

daxpedda commented Feb 2, 2023

@ecki honestly most of this stuff goes way over my head, as I said, I am not a cryptographer.

I am happy to discuss or adapt the implementation, or add or provide more test vectors if you have any specific suggestions.

My only strong opinion on this whole matter is that if we really want to improve the current situation, some cryptographer(s) will have to get together and push for standardization at the IETF, like Argon2 or anything else really.

@samuel-lucas6
Copy link

Hi folks, I'm writing an Internet-Draft for Balloon so there's an actual specification and would like to ask for feedback on the current draft and input on the open Issues. Some ideas are breaking changes but would improve the algorithm. Another important aspect is recommending parameters, which requires proper benchmarking like what Steve Thomas has done. Any help would be greatly appreciated.

I reached out to Henry Corrigan-Gibbs, but he's busy with teaching/research commitments. I tried emailing some people on the PHC panel, but nobody replied. A few people don't have a public email address, and I thought there was no point emailing you @tarcieri when leaving this message here. I haven't yet tried contacting the authors of the papers looking at Balloon, but I'm not sure how successful that will be either given they are a few years old now and the algorithm has been modified.

I'm not expecting the Internet-Draft to become an RFC given Argon2's success, but the algorithm could still see some usage like XChaCha20.

@ecki
Copy link

ecki commented Jan 1, 2024

@samuel-lucas6 i would like an I-D, especially given NISTs premature recommendation of the algorithm. A incompatible variant would allow to fix some of the issues, so it’s a good starting point:

  • require UTF-8 encoding of password bytes and pre-hashing
  • Specify optional pepper opportunity (secret key)
  • Fix the (small) modulo bias for the RNG
  • Define a standard modular hash syntax (including a pepper Id)

do you also plan to address the scheduling weakness found in the follow-up paper? (This night however require some academic involvement?)

@samuel-lucas6
Copy link

@ecki

require UTF-8 encoding of password bytes and pre-hashing

Not sure what you mean by pre-hashing here. I originally specified that the password should be UTF-8 encoded, but I noticed that the Argon2 and scrypt RFCs don't mention this so removed it.

Specify optional pepper opportunity (secret key)

See this issue.

Fix the (small) modulo bias for the RNG

This hasn't been addressed. The current draft is compatible with existing interoperable implementations.

Define a standard modular hash syntax (including a pepper Id)

This has been attempted minus the pepper part, but it's quite messy because of Balloon vs Balloon-M. I would prefer if there was just one algorithm, as mentioned here.

do you also plan to address the scheduling weakness found in the follow-up paper? (This night however require some academic involvement?)

I haven't listed that down as I haven't tried to read all the third-party papers yet. That may not be feasible as the designers have moved on. I was planning on making minor design modifications at most as I'm not a cryptographer and no cryptographers seem to be that interested in Balloon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants