public async Task <byte[]> ExecuteRest(string endpoint, byte[] payload) { var builder = new UriBuilder(Server ?? "keepersecurity.com") { Path = "/api/rest/", Scheme = "https", Port = 443 }; var uri = new Uri(builder.Uri, endpoint); var apiPayload = new ApiRequestPayload() { Payload = ByteString.CopyFrom(payload) }; var attempt = 0; while (attempt < 3) { attempt++; var request = WebRequest.Create(uri); request.ContentType = "application/octet-stream"; request.Method = "POST"; var encPayload = CryptoUtils.EncryptAesV2(apiPayload.ToByteArray(), transmissionKey); var encKey = CryptoUtils.EncryptRsa(transmissionKey, KeeperSettings.KeeperPublicKeys[ServerKeyId]); var apiRequest = new ApiRequest() { EncryptedTransmissionKey = ByteString.CopyFrom(encKey), PublicKeyId = ServerKeyId, Locale = Locale, EncryptedPayload = ByteString.CopyFrom(encPayload) }; using (var requestStream = request.GetRequestStream()) { var p = apiRequest.ToByteArray(); await requestStream.WriteAsync(p, 0, p.Length); } HttpWebResponse response; try { response = (HttpWebResponse)request.GetResponse(); } catch (WebException e) { response = (HttpWebResponse)e.Response; } if (response.StatusCode == HttpStatusCode.OK && response.ContentType == "application/octet-stream") { using (var ms = new MemoryStream()) using (var rss = response.GetResponseStream()) { await rss.CopyToAsync(ms); var bytes = ms.ToArray(); return(CryptoUtils.DecryptAesV2(bytes, transmissionKey)); } } if (response.ContentType == "application/json") { var serializer = new DataContractJsonSerializer(typeof(KeeperApiErrorResponse)); using (var rss = response.GetResponseStream()) { var keeperRs = serializer.ReadObject(rss) as KeeperApiErrorResponse; switch (keeperRs.Error) { case "key": if (KeeperSettings.KeeperPublicKeys.ContainsKey(keeperRs.KeyId)) { ServerKeyId = keeperRs.KeyId; continue; } break; case "region_redirect": throw new KeeperRegionRedirect(keeperRs.RegionHost); case "bad_request": throw new KeeperInvalidDeviceToken(); } throw new KeeperApiException(keeperRs.resultCode, keeperRs.message); } } throw new Exception("Keeper Api Http error: " + response.StatusCode); } throw new Exception("Keeper Api error"); }
public async Task Login(IUserConfiguration user = null) { var configuration = Api.Storage.Get(); user = await ResolveUserConfiguration(user, configuration); var username = user?.Username; var password = user?.Password; if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) { return; } var token = user.TwoFactorToken; var tokenType = "device_token"; string authHash = null; PreLoginResponse preLogin = null; while (true) { if (preLogin == null) { preLogin = await Api.GetPreLogin(username); authHash = null; } var authParams = preLogin.Salt[0]; int iterations = authParams.Iterations; byte[] salt = authParams.Salt_.ToByteArray(); if (authHash == null) { authHash = CryptoUtils.DeriveV1KeyHash(password, salt, iterations).Base64UrlEncode(); } var command = new LoginCommand(); command.username = username; command.authResponse = authHash; command.include = new[] { "keys", "settings", "enforcements", "is_enterprise_admin" }; command.twoFactorToken = token; command.twoFactorType = !string.IsNullOrEmpty(token) ? tokenType : null; command.deviceTokenExpiresInDays = !string.IsNullOrEmpty(token) && tokenType != "device_token" ? 9999 : (int?)null; var loginRs = await Api.ExecuteV2Command <LoginCommand, LoginResponse>(command); if (!loginRs.IsSuccess && loginRs.resultCode == "auth_failed") // invalid password { throw new Exception("Invalid user name or password"); } else { if (!string.IsNullOrEmpty(loginRs.deviceToken)) { token = loginRs.deviceToken; tokenType = "device_token"; } SessionToken = loginRs.sessionToken; Username = username; accountSettings = loginRs.accountSettings; if (loginRs.keys != null) { if (loginRs.keys.encryptedDataKey != null) { var key = CryptoUtils.DeriveKeyV2("data_key", password, salt, iterations); DataKey = CryptoUtils.DecryptAesV2(loginRs.keys.encryptedDataKey.Base64UrlDecode(), key); } else if (loginRs.keys.encryptionParams != null) { DataKey = CryptoUtils.DecryptEncryptionParams(password, loginRs.keys.encryptionParams.Base64UrlDecode()); } else { throw new Exception("Missing data key"); } if (loginRs.keys.encryptedPrivateKey != null) { privateKeyData = CryptoUtils.DecryptAesV1(loginRs.keys.encryptedPrivateKey.Base64UrlDecode(), DataKey); privateKey = null; } } if (loginRs.IsSuccess) { EncryptedPassword = CryptoUtils.EncryptAesV2(Encoding.UTF8.GetBytes(password), DataKey); TwoFactorToken = token; authResponse = authHash; IsEnterpriseAdmin = loginRs.isEnterpriseAdmin ?? false; enforcements = loginRs.enforcements; StoreConfigurationIfChanged(configuration); break; } switch (loginRs.resultCode) { case "need_totp": case "invalid_device_token": case "invalid_totp": token = await Ui.GetTwoFactorCode(); if (!string.IsNullOrEmpty(token)) { tokenType = "one_time"; continue; } break; case "auth_expired": await Ui.DisplayDialog(DialogType.Information, loginRs.message); password = await this.ChangeMasterPassword(iterations); if (!string.IsNullOrEmpty(password)) { preLogin = null; continue; } break; case "auth_expired_transfer": var shareAccountTo = loginRs.accountSettings.shareAccountTo; if (await Ui.DisplayDialog(DialogType.Confirmation, "Do you accept Account Transfer policy?")) { await this.ShareAccount(); continue; } break; } throw new KeeperApiException(loginRs.resultCode, loginRs.message); } } }