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(); }
Status loginServerChangePassword(const Login &login, DataSlice newLP1, DataSlice newLRA1, const CarePackage &carePackage, const LoginPackage &loginPackage) { const auto url = ABC_SERVER_ROOT "/account/password/update"; JsonPtr json(json_pack("{ss, ss, ss, ss, ss}", ABC_SERVER_JSON_L1_FIELD, base64Encode(login.lobby.authId()).c_str(), ABC_SERVER_JSON_LP1_FIELD, base64Encode(login.authKey()).c_str(), ABC_SERVER_JSON_NEW_LP1_FIELD, base64Encode(newLP1).c_str(), ABC_SERVER_JSON_CARE_PACKAGE_FIELD, carePackage.encode().c_str(), ABC_SERVER_JSON_LOGIN_PACKAGE_FIELD, loginPackage.encode().c_str())); if (newLRA1.size()) { json_object_set_new(json.get(), ABC_SERVER_JSON_NEW_LRA1_FIELD, json_string(base64Encode(newLRA1).c_str())); } HttpReply reply; ABC_CHECK(AirbitzRequest().post(reply, url, json.encode())); ServerReplyJson replyJson; ABC_CHECK(replyJson.decode(reply.body)); ABC_CHECK(replyJson.ok()); return Status(); }
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(); }
/** * Changes the password for an account on the server. * * This function sends information to the server to change the password for an account. * Either the old LP1 or LRA1 can be used for authentication. * * @param oldLP1 Old password hash for the account (if this is empty, LRA1 is used instead) * @param LRA1 LRA1 for the account (used if oldP1 is empty) */ tABC_CC ABC_LoginServerChangePassword(const Lobby &lobby, tABC_U08Buf oldLP1, tABC_U08Buf newLP1, tABC_U08Buf newLRA1, const CarePackage &carePackage, const LoginPackage &loginPackage, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; HttpReply reply; std::string url = ABC_SERVER_ROOT "/" ABC_SERVER_CHANGE_PASSWORD_PATH; ServerReplyJson replyJson; char *szPost = NULL; std::string carePackageStr; std::string loginPackageStr; json_t *pJSON_OldLRA1 = NULL; json_t *pJSON_NewLRA1 = NULL; json_t *pJSON_Root = NULL; ABC_CHECK_NULL_BUF(oldLP1); ABC_CHECK_NULL_BUF(newLP1); ABC_CHECK_NEW(carePackage.encode(carePackageStr)); ABC_CHECK_NEW(loginPackage.encode(loginPackageStr)); // Encode those: pJSON_Root = json_pack("{ss, ss, ss, ss, ss}", ABC_SERVER_JSON_L1_FIELD, base64Encode(lobby.authId()).c_str(), ABC_SERVER_JSON_LP1_FIELD, base64Encode(oldLP1).c_str(), ABC_SERVER_JSON_NEW_LP1_FIELD, base64Encode(newLP1).c_str(), ABC_SERVER_JSON_CARE_PACKAGE_FIELD, carePackageStr.c_str(), ABC_SERVER_JSON_LOGIN_PACKAGE_FIELD, loginPackageStr.c_str()); // set up the recovery, if any: if (newLRA1.size()) { pJSON_NewLRA1 = json_string(base64Encode(newLRA1).c_str()); json_object_set(pJSON_Root, ABC_SERVER_JSON_NEW_LRA1_FIELD, pJSON_NewLRA1); } // create the post data szPost = ABC_UtilStringFromJSONObject(pJSON_Root, JSON_COMPACT); // send the command ABC_CHECK_NEW(AirbitzRequest().post(reply, url, szPost)); ABC_CHECK_NEW(replyJson.decode(reply.body)); ABC_CHECK_NEW(replyJson.ok()); exit: ABC_FREE_STR(szPost); if (pJSON_OldLRA1) json_decref(pJSON_OldLRA1); if (pJSON_NewLRA1) json_decref(pJSON_NewLRA1); if (pJSON_Root) json_decref(pJSON_Root); return cc; }
Status loginRecovery(std::shared_ptr<Login> &result, Lobby &lobby, const std::string &recoveryAnswers, AuthError &authError) { std::string LRA = lobby.username() + recoveryAnswers; // Get the CarePackage: CarePackage carePackage; ABC_CHECK(loginServerGetCarePackage(lobby, carePackage)); // Make recoveryAuthKey (unlocks the server): DataChunk recoveryAuthKey; ABC_CHECK(usernameSnrp().hash(recoveryAuthKey, LRA)); // Get the LoginPackage: LoginPackage loginPackage; JsonPtr rootKeyBox; ABC_CHECK(loginServerGetLoginPackage(lobby, U08Buf(), recoveryAuthKey, loginPackage, rootKeyBox, authError)); // Make recoveryKey (unlocks dataKey): DataChunk recoveryKey; ABC_CHECK(carePackage.snrp3().hash(recoveryKey, LRA)); // Decrypt dataKey (unlocks the account): DataChunk dataKey; ABC_CHECK(loginPackage.recoveryBox().decrypt(dataKey, recoveryKey)); // Create the Login object: std::shared_ptr<Login> out; ABC_CHECK(Login::create(out, lobby, dataKey, loginPackage, rootKeyBox, false)); // Set up the on-disk login: ABC_CHECK(carePackage.save(out->paths.carePackagePath())); ABC_CHECK(loginPackage.save(out->paths.loginPackagePath())); result = std::move(out); return Status(); }
Status loginRecoveryQuestions(std::string &result, Lobby &lobby) { // Load CarePackage: CarePackage carePackage; ABC_CHECK(loginServerGetCarePackage(lobby, carePackage)); // Verify that the questions exist: if (!carePackage.questionBox()) return ABC_ERROR(ABC_CC_NoRecoveryQuestions, "No recovery questions"); // Create questionKey (unlocks questions): DataChunk questionKey; ABC_CHECK(carePackage.snrp4().hash(questionKey, lobby.username())); // Decrypt: DataChunk questions; ABC_CHECK(carePackage.questionBox().decrypt(questions, questionKey)); result = toString(questions); return Status(); }
tABC_CC ABC_LoginServerGetCarePackage(const Lobby &lobby, CarePackage &result, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; std::string url = ABC_SERVER_ROOT "/" ABC_SERVER_GET_CARE_PACKAGE_PATH; char *szCarePackage = NULL; ABC_CHECK_RET(ABC_LoginServerGetString(lobby, U08Buf(), U08Buf(), url.c_str(), JSON_ACCT_CARE_PACKAGE, &szCarePackage, pError)); ABC_CHECK_NEW(result.decode(szCarePackage)); exit: ABC_FREE_STR(szCarePackage); return cc; }
/** * Creates an account on the server. * * This function sends information to the server to create an account. * If the account was created, ABC_CC_Ok is returned. * If the account already exists, ABC_CC_AccountAlreadyExists is returned. * * @param LP1 Password hash for the account */ tABC_CC ABC_LoginServerCreate(const Lobby &lobby, tABC_U08Buf LP1, const CarePackage &carePackage, const LoginPackage &loginPackage, const char *szRepoAcctKey, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; HttpReply reply; std::string url = ABC_SERVER_ROOT "/" ABC_SERVER_ACCOUNT_CREATE_PATH; ServerReplyJson replyJson; char *szPost = NULL; std::string carePackageStr; std::string loginPackageStr; json_t *pJSON_Root = NULL; ABC_CHECK_NULL_BUF(LP1); ABC_CHECK_NEW(carePackage.encode(carePackageStr)); ABC_CHECK_NEW(loginPackage.encode(loginPackageStr)); // create the post data pJSON_Root = json_pack("{ssssssssss}", ABC_SERVER_JSON_L1_FIELD, base64Encode(lobby.authId()).c_str(), ABC_SERVER_JSON_LP1_FIELD, base64Encode(LP1).c_str(), ABC_SERVER_JSON_CARE_PACKAGE_FIELD, carePackageStr.c_str(), ABC_SERVER_JSON_LOGIN_PACKAGE_FIELD, loginPackageStr.c_str(), ABC_SERVER_JSON_REPO_FIELD, szRepoAcctKey); szPost = ABC_UtilStringFromJSONObject(pJSON_Root, JSON_COMPACT); // send the command ABC_CHECK_NEW(AirbitzRequest().post(reply, url, szPost)); // decode the result ABC_CHECK_NEW(replyJson.decode(reply.body)); ABC_CHECK_NEW(replyJson.ok()); exit: ABC_FREE_STR(szPost); if (pJSON_Root) json_decref(pJSON_Root); return cc; }
Status loginServerCreate(const LoginStore &store, DataSlice LP1, const CarePackage &carePackage, const LoginPackage &loginPackage, const std::string &syncKey) { const auto url = ABC_SERVER_ROOT "/v1/account/create"; ServerRequestJson json; ABC_CHECK(json.setup(store)); ABC_CHECK(json.passwordAuthSet(base64Encode(LP1))); ABC_CHECK(json.set(ABC_SERVER_JSON_CARE_PACKAGE_FIELD, carePackage.encode())); ABC_CHECK(json.set(ABC_SERVER_JSON_LOGIN_PACKAGE_FIELD, loginPackage.encode())); ABC_CHECK(json.set(ABC_SERVER_JSON_REPO_FIELD, syncKey)); HttpReply reply; ABC_CHECK(AirbitzRequest().post(reply, url, json.encode())); ServerReplyJson replyJson; ABC_CHECK(replyJson.decode(reply)); return Status(); }
Status loginServerCreate(const Lobby &lobby, DataSlice LP1, const CarePackage &carePackage, const LoginPackage &loginPackage, const std::string &syncKey) { const auto url = ABC_SERVER_ROOT "/account/create"; JsonPtr json(json_pack("{ssssssssss}", ABC_SERVER_JSON_L1_FIELD, base64Encode(lobby.authId()).c_str(), ABC_SERVER_JSON_LP1_FIELD, base64Encode(LP1).c_str(), ABC_SERVER_JSON_CARE_PACKAGE_FIELD, carePackage.encode().c_str(), ABC_SERVER_JSON_LOGIN_PACKAGE_FIELD, loginPackage.encode().c_str(), ABC_SERVER_JSON_REPO_FIELD, syncKey.c_str())); HttpReply reply; ABC_CHECK(AirbitzRequest().post(reply, url, json.encode())); ServerReplyJson replyJson; ABC_CHECK(replyJson.decode(reply.body)); ABC_CHECK(replyJson.ok()); return Status(); }
Status loginServerChangePassword(const Login &login, DataSlice newLP1, DataSlice newLRA1, const CarePackage &carePackage, const LoginPackage &loginPackage) { const auto url = ABC_SERVER_ROOT "/v1/account/password/update"; ServerRequestJson json; ABC_CHECK(json.setup(login)); ABC_CHECK(json.set(ABC_SERVER_JSON_NEW_LP1_FIELD, base64Encode(newLP1))); ABC_CHECK(json.set(ABC_SERVER_JSON_CARE_PACKAGE_FIELD, carePackage.encode())); ABC_CHECK(json.set(ABC_SERVER_JSON_LOGIN_PACKAGE_FIELD, loginPackage.encode())); if (newLRA1.size()) { ABC_CHECK(json.set(ABC_SERVER_JSON_NEW_LRA1_FIELD, base64Encode(newLRA1))); } HttpReply reply; ABC_CHECK(AirbitzRequest().post(reply, url, json.encode())); ServerReplyJson replyJson; ABC_CHECK(replyJson.decode(reply)); return Status(); }
Status loginServerGetCarePackage(const Lobby &lobby, CarePackage &result) { const auto url = ABC_SERVER_ROOT "/account/carepackage/get"; ServerRequestJson json; ABC_CHECK(json.setup(lobby)); HttpReply reply; ABC_CHECK(AirbitzRequest().post(reply, url, json.encode())); ServerReplyJson replyJson; ABC_CHECK(replyJson.decode(reply.body)); ABC_CHECK(replyJson.ok()); struct ResultJson: public JsonObject { ABC_JSON_CONSTRUCTORS(ResultJson, JsonObject) ABC_JSON_STRING(package, "care_package", nullptr) } resultJson(replyJson.results()); ABC_CHECK(resultJson.packageOk()); ABC_CHECK(result.decode(resultJson.package())); return Status(); }
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(); }