Exemplo n.º 1
0
 def test_password_stretching_and_key_derivation(self):
     # These are the test vectors from the onepw protocol document.
     email = "andr\[email protected]"
     self.assertEqual(
         email.encode("utf8"),
         dehexlify("""
         616e6472c3a94065 78616d706c652e6f 7267
     """))
     pwd = "p\xe4ssw\xf6rd"
     self.assertEqual(
         pwd.encode("utf8"),
         dehexlify("""
         70c3a4737377c3b6 7264
     """))
     qspwd = quick_stretch_password(email, pwd)
     self.assertEqual(
         qspwd,
         dehexlify("""
         e4e8889bd8bd61ad 6de6b95c059d56e7 b50dacdaf62bd846 44af7e2add84345d
     """))
     authpw = derive_key(qspwd, "authPW")
     self.assertEqual(
         authpw,
         dehexlify("""
         247b675ffb4c4631 0bc87e26d712153a be5e1c90ef00a478 4594f97ef54f2375
     """))
     ubkey = derive_key(qspwd, "unwrapBkey")
     self.assertEqual(
         ubkey,
         dehexlify("""
         de6a2648b78284fc b9ffa81ba9580330 9cfba7af583c01a8 a1a63e567234dd28
     """))
Exemplo n.º 2
0
def login(user):
    client = Client("https://api.accounts.firefox.com")
    session = client.login(user['email'], user['password'], keys=True)

    keyA,keyB = session.fetch_keys()

    info = b"identity.mozilla.com/picl/v1/oldsync"
    namespace = b"oldsync"
    keys = derive_key(secret=keyB, namespace=namespace, size=64)
    encryption_key = keys[0:32]
    hmac_key = keys[32:64]

    # TODO: Store this or a derived longer-lived token
    #       Causes a login event which causes an email
    # TODO: Should move to use OAuth which solves the long-term cred storage
    #       issue
    fxab = FxABrowserIDAuth(user['email'], user['password'], with_client_state=True)
    raw_resp = requests.get('https://token.services.mozilla.com/1.0/sync/1.5', auth=fxab)
    raw_resp.raise_for_status()
    hawk_resp = raw_resp.json()

    return {
        "hawk_resp": hawk_resp,
        "hawk_uid": hawk_resp['uid'],
        "hawk_hashalg": hawk_resp['hashalg'],
        "hawk_api_endpoint": hawk_resp['api_endpoint'],
        "hawk_duration": hawk_resp['duration'],
        "hawk_key": hawk_resp['key'],
        "hawk_hashed_fxa_uid": hawk_resp['hashed_fxa_uid'],
        "hawk_id": hawk_resp['id'],

        'encryption_key': encryption_key.hex(),
        'hmac_key': hmac_key.hex(),
    }
    def login(self,
              email,
              password=None,
              stretchpwd=None,
              keys=False,
              unblock_code=None):
        stretchpwd = self._get_stretched_password(email, password, stretchpwd)
        body = {
            "email": email,
            "authPW": hexstr(derive_key(stretchpwd, "authPW")),
        }
        url = "/account/login"
        if keys:
            url += "?keys=true"

        if unblock_code:
            body["unblockCode"] = unblock_code

        resp = self.apiclient.post(url, body)
        # XXX TODO: somehow sanity-check the schema on this endpoint
        return Session(
            client=self,
            email=email,
            stretchpwd=stretchpwd,
            uid=resp["uid"],
            token=resp["sessionToken"],
            key_fetch_token=resp.get("keyFetchToken"),
            verified=resp["verified"],
            verificationMethod=resp.get("verificationMethod"),
            auth_timestamp=resp["authAt"],
        )
