2
\$\begingroup\$

I have implemented the Twofish algorithm in C++ with an OOP approach, separation of concerns and RAII idiom for the session keys and S-boxes. I have also followed Bruce Schneier's advice and my implementation only accepts 256-bit user keys. I would like to improve the program and seek your advice on all aspects but in particular any vulnerabilities and how on earth does one defend against side channel attacks?

I have placed the code into a bitbucket repository here: twofish256 repo

I know that one is not supposed to "roll your own crypto" but I want to learn. I bought "Practical Cryptography" then acquired a musty copy of "The Twofish encryption Algorithm", did some maths revision and examined the reference implementations - in particular 1. Twofish_algorithm.java -- 1998 Raif S. Naffah 2. Twofish.h Twofish.cpp -- 2002 by Niels Ferguson

I have verified the program against the vectors in appendices B1 and B2 and timings reveal per-block en/decrpyt times of 2 microseconds on my Macbook Pro.

Here is the header file:

namespace crypto {

class twofish256 final {

    using word = uint32_t;
    using k_vector_t = std::array<word, 4>;
    using sbox_t = std::array<word, 1024>;
    using subkeys_t = std::array<word, 40>;

public:

    using byte = uint8_t;
    using user_key_t = std::array<byte, 32>;
    using session_key_t = std::pair<sbox_t, subkeys_t>;
    using block_t = std::array<byte, 16>;

    twofish256(const user_key_t& user_key);

    block_t encrypt(const block_t& p);

    block_t decrypt(const block_t& c);

    bool assurance();

    ~twofish256() {
        wipe_session_key(session_key);
    }

private:

    session_key_t session_key;

    static const byte P[2][256];

    static const word MDS[4][256];

    static const word SK_STEP = 0x02020202;
    static const word SK_BUMP = 0x01010101;
    static const word SK_ROTL = 9;

    static const word GF256_FDBK =   0x169;
    static const word GF256_FDBK_2 = 0x169 / 2;
    static const word GF256_FDBK_4 = 0x169 / 4;

    static const int RS_GF_FDBK = 0x14D; // field generator

    const std::string FAIL_MSG = "Catastrophic Failure: assurance testing - twofish256 object cannot be constructed!";

    static session_key_t make_session_key(const user_key_t& user_key);

    void wipe_session_key(session_key_t& session_key);

    static int RS_MDS_Encode(word k0, word k1);

    static word RS_rem(word x);

    static inline word g(const sbox_t& sBox, word x, word R );

