Ejemplo n.º 1
0
        internal static List <Account> OpenSharedFolder(R.SharedFolderInfo info,
                                                        Session session,
                                                        RSAParameters privateKey,
                                                        IRestTransport transport)
        {
            if (!info.Accepted)
            {
                return(new List <Account>(0));
            }

            var encryptedKey = info.EncryptedKey ?? "";

            if (encryptedKey.IsNullOrEmpty())
            {
                return(new List <Account>(0));
            }

            var password = Crypto.DecryptRsaPkcs1(encryptedKey.Decode64(), privateKey).ToUtf8();

            // Each shared folder is like an independent vault with its own password
            return(OpenFolder(session,
                              password,
                              info.Name ?? "-",
                              new RestClient(transport, ApiBaseUrl(info.Id))).Accounts);
        }
Ejemplo n.º 2
0
        public static Account[] OpenVaultCliApi(string clientId,
                                                string clientSecret,
                                                string password,
                                                string deviceId,
                                                string baseUrl,
                                                IRestTransport transport)
        {
            // Reset to default. Let the user simply pass a null or "" and not bother with an overload.
            if (baseUrl.IsNullOrEmpty())
            {
                baseUrl = DefaultBaseUrl;
            }

            var rest = new RestClient(transport, baseUrl, defaultHeaders: DefaultRestHeaders);

            // 1. Login and get the client info
            var authInfo = LoginCliApi(clientId, clientSecret, deviceId, rest);

            // 2. Fetch the vault
            var encryptedVault = DownloadVault(rest, $"{authInfo.TokenType} {authInfo.AccessToken}");

            // 3. Derive the master encryption key or KEK (key encryption key)
            var key = Util.DeriveKey(encryptedVault.Profile.Email, password, authInfo.KdfIterations);

            // 4. Decrypt and parse the vault. Done!
            return(DecryptVault(encryptedVault, key));
        }
Ejemplo n.º 3
0
        public static Account[] OpenVaultBrowser(string username,
                                                 string password,
                                                 string deviceId,
                                                 string baseUrl,
                                                 IUi ui,
                                                 ISecureStorage storage,
                                                 IRestTransport transport)
        {
            // Reset to default. Let the user simply pass a null or "" and not bother with an overload.
            if (baseUrl.IsNullOrEmpty())
            {
                baseUrl = DefaultBaseUrl;
            }

            var rest = new RestClient(transport, baseUrl, defaultHeaders: DefaultRestHeaders);

            // 1. Request the number of KDF iterations needed to derive the key
            var iterations = RequestKdfIterationCount(username, rest);

            // 2. Derive the master encryption key or KEK (key encryption key)
            var key = Util.DeriveKey(username, password, iterations);

            // 3. Hash the password that is going to be sent to the server
            var hash = Util.HashPassword(password, key);

            // 4. Authenticate with the server and get the token
            var token = Login(username, hash, deviceId, ui, storage, rest);

            // 5. Fetch the vault
            var encryptedVault = DownloadVault(rest, token);

            // 6. Decrypt and parse the vault. Done!
            return(DecryptVault(encryptedVault, key));
        }
Ejemplo n.º 4
0
        public static Account[] OpenVault(ClientInfo clientInfo, Ui ui, IRestTransport transport)
        {
            var rest    = new RestClient(transport, ApiBaseUrl(clientInfo.Username));
            var session = Login(clientInfo, ui, rest);

            try
            {
                // Open the main user's vault
                var(accounts, privateKey) = OpenFolder(session, clientInfo.Password, "", rest);

                // Open all the folders shared with the user
                if (privateKey != null)
                {
                    foreach (var info in GetSharedFolderList(session, rest))
                    {
                        accounts.AddRange(OpenSharedFolder(info, session, privateKey.Value, transport));
                    }
                }

                return(accounts.ToArray());
            }
            finally
            {
                Logout(session, rest);
            }
        }
