On 18/12/2014 04:24, jr conlin wrote:
On face, it looks reasonable. I'd introduce a bit of extra nonce to the
key generation sequence (otherwise it's conceivable that the master
could be derived from enough samples as well as it being possible to
predict keys).

HKDF *should* be sufficient protection here.

Importantly, we need the derived keys to be stable - if the same relier does the OAuth dance with the same user, it should get the same kAr and kBr. So this limits the amount of salting/noncing we can do.

Somewhat simplifying things, I understood one of the previous
conversations about this as folks wanting a "shared" key pair (For
services/reliers/ any 3rd party). This key pair would be somewhat
ephemeral, and could be changed whenever the basis for the key changes

I guess this is the same concept that Tarek was discussing in his response. My hope is that this core key infra will give us enough functionality to build that as a separate oauth-mediated service.

* Where is kB actually generated? Is this done on Mozilla controlled
servers or is it generated and stored elsewhere? How is this data
stored, indexed and retrieved?

It's...complicated:

  https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol

When you create an account on the FxA server, we generate two random 32-byte values, kA and wrap(wrap(Kb)), and store them in your account record.

When you log in to FxA, you fetch these values and do some client-side juggling with your password to unwrap the final value of kB. This is the magic sauce that keeps kB private from our servers.

* You note that the response for the OAuth Dance is a block that
contains both a key derived from the fn( fn(user_kA, relier_id),
submitted_pubKey).

It's a key derived from (user_kA, relier_id) and encrypted with submitted_pubKey. Saying "derived from submitted_pubKey" is technically accurate I guess, but I don't think it's the right way to conceive it.

Why not combine user_kApub & submitted_pubKey as a
shared secret and use more traditional RSA type encryption? I know that
encryption is scary as hell, but there are a LOT of reliable libraries
and it might be prudent to stick to known patterns rather than
concocting new ones.

Yes, absolutely we would need to find a good well-known pattern here and stick to it. I'm not committed to any particular details beyond "use the submitted pubkey to keep the derived keys secret from the server at this step".



  Ryan




On 2014/12/17 2:44 AM, Ryan Kelly wrote:

Hi All,


A limitation of the current FxA OAuth flow is that OAuth reliers
cannot get access to the user's encryption keys.

This means that we could not have build new-sync atop our OAuth
infrastructure.  I've also heard from at least two potential new
services that would like to do encryption of user data.

Let's try to figure out a way to enable this.

Below is a concrete proposal based on discussions I've had with a few
folks.  It's intended as a starting point for discussion.  I don't
think we can realistically ship anything like this before Q2, but it
would be good to reach a rough consensus on approach so that people
can starting hacking around with the ideas.

It's pretty long, so I recommend stopping here if you're not
interested OAuth or encryption keys...



A brief recap of the current situation:
=======================================

Your Firefox Account comes with a pair of encryption keys, "kA" and
"kB".  The first is known to the server and designed for encrypting
data that must be recoverable even after a password reset.  The second
is derived from the user's password and hence secret even from
Mozilla, but will not survive a password reset event.

Sync currently encrypts your stored data using a derivative of kB.

To obtain these keys, you must know the user's account password and
you must perform some special extra steps at login time.  There is no
way to fetch either key without having the account password.

Since OAuth reliers never get to see the user's password, there is
currently no way for them to take advantage of these keys.

Starting premises:

   * OAuth reliers should be able to obtain encryption keys.

   * OAuth reliers must not be able to access the raw values of kA or kB,
     but may obtain derivatives of them with user consent.

   * FxA servers must never learn kB or anything derived from it.

   * OAuth relier servers should be able to avoid learning anything
     derived from kB (although we cannot prevent them from deliberately
     sending the keys from client to server).

   * And of course, OAuth reliers must never have access to the user's
     account password, or anything that can be used to guess it.


Any amendments or additions to this list?



A proposal for scoped encryption keys:
======================================

There will be two different types of derived encryption key in our
service ecosystem.


1) Relier-specific keys

Each OAuth relier should be able to obtain a pair of derived keys
kAr/kBr that are private to that relier.

   kAr = HKDF(kA, "identity.mozilla.com/picl/v1/keys/relier/<client-id>")
   kBr = HKDF(kB, "identity.mozilla.com/picl/v1/keys/relier/<client-id>")

