Live data from Hacker News

Private by Design: How We Built Firefox Sync

hacks.mozilla.org

171–180 of 180 posts

Re: Private by Design: How We Built Firefox Sync

#171
post #168

Earlier quoted context omitted.

Even though it's still not there, YT team is working towards standard compliance [0] [0] https://news.ycombinator.com/item?id=18053935

Its amazing how low the bar for quality is on major google websites. How did such a broken feature end up running on YouTube for a year?

Works on Chrome, Google doesn't care about anything else.

Re: Private by Design: How We Built Firefox Sync

#172

Earlier quoted context omitted.

How could this even possibly be mitigated, though? At some point you have to trust something , right? This argument could extend all the way down to your OS, chipset, whatever. Unless you've built every part of your machine from scratch it's always going to rely on trusting a component you know nothing about.

If you include this code in firefox itself, you can review the code and verify that the official firefox binaries use that code.

But what about the code you run to verify the binaries? You have to trust that. Or are you reviewing every bit by hand?

Re: Private by Design: How We Built Firefox Sync

#173

Earlier quoted context omitted.

How could this even possibly be mitigated, though? At some point you have to trust something , right? This argument could extend all the way down to your OS, chipset, whatever. Unless you've built every part of your machine from scratch it's always going to rely on trusting a component you know nothing about.

Trusting the binary is a one-time effort. Here you have to trust the JS everytime you authenticate.

But my point is you also have to trust that your underlying OS, or chipset, or even some other software on your computer doesn't have some way to thwart the entire effort. If the thing you're using the do your verification is itself compromised, then you're just as screwed.

Re: Private by Design: How We Built Firefox Sync

#174
post #40
post #38

Earlier quoted context omitted.

>We're currently rewriting it ...in Rust? (I kid, although, it's a serious quesiton)

.... Yes >_> https://github.com/mozilla/application-services

Lmfao! That's amazing! Did not expect this response, and it really is >60% Rust code!

Re: Private by Design: How We Built Firefox Sync

#176

I've never heard of HKDF before but it is really an elegant solution to this. My first guess on how to do this would have been something stupid like split the Authentication token in half and 0 pad it. But this would have significantly reduced the entropy available on both keys, reducing the search space on the authentication token and the encryption key making them much more brute force able. HKDF instead expands th…

From the RFC: "Its goal is to take some source of initial keying material and derive from it one or more cryptographically strong secret keys." In our case, the initial keying material is the output of PBKDF; and the two outputs we use are used as an encryption key and a bearer token (essentially a password but I call it an authentication token to avoid confusion with your actual password). There are less complicated…

Right what I'm confused about is that first bit, my understanding from the RFC is that the implementation should have look something like

    return pbkdf2.derive(password, email, PBKDF2_ROUNDS, STRETCHED_PASS_LENGTH_BYTES)
      .then((quickStretchedPW) => {
        result.quickStretchedPW = quickStretchedPW;
        // stretch to twice the length necessary
        return hkdf(quickStretchedPW, kw('generated'), HKDF_SALT, HKDF_LENGTH * 2)
          .then((generated) => {
            // split output into two cryptographically strong keys
            result.unwrapBkey = generated.slice(0, HKDF_LENGTH);
            result.authPW = generated.slice(HKDF_LENGTH);
          }
        );
      }
    )
but my read in pseudo code of what they end up doing is closer to this:

    hashed_password = hash(password, 'salt1')
    hashed_auth_tok = hash(hashed_password, 'salt2')
    hashed_unwrap_key = hash(hashed_password, 'salt3')
which seems secure because the server can't reverse hashed_unwrap_key to find hashed_password and thus shouldn't be able to calculate hashed_auth_tok. However the point of HKDF is to make multiple cryptographic keys while it looks like in practice we are just using it as a one way funciton.

Re: Private by Design: How We Built Firefox Sync

#177

If only Firefox Multi-Account Containers were a feature, not an addon. The fact that containers don't sync well is probably my biggest frustration with Firefox at the moment.

Containers are actually built-in into Firefox. The addon only manages the user interface.

Even if that is the case, they are effectively not.

The UI has serious holes, and containers do not work with Firefox sync.

I also haven't been able to find a way to clear the cache or history for a specific container.

At this point, containers are barely even useful. Putting the UI in an addon was an awful decision.

Re: Private by Design: How We Built Firefox Sync

#178

Earlier quoted context omitted.

From the RFC: "Its goal is to take some source of initial keying material and derive from it one or more cryptographically strong secret keys." In our case, the initial keying material is the output of PBKDF; and the two outputs we use are used as an encryption key and a bearer token (essentially a password but I call it an authentication token to avoid confusion with your actual password). There are less complicated…

Right what I'm confused about is that first bit, my understanding from the RFC is that the implementation should have look something like return pbkdf2.derive(password, email, PBKDF2_ROUNDS, STRETCHED_PASS_LENGTH_BYTES) .then((quickStretchedPW) => { result.quickStretchedPW = quickStretchedPW; // stretch to twice the length necessary return hkdf(quickStretchedPW, kw('generated'), HKDF_SALT, HKDF_LENGTH * 2) .then((gen…

Ah okay, I understand better.

The (second) pseudocode you have is right (the second two 'hash()' should be 'hkdf()', and the first should be 'pbkdf()'.)

The first is an alternate way to do it. But for cryptographic reasons that tend to be buried in formal proofs; you generally don't want to derive twice the keylength you need and then split for two keys. (Besides the necessity for formal proofs (as I understand it) - it's just easier to make an indexing mistake and reuse key material. One also becomes more vulnerable to a collision attack, although that might not make sense in this context it related to the formal proofs.) I will note that sometimes - especially in embedded spaces - you'll see people taking this shortcut in the name of speed or codesize.

Instead you want to fully derive two keys using separate HKDF calls with separate 'labels'. This provides strong domain separation for the keys.

But I'm mostly trying to provide with a pointer to what to read about to convince yourself. I'd start at https://crypto.stackexchange.com/search?q=domain+separation

If you find out we're doing something that still seems weird though, please send me an email!

Re: Private by Design: How We Built Firefox Sync

#180

Earlier quoted context omitted.

Right what I'm confused about is that first bit, my understanding from the RFC is that the implementation should have look something like return pbkdf2.derive(password, email, PBKDF2_ROUNDS, STRETCHED_PASS_LENGTH_BYTES) .then((quickStretchedPW) => { result.quickStretchedPW = quickStretchedPW; // stretch to twice the length necessary return hkdf(quickStretchedPW, kw('generated'), HKDF_SALT, HKDF_LENGTH * 2) .then((gen…

Ah okay, I understand better. The (second) pseudocode you have is right (the second two 'hash()' should be 'hkdf()', and the first should be 'pbkdf()'.) The first is an alternate way to do it. But for cryptographic reasons that tend to be buried in formal proofs; you generally don't want to derive twice the keylength you need and then split for two keys. (Besides the necessity for formal proofs (as I understand it) -…

got it, thanks for the response!
Post reply on HN