// Vendored from https://github.com/LowMC/lowmc // commit e847fb160ad8ca1f373efd91a55b6d67f7deb425 // Local changes: namespace lowmc; definitions are inline so a header-only // user can include this file; Grain state restarts in instantiate_LowMC. #include #include #include #include #include #include "LowMC.h" #include namespace lowmc { // Shared by every LowMC instance. instantiate_LowMC restarts it. inline std::bitset<80> & grain_state() { static std::bitset<80> state; return state; } ///////////////////////////// // LowMC functions // ///////////////////////////// inline block LowMC::encrypt (const block message) { block c = message ^ roundkeys[0]; for (unsigned r = 1; r <= rounds; ++r) { c = Substitution(c); c = MultiplyWithGF2Matrix(LinMatrices[r-1], c); c ^= roundconstants[r-1]; c ^= roundkeys[r]; } return c; } inline block LowMC::decrypt (const block message) { block c = message; for (unsigned r = rounds; r > 0; --r) { c ^= roundkeys[r]; c ^= roundconstants[r-1]; c = MultiplyWithGF2Matrix(invLinMatrices[r-1], c); c = invSubstitution(c); } c ^= roundkeys[0]; return c; } inline void LowMC::set_key (keyblock k) { key = k; keyschedule(); } inline void LowMC::print_matrices() { std::cout << "LowMC matrices and constants" << std::endl; std::cout << "============================" << std::endl; std::cout << "Block size: " << blocksize << std::endl; std::cout << "Key size: " << keysize << std::endl; std::cout << "Rounds: " << rounds << std::endl; std::cout << std::endl; std::cout << "Linear layer matrices" << std::endl; std::cout << "---------------------" << std::endl; for (unsigned r = 1; r <= rounds; ++r) { std::cout << "Linear layer " << r << ":" << std::endl; for (auto row: LinMatrices[r-1]) { std::cout << "["; for (unsigned i = 0; i < blocksize; ++i) { std::cout << row[i]; if (i != blocksize - 1) { std::cout << ", "; } } std::cout << "]" << std::endl; } std::cout << std::endl; } std::cout << "Round constants" << std::endl; std::cout << "---------------------" << std::endl; for (unsigned r = 1; r <= rounds; ++r) { std::cout << "Round constant " << r << ":" << std::endl; std::cout << "["; for (unsigned i = 0; i < blocksize; ++i) { std::cout << roundconstants[r-1][i]; if (i != blocksize - 1) { std::cout << ", "; } } std::cout << "]" << std::endl; std::cout << std::endl; } std::cout << "Round key matrices" << std::endl; std::cout << "---------------------" << std::endl; for (unsigned r = 0; r <= rounds; ++r) { std::cout << "Round key matrix " << r << ":" << std::endl; for (auto row: KeyMatrices[r]) { std::cout << "["; for (unsigned i = 0; i < keysize; ++i) { std::cout << row[i]; if (i != keysize - 1) { std::cout << ", "; } } std::cout << "]" << std::endl; } if (r != rounds) { std::cout << std::endl; } } } ///////////////////////////// // LowMC private functions // ///////////////////////////// inline block LowMC::Substitution (const block message) { block temp = 0; //Get the identity part of the message temp ^= (message >> 3*numofboxes); //Get the rest through the Sboxes for (unsigned i = 1; i <= numofboxes; ++i) { temp <<= 3; temp ^= Sbox[ ((message >> 3*(numofboxes-i)) & block(0x7)).to_ulong()]; } return temp; } inline block LowMC::invSubstitution (const block message) { block temp = 0; //Get the identity part of the message temp ^= (message >> 3*numofboxes); //Get the rest through the invSboxes for (unsigned i = 1; i <= numofboxes; ++i) { temp <<= 3; temp ^= invSbox[ ((message >> 3*(numofboxes-i)) & block(0x7)).to_ulong()]; } return temp; } inline block LowMC::MultiplyWithGF2Matrix (const std::vector matrix, const block message) { block temp = 0; for (unsigned i = 0; i < blocksize; ++i) { temp[i] = (message & matrix[i]).count() % 2; } return temp; } inline block LowMC::MultiplyWithGF2Matrix_Key (const std::vector matrix, const keyblock k) { block temp = 0; for (unsigned i = 0; i < blocksize; ++i) { temp[i] = (k & matrix[i]).count() % 2; } return temp; } inline void LowMC::keyschedule () { roundkeys.clear(); for (unsigned r = 0; r <= rounds; ++r) { roundkeys.push_back( MultiplyWithGF2Matrix_Key (KeyMatrices[r], key) ); } return; } inline void LowMC::instantiate_LowMC () { // Grain is specified to start from the all-ones state. Restart so a // later instance does not continue the previous instance's stream. grain_state().reset(); // Create LinMatrices and invLinMatrices LinMatrices.clear(); invLinMatrices.clear(); for (unsigned r = 0; r < rounds; ++r) { // Create matrix std::vector mat; // Fill matrix with random bits do { mat.clear(); for (unsigned i = 0; i < blocksize; ++i) { mat.push_back( getrandblock () ); } // Repeat if matrix is not invertible } while ( rank_of_Matrix(mat) != blocksize ); LinMatrices.push_back(mat); invLinMatrices.push_back(invert_Matrix (LinMatrices.back())); } // Create roundconstants roundconstants.clear(); for (unsigned r = 0; r < rounds; ++r) { roundconstants.push_back( getrandblock () ); } // Create KeyMatrices KeyMatrices.clear(); for (unsigned r = 0; r <= rounds; ++r) { // Create matrix std::vector mat; // Fill matrix with random bits do { mat.clear(); for (unsigned i = 0; i < blocksize; ++i) { mat.push_back( getrandkeyblock () ); } // Repeat if matrix is not of maximal rank } while ( rank_of_Matrix_Key(mat) < std::min(blocksize, keysize) ); KeyMatrices.push_back(mat); } return; } ///////////////////////////// // Binary matrix functions // ///////////////////////////// inline unsigned LowMC::rank_of_Matrix (const std::vector matrix) { std::vector mat; //Copy of the matrix for (auto u : matrix) { mat.push_back(u); } unsigned size = mat[0].size(); //Transform to upper triangular matrix unsigned row = 0; for (unsigned col = 1; col <= size; ++col) { if ( !mat[row][size-col] ) { unsigned r = row; while (r < mat.size() && !mat[r][size-col]) { ++r; } if (r >= mat.size()) { continue; } else { auto temp = mat[row]; mat[row] = mat[r]; mat[r] = temp; } } for (unsigned i = row+1; i < mat.size(); ++i) { if ( mat[i][size-col] ) mat[i] ^= mat[row]; } ++row; if (row == size) break; } return row; } inline unsigned LowMC::rank_of_Matrix_Key (const std::vector matrix) { std::vector mat; //Copy of the matrix for (auto u : matrix) { mat.push_back(u); } unsigned size = mat[0].size(); //Transform to upper triangular matrix unsigned row = 0; for (unsigned col = 1; col <= size; ++col) { if ( !mat[row][size-col] ) { unsigned r = row; while (r < mat.size() && !mat[r][size-col]) { ++r; } if (r >= mat.size()) { continue; } else { auto temp = mat[row]; mat[row] = mat[r]; mat[r] = temp; } } for (unsigned i = row+1; i < mat.size(); ++i) { if ( mat[i][size-col] ) mat[i] ^= mat[row]; } ++row; if (row == size) break; } return row; } inline std::vector LowMC::invert_Matrix (const std::vector matrix) { std::vector mat; //Copy of the matrix for (auto u : matrix) { mat.push_back(u); } std::vector invmat(blocksize, 0); //To hold the inverted matrix for (unsigned i = 0; i < blocksize; ++i) { invmat[i][i] = 1; } unsigned size = mat[0].size(); //Transform to upper triangular matrix unsigned row = 0; for (unsigned col = 0; col < size; ++col) { if ( !mat[row][col] ) { unsigned r = row+1; while (r < mat.size() && !mat[r][col]) { ++r; } if (r >= mat.size()) { continue; } else { auto temp = mat[row]; mat[row] = mat[r]; mat[r] = temp; temp = invmat[row]; invmat[row] = invmat[r]; invmat[r] = temp; } } for (unsigned i = row+1; i < mat.size(); ++i) { if ( mat[i][col] ) { mat[i] ^= mat[row]; invmat[i] ^= invmat[row]; } } ++row; } //Transform to identity matrix for (unsigned col = size; col > 0; --col) { for (unsigned r = 0; r < col-1; ++r) { if (mat[r][col-1]) { mat[r] ^= mat[col-1]; invmat[r] ^= invmat[col-1]; } } } return invmat; } /////////////////////// // Pseudorandom bits // /////////////////////// inline block LowMC::getrandblock () { block tmp = 0; for (unsigned i = 0; i < blocksize; ++i) tmp[i] = getrandbit (); return tmp; } inline keyblock LowMC::getrandkeyblock () { keyblock tmp = 0; for (unsigned i = 0; i < keysize; ++i) tmp[i] = getrandbit (); return tmp; } // Uses the Grain LSFR as self-shrinking generator to create pseudorandom bits // Is initialized with the all 1s state // The first 160 bits are thrown away inline bool LowMC::getrandbit () { std::bitset<80> & state = grain_state(); // 80 bit LFSR state bool tmp = 0; //If state has not been initialized yet if (state.none ()) { state.set (); //Initialize with all bits set //Throw the first 160 bits away for (unsigned i = 0; i < 160; ++i) { //Update the state tmp = state[0] ^ state[13] ^ state[23] ^ state[38] ^ state[51] ^ state[62]; state >>= 1; state[79] = tmp; } } //choice records whether the first bit is 1 or 0. //The second bit is produced if the first bit is 1. bool choice = false; do { //Update the state tmp = state[0] ^ state[13] ^ state[23] ^ state[38] ^ state[51] ^ state[62]; state >>= 1; state[79] = tmp; choice = tmp; tmp = state[0] ^ state[13] ^ state[23] ^ state[38] ^ state[51] ^ state[62]; state >>= 1; state[79] = tmp; } while (! choice); return tmp; } } // namespace lowmc