Pull to refresh
21
0
Send message
Кому интересно, вот что получилось из этого всего
Easel (Мольберт)

Творите!
Кстати, если не хочется хранить некоторые параметры в самих вью-моделях, то можно создать отдельную SettingsViewModel, например, и писать так

Width={Binding [Width,100], Source={Store Type=vm:SettingsViewModel}}

Тут дело вкуса и конкретного случая.
Странно, то ли никто не заметил моего сообщения, то ли я непонятно всё написал =)

Однако нашлась сегодня интересная статья по теме:
Fault Attacks on RSA Signatures with Partially Unknown Messages
и её продолжение:
Fault Attacks Against emv Signatures

К сожалению, с моими узкоспециальными знаниями программиста и неглубокими математическими, мне трудно объективно оценить её ценность применительно к протоколу Telegram. Но насколько мне всё же удалось понять, тот факт, что мы частично знаем шифруемое сообщение, ослабляет RSA.

Всё-таки хотелось бы получить комментарии компетентных лиц по этому поводу…
Ещё немного подумав я, кажется, начал догадываться, зачем в pqInner поместили PQ, P, Q, Nonce и ServerNonce — чтобы удостовериться, что этот запрос никто не подделал или он правильно зашифрован. Но с другой стороны, не слишком ли много данных используется для такой проверки? По крайней мере, все три параметра PQ, P и Q избыточно помещать, ведь достаточно будет только двух, да и достаточно знать только Nonce либо ServerNonce, ведь они однозначно определяют друг друга. Не пойдёт ли вся эта избыточность во вред устойчивости RSA? Да и нужно ли вообще именно так поступать?
Заметил у себя маленькую неточность
Мы точно знаем значения и местоположение 64-х байт из 256 в массиве

Мы точно знаем значения и местоположение 64-х байт из 255 в исходном массиве data
Первой мыслью была возможность MITM-атаки (человек посередине) и я пошел читать api протокола. Где выяснилось, что тут защита достаточно надежная: в момент первого запуска клиента создается авторизационный ключ, создается он непосредственно на клиентском устройстве с помощью протокола обмена ключами Диффи-Хелмана, но с небольшим отличием — открытый ключ сервера Telegram уже прошит в коде клиента, что исключает его подмену третьими лицами.


Сразу скажу, что не являюсь специалистом в области криптографии, поэтому мой вопрос может показаться невежественным. Если так, то заранее прошу простить за это.
Насколько мне удалось понять, защита от атаки «человек посередине» обеспечивается протоколом уже на начальном этапе при авторизации приложения.
Разложив pq на множители, клиент генерирует NewNonce (32 байта), а затем необходимая информация оборачивается в pqInner

                new pqRequest {Nonce = Sugar.GetRandomBytes(16)}.Send(Channel);
                var pqResponce = Channel.Receive().Of<pqResponce>();
                var pq = pqResponce.PQ;
                var p = PrimeUtils.findSmallMultiplier(pq);
                var q = pq/p;
                var pqInner = new pqInner // constructor 4 bytes
                {
                    PQ = pqResponce.PQ, // 12 bytes
                    P = p, // 8 bytes
                    Q = q, // 8 bytes
                    Nonce = pqResponce.Nonce, // 16 bytes
                    ServerNonce = pqResponce.ServerNonce, // 16 bytes
                    NewNonce = Sugar.GetRandomBytes(32)
                };


Заметим, что злоумышленник в состоянии перехватить сгенерированную клиентом при pq-запросе последовательность Nonce, а также ответ сервера, содержащий ServerNonce и число PQ (поэтому он также может разложить его на множители), ведь пока ещё никакие данные не шифруются. То есть у него есть частичная информация о структуре pqInner — 64 байта из 96-ти, не известно только поле NewNonce.

