internal static void SignOut(JsonHttpClient jsonHttp) { var response = jsonHttp.Put("v1/session/signout"); if (response.IntAt("success") != 1) { throw new ClientException(ClientException.FailureReason.RespondedWithError, "Failed to sign out"); } }
internal static Account[] GetVaultAccounts(string id, AesKey sessionKey, Keychain keychain, JsonHttpClient jsonHttp) { var response = GetEncryptedJson(string.Format("v1/vault/{0}/0/items", id), sessionKey, jsonHttp); return(response.At("items", new JArray()).Select(i => ParseAccount(i, keychain)).ToArray()); }
internal static void ReauthorizeDevice(ClientInfo clientInfo, JsonHttpClient jsonHttp) { var response = jsonHttp.Put(string.Format("v1/device/{0}/reauthorize", clientInfo.Uuid)); if (response.IntAt("success") != 1) { throw new ClientException(ClientException.FailureReason.RespondedWithError, string.Format("Failed to reauthorize the device '{0}'", clientInfo.Uuid)); } }
internal static JObject PostEncryptedJson(string endpoint, Dictionary <string, string> parameters, AesKey sessionKey, JsonHttpClient jsonHttp) { var payload = JsonConvert.SerializeObject(parameters); var encryptedPayload = sessionKey.Encrypt(payload.ToBytes()); var response = jsonHttp.Post(endpoint, encryptedPayload.ToDictionary()); return(Decrypt(response, sessionKey)); }
// // Internal // internal static byte[] Perform(BigInteger secretA, ClientInfo clientInfo, Session session, JsonHttpClient http) { var sharedA = ComputeSharedA(secretA); var sharedB = ExchangeAForB(sharedA, session, http); ValidateB(sharedB); return(ComputeKey(secretA, sharedA, sharedB, clientInfo, session)); }
internal static Vault[] GetVaults(JToken accountInfo, AesKey sessionKey, Keychain keychain, JsonHttpClient jsonHttp) { var accessibleVaults = new HashSet <string>(BuildListOfAccessibleVaults(accountInfo)); return(accountInfo.At("vaults") .Where(i => accessibleVaults.Contains(i.StringAt("uuid", ""))) .Select(i => GetVault(i, sessionKey, keychain, jsonHttp)) .ToArray()); }
internal static Vault GetVault(JToken json, AesKey sessionKey, Keychain keychain, JsonHttpClient jsonHttp) { var id = json.StringAt("uuid"); var attributes = Decrypt(json.At("encAttrs"), keychain); return(new Vault(id: id, name: attributes.StringAt("name", ""), description: attributes.StringAt("desc", ""), accounts: GetVaultAccounts(id, sessionKey, keychain, jsonHttp))); }
internal static JsonHttpClient MakeJsonClient(IHttpClient http, string baseUrl, string sessionId = null) { var jsonHttp = new JsonHttpClient(http, baseUrl); jsonHttp.Headers["X-AgileBits-Client"] = ClientId; if (sessionId != null) { jsonHttp.Headers["X-AgileBits-Session-ID"] = sessionId; } return(jsonHttp); }
internal static void RegisterDevice(ClientInfo clientInfo, JsonHttpClient jsonHttp) { var response = jsonHttp.Post("v1/device", new Dictionary <string, object> { { "uuid", clientInfo.Uuid }, { "clientName", ClientName }, { "clientVersion", ClientVersion }, }); if (response.IntAt("success") != 1) { throw new ClientException(ClientException.FailureReason.RespondedWithError, string.Format("Failed to register the device '{0}'", clientInfo.Uuid)); } }
internal static BigInteger ExchangeAForB(BigInteger sharedA, Session session, JsonHttpClient http) { var response = http.Post("v1/auth", new Dictionary <string, object> { { "sessionID", session.Id }, { "userA", sharedA.ToHex() } }); if (response.StringAt("sessionID") != session.Id) { throw ExceptionFactory.MakeInvalidOperation("SRP: session ID doesn't match"); } return(response.StringAt("userB").ToBigInt()); }
internal static Session StartNewSession(ClientInfo clientInfo, JsonHttpClient jsonHttp) { var response = jsonHttp.Get(string.Format("v2/auth/{0}/{1}/{2}/{3}", clientInfo.Username, clientInfo.AccountKey.Format, clientInfo.AccountKey.Uuid, clientInfo.Uuid)); var status = response.StringAt("status"); switch (status) { case "ok": var session = Session.Parse(response); if (session.KeyUuid != clientInfo.AccountKey.Uuid) { throw new ClientException(ClientException.FailureReason.IncorrectCredentials, "The account key is incorrect"); } return(session); case "device-not-registered": RegisterDevice(clientInfo, MakeJsonClient(jsonHttp, response.StringAt("sessionID"))); break; case "device-deleted": ReauthorizeDevice(clientInfo, MakeJsonClient(jsonHttp, response.StringAt("sessionID"))); break; default: throw new ClientException( ClientException.FailureReason.InvalidResponse, string.Format( "Failed to start a new session, unsupported response status '{0}'", status)); } return(StartNewSession(clientInfo, jsonHttp)); }
// Returns the session encryption key public static AesKey Perform(ClientInfo clientInfo, Session session, JsonHttpClient http) { var key = Perform(GenerateSecretA(), clientInfo, session, http); return(new AesKey(session.Id, key)); }
// // HTTP // internal static JObject GetEncryptedJson(string endpoint, AesKey sessionKey, JsonHttpClient jsonHttp) { return(Decrypt(jsonHttp.Get(endpoint), sessionKey)); }
internal static void VerifySessionKey(ClientInfo clientInfo, Session session, AesKey sessionKey, JsonHttpClient jsonHttp) { try { var response = PostEncryptedJson( "v2/auth/verify", new Dictionary <string, string> { { "sessionID", session.Id }, { "clientVerifyHash", Crypto.CalculateClientHash(clientInfo, session) }, { "client", ClientId }, }, sessionKey, jsonHttp); // Just to verify that it's a valid JSON and it has some keys. // Technically it should have failed by now either in decrypt or JSON parse response.StringAt("userUuid"); } catch (ClientException e) { // This is a quite ugly attempt at handling a very special case. // When this specific request fails with 400, the response contains // the error code. It seems 102 means invalid credentials. // TODO: Write a test for this case. if (e.Reason != ClientException.FailureReason.NetworkError) { throw; } var web = e.InnerException as WebException; if (web == null) { throw; } var response = web.Response as HttpWebResponse; if (response == null) { throw; } var stream = response.GetResponseStream(); if (stream == null) { throw; } stream.Position = 0; var text = new System.IO.StreamReader(stream).ReadToEnd(); var json = JObject.Parse(text); if (json.IntAt("errorCode", 0) == 102) { throw new ClientException(ClientException.FailureReason.IncorrectCredentials, "Username, password or account key is incorrect", e); } throw; } }
internal static JObject GetAccountInfo(AesKey sessionKey, JsonHttpClient jsonHttp) { return(GetEncryptedJson("v1/account?attrs=billing,counts,groups,invite,me,settings,tier,user-flags,users,vaults", sessionKey, jsonHttp)); }
internal static JObject GetKeysets(AesKey sessionKey, JsonHttpClient jsonHttp) { return(GetEncryptedJson("v1/account/keysets", sessionKey, jsonHttp)); }
internal static JsonHttpClient MakeJsonClient(JsonHttpClient jsonHttp, string sessionId = null) { return(MakeJsonClient(jsonHttp.Http, jsonHttp.BaseUrl, sessionId)); }