Exemplo n.º 4
0
 def create_account(self, email, password=None, stretchpwd=None, **kwds):
     keys = kwds.pop("keys", False)
     stretchpwd = self._get_stretched_password(email, password, stretchpwd)
     body = {
         "email": email,
         "authPW": hexstr(derive_key(stretchpwd, "authPW")),
     }
     EXTRA_KEYS = ("service", "redirectTo", "resume", "preVerifyToken",
                   "preVerified")
     for extra in kwds:
         if extra in EXTRA_KEYS:
             body[extra] = kwds[extra]
         else:
             msg = "Unexpected keyword argument: {0}".format(extra)
             raise TypeError(msg)
     url = "/account/create"
     if keys:
         url += "?keys=true"
     resp = self.apiclient.post(url, body)
     # XXX TODO: somehow sanity-check the schema on this endpoint
     return Session(
         client=self,
         email=email,
         stretchpwd=stretchpwd,
         uid=resp["uid"],
         token=resp["sessionToken"],
         key_fetch_token=resp.get("keyFetchToken"),
         verified=False,
         auth_timestamp=resp["authAt"],
     )
Exemplo n.º 5
0
 def finish_password_change(self, token, stretchpwd, wrapkb):
     body = {
         "authPW": hexstr(derive_key(stretchpwd, "authPW")),
         "wrapKb": hexstr(wrapkb),
     }
     auth = HawkTokenAuth(token, "passwordChangeToken", self.apiclient)
     self.apiclient.post("/password/change/finish", body, auth=auth)
Exemplo n.º 6
0
    def login(self, email, password=None, stretchpwd=None, keys=False, unblock_code=None):
        stretchpwd = self._get_stretched_password(email, password, stretchpwd)
        body = {
            "email": email,
            "authPW": hexstr(derive_key(stretchpwd, "authPW")),
        }
        url = "/account/login"
        if keys:
            url += "?keys=true"

        if unblock_code:
            body["unblockCode"] = unblock_code

        resp = self.apiclient.post(url, body)
        # XXX TODO: somehow sanity-check the schema on this endpoint
        return Session(
            client=self,
            email=email,
            stretchpwd=stretchpwd,
            uid=resp["uid"],
            token=resp["sessionToken"],
            key_fetch_token=resp.get("keyFetchToken"),
            verified=resp["verified"],
            verificationMethod=resp.get("verificationMethod"),
            auth_timestamp=resp["authAt"],
        )
Exemplo n.º 7
0
 def finish_password_change(self, token, stretchpwd, wrapkb):
     body = {
         "authPW": hexstr(derive_key(stretchpwd, "authPW")),
         "wrapKb": hexstr(wrapkb),
     }
     auth = HawkTokenAuth(token, "passwordChangeToken", self.apiclient)
     self.apiclient.post("/password/change/finish", body, auth=auth)
Exemplo n.º 8
0
 def create_account(self, email, password=None, stretchpwd=None, **kwds):
     keys = kwds.pop("keys", False)
     stretchpwd = self._get_stretched_password(email, password, stretchpwd)
     body = {
         "email": email,
         "authPW": hexstr(derive_key(stretchpwd, "authPW")),
     }
     EXTRA_KEYS = ("service", "redirectTo", "resume", "preVerifyToken",
                   "preVerified")
     for extra in kwds:
         if extra in EXTRA_KEYS:
             body[extra] = kwds[extra]
         else:
             msg = "Unexpected keyword argument: {0}".format(extra)
             raise TypeError(msg)
     url = "/account/create"
     if keys:
         url += "?keys=true"
     resp = self.apiclient.post(url, body)
     # XXX TODO: somehow sanity-check the schema on this endpoint
     return Session(
         client=self,
         email=email,
         stretchpwd=stretchpwd,
         uid=resp["uid"],
         token=resp["sessionToken"],
         key_fetch_token=resp.get("keyFetchToken"),
         verified=False,
         auth_timestamp=resp["authAt"],
     )
Exemplo n.º 9
0
 def fetch_keys(self, key_fetch_token, stretchpwd):
     url = "/account/keys"
     auth = HawkTokenAuth(key_fetch_token, "keyFetchToken", self.apiclient)
     resp = self.apiclient.get(url, auth=auth)
     bundle = unhexlify(resp["bundle"])
     keys = auth.unbundle("account/keys", bundle)
     unwrap_key = derive_key(stretchpwd, "unwrapBkey")
     return (keys[:32], xor(keys[32:], unwrap_key))
