Threefish selber implementieren


  • Gesperrt

    Hallo, ich hab aus Übungszwecken nach einem Verschlüsselungsalgorithmus gesucht, den man einfach selber implementieren kann (und der auch sicher ist), und bin dann bei Treefish gelandet.

    So weit, so gut. Ich möchte einen string ver- und entschlüsseln können. Hier ist mein Versuch:

    #include <cstdint>
    #include <cstring>
    #include <iostream>
    #include <sstream>
    
    using namespace std;
    
    typedef uint8_t u8;
    typedef uint64_t u64;
    
    u64 rl64(u64 x, u8 r)
    {
        return (x << (r & 63)) | (x >> (64 - (r & 63)));
    }
    
    u64 rr64(u64 x, u8 r)
    {
        return (x >> (r & 63)) | (x << (64 - (r & 63)));
    }
    
    void mix(u64 a, u64 b, u64 *c, u64 *d, u8 r)
    {
        u64 tmp = a + b;
        *d = rl64(b, r) ^ tmp;
        *c = tmp;
    }
    
    void mixinv(u64 c, u64 d, u64 *a, u64 *b, u8 r)
    {
        u64 tmp = rr64(c ^ d, r);
        *a = c - tmp;
        *b = tmp;
    }
    
    void threefish_round(unsigned nwords, u64 v[], u8 r[], u8 p[])
    {
        for (unsigned w = 0; w < nwords; w += 2)
        {
            mix(v[p[w]], v[p[w + 1]], &v[p[w]], &v[p[w + 1]], r[w / 2]);
        }
    }
    
    void threefish_roundinv(unsigned nwords, u64 v[], u8 r[], u8 p[])
    {
        for (unsigned w = 0; w < nwords; w += 2)
        {
            mixinv(v[p[w]], v[p[w + 1]], &v[p[w]], &v[p[w + 1]], r[w / 2]);
        }
    }
    
    void encrypt(unsigned nwords, u64 plaintext[], u64 ciphertext[])
    {
        u8 key[nwords] = "haallo";
    
        unsigned nrounds = 72;
        u8 rot[8][2] = {
            {14, 16},
            {52, 57},
            {23, 40},
            {5, 37},
            {25, 33},
            {46, 12},
            {58, 22},
            {32, 32},
        };
        u8 perm[4][4] = {
            {0, 1, 2, 3},
            {0, 3, 2, 1},
            {0, 1, 2, 3},
            {0, 3, 2, 1},
        };
        u64 subkeys[nrounds / 4 + 1][nwords];
    
        u64 tweak[2] = {0};
        u64 xkey[nwords + 1];
        u64 xtweak[3] = {tweak[0], tweak[1], tweak[0] ^ tweak[1]};
        xkey[nwords] = 0xcafebabecafe;
        for (unsigned w = 0; w < nwords; w++)
        {
            xkey[w] = key[w];
            xkey[nwords] ^= key[w];
        }
    
        // expand the key
        for (unsigned i = 0; i < nrounds / 4 + 1; i++)
        {
            for (unsigned w = 0; w < nwords; w++)
            {
                subkeys[i][w] = xkey[(i + w) % (nwords + 1)];
            }
            subkeys[i][nwords - 3] += xtweak[i % 3];
            subkeys[i][nwords - 2] += xtweak[(i + 1) % 3];
            subkeys[i][nwords - 1] += i;
        }
    
        u64 v[nwords];
        for (unsigned w = 0; w < nwords; w++)
        {
            v[w] = plaintext[w];
        }
        for (unsigned n = 0; n < nrounds; n += 8)
        {
            for (unsigned w = 0; w < nwords; w++)
            {
                v[w] += subkeys[n / 4][w];
            }
            threefish_round(nwords, v, rot[(n + 0) % 8], perm[0]);
            threefish_round(nwords, v, rot[(n + 1) % 8], perm[1]);
            threefish_round(nwords, v, rot[(n + 2) % 8], perm[2]);
            threefish_round(nwords, v, rot[(n + 3) % 8], perm[3]);
    
            for (unsigned w = 0; w < nwords; w++)
            {
                v[w] += subkeys[n / 4 + 1][w];
            }
            threefish_round(nwords, v, rot[(n + 4) % 8], perm[0]);
            threefish_round(nwords, v, rot[(n + 5) % 8], perm[1]);
            threefish_round(nwords, v, rot[(n + 6) % 8], perm[2]);
            threefish_round(nwords, v, rot[(n + 7) % 8], perm[3]);
        }
        for (unsigned w = 0; w < nwords; w++)
        {
            ciphertext[w] = v[w] + subkeys[nrounds / 4][w];
        }
    }
    
    void decrypt(unsigned nwords, u64 plaintext[], u64 ciphertext[])
    {
        u8 key[nwords] = "haallo";
    
        unsigned nrounds = 72;
        u8 rot[8][2] = {
            {14, 16},
            {52, 57},
            {23, 40},
            {5, 37},
            {25, 33},
            {46, 12},
            {58, 22},
            {32, 32},
        };
        u8 perm[4][4] = {
            {0, 1, 2, 3},
            {0, 3, 2, 1},
            {0, 1, 2, 3},
            {0, 3, 2, 1},
        };
        u64 subkeys[nrounds / 4 + 1][nwords];
    
        u64 tweak[2] = {0};
        u64 xkey[nwords + 1];
        u64 xtweak[3] = {tweak[0], tweak[1], tweak[0] ^ tweak[1]};
        xkey[nwords] = 0xcafebabecafe;
        for (unsigned w = 0; w < nwords; w++)
        {
            xkey[w] = key[w];
            xkey[nwords] ^= key[w];
        }
    
        // expand the key
        for (unsigned i = 0; i < nrounds / 4 + 1; i++)
        {
            for (unsigned w = 0; w < nwords; w++)
            {
                subkeys[i][w] = xkey[(i + w) % (nwords + 1)];
            }
            subkeys[i][nwords - 3] += xtweak[i % 3];
            subkeys[i][nwords - 2] += xtweak[(i + 1) % 3];
            subkeys[i][nwords - 1] += i;
        }
    
        u64 v[nwords];
        for (unsigned w = 0; w < nwords; w++)
        {
            v[w] = ciphertext[w] - subkeys[nrounds / 4][w];
        }
        for (unsigned n = nrounds; n > 0;)
        {
            n -= 8;
    
            threefish_roundinv(nwords, v, rot[(n + 7) % 8], perm[3]);
            threefish_roundinv(nwords, v, rot[(n + 6) % 8], perm[2]);
            threefish_roundinv(nwords, v, rot[(n + 5) % 8], perm[1]);
            threefish_roundinv(nwords, v, rot[(n + 4) % 8], perm[0]);
            for (unsigned w = 0; w < nwords; w++)
            {
                v[w] -= subkeys[n / 4 + 1][w];
            }
    
            threefish_roundinv(nwords, v, rot[(n + 3) % 8], perm[3]);
            threefish_roundinv(nwords, v, rot[(n + 2) % 8], perm[2]);
            threefish_roundinv(nwords, v, rot[(n + 1) % 8], perm[1]);
            threefish_roundinv(nwords, v, rot[(n + 0) % 8], perm[0]);
            for (unsigned w = 0; w < nwords; w++)
            {
                v[w] -= subkeys[n / 4][w];
            }
        }
        for (unsigned w = 0; w < nwords; w++)
        {
            plaintext[w] = v[w];
        }
    }
    
    void encrypt_string(string s, u64 ciphertext[])
    {
        unsigned n1 = s.size();
        u64 plaintext[n1] = {0};
        for (size_t i = 0; i < n1; i++)
        {
            plaintext[i] = s[i];
        }
        encrypt(n1, plaintext, ciphertext);
    }
    
    string decrypt_string(unsigned n1, u64 ciphertext[])
    {
        u64 plaintext[n1];
        decrypt(n1, plaintext, ciphertext);
        string r = "";
        for (size_t i = 0; i < n1; i++)
        {
            r += (u8)plaintext[i];
        }
        return r;
    }
    
    int main()
    {
        string s1 = "Moin";
        u64 ciphertext[s1.size()] = {0};
        cout << s1 << endl;
        encrypt_string(s1, ciphertext);
        string s2 = decrypt_string(s1.size(), ciphertext);
        cout << s2 << endl;
        return 0;
    }
    

    Das Problem ist, wenn ich "Moin" in zum Beispiel "Moinn" ändere, kommt Kauderwelsch heraus...

    Ich vermute, dass etwas mit dem tweak (Zeile 74 und 150) und xkey (Zeile 77 und 153) nicht stimmt - ist der tweak tatsächlich 0 und darf man den xkey beliebig wählen? Ich glaub nicht...

    Und... vielleicht eine peinliche Frage, aber wie wandelt man einen string korrekt in ein uint64_t [] um - und vice versa?

    Danke fürs Schauen im Voraus - denke mal, da ist nur ein relativ banaler Fehler enthalten...



  • @EinNutzer0 Das ist, bis auf die eine Verwendung von von string und ´´´cout´´´ kein C++, sonder C,
    Du hast dich immer ncoh nicht über const informiert und du solltest den Umgang mit einem Debugger lernen.

    Ohne den Code genauer zu lesen, kryptographisch ist der hinfällig, sobald du den Key da fest einprogrammierst.



  • Ich bin nicht sicher ob der Threefish dabei ist, aber für für Windows gibts ne Software names Cryptool 2. Dort kannst du mit Krypto-Algorithmen rumspielen. Beim AES nach Rijndael kann man im Einzelschritt den kompletten Algorithmus visualisiert anschauen (so detaliert habe ich das auch in keinem Video je gesehen). Evtl gibts das dort auch für Threefish.



  • @EinNutzer0

    Ich habe nur mal schnell drübergeschaut:

    • void decrypt(unsigned nwords, u64 plaintext[], u64 ciphertext[])
      {
        u8 key[nwords] = "haallo";  
      
      Der Code ist falsch. Der Parameter nwords gehört per se nicht zu key. Wenn nwords größer ist als 6, greifst du auf nicht initialisierten Speicher zu.
    • Wo ist dein Key Schedule? Mir fehlt da die Konstante C = 0x1BD11BD...
    • Sollte der Key nicht als Parameter übergeben werden?
    • Verwende Zeiger nur dann, wenn du diese unbedingt benötigst. Bei der Funktion mix z.B. würde auch eine Referenz genügen.
    • Lerne Const Correctness. Was konstant ist, kann man versehentlich nicht ändern.
    • Verwende bitte kein VLA. Visual Studio mag diese nicht.
    • Achte bitte auf sorgsame Typ-Definitionen. Nichts ist schlimmer als eine undokumentiertes char Flags.
      • Deine unsigned Variablen sollten vermutlich vom Typ size_t sein.
      • Entspricht u64 nicht eigentlich einem Block? Wie sieht das Padding aus? (Stichwort OPENSSL_PKCS1_PADDING)
      • Warum machst du das typedef uint64_t u64;?
    • Wo sind die Testreihen? Kryptographie ist eine hochkritisches Stück Software, welches sorgfältig getestet werden muss. Erfüllt deine Software die Testvektoren (https://github.com/weidai11/cryptopp/blob/master/TestVectors/threefish.txt) ?

  • Gesperrt

    @Quiche-Lorraine sagte in Threefish selber implementieren:

    Der Code ist falsch. Der Parameter nwords gehört per se nicht zu key. Wenn nwords größer ist als 6, greifst du auf nicht initialisierten Speicher zu.
    Wo ist dein Key Schedule? Mir fehlt da die Konstante C = 0x1BD11BD...
    Sollte der Key nicht als Parameter übergeben werden?

    Danke, ich denke, daran wird es liegen.

    Den Key hatte ich zum Testen erst mal fest "einprogrammiert"... Kann später noch mal schauen.



  • @EinNutzer0 sagte in Threefish selber implementieren:

    Den Key hatte ich zum Testen erst mal fest "einprogrammiert"... Kann später noch mal schauen.

    Zum Testen gibt es Unit Tests. Wennn man sich an Kryptographie begibt, gibt es Dinge die man nicht macht - z.B. Keys oder Zertifikate fest einprogrammieren, auch nicht "nur zum test". Hinterher bleiben die drinnen und man hat die in Produktionscode. Wäre nicht das erste mal.


  • Gesperrt

    Vielleicht hat hier jemand eine Idee - oder sieht etwas, was ich nicht sehe...

    #include <cstdint>
    #include <cstring>
    #include <iostream>
    #include <sstream>
    
    using namespace std;
    
    typedef uint8_t u8;
    typedef uint64_t u64;
    
    size_t get_min_array_size(size_t min_size)
    {
        while (min_size == 0 || min_size % 8 != 0)
        {
            min_size++;
        }
        return min_size / 8;
    }
    
    void string_to_u64_array(size_t min_array_size, string s, u64 a[])
    {
        string s2 = "";
        for (size_t i = 0; i < min_array_size * 8; i++)
        {
            s2 += s[i % s.size()];
        }
        for (size_t i = 0; i < min_array_size; i++)
        {
            for (size_t j = 0; j < 8; j++)
            {
                a[i] <<= 8;
                a[i] |= (u8)s[i * 8 + j];
            }
        }
    }
    
    string u64_array_to_string(size_t array_size, u64 a[])
    {
        string s = "";
        for (size_t i = 0; i < array_size; i++)
        {
            u64 b = a[i];
            s += (char)(u8)b;
            // for (size_t j = 0; j < 8; j++)
            // {
            //     s += (u8)b;
            //     b >>= 8;
            // }
        }
        return s;
    }
    
    u64 rl64(u64 x, u8 r)
    {
        return (x << (r & 63)) | (x >> (64 - (r & 63)));
    }
    
    u64 rr64(u64 x, u8 r)
    {
        return (x >> (r & 63)) | (x << (64 - (r & 63)));
    }
    
    void mix(u64 a, u64 b, u64 *c, u64 *d, u8 r)
    {
        u64 tmp = a + b;
        *d = rl64(b, r) ^ tmp;
        *c = tmp;
    }
    
    void mixinv(u64 c, u64 d, u64 *a, u64 *b, u8 r)
    {
        u64 tmp = rr64(c ^ d, r);
        *a = c - tmp;
        *b = tmp;
    }
    
    void threefish_round(unsigned nwords, u64 v[], u8 r[], u8 p[])
    {
        for (unsigned w = 0; w < nwords; w += 2)
        {
            mix(v[p[w]], v[p[w + 1]], &v[p[w]], &v[p[w + 1]], r[w / 2]);
        }
    }
    
    void threefish_roundinv(unsigned nwords, u64 v[], u8 r[], u8 p[])
    {
        for (unsigned w = 0; w < nwords; w += 2)
        {
            mixinv(v[p[w]], v[p[w + 1]], &v[p[w]], &v[p[w + 1]], r[w / 2]);
        }
    }
    
    void encrypt(unsigned nwords, u64 plaintext[], u64 ciphertext[], string key)
    {
        size_t min_size = get_min_array_size(nwords);
        u64 xkey[min_size + 1] = {0};
        cout << "7" << endl;
        string_to_u64_array(min_size, key, xkey);
        cout << "8" << endl;
    
        unsigned nrounds = 72;
        u8 rot[8][2] = {
            {14, 16},
            {52, 57},
            {23, 40},
            {5, 37},
            {25, 33},
            {46, 12},
            {58, 22},
            {32, 32},
        };
        u8 perm[4][4] = {
            {0, 1, 2, 3},
            {0, 3, 2, 1},
            {0, 1, 2, 3},
            {0, 3, 2, 1},
        };
        u64 subkeys[nrounds / 4 + 1][nwords];
        u64 tweak[2] = {0};
        u64 xtweak[3] = {tweak[0], tweak[1], tweak[0] ^ tweak[1]};
        // expand the key
        for (unsigned i = 0; i < nrounds / 4 + 1; i++)
        {
            for (unsigned w = 0; w < nwords; w++)
            {
                subkeys[i][w] = xkey[(i + w) % (nwords + 1)];
            }
            subkeys[i][nwords - 3] += xtweak[i % 3];
            subkeys[i][nwords - 2] += xtweak[(i + 1) % 3];
            subkeys[i][nwords - 1] += i;
        }
    
        u64 v[nwords];
        for (unsigned w = 0; w < nwords; w++)
        {
            v[w] = plaintext[w];
        }
        for (unsigned n = 0; n < nrounds; n += 8)
        {
            for (unsigned w = 0; w < nwords; w++)
            {
                v[w] += subkeys[n / 4][w];
            }
            threefish_round(nwords, v, rot[(n + 0) % 8], perm[0]);
            threefish_round(nwords, v, rot[(n + 1) % 8], perm[1]);
            threefish_round(nwords, v, rot[(n + 2) % 8], perm[2]);
            threefish_round(nwords, v, rot[(n + 3) % 8], perm[3]);
    
            for (unsigned w = 0; w < nwords; w++)
            {
                v[w] += subkeys[n / 4 + 1][w];
            }
            threefish_round(nwords, v, rot[(n + 4) % 8], perm[0]);
            threefish_round(nwords, v, rot[(n + 5) % 8], perm[1]);
            threefish_round(nwords, v, rot[(n + 6) % 8], perm[2]);
            threefish_round(nwords, v, rot[(n + 7) % 8], perm[3]);
        }
        for (unsigned w = 0; w < nwords; w++)
        {
            ciphertext[w] = v[w] + subkeys[nrounds / 4][w];
        }
    }
    
    void decrypt(unsigned nwords, u64 plaintext[], u64 ciphertext[], string key)
    {
        size_t min_size = get_min_array_size(nwords);
        u64 xkey[min_size + 1] = {0};
        cout << "9" << endl;
        string_to_u64_array(min_size, key, xkey);
        cout << "10" << endl;
    
        unsigned nrounds = 72;
        u8 rot[8][2] = {
            {14, 16},
            {52, 57},
            {23, 40},
            {5, 37},
            {25, 33},
            {46, 12},
            {58, 22},
            {32, 32},
        };
        u8 perm[4][4] = {
            {0, 1, 2, 3},
            {0, 3, 2, 1},
            {0, 1, 2, 3},
            {0, 3, 2, 1},
        };
        u64 subkeys[nrounds / 4 + 1][nwords];
        u64 tweak[2] = {0};
        u64 xtweak[3] = {tweak[0], tweak[1], tweak[0] ^ tweak[1]};
        // expand the key
        for (unsigned i = 0; i < nrounds / 4 + 1; i++)
        {
            for (unsigned w = 0; w < nwords; w++)
            {
                subkeys[i][w] = xkey[(i + w) % (nwords + 1)];
            }
            subkeys[i][nwords - 3] += xtweak[i % 3];
            subkeys[i][nwords - 2] += xtweak[(i + 1) % 3];
            subkeys[i][nwords - 1] += i;
        }
    
        u64 v[nwords];
        for (unsigned w = 0; w < nwords; w++)
        {
            v[w] = ciphertext[w] - subkeys[nrounds / 4][w];
        }
        for (unsigned n = nrounds; n > 0;)
        {
            n -= 8;
    
            threefish_roundinv(nwords, v, rot[(n + 7) % 8], perm[3]);
            threefish_roundinv(nwords, v, rot[(n + 6) % 8], perm[2]);
            threefish_roundinv(nwords, v, rot[(n + 5) % 8], perm[1]);
            threefish_roundinv(nwords, v, rot[(n + 4) % 8], perm[0]);
            for (unsigned w = 0; w < nwords; w++)
            {
                v[w] -= subkeys[n / 4 + 1][w];
            }
    
            threefish_roundinv(nwords, v, rot[(n + 3) % 8], perm[3]);
            threefish_roundinv(nwords, v, rot[(n + 2) % 8], perm[2]);
            threefish_roundinv(nwords, v, rot[(n + 1) % 8], perm[1]);
            threefish_roundinv(nwords, v, rot[(n + 0) % 8], perm[0]);
            for (unsigned w = 0; w < nwords; w++)
            {
                v[w] -= subkeys[n / 4][w];
            }
        }
        for (unsigned w = 0; w < nwords; w++)
        {
            plaintext[w] = v[w];
        }
    }
    
    void encrypt_string(string s, u64 ciphertext[])
    {
        size_t min_size = get_min_array_size(s.size());
        u64 plaintext[min_size + 1] = {0};
        cout << "1" << endl;
        string_to_u64_array(min_size, s, plaintext);
        cout << "2" << endl;
        encrypt(s.size(), plaintext, ciphertext, "hallo");
        cout << "3" << endl;
    }
    
    string decrypt_string(unsigned n1, u64 ciphertext[])
    {
        size_t min_size = get_min_array_size(n1);
        u64 plaintext[n1 + 1] = {0};
        cout << "4" << endl;
        decrypt(n1, plaintext, ciphertext, "hallo");
        cout << "5" << endl;
        string r = u64_array_to_string(n1, plaintext);
        cout << "6" << endl;
        return r;
    }
    
    int main()
    {
        string s1 = "Moinnn";
        u64 ciphertext[s1.size() + 1] = {0};
    
        cout << s1 << endl;
        encrypt_string(s1, ciphertext);
    
        string s2 = decrypt_string(s1.size(), ciphertext);
        cout << s2 << endl;
    
        return 0;
    }
    
    

    Die Ausgabe ist bei mir:

    ./a.out 
    Moinnn
    1
    2
    7
    8
    3
    4
    9
    10
    5
    6
    m��s��
    
    

    Ich würde mich sehr ärgern, wenn jetzt einfach nur ein Konvertierungsfehler von u64 nach char dabei ist...



  • Was soll denn Zeile 43 s += (char)(u8)b; deiner Meinung nach tun?


  • Gesperrt

    @Th69 sagte in Threefish selber implementieren:

    Was soll denn Zeile 43 s += (char)(u8)b; deiner Meinung nach tun?

    Das weiß ich nicht, ich sitze jetzt mehrere Stunden davor und hab den Überblick verloren. 😞



  • Wie von @Schlangenmensch vorgeschlagen, solltest du hier Unit Tests verwenden (so kannst du jede Funktion einzeln auf Korrektheit überprüfen lassen).


  • Gesperrt

    Könnt ihr mir sagen, woher hier diese verdammten, zufälligen Ausgaben herkommen? Ich initialisiere doch jedes Array mit 0. 😥 Bei jedem Starten des Programms gibt es eine andere Ausgabe...

    #include <cstdint>
    #include <cstring>
    #include <iostream>
    #include <sstream>
    
    using namespace std;
    
    typedef uint8_t u8;
    typedef uint64_t u64;
    
    size_t get_min_array_size(size_t min_size)
    {
        while (min_size == 0 || min_size % 8 != 0)
        {
            min_size++;
        }
        return min_size / 8;
    }
    
    void string_to_u64_array(size_t min_array_size, string s1, u64 a[])
    {
        string s2 = "";
        for (size_t i = 0; i < min_array_size * 8; i++)
        {
            s2 += s1[i % s1.size()];
        }
        for (size_t i = 0; i < min_array_size; i++)
        {
            a[i] = 0;
            for (size_t j = 0; j < 8; j++)
            {
                a[i] <<= 8;
                a[i] |= (u8)s2[i * 8 + j];
            }
        }
    }
    
    string u64_array_to_string(size_t min_array_size, u64 a[])
    {
        string s = "";
        for (size_t i = 0; i < min_array_size; i++)
        {
            u64 t = a[i];
            for (size_t j = 0; j < 8; j++)
            {
                s += (u8)t;
                t >>= 8;
            }
        }
        return s;
    }
    
    u64 rl64(u64 x, u8 r)
    {
        return (x << (r & 63)) | (x >> (64 - (r & 63)));
    }
    
    u64 rr64(u64 x, u8 r)
    {
        return (x >> (r & 63)) | (x << (64 - (r & 63)));
    }
    
    void mix(u64 a, u64 b, u64 *c, u64 *d, u8 r)
    {
        u64 tmp = a + b;
        *d = rl64(b, r) ^ tmp;
        *c = tmp;
    }
    
    void mixinv(u64 c, u64 d, u64 *a, u64 *b, u8 r)
    {
        u64 tmp = rr64(c ^ d, r);
        *a = c - tmp;
        *b = tmp;
    }
    
    void threefish_round(size_t nwords, u64 v[], u8 r[], u8 p[])
    {
        for (size_t w = 0; w < nwords; w += 2)
        {
            mix(v[p[w]], v[p[w + 1]], &v[p[w]], &v[p[w + 1]], r[w / 2]);
        }
    }
    
    void threefish_roundinv(size_t nwords, u64 v[], u8 r[], u8 p[])
    {
        for (size_t w = 0; w < nwords; w += 2)
        {
            mixinv(v[p[w]], v[p[w + 1]], &v[p[w]], &v[p[w + 1]], r[(w / 2 + 1) % 2]);
        }
    }
    
    void encrypt(size_t nwords, u64 plaintext[], u64 ciphertext[], string key)
    {
        u64 xkey[nwords] = {0};
        string_to_u64_array(nwords, key, xkey);
    
        size_t nrounds = 72;
        u8 rot[8][2] = {
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
        };
        u8 perm[4][4] = {
            {0, 1, 2, 3},
            {0, 3, 2, 1},
            {0, 1, 2, 3},
            {0, 3, 2, 1},
        };
    
        u64 v[nwords] = {0};
        for (size_t w = 0; w < nwords; w++)
        {
            v[w] = plaintext[w];
        }
        for (size_t n = 0; n < nrounds; n += 8)
        {
            for (size_t w = 0; w < nwords; w++)
            {
                v[w] += xkey[w];
            }
            threefish_round(nwords, v, rot[(n + 0) % 8], perm[0]);
            threefish_round(nwords, v, rot[(n + 1) % 8], perm[1]);
            threefish_round(nwords, v, rot[(n + 2) % 8], perm[2]);
            threefish_round(nwords, v, rot[(n + 3) % 8], perm[3]);
    
            for (size_t w = 0; w < nwords; w++)
            {
                v[w] += xkey[w];
            }
            threefish_round(nwords, v, rot[(n + 4) % 8], perm[0]);
            threefish_round(nwords, v, rot[(n + 5) % 8], perm[1]);
            threefish_round(nwords, v, rot[(n + 6) % 8], perm[2]);
            threefish_round(nwords, v, rot[(n + 7) % 8], perm[3]);
        }
        for (size_t w = 0; w < nwords; w++)
        {
            ciphertext[w] = v[w] + xkey[w];
        }
    }
    
    void decrypt(size_t nwords, u64 plaintext[], u64 ciphertext[], string key)
    {
        u64 xkey[nwords] = {0};
        string_to_u64_array(nwords, key, xkey);
    
        size_t nrounds = 72;
        u8 rot[8][2] = {
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
        };
        u8 perm[4][4] = {
            {0, 1, 2, 3},
            {0, 3, 2, 1},
            {0, 1, 2, 3},
            {0, 3, 2, 1},
        };
    
        u64 v[nwords] = {0};
        for (size_t w = 0; w < nwords; w++)
        {
            v[w] = ciphertext[w] - xkey[w];
        }
        for (int n = nrounds - 8; n >= 0; n -= 8)
        {
    
            threefish_roundinv(nwords, v, rot[(n + 7) % 8], perm[3]);
            threefish_roundinv(nwords, v, rot[(n + 6) % 8], perm[2]);
            threefish_roundinv(nwords, v, rot[(n + 5) % 8], perm[1]);
            threefish_roundinv(nwords, v, rot[(n + 4) % 8], perm[0]);
            for (size_t w = 0; w < nwords; w++)
            {
                v[w] -= xkey[w];
            }
    
            threefish_roundinv(nwords, v, rot[(n + 3) % 8], perm[3]);
            threefish_roundinv(nwords, v, rot[(n + 2) % 8], perm[2]);
            threefish_roundinv(nwords, v, rot[(n + 1) % 8], perm[1]);
            threefish_roundinv(nwords, v, rot[(n + 0) % 8], perm[0]);
            for (size_t w = 0; w < nwords; w++)
            {
                v[w] -= xkey[w];
            }
        }
        for (size_t w = 0; w < nwords; w++)
        {
            plaintext[w] = v[w];
        }
    }
    
    void encrypt_string(string s, u64 ciphertext[])
    {
        string p = "halloooo";
        size_t min_array_size = get_min_array_size(s.size());
        u64 plaintext[min_array_size] = {0};
        string_to_u64_array(min_array_size, s, plaintext);
        encrypt(min_array_size, plaintext, ciphertext, p);
    }
    
    string decrypt_string(size_t min_array_size, u64 ciphertext[])
    {
        string p = "halloooo";
        u64 plaintext[min_array_size] = {0};
        decrypt(min_array_size, plaintext, ciphertext, p);
        return u64_array_to_string(min_array_size, plaintext);
    }
    
    int main()
    {
        string s1 = "moinnnnn";
        size_t min_array_size = get_min_array_size(s1.size());
        u64 ciphertext[min_array_size] = {0};
    
        cout << s1 << endl;
        encrypt_string(s1, ciphertext);
    
        string s2 = decrypt_string(min_array_size, ciphertext);
        cout << s2 << endl;
    
        return 0;
    }
    
    

    Irgendetwas sehe ich dabei nicht - kann mir das nicht erklärn. 😞



  • Schon mal etwas von Debugging gehört?


  • Gesperrt

    @Th69 sagte in Threefish selber implementieren:

    Schon mal etwas von Debugging gehört?

    Dauert zu lange da mit dem Debugger durchzugehen. Hast du auch einen konstruktiven Vorschlag?



  • @EinNutzer0 ernsthaft? Du erwartest, dass wir uns mit deinem Code beschäftigen, machst das aber selbst nicht... Ich bin gerne hilfsbereit, aber jetzt bin ich raus.
    Wenn mit dem Debugger durchgehen zu lange dauert, lern ihn zu benutzen.



  • @EinNutzer0 sagte in Threefish selber implementieren:

    Dauert zu lange da mit dem Debugger durchzugehen.

    🤣



  • Der Code compiliert mit clang nicht einmal.

    einnutzer0.cpp:95:14: error: variable-sized object may not be initialized
        u64 xkey[nwords] = {0};
                 ^~~~~~
    einnutzer0.cpp:116:11: error: variable-sized object may not be initialized
        u64 v[nwords] = {0};
              ^~~~~~
    einnutzer0.cpp:149:14: error: variable-sized object may not be initialized
        u64 xkey[nwords] = {0};
                 ^~~~~~
    einnutzer0.cpp:170:11: error: variable-sized object may not be initialized
        u64 v[nwords] = {0};
              ^~~~~~
    einnutzer0.cpp:206:19: error: variable-sized object may not be initialized
        u64 plaintext[min_array_size] = {0};
                      ^~~~~~~~~~~~~~
    einnutzer0.cpp:214:19: error: variable-sized object may not be initialized
        u64 plaintext[min_array_size] = {0};
                      ^~~~~~~~~~~~~~
    einnutzer0.cpp:223:20: error: variable-sized object may not be initialized
        u64 ciphertext[min_array_size] = {0};
                       ^~~~~~~~~~~~~~
    

  • Gesperrt

    Habs, wenn beide Länge 8 haben, funktioniert es schon mal...

    #include <cstdint>
    #include <cstring>
    #include <iostream>
    #include <sstream>
    
    using namespace std;
    
    typedef uint8_t u8;
    typedef uint64_t u64;
    
    size_t min_len_from_string(size_t len)
    {
        while (len <= 72 * 8 || len % 8 != 0)
        {
            len++;
        }
        return len / 8;
    }
    
    size_t min_len_from_string_2(size_t len)
    {
        return len / 8;
    }
    
    void string_to_u64_array(size_t min_array_size, string s1, u64 a[])
    {
        string s2 = "";
        for (size_t i = 0; i < min_array_size * 8; i++)
        {
            s2 += s1[i % s1.length()];
        }
        for (size_t i = 0; i < min_array_size; i++)
        {
            a[i] = 0;
            for (size_t j = 0; j < 8; j++)
            {
                a[i] <<= 8;
                a[i] |= (u8)s2[i * 8 + j];
            }
        }
    }
    
    string u64_array_to_string(size_t min_array_size, u64 a[])
    {
        string s = "";
        for (size_t i = 0; i < min_array_size; i++)
        {
            u64 t = a[i];
            for (size_t j = 0; j < 8; j++)
            {
                u8 c = (u8)t;
                s.insert(0,1,c);
                t >>= 8;
            }
        }
        return s;
    }
    
    u64 rl64(u64 x, u8 r)
    {
        return (x << (r & 63)) | (x >> (64 - (r & 63)));
    }
    
    u64 rr64(u64 x, u8 r)
    {
        return (x >> (r & 63)) | (x << (64 - (r & 63)));
    }
    
    void mix(u64 a, u64 b, u64 *c, u64 *d, u8 r)
    {
        u64 tmp = a + b;
        *d = rl64(b, r) ^ tmp;
        *c = tmp;
    }
    
    void mixinv(u64 c, u64 d, u64 *a, u64 *b, u8 r)
    {
        u64 tmp = rr64(c ^ d, r);
        *a = c - tmp;
        *b = tmp;
    }
    
    void threefish_round(size_t nwords, u64 v[], u8 r[], u8 p[])
    {
        for (size_t w = 0; w < nwords; w += 2)
        {
            mix(v[p[w]], v[p[w + 1]], &v[p[w]], &v[p[w + 1]], r[w / 2]);
        }
    }
    
    void threefish_roundinv(size_t nwords, u64 v[], u8 r[], u8 p[])
    {
        for (size_t w = 0; w < nwords; w += 2)
        {
            mixinv(v[p[w]], v[p[w + 1]], &v[p[w]], &v[p[w + 1]], r[w / 2]);
        }
    }
    
    void encrypt(size_t nwords, u64 plaintext[], u64 ciphertext[], string key)
    {
        u64 xkey[nwords] = {0};
        string_to_u64_array(nwords, key, xkey);
    
        size_t nrounds = 72;
        u8 rot[8][2] = {
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
        };
        u8 perm[4][4] = {
            {0, 1, 2, 3},
            {0, 3, 2, 1},
            {0, 1, 2, 3},
            {0, 3, 2, 1},
        };
    
        u64 v[nwords] = {0};
        for (size_t w = 0; w < nwords; w++)
        {
            v[w] = plaintext[w];
        }
        for (size_t n = 0; n < nrounds; n += 8)
        {
            for (size_t w = 0; w < nwords; w++)
            {
                v[w] += xkey[w];
            }
            threefish_round(nwords, v, rot[(n + 0) % 8], perm[0]);
            threefish_round(nwords, v, rot[(n + 1) % 8], perm[1]);
            threefish_round(nwords, v, rot[(n + 2) % 8], perm[2]);
            threefish_round(nwords, v, rot[(n + 3) % 8], perm[3]);
    
            for (size_t w = 0; w < nwords; w++)
            {
                v[w] += xkey[w];
            }
            threefish_round(nwords, v, rot[(n + 4) % 8], perm[0]);
            threefish_round(nwords, v, rot[(n + 5) % 8], perm[1]);
            threefish_round(nwords, v, rot[(n + 6) % 8], perm[2]);
            threefish_round(nwords, v, rot[(n + 7) % 8], perm[3]);
        }
        for (size_t w = 0; w < nwords; w++)
        {
            ciphertext[w] = v[w] + xkey[w];
        }
    }
    
    void decrypt(size_t nwords, u64 plaintext[], u64 ciphertext[], string key)
    {
        u64 xkey[nwords] = {0};
        string_to_u64_array(nwords, key, xkey);
    
        size_t nrounds = 72;
        u8 rot[8][2] = {
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
            {0, 0},
        };
        u8 perm[4][4] = {
            {0, 1, 2, 3},
            {0, 3, 2, 1},
            {0, 1, 2, 3},
            {0, 3, 2, 1},
        };
    
        u64 v[nwords] = {0};
        for (size_t w = 0; w < nwords; w++)
        {
            v[w] = ciphertext[w] - xkey[w];
        }
        for (int n = nrounds - 8; n >= 0; n -= 8)
        {
    
            threefish_roundinv(nwords, v, rot[(n + 7) % 8], perm[3]);
            threefish_roundinv(nwords, v, rot[(n + 6) % 8], perm[2]);
            threefish_roundinv(nwords, v, rot[(n + 5) % 8], perm[1]);
            threefish_roundinv(nwords, v, rot[(n + 4) % 8], perm[0]);
            for (size_t w = 0; w < nwords; w++)
            {
                v[w] -= xkey[w];
            }
    
            threefish_roundinv(nwords, v, rot[(n + 3) % 8], perm[3]);
            threefish_roundinv(nwords, v, rot[(n + 2) % 8], perm[2]);
            threefish_roundinv(nwords, v, rot[(n + 1) % 8], perm[1]);
            threefish_roundinv(nwords, v, rot[(n + 0) % 8], perm[0]);
            for (size_t w = 0; w < nwords; w++)
            {
                v[w] -= xkey[w];
            }
        }
        for (size_t w = 0; w < nwords; w++)
        {
            plaintext[w] = v[w];
        }
    }
    
    void encrypt_string(size_t len, size_t min_array_size, size_t min_string_size ,   string s, u64 ciphertext[])
    {
        string p = "Buchstab";
        u64 plaintext[min_array_size] = {0};
        string_to_u64_array(len, s, plaintext);
        encrypt(len, plaintext, ciphertext, p);
    }
    
    string decrypt_string(size_t len, size_t min_array_size, size_t min_string_size ,   u64 ciphertext[])
    {
        string p = "Buchstab";
        u64 plaintext[min_array_size] = {0};
        decrypt(len, plaintext, ciphertext, p);
        return u64_array_to_string(min_string_size, plaintext);
    }
    
    int main()
    {
        string s1 = "HhaLLoio";
        size_t len = s1.length();
        size_t min_array_size = min_len_from_string(len);
        size_t min_string_size = min_len_from_string_2(len);
        u64 ciphertext[min_array_size] = {0};
    
        cout << s1 << endl;
        encrypt_string(len, min_array_size, min_string_size,  s1, ciphertext);
    
        string s2 = decrypt_string(len, min_array_size, min_string_size,  ciphertext);
        cout << s2 << endl;
    
        return 0;
    }
    
    

    Das Problem war, die string Länge war zu klein, und das Ergebnis wurde umgekehrt herum zusammengesetzt...



  • @EinNutzer0 sagte in Threefish selber implementieren:

    Dauert zu lange da mit dem Debugger durchzugehen. Hast du auch einen konstruktiven Vorschlag?

    Dann hast du einen zentralen Punkt der Software-Entwicklung nicht verstanden.

    Lern Lesen! Oder warum glaubst du dass ein Testvektor mit Nullen beginnt? Siehe auch letzten Beitrag von mir.

    Fehlerreduktion erreiche ich nicht durch scharfes Ansehen des Codes (Stichwort Fehlerblindheit) sondern durch:

    • Testreihen / Unittests -> Liefert der Code das richtige Ergebnis?
    • Assertions -> Sind meine Annahmen korrekt?
    • Debuggen -> Was geht im Code ab?

    PS:
    Ich koche ja auch nicht ohne abzuschmecken.



  • @EinNutzer0 sagte in Threefish selber implementieren:

    @Th69 sagte in Threefish selber implementieren:

    Schon mal etwas von Debugging gehört?

    Dauert zu lange da mit dem Debugger durchzugehen. Hast du auch einen konstruktiven Vorschlag?

    Die Anforderung an ein Minimalbeispiel hier im Forum kommt nicht von ungefähr. Auch im täglichen Business ist es sinnvoll das Problem soweit zu reduzieren, bzw. zu extrahieren, bis es überschaubar ist. Bei deinem Mini-Quellcode ist das noch recht einfach. Nutze die Zeit und lerne Debuggen. Als Entwickler sollte man seinen Debugger mindestens so gut kennen, wie den Rest der IDE.

    1. Abgesehen davon: "Lerne Debuggen" ist ein konstruktiver Vorschlag.
    2. Du bist noch lange nicht so gut, dass du es dir erlauben kannst, den alten Hasen vorzuwerfen, sie wären nicht konstruktiv. 😉

  • Gesperrt

    Mal etwas anderes:

    https://i.postimg.cc/hjdsGS38/grafik.png

    Was muss ich hier umstellen, damit mich Visual Studio nicht anmeckert, dass nur Arrays mit konstanten Werten erstellt werden dürfen? Ich vermute, das ist nicht ISO-Standard...

    Na ja, und zurück zum ursprünglichen Problem... Ich glaube, dass es sinnvoller ist, alles neu zu schreiben, anstatt mit dem Debugger (mehrere) Nadeln im Heuhaufen zu suchen...

    ... um etwas dabei zu lernen, auf jeden Fall. 🙂


  • Mod

    Gar nicht, denn das gibt es in C++ schlichtweg nicht, weil wofür? Selbst in C wurden die wieder abgeschafft. Und wenn du altes C machen würdest, dann sind die auch anders gedacht als du denkst, aber das ist erst einmal egal, weil du ja kein altes C machst.


Anmelden zum Antworten