Example #1
0
        /// <summary>
        /// This event handler gets called when the QuickPass timer has elapsed,
        /// indicating that the QuickPass belonging to this specific timer shall be cleared.
        /// </summary>
        private void QuickPassTimerElapsed(object sender, ElapsedEventArgs e)
        {
            QuickPassItem qpi   = null;
            Timer         timer = (Timer)sender;

            // Get the QuickPassInfo that this Timer belongs to
            lock (_dataSyncObj)
            {
                foreach (var item in _quickPassItems)
                {
                    if (timer == item.Value.Timer)
                    {
                        qpi = item.Value;
                        break;
                    }
                }
            }

            Log.Information("QuickPass timer elapsed for identity id {IdentityUniqueId}",
                            qpi?.IdentityUniqueId);

            // If such a QuickPassInfo item exists, clear it
            if (qpi != null)
            {
                ClearQuickPass(qpi.IdentityUniqueId, QuickPassClearReason.QuickPassTimeout);
            }
        }
Example #2
0
        /// <summary>
        /// Clears the QuickPass entries of all identities from memory. After calling
        /// this method, SQRL will ask for the full master password again for all
        /// available identities.
        /// </summary>
        /// <param name="identityUniqueId">The unique id (block 0) of the identity for
        /// which to clear the QuickPass entry.</param>
        /// <param name="reason">The reason for clearing the QuickPass.</param>
        /// <param name="fireClearedEvent">If set to <c>true</c>, a
        /// <c>QuickPassCleared</c> event will be fired if the QuickPass for the given
        /// <paramref name="identityUniqueId"/> could be successfully cleared.</param>
        public bool ClearQuickPass(string identityUniqueId, QuickPassClearReason reason, bool fireClearedEvent = true)
        {
            Log.Verbose("{MethodName} called", nameof(ClearQuickPass));

            lock (_dataSyncObj)
            {
                if (!_quickPassItems.ContainsKey(identityUniqueId))
                {
                    Log.Warning("No QuickPass entry found for identity {IdentityUniqueId}", identityUniqueId);
                    return(false);
                }

                QuickPassItem qpi = _quickPassItems[identityUniqueId];

                if (reason == QuickPassClearReason.IdentityChange && !ClearQuickPassOnIdentityChange)
                {
                    return(false);
                }
                if (reason == QuickPassClearReason.IdleTimeout && !qpi.ClearQuickPassOnIdle)
                {
                    return(false);
                }
                if (reason == QuickPassClearReason.EnterBlankingState && !qpi.ClearQuickPassOnSleep)
                {
                    return(false);
                }
                if (reason == QuickPassClearReason.UserSwitching && !qpi.ClearQuickPassOnSwitchingUser)
                {
                    return(false);
                }

                // First, stop the QuickPass timer
                qpi.Timer.Stop();

                // Then, overwrite the encrypted imk and ilk so that we don't
                // leave any traces of key material in RAM.
                qpi.EncryptedImk.ZeroFill();
                qpi.EncryptedIlk.ZeroFill();

                // Delete the QuickPass entry from our dictionary
                _quickPassItems.Remove(identityUniqueId);
            }

            Log.Information("QuickPass entry cleared. ID: {IdentityUniqueId} Reason: {Reason}",
                            identityUniqueId, reason.ToString());

            // Finally, fire the QuickPassCleared event
            if (fireClearedEvent)
            {
                QuickPassCleared?.Invoke(this, new QuickPassClearedEventArgs(new List <string>()
                {
                    identityUniqueId
                }));
                Log.Information("Firing QuickPassCleared event");
            }

            return(true);
        }