Exemplo n.º 10
0
 def fetch_keys(self, key_fetch_token, stretchpwd):
     url = "/account/keys"
     auth = HawkTokenAuth(key_fetch_token, "keyFetchToken", self.apiclient)
     resp = self.apiclient.get(url, auth=auth)
     bundle = unhexlify(resp["bundle"])
     keys = auth.unbundle("account/keys", bundle)
     unwrap_key = derive_key(stretchpwd, "unwrapBkey")
     return (keys[:32], xor(keys[32:], unwrap_key))
Exemplo n.º 11
0
 def reset_account(self, email, token, password=None, stretchpwd=None):
     stretchpwd = self._get_stretched_password(email, password, stretchpwd)
     body = {
         "authPW": hexstr(derive_key(stretchpwd, "authPW")),
     }
     url = "/account/reset"
     auth = HawkTokenAuth(token, "accountResetToken", self.apiclient)
     self.apiclient.post(url, body, auth=auth)
Exemplo n.º 12
0
 def reset_account(self, email, token, password=None, stretchpwd=None):
     stretchpwd = self._get_stretched_password(email, password, stretchpwd)
     body = {
         "authPW": hexstr(derive_key(stretchpwd, "authPW")),
     }
     url = "/account/reset"
     auth = HawkTokenAuth(token, "accountResetToken", self.apiclient)
     self.apiclient.post(url, body, auth=auth)
Exemplo n.º 13
0
 def destroy_account(self, email, password=None, stretchpwd=None):
     stretchpwd = self._get_stretched_password(email, password, stretchpwd)
     body = {
         "email": email,
         "authPW": hexstr(derive_key(stretchpwd, "authPW")),
     }
     url = "/account/destroy"
     self.apiclient.post(url, body)
Exemplo n.º 14
0
 def destroy_account(self, email, password=None, stretchpwd=None):
     stretchpwd = self._get_stretched_password(email, password, stretchpwd)
     body = {
         "email": email,
         "authPW": hexstr(derive_key(stretchpwd, "authPW")),
     }
     url = "/account/destroy"
     self.apiclient.post(url, body)
Exemplo n.º 15
0
 def change_password(self, oldpwd, newpwd):
     stretched_oldpwd = quick_stretch_password(self.email, oldpwd)
     resp = self.start_password_change(stretched_oldpwd)
     keys = self.fetch_keys(resp["keyFetchToken"], stretched_oldpwd)
     token = resp["passwordChangeToken"]
     stretched_newpwd = quick_stretch_password(self.email, newpwd)
     new_wrapkb = xor(keys[1], derive_key(stretched_newpwd, "unwrapBkey"))
     self.finish_password_change(token, stretched_newpwd, new_wrapkb)
Exemplo n.º 16
0
 def change_password(self, oldpwd, newpwd):
     stretched_oldpwd = quick_stretch_password(self.email, oldpwd)
     resp = self.start_password_change(stretched_oldpwd)
     keys = self.fetch_keys(resp["keyFetchToken"], stretched_oldpwd)
     token = resp["passwordChangeToken"]
     stretched_newpwd = quick_stretch_password(self.email, newpwd)
     new_wrapkb = xor(keys[1], derive_key(stretched_newpwd, "unwrapBkey"))
     self.finish_password_change(token, stretched_newpwd, new_wrapkb)
Exemplo n.º 17
0
 def change_password(self, email, oldpwd=None, newpwd=None,
                     oldstretchpwd=None, newstretchpwd=None):
     oldstretchpwd = self._get_stretched_password(email, oldpwd,
                                                  oldstretchpwd)
     newstretchpwd = self._get_stretched_password(email, newpwd,
                                                  newstretchpwd)
     resp = self.start_password_change(email, oldstretchpwd)
     keys = self.fetch_keys(resp["keyFetchToken"], oldstretchpwd)
     token = resp["passwordChangeToken"]
     new_wrapkb = xor(keys[1], derive_key(newstretchpwd, "unwrapBkey"))
     self.finish_password_change(token, newstretchpwd, new_wrapkb)
