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 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(); }
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(); }
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"); }
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(); }
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(); }