Затем происходит следующее.
pqInner сериализуется в массив байт и находится хэш сериализации (20 байт), также добавляется рандомная последовательность из 139-ти байт (20+96+139=255). После чего данная информация шифруется с помощью публичного ключа и используется в структуре dhRequest. Злоумышленник не может её так просто расшифровать, поскольку не обладает приватным серверным ключом. То есть криптозащита обеспечивается 256 байтным (2048-битным) ключом, что само по себе считается достаточно надёжным.

                var pqInnerBytes = pqInner.ToBytes();          
                var data = new List<byte>();
                data.AddRange(pqInnerBytes.Hash());
                data.AddRange(pqInnerBytes);
                data.AddRange(Sugar.GetRandomBytes(255 - data.Count));
                var keySet = KeySet.PublicKeySets.First(s => s.Fingerprint == pqResponce.Fingrprints[0]);
                var modulus = keySet.Modulus;
                var exponent = keySet.Exponent;
                var message = new BigInteger(data.ToArray());
                var encriptedData = message.modPow(exponent, modulus);
                new dhRequest
                {
                    P = p,
                    Q = q,
                    Nonce = pqResponce.Nonce,
                    ServerNonce = pqResponce.ServerNonce,
                    FingerPrint = pqResponce.Fingrprints[0],
                    EncriptedData = encriptedData.getBytes(),
                }.Send(Channel);
                var dhResponce = Channel.Receive();
                if (dhResponce is dhOkResponce) { ... }


Обратим внимание ещё на одну деталь: dhRequest — запрос, уже содержащий зашифрованную информацию, дополнительно включает поля P, Q, Nonce и ServerNonce, сам по себе передаётся в нешифрованном виде, поэтому у подслушивающего есть очередная возможность узнать эти параметры.

Теперь мы подошли к главному вопросу…
RSA в чистом виде надёжен, но как сказывается на его устойчивости тот факт, что нам частично уже известны сами зашифрованные данные и их структура?.. Мы точно знаем значения и местоположение 64-х байт из 256 в массиве, а ещё нам известно, как формируются 20 первых байт хэша.

Я не математик, но интуитивно предполагаю, что подобные условия понижают уровень защиты алгоритма. Не могу сказать насколько, но сам факт этот настораживает.

Но что вызывает ещё больше вопросов, так это то, что pqInner дублирует соответствующие поля PQ, P, Q, Nonce, ServerNonce в нешифрованных запросах, ведь именно эти поля дают нам некоторое представление о зашифрованной информации. Не являются ли они там лишними?

Что это? Может быть, «очередная закладка» или эти поля остались в наследство от первых версий протокола, и их забыли потом убрать?

Ведь намного чище было бы оставить в pqInner только поле NewNonce и добавить больше рандомных данных. А лучше вообще убрать оболочку pqInner, поскольку нам известны четыре байта сериализации номера конструктора этой структуры. То есть тогда мы просто будем шифровать полностью рандомную последовательность, поэтому злоумышленник не сможет сделать никаких предположений насчёт передаваемой информации.

Но что нам всё это даёт? Имея потенциально ослабленный RSA и возможность активного перехвата пакетов, мы можем расшифровать NewNonce, то есть сможем получить временные ключи, которые использует клиент для расшифровки серверных сообщений и шифровки своих во время процесса dh-авторизации. А теперь главный трюк — притворимся для клиента сервером, а для сервера клиентом и договоримся с каждым об авторизационном ключе. По сути, проведём MITM-атаку.

Конечно, разработать подобную программу-шпион одновременно выдающую себя за сервер и за клиент — задача непростая, но всё же выполнимая за разумные сроки, ведь API открытый. Думаю, какое-нибудь АНБ может себе это позволить. Остаётся один не очень удобный момент — это проверочный смс-код при регистрации пользователя, но если спецслужбы имеют доступ к мобильному оператору, то подделать его, думаю, тоже возможно.

В общем главный вопрос мой сейчас к структуре pqInner. Зачем в ней нужны поля, которые дублируются в других запросах и частично раскрывают шифруемые данные? Может быть, открытость pqInner и не ведёт к существенному ухудшению надёжности RSA, но хотелось бы тогда где-нибудь про это прочитать.

Возможно, я что-то недопонял или упустил из виду, поэтому отнеситесь, пожалуйста, с пониманием ко всему, что здесь написано.
Вышла статья по WinPhone-разработке с примером, где использованы текущие идеи с учётом ограничений мобильной платформы.

WinPhone: пути к совершенству
Спасибо! Ваш вариант локализации тоже довольно интересный.
Плюс его в экономии ресурсов, но минус в том, что не всегда удобно им пользоваться, например, в DataTemplate

            <ListBox ItemsSource="{Binding Points}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock Text="{f:Localizing Key=СoordinateX, StringFormat={}'{0}: '}"/>
                            <TextBlock Text="{Binding X}"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>