Exemplo n.º 18
0
 def change_password(self, email, oldpwd=None, newpwd=None,
                     oldstretchpwd=None, newstretchpwd=None):
     oldstretchpwd = self._get_stretched_password(email, oldpwd,
                                                  oldstretchpwd)
     newstretchpwd = self._get_stretched_password(email, newpwd,
                                                  newstretchpwd)
     resp = self.start_password_change(email, oldstretchpwd)
     keys = self.fetch_keys(resp["keyFetchToken"], oldstretchpwd)
     token = resp["passwordChangeToken"]
     new_wrapkb = xor(keys[1], derive_key(newstretchpwd, "unwrapBkey"))
     self.finish_password_change(token, newstretchpwd, new_wrapkb)
Exemplo n.º 19
0
 def test_password_stretching_and_key_derivation(self):
     # These are the test vectors from the onepw protocol document.
     email = u"andr\[email protected]"
     self.assertEqual(email.encode("utf8"), dehexlify("""
         616e6472c3a94065 78616d706c652e6f 7267
     """))
     pwd = u"p\xe4ssw\xf6rd"
     self.assertEqual(pwd.encode("utf8"), dehexlify("""
         70c3a4737377c3b6 7264
     """))
     qspwd = quick_stretch_password(email, pwd)
     self.assertEqual(qspwd, dehexlify("""
         e4e8889bd8bd61ad 6de6b95c059d56e7 b50dacdaf62bd846 44af7e2add84345d
     """))
     authpw = derive_key(qspwd, "authPW")
     self.assertEqual(authpw, dehexlify("""
         247b675ffb4c4631 0bc87e26d712153a be5e1c90ef00a478 4594f97ef54f2375
     """))
     ubkey = derive_key(qspwd, "unwrapBkey")
     self.assertEqual(ubkey, dehexlify("""
         de6a2648b78284fc b9ffa81ba9580330 9cfba7af583c01a8 a1a63e567234dd28
     """))
Exemplo n.º 20
0
def get_encryption_key(client, login, password):
    keyB = __get_sync_key(login, password)

    # get_encryption key
    crypto_key = client.get_record(collection = 'crypto', record_id = 'keys')

    sync_key_bundle = derive_key(keyB, b'oldsync', 32 * 2)
    sync_key_bundle = KeyBundle(key = sync_key_bundle[:32],
                                hmac_key = sync_key_bundle[32:])

    data = decrypt_data(sync_key_bundle, crypto_key['payload'])

    encryption_key_bundle = KeyBundle(key = b64decode(data['default'][0]),
                                      hmac_key = b64decode(data['default'][1]))
    return encryption_key_bundle
Exemplo n.º 21
0
def login(user):
    """
    Logs a user into their Firefox account and returns tempoary credentials
    for use by AuthRequest.
    """
    # TODO: pull out the urls to be part of the config.
    client = Client("https://api.accounts.firefox.com")
    session = client.login(user['email'], user['password'], keys=True)

    keyA, keyB = session.fetch_keys()

    # Magic strings from the docs
    # https://moz-services-docs.readthedocs.io/en/latest/sync/storageformat5.html
    info = b"identity.mozilla.com/picl/v1/oldsync"
    namespace = b"oldsync"
    keys = derive_key(secret=keyB, namespace=namespace, size=64)
    encryption_key = keys[0:32]
    hmac_key = keys[32:64]

    # TODO: Store this or a derived longer-lived token
    #       Causes a login event which causes an email
    # TODO: Should move to use OAuth which solves the long-term cred storage
    #       issue
    fxab = FxABrowserIDAuth(user['email'],
                            user['password'],
                            with_client_state=True)
    raw_resp = requests.get('https://token.services.mozilla.com/1.0/sync/1.5',
                            auth=fxab)
    raw_resp.raise_for_status()
    hawk_resp = raw_resp.json()

    return {
        "hawk_resp": hawk_resp,
        "hawk_uid": hawk_resp['uid'],
        "hawk_hashalg": hawk_resp['hashalg'],
        "hawk_api_endpoint": hawk_resp['api_endpoint'],
        "hawk_duration": hawk_resp['duration'],
        "hawk_key": hawk_resp['key'],
        "hawk_hashed_fxa_uid": hawk_resp['hashed_fxa_uid'],
        "hawk_id": hawk_resp['id'],
        'encryption_key': encryption_key.hex(),
        'hmac_key': hmac_key.hex(),
    }