The use-case here is an external service that wants to do encryption
of its own private data, such as an FxA/daybed.io integration.


2) Service-specific keys

Each OAuth service provider should be able to have a derived key that
is specific to that service, and is provided to any relier that is
granted access to the service.

As a concrete example, suppose that the readinglist service wants to
encrypt some fields in the data stored on its server.  As a matter of
policy, we decide that  we can tolerate loss of such data in the event
of a password reset, so we define the key for scope "readinglist" as:

   kS = HKDF(kB, "identity.mozilla.com/picl/v1/keys/service/readinglist")

Any relier that is granted access to scope "readinglist" should also
be able to get this derived key, in order to properly access the data
stored in the service.


These two types completely define the encryption keys available to a
relier.  During a key-enabled oauth flow they should somehow receive:

   * the relier-specific kAr and kBr
   * any scope-specific keys defined for the scopes they are granted


Any use-cases not covered by one of these two types of derived key?



A key-fetching OAuth dance:
===========================

The real trick, of course, is safely deriving these keys and
delivering them to the relier.  I posit that this must be done as part
of the OAuth dance.  The content-server is the only thing that can
prompt the user for their password in order to derive the keys, and
the content-server only gets involved during the OAuth dance.

We have a couple of options here, but I'll sketch out the one I like
best (large chunks of which are thanks to Alexis):


1) When initiating the OAuth dance, the relier generates a
public/private keypair, stores the private key in its application
state, and includes the public key in the OAuth authorization request:

   GET /v1/authorization?client_id=AAA&scope=readinglist&keyfetch=PUBKEY


2) We're redirected to the content-server OAuth page, which does its
usual solicitation of user permission, prompts for the user's password
if necessary to obtain kA and kB, and derives the appropriate scoped
encryption keys.


3) The content-server encrypts the derived keys using the supplied
pubkey and includes them when provisioning the OAuth token:

   POST /v1/authorization
   ==>
   {
     "client_id": "AAA",
     "scope": "readinglist",
     "keys": {
       "relier": [encrypt(kAr, pubkey), encrypt(kBr, pubkey)],
       "readinglist": encrypt(kS, pubkey)
     }
   }
   <==
   {
     "code": "XXX"
   }


4) We're redirected back to the relier's registered redirect_uri,
where they retrieve the granted token and encrypted encryption keys:


   POST /v1/token
   ==>
   {
     "code": "XXX"
   }
   <==
   {
     "access_token": "blahblah",
     "token_type": "bearer",
     "scope": "readinglist",
     "keys" {
       "relier": [encrypt(kAr, pubkey), encrypt(kBr, pubkey)],
       "readinglist": encrypt(kS, pubkey)
     }
   }

The relier can use its private key to decrypt the encryption keys and
use them as it sees fit.


5) Profit.


Points to note:

   * The FxA servers never see any of the keys, because they transit
     the server in encrypted form.

   * The relier can generate and manage its keyfetch keypair in
     client-side code, to avoid learning the keys server-side.

   * This flow does not depend on persistent state on the client, all
     state can be tunneled through URL params in the OAuth flow.

   * There's a whole lot of potential for crypto to be done wrong
     here!  It's pretty scary stuff and smells kinda fragile.


I'd love to avoid public-key crypto here...perhaps there's a way for
the content-server to generate a symmetric encryption secret and
communicate it directly back to the relier without it transiting our
servers?

Chris also suggested that the encryption keys may not need to transit
the server at all, but could instead be communicated from
content-server to relier via a client-side postMessage API.  I don't
know much about postMessage but it sounds worth exploring.

Details aside, will this sort of enhanced OAuth dance give us what we
want?

Does this sound appropriately terrifying to everyone?



  Cheers,

     Ryan

_______________________________________________
Dev-fxacct mailing list
[email protected]
https://mail.mozilla.org/listinfo/dev-fxacct

_______________________________________________
Dev-fxacct mailing list
[email protected]
https://mail.mozilla.org/listinfo/dev-fxacct

_______________________________________________
Dev-fxacct mailing list
[email protected]
https://mail.mozilla.org/listinfo/dev-fxacct

Reply via email to