Троичная логика и обратимые вычисления

Discussion in 'Криптография, расшифровка хешей' started by CommandorCream, 10 Sep 2017.

  1. CommandorCream

    Joined:
    21 May 2017
    Messages:
    2
    Likes Received:
    6
    Reputations:
    0
    ТРОИЧНАЯ ЛОГИКА

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

    В классической логике высказываний каждому суждению приписывалось значение истинности, 1 = истина, 0 = ложь. Кроме суждений с известными значениями истинности рассматривались логические связки AND, OR, XOR, IMPLY, для построения сложных суждений из простых. Изучались около десяти законов логики - правил вывода истинных суждений из имеющихся истинных, записываемых в нотации с большой горизонтальной чертой. Демонстрации доказательств записывались в виде дерева логического вывода, листья которого соответствовали фактам, истинным суждениям, а корень дерева - окончательному выводу из фактов. Связям между вершинами дерева логического вывода соответствовали правила вывода, аналогично состояниям и действиям в графе состояний в искусственном интеллекте.

    В современной логике атомарным объектом является не суждение, а понятие. Суждение получается из понятий при помощи теоретико-множественных операций над объёмами понятий. Это даёт формальную модель суждения, нужную для программ искусственного интеллекта, как то говорящие чат-боты с алгоритмами логического вывода. Имея модели объёмов понятий и суждений и используя операции теории множеств, можно как устанавливать логическое следование между суждениями и понятиями, так и порождать новые суждения и понятия при помощи операций теории множеств. Поэтому, современная логика основывается на теории множеств и является логикой классов, где класс есть синонимом термина множество.

    Более примитивная математическая логика нашла приложение в схемотехнике и компьютерных науках. В этой логике значениями истинности являются 0 и 1, это известная всем программистам булева логика. Она используется и в построении выражения в компьютерных программах и в прототипировании цифровых схем по конечным автоматам в цифровой схемотехнике. Это одна из самых простых логик, из-за чего в ней не получить весомых логических результатов, так-же и и-за ошибки в модели логического следования. Например, в этой логике по воскресеньям истинно суждение "если сегодня понедельник, то сегодня вторник". То есть A влечёт не A и не выполняется закон исключённого третьего. Ошибочна не вся эта логика, а лишь модель импликации - логического следования одного суждения из другого. Однако, несмотря на этот логический дефект, булева логика получила всеобщее распространение и применение и с этим историческим недоумением приходится сосуществовать в мире схемотехники и программирования.

    Троичная логика обратимых программ расширяет булеву логику третьим значением истинности - неопределённостью между 0 и 1, что можно обозначить как целочисленный интервал [0;1], множеством двух значений {0;1} и просто третьей константой u, от слова undefined. Определения операций с участием третьего значения истинности строятся по принципу не противоречия действиям над двумя значениями истинности, ведь третье значение истинности состоит из первых двух. Итак, троичная логика обратимых программ просто расширяет булеву логику, двоичную логику, третьим значением истинности и переносит действия булевой логики на комбинации аргументов с третьим значением истинности [0;1]. Далее идут прямая и обратная троичные логики, нужные для прямого и обратного выполнения программ в обратимых вычислениях. Заметим, что возможно не единственное построение трёхзначной логики, таких логик может быть столько, сколько и их авторов. Это - вопросы формализации и построений, не противоречащих физике, математике или здравому смыслу. Краткий словарь логических терминов можно найти в брошюре автора по логике надобности, доступной для скачивания в конце страницы.


    ПРЯМАЯ ТРОИЧНАЯ АРИФМЕТИКА

    Прямая троичная логика оперирует с тремя значениями истинности: 0, 1, [0;1], где последнее значение истинности обозначает неопределённость, любое значение истинности. Как и в двоичной логике, логические связки, побитовые операции, задаются для всех возможных комбинаций аргументов и для них назначается результат действия. Это - табличное определение функции, где для каждой возможной пары аргументов задаётся результат функции. Прямая троичная арифметика используется при прямом выполнении обратимых программ, при выполнении программы сверху-вниз. Ниже приводится прямая троичная арифметика для базисных функций логики.

    NOT
    1. ~0 = 1
    2. ~1 = 0
    3. ~[0;1] = [0;1]
    AND
    1. 0 & 0 = 0
    2. 0 & [0;1] = 0
    3. 0 & 1 = 0
    4. [0;1] & 0 = 0
    5. [0;1] & [0;1] = [0;1]
    6. [0;1] & 1 = [0;1]
    7. 1 & 0 = 0
    8. 1 & [0;1] = [0;1]
    9. 1 & 1 = 1
    OR
    1. 0 | 0 = 0
    2. 0 | [0;1] = [0;1]
    3. 0 | 1 = 1
    4. [0;1] | 0 = [0;1]
    5. [0;1] | [0;1] = [0;1]
    6. [0;1] | 1 = 1
    7. 1 | 0 = 1
    8. 1 | [0;1] = 1
    9. 1 | 1 = 1
    XOR
    1. 0 ^ 0 = 0
    2. 0 ^ [0;1] = [0;1]
    3. 0 ^ 1 = 1
    4. [0;1] ^ 0 = [0;1]
    5. [0;1] ^ [0;1] = [0;1]
    6. [0;1] ^ 1 = [0;1]
    7. 1 ^ 0 = 1
    8. 1 ^ [0;1] = [0;1]
    9. 1 ^ 1 = 0

    ОБРАТНАЯ ТРОИЧНАЯ АРИФМЕТИКА

    Обратная троичная арифметика используется при обратном выполнении программ, выполнении программ снизу-вверх. В двоичном коде для операции (x &= A) = B по результату двоичного оператора B и аргументу действия A надо восстановить предыдущее состояние переменной x. Такое восстановление делается на каждой инструкции обратимой программы при обратном выполнении. Далее приводится табличное задание базисных функций обратной троичной логики, нужное для обратного выполнении обратимых программ, и позволяющее восстановление предыдущих значений переменных при проходе снизу-вверх.

    NOT
    1. ~x = 0 x = 1
    2. ~x = 1 x = 0
    3. ~x = [0;1] x = [0;1]
    AND
    1. x & 0 = 0 x = [0;1]
    2. x & [0;1] = 0 x = 0
    3. x & 1 = 0 x = 0
    4. x & 0 = [0;1] x = [0;1]
    5. x & [0;1] = [0;1] x = [0;1]
    6. x & 1 = [0;1] x = [0;1]
    7. x & 0 = 1 x = [0;1]
    8. x & [0;1] = 1 x = 1
    9. x & 1 = 1 x = 1
    OR
    1. x | 0 = 0 x = 0
    2. x | [0;1] = 0 x = 0
    3. x | 1 = 0 x = [0;1]
    4. x | 0 = [0;1] x = [0;1]
    5. x | [0;1] = [0;1] x = [0;1]
    6. x | 1 = [0;1] x = [0;1]
    7. x | 0 = 1 x = 1
    8. x | [0;1] = 1 x = 1
    9. x | 1 = 1 x = [0;1]
    XOR
    1. x ^ 0 = 0 x = 0
    2. x ^ [0;1] = 0 x = [0;1]
    3. x ^ 1 = 0 x = 1
    4. x ^ 0 = [0;1] x = [0;1]
    5. x ^ [0;1] = [0;1] x = [0;1]
    6. x ^ 1 = [0;1] x = [0;1]
    7. x ^ 0 = 1 x = 1
    8. x ^ [0;1] = 1 x = [0;1]
    9. x ^ 1 = 1 x = 0

    ПРИМЕР СОКРАЩЕНИЯ ПЕРЕБОРА С ИСПОЛЬЗОВАНИЕМ ТРОИЧНОЙ ЛОГИКИ

    В этом примере будет показано, как сократить перебор ключа для получения нужного хэш-значения модифицированной хэш-функции Дженкинсона FAQ6. Эта хэш-функция была выбрана для примера из-за её простоты и немного модифицирована, а именно операция сложения ADD была заменена на операцию побитового исключающего или XOR, из-за того, что библиотека троичной логики пока не поддерживает суммирование. Эта модификация почти ничего не изменила в поведении хэш-функции, ведь и сложение ADD и XOR меняют 0 на 1 и 1 на 0 почти одинаковым образом, если не рассматривать бит переноса из предыдущего разряда при сложении. То есть качество примера после модификации хэш-функции осталось тем-же. Ниже следует код рассматриваемой хэш-функции на языке C#, написанный с использованием вызовов библиотеки троичной логики и обратимых вычислений Ternary Logic Library. Приведённый метод строит в памяти компьютера обратимый код хэш-функции вызовом метода Emulator.Compile(). В коде - четыре тридцати-двух битных слова ключа, по которым рассчитывается хэш-значение с использование вспомогательных переменных, то есть ключ имеет длину 128 битов.

    Code:
    public override void Code()
    
            {
                Name("FAQ6_modified");
                SetInput("key0", new Mask(128u));
                SetInput("key1", new Mask(129u));
                SetInput("key2", new Mask(130u));
                SetInput("key3", new Mask(131u));
                MoveConst("hash", 0);
                for (int i = 0; i < 4; i++)
                {
                    Move("KeyCopy" + i, "key" + i);
                    Xor("KeyCopy" + i, "hash");
                    Move("store" + i, "hash");
                    Move("hash", "KeyCopy" + i);
                    Copy("tmp"+i, "hash");
                    ShiftLeftConst("tmp"+i, 10);
                    Xor("hash", "tmp"+i);
                    Copy("tmp2"+i, "hash");
                    ShiftRightConst("tmp2"+i, 6);
                    Xor("hash", "tmp2"+i);
                }
                Copy("tmp30", "hash");
                ShiftLeftConst("tmp30", 3);
                Xor("hash", "tmp30");
                Copy("tmp31", "hash");
                ShiftRightConst("tmp31", 11);
                Xor("hash", "tmp31");
                Copy("tmp32", "hash");
                ShiftLeftConst("tmp32", 15);
                Xor("hash", "tmp32");
            }

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

    Code:
    static void Main(string[] args)
    
            {
                // создадим эмулятор с обратимым кодом
                Emulator emul = new Program();
                emul.Compile();
                // выведем обратимый код на экран
                Console.WriteLine(emul.Text);
                // выполним код прямо, сверху-вниз
                emul.RunStraight();
                Console.WriteLine(emul.Variables);
                // установим известное хэш-значение на выходе
                emul.SetOutput("hash", new Mask(0xffffffff));
                // установим значения вспомагательных переменных
                emul.SetVariables(new string[] { "tmp30", "tmp31", "tmp32" },
                    new Mask[] { new Mask(11111111u), new Mask(22222222u), new Mask(33333333u)});
                emul.SetVariables(new string[] { "tmp0", "tmp20" }, new Mask[] { new Mask(0x00000000u), new Mask(0x00000000u) });
                emul.SetVariables(new string[] { "tmp1", "tmp21" }, new Mask[] { new Mask(0x00000000u), new Mask(0x00000000u) });
                emul.SetVariables(new string[] { "tmp2", "tmp22" }, new Mask[] { new Mask(0x00000000u), new Mask(0x00000000u) });
                emul.SetVariables(new string[] { "tmp3", "tmp23" }, new Mask[] { new Mask(0x00000000u), new Mask(0x00000000u) });
                emul.SetVariables(new string[] { "store0" }, new Mask[] { new Mask(0u) });
                emul.SetVariables(new string[] { "store1" }, new Mask[] { new Mask(1u) });
                emul.SetVariables(new string[] { "store2" }, new Mask[] { new Mask(2u) });
                emul.SetVariables(new string[] { "store3" }, new Mask[] { new Mask(3u) });
                // выполним программу обратно, снизу-вверх
                emul.RunBackStepByStep();
                // распечатаем найденную маску битов ключа, сокращающую перебор
                Console.WriteLine(emul.Variables);
                Console.WriteLine("cracked successfully");
            }
    Обратное выполнение хэш-функции, по заданному выходу - хэш-значению, рассчитывает вход - ключ хэш-функции. Ниже приводится результат такого обратного выполнения модифицированной хэш-функции Дженкинсона FAQ6, для установленного на выходе хэш-значения 0xffffffffu.

    VARIABLES:

    key0 = uuuuuuuuuu0000000000000000uuuuuu
    key1 = uuuuuuuuuu0000000000000000uuuuuu
    key2 = uuuuuuuuuu0000000000000000uuuuuu
    key3 = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu


    В полученной маске входа известны 48 битов, нулевые значения, их не надо перебирать, перебирая лишь неизвестные биты. Обратный логический вывод известных значений битов сократил перебор в 2^48 раз (два в степени сорок восемь раз), то есть упростил задачу перебора в 260 триллионов раз!

    Ниже приводятся обратимый код модифицированной хэш-функции Дженкинсона и трасса её обратного выполнения, отладочный вывод библиотеки Ternary Logic Library.

    Code:
    CODE:
    
    0)  FAQ6_modified:               
    1) hash <- 0                    
    2) KeyCopy0 <- key0             
    3) KeyCopy0 ^= hash             
    4) store0 <- hash               
    5) hash <- KeyCopy0             
    6) tmp0 := hash                 
    7) tmp0 <<= 10                  
    8) hash ^= tmp0                 
    9) tmp20 := hash                
    10) tmp20 >>= 6                  
    11) hash ^= tmp20                
    12) KeyCopy1 <- key1             
    13) KeyCopy1 ^= hash             
    14) store1 <- hash               
    15) hash <- KeyCopy1             
    16) tmp1 := hash                 
    17) tmp1 <<= 10                  
    18) hash ^= tmp1                 
    19) tmp21 := hash                
    20) tmp21 >>= 6                  
    21) hash ^= tmp21                
    22) KeyCopy2 <- key2             
    23) KeyCopy2 ^= hash             
    24) store2 <- hash               
    25) hash <- KeyCopy2             
    26) tmp2 := hash                 
    27) tmp2 <<= 10                  
    28) hash ^= tmp2                 
    29) tmp22 := hash                
    30) tmp22 >>= 6                  
    31) hash ^= tmp22                
    32) KeyCopy3 <- key3             
    33) KeyCopy3 ^= hash             
    34) store3 <- hash               
    35) hash <- KeyCopy3             
    36) tmp3 := hash                 
    37) tmp3 <<= 10                  
    38) hash ^= tmp3                 
    39) tmp23 := hash                
    40) tmp23 >>= 6                  
    41) hash ^= tmp23                
    42) tmp30 := hash                
    43) tmp30 <<= 3                  
    44) hash ^= tmp30                
    45) tmp31 := hash                
    46) tmp31 >>= 11                 
    47) hash ^= tmp31                
    48) tmp32 := hash                
    49) tmp32 <<= 15                 
    50) hash ^= tmp32     
              
    Running back...
    
    50)  hash ^= tmp32                   hash = 11111110000000110101111110101010
    49) tmp32 <<= 15                    tmp32 = uuuuuuuuuuuuuuu00000001111111001
    48) tmp32 := hash                   hash = uuuuuuuuuuuuuuuu0u0uuu111u1u10uu
    47) hash ^= tmp31                   hash = uuuuuuuuuuuuuuuu0u0uuu100u1u01uu
    46) tmp31 >>= 11                    tmp31 = 100110001010110001110uuuuuuuuuuu
    45) tmp31 := hash                   hash = uuuuuuuuuuuuuuuu0uuuuuuuuuuuuuuu
    44) hash ^= tmp30                   hash = uuuuuuuuuuuuuuuu1uuuuuuuuuuuuuuu
    43) tmp30 <<= 3                     tmp30 = uuu00000000101010011000101011000
    42) tmp30 := hash                   hash = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
    41) hash ^= tmp23                   hash = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
    40) tmp23 >>= 6                     tmp23 = 00000000000000000000000000uuuuuu
    39) tmp23 := hash                   hash = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
    38) hash ^= tmp3                    hash = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
    37) tmp3 <<= 10                     tmp3 = uuuuuuuuuu0000000000000000000000
    36) tmp3 := hash                    hash = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
    35) hash <- KeyCopy3                KeyCopy3 = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
    34) store3 <- hash                  hash = 00000000000000000000000000000011
    33) KeyCopy3 ^= hash                KeyCopy3 = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
    32) KeyCopy3 <- key3                key3 = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
    31) hash ^= tmp22                   hash = 00000000000000000000000000000011
    30) tmp22 >>= 6                     tmp22 = 00000000000000000000000000uuuuuu
    29) tmp22 := hash                   hash = 00000000000000000000000000uuuuuu
    28) hash ^= tmp2                    hash = 00000000000000000000000000uuuuuu
    27) tmp2 <<= 10                     tmp2 = uuuuuuuuuu0000000000000000000000
    26) tmp2 := hash                    hash = uuuuuuuuuu0000000000000000uuuuuu
    25) hash <- KeyCopy2                KeyCopy2 = uuuuuuuuuu0000000000000000uuuuuu
    24) store2 <- hash                  hash = 00000000000000000000000000000010
    23) KeyCopy2 ^= hash                KeyCopy2 = uuuuuuuuuu0000000000000000uuuuuu
    22) KeyCopy2 <- key2                key2 = uuuuuuuuuu0000000000000000uuuuuu
    21) hash ^= tmp21                   hash = 00000000000000000000000000000010
    20) tmp21 >>= 6                     tmp21 = 00000000000000000000000000uuuuuu
    19) tmp21 := hash                   hash = 00000000000000000000000000uuuuuu
    18) hash ^= tmp1                    hash = 00000000000000000000000000uuuuuu
    17) tmp1 <<= 10                     tmp1 = uuuuuuuuuu0000000000000000000000
    16) tmp1 := hash                    hash = uuuuuuuuuu0000000000000000uuuuuu
    15) hash <- KeyCopy1                KeyCopy1 = uuuuuuuuuu0000000000000000uuuuuu
    14) store1 <- hash                  hash = 00000000000000000000000000000001
    13) KeyCopy1 ^= hash                KeyCopy1 = uuuuuuuuuu0000000000000000uuuuuu
    12) KeyCopy1 <- key1                key1 = uuuuuuuuuu0000000000000000uuuuuu
    11) hash ^= tmp20                   hash = 00000000000000000000000000000001
    10) tmp20 >>= 6                     tmp20 = 00000000000000000000000000uuuuuu
    9) tmp20 := hash                   hash = 00000000000000000000000000uuuuuu
    8) hash ^= tmp0                    hash = 00000000000000000000000000uuuuuu
    7) tmp0 <<= 10                     tmp0 = uuuuuuuuuu0000000000000000000000
    6) tmp0 := hash                    hash = uuuuuuuuuu0000000000000000uuuuuu
    5) hash <- KeyCopy0                KeyCopy0 = uuuuuuuuuu0000000000000000uuuuuu
    4) store0 <- hash                  hash = 00000000000000000000000000000000
    3) KeyCopy0 ^= hash                KeyCopy0 = uuuuuuuuuu0000000000000000uuuuuu
    2) KeyCopy0 <- key0                key0 = uuuuuuuuuu0000000000000000uuuuuu
    1) hash <- 0                      
    0) FAQ6_modified:                  
    Итак, в этом не сложном примере показано, как обратное выполнение хэш-функции, в троичной логике, позволяет сократить множество перебираемых ключей в сотни триллионов раз. Обратное выполнение хэш-функции восстанавливает некоторые биты её входа, оставляя другие биты неопределёнными.


    Оригинальный сайт со статьёй
     
    intigam likes this.