Exemplo n.º 22
0
 def fetch_keys(self, key_fetch_token=None, stretchpwd=None):
     # Use values from session construction, if not overridden.
     if key_fetch_token is None:
         key_fetch_token = self._key_fetch_token
         if key_fetch_token is None:
             # XXX TODO: what error?
             raise RuntimeError("missing key_fetch_token")
     if stretchpwd is None:
         stretchpwd = self._stretchpwd
         if stretchpwd is None:
             # XXX TODO: what error?
             raise RuntimeError("missing stretchpwd")
     # Fetch the keys, and clear cached values from session construction.
     url = "/v1/account/keys"
     auth = HawkTokenAuth(key_fetch_token, "keyFetchToken", self.apiclient)
     resp = self.apiclient.get(url, auth=auth)
     self._key_fetch_token = None
     self._stretchpwd = None
     # Decrypt kB using the stretchpwd.
     bundle = unhexlify(resp["bundle"])
     keys = auth.unbundle("account/keys", bundle)
     unwrap_key = derive_key(stretchpwd, "unwrapBkey")
     self.keys = (keys[:32], xor(keys[32:], unwrap_key))
     return self.keys
Exemplo n.º 23
0
 def fetch_keys(self, key_fetch_token=None, stretchpwd=None):
     # Use values from session construction, if not overridden.
     if key_fetch_token is None:
         key_fetch_token = self._key_fetch_token
         if key_fetch_token is None:
             # XXX TODO: what error?
             raise RuntimeError("missing key_fetch_token")
     if stretchpwd is None:
         stretchpwd = self._stretchpwd
         if stretchpwd is None:
             # XXX TODO: what error?
             raise RuntimeError("missing stretchpwd")
     # Fetch the keys, and clear cached values from session construction.
     url = "/v1/account/keys"
     auth = HawkTokenAuth(key_fetch_token, "keyFetchToken", self.apiclient)
     resp = self.apiclient.get(url, auth=auth)
     self._key_fetch_token = None
     self._stretchpwd = None
     # Decrypt kB using the stretchpwd.
     bundle = unhexlify(resp["bundle"])
     keys = auth.unbundle("account/keys", bundle)
     unwrap_key = derive_key(stretchpwd, "unwrapBkey")
     self.keys = (keys[:32], xor(keys[32:], unwrap_key))
     return self.keys
Exemplo n.º 24
0
hawk_resp = None
encryption_key = None
hmac_key = None

if 'hawk' not in config or 'fxa' not in config:
    user = config['user']

    client = Client("https://api.accounts.firefox.com")
    session = client.login(user['email'], user['password'], keys=True)

    keyA, keyB = session.fetch_keys()

    info = b"identity.mozilla.com/picl/v1/oldsync"
    namespace = b"oldsync"
    keys = derive_key(secret=keyB, namespace=namespace, size=64)
    encryption_key = keys[0:32]
    hmac_key = keys[32:64]

    # TODO: Store this or a derived longer-lived token
    #       Causes a login event which causes an email
    fxab = FxABrowserIDAuth(user['email'],
                            user['password'],
                            with_client_state=True)
    raw_resp = requests.get('https://token.services.mozilla.com/1.0/sync/1.5',
                            auth=fxab)
    raw_resp.raise_for_status()

    hawk_resp = raw_resp.json()
    config['hawk'] = hawk_resp
    config['fxa'] = {
Exemplo n.º 25
0
 def start_password_change(self, email, stretchpwd):
     body = {
         "email": email,
         "oldAuthPW": hexstr(derive_key(stretchpwd, "authPW")),
     }
     return self.apiclient.post("/password/change/start", body)
Exemplo n.º 26
0
 def start_password_change(self, email, stretchpwd):
     body = {
         "email": email,
         "oldAuthPW": hexstr(derive_key(stretchpwd, "authPW")),
     }
     return self.apiclient.post("/password/change/start", body)