Esempio n. 1
0
Status
Login::rootKeyUpgrade()
{
    // Create a BIP39 mnemonic, and use it to derive the rootKey:
    DataChunk entropy;
    ABC_CHECK(randomData(entropy, 256/8));
    auto mnemonic = bc::create_mnemonic(entropy, bc::language::en);
    auto rootKeyRaw = bc::decode_mnemonic(mnemonic);
    rootKey_ = DataChunk(rootKeyRaw.begin(), rootKeyRaw.end());

    // Pack the keys into various boxes:
    JsonBox rootKeyBox;
    ABC_CHECK(rootKeyBox.encrypt(rootKey_, dataKey_));
    JsonBox mnemonicBox, dataKeyBox;
    auto infoKey = bc::hmac_sha256_hash(rootKey_, DataSlice(infoKeyHmacKey));
    ABC_CHECK(mnemonicBox.encrypt(bc::join(mnemonic), infoKey));
    ABC_CHECK(dataKeyBox.encrypt(dataKey_, infoKey));

    // Upgrade the account on the server:
    ABC_CHECK(loginServerAccountUpgrade(*this,
                                        rootKeyBox, mnemonicBox, dataKeyBox));
    ABC_CHECK(rootKeyBox.save(paths.rootKeyPath()));

    return Status();
}
Esempio n. 2
0
Status
Login::loadOffline()
{
    ABC_CHECK(store.paths(paths, true));

    LoginPackage loginPackage;
    ABC_CHECK(loginPackage.load(paths.loginPackagePath()));
    ABC_CHECK(loginPackage.passwordAuthBox().decrypt(passwordAuth_, dataKey_));

    // Look for an existing rootKeyBox:
    JsonBox rootKeyBox;
    if (fileExists(paths.rootKeyPath()))
    {
        ABC_CHECK(rootKeyBox.load(paths.rootKeyPath()));
    }
    else
    {
        // Try asking the server:
        AuthJson authJson;
        LoginReplyJson loginJson;
        ABC_CHECK(authJson.loginSet(*this));
        ABC_CHECK(loginServerLogin(loginJson, authJson));
        ABC_CHECK(loginJson.save(*this));
        rootKeyBox = loginJson.rootKeyBox();
    }
    // Otherwise, there just isn't one.

    // Extract the rootKey:
    if (rootKeyBox.ok())
        ABC_CHECK(rootKeyBox.decrypt(rootKey_, dataKey_));
    else
        ABC_CHECK(rootKeyUpgrade());

    return Status();
}
Esempio n. 3
0
Status
loginRecoverySet(Login &login,
                 const std::string &recoveryQuestions,
                 const std::string &recoveryAnswers)
{
    std::string LRA = login.lobby.username() + recoveryAnswers;

    // Load the packages:
    CarePackage carePackage;
    LoginPackage loginPackage;
    ABC_CHECK(carePackage.load(login.paths.carePackagePath()));
    ABC_CHECK(loginPackage.load(login.paths.loginPackagePath()));

    // Load the old keys:
    DataChunk authKey = login.authKey();

    // Update scrypt parameters:
    JsonSnrp snrp;
    ABC_CHECK(snrp.create());
    ABC_CHECK(carePackage.snrp3Set(snrp));
    ABC_CHECK(snrp.create());
    ABC_CHECK(carePackage.snrp4Set(snrp));

    // Make questionKey (unlocks questions):
    DataChunk questionKey;
    ABC_CHECK(carePackage.snrp4().hash(questionKey, login.lobby.username()));

    // Encrypt the questions:
    JsonBox box;
    ABC_CHECK(box.encrypt(recoveryQuestions, questionKey));
    ABC_CHECK(carePackage.questionBoxSet(box));

    // Make recoveryKey (unlocks dataKey):
    DataChunk recoveryKey;
    ABC_CHECK(carePackage.snrp3().hash(recoveryKey, LRA));

    // Encrypt dataKey:
    ABC_CHECK(box.encrypt(login.dataKey(), recoveryKey));
    ABC_CHECK(loginPackage.recoveryBoxSet(box));

    // Make recoveryAuthKey (unlocks the server):
    DataChunk recoveryAuthKey;
    ABC_CHECK(usernameSnrp().hash(recoveryAuthKey, LRA));

    // Encrypt recoveryAuthKey (needed for atomic password updates):
    ABC_CHECK(box.encrypt(recoveryAuthKey, login.dataKey()));
    ABC_CHECK(loginPackage.ELRA1Set(box));

    // Change the server login:
    ABC_CHECK(loginServerChangePassword(login, authKey, recoveryAuthKey,
                                        carePackage, loginPackage));

    // Change the on-disk login:
    ABC_CHECK(carePackage.save(login.paths.carePackagePath()));
    ABC_CHECK(loginPackage.save(login.paths.loginPackagePath()));

    return Status();
}
Esempio n. 4
0
Status
Login::repoFind(JsonPtr &result, const std::string &type, bool create)
{
    result = JsonPtr();

    // Try 1: Use what we have:
    repoFindLocal(result, type).log(); // Failure is fine
    if (result) return Status();

    // Try 2: Sync with the server:
    ABC_CHECK(update());
    repoFindLocal(result, type).log(); // Failure is fine
    if (result) return Status();

    // Try 3: Make a new repo:
    if (create)
    {
        // Make the keys:
        DataChunk dataKey;
        DataChunk syncKey;
        ABC_CHECK(randomData(dataKey, DATA_KEY_LENGTH));
        ABC_CHECK(randomData(syncKey, SYNC_KEY_LENGTH));
        AccountRepoJson repoJson;
        ABC_CHECK(repoJson.syncKeySet(base64Encode(syncKey)));
        ABC_CHECK(repoJson.dataKeySet(base64Encode(dataKey_)));

        // Make the metadata:
        DataChunk id;
        ABC_CHECK(randomData(id, keyIdLength));
        KeyJson keyJson;
        ABC_CHECK(keyJson.idSet(base64Encode(id)));
        ABC_CHECK(keyJson.typeSet(type));
        ABC_CHECK(keyJson.keysSet(repoJson));

        // Encrypt the metadata:
        JsonBox keyBox;
        ABC_CHECK(keyBox.encrypt(keyJson.encode(), dataKey_));

        // Push the wallet to the server:
        AuthJson authJson;
        ABC_CHECK(authJson.loginSet(*this));
        ABC_CHECK(loginServerKeyAdd(authJson, keyBox, base16Encode(syncKey)));

        // Save to disk:
        LoginStashJson stashJson;
        ABC_CHECK(stashJson.load(paths.stashPath()));
        if (!stashJson.keyBoxes().ok())
            ABC_CHECK(stashJson.keyBoxesSet(JsonArray()));
        ABC_CHECK(stashJson.keyBoxes().append(keyBox));
        ABC_CHECK(stashJson.save(paths.stashPath()));

        result = repoJson;
        return Status();
    }

    return ABC_ERROR(ABC_CC_AccountDoesNotExist, "No such repo");
}
Esempio n. 5
0
Status
loginRecoverySet(Login &login,
                 const std::string &recoveryQuestions,
                 const std::string &recoveryAnswers)
{
    std::string LRA = login.store.username() + recoveryAnswers;

    // Load the packages:
    CarePackage carePackage;
    LoginPackage loginPackage;
    ABC_CHECK(carePackage.load(login.paths.carePackagePath()));
    ABC_CHECK(loginPackage.load(login.paths.loginPackagePath()));

    // Load the old keys:
    DataChunk passwordAuth = login.passwordAuth();

    // Update scrypt parameters:
    JsonSnrp snrp;
    ABC_CHECK(snrp.create());
    ABC_CHECK(carePackage.recoveryKeySnrpSet(snrp));
    ABC_CHECK(snrp.create());
    ABC_CHECK(carePackage.questionKeySnrpSet(snrp));

    // Make questionKey (unlocks questions):
    DataChunk questionKey;
    ABC_CHECK(carePackage.questionKeySnrp().hash(questionKey,
              login.store.username()));

    // Encrypt the questions:
    JsonBox questionBox;
    ABC_CHECK(questionBox.encrypt(recoveryQuestions, questionKey));
    ABC_CHECK(carePackage.questionBoxSet(questionBox));

    // Make recoveryKey (unlocks dataKey):
    DataChunk recoveryKey;
    ABC_CHECK(carePackage.recoveryKeySnrp().hash(recoveryKey, LRA));

    // Encrypt dataKey:
    JsonBox recoveryBox;
    ABC_CHECK(recoveryBox.encrypt(login.dataKey(), recoveryKey));
    ABC_CHECK(loginPackage.recoveryBoxSet(recoveryBox));

    // Make recoveryAuth (unlocks the server):
    DataChunk recoveryAuth;
    ABC_CHECK(usernameSnrp().hash(recoveryAuth, LRA));

    // Change the server login:
    ABC_CHECK(loginServerChangePassword(login, passwordAuth, recoveryAuth,
                                        carePackage, loginPackage));

    // Change the on-disk login:
    ABC_CHECK(carePackage.save(login.paths.carePackagePath()));
    ABC_CHECK(loginPackage.save(login.paths.loginPackagePath()));

    return Status();
}
Esempio n. 6
0
Status
Login::createNew(const char *password)
{
    LoginPackage loginPackage;
    JsonSnrp snrp;

    // Set up care package:
    CarePackage carePackage;
    ABC_CHECK(snrp.create());
    ABC_CHECK(carePackage.passwordKeySnrpSet(snrp));

    // Set up syncKey:
    DataChunk syncKey;
    JsonBox syncKeyBox;
    ABC_CHECK(randomData(syncKey, SYNC_KEY_LENGTH));
    ABC_CHECK(syncKeyBox.encrypt(syncKey, dataKey_));
    ABC_CHECK(loginPackage.syncKeyBoxSet(syncKeyBox));

    // Set up passwordAuth (LP1):
    if (password)
    {
        std::string LP = store.username() + password;

        // Generate passwordAuth:
        ABC_CHECK(usernameSnrp().hash(passwordAuth_, LP));

        // We have a password, so use it to encrypt dataKey:
        DataChunk passwordKey;
        JsonBox passwordBox;
        ABC_CHECK(carePackage.passwordKeySnrp().hash(passwordKey, LP));
        ABC_CHECK(passwordBox.encrypt(dataKey_, passwordKey));
        ABC_CHECK(loginPackage.passwordBoxSet(passwordBox));
    }
    else
    {
        // Generate passwordAuth:
        ABC_CHECK(randomData(passwordAuth_, scryptDefaultSize));
    }
    JsonBox passwordAuthBox;
    ABC_CHECK(passwordAuthBox.encrypt(passwordAuth_, dataKey_));
    ABC_CHECK(loginPackage.passwordAuthBoxSet(passwordAuthBox));

    // Create the account and repo on server:
    ABC_CHECK(loginServerCreate(store, passwordAuth_,
                                carePackage, loginPackage, base16Encode(syncKey)));

    // Set up the on-disk login:
    ABC_CHECK(store.paths(paths, true));
    ABC_CHECK(carePackage.save(paths.carePackagePath()));
    ABC_CHECK(loginPackage.save(paths.loginPackagePath()));
    ABC_CHECK(rootKeyUpgrade());

    // Save the bare minimum needed to access the Airbitz account:
    LoginStashJson stashJson;
    ABC_CHECK(stashJson.loginIdSet(base64Encode(store.userId())));
    ABC_CHECK(stashJson.syncKeyBoxSet(syncKeyBox));
    stashJson.save(paths.stashPath());

    // Latch the account:
    ABC_CHECK(loginServerActivate(*this));

    return Status();
}
Esempio n. 7
0
Status
Login::makeEdgeLogin(JsonPtr &result, const std::string &appId,
                     const std::string &pin)
{
    result = JsonPtr();

    // Try 1: Use what we have:
    makeEdgeLoginLocal(result, appId).log(); // Failure is fine
    if (result) return Status();

    // Try 2: Sync with the server:
    ABC_CHECK(update());
    makeEdgeLoginLocal(result, appId).log(); // Failure is fine
    if (result) return Status();

    // Try 3: Make a new login:
    {
        DataChunk loginKey;
        ABC_CHECK(randomData(loginKey, DATA_KEY_LENGTH));
        JsonBox parentBox;
        ABC_CHECK(parentBox.encrypt(loginKey, dataKey_));

        // Make the access credentials:
        DataChunk loginId;
        DataChunk loginAuth;
        ABC_CHECK(randomData(loginId, scryptDefaultSize));
        ABC_CHECK(randomData(loginAuth, scryptDefaultSize));
        JsonBox loginAuthBox;
        ABC_CHECK(loginAuthBox.encrypt(loginAuth, loginKey));

        // Set up the outgoing Login object:
        LoginReplyJson server;
        ABC_CHECK(server.appIdSet(appId));
        ABC_CHECK(server.loginIdSet(base64Encode(loginId)));
        ABC_CHECK(server.loginAuthBoxSet(loginAuthBox));
        ABC_CHECK(server.parentBoxSet(parentBox));
        LoginStashJson stash = server.clone();
        ABC_CHECK(server.set("loginAuth", base64Encode(loginAuth)));

        // Set up the PIN, if we have one:
        if (pin.size())
        {
            DataChunk pin2Key;
            ABC_CHECK(randomData(pin2Key, 32));
            const auto pin2Id = hmacSha256(store.username(), pin2Key);
            const auto pin2Auth = hmacSha256(pin, pin2Key);

            // Create pin2Box:
            JsonBox pin2Box;
            ABC_CHECK(pin2Box.encrypt(loginKey, pin2Key));

            // Create pin2KeyBox:
            JsonBox pin2KeyBox;
            ABC_CHECK(pin2KeyBox.encrypt(pin2Key, loginKey));

            // Set up the server login:
            ABC_CHECK(server.set("pin2Id", base64Encode(pin2Id)));
            ABC_CHECK(server.set("pin2Auth", base64Encode(pin2Auth)));
            ABC_CHECK(server.pin2BoxSet(pin2Box));
            ABC_CHECK(server.pin2KeyBoxSet(pin2KeyBox));
            ABC_CHECK(stash.pin2KeySet(base64Encode(pin2Key)));
        }

        // Write to server:
        AuthJson authJson;
        ABC_CHECK(authJson.loginSet(*this));
        ABC_CHECK(loginServerCreateChildLogin(authJson, server));

        // Save to disk:
        LoginStashJson stashJson;
        ABC_CHECK(stashJson.load(paths.stashPath()));
        if (!stashJson.children().ok())
            ABC_CHECK(stashJson.childrenSet(JsonArray()));
        ABC_CHECK(stashJson.children().append(stash));
        ABC_CHECK(stashJson.save(paths.stashPath()));
    }

    ABC_CHECK(makeEdgeLoginLocal(result, appId).log());
    if (!result)
        return ABC_ERROR(ABC_CC_Error, "Empty edge login after creation.");
    return Status();
}