То есть, чтобы ваш вариант сработал в такой ситуации нужно, чтобы данные были обязательно унаследованы от базовой вью-модели. Но это не всегда выполнимо, плюс ведёт к разрастанию класса Point.

Использование лямбда-выражений для нотификации UI во многих случаях оправдано, хоть и работает немного медленнее.
Бояться этого не стоит, прото нужно аккуратно ими пользоваться.
Понятно, что если свойство изменяется с большой частотой, то конкретно для него лучше нотификацию реализовать другим образом.

С другой стороны, такие ситуации в приложениях встречаются не столь часто.

Да и намного красивее писать что-то вроде

this[() => Name].PropertyChanged += (o, e) => { ... };

чем глобально подписываться на событие PropertyChanged и использовать if со строковой константой

PropertyChanged += (o, e) => { if (e.PropertyName == "Name") { ... } };

Подписка, как правило, выполняется только один раз, поэтому влияние лямбда-выражения на скорость здесь минимальное.
Спасибо! Над дизайном, что называется, я сильно не заморачивался, поэтому тот, кто захочет, думаю, доработает его.
Однако если есть замечания и предложения именно по коду приложения, то я готов их выслушать и внести определённые правки, где нужно.
И вам спасибо! Но на самом деле Markup Extension — это лишь то, что лежит на поверхности.
Если глубже изучить примеры кода, то можно обнаружить ещё несколько ценных вещей…
Хотелось бы взглянуть на пример кода. Возможно, в корневом объекте так или иначе была слабая ссылка на ваш объект, поэтому он был доступен лишь до поры до времени. Также, вполне может быть, корневой объект ссылался на точную копию вашего оригинального объекта, поэтому вы приняли их за один и тот же экземпляр, а сборщик мусора удалил оригинал, оставив лишь копию. Думаю, что второй вариант наиболее вероятный.
Полностью согласен, что это один из возможных вариантов и выбор способа сохранения просто дело вкуса. Подобный паттерн часто встречается в приложениях.

Насколько я понял, пользователю areht не понравилось то, что теряется реляционное отображение для GUI-свойств (по правилам нужно создать по новому столбцу для каждой настройки). Но я, как и вы, предерживаюсь мысли, что для интерфейсных настроек это, как правило, не нужно, поэтому суть предложенного в статье подхода именно в том, чтобы избавиться от подобной рутинной работы по созданию реальных свойств и маппингу их на столбцы в БД.

Дабы не быть голословным я просто приведу реальный пример. Вот вью-модель для диалога сохранения файлов в редакторе Poet.

[DataContract]
public class SaveViewModel : ViewModel
{
    public SaveViewModel()
    {
        Initialize();
    }
      
    public bool DialogResult { get; set; }
    public ObservableCollection<DocumentView> Items { get; private set; }
    public ObservableCollection<DocumentView> SelectedItems { get; private set; }      
    public bool IsAllItemsSelected
    {
        get
        {
            return Items.Where(i => ApplicationCommands.Save.CanExecute(null, i)).
            	All(i => SelectedItems.Contains(i));
        }
    }
    
    public void SetItems(
    	ObservableCollection<DocumentView> items, 
    	IEnumerable<DocumentView> selectedItems)
    {
        Items = items;
        RaisePropertyChanged(() => Items);
        SelectedItems.Clear();
        if (selectedItems != null)
            SelectedItems.AddRange(selectedItems);
    }
      
    [OnDeserialized]
    private void Initialize(StreamingContext context = default(StreamingContext))
    {
        Executed();
        CanExecute();
        SelectedItems = new ObservableCollection<DocumentView>();
        SelectedItems.CollectionChanged += (sender, args) => 
        	RaisePropertyChanged(() => IsAllItemsSelected);
    }
      
    private void CanExecute()
    {
        this[ApplicationCommands.Save].CanExecute += (sender, args) => 
            args.CanExecute = SelectedItems.Any(i => 
            	ApplicationCommands.Save.CanExecute(null, i));
    }
      