Example #3
0
        /// <summary>
        /// Creates a QuickPass from the given <paramref name="password"/> and
        /// <paramref name="imk"/> using the QuickPass settings stored in
        /// <paramref name="identity"/>, stores it in memory and establishes a
        /// timer that will clear the QuickPass after the timeout set forth in
        /// <paramref name="identity"/>'s QuickPass settings.
        /// </summary>
        /// <param name="password">The full identity master password.</param>
        /// <param name="imk">The identity's unencrypted Identity Master Key (IMK).</param>
        /// <param name="ilk">The identity's unencrypted Identity Lock Key (ILK).</param>
        /// <param name="identity">The identity that the QuickPass should be set for.</param>
        /// <param name="progress">An object implementing the IProgress interface for tracking the operation's progress (optional).</param>
        /// <param name="progressText">A string representing a text descrition for the progress indicator (optional).</param>
        public async void SetQuickPass(string password, byte[] imk, byte[] ilk, SQRLIdentity identity, IProgress <KeyValuePair <int, string> > progress = null, string progressText = null)
        {
            if (string.IsNullOrEmpty(password))
            {
                Log.Warning("Can't use QuickPass on an empty password, aborting SetQuickPass()!");
                return;
            }

            QuickPassItem qpi = new QuickPassItem()
            {
                EstablishedDate               = DateTime.Now,
                QuickPassLength               = identity.Block1.HintLength,
                IdentityUniqueId              = identity.Block0.UniqueIdentifier.ToHex(),
                ScryptRandomSalt              = SodiumCore.GetRandomBytes(16),
                Nonce                         = SodiumCore.GetRandomBytes(24),
                QuickPassTimeoutSecs          = identity.Block1.PwdTimeoutMins * 60,
                ClearQuickPassOnIdle          = identity.Block1.OptionFlags.ClearQuickPassOnIdle,
                ClearQuickPassOnSleep         = identity.Block1.OptionFlags.ClearQuickPassOnSleep,
                ClearQuickPassOnSwitchingUser = identity.Block1.OptionFlags.ClearQuickPassOnSwitchingUser,
                Timer                         = new Timer()
            };

            qpi.Timer.Enabled   = false;
            qpi.Timer.AutoReset = false; // Dont restart timer after calling elapsed
            qpi.Timer.Interval  = QP_GENERAL_TIMEOUT_SEC;
            qpi.Timer.Elapsed  += QuickPassTimerElapsed;

            string quickPass = password.Substring(0, qpi.QuickPassLength);

            var enScryptResult = await SQRL.EnScryptTime(
                quickPass,
                qpi.ScryptRandomSalt,
                (int)Math.Pow(2, 9),
                QP_KEYDERIV_SEC,
                progress,
                progressText);

            qpi.ScryptIterationCount = enScryptResult.IterationCount;
            qpi.EncryptedImk         = StreamEncryption.Encrypt(imk, qpi.Nonce, enScryptResult.Key);
            qpi.EncryptedIlk         = StreamEncryption.Encrypt(ilk, qpi.Nonce, enScryptResult.Key);

            // If we already have a QuickPass entry for this identity, remove it first
            if (HasQuickPass(qpi.IdentityUniqueId))
            {
                ClearQuickPass(qpi.IdentityUniqueId, QuickPassClearReason.Unspecified);
            }

            // Now, add the QuickPass item to our list and start the timer
            lock (_dataSyncObj)
            {
                _quickPassItems.Add(qpi.IdentityUniqueId, qpi);
                qpi.Timer.Start();
            }

            Log.Information("QuickPass set for identity {IdentityUniqueId}",
                            qpi.IdentityUniqueId);
        }
Example #4
0
        /// <summary>
        /// Decrypts the Identity Master Key (IMK) for for the given <paramref name="identityUniqueId"/>
        /// using the provided <paramref name="quickPass"/> and returns it. If <c>null</c> is passed for
        /// <paramref name="identityUniqueId"/>, the decrypted IML for the current identity will be returned.
        /// </summary>
        /// <param name="quickPass">The QuickPass (first x characters from the identity's master password
        /// which was used to encrypt the IMK when setting the QuickPass entry for the identity.</param>
        /// <param name="identityUniqueId">The hex representation of the identity's unique id (block 0),
        /// or <c>null</c> if you want to decrypt the current identity's IMK.</param>
        /// <param name="progress">An object implementing the IProgress interface for tracking the operation's progress (optional).</param>
        /// <param name="progressText">A string representing a text descrition for the progress indicator (optional).</param>
        /// <returns>The decrypted block 1 keys (IMK, ILK) for the given <paramref name="identityUniqueId"/>, or
        /// <c>null</c> if no such QuickPass entry exists.</returns>
        public async Task <QuickPassDecryptedKeys> GetQuickPassDecryptedImk(string quickPass, string identityUniqueId        = null,
                                                                            IProgress <KeyValuePair <int, string> > progress = null, string progressText = null)
        {
            QuickPassItem qpi = null;

            if (identityUniqueId == null)
            {
                if (_identityManager.CurrentIdentity == null)
                {
                    return(null);
                }
                identityUniqueId = _identityManager.CurrentIdentity?.Block0?.UniqueIdentifier?.ToHex();
            }


            if (identityUniqueId == null)
            {
                Log.Error("Could not resolve current identity in {MethodName}, throwing Exception!",
                          nameof(GetQuickPassDecryptedImk));

                throw new InvalidOperationException("Cannot return QuickPass-decrypted IMK without an identity!");
            }

            lock (_dataSyncObj)
            {
                if (!_quickPassItems.ContainsKey(identityUniqueId))
                {
                    Log.Warning("No identity found for id {IdentityUniqueId} in {MethodName}",
                                identityUniqueId, nameof(GetQuickPassDecryptedImk));

                    return(null);
                }

                qpi = _quickPassItems[identityUniqueId];
            }

            byte[] key = await SQRL.EnScryptCT(quickPass, qpi.ScryptRandomSalt, (int)Math.Pow(2, 9),
                                               qpi.ScryptIterationCount, progress, progressText);

            byte[] decryptedImk = StreamEncryption.Decrypt(qpi.EncryptedImk, qpi.Nonce, key);
            byte[] decryptedIlk = StreamEncryption.Decrypt(qpi.EncryptedIlk, qpi.Nonce, key);

            Log.Information("QuickPass retrieved for identity {IdentityUniqueId}",
                            identityUniqueId);

            return(new QuickPassDecryptedKeys(decryptedImk, decryptedIlk));
        }