    static inline word h(word x, k_vector_t& k32);

};

Here is the implementation file:

namespace crypto {

twofish256::twofish256(const user_key_t& user_key) {
    if(!assurance()) throw std::runtime_error(FAIL_MSG);
    session_key = make_session_key(user_key);
}

twofish256::block_t twofish256::encrypt(const block_t& p) {

    const sbox_t& sbox = session_key.first;
    const subkeys_t& skey = session_key.second;

    //plaintext p is split into four 32-bit words
    word x0 = p[0] | p[1] <<  8 | p[2] << 16 | p[3] << 24;
    word x1 = p[4] | p[5] <<  8 | p[6] << 16 | p[7] << 24;
    word x2 = p[8] | p[9] <<  8 | p[10] << 16 | p[11] << 24;
    word x3 = p[12] | p[13] <<  8 | p[14] << 16 | p[15] << 24;

    //these are XORed with the input whitening subkey words (0..3)
    x0 ^= skey[0];
    x1 ^= skey[1];
    x2 ^= skey[2];
    x3 ^= skey[3];

    int t0, t1; //results of the F function
    int k = 8; //encrpyt using the remaining 32 keys (8..39)

    //this is followed by 16 rounds as 8 cycles of key dependant permutations on 2 x 64 bit values (x0, x1) and (x2, x3)
    // T0 = g(R0)
    // T1 = g(ROL(R1, 8))
    // F0 = (T0 + T1 + K[2r+8]) mod 2^32
    // F1 = (T0 + 2T1 + K[2r+9]) mod 2^32
    //stepping through the keys with k++ saves having to calculate 2r+8 and 2r+9
    for (word i = 0; i < 8; ++i) {
         t0 = g(sbox, x0, 0);
         t1 = g(sbox, x1, 3);
         x2 ^= t0 + t1 + skey[k++];
         x2  = x2 >> 1 | x2 << 31;
         x3  = x3 << 1 | x3 >> 31;
         x3 ^= t0 + 2*t1 + skey[k++];

         t0 = g(sbox, x2, 0 );
         t1 = g(sbox, x3, 3 );
         x0 ^= t0 + t1 + skey[k++];
         x0  = x0 >> 1 | x0 << 31;
         x1  = x1 << 1 | x1 >> 31;
         x1 ^= t0 + 2*t1 + skey[k++];
    }

    //XORed with the output whitening subkey words (4..7)
    x2 ^= skey[4];
    x3 ^= skey[5];
    x0 ^= skey[6];
    x1 ^= skey[7];

    //before undoing the last swap and returning the block
    return std::move(block_t {
        (byte) x2, (byte)(x2 >> 8), (byte)(x2 >> 16), (byte)(x2 >> 24),
        (byte) x3, (byte)(x3 >> 8), (byte)(x3 >> 16), (byte)(x3 >> 24),
        (byte) x0, (byte)(x0 >> 8), (byte)(x0 >> 16), (byte)(x0 >> 24),
        (byte) x1, (byte)(x1 >> 8), (byte)(x1 >> 16), (byte)(x1 >> 24),
       });

}

twofish256::block_t twofish256::decrypt(const block_t& c) {

    const sbox_t& sbox = session_key.first;
    const subkeys_t& skey = session_key.second;

    //cyphertext c is split into four swapped 32-bit words
    word x2 = c[0] | c[1] <<  8 | c[2] << 16 | c[3] << 24;
    word x3 = c[4] | c[5] <<  8 | c[6] << 16 | c[7] << 24;
    word x0 = c[8] | c[9] <<  8 | c[10] << 16 | c[11] << 24;
    word x1 = c[12] | c[13] <<  8 | c[14] << 16 | c[15] << 24;

    //reverse the output whitening XORed with subkeys (4..7)
    x2 ^= skey[4];
    x3 ^= skey[5];
    x0 ^= skey[6];
    x1 ^= skey[7];

    int t0, t1; //results of the F function
    int k = 39; //decrpyt using the remaining 32 keys in reverse order (39..8)

    //this is followed by 16 rounds as 8 cycles of key dependant reverser order permutations on 2 x 64 bit values (x0, x1) and (x2, x3)
    // T0 = g(R0)
    // T1 = g(ROL(R1, 8))
    // F0 = (T0 + T1 + K[2r+8]) mod 2^32
    // F1 = (T0 + 2T1 + K[2r+9]) mod 2^32
    //stepping through the keys with k++ saves having to calculate 2r+8 and 2r+9
    for (word i = 0; i < 8; ++i) {
        t0 = g(sbox, x2, 0);
        t1 = g(sbox, x3, 3);
        x1 ^= t0 + 2*t1 + skey[k--];
        x1  = x1 >> 1 | x1 << 31;
        x0  = x0 << 1 | x0 >> 31;
        x0 ^= t0 + t1 + skey[k--];

        t0 = g(sbox, x0, 0);
        t1 = g(sbox, x1, 3);
        x3 ^= t0 + 2*t1 + skey[k--];
        x3  = x3 >> 1 | x3 << 31;
        x2  = x2 << 1 | x2 >> 31;
        x2 ^= t0 + t1 + skey[k--];
    }

    //reverse the input whitening XORed with subkeys (0..3)
    x0 ^= skey[0];
    x1 ^= skey[1];
    x2 ^= skey[2];
    x3 ^= skey[3];

    //before undoing the last swap and returning the block
    return std::move(block_t {
        (byte) x0, (byte)(x0 >> 8), (byte)(x0 >> 16), (byte)(x0 >> 24),
        (byte) x1, (byte)(x1 >> 8), (byte)(x1 >> 16), (byte)(x1 >> 24),
        (byte) x2, (byte)(x2 >> 8), (byte)(x2 >> 16), (byte)(x2 >> 24),
        (byte) x3, (byte)(x3 >> 8), (byte)(x3 >> 16), (byte)(x3 >> 24),
    });
}

bool twofish256::assurance() {
    user_key_t key ={0, 0, 0, 0, 0, 0, 0, 0,
                     0, 0, 0, 0, 0, 0, 0, 0,
                     0, 0, 0, 0, 0, 0, 0, 0,
                     0, 0, 0, 0, 0, 0, 0, 0};
    block_t pt = {0, 0, 0, 0, 0, 0, 0, 0,
                 0, 0, 0, 0, 0, 0, 0, 0};
    block_t ct = {0, 0, 0, 0, 0, 0, 0, 0,
                  0, 0, 0, 0, 0, 0, 0, 0};
    block_t tt;

    //final cipher text after 49 iterations
    block_t ft = {0x37, 0xFE, 0x26, 0xFF, 0x1C, 0xF6, 0x61, 0x75,
                  0xF5, 0xDD, 0xF4, 0xC3, 0x3B, 0x97, 0xA2, 0x05};

    for(int i = 0; i < 49; ++i) {
        memcpy(&key[16], &key, 16);
        memcpy(&key, &pt, 16);
        session_key = make_session_key(key);
        pt = ct;
        ct = encrypt(pt);
        tt = decrypt(ct);
        if(memcmp(&pt, &tt, 16) != 0) return false;
    }
    return !(memcmp(&ct, &ft, 16));
}

//shorthand byte selectors from 32-bit word to simplify large formulae
//1st byte
#define B0(w) (w & 0xFF)
//2nd byte
#define B1(w) ((w >> 8) & 0xFF)
//3rd byte
#define B2(w) ((w >> 16) & 0xFF)
//4th byte
#define B3(w) ((w >> 24) & 0xFF)
//n-th byte
#define Bn(w, n) ((((w) >> (8*(n))) & 0xFF))

twofish256::session_key_t twofish256::make_session_key(const user_key_t& user_key) {
    //Define N = 256 bits key size and k = N/64 giving 3 vectors of length k
    k_vector_t Me; // even 32-bit entities
    k_vector_t Mo; // odd 32-bit entities
    k_vector_t S;  // key bytes in groups of 8 over GF(2^8) in reverse order
    // split user key material into even and odd 32-bit words and
    // compute S-box keys using (12, 8) Reed-Solomon code over GF(256)
    word i, j, offset = 0;
    for (i = 0, j = 3; i < 4; ++i , j--) {
        Me[i] = user_key[offset] |
                user_key[offset + 1] <<  8 |
                user_key[offset + 2] << 16 |
                user_key[offset + 3] << 24;
        Mo[i] = user_key[offset + 4] |
                user_key[offset + 5] <<  8 |
                user_key[offset + 6] << 16 |
                user_key[offset + 7] << 24;
        S[j] = RS_MDS_Encode( Me[i], Mo[i] ); // reverse order
        offset += 8;
   }

    subkeys_t subkeys;
    // compute the 40 expanded subkeys

    word q, A, B;

    for (i = q = 0; i < 20; ++i, q += SK_STEP) {
        A = h(q        , Me ); // A uses even key entities
        B = h(q+SK_BUMP, Mo ); // B uses odd  key entities
        B = B << 8 | B >> 24;
        A += B;
        subkeys[2*i] = A; // combine with a Psuedo Hamard Transformation
        A += B;
        subkeys[2*i + 1] = A << SK_ROTL | A >> (32-SK_ROTL);
    }

    sbox_t sBox;
    //compute the 4 S-Boxes

    word k0 = S[0];
    word k1 = S[1];
    word k2 = S[2];
    word k3 = S[3];
    word b0, b1, b2, b3;

    for (i = 0; i < 256; ++i) {
        b0 = b1 = b2 = b3 = i;

        b0 = (P[1][b0] & 0xFF) ^ B0(k3);
        b1 = (P[0][b1] & 0xFF) ^ B1(k3);
        b2 = (P[0][b2] & 0xFF) ^ B2(k3);
        b3 = (P[1][b3] & 0xFF) ^ B3(k3);

        b0 = (P[1][b0] & 0xFF) ^ B0(k2);
        b1 = (P[1][b1] & 0xFF) ^ B1(k2);
        b2 = (P[0][b2] & 0xFF) ^ B2(k2);
        b3 = (P[0][b3] & 0xFF) ^ B3(k2);

        sBox[        (i << 1)] = MDS[0][(P[0][(P[0][b0] ) ^ B0(k1)]) ^ B0(k0)];
        sBox[    1 + (i << 1)] = MDS[1][(P[0][(P[1][b1] ) ^ B1(k1)]) ^ B1(k0)];
        sBox[0x200 + (i << 1)] = MDS[2][(P[1][(P[0][b2] ) ^ B2(k1)]) ^ B2(k0)];
        sBox[0x201 + (i << 1)] = MDS[3][(P[1][(P[1][b3] ) ^ B3(k1)]) ^ B3(k0)];

    }

    A = B = b0 = b1 = b2 = b3 = 0; //Wipe variables that contained key material

    //combine S-boxes and subkeys as a pair for the session key
    return std::make_pair(sBox, subkeys);
}

void twofish256::wipe_session_key(session_key_t &session_key) {
    for(int i = 0; i < 1024; ++i) session_key.first[i] = 0;
    for(int i = 0; i < 40; ++i) session_key.second[i] = 0;
}

int twofish256::RS_MDS_Encode(word k0, word k1) {
    int r = k1;
    for (int i = 0; i < 4; ++i) { // shift 1 byte at a time
        r = RS_rem( r );
    }
    r ^= k0;
    for (int i = 0; i < 4; ++i) {
        r = RS_rem( r );
    }
    return r;
}

twofish256::word twofish256::RS_rem(word x) {
    word b  =  (x >> 24) & 0xFF;
    word g2 = ((b  <<  1) ^ ( (b & 0x80) != 0 ? RS_GF_FDBK : 0 )) & 0xFF;
    word g3 =  (b >>  1) ^ ( (b & 0x01) != 0 ? (RS_GF_FDBK >> 1) : 0 ) ^ g2 ;
    return (x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b;
}

twofish256::word twofish256::g(const sbox_t& sBox, word x, word R ) {
    return  sBox[        2 * Bn(x, R  )    ] ^
            sBox[        2 * Bn(x, R+1) + 1] ^
            sBox[0x200 + 2 * Bn(x, R+2)    ] ^
            sBox[0x200 + 2 * Bn(x, R+3) + 1];
}

twofish256::word twofish256::h(word x, k_vector_t& L) {
    //works in k (i.e. 4) stages, in each stage:
    //the four bytes (b0..b3)are each passed through the fixed permutation boxes then xored with a byte derived from the list.
    //the bytes are once again passed through a fixed permutation box,
    //finally, the four bytes are multiplied by the MDS matrix
    word b0 = B0(x);
    word b1 = B1(x);
    word b2 = B2(x);
    word b3 = B3(x);
    word l0 = L[0];
    word l1 = L[1];
    word l2 = L[2];
    word l3 = L[3];

     b0 = (P[1][b0] ) ^ B0(l3);
     b1 = (P[0][b1] ) ^ B1(l3);
     b2 = (P[0][b2] ) ^ B2(l3);
     b3 = (P[1][b3] ) ^ B3(l3);

     b0 = (P[1][b0] ) ^ B0(l2);
     b1 = (P[1][b1] ) ^ B1(l2);
     b2 = (P[0][b2] ) ^ B2(l2);
     b3 = (P[0][b3] ) ^ B3(l2);

     return
        MDS[0][(P[0][(P[0][b0] ) ^ B0(l1)] ) ^ B0(l0)] ^
        MDS[1][(P[0][(P[1][b1] ) ^ B1(l1)] ) ^ B1(l0)] ^
        MDS[2][(P[1][(P[0][b2] ) ^ B2(l1)] ) ^ B2(l0)] ^
        MDS[3][(P[1][(P[1][b3] ) ^ B3(l1)] ) ^ B3(l0)];

}

const twofish256::byte twofish256::P[2][256] = {
    { // q0
       0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76,
       0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38,
       0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C,
       0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48,
       0xF2, 0xD0, 0x8B, 0x30, 0x84, 0x54, 0xDF, 0x23,
       0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82,
       0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C,
       0xA6, 0xEB, 0xA5, 0xBE, 0x16, 0x0C, 0xE3, 0x61,
       0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B,
       0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1,
       0xE1, 0xE6, 0xBD, 0x45, 0xE2, 0xF4, 0xB6, 0x66,
       0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7,
       0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA,
       0xEA, 0x77, 0x39, 0xAF, 0x33, 0xC9, 0x62, 0x71,
       0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8,
       0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7,
       0xA1, 0x1D, 0xAA, 0xED, 0x06, 0x70, 0xB2, 0xD2,
       0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90,
       0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB,
       0x9E, 0x9C, 0x52, 0x1B, 0x5F, 0x93, 0x0A, 0xEF,
       0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B,
       0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64,
       0x2A, 0xCE, 0xCB, 0x2F, 0xFC, 0x97, 0x05, 0x7A,
       0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A,
       0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02,
       0xB8, 0xDA, 0xB0, 0x17, 0x55, 0x1F, 0x8A, 0x7D,
       0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72,
       0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34,
       0x6E, 0x50, 0xDE, 0x68, 0x65, 0xBC, 0xDB, 0xF8,
       0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4,
       0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00,
       0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0
    },
    {  // q1
       0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8,
       0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B,
       0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1,
       0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F,
       0x5E, 0xBA, 0xAE, 0x5B, 0x8A, 0x00, 0xBC, 0x9D,
       0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5,
       0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3,
       0xB2, 0x73, 0x4C, 0x54, 0x92, 0x74, 0x36, 0x51,
       0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96,
       0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C,
       0x13, 0x95, 0x9C, 0xC7, 0x24, 0x46, 0x3B, 0x70,
       0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8,
       0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC,
       0x03, 0x6F, 0x08, 0xBF, 0x40, 0xE7, 0x2B, 0xE2,
       0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9,
       0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17,
       0x66, 0x94, 0xA1, 0x1D, 0x3D, 0xF0, 0xDE, 0xB3,
       0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E,
       0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49,
       0x81, 0x88, 0xEE, 0x21, 0xC4, 0x1A, 0xEB, 0xD9,
       0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01,
       0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48,
       0x4F, 0xF2, 0x65, 0x8E, 0x78, 0x5C, 0x58, 0x19,
       0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64,
       0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5,
       0xCE, 0xE9, 0x68, 0x44, 0xE0, 0x4D, 0x43, 0x69,
       0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E,
       0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC,
       0x22, 0xC9, 0xC0, 0x9B, 0x89, 0xD4, 0xED, 0xAB,
       0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9,
       0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2,
       0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91
    }
};

const twofish256::word twofish256::MDS[4][256] = {
{
        0xBCBC3275,0xECEC21F3,0x202043C6,0xB3B3C9F4,0xDADA03DB,0x2028B7B,0xE2E22BFB,0x9E9EFAC8,
        0xC9C9EC4A,0xD4D409D3,0x18186BE6,0x1E1E9F6B,0x98980E45,0xB2B2387D,0xA6A6D2E8,0x2626B74B,
        0x3C3C57D6,0x93938A32,0x8282EED8,0x525298FD,0x7B7BD437,0xBBBB3771,0x5B5B97F1,0x474783E1,
        0x24243C30,0x5151E20F,0xBABAC6F8,0x4A4AF31B,0xBFBF4887,0xD0D70FA,0xB0B0B306,0x7575DE3F,
        0xD2D2FD5E,0x7D7D20BA,0x666631AE,0x3A3AA35B,0x59591C8A,0x00,0xCDCD93BC,0x1A1AE09D,
        0xAEAE2C6D,0x7F7FABC1,0x2B2BC7B1,0xBEBEB90E,0xE0E0A080,0x8A8A105D,0x3B3B52D2,0x6464BAD5,
        0xD8D888A0,0xE7E7A584,0x5F5FE807,0x1B1B1114,0x2C2CC2B5,0xFCFCB490,0x3131272C,0x808065A3,
        0x73732AB2,0xC0C8173,0x79795F4C,0x6B6B4154,0x4B4B0292,0x53536974,0x94948F36,0x83831F51,
        0x2A2A3638,0xC4C49CB0,0x2222C8BD,0xD5D5F85A,0xBDBDC3FC,0x48487860,0xFFFFCE62,0x4C4C0796,
        0x4141776C,0xC7C7E642,0xEBEB24F7,0x1C1C1410,0x5D5D637C,0x36362228,0x6767C027,0xE9E9AF8C,
        0x4444F913,0x1414EA95,0xF5F5BB9C,0xCFCF18C7,0x3F3F2D24,0xC0C0E346,0x7272DB3B,0x54546C70,
        0x29294CCA,0xF0F035E3,0x808FE85,0xC6C617CB,0xF3F34F11,0x8C8CE4D0,0xA4A45993,0xCACA96B8,
        0x68683BA6,0xB8B84D83,0x38382820,0xE5E52EFF,0xADAD569F,0xB0B8477,0xC8C81DC3,0x9999FFCC,
        0x5858ED03,0x19199A6F,0xE0E0A08,0x95957EBF,0x70705040,0xF7F730E7,0x6E6ECF2B,0x1F1F6EE2,
        0xB5B53D79,0x9090F0C,0x616134AA,0x57571682,0x9F9F0B41,0x9D9D803A,0x111164EA,0x2525CDB9,
        0xAFAFDDE4,0x4545089A,0xDFDF8DA4,0xA3A35C97,0xEAEAD57E,0x353558DA,0xEDEDD07A,0x4343FC17,
        0xF8F8CB66,0xFBFBB194,0x3737D3A1,0xFAFA401D,0xC2C2683D,0xB4B4CCF0,0x32325DDE,0x9C9C71B3,
        0x5656E70B,0xE3E3DA72,0x878760A7,0x15151B1C,0xF9F93AEF,0x6363BFD1,0x3434A953,0x9A9A853E,
        0xB1B1428F,0x7C7CD133,0x88889B26,0x3D3DA65F,0xA1A1D7EC,0xE4E4DF76,0x8181942A,0x91910149,
        0xF0FFB81,0xEEEEAA88,0x161661EE,0xD7D77321,0x9797F5C4,0xA5A5A81A,0xFEFE3FEB,0x6D6DB5D9,
        0x7878AEC5,0xC5C56D39,0x1D1DE599,0x7676A4CD,0x3E3EDCAD,0xCBCB6731,0xB6B6478B,0xEFEF5B01,
        0x12121E18,0x6060C523,0x6A6AB0DD,0x4D4DF61F,0xCECEE94E,0xDEDE7C2D,0x55559DF9,0x7E7E5A48,
        0x2121B24F,0x3037AF2,0xA0A02665,0x5E5E198E,0x5A5A6678,0x65654B5C,0x62624E58,0xFDFD4519,
        0x606F48D,0x404086E5,0xF2F2BE98,0x3333AC57,0x17179067,0x5058E7F,0xE8E85E05,0x4F4F7D64,
        0x89896AAF,0x10109563,0x74742FB6,0xA0A75FE,0x5C5C92F5,0x9B9B74B7,0x2D2D333C,0x3030D6A5,
        0x2E2E49CE,0x494989E9,0x46467268,0x77775544,0xA8A8D8E0,0x9696044D,0x2828BD43,0xA9A92969,
        0xD9D97929,0x8686912E,0xD1D187AC,0xF4F44A15,0x8D8D1559,0xD6D682A8,0xB9B9BC0A,0x42420D9E,
        0xF6F6C16E,0x2F2FB847,0xDDDD06DF,0x23233934,0xCCCC6235,0xF1F1C46A,0xC1C112CF,0x8585EBDC,
        0x8F8F9E22,0x7171A1C9,0x9090F0C0,0xAAAA539B,0x101F189,0x8B8BE1D4,0x4E4E8CED,0x8E8E6FAB,
        0xABABA212,0x6F6F3EA2,0xE6E6540D,0xDBDBF252,0x92927BBB,0xB7B7B602,0x6969CA2F,0x3939D9A9,
        0xD3D30CD7,0xA7A72361,0xA2A2AD1E,0xC3C399B4,0x6C6C4450,0x7070504,0x4047FF6,0x272746C2,
        0xACACA716,0xD0D07625,0x50501386,0xDCDCF756,0x84841A55,0xE1E15109,0x7A7A25BE,0x1313EF91
},
{
        0xA9D93939,0x67901717,0xB3719C9C,0xE8D2A6A6,0x4050707,0xFD985252,0xA3658080,0x76DFE4E4,
        0x9A084545,0x92024B4B,0x80A0E0E0,0x78665A5A,0xE4DDAFAF,0xDDB06A6A,0xD1BF6363,0x38362A2A,
        0xD54E6E6,0xC6432020,0x3562CCCC,0x98BEF2F2,0x181E1212,0xF724EBEB,0xECD7A1A1,0x6C774141,
        0x43BD2828,0x7532BCBC,0x37D47B7B,0x269B8888,0xFA700D0D,0x13F94444,0x94B1FBFB,0x485A7E7E,
        0xF27A0303,0xD0E48C8C,0x8B47B6B6,0x303C2424,0x84A5E7E7,0x54416B6B,0xDF06DDDD,0x23C56060,
        0x1945FDFD,0x5BA33A3A,0x3D68C2C2,0x59158D8D,0xF321ECEC,0xAE316666,0xA23E6F6F,0x82165757,
        0x63951010,0x15BEFEF,0x834DB8B8,0x2E918686,0xD9B56D6D,0x511F8383,0x9B53AAAA,0x7C635D5D,
        0xA63B6868,0xEB3FFEFE,0xA5D63030,0xBE257A7A,0x16A7ACAC,0xC0F0909,0xE335F0F0,0x6123A7A7,
        0xC0F09090,0x8CAFE9E9,0x3A809D9D,0xF5925C5C,0x73810C0C,0x2C273131,0x2576D0D0,0xBE75656,
        0xBB7B9292,0x4EE9CECE,0x89F10101,0x6B9F1E1E,0x53A93434,0x6AC4F1F1,0xB499C3C3,0xF1975B5B,
        0xE1834747,0xE66B1818,0xBDC82222,0x450E9898,0xE26E1F1F,0xF4C9B3B3,0xB62F7474,0x66CBF8F8,
        0xCCFF9999,0x95EA1414,0x3ED5858,0x56F7DCDC,0xD4E18B8B,0x1C1B1515,0x1EADA2A2,0xD70CD3D3,
        0xFB2BE2E2,0xC31DC8C8,0x8E195E5E,0xB5C22C2C,0xE9894949,0xCF12C1C1,0xBF7E9595,0xBA207D7D,
        0xEA641111,0x77840B0B,0x396DC5C5,0xAF6A8989,0x33D17C7C,0xC9A17171,0x62CEFFFF,0x7137BBBB,
        0x81FB0F0F,0x793DB5B5,0x951E1E1,0xADDC3E3E,0x242D3F3F,0xCDA47676,0xF99D5555,0xD8EE8282,
        0xE5864040,0xC5AE7878,0xB9CD2525,0x4D049696,0x44557777,0x80A0E0E,0x86135050,0xE730F7F7,
        0xA1D33737,0x1D40FAFA,0xAA346161,0xED8C4E4E,0x6B3B0B0,0x706C5454,0xB22A7373,0xD2523B3B,
        0x410B9F9F,0x7B8B0202,0xA088D8D8,0x114FF3F3,0x3167CBCB,0xC2462727,0x27C06767,0x90B4FCFC,
        0x20283838,0xF67F0404,0x60784848,0xFF2EE5E5,0x96074C4C,0x5C4B6565,0xB1C72B2B,0xAB6F8E8E,
        0x9E0D4242,0x9CBBF5F5,0x52F2DBDB,0x1BF34A4A,0x5FA63D3D,0x9359A4A4,0xABCB9B9,0xEF3AF9F9,
        0x91EF1313,0x85FE0808,0x49019191,0xEE611616,0x2D7CDEDE,0x4FB22121,0x8F42B1B1,0x3BDB7272,
        0x47B82F2F,0x8748BFBF,0x6D2CAEAE,0x46E3C0C0,0xD6573C3C,0x3E859A9A,0x6929A9A9,0x647D4F4F,
        0x2A948181,0xCE492E2E,0xCB17C6C6,0x2FCA6969,0xFCC3BDBD,0x975CA3A3,0x55EE8E8,0x7AD0EDED,
        0xAC87D1D1,0x7F8E0505,0xD5BA6464,0x1AA8A5A5,0x4BB72626,0xEB9BEBE,0xA7608787,0x5AF8D5D5,
        0x28223636,0x14111B1B,0x3FDE7575,0x2979D9D9,0x88AAEEEE,0x3C332D2D,0x4C5F7979,0x2B6B7B7,
        0xB896CACA,0xDA583535,0xB09CC4C4,0x17FC4343,0x551A8484,0x1FF64D4D,0x8A1C5959,0x7D38B2B2,
        0x57AC3333,0xC718CFCF,0x8DF40606,0x74695353,0xB7749B9B,0xC4F59797,0x9F56ADAD,0x72DAE3E3,
        0x7ED5EAEA,0x154AF4F4,0x229E8F8F,0x12A2ABAB,0x584E6262,0x7E85F5F,0x99E51D1D,0x34392323,
        0x6EC1F6F6,0x50446C6C,0xDE5D3232,0x68724646,0x6526A0A0,0xBC93CDCD,0xDB03DADA,0xF8C6BABA,
        0xC8FA9E9E,0xA882D6D6,0x2BCF6E6E,0x40507070,0xDCEB8585,0xFE750A0A,0x328A9393,0xA48DDFDF,
        0xCA4C2929,0x10141C1C,0x2173D7D7,0xF0CCB4B4,0xD309D4D4,0x5D108A8A,0xFE25151,0x00,
        0x6F9A1919,0x9DE01A1A,0x368F9494,0x42E6C7C7,0x4AECC9C9,0x5EFDD2D2,0xC1AB7F7F,0xE0D8A8A8
},
{
        0xBC75BC32,0xECF3EC21,0x20C62043,0xB3F4B3C9,0xDADBDA03,0x27B028B,0xE2FBE22B,0x9EC89EFA,
        0xC94AC9EC,0xD4D3D409,0x18E6186B,0x1E6B1E9F,0x9845980E,0xB27DB238,0xA6E8A6D2,0x264B26B7,
        0x3CD63C57,0x9332938A,0x82D882EE,0x52FD5298,0x7B377BD4,0xBB71BB37,0x5BF15B97,0x47E14783,
        0x2430243C,0x510F51E2,0xBAF8BAC6,0x4A1B4AF3,0xBF87BF48,0xDFA0D70,0xB006B0B3,0x753F75DE,
        0xD25ED2FD,0x7DBA7D20,0x66AE6631,0x3A5B3AA3,0x598A591C,0x00,0xCDBCCD93,0x1A9D1AE0,
        0xAE6DAE2C,0x7FC17FAB,0x2BB12BC7,0xBE0EBEB9,0xE080E0A0,0x8A5D8A10,0x3BD23B52,0x64D564BA,
        0xD8A0D888,0xE784E7A5,0x5F075FE8,0x1B141B11,0x2CB52CC2,0xFC90FCB4,0x312C3127,0x80A38065,
        0x73B2732A,0xC730C81,0x794C795F,0x6B546B41,0x4B924B02,0x53745369,0x9436948F,0x8351831F,
        0x2A382A36,0xC4B0C49C,0x22BD22C8,0xD55AD5F8,0xBDFCBDC3,0x48604878,0xFF62FFCE,0x4C964C07,
        0x416C4177,0xC742C7E6,0xEBF7EB24,0x1C101C14,0x5D7C5D63,0x36283622,0x672767C0,0xE98CE9AF,
        0x441344F9,0x149514EA,0xF59CF5BB,0xCFC7CF18,0x3F243F2D,0xC046C0E3,0x723B72DB,0x5470546C,
        0x29CA294C,0xF0E3F035,0x88508FE,0xC6CBC617,0xF311F34F,0x8CD08CE4,0xA493A459,0xCAB8CA96,
        0x68A6683B,0xB883B84D,0x38203828,0xE5FFE52E,0xAD9FAD56,0xB770B84,0xC8C3C81D,0x99CC99FF,
        0x580358ED,0x196F199A,0xE080E0A,0x95BF957E,0x70407050,0xF7E7F730,0x6E2B6ECF,0x1FE21F6E,
        0xB579B53D,0x90C090F,0x61AA6134,0x57825716,0x9F419F0B,0x9D3A9D80,0x11EA1164,0x25B925CD,
        0xAFE4AFDD,0x459A4508,0xDFA4DF8D,0xA397A35C,0xEA7EEAD5,0x35DA3558,0xED7AEDD0,0x431743FC,
        0xF866F8CB,0xFB94FBB1,0x37A137D3,0xFA1DFA40,0xC23DC268,0xB4F0B4CC,0x32DE325D,0x9CB39C71,
        0x560B56E7,0xE372E3DA,0x87A78760,0x151C151B,0xF9EFF93A,0x63D163BF,0x345334A9,0x9A3E9A85,
        0xB18FB142,0x7C337CD1,0x8826889B,0x3D5F3DA6,0xA1ECA1D7,0xE476E4DF,0x812A8194,0x91499101,
        0xF810FFB,0xEE88EEAA,0x16EE1661,0xD721D773,0x97C497F5,0xA51AA5A8,0xFEEBFE3F,0x6DD96DB5,
        0x78C578AE,0xC539C56D,0x1D991DE5,0x76CD76A4,0x3EAD3EDC,0xCB31CB67,0xB68BB647,0xEF01EF5B,
        0x1218121E,0x602360C5,0x6ADD6AB0,0x4D1F4DF6,0xCE4ECEE9,0xDE2DDE7C,0x55F9559D,0x7E487E5A,
        0x214F21B2,0x3F2037A,0xA065A026,0x5E8E5E19,0x5A785A66,0x655C654B,0x6258624E,0xFD19FD45,
        0x68D06F4,0x40E54086,0xF298F2BE,0x335733AC,0x17671790,0x57F058E,0xE805E85E,0x4F644F7D,
        0x89AF896A,0x10631095,0x74B6742F,0xAFE0A75,0x5CF55C92,0x9BB79B74,0x2D3C2D33,0x30A530D6,
        0x2ECE2E49,0x49E94989,0x46684672,0x77447755,0xA8E0A8D8,0x964D9604,0x284328BD,0xA969A929,
        0xD929D979,0x862E8691,0xD1ACD187,0xF415F44A,0x8D598D15,0xD6A8D682,0xB90AB9BC,0x429E420D,
        0xF66EF6C1,0x2F472FB8,0xDDDFDD06,0x23342339,0xCC35CC62,0xF16AF1C4,0xC1CFC112,0x85DC85EB,
        0x8F228F9E,0x71C971A1,0x90C090F0,0xAA9BAA53,0x18901F1,0x8BD48BE1,0x4EED4E8C,0x8EAB8E6F,
        0xAB12ABA2,0x6FA26F3E,0xE60DE654,0xDB52DBF2,0x92BB927B,0xB702B7B6,0x692F69CA,0x39A939D9,
        0xD3D7D30C,0xA761A723,0xA21EA2AD,0xC3B4C399,0x6C506C44,0x7040705,0x4F6047F,0x27C22746,
        0xAC16ACA7,0xD025D076,0x50865013,0xDC56DCF7,0x8455841A,0xE109E151,0x7ABE7A25,0x139113EF
},
{
        0xD939A9D9,0x90176790,0x719CB371,0xD2A6E8D2,0x5070405,0x9852FD98,0x6580A365,0xDFE476DF,
        0x8459A08,0x24B9202,0xA0E080A0,0x665A7866,0xDDAFE4DD,0xB06ADDB0,0xBF63D1BF,0x362A3836,
        0x54E60D54,0x4320C643,0x62CC3562,0xBEF298BE,0x1E12181E,0x24EBF724,0xD7A1ECD7,0x77416C77,
        0xBD2843BD,0x32BC7532,0xD47B37D4,0x9B88269B,0x700DFA70,0xF94413F9,0xB1FB94B1,0x5A7E485A,
        0x7A03F27A,0xE48CD0E4,0x47B68B47,0x3C24303C,0xA5E784A5,0x416B5441,0x6DDDF06,0xC56023C5,
        0x45FD1945,0xA33A5BA3,0x68C23D68,0x158D5915,0x21ECF321,0x3166AE31,0x3E6FA23E,0x16578216,
        0x95106395,0x5BEF015B,0x4DB8834D,0x91862E91,0xB56DD9B5,0x1F83511F,0x53AA9B53,0x635D7C63,
        0x3B68A63B,0x3FFEEB3F,0xD630A5D6,0x257ABE25,0xA7AC16A7,0xF090C0F,0x35F0E335,0x23A76123,
        0xF090C0F0,0xAFE98CAF,0x809D3A80,0x925CF592,0x810C7381,0x27312C27,0x76D02576,0xE7560BE7,
        0x7B92BB7B,0xE9CE4EE9,0xF10189F1,0x9F1E6B9F,0xA93453A9,0xC4F16AC4,0x99C3B499,0x975BF197,
        0x8347E183,0x6B18E66B,0xC822BDC8,0xE98450E,0x6E1FE26E,0xC9B3F4C9,0x2F74B62F,0xCBF866CB,
        0xFF99CCFF,0xEA1495EA,0xED5803ED,0xF7DC56F7,0xE18BD4E1,0x1B151C1B,0xADA21EAD,0xCD3D70C,
        0x2BE2FB2B,0x1DC8C31D,0x195E8E19,0xC22CB5C2,0x8949E989,0x12C1CF12,0x7E95BF7E,0x207DBA20,
        0x6411EA64,0x840B7784,0x6DC5396D,0x6A89AF6A,0xD17C33D1,0xA171C9A1,0xCEFF62CE,0x37BB7137,
        0xFB0F81FB,0x3DB5793D,0x51E10951,0xDC3EADDC,0x2D3F242D,0xA476CDA4,0x9D55F99D,0xEE82D8EE,
        0x8640E586,0xAE78C5AE,0xCD25B9CD,0x4964D04,0x55774455,0xA0E080A,0x13508613,0x30F7E730,
        0xD337A1D3,0x40FA1D40,0x3461AA34,0x8C4EED8C,0xB3B006B3,0x6C54706C,0x2A73B22A,0x523BD252,
        0xB9F410B,0x8B027B8B,0x88D8A088,0x4FF3114F,0x67CB3167,0x4627C246,0xC06727C0,0xB4FC90B4,
        0x28382028,0x7F04F67F,0x78486078,0x2EE5FF2E,0x74C9607,0x4B655C4B,0xC72BB1C7,0x6F8EAB6F,
        0xD429E0D,0xBBF59CBB,0xF2DB52F2,0xF34A1BF3,0xA63D5FA6,0x59A49359,0xBCB90ABC,0x3AF9EF3A,
        0xEF1391EF,0xFE0885FE,0x1914901,0x6116EE61,0x7CDE2D7C,0xB2214FB2,0x42B18F42,0xDB723BDB,
        0xB82F47B8,0x48BF8748,0x2CAE6D2C,0xE3C046E3,0x573CD657,0x859A3E85,0x29A96929,0x7D4F647D,
        0x94812A94,0x492ECE49,0x17C6CB17,0xCA692FCA,0xC3BDFCC3,0x5CA3975C,0x5EE8055E,0xD0ED7AD0,
        0x87D1AC87,0x8E057F8E,0xBA64D5BA,0xA8A51AA8,0xB7264BB7,0xB9BE0EB9,0x6087A760,0xF8D55AF8,
        0x22362822,0x111B1411,0xDE753FDE,0x79D92979,0xAAEE88AA,0x332D3C33,0x5F794C5F,0xB6B702B6,
        0x96CAB896,0x5835DA58,0x9CC4B09C,0xFC4317FC,0x1A84551A,0xF64D1FF6,0x1C598A1C,0x38B27D38,
        0xAC3357AC,0x18CFC718,0xF4068DF4,0x69537469,0x749BB774,0xF597C4F5,0x56AD9F56,0xDAE372DA,
        0xD5EA7ED5,0x4AF4154A,0x9E8F229E,0xA2AB12A2,0x4E62584E,0xE85F07E8,0xE51D99E5,0x39233439,
        0xC1F66EC1,0x446C5044,0x5D32DE5D,0x72466872,0x26A06526,0x93CDBC93,0x3DADB03,0xC6BAF8C6,
        0xFA9EC8FA,0x82D6A882,0xCF6E2BCF,0x50704050,0xEB85DCEB,0x750AFE75,0x8A93328A,0x8DDFA48D,
        0x4C29CA4C,0x141C1014,0x73D72173,0xCCB4F0CC,0x9D4D309,0x108A5D10,0xE2510FE2,0x00,
        0x9A196F9A,0xE01A9DE0,0x8F94368F,0xE6C742E6,0xECC94AEC,0xFDD25EFD,0xAB7FC1AB,0xD8A8E0D8
}

};

}

This is my first submission for code review & thank you in advance for your feedback.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ There is a difference in implementing your own crypto and implementing an known crypto algorithm. The first is outside nearly everybody's ability the second may not be recommended for real world applications (due to lack of testing and validation) but it can be a good exercise. \$\endgroup\$ – Martin York Nov 12 '17 at 20:24
5
\$\begingroup\$

I'm not qualified to review the actual crypto, but here are some notes (or nits) on the C++ idioms.


class twofish256 final

The final qualifier is relevant basically only if you're using classical polymorphism. It tells the compiler that your virtual methods are never going to be overridden, thus allowing extra optimization possibilities. But in your case you don't use virtual (good!), so you don't need to (and thus shouldn't) use final either.


using user_key_t = std::array<byte, 32>;

twofish256(const user_key_t& user_key);

Two things here: First, as of C++11, all your single-argument constructors should be marked explicit, to make sure that they can't be used accidentally. For example, consider this user code:

void foo(const twofish& engine);
void foo(byte *key);

std::array<byte, 32> k;
foo(k);  // oops, user might have meant "foo(k.data())"

With your non-explicit constructor, this will compile and call foo(const twofish&), because the conversion from std::array<byte, 32> to twofish is implicit, not explicit.

In general, you should allow conversions from type X to type Y only if an object of type X can meaningfully hold "the same value" as an object of type Y: for example, we allow converting int to double, or converting const char* to std::string. You don't want to allow silent implicit conversions where the conversion changes the meaning of the value: for example, changing a key into a Twofish engine.

So, add explicit.

And then backtrack: Why are we limiting ourselves to std::array<byte, 32> for user keys in the first place? I would expect to see at least an extra overload of the constructor — maybe the only constructor — either taking a classic iterator-pair like this:

template<class InputIterator>
explicit twofish256(InputIterator first, InputIterator last) {
    read_32_elements_from first;
    assert(first == last);
}

or taking a sequence in the more modern style:

template<class Sequence, class = enable_if_t<!is_same_v<decay_t<Sequence>, twofish256>>>
explicit twofish256(Sequence&& seq) {
    // as above, but from std::begin(seq) to std::end(seq)
}

Anyway, these signatures are both getting into crazy metaprogramming, but the point is that I want to be able to construct a twofish256 engine from e.g. std::vector<char> or unsigned char(&)[32] or whatever; I don't want to have to copy my key into a std::array in order to build a twofish256.


It is worth noting that wipe_session_key is unlikely to do anything, on modern compilers. It will be inlined into the destructor and then all the dead writes will (or at least may) be removed.

There are attempts out there to write a "SecureAllocator" using the C++ allocator model, which might or might not be relevant to your interests. 1 2 3 By punting the problem up a level to the person creating the twofish256 object, you at least stop needing to worry about it yourself.


FWIW, bool assurance() doesn't convey any intrinsic meaning to me. Is it kind of like sanity_check()? But it's always called whenever one of these objects is constructed...? What are you worried about failing there — mathematics itself?

Is there a way to construct a twofish256 object without paying the performance penalty for the call to assurance()? Should there be? (I highly recommend that tests for the continued working of mathematics be relegated to your unit tests, rather than paying for them at runtime.)

Or, alternatively, if its purpose is more like "initial scrambling of the key", perhaps it should have a better name. (I tried googling "twofish assurance" and didn't find any support for this idea, though.)


const std::string FAIL_MSG = ...

Why do you put a copy of this std::string (complete with dynamic memory allocation) inside every twofish256 object? Most likely you just forgot a static here.

In C++14-and-later, to avoid such typo-bugs, you can mark class-level constants as static constexpr instead of static const. This will alert you (via a diagnostic) if you accidentally omit the static; and, as a bonus in this particular case, it will alert you that your std::string here is not constexpr-initializable because it allocates heap memory. What you meant here was

static constexpr const char FAIL_MSG[] = ...;

to eliminate the allocation.


Any time I see a global constant string like FAIL_MSG, that's a code smell. (Especially such a specific string: I immediately smell that it is probably used in only one place.) So I looked a bit closer and found that it was used in only one place, in the constructor:

if(!assurance()) throw std::runtime_error(FAIL_MSG);

Ah, here's that assurance() method again. And a throw, which is also problematic. It turns out that this one line is responsible for at least three different problems with twofish256:

  • It allocates space for FAIL_MSG's text on the heap
  • It allocates ~24 bytes for FAIL_MSG in each twofish256 object
  • It throws exceptions, preventing us from marking twofish256's constructor as noexcept
  • It causes us to pay for assurance() on each construction, even though that function will never return false
  • It drags in a dependency on <stdexcept>
  • It drags in a dependency on <string>
  • Arguably, the text of the message is too long, potentially confusing, and/or difficult to translate

(Okay, seven problems. I was close with "three".)

Removing this one line (and moving the assurance() function into a unit test) would fix all of these issues in one fell swoop.


return std::move(block_t {

This std::move is harmless (in this context) but unnecessary, so you should remove it. In general, if you grep your codebase for the phrase return std::move, you should find zero instances of it. The one legitimate exceptional case I've ever seen in real code is

struct Foo {
    std::string mData;
    std::string pilfer() && {
        return std::move(mData);  // move out of a member variable
    }
};

static inline word g(const sbox_t& sBox, word x, word R );

Putting inline on a declaration that's not a definition is usually (always?) pointless. Put inline on the definition to solve ODR issues (which in your case you have not got), or maybe sprinkle it around to encourage the compiler to optimize more (which is getting to be more and more voodoo these days, so probably don't do that — and besides, how's the compiler going to inline the definition of a function that is defined in a completely different .cc file?), but in any case, put it on the definition, not on the declaration.

I would even consider removing the declaration of g (and so on) from the .h file altogether, and replacing them with lambdas somewhere in the .cc file:

twofish256::session_key_t
twofish256::make_session_key(const user_key_t& user_key) {
    auto h = [](word x, k_vector_t& k32) -> word {
        // ...
    };
    // ...
}

This won't change the codegen, but it might be worth doing if it helps with the "localization of concerns": If h is useful only to make_session_key, then it doesn't necessarily make sense for it to be visible to the entire program.

\$\endgroup\$
6
  • \$\begingroup\$ with respect to wipe_session_key what if it wrote random values instead of zero would that be "optimised" away? What about using memset(...) with 0 or another value? \$\endgroup\$ – headwedge Nov 12 '17 at 21:20
  • 1
    \$\begingroup\$ excellent! I will make these changes but, although I concur that the assurance testing does probably belong in the unit testing stage, it is on the advice of Niels Ferguson (1 of the 6 original Twofish designers) in his 2002 C++ implementation that "Yes, you need to do this every time you start your program. It is called assurance; you have to be certain that your program still works properly." but I'm not sure that I agree because what could it detect beyond tampering with the header file? Should it simply be offered as a public member function for the user to perform their own assurance? \$\endgroup\$ – headwedge Nov 12 '17 at 21:26
  • \$\begingroup\$ @headwedge: w.r.t. wipe_session_key, any write to a variable which is never read again is a "dead write" and can be removed by the optimizer. And if the write is happening literally in the destructor, it's really easy to tell that it can't ever be read again. In standard C++, "memory" is merely an abstraction. Your compiler might "do the right thing" with your code, and that's actually great; but recognize that if you recompile it somewhere (or somewhen) else, your code might "break" (by silently degrading security). This should be frightening! :) \$\endgroup\$ – Quuxplusone Nov 13 '17 at 18:31
  • \$\begingroup\$ For an example of "non-portable code" breaking and quietly degrading security when compiled "somewhen else", take a look at the Debian OpenSSL random number generator vulnerability. I claim that Debian's code there was exactly analogous to your destructor's code here: it's trying to do something that doesn't fall within the standard C++ language's guarantees, and so it may or may not achieve its goal, and may or may not continue to achieve its goal in the future. \$\endgroup\$ – Quuxplusone Nov 13 '17 at 18:34
  • \$\begingroup\$ @headwedge: "Should it simply be offered as a public member function for the user to perform their own assurance?" Perhaps. Or as a public non-member or static-member function that constructs and operates on its own local object, instead of mutating the user's original object. "Every time you start your program" is significantly different from "every time you construct an object of type twofish256," even if most programs will only ever have one such object. Maybe consider a web server that makes a new twofish256 per thread, and makes a new thread per incoming request. Plausible? \$\endgroup\$ – Quuxplusone Nov 13 '17 at 18:37
0
\$\begingroup\$

Taking into account the excellent code review advice from @Quuxplusone and the comment exchanges I have made the following changes to the header and implementation files for twofish256 :

  • make the assurance testing available as a static member function and remove it from the ctor
  • provide advice about using assurance testing in the comments
  • make the ctor explicit, no throw and general purpose by taking any sequence of 32 byte convertible values
  • be-gone foul codesmell - global constant string FAIL_MSG removed
  • assurance testing promoted beyond the concerns of twofish256 but equip with the functionality for that testing (as above)
  • expunge wipe_session_key, any write to a variable which is never read again is a "dead write" and can/will be removed by the optimizer
  • if security might be degraded don't offer that guarantee in the first place
  • removed pointless inline qualifiers
  • removed pointless/harmless std::move
  • localize the h(...) function to make_session_key(...)
  • localize concerns for Reed-Solomon functions to make_session_key(...)

I have also made the following changes:

  • convert make_session_key(...) to non-static void mutator because this is more intuitive and less error prone
  • when localizing concerns for the g(...) function convert this small, but critical, function into a macro because this has increased performance as well as ensuring implementation detail does not leak out into the header file
  • localizing concerns, increase performance and improve security by converting the calculation constants to macros and confining them to the implementation file also

Here is the refactored header file:

namespace crypto {

class twofish256 {

    using word = uint32_t;
    using k_vector_t = std::array<word, 4>;
    using sbox_t = std::array<word, 1024>;       
    using subkeys_t = std::array<word, 40>;

public:

    using byte = uint8_t;
    using user_key_t = std::array<byte, 32>;
    using session_key_t = std::pair<sbox_t, subkeys_t>;
    using block_t = std::array<byte, 16>;

    template<class Sequence>
    explicit twofish256(Sequence&& seq) noexcept {
        assert(std::distance(std::begin(seq), std::end(seq)) == 32);
        user_key_t user_key;
        auto it = std::begin(seq);
        for(int i = 0; i < 32; ++i) user_key[i] = *it++;
        make_session_key(user_key);
    }

    twofish256(twofish256 const&) = delete;

    twofish256& operator=(twofish256 const&) = delete;

    block_t encrypt(const block_t& p);

    block_t decrypt(const block_t& c);

    static bool assurance_test();

    ~twofish256() = default;

private:

    session_key_t session_key;

    static const byte P[2][256];

    static const word MDS[4][256];

    void make_session_key(const user_key_t& user_key);

};
}

Here is the refactored implementation file:

namespace crypto {

//shorthand byte selectors from 32-bit word to simplify large formulae
//1st byte
#define B0(w) (w & 0xFF)
//2nd byte
#define B1(w) ((w >> 8) & 0xFF)
//3rd byte
#define B2(w) ((w >> 16) & 0xFF)
//4th byte
#define B3(w) ((w >> 24) & 0xFF)
//n-th byte
#define Bn(w, n) ((((w) >> (8*(n))) & 0xFF))

//subkey calculation shorthand
#define SK_STEP 0x02020202
#define SK_BUMP 0x01010101
#define SK_ROTL 9

//Primitive polynomials for GF(256)
//Galois field, 2m binary finite field of 2^32 of special interest because they are particularly efficient for implementation in binary computation.
//Addition is simply bit-by-bit XOR and multiplication of binary polynomials can be implemented as simple bit-shift and XOR.
#define GF256_FDBK    0x169
#define GF256_FDBK_2  0x169 / 2
#define GF256_FDBK_4  0x169 / 4
// field generator
#define RS_GF_FDBK 0x14D

//The g() function is the heart of the F round function. The input word <i>x</i> is split into four bytes. Each byte isrun through its own key-dependent S-box.
//Each Sbox is bijective, takes 8 bits of input, and produces8 bits of output.
//The four results are interpreted as a vector of length 4 over GF(256), and multiplied bythe 4×4 MDS matrix (using the field GF(256) for the computations).
//The resulting vector is interpreted as a 32-bit word which is the result of g.
//sBox  reference to the session S-boxes
//x  input word
//R  round input rotation
#define g(sBox, x, R)  (sBox[        2 * Bn(x, R  )    ] ^ \
                        sBox[        2 * Bn(x, R+1) + 1] ^ \
                        sBox[0x200 + 2 * Bn(x, R+2)    ] ^ \
                        sBox[0x200 + 2 * Bn(x, R+3) + 1])

void twofish256::make_session_key(const user_key_t& user_key) {
    //Expressing the h() and Reed-Solomon functions as lambdas helps with localization of concerns as they are useful only to make_session_key.

    //The h() function is the heart of the Twofish key expansion.
    //It is a complicated sequence of q-box lookups, key material XORs and finally the MDS matrix.
    //The Twofish specications show h() is applied to the even key words and then odd key words.
    //The h() function takes two inputs: a 32-bit word X and a list L = (L0, . . . , Lk−1) of 32-bit words of length k
    //Returns 32-bit word partial subkey
    auto h = [](word x, k_vector_t& L) -> word {
        //works in k (i.e. 4) stages, in each stage:
        //the four bytes (b0..b3)are each passed through the fixed permutation boxes then xored with a byte derived from the list.
        //the bytes are once again passed through a fixed permutation box,
        //finally, the four bytes are multiplied by the MDS matrix
        word b0 = B0(x);
        word b1 = B1(x);
        word b2 = B2(x);
        word b3 = B3(x);
        word l0 = L[0];
        word l1 = L[1];
        word l2 = L[2];
        word l3 = L[3];

         b0 = (P[1][b0] ) ^ B0(l3);
         b1 = (P[0][b1] ) ^ B1(l3);
         b2 = (P[0][b2] ) ^ B2(l3);
         b3 = (P[1][b3] ) ^ B3(l3);

         b0 = (P[1][b0] ) ^ B0(l2);
         b1 = (P[1][b1] ) ^ B1(l2);
         b2 = (P[0][b2] ) ^ B2(l2);
         b3 = (P[0][b3] ) ^ B3(l2);

         return
            MDS[0][(P[0][(P[0][b0] ) ^ B0(l1)] ) ^ B0(l0)] ^
            MDS[1][(P[0][(P[1][b1] ) ^ B1(l1)] ) ^ B1(l0)] ^
            MDS[2][(P[1][(P[0][b2] ) ^ B2(l1)] ) ^ B2(l0)] ^
            MDS[3][(P[1][(P[1][b3] ) ^ B3(l1)] ) ^ B3(l0)];
    };

    //Reed-Solomon lambda parameters: (12, 8) reversible code:
    // g(x) = x**4 + (a + 1/a) x**3 + a x**2 + (a + 1/a) x + 1
    //where a = primitive root of field generator 0x14D
    auto RS_rem = [](word x) -> word {
        word b  =  (x >> 24) & 0xFF;
        word g2 = ((b  <<  1) ^ ( (b & 0x80) != 0 ? RS_GF_FDBK : 0 )) & 0xFF;
        word g3 =  (b >>  1) ^ ( (b & 0x01) != 0 ? (RS_GF_FDBK >> 1) : 0 ) ^ g2 ;
        return (x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b;
    };

    //Use (12, 8) Reed-Solomon over GF(256) to produce a key S-box 32-bit entity from two key material 32-bit entities.
    // k0  1st 32-bit entity, k1  2nd 32-bit entity.
    //Remainder polynomial generated using RS_rem lambda
    auto RS_MDS_Encode = [&RS_rem](word k0, word k1) -> word {
        word r = k1;
        for (int i = 0; i < 4; ++i) { // shift 1 byte at a time
            r = RS_rem( r );
        }
        r ^= k0;
        for (int i = 0; i < 4; ++i) {
            r = RS_rem( r );
        }
        return r;
    };

    //Define N = 256 bits key size and k = N/64 giving 3 vectors of length k
    k_vector_t Me; // even 32-bit entities
    k_vector_t Mo; // odd 32-bit entities
    k_vector_t S;  // key bytes in groups of 8 over GF(2^8) in reverse order
    // split user key material into even and odd 32-bit words and
    // compute S-box keys using (12, 8) Reed-Solomon code over GF(256)
    word i, j, offset = 0;
    for (i = 0, j = 3; i < 4; ++i , j--) {
        Me[i] = user_key[offset] |
                user_key[offset + 1] <<  8 |
                user_key[offset + 2] << 16 |
                user_key[offset + 3] << 24;
        Mo[i] = user_key[offset + 4] |
                user_key[offset + 5] <<  8 |
                user_key[offset + 6] << 16 |
                user_key[offset + 7] << 24;
        S[j] = RS_MDS_Encode( Me[i], Mo[i] ); // reverse order
        offset += 8;
   }

    subkeys_t subkeys;
    // compute the 40 expanded subkeys

    word q, A, B;

    for (i = q = 0; i < 20; ++i, q += SK_STEP) {
        A = h(q        , Me ); // A uses even key entities
        B = h(q+SK_BUMP, Mo ); // B uses odd  key entities
        B = B << 8 | B >> 24;
        A += B;
        subkeys[2*i] = A; // combine with a Psuedo Hamard Transformation
        A += B;
        subkeys[2*i + 1] = A << SK_ROTL | A >> (32-SK_ROTL);
    }

    sbox_t sBox;
    //compute the 4 S-Boxes

    word k0 = S[0];
    word k1 = S[1];
    word k2 = S[2];
    word k3 = S[3];
    word b0, b1, b2, b3;

    for (i = 0; i < 256; ++i) {
        b0 = b1 = b2 = b3 = i;

        b0 = (P[1][b0] & 0xFF) ^ B0(k3);
        b1 = (P[0][b1] & 0xFF) ^ B1(k3);
        b2 = (P[0][b2] & 0xFF) ^ B2(k3);
        b3 = (P[1][b3] & 0xFF) ^ B3(k3);

        b0 = (P[1][b0] & 0xFF) ^ B0(k2);
        b1 = (P[1][b1] & 0xFF) ^ B1(k2);
        b2 = (P[0][b2] & 0xFF) ^ B2(k2);
        b3 = (P[0][b3] & 0xFF) ^ B3(k2);

        sBox[        (i << 1)] = MDS[0][(P[0][(P[0][b0] ) ^ B0(k1)]) ^ B0(k0)];
        sBox[    1 + (i << 1)] = MDS[1][(P[0][(P[1][b1] ) ^ B1(k1)]) ^ B1(k0)];
        sBox[0x200 + (i << 1)] = MDS[2][(P[1][(P[0][b2] ) ^ B2(k1)]) ^ B2(k0)];
        sBox[0x201 + (i << 1)] = MDS[3][(P[1][(P[1][b3] ) ^ B3(k1)]) ^ B3(k0)];

    }

    //combine S-boxes and subkeys as a pair for the session key
    session_key = std::make_pair(sBox, subkeys);
}

twofish256::block_t twofish256::encrypt(const block_t& p) {

    const sbox_t& sbox = session_key.first;
    const subkeys_t& skey = session_key.second;

    //plaintext p is split into four 32-bit words
    word x0 = p[0] | p[1] <<  8 | p[2] << 16 | p[3] << 24;
    word x1 = p[4] | p[5] <<  8 | p[6] << 16 | p[7] << 24;
    word x2 = p[8] | p[9] <<  8 | p[10] << 16 | p[11] << 24;
    word x3 = p[12] | p[13] <<  8 | p[14] << 16 | p[15] << 24;

    //these are XORed with the input whitening subkey words (0..3)
    x0 ^= skey[0];
    x1 ^= skey[1];
    x2 ^= skey[2];
    x3 ^= skey[3];

    int t0, t1; //results of the F function
    int k = 8; //encrpyt using the remaining 32 keys (8..39)

    //this is followed by 16 rounds as 8 cycles of key dependant permutations on 2 x 64 bit values (x0, x1) and (x2, x3)
    // T0 = g(R0)
    // T1 = g(ROL(R1, 8))
    // F0 = (T0 + T1 + K[2r+8]) mod 2^32
    // F1 = (T0 + 2T1 + K[2r+9]) mod 2^32
    //stepping through the keys with k++ saves having to calculate 2r+8 and 2r+9
    for (word i = 0; i < 8; ++i) {
         t0 = g(sbox, x0, 0);
         t1 = g(sbox, x1, 3);
         x2 ^= t0 + t1 + skey[k++];
         x2  = x2 >> 1 | x2 << 31;
         x3  = x3 << 1 | x3 >> 31;
         x3 ^= t0 + 2*t1 + skey[k++];

         t0 = g(sbox, x2, 0 );
         t1 = g(sbox, x3, 3 );
         x0 ^= t0 + t1 + skey[k++];
         x0  = x0 >> 1 | x0 << 31;
         x1  = x1 << 1 | x1 >> 31;
         x1 ^= t0 + 2*t1 + skey[k++];
    }

    //XORed with the output whitening subkey words (4..7)
    x2 ^= skey[4];
    x3 ^= skey[5];
    x0 ^= skey[6];
    x1 ^= skey[7];

    //before undoing the last swap and returning the block
    return block_t {
        (byte) x2, (byte)(x2 >> 8), (byte)(x2 >> 16), (byte)(x2 >> 24),
        (byte) x3, (byte)(x3 >> 8), (byte)(x3 >> 16), (byte)(x3 >> 24),
        (byte) x0, (byte)(x0 >> 8), (byte)(x0 >> 16), (byte)(x0 >> 24),
        (byte) x1, (byte)(x1 >> 8), (byte)(x1 >> 16), (byte)(x1 >> 24),
       };

}

twofish256::block_t twofish256::decrypt(const block_t& c) {

    const sbox_t& sbox = session_key.first;
    const subkeys_t& skey = session_key.second;

    //cyphertext c is split into four swapped 32-bit words
    word x2 = c[0] | c[1] <<  8 | c[2] << 16 | c[3] << 24;
    word x3 = c[4] | c[5] <<  8 | c[6] << 16 | c[7] << 24;
    word x0 = c[8] | c[9] <<  8 | c[10] << 16 | c[11] << 24;
    word x1 = c[12] | c[13] <<  8 | c[14] << 16 | c[15] << 24;

    //reverse the output whitening XORed with subkeys (4..7)
    x2 ^= skey[4];
    x3 ^= skey[5];
    x0 ^= skey[6];
    x1 ^= skey[7];

    int t0, t1; //results of the F function
    int k = 39; //decrpyt using the remaining 32 keys in reverse order (39..8)

    //this is followed by 16 rounds as 8 cycles of key dependant reverser order permutations on 2 x 64 bit values (x0, x1) and (x2, x3)
    // T0 = g(R0)
    // T1 = g(ROL(R1, 8))
    // F0 = (T0 + T1 + K[2r+8]) mod 2^32
    // F1 = (T0 + 2T1 + K[2r+9]) mod 2^32
    //stepping through the keys with k++ saves having to calculate 2r+8 and 2r+9
    for (word i = 0; i < 8; ++i) {
        t0 = g(sbox, x2, 0);
        t1 = g(sbox, x3, 3);
        x1 ^= t0 + 2*t1 + skey[k--];
        x1  = x1 >> 1 | x1 << 31;
        x0  = x0 << 1 | x0 >> 31;
        x0 ^= t0 + t1 + skey[k--];

        t0 = g(sbox, x0, 0);
        t1 = g(sbox, x1, 3);
        x3 ^= t0 + 2*t1 + skey[k--];
        x3  = x3 >> 1 | x3 << 31;
        x2  = x2 << 1 | x2 >> 31;
        x2 ^= t0 + t1 + skey[k--];
    }

    //reverse the input whitening XORed with subkeys (0..3)
    x0 ^= skey[0];
    x1 ^= skey[1];
    x2 ^= skey[2];
    x3 ^= skey[3];

    //before undoing the last swap and returning the block
    return block_t {
        (byte) x0, (byte)(x0 >> 8), (byte)(x0 >> 16), (byte)(x0 >> 24),
        (byte) x1, (byte)(x1 >> 8), (byte)(x1 >> 16), (byte)(x1 >> 24),
        (byte) x2, (byte)(x2 >> 8), (byte)(x2 >> 16), (byte)(x2 >> 24),
        (byte) x3, (byte)(x3 >> 8), (byte)(x3 >> 16), (byte)(x3 >> 24),
    };
}

bool twofish256::assurance_test() {
    user_key_t key ={0, 0, 0, 0, 0, 0, 0, 0,
                     0, 0, 0, 0, 0, 0, 0, 0,
                     0, 0, 0, 0, 0, 0, 0, 0,
                     0, 0, 0, 0, 0, 0, 0, 0};
    block_t pt = {0, 0, 0, 0, 0, 0, 0, 0,
                 0, 0, 0, 0, 0, 0, 0, 0};
    block_t ct = {0, 0, 0, 0, 0, 0, 0, 0,
                  0, 0, 0, 0, 0, 0, 0, 0};
    block_t tt;

    //final cipher text after 49 iterations
    block_t ft = {0x37, 0xFE, 0x26, 0xFF, 0x1C, 0xF6, 0x61, 0x75,
                  0xF5, 0xDD, 0xF4, 0xC3, 0x3B, 0x97, 0xA2, 0x05};

    for(int i = 0; i < 49; ++i) {
        memcpy(&key[16], &key, 16);
        memcpy(&key, &pt, 16);
        twofish256 ff(key);
        pt = ct;
        ct = ff.encrypt(pt);
        tt = ff.decrypt(ct);
        if(memcmp(&pt, &tt, 16) != 0) return false;
    }
    return !(memcmp(&ct, &ft, 16));
}

const twofish256::byte twofish256::P[2][256] = {
    { // q0
       0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76,
       0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38,
       0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C,
       0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48,
       0xF2, 0xD0, 0x8B, 0x30, 0x84, 0x54, 0xDF, 0x23,
       0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82,
       0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C,
       0xA6, 0xEB, 0xA5, 0xBE, 0x16, 0x0C, 0xE3, 0x61,
       0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B,
       0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1,
       0xE1, 0xE6, 0xBD, 0x45, 0xE2, 0xF4, 0xB6, 0x66,
       0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7,
       0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA,
       0xEA, 0x77, 0x39, 0xAF, 0x33, 0xC9, 0x62, 0x71,
       0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8,
       0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7,
       0xA1, 0x1D, 0xAA, 0xED, 0x06, 0x70, 0xB2, 0xD2,
       0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90,
       0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB,
       0x9E, 0x9C, 0x52, 0x1B, 0x5F, 0x93, 0x0A, 0xEF,
       0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B,
       0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64,
       0x2A, 0xCE, 0xCB, 0x2F, 0xFC, 0x97, 0x05, 0x7A,
       0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A,
       0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02,
       0xB8, 0xDA, 0xB0, 0x17, 0x55, 0x1F, 0x8A, 0x7D,
       0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72,
       0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34,
       0x6E, 0x50, 0xDE, 0x68, 0x65, 0xBC, 0xDB, 0xF8,
       0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4,
       0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00,
       0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0
    },
    {  // q1
       0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8,
       0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B,
       0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1,
       0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F,
       0x5E, 0xBA, 0xAE, 0x5B, 0x8A, 0x00, 0xBC, 0x9D,
       0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5,
       0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3,
       0xB2, 0x73, 0x4C, 0x54, 0x92, 0x74, 0x36, 0x51,
       0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96,
       0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C,
       0x13, 0x95, 0x9C, 0xC7, 0x24, 0x46, 0x3B, 0x70,
       0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8,
       0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC,
       0x03, 0x6F, 0x08, 0xBF, 0x40, 0xE7, 0x2B, 0xE2,
       0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9,
       0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17,
       0x66, 0x94, 0xA1, 0x1D, 0x3D, 0xF0, 0xDE, 0xB3,
       0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E,
       0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49,
       0x81, 0x88, 0xEE, 0x21, 0xC4, 0x1A, 0xEB, 0xD9,
       0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01,
       0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48,
       0x4F, 0xF2, 0x65, 0x8E, 0x78, 0x5C, 0x58, 0x19,
       0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64,
       0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5,
       0xCE, 0xE9, 0x68, 0x44, 0xE0, 0x4D, 0x43, 0x69,
       0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E,
       0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC,
       0x22, 0xC9, 0xC0, 0x9B, 0x89, 0xD4, 0xED, 0xAB,
       0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9,
       0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2,
       0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91
    }
};

const twofish256::word twofish256::MDS[4][256] = {
{
        0xBCBC3275,0xECEC21F3,0x202043C6,0xB3B3C9F4,0xDADA03DB,0x2028B7B,0xE2E22BFB,0x9E9EFAC8,
        0xC9C9EC4A,0xD4D409D3,0x18186BE6,0x1E1E9F6B,0x98980E45,0xB2B2387D,0xA6A6D2E8,0x2626B74B,
        0x3C3C57D6,0x93938A32,0x8282EED8,0x525298FD,0x7B7BD437,0xBBBB3771,0x5B5B97F1,0x474783E1,
        0x24243C30,0x5151E20F,0xBABAC6F8,0x4A4AF31B,0xBFBF4887,0xD0D70FA,0xB0B0B306,0x7575DE3F,
        0xD2D2FD5E,0x7D7D20BA,0x666631AE,0x3A3AA35B,0x59591C8A,0x00,0xCDCD93BC,0x1A1AE09D,
        0xAEAE2C6D,0x7F7FABC1,0x2B2BC7B1,0xBEBEB90E,0xE0E0A080,0x8A8A105D,0x3B3B52D2,0x6464BAD5,
        0xD8D888A0,0xE7E7A584,0x5F5FE807,0x1B1B1114,0x2C2CC2B5,0xFCFCB490,0x3131272C,0x808065A3,
        0x73732AB2,0xC0C8173,0x79795F4C,0x6B6B4154,0x4B4B0292,0x53536974,0x94948F36,0x83831F51,
        0x2A2A3638,0xC4C49CB0,0x2222C8BD,0xD5D5F85A,0xBDBDC3FC,0x48487860,0xFFFFCE62,0x4C4C0796,
        0x4141776C,0xC7C7E642,0xEBEB24F7,0x1C1C1410,0x5D5D637C,0x36362228,0x6767C027,0xE9E9AF8C,
        0x4444F913,0x1414EA95,0xF5F5BB9C,0xCFCF18C7,0x3F3F2D24,0xC0C0E346,0x7272DB3B,0x54546C70,
        0x29294CCA,0xF0F035E3,0x808FE85,0xC6C617CB,0xF3F34F11,0x8C8CE4D0,0xA4A45993,0xCACA96B8,
        0x68683BA6,0xB8B84D83,0x38382820,0xE5E52EFF,0xADAD569F,0xB0B8477,0xC8C81DC3,0x9999FFCC,
        0x5858ED03,0x19199A6F,0xE0E0A08,0x95957EBF,0x70705040,0xF7F730E7,0x6E6ECF2B,0x1F1F6EE2,
        0xB5B53D79,0x9090F0C,0x616134AA,0x57571682,0x9F9F0B41,0x9D9D803A,0x111164EA,0x2525CDB9,
        0xAFAFDDE4,0x4545089A,0xDFDF8DA4,0xA3A35C97,0xEAEAD57E,0x353558DA,0xEDEDD07A,0x4343FC17,
        0xF8F8CB66,0xFBFBB194,0x3737D3A1,0xFAFA401D,0xC2C2683D,0xB4B4CCF0,0x32325DDE,0x9C9C71B3,
        0x5656E70B,0xE3E3DA72,0x878760A7,0x15151B1C,0xF9F93AEF,0x6363BFD1,0x3434A953,0x9A9A853E,
        0xB1B1428F,0x7C7CD133,0x88889B26,0x3D3DA65F,0xA1A1D7EC,0xE4E4DF76,0x8181942A,0x91910149,
        0xF0FFB81,0xEEEEAA88,0x161661EE,0xD7D77321,0x9797F5C4,0xA5A5A81A,0xFEFE3FEB,0x6D6DB5D9,
        0x7878AEC5,0xC5C56D39,0x1D1DE599,0x7676A4CD,0x3E3EDCAD,0xCBCB6731,0xB6B6478B,0xEFEF5B01,
        0x12121E18,0x6060C523,0x6A6AB0DD,0x4D4DF61F,0xCECEE94E,0xDEDE7C2D,0x55559DF9,0x7E7E5A48,
        0x2121B24F,0x3037AF2,0xA0A02665,0x5E5E198E,0x5A5A6678,0x65654B5C,0x62624E58,0xFDFD4519,
        0x606F48D,0x404086E5,0xF2F2BE98,0x3333AC57,0x17179067,0x5058E7F,0xE8E85E05,0x4F4F7D64,
        0x89896AAF,0x10109563,0x74742FB6,0xA0A75FE,0x5C5C92F5,0x9B9B74B7,0x2D2D333C,0x3030D6A5,
        0x2E2E49CE,0x494989E9,0x46467268,0x77775544,0xA8A8D8E0,0x9696044D,0x2828BD43,0xA9A92969,
        0xD9D97929,0x8686912E,0xD1D187AC,0xF4F44A15,0x8D8D1559,0xD6D682A8,0xB9B9BC0A,0x42420D9E,
        0xF6F6C16E,0x2F2FB847,0xDDDD06DF,0x23233934,0xCCCC6235,0xF1F1C46A,0xC1C112CF,0x8585EBDC,
        0x8F8F9E22,0x7171A1C9,0x9090F0C0,0xAAAA539B,0x101F189,0x8B8BE1D4,0x4E4E8CED,0x8E8E6FAB,
        0xABABA212,0x6F6F3EA2,0xE6E6540D,0xDBDBF252,0x92927BBB,0xB7B7B602,0x6969CA2F,0x3939D9A9,
        0xD3D30CD7,0xA7A72361,0xA2A2AD1E,0xC3C399B4,0x6C6C4450,0x7070504,0x4047FF6,0x272746C2,
        0xACACA716,0xD0D07625,0x50501386,0xDCDCF756,0x84841A55,0xE1E15109,0x7A7A25BE,0x1313EF91
},
{
        0xA9D93939,0x67901717,0xB3719C9C,0xE8D2A6A6,0x4050707,0xFD985252,0xA3658080,0x76DFE4E4,
        0x9A084545,0x92024B4B,0x80A0E0E0,0x78665A5A,0xE4DDAFAF,0xDDB06A6A,0xD1BF6363,0x38362A2A,
        0xD54E6E6,0xC6432020,0x3562CCCC,0x98BEF2F2,0x181E1212,0xF724EBEB,0xECD7A1A1,0x6C774141,
        0x43BD2828,0x7532BCBC,0x37D47B7B,0x269B8888,0xFA700D0D,0x13F94444,0x94B1FBFB,0x485A7E7E,
        0xF27A0303,0xD0E48C8C,0x8B47B6B6,0x303C2424,0x84A5E7E7,0x54416B6B,0xDF06DDDD,0x23C56060,
        0x1945FDFD,0x5BA33A3A,0x3D68C2C2,0x59158D8D,0xF321ECEC,0xAE316666,0xA23E6F6F,0x82165757,
        0x63951010,0x15BEFEF,0x834DB8B8,0x2E918686,0xD9B56D6D,0x511F8383,0x9B53AAAA,0x7C635D5D,
        0xA63B6868,0xEB3FFEFE,0xA5D63030,0xBE257A7A,0x16A7ACAC,0xC0F0909,0xE335F0F0,0x6123A7A7,
        0xC0F09090,0x8CAFE9E9,0x3A809D9D,0xF5925C5C,0x73810C0C,0x2C273131,0x2576D0D0,0xBE75656,
        0xBB7B9292,0x4EE9CECE,0x89F10101,0x6B9F1E1E,0x53A93434,0x6AC4F1F1,0xB499C3C3,0xF1975B5B,
        0xE1834747,0xE66B1818,0xBDC82222,0x450E9898,0xE26E1F1F,0xF4C9B3B3,0xB62F7474,0x66CBF8F8,
        0xCCFF9999,0x95EA1414,0x3ED5858,0x56F7DCDC,0xD4E18B8B,0x1C1B1515,0x1EADA2A2,0xD70CD3D3,
        0xFB2BE2E2,0xC31DC8C8,0x8E195E5E,0xB5C22C2C,0xE9894949,0xCF12C1C1,0xBF7E9595,0xBA207D7D,
        0xEA641111,0x77840B0B,0x396DC5C5,0xAF6A8989,0x33D17C7C,0xC9A17171,0x62CEFFFF,0x7137BBBB,
        0x81FB0F0F,0x793DB5B5,0x951E1E1,0xADDC3E3E,0x242D3F3F,0xCDA47676,0xF99D5555,0xD8EE8282,
        0xE5864040,0xC5AE7878,0xB9CD2525,0x4D049696,0x44557777,0x80A0E0E,0x86135050,0xE730F7F7,
        0xA1D33737,0x1D40FAFA,0xAA346161,0xED8C4E4E,0x6B3B0B0,0x706C5454,0xB22A7373,0xD2523B3B,
        0x410B9F9F,0x7B8B0202,0xA088D8D8,0x114FF3F3,0x3167CBCB,0xC2462727,0x27C06767,0x90B4FCFC,
        0x20283838,0xF67F0404,0x60784848,0xFF2EE5E5,0x96074C4C,0x5C4B6565,0xB1C72B2B,0xAB6F8E8E,
        0x9E0D4242,0x9CBBF5F5,0x52F2DBDB,0x1BF34A4A,0x5FA63D3D,0x9359A4A4,0xABCB9B9,0xEF3AF9F9,
        0x91EF1313,0x85FE0808,0x49019191,0xEE611616,0x2D7CDEDE,0x4FB22121,0x8F42B1B1,0x3BDB7272,
        0x47B82F2F,0x8748BFBF,0x6D2CAEAE,0x46E3C0C0,0xD6573C3C,0x3E859A9A,0x6929A9A9,0x647D4F4F,
        0x2A948181,0xCE492E2E,0xCB17C6C6,0x2FCA6969,0xFCC3BDBD,0x975CA3A3,0x55EE8E8,0x7AD0EDED,
        0xAC87D1D1,0x7F8E0505,0xD5BA6464,0x1AA8A5A5,0x4BB72626,0xEB9BEBE,0xA7608787,0x5AF8D5D5,
        0x28223636,0x14111B1B,0x3FDE7575,0x2979D9D9,0x88AAEEEE,0x3C332D2D,0x4C5F7979,0x2B6B7B7,
        0xB896CACA,0xDA583535,0xB09CC4C4,0x17FC4343,0x551A8484,0x1FF64D4D,0x8A1C5959,0x7D38B2B2,
        0x57AC3333,0xC718CFCF,0x8DF40606,0x74695353,0xB7749B9B,0xC4F59797,0x9F56ADAD,0x72DAE3E3,
        0x7ED5EAEA,0x154AF4F4,0x229E8F8F,0x12A2ABAB,0x584E6262,0x7E85F5F,0x99E51D1D,0x34392323,
        0x6EC1F6F6,0x50446C6C,0xDE5D3232,0x68724646,0x6526A0A0,0xBC93CDCD,0xDB03DADA,0xF8C6BABA,
        0xC8FA9E9E,0xA882D6D6,0x2BCF6E6E,0x40507070,0xDCEB8585,0xFE750A0A,0x328A9393,0xA48DDFDF,
        0xCA4C2929,0x10141C1C,0x2173D7D7,0xF0CCB4B4,0xD309D4D4,0x5D108A8A,0xFE25151,0x00,
        0x6F9A1919,0x9DE01A1A,0x368F9494,0x42E6C7C7,0x4AECC9C9,0x5EFDD2D2,0xC1AB7F7F,0xE0D8A8A8
},
{
        0xBC75BC32,0xECF3EC21,0x20C62043,0xB3F4B3C9,0xDADBDA03,0x27B028B,0xE2FBE22B,0x9EC89EFA,
        0xC94AC9EC,0xD4D3D409,0x18E6186B,0x1E6B1E9F,0x9845980E,0xB27DB238,0xA6E8A6D2,0x264B26B7,
        0x3CD63C57,0x9332938A,0x82D882EE,0x52FD5298,0x7B377BD4,0xBB71BB37,0x5BF15B97,0x47E14783,
        0x2430243C,0x510F51E2,0xBAF8BAC6,0x4A1B4AF3,0xBF87BF48,0xDFA0D70,0xB006B0B3,0x753F75DE,
        0xD25ED2FD,0x7DBA7D20,0x66AE6631,0x3A5B3AA3,0x598A591C,0x00,0xCDBCCD93,0x1A9D1AE0,
        0xAE6DAE2C,0x7FC17FAB,0x2BB12BC7,0xBE0EBEB9,0xE080E0A0,0x8A5D8A10,0x3BD23B52,0x64D564BA,
        0xD8A0D888,0xE784E7A5,0x5F075FE8,0x1B141B11,0x2CB52CC2,0xFC90FCB4,0x312C3127,0x80A38065,
        0x73B2732A,0xC730C81,0x794C795F,0x6B546B41,0x4B924B02,0x53745369,0x9436948F,0x8351831F,
        0x2A382A36,0xC4B0C49C,0x22BD22C8,0xD55AD5F8,0xBDFCBDC3,0x48604878,0xFF62FFCE,0x4C964C07,
        0x416C4177,0xC742C7E6,0xEBF7EB24,0x1C101C14,0x5D7C5D63,0x36283622,0x672767C0,0xE98CE9AF,
        0x441344F9,0x149514EA,0xF59CF5BB,0xCFC7CF18,0x3F243F2D,0xC046C0E3,0x723B72DB,0x5470546C,
        0x29CA294C,0xF0E3F035,0x88508FE,0xC6CBC617,0xF311F34F,0x8CD08CE4,0xA493A459,0xCAB8CA96,
        0x68A6683B,0xB883B84D,0x38203828,0xE5FFE52E,0xAD9FAD56,0xB770B84,0xC8C3C81D,0x99CC99FF,
        0x580358ED,0x196F199A,0xE080E0A,0x95BF957E,0x70407050,0xF7E7F730,0x6E2B6ECF,0x1FE21F6E,
        0xB579B53D,0x90C090F,0x61AA6134,0x57825716,0x9F419F0B,0x9D3A9D80,0x11EA1164,0x25B925CD,
        0xAFE4AFDD,0x459A4508,0xDFA4DF8D,0xA397A35C,0xEA7EEAD5,0x35DA3558,0xED7AEDD0,0x431743FC,
        0xF866F8CB,0xFB94FBB1,0x37A137D3,0xFA1DFA40,0xC23DC268,0xB4F0B4CC,0x32DE325D,0x9CB39C71,
        0x560B56E7,0xE372E3DA,0x87A78760,0x151C151B,0xF9EFF93A,0x63D163BF,0x345334A9,0x9A3E9A85,
        0xB18FB142,0x7C337CD1,0x8826889B,0x3D5F3DA6,0xA1ECA1D7,0xE476E4DF,0x812A8194,0x91499101,
        0xF810FFB,0xEE88EEAA,0x16EE1661,0xD721D773,0x97C497F5,0xA51AA5A8,0xFEEBFE3F,0x6DD96DB5,
        0x78C578AE,0xC539C56D,0x1D991DE5,0x76CD76A4,0x3EAD3EDC,0xCB31CB67,0xB68BB647,0xEF01EF5B,
        0x1218121E,0x602360C5,0x6ADD6AB0,0x4D1F4DF6,0xCE4ECEE9,0xDE2DDE7C,0x55F9559D,0x7E487E5A,
        0x214F21B2,0x3F2037A,0xA065A026,0x5E8E5E19,0x5A785A66,0x655C654B,0x6258624E,0xFD19FD45,
        0x68D06F4,0x40E54086,0xF298F2BE,0x335733AC,0x17671790,0x57F058E,0xE805E85E,0x4F644F7D,
        0x89AF896A,0x10631095,0x74B6742F,0xAFE0A75,0x5CF55C92,0x9BB79B74,0x2D3C2D33,0x30A530D6,
        0x2ECE2E49,0x49E94989,0x46684672,0x77447755,0xA8E0A8D8,0x964D9604,0x284328BD,0xA969A929,
        0xD929D979,0x862E8691,0xD1ACD187,0xF415F44A,0x8D598D15,0xD6A8D682,0xB90AB9BC,0x429E420D,
        0xF66EF6C1,0x2F472FB8,0xDDDFDD06,0x23342339,0xCC35CC62,0xF16AF1C4,0xC1CFC112,0x85DC85EB,
        0x8F228F9E,0x71C971A1,0x90C090F0,0xAA9BAA53,0x18901F1,0x8BD48BE1,0x4EED4E8C,0x8EAB8E6F,
        0xAB12ABA2,0x6FA26F3E,0xE60DE654,0xDB52DBF2,0x92BB927B,0xB702B7B6,0x692F69CA,0x39A939D9,
        0xD3D7D30C,0xA761A723,0xA21EA2AD,0xC3B4C399,0x6C506C44,0x7040705,0x4F6047F,0x27C22746,
        0xAC16ACA7,0xD025D076,0x50865013,0xDC56DCF7,0x8455841A,0xE109E151,0x7ABE7A25,0x139113EF
},
{
        0xD939A9D9,0x90176790,0x719CB371,0xD2A6E8D2,0x5070405,0x9852FD98,0x6580A365,0xDFE476DF,
        0x8459A08,0x24B9202,0xA0E080A0,0x665A7866,0xDDAFE4DD,0xB06ADDB0,0xBF63D1BF,0x362A3836,
        0x54E60D54,0x4320C643,0x62CC3562,0xBEF298BE,0x1E12181E,0x24EBF724,0xD7A1ECD7,0x77416C77,
        0xBD2843BD,0x32BC7532,0xD47B37D4,0x9B88269B,0x700DFA70,0xF94413F9,0xB1FB94B1,0x5A7E485A,
        0x7A03F27A,0xE48CD0E4,0x47B68B47,0x3C24303C,0xA5E784A5,0x416B5441,0x6DDDF06,0xC56023C5,
        0x45FD1945,0xA33A5BA3,0x68C23D68,0x158D5915,0x21ECF321,0x3166AE31,0x3E6FA23E,0x16578216,
        0x95106395,0x5BEF015B,0x4DB8834D,0x91862E91,0xB56DD9B5,0x1F83511F,0x53AA9B53,0x635D7C63,
        0x3B68A63B,0x3FFEEB3F,0xD630A5D6,0x257ABE25,0xA7AC16A7,0xF090C0F,0x35F0E335,0x23A76123,
        0xF090C0F0,0xAFE98CAF,0x809D3A80,0x925CF592,0x810C7381,0x27312C27,0x76D02576,0xE7560BE7,
        0x7B92BB7B,0xE9CE4EE9,0xF10189F1,0x9F1E6B9F,0xA93453A9,0xC4F16AC4,0x99C3B499,0x975BF197,
        0x8347E183,0x6B18E66B,0xC822BDC8,0xE98450E,0x6E1FE26E,0xC9B3F4C9,0x2F74B62F,0xCBF866CB,
        0xFF99CCFF,0xEA1495EA,0xED5803ED,0xF7DC56F7,0xE18BD4E1,0x1B151C1B,0xADA21EAD,0xCD3D70C,
        0x2BE2FB2B,0x1DC8C31D,0x195E8E19,0xC22CB5C2,0x8949E989,0x12C1CF12,0x7E95BF7E,0x207DBA20,
        0x6411EA64,0x840B7784,0x6DC5396D,0x6A89AF6A,0xD17C33D1,0xA171C9A1,0xCEFF62CE,0x37BB7137,
        0xFB0F81FB,0x3DB5793D,0x51E10951,0xDC3EADDC,0x2D3F242D,0xA476CDA4,0x9D55F99D,0xEE82D8EE,
        0x8640E586,0xAE78C5AE,0xCD25B9CD,0x4964D04,0x55774455,0xA0E080A,0x13508613,0x30F7E730,
        0xD337A1D3,0x40FA1D40,0x3461AA34,0x8C4EED8C,0xB3B006B3,0x6C54706C,0x2A73B22A,0x523BD252,
        0xB9F410B,0x8B027B8B,0x88D8A088,0x4FF3114F,0x67CB3167,0x4627C246,0xC06727C0,0xB4FC90B4,
        0x28382028,0x7F04F67F,0x78486078,0x2EE5FF2E,0x74C9607,0x4B655C4B,0xC72BB1C7,0x6F8EAB6F,
        0xD429E0D,0xBBF59CBB,0xF2DB52F2,0xF34A1BF3,0xA63D5FA6,0x59A49359,0xBCB90ABC,0x3AF9EF3A,
        0xEF1391EF,0xFE0885FE,0x1914901,0x6116EE61,0x7CDE2D7C,0xB2214FB2,0x42B18F42,0xDB723BDB,
        0xB82F47B8,0x48BF8748,0x2CAE6D2C,0xE3C046E3,0x573CD657,0x859A3E85,0x29A96929,0x7D4F647D,
        0x94812A94,0x492ECE49,0x17C6CB17,0xCA692FCA,0xC3BDFCC3,0x5CA3975C,0x5EE8055E,0xD0ED7AD0,
        0x87D1AC87,0x8E057F8E,0xBA64D5BA,0xA8A51AA8,0xB7264BB7,0xB9BE0EB9,0x6087A760,0xF8D55AF8,
        0x22362822,0x111B1411,0xDE753FDE,0x79D92979,0xAAEE88AA,0x332D3C33,0x5F794C5F,0xB6B702B6,
        0x96CAB896,0x5835DA58,0x9CC4B09C,0xFC4317FC,0x1A84551A,0xF64D1FF6,0x1C598A1C,0x38B27D38,
        0xAC3357AC,0x18CFC718,0xF4068DF4,0x69537469,0x749BB774,0xF597C4F5,0x56AD9F56,0xDAE372DA,
        0xD5EA7ED5,0x4AF4154A,0x9E8F229E,0xA2AB12A2,0x4E62584E,0xE85F07E8,0xE51D99E5,0x39233439,
        0xC1F66EC1,0x446C5044,0x5D32DE5D,0x72466872,0x26A06526,0x93CDBC93,0x3DADB03,0xC6BAF8C6,
        0xFA9EC8FA,0x82D6A882,0xCF6E2BCF,0x50704050,0xEB85DCEB,0x750AFE75,0x8A93328A,0x8DDFA48D,
        0x4C29CA4C,0x141C1014,0x73D72173,0xCCB4F0CC,0x9D4D309,0x108A5D10,0xE2510FE2,0x00,
        0x9A196F9A,0xE01A9DE0,0x8F94368F,0xE6C742E6,0xECC94AEC,0xFDD25EFD,0xAB7FC1AB,0xD8A8E0D8
}

};
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.