Ejemplo n.º 5
0
        // TODO: It's impossible to test this function because of the S3.* static calls.
        public static byte[] OpenVaultDb(string username,
                                         string password,
                                         string deviceId,
                                         string deviceName,
                                         IUi ui,
                                         IRestTransport transport)
        {
            var rest = new RestClient(transport, "https://spcb.stickypassword.com/SPCClient/");

            // Request the token that is encrypted with the master password and get the one-time PIN
            // when a new device is registered for the first time.
            var(encryptedToken, passcode) = GetEncryptedTokenAndPasscode(username, deviceId, ui, rest);

            // Decrypt the token. This token is now used to authenticate with the server.
            var token = Util.DecryptToken(username, password, encryptedToken);

            // The device must be registered first.
            AuthorizeDevice(username, token, deviceId, deviceName, passcode, DateTime.Now, rest);

            // Get the S3 credentials to access the database on AWS.
            var s3Token = GetS3Token(username, token, deviceId, DateTime.Now, rest);

            // Download the database.
            return(DownloadLatestDb(s3Token, transport));
        }
Ejemplo n.º 6
0
        public static Account[] OpenVault(string username,
                                          string password,
                                          ClientInfo clientInfo,
                                          IUi ui,
                                          IRestTransport transport)
        {
            var rest    = new RestClient(transport, "https://lastpass.com");
            var session = Login(username, password, clientInfo, ui, rest);

            try
            {
                var blob = DownloadVault(session, rest);
                var key  = Util.DeriveKey(username, password, session.KeyIterationCount);

                var privateKey = new RSAParameters();
                if (!session.EncryptedPrivateKey.IsNullOrEmpty())
                {
                    privateKey = Parser.ParseEncryptedPrivateKey(session.EncryptedPrivateKey, key);
                }

                return(ParseVault(blob, key, privateKey));
            }
            finally
            {
                Logout(session, rest);
            }
        }
Ejemplo n.º 7
0
 // TODO: Write a test that runs the whole sequence and checks the result.
 internal static Vault Open(string username,
                            string password,
                            Ui ui,
                            ISecureStorage storage,
                            IRestTransport transport)
 {
     return(new Vault(Client.OpenVault(username, password, ui, storage, transport)));
 }
Ejemplo n.º 8
0
        //
        // Internal
        //

        internal static Vault Open(string username,
                                   string accountPassword,
                                   string vaultPassword,
                                   IRestTransport restTransport,
                                   IBoshTransport boshTransport)
        {
            return(new Vault(Client.OpenVault(username, accountPassword, vaultPassword, restTransport, boshTransport)));
        }
Ejemplo n.º 9
0
        public static Account[] OpenVault(string oauthToken, string[] recoveryWords, IRestTransport transport)
        {
            // We do this first to fail early in case the recovery words are incorrect.
            var masterKey = Util.DeriveMasterKeyFromRecoveryWords(recoveryWords);

            var rest = new RestClient(transport,
                                      "https://api.dropboxapi.com/2",
                                      defaultHeaders: new Dictionary <string, string>
            {
                ["Authorization"] = $"Bearer {oauthToken}"
            });

            // 1. Get account info
            var accountInfo = Post <R.AccountInfo>("users/get_current_account",
                                                   RestClient.JsonNull, // Important to send null!
                                                   RestClient.NoHeaders,
                                                   rest);

            if (accountInfo.Disabled)
            {
                throw new InternalErrorException($"The account is disabled");
            }

            // 2. Get features
            var features = Post <R.Features>("passwords/get_features_v2",
                                             RestClient.JsonNull, // Important to send null!
                                             RestClient.NoHeaders,
                                             rest);

            if (features.Eligibility.Tag != "enabled")
            {
                throw new InternalErrorException("Dropbox Passwords is not enabled on this account");
            }

            // 3. List the root folder
            // TODO: Very long folders are not supported. See "has_more" and "cursor".
            var rootFolder = Post <R.RootFolder>("files/list_folder",
                                                 new Dictionary <string, object> {
                ["path"] = ""
            },
                                                 MakeRootPathHeaders(features.Eligibility.RootPath),
                                                 rest);

            // 4. Get all entries
            var contentRest = new RestClient(rest.Transport,
                                             "https://content.dropboxapi.com/2",
                                             defaultHeaders: rest.DefaultHeaders);
            var entries = DownloadAllEntries(rootFolder, features.Eligibility.RootPath, contentRest);

            // Try to find all keysets that decrypt (normally there's only one).
            var keysets = FindAndDecryptAllKeysets(entries, masterKey);

            // Try to decrypt all account entries and see what decrypts.
            var accounts = FindAndDecryptAllAccounts(entries, keysets);

            // Done, phew!
            return(accounts);
        }