    private void Executed()
    {
        this[ApplicationCommands.Save].Executed += (sender, args) =>
        {
            var cancelArgs = new CancelEventArgs();
            foreach (var item in SelectedItems.
            	Where(item => ApplicationCommands.Save.CanExecute(null, item)))
            {
                ApplicationCommands.Save.Execute(cancelArgs, item);
                if (cancelArgs.Cancel) break;
            }
              
            if (SelectedItems.Any(i => 
            	ApplicationCommands.Save.CanExecute(null, i)))
            {
                DialogResult = true;
                return;
            }
              
            DialogResult = true;
            OnClosing(sender, new CancelEventArgs());
        };
          
        this[ApplicationCommands.Close].Executed += (sender, args) =>
        {
            DialogResult = args.Parameter == null;
            OnClosing(sender, new CancelEventArgs());
        };
          
        this[ApplicationCommands.SelectAll].Executed += (sender, args) =>
        {
            var isChecked = (bool) args.Parameter;
            foreach (var item in Items)
            {
                if (!SelectedItems.Contains(item) && isChecked) 
                	SelectedItems.Add(item);
                if (SelectedItems.Contains(item) && !isChecked) 
                	SelectedItems.Remove(item);
            }
        };
    }
}


Совсем ничего сложного, однако если изучить сам диалог, то можно обнаружить очень «умное» поведение ListBox'а: все колонки можно переупорядочить, изменить их размер, настроить видимость, выбрать способ сортировки, причём всё это сохраняется даже при перезапуске приложения!
Казалось бы, такая лаконичная вью-модель и такое поведение у представления… А всё это и реализовано с помощью тех самых неявных (индексных)-свойств. Найдите файл в каталоге с программой SaveViewModel.json, в нём вы и обнаружите их около 30…
У меня нет такого ощущения.

Я предлагаю использовать паттерн в зависимоти от вашего желания, а нет от способа хранения.

Мне вот просто интересно… Предложите, пожалуйста, более красивый подход для хранения 100 параметров UI, не создавая их руками в БД?

Совершенно не считаю себя гуру и думаю, что у каждого своя голова на плечах. Не понравится — не будут использовать, а понравится, так и отлично.

Приведённый подход совершенно не требует, чтобы все остальные системы под него прогибались.
И я совершенно не понимаю, что вызывает в вас такое сопротивление. Ничего личного, но, на мой взгляд, вы чуткий человек, однак вам самим нужно быть более гибким.
Если вы хотите, то создавайте в БД таблицы с кучей столбцов или параметров, можете и вовсе не использовать подход из статьи. Вы хотите доказать мне и всем, что я не прав?
Да, хорошо, я не прав. Но пускай читатели сами оценят те материалы, которые я привёл. Никто никого ни к чему не принуждает. Вы обладаете полной свободой своего выбора!
> Я и говорю, что ваш паттерн не работает, пока все системы вокруг под него не прогнуться

Не подменяйте понятия, это характеризует вас не с лучшей стороны.
Думайте масштабнее и прослушайте видеоролик из предыдущего моего поста. Не хочу спорить впустую.
Раз на то пошло, то вы уж всю песню ещё раз прослушайте =)

Пессимизм или оптимизм — выбирать всегда вам.

P.S. Что ещё тебе рассказать...
Не стоит прогибаться под изменчивый мир-
Пусть лучше он прогнётся под нас,
Однажды он прогнётся под нас.

Машина Времени
Мой вам совет — ничего не используйте из этой статьи. Это вам не подходит.
По-моему, достаточно аргументов, чтобы обсудить и видоизменить ТЗ, если его, конечно, писали знающие люди. А если вас и слушать не хотят, то повод задуматься…
Я вам пример про сломанный файл привёл лишь для того, чтобы подчеркнуть стабильность идеи сериализации.
Понятно, что если вы хотите сделать валидацию, то придётся написать несложную утилиту для этого.

> Сложность в том, что вы мне указываете как хранить данные потому, что ваш паттерн на другую модель хранения нормально не ляжет.

Допустим, у меня на сложном представлении до 100 параметров, которые нужно сохранить, вы предлагаете сделать для этого в БД таблицу с сотней столбцов? Ради чего?

Даже в текстовом редакторе на некоторых представлениях число настроек у меня доходило до 30-50.
Зачем усложнять себе жизнь, когда можно сделать намного проще и тоже достаточно красиво?!

Information

Rating
Does not participate
Location
Беларусь
Registered
Activity