Ejemplo n.º 10
0
 internal Session(ClientInfo clientInfo,
                  Keychain keychain,
                  AesKey key,
                  RestClient rest,
                  IRestTransport transport)
 {
     ClientInfo = clientInfo;
     Keychain   = keychain;
     Key        = key;
     Rest       = rest;
     Transport  = transport;
 }
Ejemplo n.º 11
0
 public RestClient(IRestTransport transport,
                   string baseUrl        = "",
                   IRequestSigner signer = null,
                   ReadOnlyHttpHeaders defaultHeaders = null,
                   ReadOnlyHttpCookies defaultCookies = null)
 {
     Transport      = transport;
     BaseUrl        = baseUrl;
     Signer         = signer ?? new UnitRequestSigner();
     DefaultHeaders = defaultHeaders ?? new ReadOnlyDictionary <string, string>(NoHeaders);
     DefaultCookies = defaultCookies ?? new ReadOnlyDictionary <string, string>(NoCookies);
 }
Ejemplo n.º 12
0
        // Returns the second factor token from Duo or null when canceled by the user.
        public static Result Authenticate(string host, string signature, IDuoUi ui, IRestTransport transport)
        {
            var rest = new RestClient(transport, $"https://{host}");

            var(tx, app) = ParseSignature(signature);
            var html = DownloadFrame(tx, rest);

            var(sid, devices) = ParseFrame(html);

            while (true)
            {
                // Ask the user to choose what to do
                var choice = ui.ChooseDuoFactor(devices);
                if (choice == null)
                {
                    return(null); // Canceled by user
                }
                // SMS is a special case: it doesn't submit any codes, it rather tells the server to send
                // a new batch of passcodes to the phone via SMS.
                if (choice.Factor == DuoFactor.SendPasscodesBySms)
                {
                    SubmitFactor(sid, choice, "", rest);
                    choice = new DuoChoice(choice.Device, DuoFactor.Passcode, choice.RememberMe);
                }

                // Ask for the passcode
                var passcode = "";
                if (choice.Factor == DuoFactor.Passcode)
                {
                    passcode = ui.ProvideDuoPasscode(choice.Device);
                    if (passcode.IsNullOrEmpty())
                    {
                        return(null); // Canceled by user
                    }
                }

                var token = SubmitFactorAndWaitForToken(sid, choice, passcode, ui, rest);

                // Flow error like an incorrect passcode. The UI has been updated with the error. Keep going.
                if (token.IsNullOrEmpty())
                {
                    continue;
                }

                // All good
                return(new Result($"{token}:{app}", choice.RememberMe));
            }
        }
Ejemplo n.º 13
0
        // TODO: Don't return JObject
        public static R.Vault OpenVault(string username, string deviceId, Ui ui, IRestTransport transport)
        {
            var rest = new RestClient(transport, BaseApiUrl);

            var loginType = RequestLoginType(username, rest);

            if (loginType == LoginType.DoesntExist)
            {
                throw new BadCredentialsException("Invalid username");
            }

            var registered = IsDeviceRegistered(username, deviceId, rest);

            // We have a registered device, no 2FA code is needed unless always-OTP mode is on. In
            // always-OTP mode registering a device doesn't stop the server from asking OTP the next
            // time around. So remember-me function is this mode is more or less useless. The
            // registered device shows up in the admin area though.
            if (registered && loginType != LoginType.GoogleAuth_Always)
            {
                return(Fetch(username, deviceId, rest));
            }

            // Try to fetch a few times and then register the device
            var attempt = 0;

            while (true)
            {
                try
                {
                    var passcode = GetPasscodeFromUser(username, loginType, attempt++, ui, rest);
                    var blob     = Fetch(username, loginType, passcode.Code, rest);

                    if (passcode.RememberMe && !registered)
                    {
                        var token = blob.Token ?? passcode.Code;
                        RegisterDeviceWithToken(username, deviceId, DeviceName, token, rest);
                    }

                    return(blob);
                }
                catch (BadMultiFactorException) when(attempt < 3)
                {
                    // Ignore
                }
            }
        }
Ejemplo n.º 14
0
        public static void OpenVault(string username, Ui ui, IRestTransport transport)
        {
            var newRest = new RestClient(transport,
                                         "https://api.dashlane.com/v1/authentication/",
                                         new Dl1RequestSigner(),
                                         defaultHeaders: new Dictionary <string, string>
            {
                ["dashlane-client-agent"] =
                    "{\"platform\":\"server_leeloo\",\"version\":\"57.220.0.1495220\"}",
                ["User-Agent"] =
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36",
            });

            RequestEmailToken(username, newRest);
            var info = RegisterNewDevice(username, ui, newRest);

            // TODO: Fetch the vault with this UKI
            var uki = $"{info.AccessKey}-{info.SecretKey}";
        }
Ejemplo n.º 15
0
        public static Account[] OpenVault(string username,
                                          string password,
                                          string passphrase,
                                          IUi ui,
                                          ISecureStorage storage,
                                          IRestTransport transport)
        {
            var rest = new RestClient(transport);

            // This token is needed to access other pages of the login flow. It's sent via headers,
            // cookies and in the request data.
            var token = RequestToken(rest);

            // TLD is determined by the region/data center. Each user is associated with a specific region.
            var userInfo = RequestUserInfo(username, token, DataCenterToTld(DefaultDataCenter), rest);

            // Perform the login dance that possibly involves the MFA steps. The cookies are later
            // used by the subsequent requests.
            //
            // TODO: It would be ideal to figure out which cookies are needed for general
            // cleanliness. It was too many of them and so they are now passed altogether a bundle
            // between the requests.
            var cookies = LogIn(userInfo, password, token, ui, storage, rest);

            try
            {
                var vaultKey      = Authenticate(passphrase, cookies, userInfo.Tld, rest);
                var vaultResponse = DownloadVault(cookies, userInfo.Tld, rest);
                var sharingKey    = DecryptSharingKey(vaultResponse, vaultKey);

                return(ParseAccounts(vaultResponse, vaultKey, sharingKey));
            }
            finally
            {
                LogOut(cookies, userInfo.Tld, rest);
            }
        }
Ejemplo n.º 16
0
        //
        // Internal
        //

        internal static Vault Open(ClientInfo clientInfo, Ui ui, IRestTransport transport)
        {
            return new Vault(Client.OpenVault(clientInfo, ui, transport));
        }
Ejemplo n.º 17
0
 // Downloads an object from S3 in us-east-1 region.
 public static byte[] GetObject(string bucket, string path, Credentials credentials, IRestTransport transport)
 {
     return(GetObject(bucket, path, credentials, new RestClient(transport)));
 }
Ejemplo n.º 18
0
        public static Account[] OpenVault(string username,
                                          string password,
                                          Ui ui,
                                          ISecureStorage storage,
                                          IRestTransport transport)
        {
            var rest = new RestClient(transport);

            // Step 1: Register a new deice or use the existing one from the previous run.
            var deviceInfo = LoadDeviceInfo(storage) ?? RegisterNewDevice("truekey-sharp", rest);

            // Step 2: Parse the token to decode OTP information.
            var otpInfo = Util.ParseClientToken(deviceInfo.Token);

            // Step 3: Validate the OTP info to make sure it's got only the
            //         things we support at the moment.
            Util.ValidateOtpInfo(otpInfo);

            // Store the token and ID for the next time.
            StoreDeviceInfo(deviceInfo, storage);

            // Bundle up everything in one place
            var clientInfo = new ClientInfo(username, "truekey-sharp", deviceInfo, otpInfo);

            // Step 4: Auth step 1 gives us a transaction id to pass along to the next step.
            var transactionId = AuthStep1(clientInfo, rest);

            // Step 5: Auth step 2 gives us the instructions on what to do next. For a new client that
            //         would be some form of second factor auth. For a known client that would be a
            //         pair of OAuth tokens.
            var whatsNext = AuthStep2(clientInfo, password, transactionId, rest);

            // The device is trusted if it's already authenticated at this point and
            // no second factor is needed.
            var isTrusted = whatsNext.IsAuthenticated;

            // Step 6: Auth FSM -- walk through all the auth steps until we're done.
            var oauthToken = TwoFactorAuth.Start(clientInfo, whatsNext, ui, rest);

            // Step 7: Save this device as trusted not to repeat the two factor dance next times.
            if (!isTrusted)
            {
                SaveDeviceAsTrusted(clientInfo, transactionId, oauthToken, rest);
            }

            // Step 8: Get the vault from the server.
            var encryptedVault = GetVault(oauthToken, rest);

            // Step 9: Compute the master key.
            var masterKey = Util.DecryptMasterKey(password,
                                                  encryptedVault.MasterKeySalt,
                                                  encryptedVault.EncryptedMasterKey);

            // Step 10: Decrypt the accounts.
            var accounts = encryptedVault
                           .EncryptedAccounts
                           .Select(i => new Account(id: i.Id,
                                                    name: i.Name,
                                                    username: i.Username,
                                                    password: Util.Decrypt(masterKey, i.EncryptedPassword).ToUtf8(),
                                                    url: i.Url,
                                                    note: Util.Decrypt(masterKey, i.EncryptedNote).ToUtf8()))
                           .ToArray();

            return(accounts);
        }
Ejemplo n.º 19
0
 internal static Vault Open(string oauthToken, string[] recoveryWords, IRestTransport transport)
 {
     return(new Vault(Client.OpenVault(oauthToken, recoveryWords, transport)));
 }
Ejemplo n.º 20
0
        public static Account[] OpenVault(string username,
                                          string accountPassword,
                                          string vaultPassword,
                                          IRestTransport restTransport,
                                          IBoshTransport boshTransport)
        {
            var rest = new RestClient(restTransport);

            // 1. Login
            var(sessionCookie, authCookies) = Login(username, accountPassword, rest);

            try
            {
                // 2. Get XMPP info
                var xmpp = GetXmppInfo(authCookies, rest);

                // 3. The server returns a bunch of alternative URLs with the XMPP BOSH Js library which
                //    are located on different domains. We need to pick one and all the following request
                //    are done using this domain and its sub and sibling domains.
                var jsLibraryHost = ChooseJsLibraryHost(xmpp);

                // 4. Generate JID
                var jid = GenerateJid(xmpp.UserId, jsLibraryHost);

                // 5. Get notify server BOSH url
                var httpsBoshUrl = GetBoshUrl(jid, jsLibraryHost, rest);

                // 6. "find_bosh_bind" call returns a https:// link which doesn't work with the web sockets.
                //    It need to be converted to the wss:// before it could be used.
                var wssBoshUrl = ConvertHttpsBoshUrlToWss(httpsBoshUrl);

                // 6. Connect to the notify XMPP BOSH server
                var bosh = new Bosh(wssBoshUrl, jid, xmpp.XmppCredentials.Password, boshTransport);
                bosh.Connect();

                // 7. Get DB info which mainly contains the encryption settings (key derivation info)
                var dbInfoBlob = bosh.GetChanges(GetDatabaseInfoCommand, GetDatabaseInfoCommandId)
                                 .Where(x => x.Type == "Database")
                                 .Select(x => x.Data)
                                 .FirstOrDefault()?
                                 .Decode64();

                if (dbInfoBlob == null)
                {
                    throw MakeError("Database info is not found in the response");
                }

                var dbInfo = DatabaseInfo.Parse(dbInfoBlob);

                var version = dbInfo.Version;
                if (!SupportedDbVersions.Contains(version))
                {
                    throw new UnsupportedFeatureException($"Database version {version} is not supported");
                }

                var encryptionKey = Util.DeriveEncryptionKey(vaultPassword, dbInfo);
                var authKey       = Util.DeriveMasterPasswordAuthKey(jid.UserId, encryptionKey, dbInfo);

                // 8. Get DB that contains all of the accounts
                // TODO: Test on a huge vault to see if the accounts come in batches and
                //       we need to make multiple requests
                var db = bosh.GetChanges(GetDatabaseCommand, GetDatabaseCommandId, authKey.ToBase64());

                return(Parser.ParseVault(db, encryptionKey).ToArray());
            }
            finally
            {
                // 9. Logout
                Logout(sessionCookie, authCookies, rest);
            }
        }
Ejemplo n.º 21
0
        //
        // Internal
        //

        internal static Vault Open(string username, string password, string deviceId, Ui ui, IRestTransport transport)
        {
            return(new Vault(Remote.OpenVault(username, deviceId, ui, transport), password));
        }
Ejemplo n.º 22
0
 public RestBoshTransport(IRestTransport transport)
 {
     _rest = new RestClient(transport);
 }
Ejemplo n.º 23
0
 // This functions finds out what the latest version of the database is and downloads
 // it from S3.
 internal static byte[] DownloadLatestDb(S3Token token, IRestTransport transport)
 {
     return(DownloadLatestDb(token, new RestClient(transport)));
 }